aws-sdk-resources 2.11.557 → 2.11.562

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 +65 -25
  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 +8 -1
  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 +559 -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: 2b039b4e31ef1b4bc3318a24b4584b8dfa607e6c69ad16f4aa73078768a1fdc3
4
- data.tar.gz: 499e1f15422d07cd406271cdad756c3ceb5be3cdc4a790f11ee19d2007636750
3
+ metadata.gz: b6ac0e253aa72fe0c66930c23a50c0f1e944e79e500febf995d11aba32bc80a7
4
+ data.tar.gz: 8f60593f8927f98912a6655231a22cdfadc747e276d0f1ef277835f0a8dc3163
5
5
  SHA512:
6
- metadata.gz: be80f368802ec1fa74635d5ad887bc1d514442c8d985a41e4036f64b0543b5b91d354b7e457f772798ef1e34fc93f536e35ffbeaf70f11d2f000e4e28bac519d
7
- data.tar.gz: 3055b3844805bffe3364b67d7531bce3c0ecbe301a8b651946cf2a864ec77764a575bf9b39add4d1431a1ac46df0575cbbe27c8b9e546fd38bd131048e714b4c
6
+ metadata.gz: c6d6b6eddfead95cc30c36f3d6d760353edcea6387d4213c2164d5b5afa063836cf24c929e9f1d9efa4f1ce8b0a1ca1d53b6376531d494c11e38cf0056f57ac2
7
+ data.tar.gz: 4df465e650e56888d180f7c9b3adea51f4be02b5b456eb971a4c8193948aa0dd6a67cec90bc35cc3bc0b654599dbc2365fad31247df232d2b637dea7e9575287
@@ -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
@@ -20,15 +22,29 @@ module Aws
20
22
  x-amz-matdesc
21
23
  )
22
24
 
23
- POSSIBLE_ENVELOPE_KEYS = (V1_ENVELOPE_KEYS + V2_ENVELOPE_KEYS).uniq
25
+ V2_OPTIONAL_KEYS = %w(x-amz-tag-len)
26
+
27
+ POSSIBLE_ENVELOPE_KEYS = (V1_ENVELOPE_KEYS +
28
+ V2_ENVELOPE_KEYS + V2_OPTIONAL_KEYS).uniq
29
+
30
+ POSSIBLE_WRAPPING_FORMATS = %w(
31
+ AES/GCM
32
+ kms
33
+ kms+context
34
+ RSA-OAEP-SHA1
35
+ )
24
36
 
25
37
  POSSIBLE_ENCRYPTION_FORMATS = %w(
26
38
  AES/GCM/NoPadding
27
39
  AES/CBC/PKCS5Padding
40
+ AES/CBC/PKCS7Padding
28
41
  )
29
42
 
43
+ AUTH_REQUIRED_CEK_ALGS = %w(AES/GCM/NoPadding)
44
+
30
45
  def call(context)
31
46
  attach_http_event_listeners(context)
47
+ apply_cse_user_agent(context)
32
48
  @handler.call(context)
33
49
  end
34
50
 
@@ -37,9 +53,9 @@ module Aws
37
53
  def attach_http_event_listeners(context)
38
54
 
39
55
  context.http_response.on_headers(200) do
40
- cipher = decryption_cipher(context)
41
- decrypter = body_contains_auth_tag?(context) ?
42
- authenticated_decrypter(context, cipher) :
56
+ cipher, envelope = decryption_cipher(context)
57
+ decrypter = body_contains_auth_tag?(envelope) ?
58
+ authenticated_decrypter(context, cipher, envelope) :
43
59
  IODecrypter.new(cipher, context.http_response.body)
44
60
  context.http_response.body = decrypter
45
61
  end
@@ -60,7 +76,12 @@ module Aws
60
76
 
61
77
  def decryption_cipher(context)
62
78
  if envelope = get_encryption_envelope(context)
63
- context[:encryption][:cipher_provider].decryption_cipher(envelope)
79
+ cipher = context[:encryption][:cipher_provider]
80
+ .decryption_cipher(
81
+ envelope,
82
+ kms_encryption_context: context[:encryption][:kms_encryption_context]
83
+ )
84
+ [cipher, envelope]
64
85
  else
65
86
  raise Errors::DecryptionError, "unable to locate encryption envelope"
66
87
  end
@@ -96,13 +117,12 @@ module Aws
96
117
  end
97
118
 
98
119
  def extract_envelope(hash)
120
+ return nil unless hash
99
121
  return v1_envelope(hash) if hash.key?('x-amz-key')
100
122
  return v2_envelope(hash) if hash.key?('x-amz-key-v2')
101
123
  if hash.keys.any? { |key| key.match(/^x-amz-key-(.+)$/) }
102
124
  msg = "unsupported envelope encryption version #{$1}"
103
125
  raise Errors::DecryptionError, msg
104
- else
105
- nil # no envelope found
106
126
  end
107
127
  end
108
128
 
@@ -116,35 +136,31 @@ module Aws
116
136
  msg = "unsupported content encrypting key (cek) format: #{alg}"
117
137
  raise Errors::DecryptionError, msg
118
138
  end
119
- unless envelope['x-amz-wrap-alg'] == 'kms'
120
- # possible to support
121
- # RSA/ECB/OAEPWithSHA-256AndMGF1Padding
139
+ unless POSSIBLE_WRAPPING_FORMATS.include? envelope['x-amz-wrap-alg']
122
140
  alg = envelope['x-amz-wrap-alg'].inspect
123
141
  msg = "unsupported key wrapping algorithm: #{alg}"
124
142
  raise Errors::DecryptionError, msg
125
143
  end
126
- unless V2_ENVELOPE_KEYS.sort == envelope.keys.sort
144
+ unless (missing_keys = V2_ENVELOPE_KEYS - envelope.keys).empty?
127
145
  msg = "incomplete v2 encryption envelope:\n"
128
- msg += " expected: #{V2_ENVELOPE_KEYS.join(',')}\n"
129
- msg += " got: #{envelope_keys.join(', ')}"
146
+ msg += " missing: #{missing_keys.join(',')}\n"
130
147
  raise Errors::DecryptionError, msg
131
148
  end
132
149
  envelope
133
150
  end
134
151
 
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
152
  # This method fetches the tag from the end of the object by
140
153
  # making a GET Object w/range request. This auth tag is used
141
154
  # to initialize the cipher, and the decrypter truncates the
142
155
  # auth tag from the body when writing the final bytes.
143
- def authenticated_decrypter(context, cipher)
156
+ def authenticated_decrypter(context, cipher, envelope)
157
+ if RUBY_VERSION.match(/1.9/)
158
+ raise "authenticated decryption not supported by OpenSSL in Ruby version ~> 1.9"
159
+ raise Aws::Errors::NonSupportedRubyVersionError, msg
160
+ end
144
161
  http_resp = context.http_response
145
162
  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
163
+ auth_tag_length = auth_tag_length(envelope)
148
164
 
149
165
  auth_tag = context.client.get_object(
150
166
  bucket: context.params[:bucket],
@@ -156,16 +172,40 @@ module Aws
156
172
  cipher.auth_data = ''
157
173
 
158
174
  # 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(
175
+ # plus a trailing auth tag.
176
+ IOAuthDecrypter.new(
162
177
  io: http_resp.body,
163
178
  encrypted_content_length: content_length - auth_tag_length,
164
179
  cipher: cipher)
165
180
  end
166
181
 
167
- def body_contains_auth_tag?(context)
168
- context.http_response.headers['x-amz-meta-x-amz-tag-len']
182
+ def body_contains_auth_tag?(envelope)
183
+ AUTH_REQUIRED_CEK_ALGS.include?(envelope['x-amz-cek-alg'])
184
+ end
185
+
186
+ # Determine the auth tag length from the algorithm
187
+ # Validate it against the value provided in the x-amz-tag-len
188
+ # Return the tag length in bytes
189
+ def auth_tag_length(envelope)
190
+ tag_length =
191
+ case envelope['x-amz-cek-alg']
192
+ when 'AES/GCM/NoPadding' then AES_GCM_TAG_LEN_BYTES
193
+ else
194
+ raise ArgumentError, 'Unsupported cek-alg: ' \
195
+ "#{envelope['x-amz-cek-alg']}"
196
+ end
197
+ if (tag_length * 8) != envelope['x-amz-tag-len'].to_i
198
+ raise Errors::DecryptionError, 'x-amz-tag-len does not match expected'
199
+ end
200
+ tag_length
201
+ end
202
+
203
+ def apply_cse_user_agent(context)
204
+ if context.config.user_agent_suffix.nil?
205
+ context.config.user_agent_suffix = EC_USER_AGENT
206
+ elsif !context.config.user_agent_suffix.include? EC_USER_AGENT
207
+ context.config.user_agent_suffix += " #{EC_USER_AGENT}"
208
+ end
169
209
  end
170
210
 
171
211
  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
@@ -8,7 +10,8 @@ module Aws
8
10
  # @param [IO#write] io An IO-like object that responds to `#write`.
9
11
  def initialize(cipher, io)
10
12
  @cipher = cipher.clone
11
- @io = io
13
+ # Ensure that IO is reset between retries
14
+ @io = io.tap { |io| io.truncate(0) if io.respond_to?(:truncate) }
12
15
  end
13
16
 
14
17
  # @return [#write]
@@ -23,6 +26,10 @@ module Aws
23
26
  @io.write(@cipher.final)
24
27
  end
25
28
 
29
+ def size
30
+ @io.size
31
+ end
32
+
26
33
  end
27
34
  end
28
35
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'stringio'
2
4
  require 'tempfile'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aws
2
4
  module S3
3
5
  module Encryption