aws-sdk-s3 1.75.0 → 1.76.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/lib/aws-sdk-s3.rb +1 -1
  3. data/lib/aws-sdk-s3/bucket.rb +2 -2
  4. data/lib/aws-sdk-s3/client.rb +791 -505
  5. data/lib/aws-sdk-s3/encryption.rb +2 -0
  6. data/lib/aws-sdk-s3/encryption/client.rb +11 -0
  7. data/lib/aws-sdk-s3/encryption/decrypt_handler.rb +52 -28
  8. data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +41 -5
  9. data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +5 -5
  10. data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +32 -3
  11. data/lib/aws-sdk-s3/encryption/utils.rb +23 -0
  12. data/lib/aws-sdk-s3/encryptionV2/client.rb +198 -22
  13. data/lib/aws-sdk-s3/encryptionV2/decrypt_handler.rb +40 -12
  14. data/lib/aws-sdk-s3/encryptionV2/default_cipher_provider.rb +77 -10
  15. data/lib/aws-sdk-s3/encryptionV2/default_key_provider.rb +2 -0
  16. data/lib/aws-sdk-s3/encryptionV2/encrypt_handler.rb +7 -4
  17. data/lib/aws-sdk-s3/encryptionV2/errors.rb +24 -0
  18. data/lib/aws-sdk-s3/encryptionV2/io_auth_decrypter.rb +2 -0
  19. data/lib/aws-sdk-s3/encryptionV2/io_decrypter.rb +2 -0
  20. data/lib/aws-sdk-s3/encryptionV2/io_encrypter.rb +2 -0
  21. data/lib/aws-sdk-s3/encryptionV2/key_provider.rb +2 -0
  22. data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +90 -20
  23. data/lib/aws-sdk-s3/encryptionV2/materials.rb +2 -0
  24. data/lib/aws-sdk-s3/encryptionV2/utils.rb +2 -15
  25. data/lib/aws-sdk-s3/encryption_v2.rb +4 -1
  26. data/lib/aws-sdk-s3/multipart_upload_part.rb +5 -5
  27. data/lib/aws-sdk-s3/object.rb +10 -9
  28. data/lib/aws-sdk-s3/object_summary.rb +23 -7
  29. data/lib/aws-sdk-s3/object_version.rb +2 -2
  30. data/lib/aws-sdk-s3/presigner.rb +2 -2
  31. data/lib/aws-sdk-s3/types.rb +63 -26
  32. metadata +2 -2
@@ -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 EncryptionV2
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
@@ -38,9 +41,22 @@ module Aws
38
41
  AES/CBC/PKCS7Padding
39
42
  )
40
43
 
44
+ AUTH_REQUIRED_CEK_ALGS = %w(AES/GCM/NoPadding)
45
+
41
46
  def call(context)
42
47
  attach_http_event_listeners(context)
43
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
+
58
+ end
59
+
44
60
  @handler.call(context)
45
61
  end
46
62
 
@@ -71,11 +87,11 @@ module Aws
71
87
  end
72
88
 
73
89
  def decryption_cipher(context)
74
- if envelope = get_encryption_envelope(context)
90
+ if (envelope = get_encryption_envelope(context))
75
91
  cipher = context[:encryption][:cipher_provider]
76
92
  .decryption_cipher(
77
93
  envelope,
78
- kms_encryption_context: context[:encryption][:kms_encryption_context]
94
+ context[:encryption]
79
95
  )
80
96
  [cipher, envelope]
81
97
  else
@@ -145,10 +161,6 @@ module Aws
145
161
  envelope
146
162
  end
147
163
 
148
- # When the x-amz-meta-x-amz-tag-len header is present, it indicates
149
- # that the body of this object has a trailing auth tag. The header
150
- # indicates the length of that tag.
151
- #
152
164
  # This method fetches the tag from the end of the object by
153
165
  # making a GET Object w/range request. This auth tag is used
154
166
  # to initialize the cipher, and the decrypter truncates the
@@ -160,8 +172,7 @@ module Aws
160
172
  end
161
173
  http_resp = context.http_response
162
174
  content_length = http_resp.headers['content-length'].to_i
163
- auth_tag_length = envelope['x-amz-tag-len']
164
- auth_tag_length = auth_tag_length.to_i / 8
175
+ auth_tag_length = auth_tag_length(envelope)
165
176
 
166
177
  auth_tag = context.client.get_object(
167
178
  bucket: context.params[:bucket],
@@ -181,14 +192,31 @@ module Aws
181
192
  end
182
193
 
183
194
  def body_contains_auth_tag?(envelope)
184
- envelope.include? 'x-amz-tag-len'
195
+ AUTH_REQUIRED_CEK_ALGS.include?(envelope['x-amz-cek-alg'])
196
+ end
197
+
198
+ # Determine the auth tag length from the algorithm
199
+ # Validate it against the value provided in the x-amz-tag-len
200
+ # Return the tag length in bytes
201
+ def auth_tag_length(envelope)
202
+ tag_length =
203
+ case envelope['x-amz-cek-alg']
204
+ when 'AES/GCM/NoPadding' then AES_GCM_TAG_LEN_BYTES
205
+ else
206
+ raise ArgumentError, 'Unsupported cek-alg: ' \
207
+ "#{envelope['x-amz-cek-alg']}"
208
+ end
209
+ if (tag_length * 8) != envelope['x-amz-tag-len'].to_i
210
+ raise Errors::DecryptionError, 'x-amz-tag-len does not match expected'
211
+ end
212
+ tag_length
185
213
  end
186
214
 
187
215
  def apply_cse_user_agent(context)
188
216
  if context.config.user_agent_suffix.nil?
189
- context.config.user_agent_suffix = 'CSE_V2'
190
- elsif !context.config.user_agent_suffix.include? 'CSE_V2'
191
- context.config.user_agent_suffix += ' CSE_V2'
217
+ context.config.user_agent_suffix = EC_USER_AGENT
218
+ elsif !context.config.user_agent_suffix.include? EC_USER_AGENT
219
+ context.config.user_agent_suffix += " #{EC_USER_AGENT}"
192
220
  end
193
221
  end
194
222
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'base64'
2
4
 
3
5
  module Aws
@@ -8,27 +10,36 @@ module Aws
8
10
 
9
11
  def initialize(options = {})
10
12
  @key_provider = options[:key_provider]
13
+ @key_wrap_schema = validate_key_wrap(
14
+ options[:key_wrap_schema],
15
+ @key_provider.encryption_materials.key
16
+ )
17
+ @content_encryption_schema = validate_cek(
18
+ options[:content_encryption_schema]
19
+ )
11
20
  end
12
21
 
13
22
  # @return [Array<Hash,Cipher>] Creates an returns a new encryption
14
23
  # envelope and encryption cipher.
15
24
  def encryption_cipher(options = {})
25
+ validate_options(options)
16
26
  cipher = Utils.aes_encryption_cipher(:GCM)
17
- cek_alg = 'AES/GCM/NoPadding'
18
27
  if @key_provider.encryption_materials.key.is_a? OpenSSL::PKey::RSA
19
- wrap_alg = 'RSA-OAEP-SHA1'
20
- enc_key = encode64(encrypt_rsa(envelope_key(cipher), cek_alg))
28
+ enc_key = encode64(
29
+ encrypt_rsa(envelope_key(cipher), @content_encryption_schema)
30
+ )
21
31
  else
22
- wrap_alg = 'AES/GCM'
23
- enc_key = encode64(encrypt_aes_gcm(envelope_key(cipher), cek_alg))
32
+ enc_key = encode64(
33
+ encrypt_aes_gcm(envelope_key(cipher), @content_encryption_schema)
34
+ )
24
35
  end
25
36
  envelope = {
26
37
  'x-amz-key-v2' => enc_key,
27
- 'x-amz-cek-alg' => cek_alg,
28
- 'x-amz-tag-len' => 16 * 8,
29
- 'x-amz-wrap-alg' => wrap_alg,
38
+ 'x-amz-cek-alg' => @content_encryption_schema,
39
+ 'x-amz-tag-len' => (AES_GCM_TAG_LEN_BYTES * 8).to_s,
40
+ 'x-amz-wrap-alg' => @key_wrap_schema,
30
41
  'x-amz-iv' => encode64(envelope_iv(cipher)),
31
- 'x-amz-matdesc' => materials_description,
42
+ 'x-amz-matdesc' => materials_description
32
43
  }
33
44
  cipher.auth_data = '' # auth_data must be set after key and iv
34
45
  [envelope, cipher]
@@ -37,8 +48,12 @@ module Aws
37
48
  # @return [Cipher] Given an encryption envelope, returns a
38
49
  # decryption cipher.
39
50
  def decryption_cipher(envelope, options = {})
51
+ validate_options(options)
40
52
  master_key = @key_provider.key_for(envelope['x-amz-matdesc'])
41
53
  if envelope.key? 'x-amz-key'
54
+ unless options[:security_profile] == :v2_and_legacy
55
+ raise Errors::LegacyDecryptionError
56
+ end
42
57
  # Support for decryption of legacy objects
43
58
  key = Utils.decrypt(master_key, decode64(envelope['x-amz-key']))
44
59
  iv = decode64(envelope['x-amz-iv'])
@@ -51,13 +66,26 @@ module Aws
51
66
  key =
52
67
  case envelope['x-amz-wrap-alg']
53
68
  when 'AES/GCM'
69
+ if master_key.is_a? OpenSSL::PKey::RSA
70
+ raise ArgumentError, 'Key mismatch - Client is configured' \
71
+ ' with an RSA key and the x-amz-wrap-alg is AES/GCM.'
72
+ end
54
73
  Utils.decrypt_aes_gcm(master_key,
55
74
  decode64(envelope['x-amz-key-v2']),
56
75
  envelope['x-amz-cek-alg'])
57
76
  when 'RSA-OAEP-SHA1'
77
+ unless master_key.is_a? OpenSSL::PKey::RSA
78
+ raise ArgumentError, 'Key mismatch - Client is configured' \
79
+ ' with an AES key and the x-amz-wrap-alg is RSA-OAEP-SHA1.'
80
+ end
58
81
  key, cek_alg = Utils.decrypt_rsa(master_key, decode64(envelope['x-amz-key-v2']))
59
- raise Errors::DecryptionError unless cek_alg == envelope['x-amz-cek-alg']
82
+ raise Errors::CEKAlgMismatchError unless cek_alg == envelope['x-amz-cek-alg']
60
83
  key
84
+ when 'kms+context'
85
+ raise ArgumentError, 'Key mismatch - Client is configured' \
86
+ ' with a user provided key and the x-amz-wrap-alg is' \
87
+ ' kms+context. Please configure the client with the' \
88
+ ' required kms_key_id'
61
89
  else
62
90
  raise ArgumentError, 'Unsupported wrap-alg: ' \
63
91
  "#{envelope['x-amz-wrap-alg']}"
@@ -69,6 +97,39 @@ module Aws
69
97
 
70
98
  private
71
99
 
100
+ # Validate that the key_wrap_schema
101
+ # is valid, supported and matches the provided key.
102
+ # Returns the string version for the x-amz-key-wrap-alg
103
+ def validate_key_wrap(key_wrap_schema, key)
104
+ if key.is_a? OpenSSL::PKey::RSA
105
+ unless key_wrap_schema == :rsa_oaep_sha1
106
+ raise ArgumentError, ':key_wrap_schema must be set to :rsa_oaep_sha1 for RSA keys.'
107
+ end
108
+ else
109
+ unless key_wrap_schema == :aes_gcm
110
+ raise ArgumentError, ':key_wrap_schema must be set to :aes_gcm for AES keys.'
111
+ end
112
+ end
113
+
114
+ case key_wrap_schema
115
+ when :rsa_oaep_sha1 then 'RSA-OAEP-SHA1'
116
+ when :aes_gcm then 'AES/GCM'
117
+ when :kms_context
118
+ raise ArgumentError, 'A kms_key_id is required when using :kms_context.'
119
+ else
120
+ raise ArgumentError, "Unsupported key_wrap_schema: #{key_wrap_schema}"
121
+ end
122
+ end
123
+
124
+ def validate_cek(content_encryption_schema)
125
+ case content_encryption_schema
126
+ when :aes_gcm_no_padding
127
+ "AES/GCM/NoPadding"
128
+ else
129
+ raise ArgumentError, "Unsupported content_encryption_schema: #{content_encryption_schema}"
130
+ end
131
+ end
132
+
72
133
  def envelope_key(cipher)
73
134
  cipher.key = cipher.random_key
74
135
  end
@@ -97,6 +158,12 @@ module Aws
97
158
  Base64.decode64(str)
98
159
  end
99
160
 
161
+ def validate_options(options)
162
+ if !options[:kms_encryption_context].nil?
163
+ raise ArgumentError, 'Cannot provide :kms_encryption_context ' \
164
+ 'with non KMS client.'
165
+ end
166
+ end
100
167
  end
101
168
  end
102
169
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aws
2
4
  module S3
3
5
  module EncryptionV2
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'base64'
2
4
 
3
5
  module Aws
@@ -45,7 +47,8 @@ module Aws
45
47
  context.params[:metadata] ||= {}
46
48
  context.params[:metadata]['x-amz-unencrypted-content-length'] = io.size
47
49
  if context.params.delete(:content_md5)
48
- raise ArgumentError, 'content_md5 is not supported'
50
+ raise ArgumentError, 'Setting content_md5 on client side '\
51
+ 'encrypted objects is deprecated.'
49
52
  end
50
53
  context.http_response.on_headers do
51
54
  context.params[:body].close
@@ -54,9 +57,9 @@ module Aws
54
57
 
55
58
  def apply_cse_user_agent(context)
56
59
  if context.config.user_agent_suffix.nil?
57
- context.config.user_agent_suffix = 'CSE_V2'
58
- elsif !context.config.user_agent_suffix.include? 'CSE_V2'
59
- context.config.user_agent_suffix += ' CSE_V2'
60
+ context.config.user_agent_suffix = EC_USER_AGENT
61
+ elsif !context.config.user_agent_suffix.include? EC_USER_AGENT
62
+ context.config.user_agent_suffix += " #{EC_USER_AGENT}"
60
63
  end
61
64
  end
62
65
 
@@ -1,12 +1,36 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aws
2
4
  module S3
3
5
  module EncryptionV2
4
6
  module Errors
5
7
 
8
+ # Generic DecryptionError
6
9
  class DecryptionError < RuntimeError; end
7
10
 
8
11
  class EncryptionError < RuntimeError; end
9
12
 
13
+ # Raised when attempting to decrypt a legacy (V1) encrypted object
14
+ # when using a security_profile that does not support it.
15
+ class LegacyDecryptionError < DecryptionError
16
+ def initialize(*args)
17
+ msg = 'The requested object is ' \
18
+ 'encrypted with V1 encryption schemas that have been disabled ' \
19
+ 'by client configuration security_profile = :v2. Retry with ' \
20
+ ':v2_and_legacy or re-encrypt the object.'
21
+ super(msg)
22
+ end
23
+ end
24
+
25
+ class CEKAlgMismatchError < DecryptionError
26
+ def initialize(*args)
27
+ msg = 'The content encryption algorithm used at encryption time ' \
28
+ 'does not match the algorithm stored for decryption time. ' \
29
+ 'The object may be altered or corrupted.'
30
+ super(msg)
31
+ end
32
+ end
33
+
10
34
  end
11
35
  end
12
36
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aws
2
4
  module S3
3
5
  module EncryptionV2
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Aws
2
4
  module S3
3
5
  module EncryptionV2
@@ -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 EncryptionV2
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'base64'
2
4
 
3
5
  module Aws
@@ -7,15 +9,21 @@ module Aws
7
9
  class KmsCipherProvider
8
10
 
9
11
  def initialize(options = {})
10
- @kms_key_id = options[:kms_key_id]
12
+ @kms_key_id = validate_kms_key(options[:kms_key_id])
11
13
  @kms_client = options[:kms_client]
14
+ @key_wrap_schema = validate_key_wrap(
15
+ options[:key_wrap_schema]
16
+ )
17
+ @content_encryption_schema = validate_cek(
18
+ options[:content_encryption_schema]
19
+ )
12
20
  end
13
21
 
14
22
  # @return [Array<Hash,Cipher>] Creates and returns a new encryption
15
23
  # envelope and encryption cipher.
16
24
  def encryption_cipher(options = {})
17
- cek_alg = 'AES/GCM/NoPadding'
18
- encryption_context = build_encryption_context(cek_alg, options)
25
+ validate_key_for_encryption
26
+ encryption_context = build_encryption_context(@content_encryption_schema, options)
19
27
  key_data = @kms_client.generate_data_key(
20
28
  key_id: @kms_key_id,
21
29
  encryption_context: encryption_context,
@@ -26,9 +34,9 @@ module Aws
26
34
  envelope = {
27
35
  'x-amz-key-v2' => encode64(key_data.ciphertext_blob),
28
36
  'x-amz-iv' => encode64(cipher.iv = cipher.random_iv),
29
- 'x-amz-cek-alg' => cek_alg,
30
- 'x-amz-tag-len' => 16 * 8,
31
- 'x-amz-wrap-alg' => 'kms+context',
37
+ 'x-amz-cek-alg' => @content_encryption_schema,
38
+ 'x-amz-tag-len' => (AES_GCM_TAG_LEN_BYTES * 8).to_s,
39
+ 'x-amz-wrap-alg' => @key_wrap_schema,
32
40
  'x-amz-matdesc' => Json.dump(encryption_context)
33
41
  }
34
42
  cipher.auth_data = '' # auth_data must be set after key and iv
@@ -37,27 +45,45 @@ module Aws
37
45
 
38
46
  # @return [Cipher] Given an encryption envelope, returns a
39
47
  # decryption cipher.
40
- def decryption_cipher(envelope, options={})
48
+ def decryption_cipher(envelope, options = {})
41
49
  encryption_context = Json.load(envelope['x-amz-matdesc'])
42
- cek_alg = envelope['x-amz-wrap-alg'] == 'kms+context' ?
43
- encryption_context['aws:x-amz-cek-alg'] : envelope['x-amz-cek-alg']
44
- if cek_alg != envelope['x-amz-cek-alg']
45
- raise Errors::DecryptionError, 'Value of cek-alg from envelope'\
46
- ' does not match the value in the encryption context'
47
- end
50
+ cek_alg = envelope['x-amz-cek-alg']
51
+
52
+ case envelope['x-amz-wrap-alg']
53
+ when 'kms'
54
+ unless options[:security_profile] == :v2_and_legacy
55
+ raise Errors::LegacyDecryptionError
56
+ end
57
+ when 'kms+context'
58
+ if cek_alg != encryption_context['aws:x-amz-cek-alg']
59
+ raise Errors::CEKAlgMismatchError
60
+ end
48
61
 
49
- if envelope['x-amz-wrap-alg'] == 'kms+context' &&
50
- encryption_context != build_encryption_context(cek_alg, options)
51
- raise Errors::DecryptionError, 'Value of encryption context from'\
52
- ' envelope does not match the provided encryption context'
62
+ if encryption_context != build_encryption_context(cek_alg, options)
63
+ raise Errors::DecryptionError, 'Value of encryption context from'\
64
+ ' envelope does not match the provided encryption context'
65
+ end
66
+ when 'AES/GCM'
67
+ raise ArgumentError, 'Key mismatch - Client is configured' \
68
+ ' with a KMS key and the x-amz-wrap-alg is AES/GCM.'
69
+ when 'RSA-OAEP-SHA1'
70
+ raise ArgumentError, 'Key mismatch - Client is configured' \
71
+ ' with a KMS key and the x-amz-wrap-alg is RSA-OAEP-SHA1.'
72
+ else
73
+ raise ArgumentError, 'Unsupported wrap-alg: ' \
74
+ "#{envelope['x-amz-wrap-alg']}"
53
75
  end
54
76
 
55
- key = @kms_client.decrypt(
77
+ any_cmk_mode = false || options[:kms_allow_decrypt_with_any_cmk]
78
+ decrypt_options = {
56
79
  ciphertext_blob: decode64(envelope['x-amz-key-v2']),
57
80
  encryption_context: encryption_context
58
- ).plaintext
59
-
81
+ }
82
+ unless any_cmk_mode
83
+ decrypt_options[:key_id] = @kms_key_id
84
+ end
60
85
 
86
+ key = @kms_client.decrypt(decrypt_options).plaintext
61
87
  iv = decode64(envelope['x-amz-iv'])
62
88
  block_mode =
63
89
  case cek_alg
@@ -77,9 +103,46 @@ module Aws
77
103
 
78
104
  private
79
105
 
106
+ def validate_key_wrap(key_wrap_schema)
107
+ case key_wrap_schema
108
+ when :kms_context then 'kms+context'
109
+ else
110
+ raise ArgumentError, "Unsupported key_wrap_schema: #{key_wrap_schema}"
111
+ end
112
+ end
113
+
114
+ def validate_cek(content_encryption_schema)
115
+ case content_encryption_schema
116
+ when :aes_gcm_no_padding
117
+ "AES/GCM/NoPadding"
118
+ else
119
+ raise ArgumentError, "Unsupported content_encryption_schema: #{content_encryption_schema}"
120
+ end
121
+ end
122
+
123
+ def validate_kms_key(kms_key_id)
124
+ if kms_key_id.nil? || kms_key_id.length.zero?
125
+ raise ArgumentError, 'KMS CMK ID was not specified. ' \
126
+ 'Please specify a CMK ID, ' \
127
+ 'or set kms_key_id: :kms_allow_decrypt_with_any_cmk to use ' \
128
+ 'any valid CMK from the object.'
129
+ end
130
+
131
+ if kms_key_id.is_a?(Symbol) && kms_key_id != :kms_allow_decrypt_with_any_cmk
132
+ raise ArgumentError, 'kms_key_id must be a valid KMS CMK or be ' \
133
+ 'set to :kms_allow_decrypt_with_any_cmk'
134
+ end
135
+ kms_key_id
136
+ end
137
+
80
138
  def build_encryption_context(cek_alg, options = {})
81
139
  kms_context = (options[:kms_encryption_context] || {})
82
140
  .each_with_object({}) { |(k, v), h| h[k.to_s] = v }
141
+ if kms_context.include? 'aws:x-amz-cek-alg'
142
+ raise ArgumentError, 'Conflict in reserved KMS Encryption Context ' \
143
+ 'key aws:x-amz-cek-alg. This value is reserved for the S3 ' \
144
+ 'Encryption Client and cannot be set by the user.'
145
+ end
83
146
  {
84
147
  'aws:x-amz-cek-alg' => cek_alg
85
148
  }.merge(kms_context)
@@ -93,6 +156,13 @@ module Aws
93
156
  Base64.decode64(str)
94
157
  end
95
158
 
159
+ def validate_key_for_encryption
160
+ if @kms_key_id == :kms_allow_decrypt_with_any_cmk
161
+ raise ArgumentError, 'Unable to encrypt/write objects with '\
162
+ 'kms_key_id = :kms_allow_decrypt_with_any_cmk. Provide ' \
163
+ 'a valid kms_key_id on client construction.'
164
+ end
165
+ end
96
166
  end
97
167
  end
98
168
  end