aws-sigv4 1.2.4 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9e0fc072d8221d8d004b09d7ceac68f26d3b91d42fcee9e53372e816ba5a1920
4
- data.tar.gz: e0bd2e0c241be2c37d768f15b3d1cdecd74ddb2783ef570858949e4de0fd0fbc
3
+ metadata.gz: 482b4ffa8bd9e9e2d7dab0d61ab15553f0e8d05e1be2923b388157664a47a9fa
4
+ data.tar.gz: c5caa84527ca213826f8c802195430caacb25750530609f1b8d7267810808574
5
5
  SHA512:
6
- metadata.gz: a51ab68529670a8648fd62ea1b72f2bacc8c0c2e43dc46cb1b87e0a7fc1609630f14aad6a440c9ce6e9610ae733300a752f034a3d90cb7042e78e28715617a12
7
- data.tar.gz: 37023bc487a2b7249518a04fecffb286a6370e11ddb5d820fdffac7fa08210a01dd7b77c7d1e335594f01b8347951372b40231c16c41da36b98d1293a6ed4f21
6
+ metadata.gz: c16c5df7f8c6ca10cf073c25984506ce938f26823f99d82813b5bde3fb283d23a3c480adec2945b98975946a7b32efe57a0058cd65bb383606c5ee5228711381
7
+ data.tar.gz: b72ea1894eb1c419179325f8e715fb454ab02ae2d1ac243b90ed2f4032c0fd966ba157287a12009587908d0a6a2f62261c78e319a230196792b6ec4206a20718
data/CHANGELOG.md CHANGED
@@ -1,6 +1,51 @@
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
+
44
+ 1.3.0 (2021-09-01)
45
+ ------------------
46
+
47
+ * Feature - AWS SDK for Ruby no longer supports Ruby runtime versions 1.9, 2.0, 2.1, and 2.2.
48
+
4
49
  1.2.4 (2021-07-08)
5
50
  ------------------
6
51
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.2.4
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.2.4
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-07-08 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: '0'
63
+ version: '2.5'
64
64
  required_rubygems_version: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ">="