aws-sigv4 1.4.1.crt → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -2
- data/VERSION +1 -1
- data/lib/aws-sigv4/credentials.rb +4 -6
- data/lib/aws-sigv4/request.rb +65 -0
- data/lib/aws-sigv4/signature.rb +1 -1
- data/lib/aws-sigv4/signer.rb +522 -205
- data/lib/aws-sigv4.rb +0 -1
- metadata +18 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 709cebb0799ad1e75e1f1e69413798d4da1d523332abc0ac59899e9686aa2e07
|
4
|
+
data.tar.gz: a4751a6a7c2356bf76b2ac550f8320e3f8b24951415eaddf1328271c1835f2d3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07ebb364959ed8bf62c39192707a30caef1127fcf0ab078dab30ecaf695b2a9e22caa017a773987dac297615dfc824eeb8cc04a60a604837024ee850f6812dd2
|
7
|
+
data.tar.gz: f9e93932e59b1d43b92aa2c74d33d7553490ecf84ff61b491321c0b031896f30b5375c8a5f2c81d8514691917096e7c3dd5cc298c3a3fa5e6876cabb1f458c8e
|
data/CHANGELOG.md
CHANGED
@@ -1,11 +1,20 @@
|
|
1
1
|
Unreleased Changes
|
2
2
|
------------------
|
3
3
|
|
4
|
+
1.5.0 (2022-04-20)
|
5
|
+
------------------
|
6
|
+
|
7
|
+
* Feature - Use CRT based signers if `aws-crt` is available - provides support for `sigv4a`.
|
8
|
+
|
9
|
+
1.4.0 (2021-09-02)
|
10
|
+
------------------
|
11
|
+
|
12
|
+
* Feature - add `signing_algorithm` option with `sigv4` default.
|
4
13
|
|
5
|
-
1.3.0
|
14
|
+
1.3.0 (2021-09-01)
|
6
15
|
------------------
|
7
16
|
|
8
|
-
* Feature -
|
17
|
+
* Feature - AWS SDK for Ruby no longer supports Ruby runtime versions 1.9, 2.0, 2.1, and 2.2.
|
9
18
|
|
10
19
|
1.2.4 (2021-07-08)
|
11
20
|
------------------
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.5.0
|
@@ -11,10 +11,7 @@ module Aws
|
|
11
11
|
# @option options [required, String] :secret_access_key
|
12
12
|
# @option options [String, nil] :session_token (nil)
|
13
13
|
def initialize(options = {})
|
14
|
-
if options[:access_key_id] && options[:secret_access_key]
|
15
|
-
!options[:access_key_id].empty? &&
|
16
|
-
!options[:secret_access_key].empty?
|
17
|
-
|
14
|
+
if options[:access_key_id] && options[:secret_access_key]
|
18
15
|
@access_key_id = options[:access_key_id]
|
19
16
|
@secret_access_key = options[:secret_access_key]
|
20
17
|
@session_token = options[:session_token]
|
@@ -54,8 +51,8 @@ module Aws
|
|
54
51
|
# @option options [String] :session_token (nil)
|
55
52
|
def initialize(options = {})
|
56
53
|
@credentials = options[:credentials] ?
|
57
|
-
|
58
|
-
|
54
|
+
options[:credentials] :
|
55
|
+
Credentials.new(options)
|
59
56
|
end
|
60
57
|
|
61
58
|
# @return [Credentials]
|
@@ -66,5 +63,6 @@ module Aws
|
|
66
63
|
!!credentials && credentials.set?
|
67
64
|
end
|
68
65
|
end
|
66
|
+
|
69
67
|
end
|
70
68
|
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module Aws
|
6
|
+
module Sigv4
|
7
|
+
class Request
|
8
|
+
|
9
|
+
# @option options [required, String] :http_method
|
10
|
+
# @option options [required, HTTP::URI, HTTPS::URI, String] :endpoint
|
11
|
+
# @option options [Hash<String,String>] :headers ({})
|
12
|
+
# @option options [String, IO] :body ('')
|
13
|
+
def initialize(options = {})
|
14
|
+
@http_method = nil
|
15
|
+
@endpoint = nil
|
16
|
+
@headers = {}
|
17
|
+
@body = ''
|
18
|
+
options.each_pair do |attr_name, attr_value|
|
19
|
+
send("#{attr_name}=", attr_value)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param [String] http_method One of 'GET', 'PUT', 'POST', 'DELETE', 'HEAD', or 'PATCH'
|
24
|
+
def http_method=(http_method)
|
25
|
+
@http_method = http_method
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [String] One of 'GET', 'PUT', 'POST', 'DELETE', 'HEAD', or 'PATCH'
|
29
|
+
def http_method
|
30
|
+
@http_method
|
31
|
+
end
|
32
|
+
|
33
|
+
# @param [String, HTTP::URI, HTTPS::URI] endpoint
|
34
|
+
def endpoint=(endpoint)
|
35
|
+
@endpoint = URI.parse(endpoint.to_s)
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [HTTP::URI, HTTPS::URI]
|
39
|
+
def endpoint
|
40
|
+
@endpoint
|
41
|
+
end
|
42
|
+
|
43
|
+
# @param [Hash] headers
|
44
|
+
def headers=(headers)
|
45
|
+
@headers = headers
|
46
|
+
end
|
47
|
+
|
48
|
+
# @return [Hash<String,String>]
|
49
|
+
def headers
|
50
|
+
@headers
|
51
|
+
end
|
52
|
+
|
53
|
+
# @param [String, IO] body
|
54
|
+
def body=(body)
|
55
|
+
@body = body
|
56
|
+
end
|
57
|
+
|
58
|
+
# @return [String, IO]
|
59
|
+
def body
|
60
|
+
@body
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/aws-sigv4/signature.rb
CHANGED
data/lib/aws-sigv4/signer.rb
CHANGED
@@ -1,16 +1,79 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'openssl'
|
4
|
-
require 'time'
|
5
4
|
require 'tempfile'
|
5
|
+
require 'time'
|
6
6
|
require 'uri'
|
7
7
|
require 'set'
|
8
|
+
require 'cgi'
|
8
9
|
require 'aws-eventstream'
|
9
10
|
|
10
11
|
module Aws
|
11
12
|
module Sigv4
|
12
|
-
|
13
|
+
|
14
|
+
# Utility class for creating AWS signature version 4 signature. This class
|
15
|
+
# provides two methods for generating signatures:
|
16
|
+
#
|
17
|
+
# * {#sign_request} - Computes a signature of the given request, returning
|
18
|
+
# the hash of headers that should be applied to the request.
|
19
|
+
#
|
20
|
+
# * {#presign_url} - Computes a presigned request with an expiration.
|
21
|
+
# By default, the body of this request is not signed and the request
|
22
|
+
# expires in 15 minutes.
|
23
|
+
#
|
24
|
+
# ## Configuration
|
25
|
+
#
|
26
|
+
# To use the signer, you need to specify the service, region, and credentials.
|
27
|
+
# The service name is normally the endpoint prefix to an AWS service. For
|
28
|
+
# example:
|
29
|
+
#
|
30
|
+
# ec2.us-west-1.amazonaws.com => ec2
|
31
|
+
#
|
32
|
+
# The region is normally the second portion of the endpoint, following
|
33
|
+
# the service name.
|
34
|
+
#
|
35
|
+
# ec2.us-west-1.amazonaws.com => us-west-1
|
36
|
+
#
|
37
|
+
# It is important to have the correct service and region name, or the
|
38
|
+
# signature will be invalid.
|
39
|
+
#
|
40
|
+
# ## Credentials
|
41
|
+
#
|
42
|
+
# The signer requires credentials. You can configure the signer
|
43
|
+
# with static credentials:
|
44
|
+
#
|
45
|
+
# signer = Aws::Sigv4::Signer.new(
|
46
|
+
# service: 's3',
|
47
|
+
# region: 'us-east-1',
|
48
|
+
# # static credentials
|
49
|
+
# access_key_id: 'akid',
|
50
|
+
# secret_access_key: 'secret'
|
51
|
+
# )
|
52
|
+
#
|
53
|
+
# You can also provide refreshing credentials via the `:credentials_provider`.
|
54
|
+
# If you are using the AWS SDK for Ruby, you can use any of the credential
|
55
|
+
# classes:
|
56
|
+
#
|
57
|
+
# signer = Aws::Sigv4::Signer.new(
|
58
|
+
# service: 's3',
|
59
|
+
# region: 'us-east-1',
|
60
|
+
# credentials_provider: Aws::InstanceProfileCredentials.new
|
61
|
+
# )
|
62
|
+
#
|
63
|
+
# Other AWS SDK for Ruby classes that can be provided via `:credentials_provider`:
|
64
|
+
#
|
65
|
+
# * `Aws::Credentials`
|
66
|
+
# * `Aws::SharedCredentials`
|
67
|
+
# * `Aws::InstanceProfileCredentials`
|
68
|
+
# * `Aws::AssumeRoleCredentials`
|
69
|
+
# * `Aws::ECSCredentials`
|
70
|
+
#
|
71
|
+
# A credential provider is any object that responds to `#credentials`
|
72
|
+
# returning another object that responds to `#access_key_id`, `#secret_access_key`,
|
73
|
+
# and `#session_token`.
|
74
|
+
#
|
13
75
|
class Signer
|
76
|
+
|
14
77
|
# @overload initialize(service:, region:, access_key_id:, secret_access_key:, session_token:nil, **options)
|
15
78
|
# @param [String] :service The service signing name, e.g. 's3'.
|
16
79
|
# @param [String] :region The region name, e.g. 'us-east-1'.
|
@@ -55,19 +118,23 @@ module Aws
|
|
55
118
|
# headers. This is required for AWS Glacier, and optional for
|
56
119
|
# every other AWS service as of late 2016.
|
57
120
|
#
|
58
|
-
# @option options [
|
121
|
+
# @option options [Symbol] :signing_algorithm (:sigv4) The
|
122
|
+
# algorithm to use for signing. :sigv4a is only supported when
|
123
|
+
# `aws-crt` is available.
|
124
|
+
#
|
125
|
+
# @option options [Boolean] :omit_session_token (false)
|
126
|
+
# (Supported only when `aws-crt` is available) If `true`,
|
59
127
|
# then security token is added to the final signing result,
|
60
128
|
# but is treated as "unsigned" and does not contribute
|
61
129
|
# to the authorization signature.
|
62
130
|
#
|
63
|
-
# @option options [Boolean] :normalize_path (true)
|
64
|
-
# the uri paths will be normalized when building the canonical request
|
131
|
+
# @option options [Boolean] :normalize_path (true) (Supported only when `aws-crt` is available)
|
132
|
+
# When `true`, the uri paths will be normalized when building the canonical request
|
65
133
|
def initialize(options = {})
|
66
134
|
@service = extract_service(options)
|
67
135
|
@region = extract_region(options)
|
68
136
|
@credentials_provider = extract_credentials_provider(options)
|
69
|
-
@unsigned_headers = Set.new((options.fetch(:unsigned_headers, []))
|
70
|
-
.map(&:downcase))
|
137
|
+
@unsigned_headers = Set.new((options.fetch(:unsigned_headers, [])).map(&:downcase))
|
71
138
|
@unsigned_headers << 'authorization'
|
72
139
|
@unsigned_headers << 'x-amzn-trace-id'
|
73
140
|
@unsigned_headers << 'expect'
|
@@ -76,6 +143,12 @@ module Aws
|
|
76
143
|
@signing_algorithm = options.fetch(:signing_algorithm, :sigv4)
|
77
144
|
@normalize_path = options.fetch(:normalize_path, true)
|
78
145
|
@omit_session_token = options.fetch(:omit_session_token, false)
|
146
|
+
|
147
|
+
if @signing_algorithm == :sigv4a && !Signer.use_crt?
|
148
|
+
raise ArgumentError, 'You are attempting to sign a' \
|
149
|
+
' request with sigv4a which requires the `aws-crt` gem.'\
|
150
|
+
' Please install the gem or add it to your gemfile.'
|
151
|
+
end
|
79
152
|
end
|
80
153
|
|
81
154
|
# @return [String]
|
@@ -151,65 +224,105 @@ module Aws
|
|
151
224
|
# a `#headers` method. The headers must be applied to your request.
|
152
225
|
#
|
153
226
|
def sign_request(request)
|
227
|
+
|
228
|
+
return crt_sign_request(request) if Signer.use_crt?
|
229
|
+
|
154
230
|
creds = fetch_credentials
|
155
231
|
|
156
232
|
http_method = extract_http_method(request)
|
157
233
|
url = extract_url(request)
|
158
234
|
headers = downcase_headers(request[:headers])
|
159
235
|
|
160
|
-
datetime =
|
161
|
-
|
162
|
-
|
163
|
-
end
|
236
|
+
datetime = headers['x-amz-date']
|
237
|
+
datetime ||= Time.now.utc.strftime("%Y%m%dT%H%M%SZ")
|
238
|
+
date = datetime[0,8]
|
164
239
|
|
165
|
-
content_sha256 = headers
|
240
|
+
content_sha256 = headers['x-amz-content-sha256']
|
166
241
|
content_sha256 ||= sha256_hexdigest(request[:body] || '')
|
167
242
|
|
168
243
|
sigv4_headers = {}
|
169
244
|
sigv4_headers['host'] = headers['host'] || host(url)
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
if headers.include? 'user-agent'
|
174
|
-
headers['user-agent'] = "#{headers['user-agent']} crt-signer/#{@signing_algorithm}/#{Aws::Sigv4::VERSION}"
|
175
|
-
sigv4_headers['user-agent'] = headers['user-agent']
|
176
|
-
end
|
245
|
+
sigv4_headers['x-amz-date'] = datetime
|
246
|
+
sigv4_headers['x-amz-security-token'] = creds.session_token if creds.session_token
|
247
|
+
sigv4_headers['x-amz-content-sha256'] ||= content_sha256 if @apply_checksum_header
|
177
248
|
|
178
249
|
headers = headers.merge(sigv4_headers) # merge so we do not modify given headers hash
|
179
250
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
service: @service,
|
185
|
-
date: datetime,
|
186
|
-
signed_body_value: content_sha256,
|
187
|
-
signed_body_header_type: @apply_checksum_header ?
|
188
|
-
:sbht_content_sha256 : :sbht_none,
|
189
|
-
credentials: creds,
|
190
|
-
unsigned_headers: @unsigned_headers,
|
191
|
-
use_double_uri_encode: @uri_escape_path,
|
192
|
-
should_normalize_uri_path: @normalize_path,
|
193
|
-
omit_session_token: @omit_session_token
|
194
|
-
)
|
195
|
-
http_request = Aws::Crt::Http::Message.new(
|
196
|
-
http_method, url.to_s, headers
|
197
|
-
)
|
198
|
-
signable = Aws::Crt::Auth::Signable.new(http_request)
|
251
|
+
# compute signature parts
|
252
|
+
creq = canonical_request(http_method, url, headers, content_sha256)
|
253
|
+
sts = string_to_sign(datetime, creq)
|
254
|
+
sig = signature(creds.secret_access_key, date, sts)
|
199
255
|
|
200
|
-
|
256
|
+
# apply signature
|
257
|
+
sigv4_headers['authorization'] = [
|
258
|
+
"AWS4-HMAC-SHA256 Credential=#{credential(creds, date)}",
|
259
|
+
"SignedHeaders=#{signed_headers(headers)}",
|
260
|
+
"Signature=#{sig}",
|
261
|
+
].join(', ')
|
201
262
|
|
263
|
+
# Returning the signature components.
|
202
264
|
Signature.new(
|
203
|
-
headers: sigv4_headers
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
canonical_request: 'CRT_INTERNAL',
|
208
|
-
content_sha256: content_sha256,
|
209
|
-
extra: {config: config, signable: signable}
|
265
|
+
headers: sigv4_headers,
|
266
|
+
string_to_sign: sts,
|
267
|
+
canonical_request: creq,
|
268
|
+
content_sha256: content_sha256
|
210
269
|
)
|
211
270
|
end
|
212
271
|
|
272
|
+
# Signs a event and returns signature headers and prior signature
|
273
|
+
# used for next event signing.
|
274
|
+
#
|
275
|
+
# Headers of a sigv4 signed event message only contains 2 headers
|
276
|
+
# * ':chunk-signature'
|
277
|
+
# * computed signature of the event, binary string, 'bytes' type
|
278
|
+
# * ':date'
|
279
|
+
# * millisecond since epoch, 'timestamp' type
|
280
|
+
#
|
281
|
+
# Payload of the sigv4 signed event message contains eventstream encoded message
|
282
|
+
# which is serialized based on input and protocol
|
283
|
+
#
|
284
|
+
# To sign events
|
285
|
+
#
|
286
|
+
# headers_0, signature_0 = signer.sign_event(
|
287
|
+
# prior_signature, # hex-encoded string
|
288
|
+
# payload_0, # binary string (eventstream encoded event 0)
|
289
|
+
# encoder, # Aws::EventStreamEncoder
|
290
|
+
# )
|
291
|
+
#
|
292
|
+
# headers_1, signature_1 = signer.sign_event(
|
293
|
+
# signature_0,
|
294
|
+
# payload_1, # binary string (eventstream encoded event 1)
|
295
|
+
# encoder
|
296
|
+
# )
|
297
|
+
#
|
298
|
+
# The initial prior_signature should be using the signature computed at initial request
|
299
|
+
#
|
300
|
+
# Note:
|
301
|
+
#
|
302
|
+
# Since ':chunk-signature' header value has bytes type, the signature value provided
|
303
|
+
# needs to be a binary string instead of a hex-encoded string (like original signature
|
304
|
+
# V4 algorithm). Thus, when returning signature value used for next event siging, the
|
305
|
+
# signature value (a binary string) used at ':chunk-signature' needs to converted to
|
306
|
+
# hex-encoded string using #unpack
|
307
|
+
def sign_event(prior_signature, payload, encoder)
|
308
|
+
# Note: CRT does not currently provide event stream signing, so we always use the ruby implementation.
|
309
|
+
creds = fetch_credentials
|
310
|
+
time = Time.now
|
311
|
+
headers = {}
|
312
|
+
|
313
|
+
datetime = time.utc.strftime("%Y%m%dT%H%M%SZ")
|
314
|
+
date = datetime[0,8]
|
315
|
+
headers[':date'] = Aws::EventStream::HeaderValue.new(value: time.to_i * 1000, type: 'timestamp')
|
316
|
+
|
317
|
+
sts = event_string_to_sign(datetime, headers, payload, prior_signature, encoder)
|
318
|
+
sig = event_signature(creds.secret_access_key, date, sts)
|
319
|
+
|
320
|
+
headers[':chunk-signature'] = Aws::EventStream::HeaderValue.new(value: sig, type: 'bytes')
|
321
|
+
|
322
|
+
# Returning signed headers and signature value in hex-encoded string
|
323
|
+
[headers, sig.unpack('H*').first]
|
324
|
+
end
|
325
|
+
|
213
326
|
# Signs a URL with query authentication. Using query parameters
|
214
327
|
# to authenticate requests is useful when you want to express a
|
215
328
|
# request entirely in a URL. This method is also referred as
|
@@ -279,120 +392,249 @@ module Aws
|
|
279
392
|
# @return [HTTPS::URI, HTTP::URI]
|
280
393
|
#
|
281
394
|
def presign_url(options)
|
395
|
+
|
396
|
+
return crt_presign_url(options) if Signer.use_crt?
|
397
|
+
|
282
398
|
creds = fetch_credentials
|
283
399
|
|
284
400
|
http_method = extract_http_method(options)
|
285
401
|
url = extract_url(options)
|
402
|
+
|
286
403
|
headers = downcase_headers(options[:headers])
|
287
404
|
headers['host'] ||= host(url)
|
288
405
|
|
289
|
-
datetime = headers
|
290
|
-
datetime ||= (options[:time] || Time.now)
|
406
|
+
datetime = headers['x-amz-date']
|
407
|
+
datetime ||= (options[:time] || Time.now).utc.strftime("%Y%m%dT%H%M%SZ")
|
408
|
+
date = datetime[0,8]
|
291
409
|
|
292
|
-
content_sha256 = headers
|
410
|
+
content_sha256 = headers['x-amz-content-sha256']
|
293
411
|
content_sha256 ||= options[:body_digest]
|
294
412
|
content_sha256 ||= sha256_hexdigest(options[:body] || '')
|
295
413
|
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
signed_body_header_type: @apply_checksum_header ?
|
304
|
-
:sbht_content_sha256 : :sbht_none,
|
305
|
-
credentials: creds,
|
306
|
-
unsigned_headers: @unsigned_headers,
|
307
|
-
use_double_uri_encode: @uri_escape_path,
|
308
|
-
should_normalize_uri_path: @normalize_path,
|
309
|
-
omit_session_token: @omit_session_token,
|
310
|
-
expiration_in_seconds: options.fetch(:expires_in, 900)
|
311
|
-
)
|
312
|
-
http_request = Aws::Crt::Http::Message.new(
|
313
|
-
http_method, url.to_s, headers
|
314
|
-
)
|
315
|
-
signable = Aws::Crt::Auth::Signable.new(http_request)
|
414
|
+
params = {}
|
415
|
+
params['X-Amz-Algorithm'] = 'AWS4-HMAC-SHA256'
|
416
|
+
params['X-Amz-Credential'] = credential(creds, date)
|
417
|
+
params['X-Amz-Date'] = datetime
|
418
|
+
params['X-Amz-Expires'] = extract_expires_in(options)
|
419
|
+
params['X-Amz-Security-Token'] = creds.session_token if creds.session_token
|
420
|
+
params['X-Amz-SignedHeaders'] = signed_headers(headers)
|
316
421
|
|
317
|
-
|
318
|
-
|
422
|
+
params = params.map do |key, value|
|
423
|
+
"#{uri_escape(key)}=#{uri_escape(value)}"
|
424
|
+
end.join('&')
|
319
425
|
|
320
|
-
if
|
321
|
-
|
322
|
-
|
426
|
+
if url.query
|
427
|
+
url.query += '&' + params
|
428
|
+
else
|
429
|
+
url.query = params
|
323
430
|
end
|
431
|
+
|
432
|
+
creq = canonical_request(http_method, url, headers, content_sha256)
|
433
|
+
sts = string_to_sign(datetime, creq)
|
434
|
+
url.query += '&X-Amz-Signature=' + signature(creds.secret_access_key, date, sts)
|
324
435
|
url
|
325
436
|
end
|
326
437
|
|
438
|
+
private
|
327
439
|
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
#
|
349
|
-
#
|
350
|
-
#
|
351
|
-
#
|
352
|
-
# )
|
353
|
-
#
|
354
|
-
# The initial prior_signature should be using the signature computed at initial request
|
440
|
+
def canonical_request(http_method, url, headers, content_sha256)
|
441
|
+
[
|
442
|
+
http_method,
|
443
|
+
path(url),
|
444
|
+
normalized_querystring(url.query || ''),
|
445
|
+
canonical_headers(headers) + "\n",
|
446
|
+
signed_headers(headers),
|
447
|
+
content_sha256,
|
448
|
+
].join("\n")
|
449
|
+
end
|
450
|
+
|
451
|
+
def string_to_sign(datetime, canonical_request)
|
452
|
+
[
|
453
|
+
'AWS4-HMAC-SHA256',
|
454
|
+
datetime,
|
455
|
+
credential_scope(datetime[0,8]),
|
456
|
+
sha256_hexdigest(canonical_request),
|
457
|
+
].join("\n")
|
458
|
+
end
|
459
|
+
|
460
|
+
# Compared to original #string_to_sign at signature v4 algorithm
|
461
|
+
# there is no canonical_request concept for an eventstream event,
|
462
|
+
# instead, an event contains headers and payload two parts, and
|
463
|
+
# they will be used for computing digest in #event_string_to_sign
|
355
464
|
#
|
356
465
|
# Note:
|
466
|
+
# While headers need to be encoded under eventstream format,
|
467
|
+
# payload used is already eventstream encoded (event without signature),
|
468
|
+
# thus no extra encoding is needed.
|
469
|
+
def event_string_to_sign(datetime, headers, payload, prior_signature, encoder)
|
470
|
+
encoded_headers = encoder.encode_headers(
|
471
|
+
Aws::EventStream::Message.new(headers: headers, payload: payload)
|
472
|
+
)
|
473
|
+
[
|
474
|
+
"AWS4-HMAC-SHA256-PAYLOAD",
|
475
|
+
datetime,
|
476
|
+
credential_scope(datetime[0,8]),
|
477
|
+
prior_signature,
|
478
|
+
sha256_hexdigest(encoded_headers),
|
479
|
+
sha256_hexdigest(payload)
|
480
|
+
].join("\n")
|
481
|
+
end
|
482
|
+
|
483
|
+
def credential_scope(date)
|
484
|
+
[
|
485
|
+
date,
|
486
|
+
@region,
|
487
|
+
@service,
|
488
|
+
'aws4_request',
|
489
|
+
].join('/')
|
490
|
+
end
|
491
|
+
|
492
|
+
def credential(credentials, date)
|
493
|
+
"#{credentials.access_key_id}/#{credential_scope(date)}"
|
494
|
+
end
|
495
|
+
|
496
|
+
def signature(secret_access_key, date, string_to_sign)
|
497
|
+
k_date = hmac("AWS4" + secret_access_key, date)
|
498
|
+
k_region = hmac(k_date, @region)
|
499
|
+
k_service = hmac(k_region, @service)
|
500
|
+
k_credentials = hmac(k_service, 'aws4_request')
|
501
|
+
hexhmac(k_credentials, string_to_sign)
|
502
|
+
end
|
503
|
+
|
504
|
+
# Comparing to original signature v4 algorithm,
|
505
|
+
# returned signature is a binary string instread of
|
506
|
+
# hex-encoded string. (Since ':chunk-signature' requires
|
507
|
+
# 'bytes' type)
|
357
508
|
#
|
358
|
-
#
|
359
|
-
#
|
360
|
-
#
|
361
|
-
#
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
509
|
+
# Note:
|
510
|
+
# converting signature from binary string to hex-encoded
|
511
|
+
# string is handled at #sign_event instead. (Will be used
|
512
|
+
# as next prior signature for event signing)
|
513
|
+
def event_signature(secret_access_key, date, string_to_sign)
|
514
|
+
k_date = hmac("AWS4" + secret_access_key, date)
|
515
|
+
k_region = hmac(k_date, @region)
|
516
|
+
k_service = hmac(k_region, @service)
|
517
|
+
k_credentials = hmac(k_service, 'aws4_request')
|
518
|
+
hmac(k_credentials, string_to_sign)
|
519
|
+
end
|
369
520
|
|
370
|
-
datetime = time.utc.strftime("%Y%m%dT%H%M%SZ")
|
371
|
-
date = datetime[0,8]
|
372
|
-
headers[':date'] = Aws::EventStream::HeaderValue.new(value: time.to_i * 1000, type: 'timestamp')
|
373
521
|
|
374
|
-
|
375
|
-
|
522
|
+
def path(url)
|
523
|
+
path = url.path
|
524
|
+
path = '/' if path == ''
|
525
|
+
if @uri_escape_path
|
526
|
+
uri_escape_path(path)
|
527
|
+
else
|
528
|
+
path
|
529
|
+
end
|
530
|
+
end
|
376
531
|
|
377
|
-
|
532
|
+
def normalized_querystring(querystring)
|
533
|
+
params = querystring.split('&')
|
534
|
+
params = params.map { |p| p.match(/=/) ? p : p + '=' }
|
535
|
+
# From: https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
|
536
|
+
# Sort the parameter names by character code point in ascending order.
|
537
|
+
# Parameters with duplicate names should be sorted by value.
|
538
|
+
#
|
539
|
+
# Default sort <=> in JRuby will swap members
|
540
|
+
# occasionally when <=> is 0 (considered still sorted), but this
|
541
|
+
# causes our normalized query string to not match the sent querystring.
|
542
|
+
# When names match, we then sort by their values. When values also
|
543
|
+
# match then we sort by their original order
|
544
|
+
params.each.with_index.sort do |a, b|
|
545
|
+
a, a_offset = a
|
546
|
+
b, b_offset = b
|
547
|
+
a_name, a_value = a.split('=')
|
548
|
+
b_name, b_value = b.split('=')
|
549
|
+
if a_name == b_name
|
550
|
+
if a_value == b_value
|
551
|
+
a_offset <=> b_offset
|
552
|
+
else
|
553
|
+
a_value <=> b_value
|
554
|
+
end
|
555
|
+
else
|
556
|
+
a_name <=> b_name
|
557
|
+
end
|
558
|
+
end.map(&:first).join('&')
|
559
|
+
end
|
378
560
|
|
379
|
-
|
380
|
-
[
|
561
|
+
def signed_headers(headers)
|
562
|
+
headers.inject([]) do |signed_headers, (header, _)|
|
563
|
+
if @unsigned_headers.include?(header)
|
564
|
+
signed_headers
|
565
|
+
else
|
566
|
+
signed_headers << header
|
567
|
+
end
|
568
|
+
end.sort.join(';')
|
381
569
|
end
|
382
570
|
|
383
|
-
|
571
|
+
def canonical_headers(headers)
|
572
|
+
headers = headers.inject([]) do |hdrs, (k,v)|
|
573
|
+
if @unsigned_headers.include?(k)
|
574
|
+
hdrs
|
575
|
+
else
|
576
|
+
hdrs << [k,v]
|
577
|
+
end
|
578
|
+
end
|
579
|
+
headers = headers.sort_by(&:first)
|
580
|
+
headers.map{|k,v| "#{k}:#{canonical_header_value(v.to_s)}" }.join("\n")
|
581
|
+
end
|
582
|
+
|
583
|
+
def canonical_header_value(value)
|
584
|
+
value.match(/^".*"$/) ? value : value.gsub(/\s+/, ' ').strip
|
585
|
+
end
|
586
|
+
|
587
|
+
def host(uri)
|
588
|
+
# Handles known and unknown URI schemes; default_port nil when unknown.
|
589
|
+
if uri.default_port == uri.port
|
590
|
+
uri.host
|
591
|
+
else
|
592
|
+
"#{uri.host}:#{uri.port}"
|
593
|
+
end
|
594
|
+
end
|
595
|
+
|
596
|
+
# @param [File, Tempfile, IO#read, String] value
|
597
|
+
# @return [String<SHA256 Hexdigest>]
|
598
|
+
def sha256_hexdigest(value)
|
599
|
+
if (File === value || Tempfile === value) && !value.path.nil? && File.exist?(value.path)
|
600
|
+
OpenSSL::Digest::SHA256.file(value).hexdigest
|
601
|
+
elsif value.respond_to?(:read)
|
602
|
+
sha256 = OpenSSL::Digest::SHA256.new
|
603
|
+
loop do
|
604
|
+
chunk = value.read(1024 * 1024) # 1MB
|
605
|
+
break unless chunk
|
606
|
+
sha256.update(chunk)
|
607
|
+
end
|
608
|
+
value.rewind
|
609
|
+
sha256.hexdigest
|
610
|
+
else
|
611
|
+
OpenSSL::Digest::SHA256.hexdigest(value)
|
612
|
+
end
|
613
|
+
end
|
614
|
+
|
615
|
+
def hmac(key, value)
|
616
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), key, value)
|
617
|
+
end
|
618
|
+
|
619
|
+
def hexhmac(key, value)
|
620
|
+
OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), key, value)
|
621
|
+
end
|
384
622
|
|
385
623
|
def extract_service(options)
|
386
624
|
if options[:service]
|
387
625
|
options[:service]
|
388
626
|
else
|
389
|
-
msg =
|
627
|
+
msg = "missing required option :service"
|
390
628
|
raise ArgumentError, msg
|
391
629
|
end
|
392
630
|
end
|
393
631
|
|
394
632
|
def extract_region(options)
|
395
|
-
options[:region]
|
633
|
+
if options[:region]
|
634
|
+
options[:region]
|
635
|
+
else
|
636
|
+
raise Errors::MissingRegionError
|
637
|
+
end
|
396
638
|
end
|
397
639
|
|
398
640
|
def extract_credentials_provider(options)
|
@@ -405,22 +647,11 @@ module Aws
|
|
405
647
|
end
|
406
648
|
end
|
407
649
|
|
408
|
-
# the credentials used by CRT must be a
|
409
|
-
# CRT StaticCredentialsProvider object
|
410
|
-
def fetch_credentials
|
411
|
-
credentials = @credentials_provider.credentials
|
412
|
-
Aws::Crt::Auth::StaticCredentialsProvider.new(
|
413
|
-
credentials.access_key_id,
|
414
|
-
credentials.secret_access_key,
|
415
|
-
credentials.session_token
|
416
|
-
)
|
417
|
-
end
|
418
|
-
|
419
650
|
def extract_http_method(request)
|
420
651
|
if request[:http_method]
|
421
652
|
request[:http_method].upcase
|
422
653
|
else
|
423
|
-
msg =
|
654
|
+
msg = "missing required option :http_method"
|
424
655
|
raise ArgumentError, msg
|
425
656
|
end
|
426
657
|
end
|
@@ -429,101 +660,187 @@ module Aws
|
|
429
660
|
if request[:url]
|
430
661
|
URI.parse(request[:url].to_s)
|
431
662
|
else
|
432
|
-
msg =
|
663
|
+
msg = "missing required option :url"
|
433
664
|
raise ArgumentError, msg
|
434
665
|
end
|
435
666
|
end
|
436
667
|
|
437
668
|
def downcase_headers(headers)
|
438
|
-
(headers || {}).to_hash.
|
669
|
+
(headers || {}).to_hash.inject({}) do |hash, (key, value)|
|
670
|
+
hash[key.downcase] = value
|
671
|
+
hash
|
672
|
+
end
|
439
673
|
end
|
440
674
|
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
OpenSSL::Digest::SHA256.file(value).hexdigest
|
446
|
-
elsif value.respond_to?(:read)
|
447
|
-
sha256 = OpenSSL::Digest.new('SHA256')
|
448
|
-
loop do
|
449
|
-
chunk = value.read(1024 * 1024) # 1MB
|
450
|
-
break unless chunk
|
451
|
-
|
452
|
-
sha256.update(chunk)
|
453
|
-
end
|
454
|
-
value.rewind
|
455
|
-
sha256.hexdigest
|
675
|
+
def extract_expires_in(options)
|
676
|
+
case options[:expires_in]
|
677
|
+
when nil then 900.to_s
|
678
|
+
when Integer then options[:expires_in].to_s
|
456
679
|
else
|
457
|
-
|
680
|
+
msg = "expected :expires_in to be a number of seconds"
|
681
|
+
raise ArgumentError, msg
|
458
682
|
end
|
459
683
|
end
|
460
684
|
|
461
|
-
def
|
462
|
-
|
463
|
-
|
464
|
-
|
685
|
+
def uri_escape(string)
|
686
|
+
self.class.uri_escape(string)
|
687
|
+
end
|
688
|
+
|
689
|
+
def uri_escape_path(string)
|
690
|
+
self.class.uri_escape_path(string)
|
691
|
+
end
|
692
|
+
|
693
|
+
|
694
|
+
def fetch_credentials
|
695
|
+
credentials = @credentials_provider.credentials
|
696
|
+
if credentials_set?(credentials)
|
697
|
+
credentials
|
465
698
|
else
|
466
|
-
|
699
|
+
raise Errors::MissingCredentialsError,
|
700
|
+
'unable to sign request without credentials set'
|
467
701
|
end
|
468
702
|
end
|
469
703
|
|
470
|
-
#
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
704
|
+
# Returns true if credentials are set (not nil or empty)
|
705
|
+
# Credentials may not implement the Credentials interface
|
706
|
+
# and may just be credential like Client response objects
|
707
|
+
# (eg those returned by sts#assume_role)
|
708
|
+
def credentials_set?(credentials)
|
709
|
+
!credentials.access_key_id.nil? &&
|
710
|
+
!credentials.access_key_id.empty? &&
|
711
|
+
!credentials.secret_access_key.nil? &&
|
712
|
+
!credentials.secret_access_key.empty?
|
478
713
|
end
|
479
714
|
|
480
|
-
|
481
|
-
|
482
|
-
|
715
|
+
### CRT Code
|
716
|
+
|
717
|
+
# the credentials used by CRT must be a
|
718
|
+
# CRT StaticCredentialsProvider object
|
719
|
+
def crt_fetch_credentials
|
720
|
+
creds = fetch_credentials
|
721
|
+
Aws::Crt::Auth::StaticCredentialsProvider.new(
|
722
|
+
creds.access_key_id,
|
723
|
+
creds.secret_access_key,
|
724
|
+
creds.session_token
|
725
|
+
)
|
483
726
|
end
|
484
727
|
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
728
|
+
def crt_sign_request(request)
|
729
|
+
creds = crt_fetch_credentials
|
730
|
+
http_method = extract_http_method(request)
|
731
|
+
url = extract_url(request)
|
732
|
+
headers = downcase_headers(request[:headers])
|
733
|
+
|
734
|
+
datetime =
|
735
|
+
if headers.include? 'x-amz-date'
|
736
|
+
Time.parse(headers.delete('x-amz-date'))
|
737
|
+
end
|
738
|
+
|
739
|
+
content_sha256 = headers.delete('x-amz-content-sha256')
|
740
|
+
content_sha256 ||= sha256_hexdigest(request[:body] || '')
|
741
|
+
|
742
|
+
sigv4_headers = {}
|
743
|
+
sigv4_headers['host'] = headers['host'] || host(url)
|
744
|
+
|
745
|
+
# Modify the user-agent to add usage of crt-signer
|
746
|
+
# This should be temporary during developer preview only
|
747
|
+
if headers.include? 'user-agent'
|
748
|
+
headers['user-agent'] = "#{headers['user-agent']} crt-signer/#{@signing_algorithm}/#{Aws::Sigv4::VERSION}"
|
749
|
+
sigv4_headers['user-agent'] = headers['user-agent']
|
750
|
+
end
|
751
|
+
|
752
|
+
headers = headers.merge(sigv4_headers) # merge so we do not modify given headers hash
|
753
|
+
|
754
|
+
config = Aws::Crt::Auth::SigningConfig.new(
|
755
|
+
algorithm: @signing_algorithm,
|
756
|
+
signature_type: :http_request_headers,
|
757
|
+
region: @region,
|
758
|
+
service: @service,
|
759
|
+
date: datetime,
|
760
|
+
signed_body_value: content_sha256,
|
761
|
+
signed_body_header_type: @apply_checksum_header ?
|
762
|
+
:sbht_content_sha256 : :sbht_none,
|
763
|
+
credentials: creds,
|
764
|
+
unsigned_headers: @unsigned_headers,
|
765
|
+
use_double_uri_encode: @uri_escape_path,
|
766
|
+
should_normalize_uri_path: @normalize_path,
|
767
|
+
omit_session_token: @omit_session_token
|
768
|
+
)
|
769
|
+
http_request = Aws::Crt::Http::Message.new(
|
770
|
+
http_method, url.to_s, headers
|
771
|
+
)
|
772
|
+
signable = Aws::Crt::Auth::Signable.new(http_request)
|
773
|
+
|
774
|
+
signing_result = Aws::Crt::Auth::Signer.sign_request(config, signable)
|
775
|
+
|
776
|
+
Signature.new(
|
777
|
+
headers: sigv4_headers.merge(
|
778
|
+
downcase_headers(signing_result[:headers])
|
779
|
+
),
|
780
|
+
string_to_sign: 'CRT_INTERNAL',
|
781
|
+
canonical_request: 'CRT_INTERNAL',
|
782
|
+
content_sha256: content_sha256,
|
783
|
+
extra: {config: config, signable: signable}
|
497
784
|
)
|
498
|
-
[
|
499
|
-
"AWS4-HMAC-SHA256-PAYLOAD",
|
500
|
-
datetime,
|
501
|
-
credential_scope(datetime[0,8]),
|
502
|
-
prior_signature,
|
503
|
-
sha256_hexdigest(encoded_headers),
|
504
|
-
sha256_hexdigest(payload)
|
505
|
-
].join("\n")
|
506
785
|
end
|
507
786
|
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
787
|
+
def crt_presign_url(options)
|
788
|
+
creds = crt_fetch_credentials
|
789
|
+
|
790
|
+
http_method = extract_http_method(options)
|
791
|
+
url = extract_url(options)
|
792
|
+
headers = downcase_headers(options[:headers])
|
793
|
+
headers['host'] ||= host(url)
|
794
|
+
|
795
|
+
datetime = headers.delete('x-amz-date')
|
796
|
+
datetime ||= (options[:time] || Time.now)
|
797
|
+
|
798
|
+
content_sha256 = headers.delete('x-amz-content-sha256')
|
799
|
+
content_sha256 ||= options[:body_digest]
|
800
|
+
content_sha256 ||= sha256_hexdigest(options[:body] || '')
|
801
|
+
|
802
|
+
config = Aws::Crt::Auth::SigningConfig.new(
|
803
|
+
algorithm: @signing_algorithm,
|
804
|
+
signature_type: :http_request_query_params,
|
805
|
+
region: @region,
|
806
|
+
service: @service,
|
807
|
+
date: datetime,
|
808
|
+
signed_body_value: content_sha256,
|
809
|
+
signed_body_header_type: @apply_checksum_header ?
|
810
|
+
:sbht_content_sha256 : :sbht_none,
|
811
|
+
credentials: creds,
|
812
|
+
unsigned_headers: @unsigned_headers,
|
813
|
+
use_double_uri_encode: @uri_escape_path,
|
814
|
+
should_normalize_uri_path: @normalize_path,
|
815
|
+
omit_session_token: @omit_session_token,
|
816
|
+
expiration_in_seconds: options.fetch(:expires_in, 900)
|
817
|
+
)
|
818
|
+
http_request = Aws::Crt::Http::Message.new(
|
819
|
+
http_method, url.to_s, headers
|
820
|
+
)
|
821
|
+
signable = Aws::Crt::Auth::Signable.new(http_request)
|
822
|
+
|
823
|
+
signing_result = Aws::Crt::Auth::Signer.sign_request(config, signable, http_method, url.to_s)
|
824
|
+
url = URI.parse(signing_result[:path])
|
825
|
+
|
826
|
+
if options[:extra] && options[:extra].is_a?(Hash)
|
827
|
+
options[:extra][:config] = config
|
828
|
+
options[:extra][:signable] = signable
|
829
|
+
end
|
830
|
+
url
|
523
831
|
end
|
524
832
|
|
525
833
|
class << self
|
526
834
|
|
835
|
+
def use_crt?
|
836
|
+
begin
|
837
|
+
require 'aws-crt'
|
838
|
+
return true
|
839
|
+
rescue LoadError
|
840
|
+
return false
|
841
|
+
end
|
842
|
+
end
|
843
|
+
|
527
844
|
# @api private
|
528
845
|
def uri_escape_path(path)
|
529
846
|
path.gsub(/[^\/]+/) { |part| uri_escape(part) }
|
@@ -537,8 +854,8 @@ module Aws
|
|
537
854
|
CGI.escape(string.encode('UTF-8')).gsub('+', '%20').gsub('%7E', '~')
|
538
855
|
end
|
539
856
|
end
|
857
|
+
|
540
858
|
end
|
541
859
|
end
|
542
860
|
end
|
543
861
|
end
|
544
|
-
|
data/lib/aws-sigv4.rb
CHANGED
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: aws-sigv4
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Amazon Web Services
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-04-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: aws-crt
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: aws-eventstream
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -44,22 +30,9 @@ dependencies:
|
|
44
30
|
- - ">="
|
45
31
|
- !ruby/object:Gem::Version
|
46
32
|
version: 1.0.2
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
requirements:
|
51
|
-
- - ">="
|
52
|
-
- !ruby/object:Gem::Version
|
53
|
-
version: '0'
|
54
|
-
type: :development
|
55
|
-
prerelease: false
|
56
|
-
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
requirements:
|
58
|
-
- - ">="
|
59
|
-
- !ruby/object:Gem::Version
|
60
|
-
version: '0'
|
61
|
-
description: Amazon Web Services signing library. Generates signatures for HTTP requests
|
62
|
-
email:
|
33
|
+
description: Amazon Web Services Signature Version 4 signing library. Generates sigv4
|
34
|
+
signature for HTTP requests.
|
35
|
+
email:
|
63
36
|
executables: []
|
64
37
|
extensions: []
|
65
38
|
extra_rdoc_files: []
|
@@ -70,13 +43,16 @@ files:
|
|
70
43
|
- lib/aws-sigv4.rb
|
71
44
|
- lib/aws-sigv4/credentials.rb
|
72
45
|
- lib/aws-sigv4/errors.rb
|
46
|
+
- lib/aws-sigv4/request.rb
|
73
47
|
- lib/aws-sigv4/signature.rb
|
74
48
|
- lib/aws-sigv4/signer.rb
|
75
|
-
homepage: https://github.com/
|
49
|
+
homepage: https://github.com/aws/aws-sdk-ruby
|
76
50
|
licenses:
|
77
51
|
- Apache-2.0
|
78
|
-
metadata:
|
79
|
-
|
52
|
+
metadata:
|
53
|
+
source_code_uri: https://github.com/aws/aws-sdk-ruby/tree/version-3/gems/aws-sigv4
|
54
|
+
changelog_uri: https://github.com/aws/aws-sdk-ruby/tree/version-3/gems/aws-sigv4/CHANGELOG.md
|
55
|
+
post_install_message:
|
80
56
|
rdoc_options: []
|
81
57
|
require_paths:
|
82
58
|
- lib
|
@@ -84,15 +60,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
84
60
|
requirements:
|
85
61
|
- - ">="
|
86
62
|
- !ruby/object:Gem::Version
|
87
|
-
version: '2.
|
63
|
+
version: '2.3'
|
88
64
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
65
|
requirements:
|
90
|
-
- - "
|
66
|
+
- - ">="
|
91
67
|
- !ruby/object:Gem::Version
|
92
|
-
version:
|
68
|
+
version: '0'
|
93
69
|
requirements: []
|
94
|
-
rubygems_version: 3.
|
95
|
-
signing_key:
|
70
|
+
rubygems_version: 3.1.6
|
71
|
+
signing_key:
|
96
72
|
specification_version: 4
|
97
|
-
summary: AWS
|
73
|
+
summary: AWS Signature Version 4 library.
|
98
74
|
test_files: []
|