aws-sdk-s3 1.73.0 → 1.78.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 +4 -4
- data/lib/aws-sdk-s3.rb +1 -1
- data/lib/aws-sdk-s3/bucket.rb +2 -2
- data/lib/aws-sdk-s3/client.rb +127 -114
- data/lib/aws-sdk-s3/customizations/object.rb +12 -1
- 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 +11 -0
- data/lib/aws-sdk-s3/multipart_file_uploader.rb +37 -2
- data/lib/aws-sdk-s3/multipart_upload_part.rb +1 -1
- data/lib/aws-sdk-s3/object.rb +1 -1
- data/lib/aws-sdk-s3/object_summary.rb +19 -3
- data/lib/aws-sdk-s3/plugins/accelerate.rb +27 -38
- data/lib/aws-sdk-s3/plugins/dualstack.rb +3 -1
- data/lib/aws-sdk-s3/plugins/sse_cpk.rb +1 -1
- data/lib/aws-sdk-s3/presigned_post.rb +61 -28
- data/lib/aws-sdk-s3/presigner.rb +2 -2
- data/lib/aws-sdk-s3/resource.rb +1 -1
- data/lib/aws-sdk-s3/types.rb +25 -8
- metadata +4 -4
@@ -296,6 +296,14 @@ module Aws
|
|
296
296
|
# etag = response.etag
|
297
297
|
# end
|
298
298
|
#
|
299
|
+
# You can provide a callback to monitor progress of the upload:
|
300
|
+
#
|
301
|
+
# # bytes and totals are each an array with 1 entry per part
|
302
|
+
# progress = Proc.new do |bytes, totals|
|
303
|
+
# puts bytes.map.with_index { |b, i| "Part #{i+1}: #{b} / #{totals[i]}"}.join(' ') + "Total: #{100.0 * bytes.sum / totals.sum }%" }
|
304
|
+
# end
|
305
|
+
# obj.upload_file('/path/to/file')
|
306
|
+
#
|
299
307
|
# @param [String, Pathname, File, Tempfile] source A file on the local
|
300
308
|
# file system that will be uploaded as this object. This can either be
|
301
309
|
# a String or Pathname to the file, an open File object, or an open
|
@@ -312,6 +320,10 @@ module Aws
|
|
312
320
|
# multipart uploads. This option is not used if the file is smaller than
|
313
321
|
# `:multipart_threshold`.
|
314
322
|
#
|
323
|
+
# @option options [Proc] :progress_callback
|
324
|
+
# A Proc that will be called when each chunk of the upload is sent.
|
325
|
+
# It will be invoked with [bytes_read], [total_sizes]
|
326
|
+
#
|
315
327
|
# @raise [MultipartUploadError] If an object is being uploaded in
|
316
328
|
# parts, and the upload can not be completed, then the upload is
|
317
329
|
# aborted and this error is raised. The raised error has a `#errors`
|
@@ -320,7 +332,6 @@ module Aws
|
|
320
332
|
#
|
321
333
|
# @return [Boolean] Returns `true` when the object is uploaded
|
322
334
|
# without any errors.
|
323
|
-
#
|
324
335
|
def upload_file(source, options = {})
|
325
336
|
uploading_options = options.dup
|
326
337
|
uploader = FileUploader.new(
|
@@ -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
|