class AWS::Core::Signers::Version4
@api private
Constants
- EMPTY_DIGEST
@api private SHA256 hex digest of the empty string
- STREAMING_CHECKSUM
@api private
Attributes
@return [CredentialProviders::Provider]
@return [String]
@return [String]
Public Class Methods
@param [CredentialProviders::Provider] credentials @param [String] #service_name @param [String] region
# File lib/aws/core/signers/version_4.rb, line 36 def initialize credentials, service_name, region @credentials = credentials @service_name = service_name @region = region end
Public Instance Methods
# File lib/aws/core/signers/version_4.rb, line 87 def credential(datetime) "#{credentials.access_key_id}/#{key_path(datetime)}" end
# File lib/aws/core/signers/version_4.rb, line 91 def derive_key(datetime) k_secret = credentials.secret_access_key k_date = hmac("AWS4" + k_secret, datetime[0,8]) k_region = hmac(k_date, region) k_service = hmac(k_region, service_name) k_credentials = hmac(k_service, 'aws4_request') end
@param [Http::Request] req @option options [Boolean] :chunk_signing (false)
When true
, the
request body will be signed in chunk.
@option options [DateTime String<YYYYMMDDTHHMMSSZ>] :datetime @return [Http::Request]
# File lib/aws/core/signers/version_4.rb, line 56 def sign_request req, options = {} datetime = options[:datetime] || Time.now.utc.strftime("%Y%m%dT%H%M%SZ") key = derive_key(datetime) token = credentials.session_token chunk_signing = !!options[:chunk_signing] content_sha256 = req.headers['x-amz-content-sha256'] || body_digest(req, chunk_signing) req.headers['host'] = req.host req.headers['x-amz-date'] = datetime req.headers['x-amz-security-token'] = token if token req.headers['x-amz-content-sha256'] = content_sha256 if chunk_signing orig_size = req.headers['content-length'].to_i signed_size = ChunkSignedStream.signed_size(orig_size.to_i) req.headers['content-length'] = signed_size.to_s req.headers['x-amz-decoded-content-length'] = orig_size.to_s end req.headers['authorization'] = authorization(req, key, datetime, content_sha256) req.body_stream = chunk_signed_stream(req, key) if chunk_signing req end
# File lib/aws/core/signers/version_4.rb, line 82 def signature(request, key, datetime, content_sha256) string = string_to_sign(request, datetime, content_sha256) hexhmac(key, string) end
Private Instance Methods
@param [Http::Request] req @param [Boolean] chunk_signing @return [String]
# File lib/aws/core/signers/version_4.rb, line 184 def body_digest req, chunk_signing case when chunk_signing then STREAMING_CHECKSUM when ['', nil].include?(req.body) then EMPTY_DIGEST else hexdigest(req.body) end end
@param [String,Array<String>] values
# File lib/aws/core/signers/version_4.rb, line 176 def canonical_header_values values values = [values] unless values.is_a?(Array) values.map(&:to_s).join(',').gsub(/\s+/, ' ').strip end
@param [Http::Request] req
# File lib/aws/core/signers/version_4.rb, line 166 def canonical_headers req headers = [] req.headers.each_pair do |k,v| headers << [k,v] unless k == 'authorization' end headers = headers.sort_by(&:first) headers.map{|k,v| "#{k}:#{canonical_header_values(v)}" }.join("\n") end
@param [Http::Request] req
# File lib/aws/core/signers/version_4.rb, line 147 def canonical_request req, content_sha256 parts = [] parts << req.http_method parts << req.path parts << req.querystring parts << canonical_headers(req) + "\n" parts << signed_headers(req) parts << content_sha256 parts.join("\n") end
Wraps the req body stream with another stream. The wrapper signs the original body as it is read, injecting signatures of indiviaul chunks into the resultant stream. @param [Http::Request] req @param [String] key @param [String] datetime
# File lib/aws/core/signers/version_4.rb, line 107 def chunk_signed_stream req, key args = [] args << req.body_stream args << req.headers['x-amz-decoded-content-length'].to_i args << key args << key_path(req.headers['x-amz-date']) args << req.headers['x-amz-date'] args << req.headers['authorization'].split('Signature=')[1] ChunkSignedStream.new(*args) end
@param [String] value @return [String]
# File lib/aws/core/signers/version_4.rb, line 194 def hexdigest value digest = OpenSSL::Digest::SHA256.new if value.respond_to?(:read) chunk = nil chunk_size = 1024 * 1024 # 1 megabyte digest.update(chunk) while chunk = value.read(chunk_size) value.rewind else digest.update(value) end digest.hexdigest end
@param [String] key @param [String] value @return [String]
# File lib/aws/core/signers/version_4.rb, line 217 def hexhmac key, value OpenSSL::HMAC.hexdigest(sha256_digest, key, value) end
@param [String] key @param [String] value @return [String]
# File lib/aws/core/signers/version_4.rb, line 210 def hmac key, value OpenSSL::HMAC.digest(sha256_digest, key, value) end
@param [String] datetime @return [String] the signature scope.
# File lib/aws/core/signers/version_4.rb, line 137 def key_path datetime parts = [] parts << datetime[0,8] parts << region parts << service_name parts << 'aws4_request' parts.join("/") end
# File lib/aws/core/signers/version_4.rb, line 221 def sha256_digest OpenSSL::Digest.new('sha256') end
@param [Http::Request] req
# File lib/aws/core/signers/version_4.rb, line 159 def signed_headers req to_sign = req.headers.keys.map{|k| k.to_s.downcase } to_sign.delete('authorization') to_sign.sort.join(";") end
# File lib/aws/core/signers/version_4.rb, line 126 def string_to_sign req, datetime, content_sha256 parts = [] parts << 'AWS4-HMAC-SHA256' parts << datetime parts << key_path(datetime) parts << hexdigest(canonical_request(req, content_sha256)) parts.join("\n") end