aws-sdk-s3 1.75.0 → 1.83.1
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 +4 -4
- data/lib/aws-sdk-s3.rb +3 -2
- data/lib/aws-sdk-s3/arn/access_point_arn.rb +62 -0
- data/lib/aws-sdk-s3/arn/outpost_access_point_arn.rb +71 -0
- data/lib/aws-sdk-s3/bucket.rb +56 -7
- data/lib/aws-sdk-s3/bucket_acl.rb +5 -0
- data/lib/aws-sdk-s3/bucket_cors.rb +12 -1
- data/lib/aws-sdk-s3/bucket_lifecycle.rb +12 -1
- data/lib/aws-sdk-s3/bucket_lifecycle_configuration.rb +12 -1
- data/lib/aws-sdk-s3/bucket_logging.rb +5 -0
- data/lib/aws-sdk-s3/bucket_notification.rb +5 -0
- data/lib/aws-sdk-s3/bucket_policy.rb +12 -1
- data/lib/aws-sdk-s3/bucket_request_payment.rb +5 -0
- data/lib/aws-sdk-s3/bucket_tagging.rb +12 -1
- data/lib/aws-sdk-s3/bucket_versioning.rb +15 -0
- data/lib/aws-sdk-s3/bucket_website.rb +12 -1
- data/lib/aws-sdk-s3/client.rb +2127 -678
- data/lib/aws-sdk-s3/client_api.rb +148 -0
- data/lib/aws-sdk-s3/customizations/bucket.rb +7 -4
- data/lib/aws-sdk-s3/customizations/object.rb +4 -3
- data/lib/aws-sdk-s3/encryption.rb +2 -0
- data/lib/aws-sdk-s3/encryption/client.rb +11 -0
- data/lib/aws-sdk-s3/encryption/decrypt_handler.rb +64 -29
- data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +41 -5
- data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +5 -5
- data/lib/aws-sdk-s3/encryption/io_decrypter.rb +7 -6
- data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +32 -3
- data/lib/aws-sdk-s3/encryption/utils.rb +23 -0
- data/lib/aws-sdk-s3/encryptionV2/client.rb +201 -23
- data/lib/aws-sdk-s3/encryptionV2/decrypt_handler.rb +40 -12
- data/lib/aws-sdk-s3/encryptionV2/default_cipher_provider.rb +77 -10
- data/lib/aws-sdk-s3/encryptionV2/default_key_provider.rb +2 -0
- data/lib/aws-sdk-s3/encryptionV2/encrypt_handler.rb +7 -4
- data/lib/aws-sdk-s3/encryptionV2/errors.rb +24 -0
- data/lib/aws-sdk-s3/encryptionV2/io_auth_decrypter.rb +2 -0
- data/lib/aws-sdk-s3/encryptionV2/io_decrypter.rb +2 -0
- data/lib/aws-sdk-s3/encryptionV2/io_encrypter.rb +2 -0
- data/lib/aws-sdk-s3/encryptionV2/key_provider.rb +2 -0
- data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +90 -20
- data/lib/aws-sdk-s3/encryptionV2/materials.rb +2 -0
- data/lib/aws-sdk-s3/encryptionV2/utils.rb +2 -15
- data/lib/aws-sdk-s3/encryption_v2.rb +4 -1
- data/lib/aws-sdk-s3/file_uploader.rb +1 -1
- data/lib/aws-sdk-s3/multipart_upload.rb +15 -0
- data/lib/aws-sdk-s3/multipart_upload_part.rb +64 -7
- data/lib/aws-sdk-s3/object.rb +158 -19
- data/lib/aws-sdk-s3/object_acl.rb +13 -0
- data/lib/aws-sdk-s3/object_summary.rb +171 -17
- data/lib/aws-sdk-s3/object_version.rb +27 -3
- data/lib/aws-sdk-s3/plugins/arn.rb +187 -0
- data/lib/aws-sdk-s3/plugins/bucket_dns.rb +0 -2
- data/lib/aws-sdk-s3/plugins/bucket_name_restrictions.rb +1 -1
- data/lib/aws-sdk-s3/plugins/s3_signer.rb +29 -7
- data/lib/aws-sdk-s3/presigned_post.rb +1 -0
- data/lib/aws-sdk-s3/presigner.rb +3 -2
- data/lib/aws-sdk-s3/resource.rb +1 -1
- data/lib/aws-sdk-s3/types.rb +1616 -222
- metadata +7 -5
- data/lib/aws-sdk-s3/plugins/bucket_arn.rb +0 -212
@@ -12,13 +12,12 @@ module Aws
|
|
12
12
|
# Define a new initialize method that extracts out a bucket ARN.
|
13
13
|
define_method(:initialize) do |*args|
|
14
14
|
old_initialize.bind(self).call(*args)
|
15
|
-
|
15
|
+
resolved_region, arn = Plugins::ARN.resolve_arn!(
|
16
16
|
name,
|
17
17
|
client.config.region,
|
18
18
|
client.config.s3_use_arn_region
|
19
19
|
)
|
20
|
-
@
|
21
|
-
@client.config.region = region
|
20
|
+
@resolved_region = resolved_region
|
22
21
|
@arn = arn
|
23
22
|
end
|
24
23
|
|
@@ -102,7 +101,11 @@ module Aws
|
|
102
101
|
if options[:virtual_host]
|
103
102
|
"http://#{name}"
|
104
103
|
elsif @arn
|
105
|
-
Plugins::
|
104
|
+
Plugins::ARN.resolve_url!(
|
105
|
+
client.config.endpoint.dup,
|
106
|
+
@arn,
|
107
|
+
@resolved_region
|
108
|
+
).to_s
|
106
109
|
else
|
107
110
|
s3_bucket_url
|
108
111
|
end
|
@@ -282,8 +282,8 @@ module Aws
|
|
282
282
|
# # small files are uploaded in a single API call
|
283
283
|
# obj.upload_file('/path/to/file')
|
284
284
|
#
|
285
|
-
# Files larger than `:multipart_threshold` are uploaded
|
286
|
-
# Amazon S3 multipart upload APIs.
|
285
|
+
# Files larger than or equal to `:multipart_threshold` are uploaded
|
286
|
+
# using the Amazon S3 multipart upload APIs.
|
287
287
|
#
|
288
288
|
# # large files are automatically split into parts
|
289
289
|
# # and the parts are uploaded in parallel
|
@@ -313,7 +313,8 @@ module Aws
|
|
313
313
|
# will be empty.
|
314
314
|
#
|
315
315
|
# @option options [Integer] :multipart_threshold (15728640) Files larger
|
316
|
-
# than `:multipart_threshold` are uploaded using the S3
|
316
|
+
# than or equal to `:multipart_threshold` are uploaded using the S3
|
317
|
+
# multipart APIs.
|
317
318
|
# Default threshold is 15MB.
|
318
319
|
#
|
319
320
|
# @option options [Integer] :thread_count (10) The number of parallel
|
@@ -5,6 +5,10 @@ require 'forwardable'
|
|
5
5
|
module Aws
|
6
6
|
module S3
|
7
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}.
|
8
12
|
# Provides an encryption client that encrypts and decrypts data client-side,
|
9
13
|
# storing the encrypted data in Amazon S3.
|
10
14
|
#
|
@@ -229,6 +233,13 @@ module Aws
|
|
229
233
|
@envelope_location = extract_location(options)
|
230
234
|
@instruction_file_suffix = extract_suffix(options)
|
231
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
|
+
|
232
243
|
|
233
244
|
# @return [S3::Client]
|
234
245
|
attr_reader :client
|
@@ -7,6 +7,7 @@ module Aws
|
|
7
7
|
module Encryption
|
8
8
|
# @api private
|
9
9
|
class DecryptHandler < Seahorse::Client::Handler
|
10
|
+
@@warned_response_target_proc = false
|
10
11
|
|
11
12
|
V1_ENVELOPE_KEYS = %w(
|
12
13
|
x-amz-key
|
@@ -22,7 +23,17 @@ module Aws
|
|
22
23
|
x-amz-matdesc
|
23
24
|
)
|
24
25
|
|
25
|
-
|
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
|
+
)
|
26
37
|
|
27
38
|
POSSIBLE_ENCRYPTION_FORMATS = %w(
|
28
39
|
AES/GCM/NoPadding
|
@@ -30,9 +41,21 @@ module Aws
|
|
30
41
|
AES/CBC/PKCS7Padding
|
31
42
|
)
|
32
43
|
|
44
|
+
AUTH_REQUIRED_CEK_ALGS = %w(AES/GCM/NoPadding)
|
45
|
+
|
33
46
|
def call(context)
|
34
47
|
attach_http_event_listeners(context)
|
35
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
|
+
|
36
59
|
@handler.call(context)
|
37
60
|
end
|
38
61
|
|
@@ -41,9 +64,9 @@ module Aws
|
|
41
64
|
def attach_http_event_listeners(context)
|
42
65
|
|
43
66
|
context.http_response.on_headers(200) do
|
44
|
-
cipher = decryption_cipher(context)
|
45
|
-
decrypter = body_contains_auth_tag?(
|
46
|
-
authenticated_decrypter(context, cipher) :
|
67
|
+
cipher, envelope = decryption_cipher(context)
|
68
|
+
decrypter = body_contains_auth_tag?(envelope) ?
|
69
|
+
authenticated_decrypter(context, cipher, envelope) :
|
47
70
|
IODecrypter.new(cipher, context.http_response.body)
|
48
71
|
context.http_response.body = decrypter
|
49
72
|
end
|
@@ -63,8 +86,13 @@ module Aws
|
|
63
86
|
end
|
64
87
|
|
65
88
|
def decryption_cipher(context)
|
66
|
-
if envelope = get_encryption_envelope(context)
|
67
|
-
context[:encryption][:cipher_provider]
|
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]
|
68
96
|
else
|
69
97
|
raise Errors::DecryptionError, "unable to locate encryption envelope"
|
70
98
|
end
|
@@ -100,13 +128,12 @@ module Aws
|
|
100
128
|
end
|
101
129
|
|
102
130
|
def extract_envelope(hash)
|
131
|
+
return nil unless hash
|
103
132
|
return v1_envelope(hash) if hash.key?('x-amz-key')
|
104
133
|
return v2_envelope(hash) if hash.key?('x-amz-key-v2')
|
105
134
|
if hash.keys.any? { |key| key.match(/^x-amz-key-(.+)$/) }
|
106
135
|
msg = "unsupported envelope encryption version #{$1}"
|
107
136
|
raise Errors::DecryptionError, msg
|
108
|
-
else
|
109
|
-
nil # no envelope found
|
110
137
|
end
|
111
138
|
end
|
112
139
|
|
@@ -120,39 +147,31 @@ module Aws
|
|
120
147
|
msg = "unsupported content encrypting key (cek) format: #{alg}"
|
121
148
|
raise Errors::DecryptionError, msg
|
122
149
|
end
|
123
|
-
unless envelope['x-amz-wrap-alg']
|
124
|
-
# possible to support
|
125
|
-
# RSA/ECB/OAEPWithSHA-256AndMGF1Padding
|
150
|
+
unless POSSIBLE_WRAPPING_FORMATS.include? envelope['x-amz-wrap-alg']
|
126
151
|
alg = envelope['x-amz-wrap-alg'].inspect
|
127
152
|
msg = "unsupported key wrapping algorithm: #{alg}"
|
128
153
|
raise Errors::DecryptionError, msg
|
129
154
|
end
|
130
|
-
unless V2_ENVELOPE_KEYS
|
155
|
+
unless (missing_keys = V2_ENVELOPE_KEYS - envelope.keys).empty?
|
131
156
|
msg = "incomplete v2 encryption envelope:\n"
|
132
|
-
msg += "
|
133
|
-
msg += " got: #{envelope_keys.join(', ')}"
|
157
|
+
msg += " missing: #{missing_keys.join(',')}\n"
|
134
158
|
raise Errors::DecryptionError, msg
|
135
159
|
end
|
136
160
|
envelope
|
137
161
|
end
|
138
162
|
|
139
|
-
# When the x-amz-meta-x-amz-tag-len header is present, it indicates
|
140
|
-
# that the body of this object has a trailing auth tag. The header
|
141
|
-
# indicates the length of that tag.
|
142
|
-
#
|
143
163
|
# This method fetches the tag from the end of the object by
|
144
164
|
# making a GET Object w/range request. This auth tag is used
|
145
165
|
# to initialize the cipher, and the decrypter truncates the
|
146
166
|
# auth tag from the body when writing the final bytes.
|
147
|
-
def authenticated_decrypter(context, cipher)
|
167
|
+
def authenticated_decrypter(context, cipher, envelope)
|
148
168
|
if RUBY_VERSION.match(/1.9/)
|
149
|
-
raise "authenticated decryption not supported by
|
169
|
+
raise "authenticated decryption not supported by OpenSSL in Ruby version ~> 1.9"
|
150
170
|
raise Aws::Errors::NonSupportedRubyVersionError, msg
|
151
171
|
end
|
152
172
|
http_resp = context.http_response
|
153
173
|
content_length = http_resp.headers['content-length'].to_i
|
154
|
-
auth_tag_length =
|
155
|
-
auth_tag_length = auth_tag_length.to_i / 8
|
174
|
+
auth_tag_length = auth_tag_length(envelope)
|
156
175
|
|
157
176
|
auth_tag = context.client.get_object(
|
158
177
|
bucket: context.params[:bucket],
|
@@ -164,23 +183,39 @@ module Aws
|
|
164
183
|
cipher.auth_data = ''
|
165
184
|
|
166
185
|
# The encrypted object contains both the cipher text
|
167
|
-
# plus a trailing auth tag.
|
168
|
-
# expect for the trailing auth tag.
|
186
|
+
# plus a trailing auth tag.
|
169
187
|
IOAuthDecrypter.new(
|
170
188
|
io: http_resp.body,
|
171
189
|
encrypted_content_length: content_length - auth_tag_length,
|
172
190
|
cipher: cipher)
|
173
191
|
end
|
174
192
|
|
175
|
-
def body_contains_auth_tag?(
|
176
|
-
|
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
|
177
212
|
end
|
178
213
|
|
179
214
|
def apply_cse_user_agent(context)
|
180
215
|
if context.config.user_agent_suffix.nil?
|
181
|
-
context.config.user_agent_suffix =
|
182
|
-
elsif !context.config.user_agent_suffix.include?
|
183
|
-
context.config.user_agent_suffix +=
|
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}"
|
184
219
|
end
|
185
220
|
end
|
186
221
|
|
@@ -26,11 +26,48 @@ module Aws
|
|
26
26
|
|
27
27
|
# @return [Cipher] Given an encryption envelope, returns a
|
28
28
|
# decryption cipher.
|
29
|
-
def decryption_cipher(envelope)
|
29
|
+
def decryption_cipher(envelope, options = {})
|
30
30
|
master_key = @key_provider.key_for(envelope['x-amz-matdesc'])
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
34
71
|
end
|
35
72
|
|
36
73
|
private
|
@@ -58,7 +95,6 @@ module Aws
|
|
58
95
|
def decode64(str)
|
59
96
|
Base64.decode64(str)
|
60
97
|
end
|
61
|
-
|
62
98
|
end
|
63
99
|
end
|
64
100
|
end
|
@@ -39,8 +39,8 @@ module Aws
|
|
39
39
|
context.params[:body] = IOEncrypter.new(cipher, io)
|
40
40
|
context.params[:metadata] ||= {}
|
41
41
|
context.params[:metadata]['x-amz-unencrypted-content-length'] = io.size
|
42
|
-
if
|
43
|
-
|
42
|
+
if context.params.delete(:content_md5)
|
43
|
+
warn('Setting content_md5 on client side encrypted objects is deprecated')
|
44
44
|
end
|
45
45
|
context.http_response.on_headers do
|
46
46
|
context.params[:body].close
|
@@ -49,9 +49,9 @@ module Aws
|
|
49
49
|
|
50
50
|
def apply_cse_user_agent(context)
|
51
51
|
if context.config.user_agent_suffix.nil?
|
52
|
-
context.config.user_agent_suffix =
|
53
|
-
elsif !context.config.user_agent_suffix.include?
|
54
|
-
context.config.user_agent_suffix +=
|
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
55
|
end
|
56
56
|
end
|
57
57
|
|
@@ -9,9 +9,10 @@ module Aws
|
|
9
9
|
# @param [OpenSSL::Cipher] cipher
|
10
10
|
# @param [IO#write] io An IO-like object that responds to `#write`.
|
11
11
|
def initialize(cipher, io)
|
12
|
-
@cipher = cipher
|
12
|
+
@cipher = cipher
|
13
13
|
# Ensure that IO is reset between retries
|
14
14
|
@io = io.tap { |io| io.truncate(0) if io.respond_to?(:truncate) }
|
15
|
+
@cipher_buffer = String.new
|
15
16
|
end
|
16
17
|
|
17
18
|
# @return [#write]
|
@@ -19,17 +20,17 @@ module Aws
|
|
19
20
|
|
20
21
|
def write(chunk)
|
21
22
|
# decrypt and write
|
22
|
-
@
|
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
|
23
28
|
end
|
24
29
|
|
25
30
|
def finalize
|
26
31
|
@io.write(@cipher.final)
|
27
32
|
end
|
28
33
|
|
29
|
-
def size
|
30
|
-
@io.size
|
31
|
-
end
|
32
|
-
|
33
34
|
end
|
34
35
|
end
|
35
36
|
end
|
@@ -36,15 +36,36 @@ module Aws
|
|
36
36
|
|
37
37
|
# @return [Cipher] Given an encryption envelope, returns a
|
38
38
|
# decryption cipher.
|
39
|
-
def decryption_cipher(envelope)
|
39
|
+
def decryption_cipher(envelope, options = {})
|
40
40
|
encryption_context = Json.load(envelope['x-amz-matdesc'])
|
41
|
+
cek_alg = envelope['x-amz-cek-alg']
|
42
|
+
|
43
|
+
case envelope['x-amz-wrap-alg']
|
44
|
+
when 'kms'; # NO OP
|
45
|
+
when 'kms+context'
|
46
|
+
if cek_alg != encryption_context['aws:x-amz-cek-alg']
|
47
|
+
raise Errors::DecryptionError, 'Value of cek-alg from envelope'\
|
48
|
+
' does not match the value in the encryption context'
|
49
|
+
end
|
50
|
+
when 'AES/GCM'
|
51
|
+
raise ArgumentError, 'Key mismatch - Client is configured' \
|
52
|
+
' with a KMS key and the x-amz-wrap-alg is AES/GCM.'
|
53
|
+
when 'RSA-OAEP-SHA1'
|
54
|
+
raise ArgumentError, 'Key mismatch - Client is configured' \
|
55
|
+
' with a KMS key and the x-amz-wrap-alg is RSA-OAEP-SHA1.'
|
56
|
+
else
|
57
|
+
raise ArgumentError, 'Unsupported wrap-alg: ' \
|
58
|
+
"#{envelope['x-amz-wrap-alg']}"
|
59
|
+
end
|
60
|
+
|
41
61
|
key = @kms_client.decrypt(
|
42
62
|
ciphertext_blob: decode64(envelope['x-amz-key-v2']),
|
43
|
-
encryption_context: encryption_context
|
63
|
+
encryption_context: encryption_context
|
44
64
|
).plaintext
|
65
|
+
|
45
66
|
iv = decode64(envelope['x-amz-iv'])
|
46
67
|
block_mode =
|
47
|
-
case
|
68
|
+
case cek_alg
|
48
69
|
when 'AES/CBC/PKCS5Padding'
|
49
70
|
:CBC
|
50
71
|
when 'AES/CBC/PKCS7Padding'
|
@@ -61,6 +82,14 @@ module Aws
|
|
61
82
|
|
62
83
|
private
|
63
84
|
|
85
|
+
def build_encryption_context(cek_alg, options = {})
|
86
|
+
kms_context = (options[:kms_encryption_context] || {})
|
87
|
+
.each_with_object({}) { |(k, v), h| h[k.to_s] = v }
|
88
|
+
{
|
89
|
+
'aws:x-amz-cek-alg' => cek_alg
|
90
|
+
}.merge(kms_context)
|
91
|
+
end
|
92
|
+
|
64
93
|
def encode64(str)
|
65
94
|
Base64.encode64(str).split("\n") * ""
|
66
95
|
end
|
@@ -39,6 +39,29 @@ module Aws
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
+
|
43
|
+
def decrypt_aes_gcm(key, data, auth_data)
|
44
|
+
# data is iv (12B) + key + tag (16B)
|
45
|
+
buf = data.unpack('C*')
|
46
|
+
iv = buf[0,12].pack('C*') # iv will always be 12 bytes
|
47
|
+
tag = buf[-16, 16].pack('C*') # tag is 16 bytes
|
48
|
+
enc_key = buf[12, buf.size - (12+16)].pack('C*')
|
49
|
+
cipher = aes_cipher(:decrypt, :GCM, key, iv)
|
50
|
+
cipher.auth_tag = tag
|
51
|
+
cipher.auth_data = auth_data
|
52
|
+
cipher.update(enc_key) + cipher.final
|
53
|
+
end
|
54
|
+
|
55
|
+
# returns the decrypted data + auth_data
|
56
|
+
def decrypt_rsa(key, enc_data)
|
57
|
+
# Plaintext must be KeyLengthInBytes (1 Byte) + DataKey + AuthData
|
58
|
+
buf = key.private_decrypt(enc_data, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING).unpack('C*')
|
59
|
+
key_length = buf[0]
|
60
|
+
data = buf[1, key_length].pack('C*')
|
61
|
+
auth_data = buf[key_length+1, buf.length - key_length].pack('C*')
|
62
|
+
[data, auth_data]
|
63
|
+
end
|
64
|
+
|
42
65
|
# @param [String] block_mode "CBC" or "ECB"
|
43
66
|
# @param [OpenSSL::PKey::RSA, String, nil] key
|
44
67
|
# @param [String, nil] iv The initialization vector
|