aws-sdk-s3 1.48.0 → 1.183.0
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 +5 -5
- data/CHANGELOG.md +1352 -0
- data/LICENSE.txt +202 -0
- data/VERSION +1 -0
- data/lib/aws-sdk-s3/access_grants_credentials.rb +57 -0
- data/lib/aws-sdk-s3/access_grants_credentials_provider.rb +250 -0
- data/lib/aws-sdk-s3/bucket.rb +1005 -106
- data/lib/aws-sdk-s3/bucket_acl.rb +65 -18
- data/lib/aws-sdk-s3/bucket_cors.rb +80 -18
- data/lib/aws-sdk-s3/bucket_lifecycle.rb +71 -20
- data/lib/aws-sdk-s3/bucket_lifecycle_configuration.rb +126 -21
- data/lib/aws-sdk-s3/bucket_logging.rb +68 -16
- data/lib/aws-sdk-s3/bucket_notification.rb +52 -20
- data/lib/aws-sdk-s3/bucket_policy.rb +108 -17
- data/lib/aws-sdk-s3/bucket_region_cache.rb +11 -5
- data/lib/aws-sdk-s3/bucket_request_payment.rb +60 -15
- data/lib/aws-sdk-s3/bucket_tagging.rb +71 -18
- data/lib/aws-sdk-s3/bucket_versioning.rb +133 -17
- data/lib/aws-sdk-s3/bucket_website.rb +78 -21
- data/lib/aws-sdk-s3/client.rb +14517 -941
- data/lib/aws-sdk-s3/client_api.rb +1296 -197
- data/lib/aws-sdk-s3/customizations/bucket.rb +56 -37
- data/lib/aws-sdk-s3/customizations/errors.rb +40 -0
- data/lib/aws-sdk-s3/customizations/multipart_upload.rb +2 -0
- data/lib/aws-sdk-s3/customizations/object.rb +288 -68
- data/lib/aws-sdk-s3/customizations/object_summary.rb +10 -0
- data/lib/aws-sdk-s3/customizations/object_version.rb +13 -0
- data/lib/aws-sdk-s3/customizations/types/list_object_versions_output.rb +2 -0
- data/lib/aws-sdk-s3/customizations/types/permanent_redirect.rb +26 -0
- data/lib/aws-sdk-s3/customizations.rb +27 -28
- data/lib/aws-sdk-s3/encryption/client.rb +28 -7
- data/lib/aws-sdk-s3/encryption/decrypt_handler.rb +71 -29
- data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +43 -5
- data/lib/aws-sdk-s3/encryption/default_key_provider.rb +2 -0
- data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +13 -2
- data/lib/aws-sdk-s3/encryption/errors.rb +2 -0
- data/lib/aws-sdk-s3/encryption/io_auth_decrypter.rb +2 -0
- data/lib/aws-sdk-s3/encryption/io_decrypter.rb +11 -3
- data/lib/aws-sdk-s3/encryption/io_encrypter.rb +2 -0
- data/lib/aws-sdk-s3/encryption/key_provider.rb +2 -0
- data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +46 -11
- data/lib/aws-sdk-s3/encryption/materials.rb +8 -6
- data/lib/aws-sdk-s3/encryption/utils.rb +25 -0
- data/lib/aws-sdk-s3/encryption.rb +4 -0
- data/lib/aws-sdk-s3/encryptionV2/client.rb +570 -0
- data/lib/aws-sdk-s3/encryptionV2/decrypt_handler.rb +223 -0
- data/lib/aws-sdk-s3/encryptionV2/default_cipher_provider.rb +170 -0
- data/lib/aws-sdk-s3/encryptionV2/default_key_provider.rb +40 -0
- data/lib/aws-sdk-s3/encryptionV2/encrypt_handler.rb +65 -0
- data/lib/aws-sdk-s3/encryptionV2/errors.rb +37 -0
- data/lib/aws-sdk-s3/encryptionV2/io_auth_decrypter.rb +58 -0
- data/lib/aws-sdk-s3/encryptionV2/io_decrypter.rb +37 -0
- data/lib/aws-sdk-s3/encryptionV2/io_encrypter.rb +73 -0
- data/lib/aws-sdk-s3/encryptionV2/key_provider.rb +31 -0
- data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +173 -0
- data/lib/aws-sdk-s3/encryptionV2/materials.rb +60 -0
- data/lib/aws-sdk-s3/encryptionV2/utils.rb +103 -0
- data/lib/aws-sdk-s3/encryption_v2.rb +23 -0
- data/lib/aws-sdk-s3/endpoint_parameters.rb +181 -0
- data/lib/aws-sdk-s3/endpoint_provider.rb +716 -0
- data/lib/aws-sdk-s3/endpoints.rb +1434 -0
- data/lib/aws-sdk-s3/errors.rb +170 -1
- data/lib/aws-sdk-s3/event_streams.rb +8 -1
- data/lib/aws-sdk-s3/express_credentials.rb +55 -0
- data/lib/aws-sdk-s3/express_credentials_provider.rb +59 -0
- data/lib/aws-sdk-s3/file_downloader.rb +161 -46
- data/lib/aws-sdk-s3/file_part.rb +11 -6
- data/lib/aws-sdk-s3/file_uploader.rb +39 -18
- data/lib/aws-sdk-s3/legacy_signer.rb +17 -25
- data/lib/aws-sdk-s3/multipart_file_uploader.rb +104 -27
- data/lib/aws-sdk-s3/multipart_stream_uploader.rb +61 -21
- data/lib/aws-sdk-s3/multipart_upload.rb +342 -32
- data/lib/aws-sdk-s3/multipart_upload_error.rb +2 -0
- data/lib/aws-sdk-s3/multipart_upload_part.rb +384 -46
- data/lib/aws-sdk-s3/object.rb +2600 -231
- data/lib/aws-sdk-s3/object_acl.rb +103 -25
- data/lib/aws-sdk-s3/object_copier.rb +9 -5
- data/lib/aws-sdk-s3/object_multipart_copier.rb +48 -22
- data/lib/aws-sdk-s3/object_summary.rb +2174 -204
- data/lib/aws-sdk-s3/object_version.rb +539 -80
- data/lib/aws-sdk-s3/plugins/accelerate.rb +17 -64
- data/lib/aws-sdk-s3/plugins/access_grants.rb +178 -0
- data/lib/aws-sdk-s3/plugins/arn.rb +70 -0
- data/lib/aws-sdk-s3/plugins/bucket_dns.rb +7 -43
- data/lib/aws-sdk-s3/plugins/bucket_name_restrictions.rb +20 -3
- data/lib/aws-sdk-s3/plugins/checksum_algorithm.rb +31 -0
- data/lib/aws-sdk-s3/plugins/dualstack.rb +7 -50
- data/lib/aws-sdk-s3/plugins/endpoints.rb +86 -0
- data/lib/aws-sdk-s3/plugins/expect_100_continue.rb +5 -4
- data/lib/aws-sdk-s3/plugins/express_session_auth.rb +88 -0
- data/lib/aws-sdk-s3/plugins/get_bucket_location_fix.rb +3 -1
- data/lib/aws-sdk-s3/plugins/http_200_errors.rb +62 -17
- data/lib/aws-sdk-s3/plugins/iad_regional_endpoint.rb +44 -0
- data/lib/aws-sdk-s3/plugins/location_constraint.rb +5 -1
- data/lib/aws-sdk-s3/plugins/md5s.rb +14 -70
- data/lib/aws-sdk-s3/plugins/redirects.rb +2 -0
- data/lib/aws-sdk-s3/plugins/s3_host_id.rb +2 -0
- data/lib/aws-sdk-s3/plugins/s3_signer.rb +63 -94
- data/lib/aws-sdk-s3/plugins/sse_cpk.rb +3 -1
- data/lib/aws-sdk-s3/plugins/streaming_retry.rb +139 -0
- data/lib/aws-sdk-s3/plugins/url_encoded_keys.rb +2 -0
- data/lib/aws-sdk-s3/presigned_post.rb +160 -99
- data/lib/aws-sdk-s3/presigner.rb +141 -62
- data/lib/aws-sdk-s3/resource.rb +156 -17
- data/lib/aws-sdk-s3/types.rb +13021 -4106
- data/lib/aws-sdk-s3/waiters.rb +67 -1
- data/lib/aws-sdk-s3.rb +46 -32
- data/sig/bucket.rbs +222 -0
- data/sig/bucket_acl.rbs +78 -0
- data/sig/bucket_cors.rbs +69 -0
- data/sig/bucket_lifecycle.rbs +88 -0
- data/sig/bucket_lifecycle_configuration.rbs +115 -0
- data/sig/bucket_logging.rbs +76 -0
- data/sig/bucket_notification.rbs +114 -0
- data/sig/bucket_policy.rbs +59 -0
- data/sig/bucket_request_payment.rbs +54 -0
- data/sig/bucket_tagging.rbs +65 -0
- data/sig/bucket_versioning.rbs +77 -0
- data/sig/bucket_website.rbs +93 -0
- data/sig/client.rbs +2472 -0
- data/sig/customizations/bucket.rbs +19 -0
- data/sig/customizations/object.rbs +38 -0
- data/sig/customizations/object_summary.rbs +35 -0
- data/sig/errors.rbs +42 -0
- data/sig/multipart_upload.rbs +120 -0
- data/sig/multipart_upload_part.rbs +109 -0
- data/sig/object.rbs +459 -0
- data/sig/object_acl.rbs +86 -0
- data/sig/object_summary.rbs +345 -0
- data/sig/object_version.rbs +143 -0
- data/sig/resource.rbs +134 -0
- data/sig/types.rbs +2712 -0
- data/sig/waiters.rbs +95 -0
- metadata +74 -15
@@ -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
|
#
|
@@ -112,7 +120,7 @@ module Aws
|
|
112
120
|
# attr_reader :encryption_materials
|
113
121
|
#
|
114
122
|
# def key_for(matdesc)
|
115
|
-
# key_name = JSON.
|
123
|
+
# key_name = JSON.parse(matdesc)['key']
|
116
124
|
# if key = @keys[key_name]
|
117
125
|
# key
|
118
126
|
# else
|
@@ -178,6 +186,8 @@ 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
192
|
# Creates a new encryption client. You must provide one of the following
|
183
193
|
# options:
|
@@ -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
|
@@ -253,7 +270,9 @@ module Aws
|
|
253
270
|
envelope_location: @envelope_location,
|
254
271
|
instruction_file_suffix: @instruction_file_suffix,
|
255
272
|
}
|
256
|
-
|
273
|
+
Aws::Plugins::UserAgent.metric('S3_CRYPTO_V1N') do
|
274
|
+
req.send_request
|
275
|
+
end
|
257
276
|
end
|
258
277
|
|
259
278
|
# Gets an object from Amazon S3, decrypting data locally.
|
@@ -281,7 +300,9 @@ module Aws
|
|
281
300
|
envelope_location: envelope_location,
|
282
301
|
instruction_file_suffix: instruction_file_suffix,
|
283
302
|
}
|
284
|
-
|
303
|
+
Aws::Plugins::UserAgent.metric('S3_CRYPTO_V1N') do
|
304
|
+
req.send_request(target: block)
|
305
|
+
end
|
285
306
|
end
|
286
307
|
|
287
308
|
private
|
@@ -327,7 +348,7 @@ module Aws
|
|
327
348
|
elsif options[:encryption_key]
|
328
349
|
DefaultKeyProvider.new(options)
|
329
350
|
else
|
330
|
-
msg =
|
351
|
+
msg = 'you must pass a :kms_key_id, :key_provider, or :encryption_key'
|
331
352
|
raise ArgumentError, msg
|
332
353
|
end
|
333
354
|
end
|
@@ -347,8 +368,8 @@ module Aws
|
|
347
368
|
if [:metadata, :instruction_file].include?(location)
|
348
369
|
location
|
349
370
|
else
|
350
|
-
msg =
|
351
|
-
|
371
|
+
msg = ':envelope_location must be :metadata or :instruction_file '\
|
372
|
+
"got #{location.inspect}"
|
352
373
|
raise ArgumentError, msg
|
353
374
|
end
|
354
375
|
end
|
@@ -358,7 +379,7 @@ module Aws
|
|
358
379
|
if String === suffix
|
359
380
|
suffix
|
360
381
|
else
|
361
|
-
msg =
|
382
|
+
msg = ':instruction_file_suffix must be a String'
|
362
383
|
raise ArgumentError, msg
|
363
384
|
end
|
364
385
|
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,7 +23,17 @@ module Aws
|
|
20
23
|
x-amz-matdesc
|
21
24
|
)
|
22
25
|
|
23
|
-
|
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
|
@@ -28,8 +41,21 @@ module Aws
|
|
28
41
|
AES/CBC/PKCS7Padding
|
29
42
|
)
|
30
43
|
|
44
|
+
AUTH_REQUIRED_CEK_ALGS = %w(AES/GCM/NoPadding)
|
45
|
+
|
31
46
|
def call(context)
|
32
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
|
+
|
33
59
|
@handler.call(context)
|
34
60
|
end
|
35
61
|
|
@@ -38,9 +64,9 @@ module Aws
|
|
38
64
|
def attach_http_event_listeners(context)
|
39
65
|
|
40
66
|
context.http_response.on_headers(200) do
|
41
|
-
cipher = decryption_cipher(context)
|
42
|
-
decrypter = body_contains_auth_tag?(
|
43
|
-
authenticated_decrypter(context, cipher) :
|
67
|
+
cipher, envelope = decryption_cipher(context)
|
68
|
+
decrypter = body_contains_auth_tag?(envelope) ?
|
69
|
+
authenticated_decrypter(context, cipher, envelope) :
|
44
70
|
IODecrypter.new(cipher, context.http_response.body)
|
45
71
|
context.http_response.body = decrypter
|
46
72
|
end
|
@@ -60,8 +86,13 @@ module Aws
|
|
60
86
|
end
|
61
87
|
|
62
88
|
def decryption_cipher(context)
|
63
|
-
if envelope = get_encryption_envelope(context)
|
64
|
-
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]
|
65
96
|
else
|
66
97
|
raise Errors::DecryptionError, "unable to locate encryption envelope"
|
67
98
|
end
|
@@ -97,13 +128,12 @@ module Aws
|
|
97
128
|
end
|
98
129
|
|
99
130
|
def extract_envelope(hash)
|
131
|
+
return nil unless hash
|
100
132
|
return v1_envelope(hash) if hash.key?('x-amz-key')
|
101
133
|
return v2_envelope(hash) if hash.key?('x-amz-key-v2')
|
102
134
|
if hash.keys.any? { |key| key.match(/^x-amz-key-(.+)$/) }
|
103
135
|
msg = "unsupported envelope encryption version #{$1}"
|
104
136
|
raise Errors::DecryptionError, msg
|
105
|
-
else
|
106
|
-
nil # no envelope found
|
107
137
|
end
|
108
138
|
end
|
109
139
|
|
@@ -117,39 +147,27 @@ module Aws
|
|
117
147
|
msg = "unsupported content encrypting key (cek) format: #{alg}"
|
118
148
|
raise Errors::DecryptionError, msg
|
119
149
|
end
|
120
|
-
unless envelope['x-amz-wrap-alg']
|
121
|
-
# possible to support
|
122
|
-
# RSA/ECB/OAEPWithSHA-256AndMGF1Padding
|
150
|
+
unless POSSIBLE_WRAPPING_FORMATS.include? envelope['x-amz-wrap-alg']
|
123
151
|
alg = envelope['x-amz-wrap-alg'].inspect
|
124
152
|
msg = "unsupported key wrapping algorithm: #{alg}"
|
125
153
|
raise Errors::DecryptionError, msg
|
126
154
|
end
|
127
|
-
unless V2_ENVELOPE_KEYS
|
155
|
+
unless (missing_keys = V2_ENVELOPE_KEYS - envelope.keys).empty?
|
128
156
|
msg = "incomplete v2 encryption envelope:\n"
|
129
|
-
msg += "
|
130
|
-
msg += " got: #{envelope_keys.join(', ')}"
|
157
|
+
msg += " missing: #{missing_keys.join(',')}\n"
|
131
158
|
raise Errors::DecryptionError, msg
|
132
159
|
end
|
133
160
|
envelope
|
134
161
|
end
|
135
162
|
|
136
|
-
# When the x-amz-meta-x-amz-tag-len header is present, it indicates
|
137
|
-
# that the body of this object has a trailing auth tag. The header
|
138
|
-
# indicates the length of that tag.
|
139
|
-
#
|
140
163
|
# This method fetches the tag from the end of the object by
|
141
164
|
# making a GET Object w/range request. This auth tag is used
|
142
165
|
# to initialize the cipher, and the decrypter truncates the
|
143
166
|
# auth tag from the body when writing the final bytes.
|
144
|
-
def authenticated_decrypter(context, cipher)
|
145
|
-
if RUBY_VERSION.match(/1.9/)
|
146
|
-
raise "authenticated decryption not supported by OpeenSSL in Ruby version ~> 1.9"
|
147
|
-
raise Aws::Errors::NonSupportedRubyVersionError, msg
|
148
|
-
end
|
167
|
+
def authenticated_decrypter(context, cipher, envelope)
|
149
168
|
http_resp = context.http_response
|
150
169
|
content_length = http_resp.headers['content-length'].to_i
|
151
|
-
auth_tag_length =
|
152
|
-
auth_tag_length = auth_tag_length.to_i / 8
|
170
|
+
auth_tag_length = auth_tag_length(envelope)
|
153
171
|
|
154
172
|
auth_tag = context.client.get_object(
|
155
173
|
bucket: context.params[:bucket],
|
@@ -161,16 +179,40 @@ module Aws
|
|
161
179
|
cipher.auth_data = ''
|
162
180
|
|
163
181
|
# The encrypted object contains both the cipher text
|
164
|
-
# plus a trailing auth tag.
|
165
|
-
# expect for the trailing auth tag.
|
182
|
+
# plus a trailing auth tag.
|
166
183
|
IOAuthDecrypter.new(
|
167
184
|
io: http_resp.body,
|
168
185
|
encrypted_content_length: content_length - auth_tag_length,
|
169
186
|
cipher: cipher)
|
170
187
|
end
|
171
188
|
|
172
|
-
def body_contains_auth_tag?(
|
173
|
-
|
189
|
+
def body_contains_auth_tag?(envelope)
|
190
|
+
AUTH_REQUIRED_CEK_ALGS.include?(envelope['x-amz-cek-alg'])
|
191
|
+
end
|
192
|
+
|
193
|
+
# Determine the auth tag length from the algorithm
|
194
|
+
# Validate it against the value provided in the x-amz-tag-len
|
195
|
+
# Return the tag length in bytes
|
196
|
+
def auth_tag_length(envelope)
|
197
|
+
tag_length =
|
198
|
+
case envelope['x-amz-cek-alg']
|
199
|
+
when 'AES/GCM/NoPadding' then AES_GCM_TAG_LEN_BYTES
|
200
|
+
else
|
201
|
+
raise ArgumentError, 'Unsupported cek-alg: ' \
|
202
|
+
"#{envelope['x-amz-cek-alg']}"
|
203
|
+
end
|
204
|
+
if (tag_length * 8) != envelope['x-amz-tag-len'].to_i
|
205
|
+
raise Errors::DecryptionError, 'x-amz-tag-len does not match expected'
|
206
|
+
end
|
207
|
+
tag_length
|
208
|
+
end
|
209
|
+
|
210
|
+
def apply_cse_user_agent(context)
|
211
|
+
if context.config.user_agent_suffix.nil?
|
212
|
+
context.config.user_agent_suffix = EC_USER_AGENT
|
213
|
+
elsif !context.config.user_agent_suffix.include? EC_USER_AGENT
|
214
|
+
context.config.user_agent_suffix += " #{EC_USER_AGENT}"
|
215
|
+
end
|
174
216
|
end
|
175
217
|
|
176
218
|
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
|
-
|
30
|
-
|
31
|
-
|
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
|
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
|
40
|
-
|
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
|
@@ -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
|
11
|
-
|
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
|
-
@
|
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
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'base64'
|
2
4
|
|
3
5
|
module Aws
|
@@ -15,11 +17,13 @@ module Aws
|
|
15
17
|
# envelope and encryption cipher.
|
16
18
|
def encryption_cipher
|
17
19
|
encryption_context = { "kms_cmk_id" => @kms_key_id }
|
18
|
-
key_data =
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
key_data = Aws::Plugins::UserAgent.metric('S3_CRYPTO_V1N') do
|
21
|
+
@kms_client.generate_data_key(
|
22
|
+
key_id: @kms_key_id,
|
23
|
+
encryption_context: encryption_context,
|
24
|
+
key_spec: 'AES_256'
|
25
|
+
)
|
26
|
+
end
|
23
27
|
cipher = Utils.aes_encryption_cipher(:CBC)
|
24
28
|
cipher.key = key_data.plaintext
|
25
29
|
envelope = {
|
@@ -34,15 +38,38 @@ module Aws
|
|
34
38
|
|
35
39
|
# @return [Cipher] Given an encryption envelope, returns a
|
36
40
|
# decryption cipher.
|
37
|
-
def decryption_cipher(envelope)
|
41
|
+
def decryption_cipher(envelope, options = {})
|
38
42
|
encryption_context = Json.load(envelope['x-amz-matdesc'])
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
+
cek_alg = envelope['x-amz-cek-alg']
|
44
|
+
|
45
|
+
case envelope['x-amz-wrap-alg']
|
46
|
+
when 'kms'; # NO OP
|
47
|
+
when 'kms+context'
|
48
|
+
if cek_alg != encryption_context['aws:x-amz-cek-alg']
|
49
|
+
raise Errors::DecryptionError, 'Value of cek-alg from envelope'\
|
50
|
+
' does not match the value in the encryption context'
|
51
|
+
end
|
52
|
+
when 'AES/GCM'
|
53
|
+
raise ArgumentError, 'Key mismatch - Client is configured' \
|
54
|
+
' with a KMS key and the x-amz-wrap-alg is AES/GCM.'
|
55
|
+
when 'RSA-OAEP-SHA1'
|
56
|
+
raise ArgumentError, 'Key mismatch - Client is configured' \
|
57
|
+
' with a KMS key and the x-amz-wrap-alg is RSA-OAEP-SHA1.'
|
58
|
+
else
|
59
|
+
raise ArgumentError, 'Unsupported wrap-alg: ' \
|
60
|
+
"#{envelope['x-amz-wrap-alg']}"
|
61
|
+
end
|
62
|
+
|
63
|
+
key = Aws::Plugins::UserAgent.metric('S3_CRYPTO_V1N') do
|
64
|
+
@kms_client.decrypt(
|
65
|
+
ciphertext_blob: decode64(envelope['x-amz-key-v2']),
|
66
|
+
encryption_context: encryption_context
|
67
|
+
).plaintext
|
68
|
+
end
|
69
|
+
|
43
70
|
iv = decode64(envelope['x-amz-iv'])
|
44
71
|
block_mode =
|
45
|
-
case
|
72
|
+
case cek_alg
|
46
73
|
when 'AES/CBC/PKCS5Padding'
|
47
74
|
:CBC
|
48
75
|
when 'AES/CBC/PKCS7Padding'
|
@@ -59,6 +86,14 @@ module Aws
|
|
59
86
|
|
60
87
|
private
|
61
88
|
|
89
|
+
def build_encryption_context(cek_alg, options = {})
|
90
|
+
kms_context = (options[:kms_encryption_context] || {})
|
91
|
+
.each_with_object({}) { |(k, v), h| h[k.to_s] = v }
|
92
|
+
{
|
93
|
+
'aws:x-amz-cek-alg' => cek_alg
|
94
|
+
}.merge(kms_context)
|
95
|
+
end
|
96
|
+
|
62
97
|
def encode64(str)
|
63
98
|
Base64.encode64(str).split("\n") * ""
|
64
99
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'base64'
|
2
4
|
|
3
5
|
module Aws
|
@@ -32,14 +34,14 @@ module Aws
|
|
32
34
|
if [32, 24, 16].include?(key.bytesize)
|
33
35
|
key
|
34
36
|
else
|
35
|
-
msg =
|
36
|
-
|
37
|
+
msg = 'invalid key, symmetric key required to be 16, 24, or '\
|
38
|
+
'32 bytes in length, saw length ' + key.bytesize.to_s
|
37
39
|
raise ArgumentError, msg
|
38
40
|
end
|
39
41
|
else
|
40
|
-
msg =
|
41
|
-
|
42
|
-
|
42
|
+
msg = 'invalid encryption key, expected an OpenSSL::PKey::RSA key '\
|
43
|
+
'(for asymmetric encryption) or a String (for symmetric '\
|
44
|
+
'encryption).'
|
43
45
|
raise ArgumentError, msg
|
44
46
|
end
|
45
47
|
end
|
@@ -48,7 +50,7 @@ module Aws
|
|
48
50
|
Json.load(description)
|
49
51
|
description
|
50
52
|
rescue Json::ParseError, EncodingError
|
51
|
-
msg =
|
53
|
+
msg = 'expected description to be a valid JSON document string'
|
52
54
|
raise ArgumentError, msg
|
53
55
|
end
|
54
56
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'openssl'
|
2
4
|
|
3
5
|
module Aws
|
@@ -37,6 +39,29 @@ module Aws
|
|
37
39
|
end
|
38
40
|
end
|
39
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
|
+
|
40
65
|
# @param [String] block_mode "CBC" or "ECB"
|
41
66
|
# @param [OpenSSL::PKey::RSA, String, nil] key
|
42
67
|
# @param [String, nil] iv The initialization vector
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'aws-sdk-s3/encryption/client'
|
2
4
|
require 'aws-sdk-s3/encryption/decrypt_handler'
|
3
5
|
require 'aws-sdk-s3/encryption/default_cipher_provider'
|
@@ -15,5 +17,7 @@ require 'aws-sdk-s3/encryption/default_key_provider'
|
|
15
17
|
module Aws
|
16
18
|
module S3
|
17
19
|
module Encryption; end
|
20
|
+
AES_GCM_TAG_LEN_BYTES = 16
|
21
|
+
EC_USER_AGENT = 'S3CryptoV1n'
|
18
22
|
end
|
19
23
|
end
|