aws-sdk-s3 1.207.0 → 1.208.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/CHANGELOG.md +5 -0
- data/VERSION +1 -1
- data/lib/aws-sdk-s3/client.rb +1 -1
- data/lib/aws-sdk-s3/customizations.rb +1 -0
- data/lib/aws-sdk-s3/encryption/client.rb +2 -2
- data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +2 -0
- data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +2 -0
- data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +2 -0
- data/lib/aws-sdk-s3/encryptionV2/client.rb +98 -23
- data/lib/aws-sdk-s3/encryptionV2/decrypt_handler.rb +7 -162
- data/lib/aws-sdk-s3/encryptionV2/decryption.rb +205 -0
- data/lib/aws-sdk-s3/encryptionV2/default_cipher_provider.rb +17 -0
- data/lib/aws-sdk-s3/encryptionV2/encrypt_handler.rb +2 -0
- data/lib/aws-sdk-s3/encryptionV2/io_encrypter.rb +2 -0
- data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +8 -0
- data/lib/aws-sdk-s3/encryptionV2/utils.rb +5 -0
- data/lib/aws-sdk-s3/encryptionV3/client.rb +885 -0
- data/lib/aws-sdk-s3/encryptionV3/decrypt_handler.rb +98 -0
- data/lib/aws-sdk-s3/encryptionV3/decryption.rb +244 -0
- data/lib/aws-sdk-s3/encryptionV3/default_cipher_provider.rb +159 -0
- data/lib/aws-sdk-s3/encryptionV3/default_key_provider.rb +35 -0
- data/lib/aws-sdk-s3/encryptionV3/encrypt_handler.rb +98 -0
- data/lib/aws-sdk-s3/encryptionV3/errors.rb +47 -0
- data/lib/aws-sdk-s3/encryptionV3/io_auth_decrypter.rb +60 -0
- data/lib/aws-sdk-s3/encryptionV3/io_decrypter.rb +35 -0
- data/lib/aws-sdk-s3/encryptionV3/io_encrypter.rb +84 -0
- data/lib/aws-sdk-s3/encryptionV3/key_provider.rb +28 -0
- data/lib/aws-sdk-s3/encryptionV3/kms_cipher_provider.rb +159 -0
- data/lib/aws-sdk-s3/encryptionV3/materials.rb +58 -0
- data/lib/aws-sdk-s3/encryptionV3/utils.rb +321 -0
- data/lib/aws-sdk-s3/encryption_v2.rb +1 -0
- data/lib/aws-sdk-s3/encryption_v3.rb +24 -0
- data/lib/aws-sdk-s3.rb +1 -1
- metadata +17 -1
|
@@ -0,0 +1,885 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'forwardable'
|
|
4
|
+
|
|
5
|
+
module Aws
|
|
6
|
+
module S3
|
|
7
|
+
# Provides an encryption client that encrypts and decrypts data client-side,
|
|
8
|
+
# storing the encrypted data in Amazon S3. The `EncryptionV3::Client` (V3 Client)
|
|
9
|
+
# provides improved security over the `EncryptionV2::Client` (V2 Client)
|
|
10
|
+
# through key commitment. You can use the V3 Client to continue decrypting
|
|
11
|
+
# objects encrypted by V2 by setting security_profile: :v3_and_legacy.
|
|
12
|
+
# The latest V2 Client also supports reading and decrypting objects
|
|
13
|
+
# encrypted by the V3 Client.
|
|
14
|
+
#
|
|
15
|
+
# This client uses a process called "envelope encryption". Your private
|
|
16
|
+
# encryption keys and your data's plain-text are **never** sent to
|
|
17
|
+
# Amazon S3. **If you lose you encryption keys, you will not be able to
|
|
18
|
+
# decrypt your data.**
|
|
19
|
+
#
|
|
20
|
+
# ## Key Commitment
|
|
21
|
+
#
|
|
22
|
+
# Key commitment (also known as robustness) is a security property that
|
|
23
|
+
# guarantees that each ciphertext can be decrypted to only a single plaintext.
|
|
24
|
+
# This prevents sophisticated attacks where a ciphertext could theoretically
|
|
25
|
+
# decrypt to different plaintexts under different keys.
|
|
26
|
+
#
|
|
27
|
+
# The V3 client encrypts with key commitment by default using the
|
|
28
|
+
# `:alg_aes_256_gcm_hkdf_sha512_commit_key` algorithm. Key commitment adds
|
|
29
|
+
# approximately 32 bytes to each encrypted object and slightly increases
|
|
30
|
+
# processing time, but significantly enhances security.
|
|
31
|
+
#
|
|
32
|
+
# ## Envelope Encryption Overview
|
|
33
|
+
#
|
|
34
|
+
# The goal of envelope encryption is to combine the performance of
|
|
35
|
+
# fast symmetric encryption while maintaining the secure key management
|
|
36
|
+
# that asymmetric keys provide.
|
|
37
|
+
#
|
|
38
|
+
# A one-time-use symmetric key (envelope key) is generated client-side.
|
|
39
|
+
# This is used to encrypt the data client-side. This key is then
|
|
40
|
+
# encrypted by your master key and stored alongside your data in Amazon
|
|
41
|
+
# S3.
|
|
42
|
+
#
|
|
43
|
+
# When accessing your encrypted data with the encryption client,
|
|
44
|
+
# the encrypted envelope key is retrieved and decrypted client-side
|
|
45
|
+
# with your master key. The envelope key is then used to decrypt the
|
|
46
|
+
# data client-side.
|
|
47
|
+
#
|
|
48
|
+
# One of the benefits of envelope encryption is that if your master key
|
|
49
|
+
# is compromised, you have the option of just re-encrypting the stored
|
|
50
|
+
# envelope symmetric keys, instead of re-encrypting all of the
|
|
51
|
+
# data in your account.
|
|
52
|
+
#
|
|
53
|
+
# ## Basic Usage
|
|
54
|
+
#
|
|
55
|
+
# The encryption client requires an {Aws::S3::Client}. If you do not
|
|
56
|
+
# provide a `:client`, then a client will be constructed for you.
|
|
57
|
+
#
|
|
58
|
+
# require 'openssl'
|
|
59
|
+
# key = OpenSSL::PKey::RSA.new(1024)
|
|
60
|
+
#
|
|
61
|
+
# # encryption client
|
|
62
|
+
# s3 = Aws::S3::EncryptionV3::Client.new(
|
|
63
|
+
# encryption_key: key,
|
|
64
|
+
# key_wrap_schema: :rsa_oaep_sha1 # the key_wrap_schema must be rsa_oaep_sha1 for asymmetric keys
|
|
65
|
+
# )
|
|
66
|
+
#
|
|
67
|
+
# # round-trip an object, encrypted/decrypted locally
|
|
68
|
+
# s3.put_object(bucket:'aws-sdk', key:'secret', body:'handshake')
|
|
69
|
+
# s3.get_object(bucket:'aws-sdk', key:'secret').body.read
|
|
70
|
+
# #=> 'handshake'
|
|
71
|
+
#
|
|
72
|
+
# # reading encrypted object without the encryption client
|
|
73
|
+
# # results in the getting the cipher text
|
|
74
|
+
# Aws::S3::Client.new.get_object(bucket:'aws-sdk', key:'secret').body.read
|
|
75
|
+
# #=> "... cipher text ..."
|
|
76
|
+
#
|
|
77
|
+
# ## Required Configuration
|
|
78
|
+
#
|
|
79
|
+
# You must configure the following:
|
|
80
|
+
#
|
|
81
|
+
# * a key or key provider - See the Keys section below. The key provided determines
|
|
82
|
+
# the key wrapping schema(s) supported for both encryption and decryption.
|
|
83
|
+
# * `key_wrap_schema` - The key wrapping schema. It must match the type of key configured.
|
|
84
|
+
#
|
|
85
|
+
# The following have defaults and are optional:
|
|
86
|
+
#
|
|
87
|
+
# * `content_encryption_schema` - Defaults to `:alg_aes_256_gcm_hkdf_sha512_commit_key`
|
|
88
|
+
# * `security_profile` - Defaults to `:v3`. Set to `:v3_and_legacy` to read V2-encrypted objects.
|
|
89
|
+
# * `commitment_policy` - Defaults to `:require_encrypt_require_decrypt` (most secure)
|
|
90
|
+
#
|
|
91
|
+
# ## Keys
|
|
92
|
+
#
|
|
93
|
+
# For client-side encryption to work, you must provide one of the following:
|
|
94
|
+
#
|
|
95
|
+
# * An encryption key
|
|
96
|
+
# * A {KeyProvider}
|
|
97
|
+
# * A KMS encryption key id
|
|
98
|
+
#
|
|
99
|
+
# Additionally, the key wrapping schema must agree with the type of the key:
|
|
100
|
+
# * :aes_gcm: An AES encryption key or a key provider.
|
|
101
|
+
# * :rsa_oaep_sha1: An RSA encryption key or key provider.
|
|
102
|
+
# * :kms_context: A KMS encryption key id
|
|
103
|
+
#
|
|
104
|
+
# ### An Encryption Key
|
|
105
|
+
#
|
|
106
|
+
# You can pass a single encryption key. This is used as a master key
|
|
107
|
+
# encrypting and decrypting all object keys.
|
|
108
|
+
#
|
|
109
|
+
# key = OpenSSL::Cipher.new("AES-256-ECB").random_key # symmetric key - used with `key_wrap_schema: :aes_gcm`
|
|
110
|
+
# key = OpenSSL::PKey::RSA.new(1024) # asymmetric key pair - used with `key_wrap_schema: :rsa_oaep_sha1`
|
|
111
|
+
#
|
|
112
|
+
# s3 = Aws::S3::EncryptionV3::Client.new(
|
|
113
|
+
# encryption_key: key,
|
|
114
|
+
# key_wrap_schema: :aes_gcm # or :rsa_oaep_sha1 if using RSA
|
|
115
|
+
# )
|
|
116
|
+
#
|
|
117
|
+
# ### Key Provider
|
|
118
|
+
#
|
|
119
|
+
# Alternatively, you can use a {KeyProvider}. A key provider makes
|
|
120
|
+
# it easy to work with multiple keys and simplifies key rotation.
|
|
121
|
+
#
|
|
122
|
+
# ### KMS Encryption Key Id
|
|
123
|
+
#
|
|
124
|
+
# If you pass the id of an AWS Key Management Service (KMS) key and
|
|
125
|
+
# use :kms_content for the key_wrap_schema, then KMS will be used to
|
|
126
|
+
# generate, encrypt and decrypt object keys.
|
|
127
|
+
#
|
|
128
|
+
# # keep track of the kms key id
|
|
129
|
+
# kms = Aws::KMS::Client.new
|
|
130
|
+
# key_id = kms.create_key.key_metadata.key_id
|
|
131
|
+
#
|
|
132
|
+
# Aws::S3::EncryptionV3::Client.new(
|
|
133
|
+
# kms_key_id: key_id,
|
|
134
|
+
# kms_client: kms,
|
|
135
|
+
# key_wrap_schema: :kms_context
|
|
136
|
+
# )
|
|
137
|
+
#
|
|
138
|
+
# ## Custom Key Providers
|
|
139
|
+
#
|
|
140
|
+
# A {KeyProvider} is any object that responds to:
|
|
141
|
+
#
|
|
142
|
+
# * `#encryption_materials`
|
|
143
|
+
# * `#key_for(materials_description)`
|
|
144
|
+
#
|
|
145
|
+
# Here is a trivial implementation of an in-memory key provider.
|
|
146
|
+
# This is provided as a demonstration of the key provider interface,
|
|
147
|
+
# and should not be used in production:
|
|
148
|
+
#
|
|
149
|
+
# class KeyProvider
|
|
150
|
+
#
|
|
151
|
+
# def initialize(default_key_name, keys)
|
|
152
|
+
# @keys = keys
|
|
153
|
+
# @encryption_materials = Aws::S3::EncryptionV3::Materials.new(
|
|
154
|
+
# key: @keys[default_key_name],
|
|
155
|
+
# description: JSON.dump(key: default_key_name),
|
|
156
|
+
# )
|
|
157
|
+
# end
|
|
158
|
+
#
|
|
159
|
+
# attr_reader :encryption_materials
|
|
160
|
+
#
|
|
161
|
+
# def key_for(matdesc)
|
|
162
|
+
# key_name = JSON.parse(matdesc)['key']
|
|
163
|
+
# if key = @keys[key_name]
|
|
164
|
+
# key
|
|
165
|
+
# else
|
|
166
|
+
# raise "encryption key not found for: #{matdesc.inspect}"
|
|
167
|
+
# end
|
|
168
|
+
# end
|
|
169
|
+
# end
|
|
170
|
+
#
|
|
171
|
+
# Given the above key provider, you can create an encryption client that
|
|
172
|
+
# chooses the key to use based on the materials description stored with
|
|
173
|
+
# the encrypted object. This makes it possible to use multiple keys
|
|
174
|
+
# and simplifies key rotation.
|
|
175
|
+
#
|
|
176
|
+
# # uses "new-key" for encrypting objects, uses either for decrypting
|
|
177
|
+
# keys = KeyProvider.new('new-key', {
|
|
178
|
+
# "old-key" => Base64.decode64("kM5UVbhE/4rtMZJfsadYEdm2vaKFsmV2f5+URSeUCV4="),
|
|
179
|
+
# "new-key" => Base64.decode64("w1WLio3agRWRTSJK/Ouh8NHoqRQ6fn5WbSXDTHjXMSo="),
|
|
180
|
+
# }),
|
|
181
|
+
#
|
|
182
|
+
# # chooses the key based on the materials description stored
|
|
183
|
+
# # with the encrypted object
|
|
184
|
+
# s3 = Aws::S3::EncryptionV3::Client.new(
|
|
185
|
+
# key_provider: keys,
|
|
186
|
+
# key_wrap_schema: :aes_gcm # or :rsa_oaep_sha1 for RSA keys
|
|
187
|
+
# )
|
|
188
|
+
#
|
|
189
|
+
# ## Materials Description
|
|
190
|
+
#
|
|
191
|
+
# A materials description is JSON document string that is stored
|
|
192
|
+
# in the metadata (or instruction file) of an encrypted object.
|
|
193
|
+
# The {DefaultKeyProvider} uses the empty JSON document `"{}"`.
|
|
194
|
+
#
|
|
195
|
+
# When building a key provider, you are free to store whatever
|
|
196
|
+
# information you need to identify the master key that was used
|
|
197
|
+
# to encrypt the object.
|
|
198
|
+
#
|
|
199
|
+
# ## Envelope Location
|
|
200
|
+
#
|
|
201
|
+
# By default, the encryption client store the encryption envelope
|
|
202
|
+
# with the object, as metadata. You can choose to have the envelope
|
|
203
|
+
# stored in a separate "instruction file". An instruction file
|
|
204
|
+
# is an object, with the key of the encrypted object, suffixed with
|
|
205
|
+
# `".instruction"`.
|
|
206
|
+
#
|
|
207
|
+
# Specify the `:envelope_location` option as `:instruction_file` to
|
|
208
|
+
# use an instruction file for storing the envelope.
|
|
209
|
+
#
|
|
210
|
+
# # default behavior
|
|
211
|
+
# s3 = Aws::S3::EncryptionV3::Client.new(
|
|
212
|
+
# encryption_key: your_key,
|
|
213
|
+
# key_wrap_schema: :aes_gcm,
|
|
214
|
+
# envelope_location: :metadata
|
|
215
|
+
# )
|
|
216
|
+
#
|
|
217
|
+
# # store envelope in a separate object
|
|
218
|
+
# s3 = Aws::S3::EncryptionV3::Client.new(
|
|
219
|
+
# encryption_key: your_key,
|
|
220
|
+
# key_wrap_schema: :aes_gcm,
|
|
221
|
+
# envelope_location: :instruction_file,
|
|
222
|
+
# instruction_file_suffix: '.instruction' # default
|
|
223
|
+
# )
|
|
224
|
+
#
|
|
225
|
+
# When using an instruction file, multiple requests are made when
|
|
226
|
+
# putting and getting the object. **This may cause issues if you are
|
|
227
|
+
# issuing concurrent PUT and GET requests to an encrypted object.**
|
|
228
|
+
#
|
|
229
|
+
# ## Commitment Policies Explained
|
|
230
|
+
#
|
|
231
|
+
# * `:forbid_encrypt_allow_decrypt` - Encrypts without key commitment (for
|
|
232
|
+
# backward compatibility with systems that have not been updated), but can decrypt
|
|
233
|
+
# objects with or without commitment. Use if you are not sure that all readers
|
|
234
|
+
# can decrypt objects encrypted with key commitment.
|
|
235
|
+
#
|
|
236
|
+
# * `:require_encrypt_allow_decrypt` - Encrypts with key commitment, can decrypt
|
|
237
|
+
# objects with or without commitment. Use once all readers
|
|
238
|
+
# can decrypt objects encrypted with key commitment.
|
|
239
|
+
#
|
|
240
|
+
# * `:require_encrypt_require_decrypt` - Encrypts with key commitment, can only
|
|
241
|
+
# decrypt objects with key commitment. **Recommended for new applications and
|
|
242
|
+
# after migrations are complete.** This is the default.
|
|
243
|
+
#
|
|
244
|
+
module EncryptionV3
|
|
245
|
+
class Client
|
|
246
|
+
##= ../specification/s3-encryption/client.md#aws-sdk-compatibility
|
|
247
|
+
##= type=implication
|
|
248
|
+
##% The S3EC MUST provide a different set of configuration options than the conventional S3 client.
|
|
249
|
+
|
|
250
|
+
REQUIRED_PARAMS = [:key_wrap_schema].freeze
|
|
251
|
+
|
|
252
|
+
OPTIONAL_PARAMS = [
|
|
253
|
+
:kms_key_id,
|
|
254
|
+
:kms_client,
|
|
255
|
+
:key_provider,
|
|
256
|
+
:encryption_key,
|
|
257
|
+
:envelope_location,
|
|
258
|
+
##= ../specification/s3-encryption/client.md#instruction-file-configuration
|
|
259
|
+
##% In this case, the Instruction File Configuration SHOULD be optional, such that its default configuration is used when none is provided.
|
|
260
|
+
:instruction_file_suffix,
|
|
261
|
+
##= ../specification/s3-encryption/client.md#encryption-algorithm
|
|
262
|
+
##% The S3EC MUST support configuration of the encryption algorithm (or algorithm suite) during its initialization.
|
|
263
|
+
:content_encryption_schema,
|
|
264
|
+
:security_profile,
|
|
265
|
+
##= ../specification/s3-encryption/client.md#key-commitment
|
|
266
|
+
##% The S3EC MUST support configuration of the [Key Commitment policy](./key-commitment.md) during its initialization.
|
|
267
|
+
:commitment_policy
|
|
268
|
+
].freeze
|
|
269
|
+
SUPPORTED_COMMITMENT_POLICIES = %i[
|
|
270
|
+
forbid_encrypt_allow_decrypt
|
|
271
|
+
require_encrypt_allow_decrypt
|
|
272
|
+
require_encrypt_require_decrypt
|
|
273
|
+
].freeze
|
|
274
|
+
|
|
275
|
+
##= ../specification/s3-encryption/client.md#enable-legacy-wrapping-algorithms
|
|
276
|
+
##% The S3EC MUST support the option to enable or disable legacy wrapping algorithms.
|
|
277
|
+
##= ../specification/s3-encryption/client.md#enable-legacy-unauthenticated-modes
|
|
278
|
+
##% The S3EC MUST support the option to enable or disable legacy unauthenticated modes (content encryption algorithms).
|
|
279
|
+
SUPPORTED_SECURITY_PROFILES = %i[v3 v3_and_legacy].freeze
|
|
280
|
+
|
|
281
|
+
##= ../specification/s3-encryption/client.md#enable-legacy-unauthenticated-modes
|
|
282
|
+
##% The option to enable legacy unauthenticated modes MUST be set to false by default.
|
|
283
|
+
##= ../specification/s3-encryption/client.md#enable-legacy-wrapping-algorithms
|
|
284
|
+
##% The option to enable legacy wrapping algorithms MUST be set to false by default.
|
|
285
|
+
DEFAULT_SECURITY_PROFILES = :v3
|
|
286
|
+
DEFAULT_COMMITMENT_POLICIES = :require_encrypt_require_decrypt
|
|
287
|
+
DEFAULT_CONTENT_ENCRYPTION_SCHEMA = :alg_aes_256_gcm_hkdf_sha512_commit_key
|
|
288
|
+
|
|
289
|
+
extend Deprecations
|
|
290
|
+
extend Forwardable
|
|
291
|
+
def_delegators :@client, :config, :delete_object, :head_object, :build_request
|
|
292
|
+
|
|
293
|
+
# Creates a new encryption client.
|
|
294
|
+
#
|
|
295
|
+
# ## Required Configuration
|
|
296
|
+
#
|
|
297
|
+
# * a key or key provider - The key provided also determines the key wrapping
|
|
298
|
+
# schema(s) supported for both encryption and decryption.
|
|
299
|
+
# * `key_wrap_schema` - The key wrapping schema. It must match the type of key configured.
|
|
300
|
+
#
|
|
301
|
+
# ## Optional Configuration (with defaults)
|
|
302
|
+
#
|
|
303
|
+
# * `content_encryption_schema` - Defaults to `:alg_aes_256_gcm_hkdf_sha512_commit_key`
|
|
304
|
+
# * `security_profile` - Defaults to `:v3`. Set to `:v3_and_legacy` to read V2-encrypted objects.
|
|
305
|
+
# * `commitment_policy` - Defaults to `:require_encrypt_require_decrypt` (most secure)
|
|
306
|
+
#
|
|
307
|
+
# To configure the key you must provide one of the following set of options:
|
|
308
|
+
#
|
|
309
|
+
# * `:encryption_key`
|
|
310
|
+
# * `:kms_key_id`
|
|
311
|
+
# * `:key_provider`
|
|
312
|
+
#
|
|
313
|
+
# You may also pass any other options accepted by `Client#initialize`.
|
|
314
|
+
#
|
|
315
|
+
# @option options [S3::Client] :client A basic S3 client that is used
|
|
316
|
+
# to make api calls. If a `:client` is not provided, a new {S3::Client}
|
|
317
|
+
# will be constructed.
|
|
318
|
+
#
|
|
319
|
+
# @option options [OpenSSL::PKey::RSA, String] :encryption_key The master
|
|
320
|
+
# key to use for encrypting/decrypting all objects.
|
|
321
|
+
#
|
|
322
|
+
# @option options [String] :kms_key_id When you provide a `:kms_key_id`,
|
|
323
|
+
# then AWS Key Management Service (KMS) will be used to manage the
|
|
324
|
+
# object encryption keys. By default a {KMS::Client} will be
|
|
325
|
+
# constructed for KMS API calls. Alternatively, you can provide
|
|
326
|
+
# your own via `:kms_client`. To only support decryption/reads, you may
|
|
327
|
+
# provide `:allow_decrypt_with_any_cmk` which will use
|
|
328
|
+
# the implicit CMK associated with the data during reads but will
|
|
329
|
+
# not allow you to encrypt/write objects with this client.
|
|
330
|
+
#
|
|
331
|
+
# @option options [#key_for] :key_provider Any object that responds
|
|
332
|
+
# to `#key_for`. This method should accept a materials description
|
|
333
|
+
# JSON document string and return return an encryption key.
|
|
334
|
+
#
|
|
335
|
+
# @option options [required, Symbol] :key_wrap_schema The Key wrapping
|
|
336
|
+
# schema to be used. It must match the type of key configured.
|
|
337
|
+
# Must be one of the following:
|
|
338
|
+
#
|
|
339
|
+
# * :kms_context (Must provide kms_key_id)
|
|
340
|
+
# * :aes_gcm (Must provide an AES (string) key)
|
|
341
|
+
# * :rsa_oaep_sha1 (Must provide an RSA key)
|
|
342
|
+
#
|
|
343
|
+
# @option options [Symbol] :content_encryption_schema (:alg_aes_256_gcm_hkdf_sha512_commit_key)
|
|
344
|
+
# The content encryption algorithm to use. Defaults to the V3 algorithm with key commitment.
|
|
345
|
+
#
|
|
346
|
+
# @option options [Symbol] :security_profile (:v3)
|
|
347
|
+
# Determines the support for reading objects written using older
|
|
348
|
+
# encryption schemas. Must be one of the following:
|
|
349
|
+
#
|
|
350
|
+
# * :v3 - Only reads V3-encrypted objects (default, most secure)
|
|
351
|
+
# * :v3_and_legacy - Enables reading of V2-encrypted objects
|
|
352
|
+
#
|
|
353
|
+
# @option options [Symbol] :commitment_policy (:require_encrypt_require_decrypt)
|
|
354
|
+
# Determines support for key commitment. Must be one of the following:
|
|
355
|
+
#
|
|
356
|
+
# * :forbid_encrypt_allow_decrypt - Does not encrypt with key commitment,
|
|
357
|
+
# can decrypt with or without. Use only for specific compatibility needs.
|
|
358
|
+
# * :require_encrypt_allow_decrypt - Encrypts with key commitment, can
|
|
359
|
+
# decrypt with or without.
|
|
360
|
+
# * :require_encrypt_require_decrypt - Encrypts with key commitment, only
|
|
361
|
+
# decrypts objects with key commitment (default, most secure)
|
|
362
|
+
#
|
|
363
|
+
# @option options [Symbol] :envelope_location (:metadata) Where to
|
|
364
|
+
# store the envelope encryption keys. By default, the envelope is
|
|
365
|
+
# stored with the encrypted object. If you pass `:instruction_file`,
|
|
366
|
+
# then the envelope is stored in a separate object in Amazon S3.
|
|
367
|
+
#
|
|
368
|
+
# @option options [String] :instruction_file_suffix ('.instruction')
|
|
369
|
+
# When `:envelope_location` is `:instruction_file` then the
|
|
370
|
+
# instruction file uses the object key with this suffix appended.
|
|
371
|
+
#
|
|
372
|
+
# @option options [KMS::Client] :kms_client A default {KMS::Client}
|
|
373
|
+
# is constructed when using KMS to manage encryption keys.
|
|
374
|
+
#
|
|
375
|
+
def initialize(options = {})
|
|
376
|
+
validate_params(options)
|
|
377
|
+
##= ../specification/s3-encryption/client.md#wrapped-s3-client-s
|
|
378
|
+
##% The S3EC MUST support the option to provide an SDK S3 client instance during its initialization.
|
|
379
|
+
@client = extract_client(options)
|
|
380
|
+
##= ../specification/s3-encryption/data-format/metadata-strategy.md#instruction-file
|
|
381
|
+
##% Instruction File writes MUST be optionally configured during client creation or on each PutObject request.
|
|
382
|
+
##= ../specification/s3-encryption/client.md#instruction-file-configuration
|
|
383
|
+
##% The S3EC MAY support the option to provide Instruction File Configuration during its initialization.
|
|
384
|
+
##= ../specification/s3-encryption/client.md#instruction-file-configuration
|
|
385
|
+
##% If the S3EC in a given language supports Instruction Files, then it MUST accept Instruction File Configuration during its initialization.
|
|
386
|
+
@envelope_location = extract_location(options)
|
|
387
|
+
@instruction_file_suffix = extract_suffix(options)
|
|
388
|
+
@kms_allow_decrypt_with_any_cmk =
|
|
389
|
+
options[:kms_key_id] == :kms_allow_decrypt_with_any_cmk
|
|
390
|
+
@commitment_policy = extract_commitment_policy(options)
|
|
391
|
+
@security_profile = extract_security_profile(options)
|
|
392
|
+
|
|
393
|
+
##= ../specification/s3-encryption/client.md#key-commitment
|
|
394
|
+
##% The S3EC MUST validate the configured Encryption Algorithm against the provided key commitment policy.
|
|
395
|
+
if @commitment_policy != :require_encrypt_require_decrypt
|
|
396
|
+
new_options = options.merge(
|
|
397
|
+
{
|
|
398
|
+
security_profile: security_profile_to_v2(@security_profile),
|
|
399
|
+
##= ../specification/s3-encryption/client.md#key-commitment
|
|
400
|
+
##% If the configured Encryption Algorithm is incompatible with the key commitment policy, then it MUST throw an exception.
|
|
401
|
+
content_encryption_schema: if @commitment_policy == :forbid_encrypt_allow_decrypt
|
|
402
|
+
options[:content_encryption_schema]
|
|
403
|
+
else
|
|
404
|
+
# assert @commitment_policy = :require_encrypt_allow_decrypt
|
|
405
|
+
# In this case the v2_cipher_provider is only used for decrypt
|
|
406
|
+
:aes_gcm_no_padding
|
|
407
|
+
end
|
|
408
|
+
}
|
|
409
|
+
)
|
|
410
|
+
@v2_cipher_provider = build_v2_cipher_provider_for_decrypt(new_options)
|
|
411
|
+
# In this case the v3 cipher is only used for decrypt.
|
|
412
|
+
@v3_cipher_provider = build_cipher_provider(options.reject { |k, _| k == :content_encryption_schema })
|
|
413
|
+
@key_provider = @v2_cipher_provider.key_provider if @v2_cipher_provider.is_a?(DefaultCipherProvider)
|
|
414
|
+
else
|
|
415
|
+
@v3_cipher_provider = build_cipher_provider(options)
|
|
416
|
+
@key_provider = @v3_cipher_provider.key_provider if @v3_cipher_provider.is_a?(DefaultCipherProvider)
|
|
417
|
+
end
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
# @return [S3::Client]
|
|
421
|
+
attr_reader :client
|
|
422
|
+
|
|
423
|
+
# @return [KeyProvider, nil] Returns `nil` if you are using
|
|
424
|
+
# AWS Key Management Service (KMS).
|
|
425
|
+
attr_reader :key_provider
|
|
426
|
+
|
|
427
|
+
# @return [Symbol] Determines the support for reading objects written
|
|
428
|
+
# using older key wrap or content encryption schemas.
|
|
429
|
+
attr_reader :commitment_policy
|
|
430
|
+
|
|
431
|
+
# @return [Boolean] If true the provided KMS key_id will not be used
|
|
432
|
+
# during decrypt, allowing decryption with the key_id from the object.
|
|
433
|
+
attr_reader :kms_allow_decrypt_with_any_cmk
|
|
434
|
+
|
|
435
|
+
# @return [Symbol<:metadata, :instruction_file>]
|
|
436
|
+
attr_reader :envelope_location
|
|
437
|
+
|
|
438
|
+
# @return [String] When {#envelope_location} is `:instruction_file`,
|
|
439
|
+
# the envelope is stored in the object with the object key suffixed
|
|
440
|
+
# by this string.
|
|
441
|
+
attr_reader :instruction_file_suffix
|
|
442
|
+
|
|
443
|
+
##= ../specification/s3-encryption/client.md#aws-sdk-compatibility
|
|
444
|
+
##= type=implication
|
|
445
|
+
##% The S3EC MUST adhere to the same interface for API operations as the conventional AWS SDK S3 client.
|
|
446
|
+
##= ../specification/s3-encryption/client.md#aws-sdk-compatibility
|
|
447
|
+
##= type=exception
|
|
448
|
+
##= reason=The ruby client does not support other operations
|
|
449
|
+
##% The S3EC SHOULD support invoking operations unrelated to client-side encryption e.g.
|
|
450
|
+
|
|
451
|
+
# Uploads an object to Amazon S3, encrypting data client-side.
|
|
452
|
+
# See {S3::Client#put_object} for documentation on accepted
|
|
453
|
+
# request parameters.
|
|
454
|
+
# @option params [Hash] :kms_encryption_context Additional encryption
|
|
455
|
+
# context to use with KMS. Applies only when KMS is used. In order
|
|
456
|
+
# to decrypt the object you will need to provide the identical
|
|
457
|
+
# :kms_encryption_context to `get_object`.
|
|
458
|
+
# @option (see S3::Client#put_object)
|
|
459
|
+
# @return (see S3::Client#put_object)
|
|
460
|
+
# @see S3::Client#put_object
|
|
461
|
+
def put_object(params = {})
|
|
462
|
+
kms_encryption_context = params.delete(:kms_encryption_context)
|
|
463
|
+
##= ../specification/s3-encryption/client.md#required-api-operations
|
|
464
|
+
##% - PutObject MUST be implemented by the S3EC.
|
|
465
|
+
req = @client.build_request(:put_object, params)
|
|
466
|
+
##= ../specification/s3-encryption/client.md#required-api-operations
|
|
467
|
+
##% - PutObject MUST encrypt its input data before it is uploaded to S3.
|
|
468
|
+
req.handlers.add(EncryptHandler, priority: 95)
|
|
469
|
+
req.context[:encryption] = {
|
|
470
|
+
cipher_provider:
|
|
471
|
+
if @commitment_policy == :forbid_encrypt_allow_decrypt
|
|
472
|
+
##= ../specification/s3-encryption/key-commitment.md#commitment-policy
|
|
473
|
+
##% When the commitment policy is FORBID_ENCRYPT_ALLOW_DECRYPT, the S3EC MUST NOT encrypt using an algorithm suite which supports key commitment.
|
|
474
|
+
@v2_cipher_provider
|
|
475
|
+
else
|
|
476
|
+
##= ../specification/s3-encryption/key-commitment.md#commitment-policy
|
|
477
|
+
##% When the commitment policy is REQUIRE_ENCRYPT_ALLOW_DECRYPT, the S3EC MUST only encrypt using an algorithm suite which supports key commitment.
|
|
478
|
+
##= ../specification/s3-encryption/key-commitment.md#commitment-policy
|
|
479
|
+
##% When the commitment policy is REQUIRE_ENCRYPT_REQUIRE_DECRYPT, the S3EC MUST only encrypt using an algorithm suite which supports key commitment.
|
|
480
|
+
@v3_cipher_provider
|
|
481
|
+
end,
|
|
482
|
+
envelope_location: @envelope_location,
|
|
483
|
+
instruction_file_suffix: @instruction_file_suffix,
|
|
484
|
+
kms_encryption_context: kms_encryption_context
|
|
485
|
+
}
|
|
486
|
+
Aws::Plugins::UserAgent.metric('S3_CRYPTO_V3') do
|
|
487
|
+
req.send_request
|
|
488
|
+
end
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
# Gets an object from Amazon S3, decrypting data locally.
|
|
492
|
+
# See {S3::Client#get_object} for documentation on accepted
|
|
493
|
+
# request parameters.
|
|
494
|
+
# Warning: If you provide a block to get_object or set the request
|
|
495
|
+
# parameter :response_target to a Proc, then read the entire object to the
|
|
496
|
+
# end before you start using the decrypted data. This is to verify that
|
|
497
|
+
# the object has not been modified since it was encrypted.
|
|
498
|
+
#
|
|
499
|
+
# @option options [Symbol] :security_profile
|
|
500
|
+
# Determines the support for reading objects written using older
|
|
501
|
+
# encryption schemas. Overrides the value set on client construction if provided.
|
|
502
|
+
# Must be one of the following:
|
|
503
|
+
#
|
|
504
|
+
# * :v3 - Only reads V3-encrypted objects (most secure)
|
|
505
|
+
# * :v3_and_legacy - Enables reading of V2-encrypted objects
|
|
506
|
+
# @option params [String] :instruction_file_suffix The suffix
|
|
507
|
+
# used to find the instruction file containing the encryption
|
|
508
|
+
# envelope. You should not set this option when the envelope
|
|
509
|
+
# is stored in the object metadata. Defaults to
|
|
510
|
+
# {#instruction_file_suffix}.
|
|
511
|
+
# @option params [Hash] :kms_encryption_context Additional encryption
|
|
512
|
+
# context to use with KMS. Applies only when KMS is used.
|
|
513
|
+
# @option options [Boolean] :kms_allow_decrypt_with_any_cmk (false)
|
|
514
|
+
# By default the KMS CMK ID (kms_key_id) will be used during decrypt
|
|
515
|
+
# and will fail if there is a mismatch. Setting this to true
|
|
516
|
+
# will use the implicit CMK associated with the data.
|
|
517
|
+
# @option (see S3::Client#get_object)
|
|
518
|
+
# @return (see S3::Client#get_object)
|
|
519
|
+
# @see S3::Client#get_object
|
|
520
|
+
# @note The `:range` request parameter is not supported.
|
|
521
|
+
def get_object(params = {}, &block)
|
|
522
|
+
raise NotImplementedError, '#get_object with :range not supported' if params[:range]
|
|
523
|
+
|
|
524
|
+
envelope_location, instruction_file_suffix = envelope_options(params)
|
|
525
|
+
kms_encryption_context = params.delete(:kms_encryption_context)
|
|
526
|
+
kms_any_cmk_mode = kms_any_cmk_mode(params)
|
|
527
|
+
commitment_policy = commitment_policy_from_params(params)
|
|
528
|
+
|
|
529
|
+
##= ../specification/s3-encryption/client.md#required-api-operations
|
|
530
|
+
##% - GetObject MUST be implemented by the S3EC.
|
|
531
|
+
req = @client.build_request(:get_object, params)
|
|
532
|
+
##= ../specification/s3-encryption/client.md#required-api-operations
|
|
533
|
+
##% - GetObject MUST decrypt data received from the S3 server and return it as plaintext.
|
|
534
|
+
req.handlers.add(DecryptHandler)
|
|
535
|
+
req.context[:encryption] = {
|
|
536
|
+
v3_cipher_provider: @v3_cipher_provider,
|
|
537
|
+
envelope_location: envelope_location,
|
|
538
|
+
instruction_file_suffix: instruction_file_suffix,
|
|
539
|
+
kms_encryption_context: kms_encryption_context,
|
|
540
|
+
kms_allow_decrypt_with_any_cmk: kms_any_cmk_mode,
|
|
541
|
+
commitment_policy: commitment_policy
|
|
542
|
+
}.tap do |hash|
|
|
543
|
+
if commitment_policy != :require_encrypt_require_decrypt
|
|
544
|
+
security_profile = security_profile_from_params(params)
|
|
545
|
+
hash[:security_profile] = security_profile_to_v2(security_profile)
|
|
546
|
+
hash[:cipher_provider] = @v2_cipher_provider
|
|
547
|
+
end
|
|
548
|
+
end
|
|
549
|
+
Aws::Plugins::UserAgent.metric('S3_CRYPTO_V3') do
|
|
550
|
+
req.send_request(target: block)
|
|
551
|
+
end
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
##= ../specification/s3-encryption/data-format/metadata-strategy.md#instruction-file
|
|
555
|
+
##= type=exception
|
|
556
|
+
##= reason=This has never been supported in Ruby
|
|
557
|
+
##% The S3EC MAY support re-encryption/key rotation via Instruction Files.
|
|
558
|
+
##= ../specification/s3-encryption/decryption.md#ranged-gets
|
|
559
|
+
##= type=exception
|
|
560
|
+
##= reason=This has never been supported in Ruby
|
|
561
|
+
##% The S3EC MAY support the "range" parameter on GetObject which specifies a subset of bytes to download and decrypt.
|
|
562
|
+
##= ../specification/s3-encryption/decryption.md#ranged-gets
|
|
563
|
+
##= type=exception
|
|
564
|
+
##= reason=This has never been supported in Ruby
|
|
565
|
+
##% If the S3EC supports Ranged Gets, the S3EC MUST adjust the customer-provided range to include the beginning and end of the cipher blocks for the given range.
|
|
566
|
+
##= ../specification/s3-encryption/decryption.md#ranged-gets
|
|
567
|
+
##= type=exception
|
|
568
|
+
##= reason=This has never been supported in Ruby
|
|
569
|
+
##% If the object was encrypted with ALG_AES_256_GCM_IV12_TAG16_NO_KDF, then ALG_AES_256_CTR_IV16_TAG16_NO_KDF MUST be used to decrypt the range of the object.
|
|
570
|
+
##= ../specification/s3-encryption/decryption.md#ranged-gets
|
|
571
|
+
##= type=exception
|
|
572
|
+
##= reason=This has never been supported in Ruby
|
|
573
|
+
##% If the object was encrypted with ALG_AES_256_GCM_HKDF_SHA512_COMMIT_KEY, then ALG_AES_256_CTR_HKDF_SHA512_COMMIT_KEY MUST be used to decrypt the range of the object.
|
|
574
|
+
##= ../specification/s3-encryption/decryption.md#ranged-gets
|
|
575
|
+
##= type=exception
|
|
576
|
+
##= reason=This has never been supported in Ruby
|
|
577
|
+
##% If the GetObject response contains a range, but the GetObject request does not contain a range, the S3EC MUST throw an exception.
|
|
578
|
+
|
|
579
|
+
private
|
|
580
|
+
|
|
581
|
+
def build_cipher_provider(options)
|
|
582
|
+
if options[:kms_key_id]
|
|
583
|
+
KmsCipherProvider.new(
|
|
584
|
+
kms_key_id: options[:kms_key_id],
|
|
585
|
+
kms_client: kms_client(options),
|
|
586
|
+
key_wrap_schema: options[:key_wrap_schema],
|
|
587
|
+
content_encryption_schema: options[:content_encryption_schema]
|
|
588
|
+
)
|
|
589
|
+
else
|
|
590
|
+
##= ../specification/s3-encryption/client.md#cryptographic-materials
|
|
591
|
+
##% The S3EC MAY accept key material directly.
|
|
592
|
+
key_provider = extract_key_provider(options)
|
|
593
|
+
DefaultCipherProvider.new(
|
|
594
|
+
key_provider: key_provider,
|
|
595
|
+
key_wrap_schema: options[:key_wrap_schema],
|
|
596
|
+
content_encryption_schema: options[:content_encryption_schema]
|
|
597
|
+
)
|
|
598
|
+
end
|
|
599
|
+
end
|
|
600
|
+
|
|
601
|
+
def build_v2_cipher_provider_for_decrypt(options)
|
|
602
|
+
if options[:kms_key_id]
|
|
603
|
+
Aws::S3::EncryptionV2::KmsCipherProvider.new(
|
|
604
|
+
kms_key_id: options[:kms_key_id],
|
|
605
|
+
kms_client: kms_client(options),
|
|
606
|
+
key_wrap_schema: options[:key_wrap_schema],
|
|
607
|
+
content_encryption_schema: options[:content_encryption_schema]
|
|
608
|
+
)
|
|
609
|
+
else
|
|
610
|
+
# Create V2 key provider explicitly for proper namespace consistency
|
|
611
|
+
key_provider = if options[:key_provider]
|
|
612
|
+
options[:key_provider]
|
|
613
|
+
elsif options[:encryption_key]
|
|
614
|
+
Aws::S3::EncryptionV2::DefaultKeyProvider.new(options)
|
|
615
|
+
else
|
|
616
|
+
msg = 'you must pass a :kms_key_id, :key_provider, or :encryption_key'
|
|
617
|
+
raise ArgumentError, msg
|
|
618
|
+
end
|
|
619
|
+
Aws::S3::EncryptionV2::DefaultCipherProvider.new(
|
|
620
|
+
key_provider: key_provider,
|
|
621
|
+
key_wrap_schema: options[:key_wrap_schema],
|
|
622
|
+
content_encryption_schema: options[:content_encryption_schema]
|
|
623
|
+
)
|
|
624
|
+
end
|
|
625
|
+
end
|
|
626
|
+
|
|
627
|
+
# Validate required parameters exist and don't conflict.
|
|
628
|
+
# The cek_alg and wrap_alg are passed on to the CipherProviders
|
|
629
|
+
# and further validated there
|
|
630
|
+
def validate_params(options)
|
|
631
|
+
unless (missing_params = REQUIRED_PARAMS - options.keys).empty?
|
|
632
|
+
raise ArgumentError, 'Missing required parameter(s): '\
|
|
633
|
+
"#{missing_params.map { |s| ":#{s}" }.join(', ')}"
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
wrap_alg = options[:key_wrap_schema]
|
|
637
|
+
|
|
638
|
+
# validate that the wrap alg matches the type of key given
|
|
639
|
+
case wrap_alg
|
|
640
|
+
when :kms_context
|
|
641
|
+
raise ArgumentError, 'You must provide :kms_key_id to use :kms_context' unless options[:kms_key_id]
|
|
642
|
+
end
|
|
643
|
+
end
|
|
644
|
+
|
|
645
|
+
def extract_client(options)
|
|
646
|
+
##= ../specification/s3-encryption/client.md#wrapped-s3-client-s
|
|
647
|
+
##= type=exception
|
|
648
|
+
##= reason=this would be a breaking change to ruby
|
|
649
|
+
##% The S3EC MUST NOT support use of S3EC as the provided S3 client during its initialization; it MUST throw an exception in this case.
|
|
650
|
+
options[:client] || begin
|
|
651
|
+
##= ../specification/s3-encryption/client.md#inherited-sdk-configuration
|
|
652
|
+
##% The S3EC MAY support directly configuring the wrapped SDK clients through its initialization.
|
|
653
|
+
##= ../specification/s3-encryption/client.md#inherited-sdk-configuration
|
|
654
|
+
##% For example, the S3EC MAY accept a credentials provider instance during its initialization.
|
|
655
|
+
##= ../specification/s3-encryption/client.md#inherited-sdk-configuration
|
|
656
|
+
##% If the S3EC accepts SDK client configuration, the configuration MUST be applied to all wrapped S3 clients.
|
|
657
|
+
S3::Client.new(extract_sdk_options(options))
|
|
658
|
+
end
|
|
659
|
+
end
|
|
660
|
+
|
|
661
|
+
def kms_client(options)
|
|
662
|
+
options[:kms_client] || (@kms_client ||=
|
|
663
|
+
KMS::Client.new(
|
|
664
|
+
# extract the region and credentials first, if they are not configured, then getting them from an existing client is faster
|
|
665
|
+
##= ../specification/s3-encryption/client.md#inherited-sdk-configuration
|
|
666
|
+
##% If the S3EC accepts SDK client configuration, the configuration MUST be applied to all wrapped SDK clients including the KMS client.
|
|
667
|
+
{
|
|
668
|
+
region: @client.config.region,
|
|
669
|
+
credentials: @client.config.credentials
|
|
670
|
+
}.merge(extract_sdk_options(options))
|
|
671
|
+
)
|
|
672
|
+
)
|
|
673
|
+
end
|
|
674
|
+
|
|
675
|
+
def extract_sdk_options(options)
|
|
676
|
+
options = options.dup
|
|
677
|
+
OPTIONAL_PARAMS.each { |p| options.delete(p) }
|
|
678
|
+
REQUIRED_PARAMS.each { |p| options.delete(p) }
|
|
679
|
+
options
|
|
680
|
+
end
|
|
681
|
+
|
|
682
|
+
def extract_key_provider(options)
|
|
683
|
+
if options[:key_provider]
|
|
684
|
+
options[:key_provider]
|
|
685
|
+
elsif options[:encryption_key]
|
|
686
|
+
DefaultKeyProvider.new(options)
|
|
687
|
+
else
|
|
688
|
+
msg = 'you must pass a :kms_key_id, :key_provider, or :encryption_key'
|
|
689
|
+
raise ArgumentError, msg
|
|
690
|
+
end
|
|
691
|
+
end
|
|
692
|
+
|
|
693
|
+
def envelope_options(params)
|
|
694
|
+
location = params.delete(:envelope_location) || @envelope_location
|
|
695
|
+
suffix = params.delete(:instruction_file_suffix)
|
|
696
|
+
if suffix
|
|
697
|
+
##= ../specification/s3-encryption/data-format/metadata-strategy.md#instruction-file
|
|
698
|
+
##% The S3EC SHOULD support providing a custom Instruction File suffix on GetObject requests, regardless of whether or not re-encryption is supported.
|
|
699
|
+
[:instruction_file, suffix]
|
|
700
|
+
else
|
|
701
|
+
[location, @instruction_file_suffix]
|
|
702
|
+
end
|
|
703
|
+
end
|
|
704
|
+
|
|
705
|
+
def extract_location(options)
|
|
706
|
+
##= ../specification/s3-encryption/data-format/metadata-strategy.md#object-metadata
|
|
707
|
+
##% By default, the S3EC MUST store content metadata in the S3 Object Metadata.
|
|
708
|
+
##= ../specification/s3-encryption/data-format/metadata-strategy.md#instruction-file
|
|
709
|
+
##% Instruction File writes MUST NOT be enabled by default.
|
|
710
|
+
location = options[:envelope_location] || :metadata
|
|
711
|
+
if %i[metadata instruction_file].include?(location)
|
|
712
|
+
location
|
|
713
|
+
else
|
|
714
|
+
msg = ':envelope_location must be :metadata or :instruction_file '\
|
|
715
|
+
"got #{location.inspect}"
|
|
716
|
+
raise ArgumentError, msg
|
|
717
|
+
end
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
def extract_suffix(options)
|
|
721
|
+
##= ../specification/s3-encryption/data-format/metadata-strategy.md#instruction-file
|
|
722
|
+
##% The default Instruction File behavior uses the same S3 object key as its associated object suffixed with ".instruction".
|
|
723
|
+
suffix = options[:instruction_file_suffix] || '.instruction'
|
|
724
|
+
if suffix.is_a? String
|
|
725
|
+
##= ../specification/s3-encryption/data-format/metadata-strategy.md#instruction-file
|
|
726
|
+
##= type=exception
|
|
727
|
+
##= reason=Ruby has always supported this option
|
|
728
|
+
##% The S3EC MUST NOT support providing a custom Instruction File suffix on ordinary writes; custom suffixes MUST only be used during re-encryption.
|
|
729
|
+
suffix
|
|
730
|
+
else
|
|
731
|
+
msg = ':instruction_file_suffix must be a String'
|
|
732
|
+
raise ArgumentError, msg
|
|
733
|
+
end
|
|
734
|
+
end
|
|
735
|
+
|
|
736
|
+
def kms_any_cmk_mode(params)
|
|
737
|
+
if !params[:kms_allow_decrypt_with_any_cmk].nil?
|
|
738
|
+
params.delete(:kms_allow_decrypt_with_any_cmk)
|
|
739
|
+
else
|
|
740
|
+
@kms_allow_decrypt_with_any_cmk
|
|
741
|
+
end
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
def extract_commitment_policy(options)
|
|
745
|
+
validate_commitment_policy(options[:commitment_policy])
|
|
746
|
+
end
|
|
747
|
+
|
|
748
|
+
def commitment_policy_from_params(params)
|
|
749
|
+
commitment_policy =
|
|
750
|
+
if !params[:commitment_policy].nil?
|
|
751
|
+
params.delete(:commitment_policy)
|
|
752
|
+
else
|
|
753
|
+
@commitment_policy
|
|
754
|
+
end
|
|
755
|
+
validate_commitment_policy(commitment_policy)
|
|
756
|
+
end
|
|
757
|
+
|
|
758
|
+
def validate_commitment_policy(commitment_policy)
|
|
759
|
+
return DEFAULT_COMMITMENT_POLICIES if commitment_policy.nil?
|
|
760
|
+
|
|
761
|
+
unless SUPPORTED_COMMITMENT_POLICIES.include? commitment_policy
|
|
762
|
+
raise ArgumentError, "Unsupported security profile: :#{commitment_policy}. " \
|
|
763
|
+
"Please provide one of: #{SUPPORTED_COMMITMENT_POLICIES.map { |s| ":#{s}" }.join(', ')}"
|
|
764
|
+
end
|
|
765
|
+
commitment_policy
|
|
766
|
+
end
|
|
767
|
+
|
|
768
|
+
def extract_security_profile(options)
|
|
769
|
+
validate_security_profile(options[:security_profile])
|
|
770
|
+
end
|
|
771
|
+
|
|
772
|
+
def security_profile_from_params(params)
|
|
773
|
+
security_profile =
|
|
774
|
+
if !params[:security_profile].nil?
|
|
775
|
+
params.delete(:security_profile)
|
|
776
|
+
else
|
|
777
|
+
@security_profile
|
|
778
|
+
end
|
|
779
|
+
validate_security_profile(security_profile)
|
|
780
|
+
end
|
|
781
|
+
|
|
782
|
+
def validate_security_profile(security_profile)
|
|
783
|
+
return DEFAULT_SECURITY_PROFILES if security_profile.nil?
|
|
784
|
+
|
|
785
|
+
unless SUPPORTED_SECURITY_PROFILES.include? security_profile
|
|
786
|
+
raise ArgumentError, "Unsupported security profile: :#{security_profile}. " \
|
|
787
|
+
"Please provide one of: #{SUPPORTED_SECURITY_PROFILES.map { |s| ":#{s}" }.join(', ')}"
|
|
788
|
+
end
|
|
789
|
+
if security_profile == :v3_and_legacy && !@warned_about_legacy
|
|
790
|
+
@warned_about_legacy = true
|
|
791
|
+
warn(
|
|
792
|
+
'The S3 Encryption Client is configured to read encrypted objects ' \
|
|
793
|
+
"with legacy encryption modes. If you don't have objects " \
|
|
794
|
+
'encrypted with these legacy modes, you should disable support ' \
|
|
795
|
+
'for them to enhance security.'
|
|
796
|
+
)
|
|
797
|
+
end
|
|
798
|
+
security_profile
|
|
799
|
+
end
|
|
800
|
+
|
|
801
|
+
def security_profile_to_v2(security_profile)
|
|
802
|
+
case security_profile
|
|
803
|
+
when :v3
|
|
804
|
+
:v2
|
|
805
|
+
when :v3_and_legacy
|
|
806
|
+
:v2_and_legacy
|
|
807
|
+
end
|
|
808
|
+
end
|
|
809
|
+
end
|
|
810
|
+
end
|
|
811
|
+
end
|
|
812
|
+
end
|
|
813
|
+
|
|
814
|
+
##= ../specification/s3-encryption/client.md#cryptographic-materials
|
|
815
|
+
##= type=exception
|
|
816
|
+
##= reason=the ruby client does not use keyrings
|
|
817
|
+
##% The S3EC MUST accept either one CMM or one Keyring instance upon initialization.
|
|
818
|
+
##= ../specification/s3-encryption/client.md#cryptographic-materials
|
|
819
|
+
##= type=exception
|
|
820
|
+
##= reason=the ruby client does not use keyrings
|
|
821
|
+
##% If both a CMM and a Keyring are provided, the S3EC MUST throw an exception.
|
|
822
|
+
##= ../specification/s3-encryption/client.md#cryptographic-materials
|
|
823
|
+
##= type=exception
|
|
824
|
+
##= reason=the ruby client does not use keyrings
|
|
825
|
+
##% When a Keyring is provided, the S3EC MUST create an instance of the DefaultCMM using the provided Keyring.
|
|
826
|
+
##= ../specification/s3-encryption/client.md#enable-delayed-authentication
|
|
827
|
+
##= type=exception
|
|
828
|
+
##= reason=the ruby client does not support delayed authentication
|
|
829
|
+
##% The S3EC MUST support the option to enable or disable Delayed Authentication mode.
|
|
830
|
+
##= ../specification/s3-encryption/client.md#enable-delayed-authentication
|
|
831
|
+
##= type=exception
|
|
832
|
+
##= reason=the ruby client does not support delayed authentication
|
|
833
|
+
##% Delayed Authentication mode MUST be set to false by default.
|
|
834
|
+
##= ../specification/s3-encryption/client.md#enable-delayed-authentication
|
|
835
|
+
##= type=exception
|
|
836
|
+
##= reason=the ruby client does not support delayed authentication
|
|
837
|
+
##% When enabled, the S3EC MAY release plaintext from a stream which has not been authenticated.
|
|
838
|
+
##= ../specification/s3-encryption/client.md#enable-delayed-authentication
|
|
839
|
+
##= type=exception
|
|
840
|
+
##= reason=the ruby client does not support delayed authentication
|
|
841
|
+
##% When disabled the S3EC MUST NOT release plaintext from a stream which has not been authenticated.
|
|
842
|
+
##= ../specification/s3-encryption/client.md#set-buffer-size
|
|
843
|
+
##= type=exception
|
|
844
|
+
##= reason=the ruby client does not support delayed authentication
|
|
845
|
+
##% The S3EC SHOULD accept a configurable buffer size which refers to the maximum ciphertext length in bytes to store in memory when Delayed Authentication mode is disabled.
|
|
846
|
+
##= ../specification/s3-encryption/client.md#set-buffer-size
|
|
847
|
+
##= type=exception
|
|
848
|
+
##= reason=the ruby client does not support delayed authentication
|
|
849
|
+
##% If Delayed Authentication mode is enabled, and the buffer size has been set to a value other than its default, the S3EC MUST throw an exception.
|
|
850
|
+
##= ../specification/s3-encryption/client.md#set-buffer-size
|
|
851
|
+
##= type=exception
|
|
852
|
+
##= reason=the ruby client does not support delayed authentication
|
|
853
|
+
##% If Delayed Authentication mode is disabled, and no buffer size is provided, the S3EC MUST set the buffer size to a reasonable default.
|
|
854
|
+
##= ../specification/s3-encryption/client.md#randomness
|
|
855
|
+
##= type=exception
|
|
856
|
+
##= reason=the ruby client does not support a configured source of randomness
|
|
857
|
+
##% The S3EC MAY accept a source of randomness during client initialization.
|
|
858
|
+
##= ../specification/s3-encryption/client.md#optional-api-operations
|
|
859
|
+
##= type=exception
|
|
860
|
+
##= reason=the ruby client does not support any additional S3 operations
|
|
861
|
+
##% - CreateMultipartUpload MAY be implemented by the S3EC.
|
|
862
|
+
##% - If implemented, CreateMultipartUpload MUST initiate a multipart upload.
|
|
863
|
+
##% - UploadPart MAY be implemented by the S3EC.
|
|
864
|
+
##% - UploadPart MUST encrypt each part.
|
|
865
|
+
##% - Each part MUST be encrypted in sequence.
|
|
866
|
+
##% - Each part MUST be encrypted using the same cipher instance for each part.
|
|
867
|
+
##% - CompleteMultipartUpload MAY be implemented by the S3EC.
|
|
868
|
+
##% - CompleteMultipartUpload MUST complete the multipart upload.
|
|
869
|
+
##% - AbortMultipartUpload MAY be implemented by the S3EC.
|
|
870
|
+
##% - AbortMultipartUpload MUST abort the multipart upload.
|
|
871
|
+
##%
|
|
872
|
+
##% The S3EC may provide implementations for the following S3EC-specific operation(s):
|
|
873
|
+
##%
|
|
874
|
+
##% - ReEncryptInstructionFile MAY be implemented by the S3EC.
|
|
875
|
+
##% - ReEncryptInstructionFile MUST decrypt the instruction file's encrypted data key for the given object using the client's CMM.
|
|
876
|
+
##% - ReEncryptInstructionFile MUST re-encrypt the plaintext data key with a provided keyring.
|
|
877
|
+
##= ../specification/s3-encryption/client.md#required-api-operations
|
|
878
|
+
##= type=exception
|
|
879
|
+
##= reason=the ruby client does not support the delete operation, this would be a bending change
|
|
880
|
+
##% - DeleteObject MUST be implemented by the S3EC.
|
|
881
|
+
##% - DeleteObject MUST delete the given object key.
|
|
882
|
+
##% - DeleteObject MUST delete the associated instruction file using the default instruction file suffix.
|
|
883
|
+
##% - DeleteObjects MUST be implemented by the S3EC.
|
|
884
|
+
##% - DeleteObjects MUST delete each of the given objects.
|
|
885
|
+
##% - DeleteObjects MUST delete each of the corresponding instruction files using the default instruction file suffix.
|