aws-sdk-s3 1.10.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 +5 -5
- data/CHANGELOG.md +1517 -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 +1062 -99
- data/lib/aws-sdk-s3/bucket_acl.rb +67 -17
- data/lib/aws-sdk-s3/bucket_cors.rb +80 -17
- data/lib/aws-sdk-s3/bucket_lifecycle.rb +71 -19
- data/lib/aws-sdk-s3/bucket_lifecycle_configuration.rb +126 -20
- data/lib/aws-sdk-s3/bucket_logging.rb +68 -18
- data/lib/aws-sdk-s3/bucket_notification.rb +56 -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 -17
- data/lib/aws-sdk-s3/bucket_versioning.rb +166 -17
- data/lib/aws-sdk-s3/bucket_website.rb +78 -17
- data/lib/aws-sdk-s3/client.rb +20068 -3879
- data/lib/aws-sdk-s3/client_api.rb +1957 -209
- data/lib/aws-sdk-s3/customizations/bucket.rb +57 -38
- 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 +338 -68
- data/lib/aws-sdk-s3/customizations/object_summary.rb +17 -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 +30 -27
- data/lib/aws-sdk-s3/default_executor.rb +103 -0
- data/lib/aws-sdk-s3/encryption/client.rb +29 -8
- data/lib/aws-sdk-s3/encryption/decrypt_handler.rb +71 -29
- data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +45 -5
- data/lib/aws-sdk-s3/encryption/default_key_provider.rb +2 -0
- data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +15 -2
- data/lib/aws-sdk-s3/encryption/errors.rb +2 -0
- data/lib/aws-sdk-s3/encryption/io_auth_decrypter.rb +11 -3
- 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 +48 -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 +645 -0
- data/lib/aws-sdk-s3/encryptionV2/decrypt_handler.rb +68 -0
- data/lib/aws-sdk-s3/encryptionV2/decryption.rb +205 -0
- data/lib/aws-sdk-s3/encryptionV2/default_cipher_provider.rb +187 -0
- data/lib/aws-sdk-s3/encryptionV2/default_key_provider.rb +40 -0
- data/lib/aws-sdk-s3/encryptionV2/encrypt_handler.rb +67 -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 +75 -0
- data/lib/aws-sdk-s3/encryptionV2/key_provider.rb +31 -0
- data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +181 -0
- data/lib/aws-sdk-s3/encryptionV2/materials.rb +60 -0
- data/lib/aws-sdk-s3/encryptionV2/utils.rb +108 -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 +24 -0
- data/lib/aws-sdk-s3/encryption_v3.rb +24 -0
- data/lib/aws-sdk-s3/endpoint_parameters.rb +181 -0
- data/lib/aws-sdk-s3/endpoint_provider.rb +886 -0
- data/lib/aws-sdk-s3/endpoints.rb +1544 -0
- data/lib/aws-sdk-s3/errors.rb +181 -1
- data/lib/aws-sdk-s3/event_streams.rb +69 -0
- 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 +261 -82
- data/lib/aws-sdk-s3/file_part.rb +16 -13
- data/lib/aws-sdk-s3/file_uploader.rb +37 -22
- data/lib/aws-sdk-s3/legacy_signer.rb +19 -26
- data/lib/aws-sdk-s3/multipart_download_error.rb +8 -0
- data/lib/aws-sdk-s3/multipart_file_uploader.rb +142 -80
- data/lib/aws-sdk-s3/multipart_stream_uploader.rb +191 -0
- data/lib/aws-sdk-s3/multipart_upload.rb +342 -31
- data/lib/aws-sdk-s3/multipart_upload_error.rb +5 -4
- data/lib/aws-sdk-s3/multipart_upload_part.rb +387 -47
- data/lib/aws-sdk-s3/object.rb +2733 -204
- data/lib/aws-sdk-s3/object_acl.rb +112 -25
- data/lib/aws-sdk-s3/object_copier.rb +9 -5
- data/lib/aws-sdk-s3/object_multipart_copier.rb +50 -23
- data/lib/aws-sdk-s3/object_summary.rb +2265 -181
- data/lib/aws-sdk-s3/object_version.rb +542 -74
- 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 -67
- data/lib/aws-sdk-s3/plugins/redirects.rb +5 -1
- data/lib/aws-sdk-s3/plugins/s3_host_id.rb +2 -0
- data/lib/aws-sdk-s3/plugins/s3_signer.rb +67 -93
- data/lib/aws-sdk-s3/plugins/sse_cpk.rb +3 -1
- data/lib/aws-sdk-s3/plugins/streaming_retry.rb +137 -0
- data/lib/aws-sdk-s3/plugins/url_encoded_keys.rb +4 -1
- data/lib/aws-sdk-s3/presigned_post.rb +160 -99
- data/lib/aws-sdk-s3/presigner.rb +178 -81
- data/lib/aws-sdk-s3/resource.rb +164 -15
- data/lib/aws-sdk-s3/transfer_manager.rb +303 -0
- data/lib/aws-sdk-s3/types.rb +15981 -4168
- data/lib/aws-sdk-s3/waiters.rb +67 -1
- data/lib/aws-sdk-s3.rb +46 -31
- data/sig/bucket.rbs +231 -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 +2612 -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 +44 -0
- data/sig/multipart_upload.rbs +120 -0
- data/sig/multipart_upload_part.rbs +109 -0
- data/sig/object.rbs +464 -0
- data/sig/object_acl.rbs +86 -0
- data/sig/object_summary.rbs +347 -0
- data/sig/object_version.rbs +143 -0
- data/sig/resource.rbs +141 -0
- data/sig/types.rbs +2899 -0
- data/sig/waiters.rbs +95 -0
- metadata +97 -14
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Aws
|
|
4
|
+
module S3
|
|
5
|
+
module EncryptionV2
|
|
6
|
+
# @api private
|
|
7
|
+
class IODecrypter
|
|
8
|
+
|
|
9
|
+
# @param [OpenSSL::Cipher] cipher
|
|
10
|
+
# @param [IO#write] io An IO-like object that responds to `#write`.
|
|
11
|
+
def initialize(cipher, io)
|
|
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
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @return [#write]
|
|
19
|
+
attr_reader :io
|
|
20
|
+
|
|
21
|
+
def write(chunk)
|
|
22
|
+
# decrypt and write
|
|
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
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def finalize
|
|
31
|
+
@io.write(@cipher.final)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'stringio'
|
|
4
|
+
require 'tempfile'
|
|
5
|
+
|
|
6
|
+
module Aws
|
|
7
|
+
module S3
|
|
8
|
+
module EncryptionV2
|
|
9
|
+
|
|
10
|
+
# Provides an IO wrapper encrypting a stream of data.
|
|
11
|
+
# @api private
|
|
12
|
+
class IOEncrypter
|
|
13
|
+
|
|
14
|
+
# @api private
|
|
15
|
+
ONE_MEGABYTE = 1024 * 1024
|
|
16
|
+
|
|
17
|
+
def initialize(cipher, io)
|
|
18
|
+
@encrypted = io.size <= ONE_MEGABYTE ?
|
|
19
|
+
encrypt_to_stringio(cipher, io.read) :
|
|
20
|
+
encrypt_to_tempfile(cipher, io)
|
|
21
|
+
@size = @encrypted.size
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @return [Integer]
|
|
25
|
+
attr_reader :size
|
|
26
|
+
|
|
27
|
+
def read(bytes = nil, output_buffer = nil)
|
|
28
|
+
if @encrypted.is_a?(Tempfile) && @encrypted.closed?
|
|
29
|
+
@encrypted.open
|
|
30
|
+
@encrypted.binmode
|
|
31
|
+
end
|
|
32
|
+
@encrypted.read(bytes, output_buffer)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def rewind
|
|
36
|
+
@encrypted.rewind
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @api private
|
|
40
|
+
def close
|
|
41
|
+
@encrypted.close if @encrypted.is_a?(Tempfile)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def encrypt_to_stringio(cipher, plain_text)
|
|
47
|
+
##= ../specification/s3-encryption/encryption.md#alg-aes-256-gcm-iv12-tag16-no-kdf
|
|
48
|
+
##% The client MUST append the GCM auth tag to the ciphertext if the underlying crypto provider does not do so automatically.
|
|
49
|
+
if plain_text.empty?
|
|
50
|
+
StringIO.new(cipher.final + cipher.auth_tag)
|
|
51
|
+
else
|
|
52
|
+
StringIO.new(cipher.update(plain_text) + cipher.final + cipher.auth_tag)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def encrypt_to_tempfile(cipher, io)
|
|
57
|
+
encrypted = Tempfile.new(self.object_id.to_s)
|
|
58
|
+
encrypted.binmode
|
|
59
|
+
while chunk = io.read(ONE_MEGABYTE, read_buffer ||= String.new)
|
|
60
|
+
if cipher.method(:update).arity == 1
|
|
61
|
+
encrypted.write(cipher.update(chunk))
|
|
62
|
+
else
|
|
63
|
+
encrypted.write(cipher.update(chunk, cipher_buffer ||= String.new))
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
encrypted.write(cipher.final)
|
|
67
|
+
encrypted.write(cipher.auth_tag)
|
|
68
|
+
encrypted.rewind
|
|
69
|
+
encrypted
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Aws
|
|
4
|
+
module S3
|
|
5
|
+
module EncryptionV2
|
|
6
|
+
|
|
7
|
+
# This module defines the interface required for a {Client#key_provider}.
|
|
8
|
+
# A key provider is any object that:
|
|
9
|
+
#
|
|
10
|
+
# * Responds to {#encryption_materials} with an {Materials} object.
|
|
11
|
+
#
|
|
12
|
+
# * Responds to {#key_for}, receiving a JSON document String,
|
|
13
|
+
# returning an encryption key. The returned encryption key
|
|
14
|
+
# must be one of:
|
|
15
|
+
#
|
|
16
|
+
# * `OpenSSL::PKey::RSA` - for asymmetric encryption
|
|
17
|
+
# * `String` - 32, 24, or 16 bytes long, for symmetric encryption
|
|
18
|
+
#
|
|
19
|
+
module KeyProvider
|
|
20
|
+
|
|
21
|
+
# @return [Materials]
|
|
22
|
+
def encryption_materials; end
|
|
23
|
+
|
|
24
|
+
# @param [String<JSON>] materials_description
|
|
25
|
+
# @return [OpenSSL::PKey::RSA, String] encryption_key
|
|
26
|
+
def key_for(materials_description); end
|
|
27
|
+
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'base64'
|
|
4
|
+
|
|
5
|
+
module Aws
|
|
6
|
+
module S3
|
|
7
|
+
module EncryptionV2
|
|
8
|
+
# @api private
|
|
9
|
+
class KmsCipherProvider
|
|
10
|
+
|
|
11
|
+
def initialize(options = {})
|
|
12
|
+
@kms_key_id = validate_kms_key(options[:kms_key_id])
|
|
13
|
+
@kms_client = options[:kms_client]
|
|
14
|
+
@key_wrap_schema = validate_key_wrap(
|
|
15
|
+
options[:key_wrap_schema]
|
|
16
|
+
)
|
|
17
|
+
@content_encryption_schema = validate_cek(
|
|
18
|
+
options[:content_encryption_schema]
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @return [Array<Hash,Cipher>] Creates and returns a new encryption
|
|
23
|
+
# envelope and encryption cipher.
|
|
24
|
+
def encryption_cipher(options = {})
|
|
25
|
+
validate_key_for_encryption
|
|
26
|
+
encryption_context = build_encryption_context(@content_encryption_schema, options)
|
|
27
|
+
key_data = Aws::Plugins::UserAgent.metric('S3_CRYPTO_V2') do
|
|
28
|
+
@kms_client.generate_data_key(
|
|
29
|
+
key_id: @kms_key_id,
|
|
30
|
+
encryption_context: encryption_context,
|
|
31
|
+
key_spec: 'AES_256'
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
cipher = Utils.aes_encryption_cipher(:GCM)
|
|
35
|
+
cipher.key = key_data.plaintext
|
|
36
|
+
##= ../specification/s3-encryption/data-format/content-metadata.md#algorithm-suite-and-message-format-version-compatibility
|
|
37
|
+
##% Objects encrypted with ALG_AES_256_GCM_IV12_TAG16_NO_KDF MUST use the V2 message format version only.
|
|
38
|
+
envelope = {
|
|
39
|
+
'x-amz-key-v2' => encode64(key_data.ciphertext_blob),
|
|
40
|
+
'x-amz-iv' => encode64(cipher.iv = cipher.random_iv),
|
|
41
|
+
'x-amz-cek-alg' => @content_encryption_schema,
|
|
42
|
+
'x-amz-tag-len' => (AES_GCM_TAG_LEN_BYTES * 8).to_s,
|
|
43
|
+
'x-amz-wrap-alg' => @key_wrap_schema,
|
|
44
|
+
'x-amz-matdesc' => Json.dump(encryption_context)
|
|
45
|
+
}
|
|
46
|
+
cipher.auth_data = '' # auth_data must be set after key and iv
|
|
47
|
+
[envelope, cipher]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# @return [Cipher] Given an encryption envelope, returns a
|
|
51
|
+
# decryption cipher.
|
|
52
|
+
def decryption_cipher(envelope, options = {})
|
|
53
|
+
encryption_context = Json.load(envelope['x-amz-matdesc'])
|
|
54
|
+
cek_alg = envelope['x-amz-cek-alg']
|
|
55
|
+
|
|
56
|
+
case envelope['x-amz-wrap-alg']
|
|
57
|
+
when 'kms'
|
|
58
|
+
##= ../specification/s3-encryption/client.md#enable-legacy-wrapping-algorithms
|
|
59
|
+
##% The S3EC MUST support the option to enable or disable legacy wrapping algorithms.
|
|
60
|
+
unless options[:security_profile] == :v2_and_legacy
|
|
61
|
+
##= ../specification/s3-encryption/client.md#enable-legacy-wrapping-algorithms
|
|
62
|
+
##% When disabled, the S3EC MUST NOT decrypt objects encrypted using legacy wrapping algorithms; it MUST throw an exception when attempting to decrypt an object encrypted with a legacy wrapping algorithm.
|
|
63
|
+
raise Errors::LegacyDecryptionError
|
|
64
|
+
end
|
|
65
|
+
##= ../specification/s3-encryption/client.md#enable-legacy-wrapping-algorithms
|
|
66
|
+
##% When enabled, the S3EC MUST be able to decrypt objects encrypted with all supported wrapping algorithms (both legacy and fully supported).
|
|
67
|
+
when 'kms+context'
|
|
68
|
+
if cek_alg != encryption_context['aws:x-amz-cek-alg']
|
|
69
|
+
raise Errors::CEKAlgMismatchError
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
if encryption_context != build_encryption_context(cek_alg, options)
|
|
73
|
+
raise Errors::DecryptionError, 'Value of encryption context from'\
|
|
74
|
+
' envelope does not match the provided encryption context'
|
|
75
|
+
end
|
|
76
|
+
when 'AES/GCM'
|
|
77
|
+
raise ArgumentError, 'Key mismatch - Client is configured' \
|
|
78
|
+
' with a KMS key and the x-amz-wrap-alg is AES/GCM.'
|
|
79
|
+
when 'RSA-OAEP-SHA1'
|
|
80
|
+
raise ArgumentError, 'Key mismatch - Client is configured' \
|
|
81
|
+
' with a KMS key and the x-amz-wrap-alg is RSA-OAEP-SHA1.'
|
|
82
|
+
else
|
|
83
|
+
raise ArgumentError, 'Unsupported wrap-alg: ' \
|
|
84
|
+
"#{envelope['x-amz-wrap-alg']}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
any_cmk_mode = false || options[:kms_allow_decrypt_with_any_cmk]
|
|
88
|
+
decrypt_options = {
|
|
89
|
+
ciphertext_blob: decode64(envelope['x-amz-key-v2']),
|
|
90
|
+
encryption_context: encryption_context
|
|
91
|
+
}
|
|
92
|
+
unless any_cmk_mode
|
|
93
|
+
decrypt_options[:key_id] = @kms_key_id
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
key = Aws::Plugins::UserAgent.metric('S3_CRYPTO_V2') do
|
|
97
|
+
@kms_client.decrypt(decrypt_options).plaintext
|
|
98
|
+
end
|
|
99
|
+
iv = decode64(envelope['x-amz-iv'])
|
|
100
|
+
block_mode =
|
|
101
|
+
case cek_alg
|
|
102
|
+
when 'AES/CBC/PKCS5Padding'
|
|
103
|
+
:CBC
|
|
104
|
+
when 'AES/CBC/PKCS7Padding'
|
|
105
|
+
:CBC
|
|
106
|
+
when 'AES/GCM/NoPadding'
|
|
107
|
+
:GCM
|
|
108
|
+
else
|
|
109
|
+
type = envelope['x-amz-cek-alg'].inspect
|
|
110
|
+
msg = "unsupported content encrypting key (cek) format: #{type}"
|
|
111
|
+
raise Errors::DecryptionError, msg
|
|
112
|
+
end
|
|
113
|
+
Utils.aes_decryption_cipher(block_mode, key, iv)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
private
|
|
117
|
+
|
|
118
|
+
def validate_key_wrap(key_wrap_schema)
|
|
119
|
+
case key_wrap_schema
|
|
120
|
+
when :kms_context then 'kms+context'
|
|
121
|
+
else
|
|
122
|
+
raise ArgumentError, "Unsupported key_wrap_schema: #{key_wrap_schema}"
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def validate_cek(content_encryption_schema)
|
|
127
|
+
case content_encryption_schema
|
|
128
|
+
when :aes_gcm_no_padding
|
|
129
|
+
"AES/GCM/NoPadding"
|
|
130
|
+
else
|
|
131
|
+
raise ArgumentError, "Unsupported content_encryption_schema: #{content_encryption_schema}"
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def validate_kms_key(kms_key_id)
|
|
136
|
+
if kms_key_id.nil? || kms_key_id.length.zero?
|
|
137
|
+
raise ArgumentError, 'KMS CMK ID was not specified. ' \
|
|
138
|
+
'Please specify a CMK ID, ' \
|
|
139
|
+
'or set kms_key_id: :kms_allow_decrypt_with_any_cmk to use ' \
|
|
140
|
+
'any valid CMK from the object.'
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
if kms_key_id.is_a?(Symbol) && kms_key_id != :kms_allow_decrypt_with_any_cmk
|
|
144
|
+
raise ArgumentError, 'kms_key_id must be a valid KMS CMK or be ' \
|
|
145
|
+
'set to :kms_allow_decrypt_with_any_cmk'
|
|
146
|
+
end
|
|
147
|
+
kms_key_id
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def build_encryption_context(cek_alg, options = {})
|
|
151
|
+
kms_context = (options[:kms_encryption_context] || {})
|
|
152
|
+
.each_with_object({}) { |(k, v), h| h[k.to_s] = v }
|
|
153
|
+
if kms_context.include? 'aws:x-amz-cek-alg'
|
|
154
|
+
raise ArgumentError, 'Conflict in reserved KMS Encryption Context ' \
|
|
155
|
+
'key aws:x-amz-cek-alg. This value is reserved for the S3 ' \
|
|
156
|
+
'Encryption Client and cannot be set by the user.'
|
|
157
|
+
end
|
|
158
|
+
{
|
|
159
|
+
'aws:x-amz-cek-alg' => cek_alg
|
|
160
|
+
}.merge(kms_context)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def encode64(str)
|
|
164
|
+
Base64.encode64(str).split("\n") * ""
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def decode64(str)
|
|
168
|
+
Base64.decode64(str)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def validate_key_for_encryption
|
|
172
|
+
if @kms_key_id == :kms_allow_decrypt_with_any_cmk
|
|
173
|
+
raise ArgumentError, 'Unable to encrypt/write objects with '\
|
|
174
|
+
'kms_key_id = :kms_allow_decrypt_with_any_cmk. Provide ' \
|
|
175
|
+
'a valid kms_key_id on client construction.'
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'base64'
|
|
4
|
+
|
|
5
|
+
module Aws
|
|
6
|
+
module S3
|
|
7
|
+
module EncryptionV2
|
|
8
|
+
class Materials
|
|
9
|
+
|
|
10
|
+
# @option options [required, OpenSSL::PKey::RSA, String] :key
|
|
11
|
+
# The master key to use for encrypting/decrypting all objects.
|
|
12
|
+
#
|
|
13
|
+
# @option options [String<JSON>] :description ('{}')
|
|
14
|
+
# The encryption materials description. This is must be
|
|
15
|
+
# a JSON document string.
|
|
16
|
+
#
|
|
17
|
+
def initialize(options = {})
|
|
18
|
+
@key = validate_key(options[:key])
|
|
19
|
+
@description = validate_desc(options[:description])
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @return [OpenSSL::PKey::RSA, String]
|
|
23
|
+
attr_reader :key
|
|
24
|
+
|
|
25
|
+
# @return [String<JSON>]
|
|
26
|
+
attr_reader :description
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def validate_key(key)
|
|
31
|
+
case key
|
|
32
|
+
when OpenSSL::PKey::RSA then key
|
|
33
|
+
when String
|
|
34
|
+
if [32, 24, 16].include?(key.bytesize)
|
|
35
|
+
key
|
|
36
|
+
else
|
|
37
|
+
msg = 'invalid key, symmetric key required to be 16, 24, or '\
|
|
38
|
+
'32 bytes in length, saw length ' + key.bytesize.to_s
|
|
39
|
+
raise ArgumentError, msg
|
|
40
|
+
end
|
|
41
|
+
else
|
|
42
|
+
msg = 'invalid encryption key, expected an OpenSSL::PKey::RSA key '\
|
|
43
|
+
'(for asymmetric encryption) or a String (for symmetric '\
|
|
44
|
+
'encryption).'
|
|
45
|
+
raise ArgumentError, msg
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def validate_desc(description)
|
|
50
|
+
Json.load(description)
|
|
51
|
+
description
|
|
52
|
+
rescue Json::ParseError, EncodingError
|
|
53
|
+
msg = 'expected description to be a valid JSON document string'
|
|
54
|
+
raise ArgumentError, msg
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'openssl'
|
|
4
|
+
|
|
5
|
+
module Aws
|
|
6
|
+
module S3
|
|
7
|
+
module EncryptionV2
|
|
8
|
+
# @api private
|
|
9
|
+
module Utils
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
|
|
13
|
+
def encrypt_aes_gcm(key, data, auth_data)
|
|
14
|
+
cipher = aes_encryption_cipher(:GCM, key)
|
|
15
|
+
cipher.iv = (iv = cipher.random_iv)
|
|
16
|
+
cipher.auth_data = auth_data
|
|
17
|
+
|
|
18
|
+
iv + cipher.update(data) + cipher.final + cipher.auth_tag
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def encrypt_rsa(key, data, auth_data)
|
|
22
|
+
# Plaintext must be KeyLengthInBytes (1 Byte) + DataKey + AuthData
|
|
23
|
+
buf = [data.bytesize] + data.unpack('C*') + auth_data.unpack('C*')
|
|
24
|
+
key.public_encrypt(buf.pack('C*'), OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def decrypt(key, data)
|
|
28
|
+
begin
|
|
29
|
+
case key
|
|
30
|
+
when OpenSSL::PKey::RSA # asymmetric decryption
|
|
31
|
+
key.private_decrypt(data)
|
|
32
|
+
when String # symmetric Decryption
|
|
33
|
+
cipher = aes_cipher(:decrypt, :ECB, key, nil)
|
|
34
|
+
cipher.update(data) + cipher.final
|
|
35
|
+
end
|
|
36
|
+
rescue OpenSSL::Cipher::CipherError
|
|
37
|
+
msg = 'decryption failed, possible incorrect key'
|
|
38
|
+
raise Errors::DecryptionError, msg
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def decrypt_aes_gcm(key, data, auth_data)
|
|
43
|
+
# data is iv (12B) + key + tag (16B)
|
|
44
|
+
buf = data.unpack('C*')
|
|
45
|
+
iv = buf[0,12].pack('C*') # iv will always be 12 bytes
|
|
46
|
+
tag = buf[-16, 16].pack('C*') # tag is 16 bytes
|
|
47
|
+
enc_key = buf[12, buf.size - (12+16)].pack('C*')
|
|
48
|
+
cipher = aes_cipher(:decrypt, :GCM, key, iv)
|
|
49
|
+
cipher.auth_tag = tag
|
|
50
|
+
cipher.auth_data = auth_data
|
|
51
|
+
cipher.update(enc_key) + cipher.final
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# returns the decrypted data + auth_data
|
|
55
|
+
def decrypt_rsa(key, enc_data)
|
|
56
|
+
# Plaintext must be KeyLengthInBytes (1 Byte) + DataKey + AuthData
|
|
57
|
+
buf = key.private_decrypt(enc_data, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING).unpack('C*')
|
|
58
|
+
key_length = buf[0]
|
|
59
|
+
data = buf[1, key_length].pack('C*')
|
|
60
|
+
auth_data = buf[key_length+1, buf.length - key_length].pack('C*')
|
|
61
|
+
[data, auth_data]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @param [String] block_mode "CBC" or "ECB"
|
|
65
|
+
# @param [OpenSSL::PKey::RSA, String, nil] key
|
|
66
|
+
# @param [String, nil] iv The initialization vector
|
|
67
|
+
def aes_encryption_cipher(block_mode, key = nil, iv = nil)
|
|
68
|
+
aes_cipher(:encrypt, block_mode, key, iv)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @param [String] block_mode "CBC" or "ECB"
|
|
72
|
+
# @param [OpenSSL::PKey::RSA, String, nil] key
|
|
73
|
+
# @param [String, nil] iv The initialization vector
|
|
74
|
+
def aes_decryption_cipher(block_mode, key = nil, iv = nil)
|
|
75
|
+
aes_cipher(:decrypt, block_mode, key, iv)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @param [String] mode "encrypt" or "decrypt"
|
|
79
|
+
# @param [String] block_mode "CBC" or "ECB"
|
|
80
|
+
# @param [OpenSSL::PKey::RSA, String, nil] key
|
|
81
|
+
# @param [String, nil] iv The initialization vector
|
|
82
|
+
def aes_cipher(mode, block_mode, key, iv)
|
|
83
|
+
##= ../specification/s3-encryption/encryption.md#alg-aes-256-gcm-iv12-tag16-no-kdf
|
|
84
|
+
##% The client MUST initialize the cipher,
|
|
85
|
+
##% or call an AES-GCM encryption API, with the plaintext data key, the generated IV,
|
|
86
|
+
##% and the tag length defined in the Algorithm Suite
|
|
87
|
+
##% when encrypting with ALG_AES_256_GCM_IV12_TAG16_NO_KDF.
|
|
88
|
+
cipher = key ?
|
|
89
|
+
OpenSSL::Cipher.new("aes-#{cipher_size(key)}-#{block_mode.downcase}") :
|
|
90
|
+
OpenSSL::Cipher.new("aes-256-#{block_mode.downcase}")
|
|
91
|
+
cipher.send(mode) # encrypt or decrypt
|
|
92
|
+
cipher.key = key if key
|
|
93
|
+
cipher.iv = iv if iv
|
|
94
|
+
cipher
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# @param [String] key
|
|
98
|
+
# @return [Integer]
|
|
99
|
+
# @raise ArgumentError
|
|
100
|
+
def cipher_size(key)
|
|
101
|
+
key.bytesize * 8
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|