aws-sdk-resources 2.11.561 → 2.11.566

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.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/lib/aws-sdk-resources/services/s3.rb +1 -0
  3. data/lib/aws-sdk-resources/services/s3/encryption.rb +3 -0
  4. data/lib/aws-sdk-resources/services/s3/encryption/client.rb +24 -7
  5. data/lib/aws-sdk-resources/services/s3/encryption/decrypt_handler.rb +77 -26
  6. data/lib/aws-sdk-resources/services/s3/encryption/default_cipher_provider.rb +43 -5
  7. data/lib/aws-sdk-resources/services/s3/encryption/default_key_provider.rb +2 -0
  8. data/lib/aws-sdk-resources/services/s3/encryption/encrypt_handler.rb +13 -2
  9. data/lib/aws-sdk-resources/services/s3/encryption/errors.rb +2 -0
  10. data/lib/aws-sdk-resources/services/s3/encryption/io_auth_decrypter.rb +2 -0
  11. data/lib/aws-sdk-resources/services/s3/encryption/io_decrypter.rb +11 -3
  12. data/lib/aws-sdk-resources/services/s3/encryption/io_encrypter.rb +2 -0
  13. data/lib/aws-sdk-resources/services/s3/encryption/key_provider.rb +2 -0
  14. data/lib/aws-sdk-resources/services/s3/encryption/kms_cipher_provider.rb +36 -3
  15. data/lib/aws-sdk-resources/services/s3/encryption/materials.rb +8 -6
  16. data/lib/aws-sdk-resources/services/s3/encryption/utils.rb +25 -0
  17. data/lib/aws-sdk-resources/services/s3/encryptionV2/client.rb +561 -0
  18. data/lib/aws-sdk-resources/services/s3/encryptionV2/decrypt_handler.rb +214 -0
  19. data/lib/aws-sdk-resources/services/s3/encryptionV2/default_cipher_provider.rb +170 -0
  20. data/lib/aws-sdk-resources/services/s3/encryptionV2/default_key_provider.rb +40 -0
  21. data/lib/aws-sdk-resources/services/s3/encryptionV2/encrypt_handler.rb +69 -0
  22. data/lib/aws-sdk-resources/services/s3/encryptionV2/errors.rb +37 -0
  23. data/lib/aws-sdk-resources/services/s3/encryptionV2/io_auth_decrypter.rb +58 -0
  24. data/lib/aws-sdk-resources/services/s3/encryptionV2/io_decrypter.rb +37 -0
  25. data/lib/aws-sdk-resources/services/s3/encryptionV2/io_encrypter.rb +73 -0
  26. data/lib/aws-sdk-resources/services/s3/encryptionV2/key_provider.rb +31 -0
  27. data/lib/aws-sdk-resources/services/s3/encryptionV2/kms_cipher_provider.rb +169 -0
  28. data/lib/aws-sdk-resources/services/s3/encryptionV2/materials.rb +60 -0
  29. data/lib/aws-sdk-resources/services/s3/encryptionV2/utils.rb +103 -0
  30. data/lib/aws-sdk-resources/services/s3/encryption_v2.rb +24 -0
  31. metadata +18 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3f6cb647e78df5134e70a85eb69e16c3bb862b5fdb1c133a399931d522154798
4
- data.tar.gz: 60beb67956debde6bca7341893d5e471b709a3c57de18c4aeec00ab83c9f4dd7
3
+ metadata.gz: a053b10dceb940a82b9af9b44a0c545d8dc177ed74b4c0f60aa046899a3e5167
4
+ data.tar.gz: 7d801de7114f99cb717865d593f9ee074ae783602e4847d0296c8346811ce539
5
5
  SHA512:
6
- metadata.gz: 7c6ed565e5f5462775e10bba44765e2f2def629bd2451f137b1ec0d0fed6918d0c604db95b480ec5cbde59c2eeea58b860a8f3d3c88f3a74fb61a50c39c204ba
7
- data.tar.gz: 9f64cbd1f823f98c362098b6a7816ee4db6efdc26483ff13e3e563383a62be93ae830a0a8b54e68b1df4dcd0adccc17130d926316b473c12d9ef6e385fde129a
6
+ metadata.gz: bcfde9c4b3155f005d3708eb780bc9bf3ee6cab676c48f76a45da64fca8c470fdfdf5aef7313188fcabfcc532efd20b57e9edb27de0956d40b7481f79e7364ff
7
+ data.tar.gz: 5f5209c5385cf8f671f8ec6c2e13f9b1477111dd6297925527b50c975338cf7322fe8610d4e7d04309ea791b9f7008fe2bbd191c065fd47720c400fd962e3ebf
@@ -7,6 +7,7 @@ module Aws
7
7
  require 'aws-sdk-resources/services/s3/multipart_upload'
8
8
 
9
9
  autoload :Encryption, 'aws-sdk-resources/services/s3/encryption'
10
+ autoload :EncryptionV2, 'aws-sdk-resources/services/s3/encryption_v2'
10
11
  autoload :FilePart, 'aws-sdk-resources/services/s3/file_part'
11
12
  autoload :FileUploader, 'aws-sdk-resources/services/s3/file_uploader'
12
13
  autoload :FileDownloader, 'aws-sdk-resources/services/s3/file_downloader'
@@ -2,6 +2,9 @@ module Aws
2
2
  module S3
3
3
  module Encryption
4
4
 
5
+ AES_GCM_TAG_LEN_BYTES = 16
6
+ EC_USER_AGENT = 'S3CryptoV1n'
7
+
5
8
  autoload :Client, 'aws-sdk-resources/services/s3/encryption/client'
6
9
  autoload :DecryptHandler, 'aws-sdk-resources/services/s3/encryption/decrypt_handler'
7
10
  autoload :DefaultCipherProvider, 'aws-sdk-resources/services/s3/encryption/default_cipher_provider'
@@ -1,6 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'forwardable'
4
+
1
5
  module Aws
2
6
  module S3
3
7
 
8
+ # [MAINTENANCE MODE] There is a new version of the Encryption Client.
9
+ # AWS strongly recommends upgrading to the {Aws::S3::EncryptionV2::Client},
10
+ # which provides updated data security best practices.
11
+ # See documentation for {Aws::S3::EncryptionV2::Client}.
4
12
  # Provides an encryption client that encrypts and decrypts data client-side,
5
13
  # storing the encrypted data in Amazon S3.
6
14
  #
@@ -26,7 +34,7 @@ module Aws
26
34
  # data client-side.
27
35
  #
28
36
  # One of the benefits of envelope encryption is that if your master key
29
- # is compromised, you have the option of jut re-encrypting the stored
37
+ # is compromised, you have the option of just re-encrypting the stored
30
38
  # envelope symmetric keys, instead of re-encrypting all of the
31
39
  # data in your account.
32
40
  #
@@ -178,15 +186,17 @@ module Aws
178
186
  class Client
179
187
 
180
188
  extend Deprecations
189
+ extend Forwardable
190
+ def_delegators :@client, :config, :delete_object, :head_object, :build_request
181
191
 
182
- # Creates a new encryption client. You must provide on of the following
192
+ # Creates a new encryption client. You must provide one of the following
183
193
  # options:
184
194
  #
185
195
  # * `:encryption_key`
186
196
  # * `:kms_key_id`
187
197
  # * `:key_provider`
188
198
  #
189
- # You may also pass any other options accepted by {S3::Client#initialize}.
199
+ # You may also pass any other options accepted by `Client#initialize`.
190
200
  #
191
201
  # @option options [S3::Client] :client A basic S3 client that is used
192
202
  # to make api calls. If a `:client` is not provided, a new {S3::Client}
@@ -223,6 +233,13 @@ module Aws
223
233
  @envelope_location = extract_location(options)
224
234
  @instruction_file_suffix = extract_suffix(options)
225
235
  end
236
+ deprecated :initialize,
237
+ message:
238
+ '[MAINTENANCE MODE] This version of the S3 Encryption client is currently in maintenance mode. ' \
239
+ 'AWS strongly recommends upgrading to the Aws::S3::EncryptionV2::Client, ' \
240
+ 'which provides updated data security best practices. ' \
241
+ 'See documentation for Aws::S3::EncryptionV2::Client.'
242
+
226
243
 
227
244
  # @return [S3::Client]
228
245
  attr_reader :client
@@ -327,7 +344,7 @@ module Aws
327
344
  elsif options[:encryption_key]
328
345
  DefaultKeyProvider.new(options)
329
346
  else
330
- msg = "you must pass a :kms_key_id, :key_provider, or :encryption_key"
347
+ msg = 'you must pass a :kms_key_id, :key_provider, or :encryption_key'
331
348
  raise ArgumentError, msg
332
349
  end
333
350
  end
@@ -347,8 +364,8 @@ module Aws
347
364
  if [:metadata, :instruction_file].include?(location)
348
365
  location
349
366
  else
350
- msg = ":envelope_location must be :metadata or :instruction_file "
351
- msg << "got #{location.inspect}"
367
+ msg = ':envelope_location must be :metadata or :instruction_file '\
368
+ "got #{location.inspect}"
352
369
  raise ArgumentError, msg
353
370
  end
354
371
  end
@@ -358,7 +375,7 @@ module Aws
358
375
  if String === suffix
359
376
  suffix
360
377
  else
361
- msg = ":instruction_file_suffix must be a String"
378
+ msg = ':instruction_file_suffix must be a String'
362
379
  raise ArgumentError, msg
363
380
  end
364
381
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'base64'
2
4
 
3
5
  module Aws
@@ -5,6 +7,7 @@ module Aws
5
7
  module Encryption
6
8
  # @api private
7
9
  class DecryptHandler < Seahorse::Client::Handler
10
+ @@warned_response_target_proc = false
8
11
 
9
12
  V1_ENVELOPE_KEYS = %w(
10
13
  x-amz-key
@@ -20,15 +23,39 @@ module Aws
20
23
  x-amz-matdesc
21
24
  )
22
25
 
23
- POSSIBLE_ENVELOPE_KEYS = (V1_ENVELOPE_KEYS + V2_ENVELOPE_KEYS).uniq
26
+ V2_OPTIONAL_KEYS = %w(x-amz-tag-len)
27
+
28
+ POSSIBLE_ENVELOPE_KEYS = (V1_ENVELOPE_KEYS +
29
+ V2_ENVELOPE_KEYS + V2_OPTIONAL_KEYS).uniq
30
+
31
+ POSSIBLE_WRAPPING_FORMATS = %w(
32
+ AES/GCM
33
+ kms
34
+ kms+context
35
+ RSA-OAEP-SHA1
36
+ )
24
37
 
25
38
  POSSIBLE_ENCRYPTION_FORMATS = %w(
26
39
  AES/GCM/NoPadding
27
40
  AES/CBC/PKCS5Padding
41
+ AES/CBC/PKCS7Padding
28
42
  )
29
43
 
44
+ AUTH_REQUIRED_CEK_ALGS = %w(AES/GCM/NoPadding)
45
+
30
46
  def call(context)
31
47
  attach_http_event_listeners(context)
48
+ apply_cse_user_agent(context)
49
+
50
+ if context[:response_target].is_a?(Proc) && !@@warned_response_target_proc
51
+ @@warned_response_target_proc = true
52
+ warn(':response_target is a Proc, or a block was provided. ' \
53
+ 'Read the entire object to the ' \
54
+ 'end before you start using the decrypted data. This is to ' \
55
+ 'verify that the object has not been modified since it ' \
56
+ 'was encrypted.')
57
+ end
58
+
32
59
  @handler.call(context)
33
60
  end
34
61
 
@@ -37,9 +64,9 @@ module Aws
37
64
  def attach_http_event_listeners(context)
38
65
 
39
66
  context.http_response.on_headers(200) do
40
- cipher = decryption_cipher(context)
41
- decrypter = body_contains_auth_tag?(context) ?
42
- authenticated_decrypter(context, cipher) :
67
+ cipher, envelope = decryption_cipher(context)
68
+ decrypter = body_contains_auth_tag?(envelope) ?
69
+ authenticated_decrypter(context, cipher, envelope) :
43
70
  IODecrypter.new(cipher, context.http_response.body)
44
71
  context.http_response.body = decrypter
45
72
  end
@@ -59,8 +86,13 @@ module Aws
59
86
  end
60
87
 
61
88
  def decryption_cipher(context)
62
- if envelope = get_encryption_envelope(context)
63
- context[:encryption][:cipher_provider].decryption_cipher(envelope)
89
+ if (envelope = get_encryption_envelope(context))
90
+ cipher = context[:encryption][:cipher_provider]
91
+ .decryption_cipher(
92
+ envelope,
93
+ context[:encryption]
94
+ )
95
+ [cipher, envelope]
64
96
  else
65
97
  raise Errors::DecryptionError, "unable to locate encryption envelope"
66
98
  end
@@ -96,13 +128,12 @@ module Aws
96
128
  end
97
129
 
98
130
  def extract_envelope(hash)
131
+ return nil unless hash
99
132
  return v1_envelope(hash) if hash.key?('x-amz-key')
100
133
  return v2_envelope(hash) if hash.key?('x-amz-key-v2')
101
134
  if hash.keys.any? { |key| key.match(/^x-amz-key-(.+)$/) }
102
135
  msg = "unsupported envelope encryption version #{$1}"
103
136
  raise Errors::DecryptionError, msg
104
- else
105
- nil # no envelope found
106
137
  end
107
138
  end
108
139
 
@@ -116,35 +147,31 @@ module Aws
116
147
  msg = "unsupported content encrypting key (cek) format: #{alg}"
117
148
  raise Errors::DecryptionError, msg
118
149
  end
119
- unless envelope['x-amz-wrap-alg'] == 'kms'
120
- # possible to support
121
- # RSA/ECB/OAEPWithSHA-256AndMGF1Padding
150
+ unless POSSIBLE_WRAPPING_FORMATS.include? envelope['x-amz-wrap-alg']
122
151
  alg = envelope['x-amz-wrap-alg'].inspect
123
152
  msg = "unsupported key wrapping algorithm: #{alg}"
124
153
  raise Errors::DecryptionError, msg
125
154
  end
126
- unless V2_ENVELOPE_KEYS.sort == envelope.keys.sort
155
+ unless (missing_keys = V2_ENVELOPE_KEYS - envelope.keys).empty?
127
156
  msg = "incomplete v2 encryption envelope:\n"
128
- msg += " expected: #{V2_ENVELOPE_KEYS.join(',')}\n"
129
- msg += " got: #{envelope_keys.join(', ')}"
157
+ msg += " missing: #{missing_keys.join(',')}\n"
130
158
  raise Errors::DecryptionError, msg
131
159
  end
132
160
  envelope
133
161
  end
134
162
 
135
- # When the x-amz-meta-x-amz-tag-len header is present, it indicates
136
- # that the body of this object has a trailing auth tag. The header
137
- # indicates the length of that tag.
138
- #
139
163
  # This method fetches the tag from the end of the object by
140
164
  # making a GET Object w/range request. This auth tag is used
141
165
  # to initialize the cipher, and the decrypter truncates the
142
166
  # auth tag from the body when writing the final bytes.
143
- def authenticated_decrypter(context, cipher)
167
+ def authenticated_decrypter(context, cipher, envelope)
168
+ if RUBY_VERSION.match(/1.9/)
169
+ raise "authenticated decryption not supported by OpenSSL in Ruby version ~> 1.9"
170
+ raise Aws::Errors::NonSupportedRubyVersionError, msg
171
+ end
144
172
  http_resp = context.http_response
145
173
  content_length = http_resp.headers['content-length'].to_i
146
- auth_tag_length = http_resp.headers['x-amz-meta-x-amz-tag-len']
147
- auth_tag_length = auth_tag_length.to_i / 8
174
+ auth_tag_length = auth_tag_length(envelope)
148
175
 
149
176
  auth_tag = context.client.get_object(
150
177
  bucket: context.params[:bucket],
@@ -156,16 +183,40 @@ module Aws
156
183
  cipher.auth_data = ''
157
184
 
158
185
  # The encrypted object contains both the cipher text
159
- # plus a trailing auth tag. This decrypter will the body
160
- # expect for the trailing auth tag.
161
- decrypter = IOAuthDecrypter.new(
186
+ # plus a trailing auth tag.
187
+ IOAuthDecrypter.new(
162
188
  io: http_resp.body,
163
189
  encrypted_content_length: content_length - auth_tag_length,
164
190
  cipher: cipher)
165
191
  end
166
192
 
167
- def body_contains_auth_tag?(context)
168
- context.http_response.headers['x-amz-meta-x-amz-tag-len']
193
+ def body_contains_auth_tag?(envelope)
194
+ AUTH_REQUIRED_CEK_ALGS.include?(envelope['x-amz-cek-alg'])
195
+ end
196
+
197
+ # Determine the auth tag length from the algorithm
198
+ # Validate it against the value provided in the x-amz-tag-len
199
+ # Return the tag length in bytes
200
+ def auth_tag_length(envelope)
201
+ tag_length =
202
+ case envelope['x-amz-cek-alg']
203
+ when 'AES/GCM/NoPadding' then AES_GCM_TAG_LEN_BYTES
204
+ else
205
+ raise ArgumentError, 'Unsupported cek-alg: ' \
206
+ "#{envelope['x-amz-cek-alg']}"
207
+ end
208
+ if (tag_length * 8) != envelope['x-amz-tag-len'].to_i
209
+ raise Errors::DecryptionError, 'x-amz-tag-len does not match expected'
210
+ end
211
+ tag_length
212
+ end
213
+
214
+ def apply_cse_user_agent(context)
215
+ if context.config.user_agent_suffix.nil?
216
+ context.config.user_agent_suffix = EC_USER_AGENT
217
+ elsif !context.config.user_agent_suffix.include? EC_USER_AGENT
218
+ context.config.user_agent_suffix += " #{EC_USER_AGENT}"
219
+ end
169
220
  end
170
221
 
171
222
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'base64'
2
4
 
3
5
  module Aws
@@ -24,11 +26,48 @@ module Aws
24
26
 
25
27
  # @return [Cipher] Given an encryption envelope, returns a
26
28
  # decryption cipher.
27
- def decryption_cipher(envelope)
29
+ def decryption_cipher(envelope, options = {})
28
30
  master_key = @key_provider.key_for(envelope['x-amz-matdesc'])
29
- key = Utils.decrypt(master_key, decode64(envelope['x-amz-key']))
30
- iv = decode64(envelope['x-amz-iv'])
31
- Utils.aes_decryption_cipher(:CBC, key, iv)
31
+ if envelope.key? 'x-amz-key'
32
+ # Support for decryption of legacy objects
33
+ key = Utils.decrypt(master_key, decode64(envelope['x-amz-key']))
34
+ iv = decode64(envelope['x-amz-iv'])
35
+ Utils.aes_decryption_cipher(:CBC, key, iv)
36
+ else
37
+ if envelope['x-amz-cek-alg'] != 'AES/GCM/NoPadding'
38
+ raise ArgumentError, 'Unsupported cek-alg: ' \
39
+ "#{envelope['x-amz-cek-alg']}"
40
+ end
41
+ key =
42
+ case envelope['x-amz-wrap-alg']
43
+ when 'AES/GCM'
44
+ if master_key.is_a? OpenSSL::PKey::RSA
45
+ raise ArgumentError, 'Key mismatch - Client is configured' \
46
+ ' with an RSA key and the x-amz-wrap-alg is AES/GCM.'
47
+ end
48
+ Utils.decrypt_aes_gcm(master_key,
49
+ decode64(envelope['x-amz-key-v2']),
50
+ envelope['x-amz-cek-alg'])
51
+ when 'RSA-OAEP-SHA1'
52
+ unless master_key.is_a? OpenSSL::PKey::RSA
53
+ raise ArgumentError, 'Key mismatch - Client is configured' \
54
+ ' with an AES key and the x-amz-wrap-alg is RSA-OAEP-SHA1.'
55
+ end
56
+ key, cek_alg = Utils.decrypt_rsa(master_key, decode64(envelope['x-amz-key-v2']))
57
+ raise Errors::DecryptionError unless cek_alg == envelope['x-amz-cek-alg']
58
+ key
59
+ when 'kms+context'
60
+ raise ArgumentError, 'Key mismatch - Client is configured' \
61
+ ' with a user provided key and the x-amz-wrap-alg is' \
62
+ ' kms+context. Please configure the client with the' \
63
+ ' required kms_key_id'
64
+ else
65
+ raise ArgumentError, 'Unsupported wrap-alg: ' \
66
+ "#{envelope['x-amz-wrap-alg']}"
67
+ end
68
+ iv = decode64(envelope['x-amz-iv'])
69
+ Utils.aes_decryption_cipher(:GCM, key, iv)
70
+ end
32
71
  end
33
72
 
34
73
  private
@@ -56,7 +95,6 @@ module Aws
56
95
  def decode64(str)
57
96
  Base64.decode64(str)
58
97
  end
59
-
60
98
  end
61
99
  end
62
100
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aws
2
4
  module S3
3
5
  module Encryption
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'base64'
2
4
 
3
5
  module Aws
@@ -10,6 +12,7 @@ module Aws
10
12
  envelope, cipher = context[:encryption][:cipher_provider].encryption_cipher
11
13
  apply_encryption_envelope(context, envelope, cipher)
12
14
  apply_encryption_cipher(context, cipher)
15
+ apply_cse_user_agent(context)
13
16
  @handler.call(context)
14
17
  end
15
18
 
@@ -36,14 +39,22 @@ module Aws
36
39
  context.params[:body] = IOEncrypter.new(cipher, io)
37
40
  context.params[:metadata] ||= {}
38
41
  context.params[:metadata]['x-amz-unencrypted-content-length'] = io.size
39
- if md5 = context.params.delete(:content_md5)
40
- context.params[:metadata]['x-amz-unencrypted-content-md5'] = md5
42
+ if context.params.delete(:content_md5)
43
+ warn('Setting content_md5 on client side encrypted objects is deprecated')
41
44
  end
42
45
  context.http_response.on_headers do
43
46
  context.params[:body].close
44
47
  end
45
48
  end
46
49
 
50
+ def apply_cse_user_agent(context)
51
+ if context.config.user_agent_suffix.nil?
52
+ context.config.user_agent_suffix = EC_USER_AGENT
53
+ elsif !context.config.user_agent_suffix.include? EC_USER_AGENT
54
+ context.config.user_agent_suffix += " #{EC_USER_AGENT}"
55
+ end
56
+ end
57
+
47
58
  end
48
59
  end
49
60
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aws
2
4
  module S3
3
5
  module Encryption
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aws
2
4
  module S3
3
5
  module Encryption
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aws
2
4
  module S3
3
5
  module Encryption
@@ -7,8 +9,10 @@ module Aws
7
9
  # @param [OpenSSL::Cipher] cipher
8
10
  # @param [IO#write] io An IO-like object that responds to `#write`.
9
11
  def initialize(cipher, io)
10
- @cipher = cipher.clone
11
- @io = io
12
+ @cipher = cipher
13
+ # Ensure that IO is reset between retries
14
+ @io = io.tap { |io| io.truncate(0) if io.respond_to?(:truncate) }
15
+ @cipher_buffer = String.new
12
16
  end
13
17
 
14
18
  # @return [#write]
@@ -16,7 +20,11 @@ module Aws
16
20
 
17
21
  def write(chunk)
18
22
  # decrypt and write
19
- @io.write(@cipher.update(chunk))
23
+ if @cipher.method(:update).arity == 1
24
+ @io.write(@cipher.update(chunk))
25
+ else
26
+ @io.write(@cipher.update(chunk, @cipher_buffer))
27
+ end
20
28
  end
21
29
 
22
30
  def finalize