aws-sdk-resources 2.11.561 → 2.11.562
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/aws-sdk-resources/services/s3.rb +1 -0
- data/lib/aws-sdk-resources/services/s3/encryption.rb +3 -0
- data/lib/aws-sdk-resources/services/s3/encryption/client.rb +24 -7
- data/lib/aws-sdk-resources/services/s3/encryption/decrypt_handler.rb +65 -25
- data/lib/aws-sdk-resources/services/s3/encryption/default_cipher_provider.rb +43 -5
- data/lib/aws-sdk-resources/services/s3/encryption/default_key_provider.rb +2 -0
- data/lib/aws-sdk-resources/services/s3/encryption/encrypt_handler.rb +13 -2
- data/lib/aws-sdk-resources/services/s3/encryption/errors.rb +2 -0
- data/lib/aws-sdk-resources/services/s3/encryption/io_auth_decrypter.rb +2 -0
- data/lib/aws-sdk-resources/services/s3/encryption/io_decrypter.rb +8 -1
- data/lib/aws-sdk-resources/services/s3/encryption/io_encrypter.rb +2 -0
- data/lib/aws-sdk-resources/services/s3/encryption/key_provider.rb +2 -0
- data/lib/aws-sdk-resources/services/s3/encryption/kms_cipher_provider.rb +36 -3
- data/lib/aws-sdk-resources/services/s3/encryption/materials.rb +8 -6
- data/lib/aws-sdk-resources/services/s3/encryption/utils.rb +25 -0
- data/lib/aws-sdk-resources/services/s3/encryptionV2/client.rb +559 -0
- data/lib/aws-sdk-resources/services/s3/encryptionV2/decrypt_handler.rb +214 -0
- data/lib/aws-sdk-resources/services/s3/encryptionV2/default_cipher_provider.rb +170 -0
- data/lib/aws-sdk-resources/services/s3/encryptionV2/default_key_provider.rb +40 -0
- data/lib/aws-sdk-resources/services/s3/encryptionV2/encrypt_handler.rb +69 -0
- data/lib/aws-sdk-resources/services/s3/encryptionV2/errors.rb +37 -0
- data/lib/aws-sdk-resources/services/s3/encryptionV2/io_auth_decrypter.rb +58 -0
- data/lib/aws-sdk-resources/services/s3/encryptionV2/io_decrypter.rb +37 -0
- data/lib/aws-sdk-resources/services/s3/encryptionV2/io_encrypter.rb +73 -0
- data/lib/aws-sdk-resources/services/s3/encryptionV2/key_provider.rb +31 -0
- data/lib/aws-sdk-resources/services/s3/encryptionV2/kms_cipher_provider.rb +169 -0
- data/lib/aws-sdk-resources/services/s3/encryptionV2/materials.rb +60 -0
- data/lib/aws-sdk-resources/services/s3/encryptionV2/utils.rb +103 -0
- data/lib/aws-sdk-resources/services/s3/encryption_v2.rb +24 -0
- metadata +18 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b6ac0e253aa72fe0c66930c23a50c0f1e944e79e500febf995d11aba32bc80a7
|
4
|
+
data.tar.gz: 8f60593f8927f98912a6655231a22cdfadc747e276d0f1ef277835f0a8dc3163
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
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
|
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 =
|
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 =
|
351
|
-
|
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 =
|
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
|
-
|
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?(
|
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]
|
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']
|
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
|
144
|
+
unless (missing_keys = V2_ENVELOPE_KEYS - envelope.keys).empty?
|
127
145
|
msg = "incomplete v2 encryption envelope:\n"
|
128
|
-
msg += "
|
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 =
|
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.
|
160
|
-
|
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?(
|
168
|
-
|
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
|
-
|
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
|
@@ -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
|
-
|
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
|