Class: Aws4Signer::Signature

Inherits:
Object
  • Object
show all
Defined in:
lib/aws4_signer/signature.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(access_key_id, secret_access_key, region, service, verb, uri, headers, body, security_token: nil) ⇒ Signature

Returns a new instance of Signature.



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/aws4_signer/signature.rb', line 3

def initialize(access_key_id, secret_access_key, region, service, verb, uri, headers, body, security_token: nil)
  @access_key_id = access_key_id
  @secret_access_key = secret_access_key
  @region = region
  @service = service
  @verb = verb
  @uri = uri
  @headers = headers.dup
  @body = body

  @headers.each do |name, value|
    if value.kind_of?(Array)
      @headers[name] = value.first
    end
  end

  @headers["x-amz-date"] ||= @headers.delete("X-Amz-Date")
  @headers["x-amz-security-token"] = security_token if security_token
  unless @headers["x-amz-date"]
    @date = Time.now.utc
    @headers["x-amz-date"] = @date.strftime("%Y%m%dT%H%M%SZ")
  end
  @headers["Host"] ||= @headers.delete("host") || uri.host
end

Instance Attribute Details

#access_key_idObject (readonly)

Returns the value of attribute access_key_id.



28
29
30
# File 'lib/aws4_signer/signature.rb', line 28

def access_key_id
  @access_key_id
end

#bodyObject (readonly)

Returns the value of attribute body.



28
29
30
# File 'lib/aws4_signer/signature.rb', line 28

def body
  @body
end

#headersObject (readonly)

Returns the value of attribute headers.



28
29
30
# File 'lib/aws4_signer/signature.rb', line 28

def headers
  @headers
end

#regionObject (readonly)

Returns the value of attribute region.



28
29
30
# File 'lib/aws4_signer/signature.rb', line 28

def region
  @region
end

#secret_access_keyObject (readonly)

Returns the value of attribute secret_access_key.



28
29
30
# File 'lib/aws4_signer/signature.rb', line 28

def secret_access_key
  @secret_access_key
end

#serviceObject (readonly)

Returns the value of attribute service.



28
29
30
# File 'lib/aws4_signer/signature.rb', line 28

def service
  @service
end

#uriObject (readonly)

Returns the value of attribute uri.



28
29
30
# File 'lib/aws4_signer/signature.rb', line 28

def uri
  @uri
end

#verbObject (readonly)

Returns the value of attribute verb.



28
29
30
# File 'lib/aws4_signer/signature.rb', line 28

def verb
  @verb
end

Instance Method Details

#attach_to_http_request(req) ⇒ Object



30
31
32
33
34
35
36
37
38
39
# File 'lib/aws4_signer/signature.rb', line 30

def attach_to_http_request(req)
  headers.each do |name, value|
    req[name.downcase] = value
  end

  req["x-amz-content-sha256"] = Digest::SHA2.hexdigest(req.body || '', 256)
  req["authorization"] = authorization_header

  req
end

#authorization_headerObject



41
42
43
44
45
46
# File 'lib/aws4_signer/signature.rb', line 41

def authorization_header
  "AWS4-HMAC-SHA256 " \
    "Credential=#{@access_key_id}/#{scope}," \
    "SignedHeaders=#{signed_headers}," \
    "Signature=#{signature}"
end

#canonical_headersObject



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/aws4_signer/signature.rb', line 56

def canonical_headers
  @canonical_headers ||= begin
    signed = []
    hs = headers.sort_by { |name, _| name.downcase }.flat_map { |name, value|
      next if name == "Authorization"

      signed << name.downcase
      case value
      when Array
        value.each do |v|
          "#{name.downcase}:#{v.to_s.strip}\n"
        end
      else
        "#{name.downcase}:#{value.to_s.strip}\n"
      end
    }.compact.join.freeze

    @signed_headers = signed.join(";").freeze
    hs
  end
end

#canonical_requestObject



86
87
88
89
90
91
92
93
94
95
96
# File 'lib/aws4_signer/signature.rb', line 86

def canonical_request
  @canonical_request ||= [
    @verb.upcase,
    @uri.path,
    @uri.query&.split('&').map { |x| x.split('=') }.sort_by(&:first)
                          .map { |x| x.join('=') }.join('&'),
    canonical_headers,
    signed_headers,
    hashed_payload,
  ].join("\n")
end

#dateObject



48
49
50
51
52
53
54
# File 'lib/aws4_signer/signature.rb', line 48

def date
  @date ||= begin
    time = Time.strptime(headers["x-amz-date"],"%Y%m%dT%H%M%SZ")
    time += time.utc_offset
    time.utc
  end
end

#date_keyObject



111
112
113
# File 'lib/aws4_signer/signature.rb', line 111

def date_key
  @date_key ||= hmac("AWS4#{@secret_access_key}", date.strftime("%Y%m%d"))
end

#date_region_keyObject



115
116
117
# File 'lib/aws4_signer/signature.rb', line 115

def date_region_key
  @date_region_key ||= hmac(date_key, region)
end

#date_region_service_keyObject



119
120
121
# File 'lib/aws4_signer/signature.rb', line 119

def date_region_service_key
  @date_region_service_key ||= hmac(date_region_key, service)
end

#hashed_payloadObject



82
83
84
# File 'lib/aws4_signer/signature.rb', line 82

def hashed_payload
  @hashed_payload ||= Digest::SHA2.hexdigest(body, 256)
end

#scopeObject



98
99
100
# File 'lib/aws4_signer/signature.rb', line 98

def scope
  "#{date.strftime("%Y%m%d")}/#{@region}/#{service}/aws4_request"
end

#signatureObject



127
128
129
# File 'lib/aws4_signer/signature.rb', line 127

def signature
  @signature ||= hmac(signing_key, string_to_sign, :hex)
end

#signed_headersObject



78
79
80
# File 'lib/aws4_signer/signature.rb', line 78

def signed_headers
  canonical_headers; @signed_headers
end

#signing_keyObject



123
124
125
# File 'lib/aws4_signer/signature.rb', line 123

def signing_key
  @signing_key ||= hmac(date_region_service_key, 'aws4_request')
end

#string_to_signObject



102
103
104
105
106
107
108
109
# File 'lib/aws4_signer/signature.rb', line 102

def string_to_sign
  @string_to_sign ||= [
    "AWS4-HMAC-SHA256",
    headers["x-amz-date"],
    scope,
    Digest::SHA2.hexdigest(canonical_request, 256),
  ].join("\n")
end