aws-sigv4 1.3.0 → 1.8.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: b8b705b7ccc4d893d12bcc41c42921b0d6d5c264fc24c8aa04195736f05ecaac
4
- data.tar.gz: ab36dadea138a0593bb84d123fbe667c37bcdbb3e626e05628bac05493fbd1e6
3
+ metadata.gz: 482b4ffa8bd9e9e2d7dab0d61ab15553f0e8d05e1be2923b388157664a47a9fa
4
+ data.tar.gz: c5caa84527ca213826f8c802195430caacb25750530609f1b8d7267810808574
5
5
  SHA512:
6
- metadata.gz: 421a1d3f97ee0037d2ddeeed88e454dc7fdd88314d1503abe488b224793e63b2e131d197c67cce9efdc09bcf6c63560a976b72b6db636cf91f89afcdfce1b79e
7
- data.tar.gz: cbb4297ad962402d891593d6326d460df3fe919bd1b3151ca9b80d3aadcc4f14384a4e4826d71f4d0faac9cacad4093b43a6b9be5a11fa206a20f3fb5a00a5f4
6
+ metadata.gz: c16c5df7f8c6ca10cf073c25984506ce938f26823f99d82813b5bde3fb283d23a3c480adec2945b98975946a7b32efe57a0058cd65bb383606c5ee5228711381
7
+ data.tar.gz: b72ea1894eb1c419179325f8e715fb454ab02ae2d1ac243b90ed2f4032c0fd966ba157287a12009587908d0a6a2f62261c78e319a230196792b6ec4206a20718
data/CHANGELOG.md CHANGED
@@ -1,6 +1,46 @@
1
1
  Unreleased Changes
2
2
  ------------------
3
3
 
4
+ 1.8.0 (2023-11-28)
5
+ ------------------
6
+
7
+ * Feature - Support `sigv4-s3express` signing algorithm.
8
+
9
+ 1.7.0 (2023-11-22)
10
+ ------------------
11
+
12
+ * Feature - AWS SDK for Ruby no longer supports Ruby runtime versions 2.3 and 2.4.
13
+
14
+ 1.6.1 (2023-10-25)
15
+ ------------------
16
+
17
+ * Issue - (Static Stability) use provided `expires_in` in presigned url when credentials are expired.
18
+
19
+ 1.6.0 (2023-06-28)
20
+ ------------------
21
+
22
+ * Feature - Select the minimum expiration time for presigned urls between the expiration time option and the credential expiration time.
23
+
24
+ 1.5.2 (2022-09-30)
25
+ ------------------
26
+
27
+ * Issue - Fix an issue where quoted strings with multiple spaces are not trimmed. (#2758)
28
+
29
+ 1.5.1 (2022-07-19)
30
+ ------------------
31
+
32
+ * Issue - Fix performance regression when checking if `aws-crt` is available. (#2729)
33
+
34
+ 1.5.0 (2022-04-20)
35
+ ------------------
36
+
37
+ * Feature - Use CRT based signers if `aws-crt` is available - provides support for `sigv4a`.
38
+
39
+ 1.4.0 (2021-09-02)
40
+ ------------------
41
+
42
+ * Feature - add `signing_algorithm` option with `sigv4` default.
43
+
4
44
  1.3.0 (2021-09-01)
5
45
  ------------------
6
46
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.3.0
1
+ 1.8.0
@@ -32,6 +32,8 @@ module Aws
32
32
  # @return [String] For debugging purposes.
33
33
  attr_accessor :content_sha256
34
34
 
35
+ # @return [Hash] Internal data for debugging purposes.
36
+ attr_accessor :extra
35
37
  end
36
38
  end
37
39
  end
@@ -74,6 +74,14 @@ module Aws
74
74
  #
75
75
  class Signer
76
76
 
77
+ @@use_crt =
78
+ begin
79
+ require 'aws-crt'
80
+ true
81
+ rescue LoadError
82
+ false
83
+ end
84
+
77
85
  # @overload initialize(service:, region:, access_key_id:, secret_access_key:, session_token:nil, **options)
78
86
  # @param [String] :service The service signing name, e.g. 's3'.
79
87
  # @param [String] :region The region name, e.g. 'us-east-1'.
@@ -118,6 +126,18 @@ module Aws
118
126
  # headers. This is required for AWS Glacier, and optional for
119
127
  # every other AWS service as of late 2016.
120
128
  #
129
+ # @option options [Symbol] :signing_algorithm (:sigv4) The
130
+ # algorithm to use for signing. :sigv4a is only supported when
131
+ # `aws-crt` is available.
132
+ #
133
+ # @option options [Boolean] :omit_session_token (false)
134
+ # (Supported only when `aws-crt` is available) If `true`,
135
+ # then security token is added to the final signing result,
136
+ # but is treated as "unsigned" and does not contribute
137
+ # to the authorization signature.
138
+ #
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
121
141
  def initialize(options = {})
122
142
  @service = extract_service(options)
123
143
  @region = extract_region(options)
@@ -126,8 +146,23 @@ module Aws
126
146
  @unsigned_headers << 'authorization'
127
147
  @unsigned_headers << 'x-amzn-trace-id'
128
148
  @unsigned_headers << 'expect'
129
- [:uri_escape_path, :apply_checksum_header].each do |opt|
130
- instance_variable_set("@#{opt}", options.key?(opt) ? !!options[opt] : true)
149
+ @uri_escape_path = options.fetch(:uri_escape_path, true)
150
+ @apply_checksum_header = options.fetch(:apply_checksum_header, true)
151
+ @signing_algorithm = options.fetch(:signing_algorithm, :sigv4)
152
+ @normalize_path = options.fetch(:normalize_path, true)
153
+ @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
+
161
+ if @signing_algorithm == 'sigv4-s3express'.to_sym &&
162
+ Signer.use_crt? && Aws::Crt::GEM_VERSION <= '0.1.9'
163
+ raise ArgumentError,
164
+ 'This version of aws-crt does not support S3 Express. Please
165
+ update this gem to at least version 0.2.0.'
131
166
  end
132
167
  end
133
168
 
@@ -205,7 +240,9 @@ module Aws
205
240
  #
206
241
  def sign_request(request)
207
242
 
208
- creds = fetch_credentials
243
+ return crt_sign_request(request) if Signer.use_crt?
244
+
245
+ creds, _ = fetch_credentials
209
246
 
210
247
  http_method = extract_http_method(request)
211
248
  url = extract_url(request)
@@ -221,7 +258,14 @@ module Aws
221
258
  sigv4_headers = {}
222
259
  sigv4_headers['host'] = headers['host'] || host(url)
223
260
  sigv4_headers['x-amz-date'] = datetime
224
- sigv4_headers['x-amz-security-token'] = creds.session_token if creds.session_token
261
+ if creds.session_token
262
+ if @signing_algorithm == 'sigv4-s3express'.to_sym
263
+ sigv4_headers['x-amz-s3session-token'] = creds.session_token
264
+ else
265
+ sigv4_headers['x-amz-security-token'] = creds.session_token
266
+ end
267
+ end
268
+
225
269
  sigv4_headers['x-amz-content-sha256'] ||= content_sha256 if @apply_checksum_header
226
270
 
227
271
  headers = headers.merge(sigv4_headers) # merge so we do not modify given headers hash
@@ -283,7 +327,8 @@ module Aws
283
327
  # signature value (a binary string) used at ':chunk-signature' needs to converted to
284
328
  # hex-encoded string using #unpack
285
329
  def sign_event(prior_signature, payload, encoder)
286
- creds = fetch_credentials
330
+ # Note: CRT does not currently provide event stream signing, so we always use the ruby implementation.
331
+ creds, _ = fetch_credentials
287
332
  time = Time.now
288
333
  headers = {}
289
334
 
@@ -370,7 +415,9 @@ module Aws
370
415
  #
371
416
  def presign_url(options)
372
417
 
373
- creds = fetch_credentials
418
+ return crt_presign_url(options) if Signer.use_crt?
419
+
420
+ creds, expiration = fetch_credentials
374
421
 
375
422
  http_method = extract_http_method(options)
376
423
  url = extract_url(options)
@@ -390,8 +437,14 @@ module Aws
390
437
  params['X-Amz-Algorithm'] = 'AWS4-HMAC-SHA256'
391
438
  params['X-Amz-Credential'] = credential(creds, date)
392
439
  params['X-Amz-Date'] = datetime
393
- params['X-Amz-Expires'] = extract_expires_in(options)
394
- params['X-Amz-Security-Token'] = creds.session_token if creds.session_token
440
+ params['X-Amz-Expires'] = presigned_url_expiration(options, expiration, Time.strptime(datetime, "%Y%m%dT%H%M%S%Z")).to_s
441
+ if creds.session_token
442
+ if @signing_algorithm == 'sigv4-s3express'.to_sym
443
+ params['X-Amz-S3session-Token'] = creds.session_token
444
+ else
445
+ params['X-Amz-Security-Token'] = creds.session_token
446
+ end
447
+ end
395
448
  params['X-Amz-SignedHeaders'] = signed_headers(headers)
396
449
 
397
450
  params = params.map do |key, value|
@@ -493,7 +546,6 @@ module Aws
493
546
  hmac(k_credentials, string_to_sign)
494
547
  end
495
548
 
496
-
497
549
  def path(url)
498
550
  path = url.path
499
551
  path = '/' if path == ''
@@ -556,7 +608,7 @@ module Aws
556
608
  end
557
609
 
558
610
  def canonical_header_value(value)
559
- value.match(/^".*"$/) ? value : value.gsub(/\s+/, ' ').strip
611
+ value.gsub(/\s+/, ' ').strip
560
612
  end
561
613
 
562
614
  def host(uri)
@@ -649,8 +701,8 @@ module Aws
649
701
 
650
702
  def extract_expires_in(options)
651
703
  case options[:expires_in]
652
- when nil then 900.to_s
653
- when Integer then options[:expires_in].to_s
704
+ when nil then 900
705
+ when Integer then options[:expires_in]
654
706
  else
655
707
  msg = "expected :expires_in to be a number of seconds"
656
708
  raise ArgumentError, msg
@@ -665,11 +717,14 @@ module Aws
665
717
  self.class.uri_escape_path(string)
666
718
  end
667
719
 
668
-
669
720
  def fetch_credentials
670
721
  credentials = @credentials_provider.credentials
671
722
  if credentials_set?(credentials)
672
- credentials
723
+ expiration = nil
724
+ if @credentials_provider.respond_to?(:expiration)
725
+ expiration = @credentials_provider.expiration
726
+ end
727
+ [credentials, expiration]
673
728
  else
674
729
  raise Errors::MissingCredentialsError,
675
730
  'unable to sign request without credentials set'
@@ -687,8 +742,146 @@ module Aws
687
742
  !credentials.secret_access_key.empty?
688
743
  end
689
744
 
745
+ def presigned_url_expiration(options, expiration, datetime)
746
+ expires_in = extract_expires_in(options)
747
+ return expires_in unless expiration
748
+
749
+ expiration_seconds = (expiration - datetime).to_i
750
+ # In the static stability case, credentials may expire in the past
751
+ # but still be valid. For those cases, use the user configured
752
+ # expires_in and ingore expiration.
753
+ if expiration_seconds <= 0
754
+ expires_in
755
+ else
756
+ [expires_in, expiration_seconds].min
757
+ end
758
+ end
759
+
760
+ ### CRT Code
761
+
762
+ # the credentials used by CRT must be a
763
+ # CRT StaticCredentialsProvider object
764
+ def crt_fetch_credentials
765
+ creds, expiration = fetch_credentials
766
+ crt_creds = Aws::Crt::Auth::StaticCredentialsProvider.new(
767
+ creds.access_key_id,
768
+ creds.secret_access_key,
769
+ creds.session_token
770
+ )
771
+ [crt_creds, expiration]
772
+ end
773
+
774
+ def crt_sign_request(request)
775
+ creds, _ = crt_fetch_credentials
776
+ http_method = extract_http_method(request)
777
+ url = extract_url(request)
778
+ headers = downcase_headers(request[:headers])
779
+
780
+ datetime =
781
+ if headers.include? 'x-amz-date'
782
+ Time.parse(headers.delete('x-amz-date'))
783
+ end
784
+
785
+ content_sha256 = headers.delete('x-amz-content-sha256')
786
+ content_sha256 ||= sha256_hexdigest(request[:body] || '')
787
+
788
+ sigv4_headers = {}
789
+ sigv4_headers['host'] = headers['host'] || host(url)
790
+
791
+ # Modify the user-agent to add usage of crt-signer
792
+ # This should be temporary during developer preview only
793
+ if headers.include? 'user-agent'
794
+ headers['user-agent'] = "#{headers['user-agent']} crt-signer/#{@signing_algorithm}/#{Aws::Sigv4::VERSION}"
795
+ sigv4_headers['user-agent'] = headers['user-agent']
796
+ end
797
+
798
+ headers = headers.merge(sigv4_headers) # merge so we do not modify given headers hash
799
+
800
+ config = Aws::Crt::Auth::SigningConfig.new(
801
+ algorithm: @signing_algorithm,
802
+ signature_type: :http_request_headers,
803
+ region: @region,
804
+ service: @service,
805
+ date: datetime,
806
+ signed_body_value: content_sha256,
807
+ signed_body_header_type: @apply_checksum_header ?
808
+ :sbht_content_sha256 : :sbht_none,
809
+ credentials: creds,
810
+ unsigned_headers: @unsigned_headers,
811
+ use_double_uri_encode: @uri_escape_path,
812
+ should_normalize_uri_path: @normalize_path,
813
+ omit_session_token: @omit_session_token
814
+ )
815
+ http_request = Aws::Crt::Http::Message.new(
816
+ http_method, url.to_s, headers
817
+ )
818
+ signable = Aws::Crt::Auth::Signable.new(http_request)
819
+
820
+ signing_result = Aws::Crt::Auth::Signer.sign_request(config, signable)
821
+
822
+ Signature.new(
823
+ headers: sigv4_headers.merge(
824
+ downcase_headers(signing_result[:headers])
825
+ ),
826
+ string_to_sign: 'CRT_INTERNAL',
827
+ canonical_request: 'CRT_INTERNAL',
828
+ content_sha256: content_sha256,
829
+ extra: {config: config, signable: signable}
830
+ )
831
+ end
832
+
833
+ def crt_presign_url(options)
834
+ creds, expiration = crt_fetch_credentials
835
+
836
+ http_method = extract_http_method(options)
837
+ url = extract_url(options)
838
+ headers = downcase_headers(options[:headers])
839
+ headers['host'] ||= host(url)
840
+
841
+ datetime = Time.strptime(headers.delete('x-amz-date'), "%Y%m%dT%H%M%S%Z") if headers['x-amz-date']
842
+ datetime ||= (options[:time] || Time.now)
843
+
844
+ content_sha256 = headers.delete('x-amz-content-sha256')
845
+ content_sha256 ||= options[:body_digest]
846
+ content_sha256 ||= sha256_hexdigest(options[:body] || '')
847
+
848
+ config = Aws::Crt::Auth::SigningConfig.new(
849
+ algorithm: @signing_algorithm,
850
+ signature_type: :http_request_query_params,
851
+ region: @region,
852
+ service: @service,
853
+ date: datetime,
854
+ signed_body_value: content_sha256,
855
+ signed_body_header_type: @apply_checksum_header ?
856
+ :sbht_content_sha256 : :sbht_none,
857
+ credentials: creds,
858
+ unsigned_headers: @unsigned_headers,
859
+ use_double_uri_encode: @uri_escape_path,
860
+ should_normalize_uri_path: @normalize_path,
861
+ omit_session_token: @omit_session_token,
862
+ expiration_in_seconds: presigned_url_expiration(options, expiration, datetime)
863
+ )
864
+ http_request = Aws::Crt::Http::Message.new(
865
+ http_method, url.to_s, headers
866
+ )
867
+ signable = Aws::Crt::Auth::Signable.new(http_request)
868
+
869
+ signing_result = Aws::Crt::Auth::Signer.sign_request(config, signable, http_method, url.to_s)
870
+ url = URI.parse(signing_result[:path])
871
+
872
+ if options[:extra] && options[:extra].is_a?(Hash)
873
+ options[:extra][:config] = config
874
+ options[:extra][:signable] = signable
875
+ end
876
+ url
877
+ end
878
+
690
879
  class << self
691
880
 
881
+ def use_crt?
882
+ @@use_crt
883
+ end
884
+
692
885
  # @api private
693
886
  def uri_escape_path(path)
694
887
  path.gsub(/[^\/]+/) { |part| uri_escape(part) }
data/lib/aws-sigv4.rb CHANGED
@@ -4,3 +4,9 @@ require_relative 'aws-sigv4/credentials'
4
4
  require_relative 'aws-sigv4/errors'
5
5
  require_relative 'aws-sigv4/signature'
6
6
  require_relative 'aws-sigv4/signer'
7
+
8
+ module Aws
9
+ module Sigv4
10
+ VERSION = File.read(File.expand_path('../VERSION', __dir__)).strip
11
+ end
12
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aws-sigv4
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Amazon Web Services
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-01 00:00:00.000000000 Z
11
+ date: 2023-11-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-eventstream
@@ -60,7 +60,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
60
60
  requirements:
61
61
  - - ">="
62
62
  - !ruby/object:Gem::Version
63
- version: '2.3'
63
+ version: '2.5'
64
64
  required_rubygems_version: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ">="