Class: EC2::Common::HeadersV4

Inherits:
Object
  • Object
show all
Defined in:
lib/ec2/common/headersv4.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(values, headers = {}) ⇒ HeadersV4

Create an HttpHeaderV4, values =

:host -> http host
:hexdigest_body -> hexdigest of the http request's body
:region -> region of the endpoint
:service -> the service that should recieve this request
:http_method -> http method
:path -> URI
:querystring -> Everything after ? in URI
:access_key_id -> access key
:secret_access_key -> secret access kry
[optional] :fixed_datetime -> Fix the datetime using DateTime object, do not use Time.now

For more info see: docs.aws.amazon.com/general/latest/gr/signature-version-4.html



44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/ec2/common/headersv4.rb', line 44

def initialize values, headers={}
  @headers = headers
  @host = values[:host]
  @hexdigest_body = values[:hexdigest_body]
  @region = values[:region]
  @service = values[:service]
  @http_method = values[:http_method]
  @path = values[:path]
  @querystring = values[:querystring]
  @access_key_id = values[:access_key_id]
  @secret_access_key = values[:secret_access_key]
  @fixed_datetime = values[:fixed_datetime]
end

Class Method Details

.hexdigest(value, chunk_size = 1024 * 1024) ⇒ Object

Returns a SHA256 hexdigest of value. Should be used to hexdigest body of http request.



160
161
162
163
164
165
166
167
168
169
170
# File 'lib/ec2/common/headersv4.rb', line 160

def self.hexdigest value, chunk_size = 1024 * 1024
  digest = Digest::SHA256.new
  if value.respond_to?(:read)
    chunk = nil
    digest.update(chunk) while chunk = value.read(chunk_size)
    value.rewind
  else
    digest.update(value)
  end
  digest.hexdigest
end

Instance Method Details

#add_authorization!Object



58
59
60
61
62
63
64
65
# File 'lib/ec2/common/headersv4.rb', line 58

def add_authorization!
  datetime = get_datetime
  @headers['host'] = @host
  @headers['x-amz-date'] = datetime
  @headers['x-amz-content-sha256'] ||= @hexdigest_body || EC2::Common::HeadersV4::hexdigest('')
  @headers['authorization'] = authorization(datetime)
  @headers
end

#authorization(datetime) ⇒ Object



67
68
69
70
71
72
73
# File 'lib/ec2/common/headersv4.rb', line 67

def authorization datetime
  parts = []
  parts << "AWS4-HMAC-SHA256 Credential=#{credential(datetime)}"
  parts << "SignedHeaders=#{signed_headers}"
  parts << "Signature=#{signature(datetime)}"
  parts.join(', ')
end

#canonical_header_values(values) ⇒ Object



140
141
142
143
# File 'lib/ec2/common/headersv4.rb', line 140

def canonical_header_values values
  values = [values] unless values.is_a?(Array)
  values.map{|v|v.to_s}.join(',').gsub(/\s+/, ' ').strip
end

#canonical_headersObject



130
131
132
133
134
135
136
137
138
# File 'lib/ec2/common/headersv4.rb', line 130

def canonical_headers
  headers = []
  @headers.each_pair do |k,v|
    k_lower = k.downcase
    headers << [k_lower,v] unless k_lower == 'authorization'
  end
  headers = headers.sort_by{|k,v| k}
  headers.map{|k,v| "#{k}:#{canonical_header_values(v)}"}.join("\n")
end

#canonical_querystringObject



123
124
125
126
127
128
# File 'lib/ec2/common/headersv4.rb', line 123

def canonical_querystring
  CGI::parse(@querystring).sort_by{|k,v| CGI::escape(k)}.map do |v|
    value = v[1][0] || ""
    "#{CGI::escape(v[0])}=#{CGI::escape(value)}"
  end.join('&')
end

#canonical_requestObject



106
107
108
109
110
111
112
113
114
115
# File 'lib/ec2/common/headersv4.rb', line 106

def canonical_request
  parts = []
  parts << @http_method
  parts << CGI::unescape(@path)
  parts << canonical_querystring
  parts << canonical_headers + "\n"
  parts << signed_headers
  parts << @headers['x-amz-content-sha256']
  parts.join("\n")
end

#credential(datetime) ⇒ Object



93
94
95
# File 'lib/ec2/common/headersv4.rb', line 93

def credential datetime
  "#{@access_key_id}/#{credential_string(datetime)}"
end

#credential_string(datetime) ⇒ Object



97
98
99
100
101
102
103
104
# File 'lib/ec2/common/headersv4.rb', line 97

def credential_string datetime
  parts = []
  parts << datetime[0,8]
  parts << @region
  parts << @service
  parts << 'aws4_request'
  parts.join("/")
end

#get_datetimeObject



153
154
155
156
# File 'lib/ec2/common/headersv4.rb', line 153

def get_datetime
  return @fixed_datetime.strftime("%Y%m%dT%H%M%SZ") if @fixed_datetime != nil
  Time.now.utc.strftime("%Y%m%dT%H%M%SZ")
end

#hexhmac(key, value) ⇒ Object



149
150
151
# File 'lib/ec2/common/headersv4.rb', line 149

def hexhmac key, value
  OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new('sha256'), key, value)
end

#hmac(key, value) ⇒ Object



145
146
147
# File 'lib/ec2/common/headersv4.rb', line 145

def hmac key, value
  OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha256'), key, value)
end

#signature(datetime) ⇒ Object



75
76
77
78
79
80
81
82
# File 'lib/ec2/common/headersv4.rb', line 75

def signature datetime
  k_secret = @secret_access_key
  k_date = hmac("AWS4" + k_secret, datetime[0,8])
  k_region = hmac(k_date, @region)
  k_service = hmac(k_region, @service)
  k_credentials = hmac(k_service, 'aws4_request')
  hexhmac(k_credentials, string_to_sign(datetime))
end

#signed_headersObject



117
118
119
120
121
# File 'lib/ec2/common/headersv4.rb', line 117

def signed_headers
  to_sign = @headers.keys.map{|k| k.to_s.downcase}
  to_sign.delete('authorization')
  to_sign.sort.join(";")
end

#string_to_sign(datetime) ⇒ Object



84
85
86
87
88
89
90
91
# File 'lib/ec2/common/headersv4.rb', line 84

def string_to_sign datetime
  parts = []
  parts << 'AWS4-HMAC-SHA256'
  parts << datetime
  parts << credential_string(datetime)
  parts << EC2::Common::HeadersV4::hexdigest(canonical_request)
  parts.join("\n")
end