aws-sigv4 1.5.2 → 1.12.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 33cb09610570a5aefa4e83ab34277756201d8f1e50197bfe343fc49cce668672
4
- data.tar.gz: 4d070f7cf41c0a77b69e7ca6cc80c2fb7a8111eb3640819fb01ee6602027b5d9
3
+ metadata.gz: 2f65828f524fd8437e342204ac5a7ae1deff92dfcfae2affbd636ceef0a1cb4d
4
+ data.tar.gz: 260bf03de93d779a544226ef512e958a3a6256bb189918be7a63c5c4bf58395f
5
5
  SHA512:
6
- metadata.gz: ddb8c5fc04288a396501afb0cd74907232ac78a2ca5e3bbb4c0879c27c15d72c19e30b9ddcaf5b8fe536e8b04d4ccc3c98eee74f27f92b594058a54b29edf704
7
- data.tar.gz: 283afcb61ae4b06a68b5a644a529b560864b82f0e69c107a8c059c04b9b6448421ac93015c483aae1bdf77bed41cb3088942c1e18dc26fc4b569aa7fde65f563
6
+ metadata.gz: 456725f9d6b39c36030e321854d1ef6361e504fb0af829fd95427cbf9762aba52be46f178f32db3ba332598b525ac9bbb287a19ade98b23892ff2d7613e3aca4
7
+ data.tar.gz: 80301bb28c264d5a73984e7b1ec3423b26daca0451996f47a21219d1d8dd615889c701678b9551cf3f724a6a6ab59c623fe71a89b62c9f54ff9dc38b849e058d
data/CHANGELOG.md CHANGED
@@ -1,6 +1,56 @@
1
1
  Unreleased Changes
2
2
  ------------------
3
3
 
4
+ 1.12.0 (2025-06-02)
5
+ ------------------
6
+
7
+ * Feature - AWS SDK for Ruby no longer supports Ruby runtime versions 2.5 and 2.6.
8
+
9
+ 1.11.0 (2025-01-10)
10
+ ------------------
11
+
12
+ * Feature - Add RBS signature files to support static type checking
13
+
14
+ 1.10.1 (2024-10-21)
15
+ ------------------
16
+
17
+ * Issue - Fix sigv4a signing issue with derive_asymmetric_key for certain credentials.
18
+
19
+ 1.10.0 (2024-09-17)
20
+ ------------------
21
+
22
+ * Feature - Remove CRT `sigv4a` signing capability.
23
+
24
+ 1.9.1 (2024-07-29)
25
+ ------------------
26
+
27
+ * Issue - Add missing require of `pathname` to `Signer`.
28
+
29
+ 1.9.0 (2024-07-23)
30
+ ------------------
31
+
32
+ * Feature - Support `sigv4a` signing algorithm without `aws-crt`.
33
+
34
+ 1.8.0 (2023-11-28)
35
+ ------------------
36
+
37
+ * Feature - Support `sigv4-s3express` signing algorithm.
38
+
39
+ 1.7.0 (2023-11-22)
40
+ ------------------
41
+
42
+ * Feature - AWS SDK for Ruby no longer supports Ruby runtime versions 2.3 and 2.4.
43
+
44
+ 1.6.1 (2023-10-25)
45
+ ------------------
46
+
47
+ * Issue - (Static Stability) use provided `expires_in` in presigned url when credentials are expired.
48
+
49
+ 1.6.0 (2023-06-28)
50
+ ------------------
51
+
52
+ * Feature - Select the minimum expiration time for presigned urls between the expiration time option and the credential expiration time.
53
+
4
54
  1.5.2 (2022-09-30)
5
55
  ------------------
6
56
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.5.2
1
+ 1.12.0
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ module Sigv4
5
+ # To make it easier to support mixed mode, we have created an asymmetric
6
+ # key derivation mechanism. This module derives
7
+ # asymmetric keys from the current secret for use with
8
+ # Asymmetric signatures.
9
+ # @api private
10
+ module AsymmetricCredentials
11
+
12
+ N_MINUS_2 = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551 - 2
13
+
14
+ # @return [OpenSSL::PKey::EC, Hash]
15
+ def self.derive_asymmetric_key(access_key_id, secret_access_key)
16
+ check_openssl_support!
17
+ label = 'AWS4-ECDSA-P256-SHA256'
18
+ bit_len = 256
19
+ counter = 0x1
20
+ input_key = "AWS4A#{secret_access_key}"
21
+ d = 0 # d will end up being the private key
22
+ while true do
23
+
24
+ kdf_context = access_key_id.unpack('C*') + [counter].pack('C').unpack('C') #1 byte for counter
25
+ input = label.unpack('C*') + [0x00] + kdf_context + [bit_len].pack('L>').unpack('CCCC') # 4 bytes (change endianess)
26
+ k0 = OpenSSL::HMAC.digest("SHA256", input_key, ([0, 0, 0, 0x01] + input).pack('C*'))
27
+ c = be_bytes_to_num( k0.unpack('C*') )
28
+ if c <= N_MINUS_2
29
+ d = c + 1
30
+ break
31
+ elsif counter > 0xFF
32
+ raise 'Counter exceeded 1 byte - unable to get asym creds'
33
+ else
34
+ counter += 1
35
+ end
36
+ end
37
+
38
+ # compute the public key
39
+ group = OpenSSL::PKey::EC::Group.new('prime256v1')
40
+ public_key = group.generator.mul(d)
41
+
42
+ ec = generate_ec(public_key, d)
43
+
44
+ # pk_x and pk_y are not needed for signature, but useful in verification/testing
45
+ pk_b = public_key.to_octet_string(:uncompressed).unpack('C*') # 0x04 byte followed by 2 32-byte integers
46
+ pk_x = be_bytes_to_num(pk_b[1,32])
47
+ pk_y = be_bytes_to_num(pk_b[33,32])
48
+ [ec, {ec: ec, public_key: public_key, pk_x: pk_x, pk_y: pk_y, d: d}]
49
+ end
50
+
51
+ private
52
+
53
+ # @return [Number] The value of the bytes interpreted as a big-endian
54
+ # unsigned integer.
55
+ def self.be_bytes_to_num(bytes)
56
+ x = 0
57
+ bytes.each { |b| x = (x*256) + b }
58
+ x
59
+ end
60
+
61
+ # @return [Array] value of the BigNumber as a big-endian unsigned byte array.
62
+ def self.bn_to_be_bytes(bn)
63
+ bytes = []
64
+ while bn > 0
65
+ bytes << (bn & 0xff)
66
+ bn = bn >> 8
67
+ end
68
+ bytes.reverse
69
+ end
70
+
71
+ # Prior to openssl3 we could directly set public and private key on EC
72
+ # However, openssl3 deprecated those methods and we must now construct
73
+ # a der with the keys and load the EC from it.
74
+ def self.generate_ec(public_key, d)
75
+ # format reversed from: OpenSSL::ASN1.decode_all(OpenSSL::PKey::EC.new.to_der)
76
+ asn1 = OpenSSL::ASN1::Sequence([
77
+ OpenSSL::ASN1::Integer(OpenSSL::BN.new(1)),
78
+ OpenSSL::ASN1::OctetString(bn_to_be_bytes(d).pack('C*')),
79
+ OpenSSL::ASN1::ASN1Data.new([OpenSSL::ASN1::ObjectId("prime256v1")], 0, :CONTEXT_SPECIFIC),
80
+ OpenSSL::ASN1::ASN1Data.new(
81
+ [OpenSSL::ASN1::BitString(public_key.to_octet_string(:uncompressed))],
82
+ 1, :CONTEXT_SPECIFIC
83
+ )
84
+ ])
85
+ OpenSSL::PKey::EC.new(asn1.to_der)
86
+ end
87
+
88
+ def self.check_openssl_support!
89
+ return true unless defined?(JRUBY_VERSION)
90
+
91
+ # See: https://github.com/jruby/jruby-openssl/issues/306
92
+ # JRuby-openssl < 0.15 does not support OpenSSL::PKey::EC::Point#mul
93
+ return true if OpenSSL::PKey::EC::Point.instance_methods.include?(:mul)
94
+
95
+ raise 'Sigv4a Asymmetric Credential derivation requires jruby-openssl >= 0.15'
96
+ end
97
+ end
98
+ end
99
+ end
@@ -7,7 +7,7 @@ module Aws
7
7
  class Request
8
8
 
9
9
  # @option options [required, String] :http_method
10
- # @option options [required, HTTP::URI, HTTPS::URI, String] :endpoint
10
+ # @option options [required, String, URI::HTTP, URI::HTTPS] :endpoint
11
11
  # @option options [Hash<String,String>] :headers ({})
12
12
  # @option options [String, IO] :body ('')
13
13
  def initialize(options = {})
@@ -30,12 +30,12 @@ module Aws
30
30
  @http_method
31
31
  end
32
32
 
33
- # @param [String, HTTP::URI, HTTPS::URI] endpoint
33
+ # @param [String, URI::HTTP, URI::HTTPS] endpoint
34
34
  def endpoint=(endpoint)
35
35
  @endpoint = URI.parse(endpoint.to_s)
36
36
  end
37
37
 
38
- # @return [HTTP::URI, HTTPS::URI]
38
+ # @return [URI::HTTP, URI::HTTPS]
39
39
  def endpoint
40
40
  @endpoint
41
41
  end
@@ -32,6 +32,9 @@ module Aws
32
32
  # @return [String] For debugging purposes.
33
33
  attr_accessor :content_sha256
34
34
 
35
+ # @return [String] For debugging purposes.
36
+ attr_accessor :signature
37
+
35
38
  # @return [Hash] Internal data for debugging purposes.
36
39
  attr_accessor :extra
37
40
  end
@@ -6,6 +6,7 @@ require 'time'
6
6
  require 'uri'
7
7
  require 'set'
8
8
  require 'cgi'
9
+ require 'pathname'
9
10
  require 'aws-eventstream'
10
11
 
11
12
  module Aws
@@ -73,25 +74,18 @@ module Aws
73
74
  # and `#session_token`.
74
75
  #
75
76
  class Signer
76
-
77
- @@use_crt =
78
- begin
79
- require 'aws-crt'
80
- true
81
- rescue LoadError
82
- false
83
- end
84
-
85
77
  # @overload initialize(service:, region:, access_key_id:, secret_access_key:, session_token:nil, **options)
86
78
  # @param [String] :service The service signing name, e.g. 's3'.
87
- # @param [String] :region The region name, e.g. 'us-east-1'.
79
+ # @param [String] :region The region name, e.g. 'us-east-1'. When signing
80
+ # with sigv4a, this should be a comma separated list of regions.
88
81
  # @param [String] :access_key_id
89
82
  # @param [String] :secret_access_key
90
83
  # @param [String] :session_token (nil)
91
84
  #
92
85
  # @overload initialize(service:, region:, credentials:, **options)
93
86
  # @param [String] :service The service signing name, e.g. 's3'.
94
- # @param [String] :region The region name, e.g. 'us-east-1'.
87
+ # @param [String] :region The region name, e.g. 'us-east-1'. When signing
88
+ # with sigv4a, this should be a comma separated list of regions.
95
89
  # @param [Credentials] :credentials Any object that responds to the following
96
90
  # methods:
97
91
  #
@@ -102,7 +96,8 @@ module Aws
102
96
  #
103
97
  # @overload initialize(service:, region:, credentials_provider:, **options)
104
98
  # @param [String] :service The service signing name, e.g. 's3'.
105
- # @param [String] :region The region name, e.g. 'us-east-1'.
99
+ # @param [String] :region The region name, e.g. 'us-east-1'. When signing
100
+ # with sigv4a, this should be a comma separated list of regions.
106
101
  # @param [#credentials] :credentials_provider An object that responds
107
102
  # to `#credentials`, returning an object that responds to the following
108
103
  # methods:
@@ -127,8 +122,7 @@ module Aws
127
122
  # every other AWS service as of late 2016.
128
123
  #
129
124
  # @option options [Symbol] :signing_algorithm (:sigv4) The
130
- # algorithm to use for signing. :sigv4a is only supported when
131
- # `aws-crt` is available.
125
+ # algorithm to use for signing.
132
126
  #
133
127
  # @option options [Boolean] :omit_session_token (false)
134
128
  # (Supported only when `aws-crt` is available) If `true`,
@@ -136,8 +130,8 @@ module Aws
136
130
  # but is treated as "unsigned" and does not contribute
137
131
  # to the authorization signature.
138
132
  #
139
- # @option options [Boolean] :normalize_path (true) (Supported only when `aws-crt` is available)
140
- # When `true`, the uri paths will be normalized when building the canonical request
133
+ # @option options [Boolean] :normalize_path (true) When `true`, the
134
+ # uri paths will be normalized when building the canonical request.
141
135
  def initialize(options = {})
142
136
  @service = extract_service(options)
143
137
  @region = extract_region(options)
@@ -151,12 +145,6 @@ module Aws
151
145
  @signing_algorithm = options.fetch(:signing_algorithm, :sigv4)
152
146
  @normalize_path = options.fetch(:normalize_path, true)
153
147
  @omit_session_token = options.fetch(:omit_session_token, false)
154
-
155
- if @signing_algorithm == :sigv4a && !Signer.use_crt?
156
- raise ArgumentError, 'You are attempting to sign a' \
157
- ' request with sigv4a which requires the `aws-crt` gem.'\
158
- ' Please install the gem or add it to your gemfile.'
159
- end
160
148
  end
161
149
 
162
150
  # @return [String]
@@ -217,7 +205,7 @@ module Aws
217
205
  # @option request [required, String] :http_method One of
218
206
  # 'GET', 'HEAD', 'PUT', 'POST', 'PATCH', or 'DELETE'
219
207
  #
220
- # @option request [required, String, URI::HTTPS, URI::HTTP] :url
208
+ # @option request [required, String, URI::HTTP, URI::HTTPS] :url
221
209
  # The request URI. Must be a valid HTTP or HTTPS URI.
222
210
  #
223
211
  # @option request [optional, Hash] :headers ({}) A hash of headers
@@ -232,13 +220,11 @@ module Aws
232
220
  # a `#headers` method. The headers must be applied to your request.
233
221
  #
234
222
  def sign_request(request)
235
-
236
- return crt_sign_request(request) if Signer.use_crt?
237
-
238
- creds = fetch_credentials
223
+ creds, _ = fetch_credentials
239
224
 
240
225
  http_method = extract_http_method(request)
241
226
  url = extract_url(request)
227
+ Signer.normalize_path(url) if @normalize_path
242
228
  headers = downcase_headers(request[:headers])
243
229
 
244
230
  datetime = headers['x-amz-date']
@@ -251,29 +237,55 @@ module Aws
251
237
  sigv4_headers = {}
252
238
  sigv4_headers['host'] = headers['host'] || host(url)
253
239
  sigv4_headers['x-amz-date'] = datetime
254
- sigv4_headers['x-amz-security-token'] = creds.session_token if creds.session_token
240
+ if creds.session_token && !@omit_session_token
241
+ if @signing_algorithm == 'sigv4-s3express'.to_sym
242
+ sigv4_headers['x-amz-s3session-token'] = creds.session_token
243
+ else
244
+ sigv4_headers['x-amz-security-token'] = creds.session_token
245
+ end
246
+ end
247
+
255
248
  sigv4_headers['x-amz-content-sha256'] ||= content_sha256 if @apply_checksum_header
256
249
 
250
+ if @signing_algorithm == :sigv4a && @region && !@region.empty?
251
+ sigv4_headers['x-amz-region-set'] = @region
252
+ end
257
253
  headers = headers.merge(sigv4_headers) # merge so we do not modify given headers hash
258
254
 
255
+ algorithm = sts_algorithm
256
+
259
257
  # compute signature parts
260
258
  creq = canonical_request(http_method, url, headers, content_sha256)
261
- sts = string_to_sign(datetime, creq)
262
- sig = signature(creds.secret_access_key, date, sts)
259
+ sts = string_to_sign(datetime, creq, algorithm)
260
+
261
+ sig =
262
+ if @signing_algorithm == :sigv4a
263
+ asymmetric_signature(creds, sts)
264
+ else
265
+ signature(creds.secret_access_key, date, sts)
266
+ end
267
+
268
+ algorithm = sts_algorithm
263
269
 
264
270
  # apply signature
265
271
  sigv4_headers['authorization'] = [
266
- "AWS4-HMAC-SHA256 Credential=#{credential(creds, date)}",
272
+ "#{algorithm} Credential=#{credential(creds, date)}",
267
273
  "SignedHeaders=#{signed_headers(headers)}",
268
274
  "Signature=#{sig}",
269
275
  ].join(', ')
270
276
 
277
+ # skip signing the session token, but include it in the headers
278
+ if creds.session_token && @omit_session_token
279
+ sigv4_headers['x-amz-security-token'] = creds.session_token
280
+ end
281
+
271
282
  # Returning the signature components.
272
283
  Signature.new(
273
284
  headers: sigv4_headers,
274
285
  string_to_sign: sts,
275
286
  canonical_request: creq,
276
- content_sha256: content_sha256
287
+ content_sha256: content_sha256,
288
+ signature: sig
277
289
  )
278
290
  end
279
291
 
@@ -313,8 +325,7 @@ module Aws
313
325
  # signature value (a binary string) used at ':chunk-signature' needs to converted to
314
326
  # hex-encoded string using #unpack
315
327
  def sign_event(prior_signature, payload, encoder)
316
- # Note: CRT does not currently provide event stream signing, so we always use the ruby implementation.
317
- creds = fetch_credentials
328
+ creds, _ = fetch_credentials
318
329
  time = Time.now
319
330
  headers = {}
320
331
 
@@ -372,7 +383,7 @@ module Aws
372
383
  # @option options [required, String] :http_method The HTTP request method,
373
384
  # e.g. 'GET', 'HEAD', 'PUT', 'POST', 'PATCH', or 'DELETE'.
374
385
  #
375
- # @option options [required, String, HTTPS::URI, HTTP::URI] :url
386
+ # @option options [required, String, URI::HTTP, URI::HTTPS] :url
376
387
  # The URI to sign.
377
388
  #
378
389
  # @option options [Hash] :headers ({}) Headers that should
@@ -400,13 +411,11 @@ module Aws
400
411
  # @return [HTTPS::URI, HTTP::URI]
401
412
  #
402
413
  def presign_url(options)
403
-
404
- return crt_presign_url(options) if Signer.use_crt?
405
-
406
- creds = fetch_credentials
414
+ creds, expiration = fetch_credentials
407
415
 
408
416
  http_method = extract_http_method(options)
409
417
  url = extract_url(options)
418
+ Signer.normalize_path(url) if @normalize_path
410
419
 
411
420
  headers = downcase_headers(options[:headers])
412
421
  headers['host'] ||= host(url)
@@ -419,14 +428,26 @@ module Aws
419
428
  content_sha256 ||= options[:body_digest]
420
429
  content_sha256 ||= sha256_hexdigest(options[:body] || '')
421
430
 
431
+ algorithm = sts_algorithm
432
+
422
433
  params = {}
423
- params['X-Amz-Algorithm'] = 'AWS4-HMAC-SHA256'
434
+ params['X-Amz-Algorithm'] = algorithm
424
435
  params['X-Amz-Credential'] = credential(creds, date)
425
436
  params['X-Amz-Date'] = datetime
426
- params['X-Amz-Expires'] = extract_expires_in(options)
427
- params['X-Amz-Security-Token'] = creds.session_token if creds.session_token
437
+ params['X-Amz-Expires'] = presigned_url_expiration(options, expiration, Time.strptime(datetime, "%Y%m%dT%H%M%S%Z")).to_s
438
+ if creds.session_token
439
+ if @signing_algorithm == 'sigv4-s3express'.to_sym
440
+ params['X-Amz-S3session-Token'] = creds.session_token
441
+ else
442
+ params['X-Amz-Security-Token'] = creds.session_token
443
+ end
444
+ end
428
445
  params['X-Amz-SignedHeaders'] = signed_headers(headers)
429
446
 
447
+ if @signing_algorithm == :sigv4a && @region
448
+ params['X-Amz-Region-Set'] = @region
449
+ end
450
+
430
451
  params = params.map do |key, value|
431
452
  "#{uri_escape(key)}=#{uri_escape(value)}"
432
453
  end.join('&')
@@ -438,13 +459,23 @@ module Aws
438
459
  end
439
460
 
440
461
  creq = canonical_request(http_method, url, headers, content_sha256)
441
- sts = string_to_sign(datetime, creq)
442
- url.query += '&X-Amz-Signature=' + signature(creds.secret_access_key, date, sts)
462
+ sts = string_to_sign(datetime, creq, algorithm)
463
+ signature =
464
+ if @signing_algorithm == :sigv4a
465
+ asymmetric_signature(creds, sts)
466
+ else
467
+ signature(creds.secret_access_key, date, sts)
468
+ end
469
+ url.query += '&X-Amz-Signature=' + signature
443
470
  url
444
471
  end
445
472
 
446
473
  private
447
474
 
475
+ def sts_algorithm
476
+ @signing_algorithm == :sigv4a ? 'AWS4-ECDSA-P256-SHA256' : 'AWS4-HMAC-SHA256'
477
+ end
478
+
448
479
  def canonical_request(http_method, url, headers, content_sha256)
449
480
  [
450
481
  http_method,
@@ -456,9 +487,9 @@ module Aws
456
487
  ].join("\n")
457
488
  end
458
489
 
459
- def string_to_sign(datetime, canonical_request)
490
+ def string_to_sign(datetime, canonical_request, algorithm)
460
491
  [
461
- 'AWS4-HMAC-SHA256',
492
+ algorithm,
462
493
  datetime,
463
494
  credential_scope(datetime[0,8]),
464
495
  sha256_hexdigest(canonical_request),
@@ -491,10 +522,10 @@ module Aws
491
522
  def credential_scope(date)
492
523
  [
493
524
  date,
494
- @region,
525
+ (@region unless @signing_algorithm == :sigv4a),
495
526
  @service,
496
- 'aws4_request',
497
- ].join('/')
527
+ 'aws4_request'
528
+ ].compact.join('/')
498
529
  end
499
530
 
500
531
  def credential(credentials, date)
@@ -509,6 +540,16 @@ module Aws
509
540
  hexhmac(k_credentials, string_to_sign)
510
541
  end
511
542
 
543
+ def asymmetric_signature(creds, string_to_sign)
544
+ ec, _ = Aws::Sigv4::AsymmetricCredentials.derive_asymmetric_key(
545
+ creds.access_key_id, creds.secret_access_key
546
+ )
547
+ sts_digest = OpenSSL::Digest::SHA256.digest(string_to_sign)
548
+ s = ec.dsa_sign_asn1(sts_digest)
549
+
550
+ Digest.hexencode(s)
551
+ end
552
+
512
553
  # Comparing to original signature v4 algorithm,
513
554
  # returned signature is a binary string instread of
514
555
  # hex-encoded string. (Since ':chunk-signature' requires
@@ -526,7 +567,6 @@ module Aws
526
567
  hmac(k_credentials, string_to_sign)
527
568
  end
528
569
 
529
-
530
570
  def path(url)
531
571
  path = url.path
532
572
  path = '/' if path == ''
@@ -682,8 +722,8 @@ module Aws
682
722
 
683
723
  def extract_expires_in(options)
684
724
  case options[:expires_in]
685
- when nil then 900.to_s
686
- when Integer then options[:expires_in].to_s
725
+ when nil then 900
726
+ when Integer then options[:expires_in]
687
727
  else
688
728
  msg = "expected :expires_in to be a number of seconds"
689
729
  raise ArgumentError, msg
@@ -698,11 +738,14 @@ module Aws
698
738
  self.class.uri_escape_path(string)
699
739
  end
700
740
 
701
-
702
741
  def fetch_credentials
703
742
  credentials = @credentials_provider.credentials
704
743
  if credentials_set?(credentials)
705
- credentials
744
+ expiration = nil
745
+ if @credentials_provider.respond_to?(:expiration)
746
+ expiration = @credentials_provider.expiration
747
+ end
748
+ [credentials, expiration]
706
749
  else
707
750
  raise Errors::MissingCredentialsError,
708
751
  'unable to sign request without credentials set'
@@ -720,128 +763,27 @@ module Aws
720
763
  !credentials.secret_access_key.empty?
721
764
  end
722
765
 
723
- ### CRT Code
766
+ def presigned_url_expiration(options, expiration, datetime)
767
+ expires_in = extract_expires_in(options)
768
+ return expires_in unless expiration
724
769
 
725
- # the credentials used by CRT must be a
726
- # CRT StaticCredentialsProvider object
727
- def crt_fetch_credentials
728
- creds = fetch_credentials
729
- Aws::Crt::Auth::StaticCredentialsProvider.new(
730
- creds.access_key_id,
731
- creds.secret_access_key,
732
- creds.session_token
733
- )
734
- end
735
-
736
- def crt_sign_request(request)
737
- creds = crt_fetch_credentials
738
- http_method = extract_http_method(request)
739
- url = extract_url(request)
740
- headers = downcase_headers(request[:headers])
741
-
742
- datetime =
743
- if headers.include? 'x-amz-date'
744
- Time.parse(headers.delete('x-amz-date'))
745
- end
746
-
747
- content_sha256 = headers.delete('x-amz-content-sha256')
748
- content_sha256 ||= sha256_hexdigest(request[:body] || '')
749
-
750
- sigv4_headers = {}
751
- sigv4_headers['host'] = headers['host'] || host(url)
752
-
753
- # Modify the user-agent to add usage of crt-signer
754
- # This should be temporary during developer preview only
755
- if headers.include? 'user-agent'
756
- headers['user-agent'] = "#{headers['user-agent']} crt-signer/#{@signing_algorithm}/#{Aws::Sigv4::VERSION}"
757
- sigv4_headers['user-agent'] = headers['user-agent']
758
- end
759
-
760
- headers = headers.merge(sigv4_headers) # merge so we do not modify given headers hash
761
-
762
- config = Aws::Crt::Auth::SigningConfig.new(
763
- algorithm: @signing_algorithm,
764
- signature_type: :http_request_headers,
765
- region: @region,
766
- service: @service,
767
- date: datetime,
768
- signed_body_value: content_sha256,
769
- signed_body_header_type: @apply_checksum_header ?
770
- :sbht_content_sha256 : :sbht_none,
771
- credentials: creds,
772
- unsigned_headers: @unsigned_headers,
773
- use_double_uri_encode: @uri_escape_path,
774
- should_normalize_uri_path: @normalize_path,
775
- omit_session_token: @omit_session_token
776
- )
777
- http_request = Aws::Crt::Http::Message.new(
778
- http_method, url.to_s, headers
779
- )
780
- signable = Aws::Crt::Auth::Signable.new(http_request)
781
-
782
- signing_result = Aws::Crt::Auth::Signer.sign_request(config, signable)
783
-
784
- Signature.new(
785
- headers: sigv4_headers.merge(
786
- downcase_headers(signing_result[:headers])
787
- ),
788
- string_to_sign: 'CRT_INTERNAL',
789
- canonical_request: 'CRT_INTERNAL',
790
- content_sha256: content_sha256,
791
- extra: {config: config, signable: signable}
792
- )
793
- end
794
-
795
- def crt_presign_url(options)
796
- creds = crt_fetch_credentials
797
-
798
- http_method = extract_http_method(options)
799
- url = extract_url(options)
800
- headers = downcase_headers(options[:headers])
801
- headers['host'] ||= host(url)
802
-
803
- datetime = headers.delete('x-amz-date')
804
- datetime ||= (options[:time] || Time.now)
805
-
806
- content_sha256 = headers.delete('x-amz-content-sha256')
807
- content_sha256 ||= options[:body_digest]
808
- content_sha256 ||= sha256_hexdigest(options[:body] || '')
809
-
810
- config = Aws::Crt::Auth::SigningConfig.new(
811
- algorithm: @signing_algorithm,
812
- signature_type: :http_request_query_params,
813
- region: @region,
814
- service: @service,
815
- date: datetime,
816
- signed_body_value: content_sha256,
817
- signed_body_header_type: @apply_checksum_header ?
818
- :sbht_content_sha256 : :sbht_none,
819
- credentials: creds,
820
- unsigned_headers: @unsigned_headers,
821
- use_double_uri_encode: @uri_escape_path,
822
- should_normalize_uri_path: @normalize_path,
823
- omit_session_token: @omit_session_token,
824
- expiration_in_seconds: options.fetch(:expires_in, 900)
825
- )
826
- http_request = Aws::Crt::Http::Message.new(
827
- http_method, url.to_s, headers
828
- )
829
- signable = Aws::Crt::Auth::Signable.new(http_request)
830
-
831
- signing_result = Aws::Crt::Auth::Signer.sign_request(config, signable, http_method, url.to_s)
832
- url = URI.parse(signing_result[:path])
833
-
834
- if options[:extra] && options[:extra].is_a?(Hash)
835
- options[:extra][:config] = config
836
- options[:extra][:signable] = signable
770
+ expiration_seconds = (expiration - datetime).to_i
771
+ # In the static stability case, credentials may expire in the past
772
+ # but still be valid. For those cases, use the user configured
773
+ # expires_in and ingore expiration.
774
+ if expiration_seconds <= 0
775
+ expires_in
776
+ else
777
+ [expires_in, expiration_seconds].min
837
778
  end
838
- url
839
779
  end
840
780
 
841
781
  class << self
842
782
 
783
+ # Kept for backwards compatability
784
+ # Always return false since we are not using crt signing functionality
843
785
  def use_crt?
844
- @@use_crt
786
+ false
845
787
  end
846
788
 
847
789
  # @api private
@@ -858,6 +800,18 @@ module Aws
858
800
  end
859
801
  end
860
802
 
803
+ # @api private
804
+ def normalize_path(uri)
805
+ normalized_path = Pathname.new(uri.path).cleanpath.to_s
806
+ # Pathname is probably not correct to use. Empty paths will
807
+ # resolve to "." and should be disregarded
808
+ normalized_path = '' if normalized_path == '.'
809
+ # Ensure trailing slashes are correctly preserved
810
+ if uri.path.end_with?('/') && !normalized_path.end_with?('/')
811
+ normalized_path << '/'
812
+ end
813
+ uri.path = normalized_path
814
+ end
861
815
  end
862
816
  end
863
817
  end
data/lib/aws-sigv4.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'aws-sigv4/asymmetric_credentials'
3
4
  require_relative 'aws-sigv4/credentials'
4
5
  require_relative 'aws-sigv4/errors'
5
6
  require_relative 'aws-sigv4/signature'
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aws-sigv4
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.2
4
+ version: 1.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Amazon Web Services
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2022-09-30 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: aws-eventstream
@@ -32,7 +31,6 @@ dependencies:
32
31
  version: 1.0.2
33
32
  description: Amazon Web Services Signature Version 4 signing library. Generates sigv4
34
33
  signature for HTTP requests.
35
- email:
36
34
  executables: []
37
35
  extensions: []
38
36
  extra_rdoc_files: []
@@ -41,6 +39,7 @@ files:
41
39
  - LICENSE.txt
42
40
  - VERSION
43
41
  - lib/aws-sigv4.rb
42
+ - lib/aws-sigv4/asymmetric_credentials.rb
44
43
  - lib/aws-sigv4/credentials.rb
45
44
  - lib/aws-sigv4/errors.rb
46
45
  - lib/aws-sigv4/request.rb
@@ -52,7 +51,6 @@ licenses:
52
51
  metadata:
53
52
  source_code_uri: https://github.com/aws/aws-sdk-ruby/tree/version-3/gems/aws-sigv4
54
53
  changelog_uri: https://github.com/aws/aws-sdk-ruby/tree/version-3/gems/aws-sigv4/CHANGELOG.md
55
- post_install_message:
56
54
  rdoc_options: []
57
55
  require_paths:
58
56
  - lib
@@ -60,15 +58,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
60
58
  requirements:
61
59
  - - ">="
62
60
  - !ruby/object:Gem::Version
63
- version: '2.3'
61
+ version: '2.7'
64
62
  required_rubygems_version: !ruby/object:Gem::Requirement
65
63
  requirements:
66
64
  - - ">="
67
65
  - !ruby/object:Gem::Version
68
66
  version: '0'
69
67
  requirements: []
70
- rubygems_version: 3.1.6
71
- signing_key:
68
+ rubygems_version: 3.6.7
72
69
  specification_version: 4
73
70
  summary: AWS Signature Version 4 library.
74
71
  test_files: []