aws-sdk-s3 1.48.0 → 1.183.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1352 -0
- data/LICENSE.txt +202 -0
- data/VERSION +1 -0
- data/lib/aws-sdk-s3/access_grants_credentials.rb +57 -0
- data/lib/aws-sdk-s3/access_grants_credentials_provider.rb +250 -0
- data/lib/aws-sdk-s3/bucket.rb +1005 -106
- data/lib/aws-sdk-s3/bucket_acl.rb +65 -18
- data/lib/aws-sdk-s3/bucket_cors.rb +80 -18
- data/lib/aws-sdk-s3/bucket_lifecycle.rb +71 -20
- data/lib/aws-sdk-s3/bucket_lifecycle_configuration.rb +126 -21
- data/lib/aws-sdk-s3/bucket_logging.rb +68 -16
- data/lib/aws-sdk-s3/bucket_notification.rb +52 -20
- data/lib/aws-sdk-s3/bucket_policy.rb +108 -17
- data/lib/aws-sdk-s3/bucket_region_cache.rb +11 -5
- data/lib/aws-sdk-s3/bucket_request_payment.rb +60 -15
- data/lib/aws-sdk-s3/bucket_tagging.rb +71 -18
- data/lib/aws-sdk-s3/bucket_versioning.rb +133 -17
- data/lib/aws-sdk-s3/bucket_website.rb +78 -21
- data/lib/aws-sdk-s3/client.rb +14517 -941
- data/lib/aws-sdk-s3/client_api.rb +1296 -197
- data/lib/aws-sdk-s3/customizations/bucket.rb +56 -37
- data/lib/aws-sdk-s3/customizations/errors.rb +40 -0
- data/lib/aws-sdk-s3/customizations/multipart_upload.rb +2 -0
- data/lib/aws-sdk-s3/customizations/object.rb +288 -68
- data/lib/aws-sdk-s3/customizations/object_summary.rb +10 -0
- data/lib/aws-sdk-s3/customizations/object_version.rb +13 -0
- data/lib/aws-sdk-s3/customizations/types/list_object_versions_output.rb +2 -0
- data/lib/aws-sdk-s3/customizations/types/permanent_redirect.rb +26 -0
- data/lib/aws-sdk-s3/customizations.rb +27 -28
- data/lib/aws-sdk-s3/encryption/client.rb +28 -7
- data/lib/aws-sdk-s3/encryption/decrypt_handler.rb +71 -29
- data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +43 -5
- data/lib/aws-sdk-s3/encryption/default_key_provider.rb +2 -0
- data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +13 -2
- data/lib/aws-sdk-s3/encryption/errors.rb +2 -0
- data/lib/aws-sdk-s3/encryption/io_auth_decrypter.rb +2 -0
- data/lib/aws-sdk-s3/encryption/io_decrypter.rb +11 -3
- data/lib/aws-sdk-s3/encryption/io_encrypter.rb +2 -0
- data/lib/aws-sdk-s3/encryption/key_provider.rb +2 -0
- data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +46 -11
- data/lib/aws-sdk-s3/encryption/materials.rb +8 -6
- data/lib/aws-sdk-s3/encryption/utils.rb +25 -0
- data/lib/aws-sdk-s3/encryption.rb +4 -0
- data/lib/aws-sdk-s3/encryptionV2/client.rb +570 -0
- data/lib/aws-sdk-s3/encryptionV2/decrypt_handler.rb +223 -0
- data/lib/aws-sdk-s3/encryptionV2/default_cipher_provider.rb +170 -0
- data/lib/aws-sdk-s3/encryptionV2/default_key_provider.rb +40 -0
- data/lib/aws-sdk-s3/encryptionV2/encrypt_handler.rb +65 -0
- data/lib/aws-sdk-s3/encryptionV2/errors.rb +37 -0
- data/lib/aws-sdk-s3/encryptionV2/io_auth_decrypter.rb +58 -0
- data/lib/aws-sdk-s3/encryptionV2/io_decrypter.rb +37 -0
- data/lib/aws-sdk-s3/encryptionV2/io_encrypter.rb +73 -0
- data/lib/aws-sdk-s3/encryptionV2/key_provider.rb +31 -0
- data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +173 -0
- data/lib/aws-sdk-s3/encryptionV2/materials.rb +60 -0
- data/lib/aws-sdk-s3/encryptionV2/utils.rb +103 -0
- data/lib/aws-sdk-s3/encryption_v2.rb +23 -0
- data/lib/aws-sdk-s3/endpoint_parameters.rb +181 -0
- data/lib/aws-sdk-s3/endpoint_provider.rb +716 -0
- data/lib/aws-sdk-s3/endpoints.rb +1434 -0
- data/lib/aws-sdk-s3/errors.rb +170 -1
- data/lib/aws-sdk-s3/event_streams.rb +8 -1
- data/lib/aws-sdk-s3/express_credentials.rb +55 -0
- data/lib/aws-sdk-s3/express_credentials_provider.rb +59 -0
- data/lib/aws-sdk-s3/file_downloader.rb +161 -46
- data/lib/aws-sdk-s3/file_part.rb +11 -6
- data/lib/aws-sdk-s3/file_uploader.rb +39 -18
- data/lib/aws-sdk-s3/legacy_signer.rb +17 -25
- data/lib/aws-sdk-s3/multipart_file_uploader.rb +104 -27
- data/lib/aws-sdk-s3/multipart_stream_uploader.rb +61 -21
- data/lib/aws-sdk-s3/multipart_upload.rb +342 -32
- data/lib/aws-sdk-s3/multipart_upload_error.rb +2 -0
- data/lib/aws-sdk-s3/multipart_upload_part.rb +384 -46
- data/lib/aws-sdk-s3/object.rb +2600 -231
- data/lib/aws-sdk-s3/object_acl.rb +103 -25
- data/lib/aws-sdk-s3/object_copier.rb +9 -5
- data/lib/aws-sdk-s3/object_multipart_copier.rb +48 -22
- data/lib/aws-sdk-s3/object_summary.rb +2174 -204
- data/lib/aws-sdk-s3/object_version.rb +539 -80
- data/lib/aws-sdk-s3/plugins/accelerate.rb +17 -64
- data/lib/aws-sdk-s3/plugins/access_grants.rb +178 -0
- data/lib/aws-sdk-s3/plugins/arn.rb +70 -0
- data/lib/aws-sdk-s3/plugins/bucket_dns.rb +7 -43
- data/lib/aws-sdk-s3/plugins/bucket_name_restrictions.rb +20 -3
- data/lib/aws-sdk-s3/plugins/checksum_algorithm.rb +31 -0
- data/lib/aws-sdk-s3/plugins/dualstack.rb +7 -50
- data/lib/aws-sdk-s3/plugins/endpoints.rb +86 -0
- data/lib/aws-sdk-s3/plugins/expect_100_continue.rb +5 -4
- data/lib/aws-sdk-s3/plugins/express_session_auth.rb +88 -0
- data/lib/aws-sdk-s3/plugins/get_bucket_location_fix.rb +3 -1
- data/lib/aws-sdk-s3/plugins/http_200_errors.rb +62 -17
- data/lib/aws-sdk-s3/plugins/iad_regional_endpoint.rb +44 -0
- data/lib/aws-sdk-s3/plugins/location_constraint.rb +5 -1
- data/lib/aws-sdk-s3/plugins/md5s.rb +14 -70
- data/lib/aws-sdk-s3/plugins/redirects.rb +2 -0
- data/lib/aws-sdk-s3/plugins/s3_host_id.rb +2 -0
- data/lib/aws-sdk-s3/plugins/s3_signer.rb +63 -94
- data/lib/aws-sdk-s3/plugins/sse_cpk.rb +3 -1
- data/lib/aws-sdk-s3/plugins/streaming_retry.rb +139 -0
- data/lib/aws-sdk-s3/plugins/url_encoded_keys.rb +2 -0
- data/lib/aws-sdk-s3/presigned_post.rb +160 -99
- data/lib/aws-sdk-s3/presigner.rb +141 -62
- data/lib/aws-sdk-s3/resource.rb +156 -17
- data/lib/aws-sdk-s3/types.rb +13021 -4106
- data/lib/aws-sdk-s3/waiters.rb +67 -1
- data/lib/aws-sdk-s3.rb +46 -32
- data/sig/bucket.rbs +222 -0
- data/sig/bucket_acl.rbs +78 -0
- data/sig/bucket_cors.rbs +69 -0
- data/sig/bucket_lifecycle.rbs +88 -0
- data/sig/bucket_lifecycle_configuration.rbs +115 -0
- data/sig/bucket_logging.rbs +76 -0
- data/sig/bucket_notification.rbs +114 -0
- data/sig/bucket_policy.rbs +59 -0
- data/sig/bucket_request_payment.rbs +54 -0
- data/sig/bucket_tagging.rbs +65 -0
- data/sig/bucket_versioning.rbs +77 -0
- data/sig/bucket_website.rbs +93 -0
- data/sig/client.rbs +2472 -0
- data/sig/customizations/bucket.rbs +19 -0
- data/sig/customizations/object.rbs +38 -0
- data/sig/customizations/object_summary.rbs +35 -0
- data/sig/errors.rbs +42 -0
- data/sig/multipart_upload.rbs +120 -0
- data/sig/multipart_upload_part.rbs +109 -0
- data/sig/object.rbs +459 -0
- data/sig/object_acl.rbs +86 -0
- data/sig/object_summary.rbs +345 -0
- data/sig/object_version.rbs +143 -0
- data/sig/resource.rbs +134 -0
- data/sig/types.rbs +2712 -0
- data/sig/waiters.rbs +95 -0
- metadata +74 -15
@@ -0,0 +1,73 @@
|
|
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
|
+
if plain_text.empty?
|
48
|
+
StringIO.new(cipher.final + cipher.auth_tag)
|
49
|
+
else
|
50
|
+
StringIO.new(cipher.update(plain_text) + cipher.final + cipher.auth_tag)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def encrypt_to_tempfile(cipher, io)
|
55
|
+
encrypted = Tempfile.new(self.object_id.to_s)
|
56
|
+
encrypted.binmode
|
57
|
+
while chunk = io.read(ONE_MEGABYTE, read_buffer ||= String.new)
|
58
|
+
if cipher.method(:update).arity == 1
|
59
|
+
encrypted.write(cipher.update(chunk))
|
60
|
+
else
|
61
|
+
encrypted.write(cipher.update(chunk, cipher_buffer ||= String.new))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
encrypted.write(cipher.final)
|
65
|
+
encrypted.write(cipher.auth_tag)
|
66
|
+
encrypted.rewind
|
67
|
+
encrypted
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
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,173 @@
|
|
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
|
+
envelope = {
|
37
|
+
'x-amz-key-v2' => encode64(key_data.ciphertext_blob),
|
38
|
+
'x-amz-iv' => encode64(cipher.iv = cipher.random_iv),
|
39
|
+
'x-amz-cek-alg' => @content_encryption_schema,
|
40
|
+
'x-amz-tag-len' => (AES_GCM_TAG_LEN_BYTES * 8).to_s,
|
41
|
+
'x-amz-wrap-alg' => @key_wrap_schema,
|
42
|
+
'x-amz-matdesc' => Json.dump(encryption_context)
|
43
|
+
}
|
44
|
+
cipher.auth_data = '' # auth_data must be set after key and iv
|
45
|
+
[envelope, cipher]
|
46
|
+
end
|
47
|
+
|
48
|
+
# @return [Cipher] Given an encryption envelope, returns a
|
49
|
+
# decryption cipher.
|
50
|
+
def decryption_cipher(envelope, options = {})
|
51
|
+
encryption_context = Json.load(envelope['x-amz-matdesc'])
|
52
|
+
cek_alg = envelope['x-amz-cek-alg']
|
53
|
+
|
54
|
+
case envelope['x-amz-wrap-alg']
|
55
|
+
when 'kms'
|
56
|
+
unless options[:security_profile] == :v2_and_legacy
|
57
|
+
raise Errors::LegacyDecryptionError
|
58
|
+
end
|
59
|
+
when 'kms+context'
|
60
|
+
if cek_alg != encryption_context['aws:x-amz-cek-alg']
|
61
|
+
raise Errors::CEKAlgMismatchError
|
62
|
+
end
|
63
|
+
|
64
|
+
if encryption_context != build_encryption_context(cek_alg, options)
|
65
|
+
raise Errors::DecryptionError, 'Value of encryption context from'\
|
66
|
+
' envelope does not match the provided encryption context'
|
67
|
+
end
|
68
|
+
when 'AES/GCM'
|
69
|
+
raise ArgumentError, 'Key mismatch - Client is configured' \
|
70
|
+
' with a KMS key and the x-amz-wrap-alg is AES/GCM.'
|
71
|
+
when 'RSA-OAEP-SHA1'
|
72
|
+
raise ArgumentError, 'Key mismatch - Client is configured' \
|
73
|
+
' with a KMS key and the x-amz-wrap-alg is RSA-OAEP-SHA1.'
|
74
|
+
else
|
75
|
+
raise ArgumentError, 'Unsupported wrap-alg: ' \
|
76
|
+
"#{envelope['x-amz-wrap-alg']}"
|
77
|
+
end
|
78
|
+
|
79
|
+
any_cmk_mode = false || options[:kms_allow_decrypt_with_any_cmk]
|
80
|
+
decrypt_options = {
|
81
|
+
ciphertext_blob: decode64(envelope['x-amz-key-v2']),
|
82
|
+
encryption_context: encryption_context
|
83
|
+
}
|
84
|
+
unless any_cmk_mode
|
85
|
+
decrypt_options[:key_id] = @kms_key_id
|
86
|
+
end
|
87
|
+
|
88
|
+
key = Aws::Plugins::UserAgent.metric('S3_CRYPTO_V2') do
|
89
|
+
@kms_client.decrypt(decrypt_options).plaintext
|
90
|
+
end
|
91
|
+
iv = decode64(envelope['x-amz-iv'])
|
92
|
+
block_mode =
|
93
|
+
case cek_alg
|
94
|
+
when 'AES/CBC/PKCS5Padding'
|
95
|
+
:CBC
|
96
|
+
when 'AES/CBC/PKCS7Padding'
|
97
|
+
:CBC
|
98
|
+
when 'AES/GCM/NoPadding'
|
99
|
+
:GCM
|
100
|
+
else
|
101
|
+
type = envelope['x-amz-cek-alg'].inspect
|
102
|
+
msg = "unsupported content encrypting key (cek) format: #{type}"
|
103
|
+
raise Errors::DecryptionError, msg
|
104
|
+
end
|
105
|
+
Utils.aes_decryption_cipher(block_mode, key, iv)
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def validate_key_wrap(key_wrap_schema)
|
111
|
+
case key_wrap_schema
|
112
|
+
when :kms_context then 'kms+context'
|
113
|
+
else
|
114
|
+
raise ArgumentError, "Unsupported key_wrap_schema: #{key_wrap_schema}"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def validate_cek(content_encryption_schema)
|
119
|
+
case content_encryption_schema
|
120
|
+
when :aes_gcm_no_padding
|
121
|
+
"AES/GCM/NoPadding"
|
122
|
+
else
|
123
|
+
raise ArgumentError, "Unsupported content_encryption_schema: #{content_encryption_schema}"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def validate_kms_key(kms_key_id)
|
128
|
+
if kms_key_id.nil? || kms_key_id.length.zero?
|
129
|
+
raise ArgumentError, 'KMS CMK ID was not specified. ' \
|
130
|
+
'Please specify a CMK ID, ' \
|
131
|
+
'or set kms_key_id: :kms_allow_decrypt_with_any_cmk to use ' \
|
132
|
+
'any valid CMK from the object.'
|
133
|
+
end
|
134
|
+
|
135
|
+
if kms_key_id.is_a?(Symbol) && kms_key_id != :kms_allow_decrypt_with_any_cmk
|
136
|
+
raise ArgumentError, 'kms_key_id must be a valid KMS CMK or be ' \
|
137
|
+
'set to :kms_allow_decrypt_with_any_cmk'
|
138
|
+
end
|
139
|
+
kms_key_id
|
140
|
+
end
|
141
|
+
|
142
|
+
def build_encryption_context(cek_alg, options = {})
|
143
|
+
kms_context = (options[:kms_encryption_context] || {})
|
144
|
+
.each_with_object({}) { |(k, v), h| h[k.to_s] = v }
|
145
|
+
if kms_context.include? 'aws:x-amz-cek-alg'
|
146
|
+
raise ArgumentError, 'Conflict in reserved KMS Encryption Context ' \
|
147
|
+
'key aws:x-amz-cek-alg. This value is reserved for the S3 ' \
|
148
|
+
'Encryption Client and cannot be set by the user.'
|
149
|
+
end
|
150
|
+
{
|
151
|
+
'aws:x-amz-cek-alg' => cek_alg
|
152
|
+
}.merge(kms_context)
|
153
|
+
end
|
154
|
+
|
155
|
+
def encode64(str)
|
156
|
+
Base64.encode64(str).split("\n") * ""
|
157
|
+
end
|
158
|
+
|
159
|
+
def decode64(str)
|
160
|
+
Base64.decode64(str)
|
161
|
+
end
|
162
|
+
|
163
|
+
def validate_key_for_encryption
|
164
|
+
if @kms_key_id == :kms_allow_decrypt_with_any_cmk
|
165
|
+
raise ArgumentError, 'Unable to encrypt/write objects with '\
|
166
|
+
'kms_key_id = :kms_allow_decrypt_with_any_cmk. Provide ' \
|
167
|
+
'a valid kms_key_id on client construction.'
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
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,103 @@
|
|
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
|
+
cipher = key ?
|
84
|
+
OpenSSL::Cipher.new("aes-#{cipher_size(key)}-#{block_mode.downcase}") :
|
85
|
+
OpenSSL::Cipher.new("aes-256-#{block_mode.downcase}")
|
86
|
+
cipher.send(mode) # encrypt or decrypt
|
87
|
+
cipher.key = key if key
|
88
|
+
cipher.iv = iv if iv
|
89
|
+
cipher
|
90
|
+
end
|
91
|
+
|
92
|
+
# @param [String] key
|
93
|
+
# @return [Integer]
|
94
|
+
# @raise ArgumentError
|
95
|
+
def cipher_size(key)
|
96
|
+
key.bytesize * 8
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'aws-sdk-s3/encryptionV2/client'
|
2
|
+
require 'aws-sdk-s3/encryptionV2/decrypt_handler'
|
3
|
+
require 'aws-sdk-s3/encryptionV2/default_cipher_provider'
|
4
|
+
require 'aws-sdk-s3/encryptionV2/encrypt_handler'
|
5
|
+
require 'aws-sdk-s3/encryptionV2/errors'
|
6
|
+
require 'aws-sdk-s3/encryptionV2/io_encrypter'
|
7
|
+
require 'aws-sdk-s3/encryptionV2/io_decrypter'
|
8
|
+
require 'aws-sdk-s3/encryptionV2/io_auth_decrypter'
|
9
|
+
require 'aws-sdk-s3/encryptionV2/key_provider'
|
10
|
+
require 'aws-sdk-s3/encryptionV2/kms_cipher_provider'
|
11
|
+
require 'aws-sdk-s3/encryptionV2/materials'
|
12
|
+
require 'aws-sdk-s3/encryptionV2/utils'
|
13
|
+
require 'aws-sdk-s3/encryptionV2/default_key_provider'
|
14
|
+
|
15
|
+
module Aws
|
16
|
+
module S3
|
17
|
+
module EncryptionV2
|
18
|
+
AES_GCM_TAG_LEN_BYTES = 16
|
19
|
+
EC_USER_AGENT = 'S3CryptoV2'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,181 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# WARNING ABOUT GENERATED CODE
|
4
|
+
#
|
5
|
+
# This file is generated. See the contributing guide for more information:
|
6
|
+
# https://github.com/aws/aws-sdk-ruby/blob/version-3/CONTRIBUTING.md
|
7
|
+
#
|
8
|
+
# WARNING ABOUT GENERATED CODE
|
9
|
+
|
10
|
+
module Aws::S3
|
11
|
+
# Endpoint parameters used to influence endpoints per request.
|
12
|
+
#
|
13
|
+
# @!attribute bucket
|
14
|
+
# The S3 bucket used to send the request. This is an optional parameter that will be set automatically for operations that are scoped to an S3 bucket.
|
15
|
+
#
|
16
|
+
# @return [String]
|
17
|
+
#
|
18
|
+
# @!attribute region
|
19
|
+
# The AWS region used to dispatch the request.
|
20
|
+
#
|
21
|
+
# @return [String]
|
22
|
+
#
|
23
|
+
# @!attribute use_fips
|
24
|
+
# When true, send this request to the FIPS-compliant regional endpoint. If the configured endpoint does not have a FIPS compliant endpoint, dispatching the request will return an error.
|
25
|
+
#
|
26
|
+
# @return [Boolean]
|
27
|
+
#
|
28
|
+
# @!attribute use_dual_stack
|
29
|
+
# When true, use the dual-stack endpoint. If the configured endpoint does not support dual-stack, dispatching the request MAY return an error.
|
30
|
+
#
|
31
|
+
# @return [Boolean]
|
32
|
+
#
|
33
|
+
# @!attribute endpoint
|
34
|
+
# Override the endpoint used to send this request
|
35
|
+
#
|
36
|
+
# @return [String]
|
37
|
+
#
|
38
|
+
# @!attribute force_path_style
|
39
|
+
# When true, force a path-style endpoint to be used where the bucket name is part of the path.
|
40
|
+
#
|
41
|
+
# @return [Boolean]
|
42
|
+
#
|
43
|
+
# @!attribute accelerate
|
44
|
+
# When true, use S3 Accelerate. NOTE: Not all regions support S3 accelerate.
|
45
|
+
#
|
46
|
+
# @return [Boolean]
|
47
|
+
#
|
48
|
+
# @!attribute use_global_endpoint
|
49
|
+
# Whether the global endpoint should be used, rather then the regional endpoint for us-east-1.
|
50
|
+
#
|
51
|
+
# @return [Boolean]
|
52
|
+
#
|
53
|
+
# @!attribute use_object_lambda_endpoint
|
54
|
+
# Internal parameter to use object lambda endpoint for an operation (eg: WriteGetObjectResponse)
|
55
|
+
#
|
56
|
+
# @return [Boolean]
|
57
|
+
#
|
58
|
+
# @!attribute key
|
59
|
+
# The S3 Key used to send the request. This is an optional parameter that will be set automatically for operations that are scoped to an S3 Key.
|
60
|
+
#
|
61
|
+
# @return [String]
|
62
|
+
#
|
63
|
+
# @!attribute prefix
|
64
|
+
# The S3 Prefix used to send the request. This is an optional parameter that will be set automatically for operations that are scoped to an S3 Prefix.
|
65
|
+
#
|
66
|
+
# @return [String]
|
67
|
+
#
|
68
|
+
# @!attribute copy_source
|
69
|
+
# The Copy Source used for Copy Object request. This is an optional parameter that will be set automatically for operations that are scoped to Copy Source.
|
70
|
+
#
|
71
|
+
# @return [String]
|
72
|
+
#
|
73
|
+
# @!attribute disable_access_points
|
74
|
+
# Internal parameter to disable Access Point Buckets
|
75
|
+
#
|
76
|
+
# @return [Boolean]
|
77
|
+
#
|
78
|
+
# @!attribute disable_multi_region_access_points
|
79
|
+
# Whether multi-region access points (MRAP) should be disabled.
|
80
|
+
#
|
81
|
+
# @return [Boolean]
|
82
|
+
#
|
83
|
+
# @!attribute use_arn_region
|
84
|
+
# When an Access Point ARN is provided and this flag is enabled, the SDK MUST use the ARN's region when constructing the endpoint instead of the client's configured region.
|
85
|
+
#
|
86
|
+
# @return [Boolean]
|
87
|
+
#
|
88
|
+
# @!attribute use_s3_express_control_endpoint
|
89
|
+
# Internal parameter to indicate whether S3Express operation should use control plane, (ex. CreateBucket)
|
90
|
+
#
|
91
|
+
# @return [Boolean]
|
92
|
+
#
|
93
|
+
# @!attribute disable_s3_express_session_auth
|
94
|
+
# Parameter to indicate whether S3Express session auth should be disabled
|
95
|
+
#
|
96
|
+
# @return [Boolean]
|
97
|
+
#
|
98
|
+
EndpointParameters = Struct.new(
|
99
|
+
:bucket,
|
100
|
+
:region,
|
101
|
+
:use_fips,
|
102
|
+
:use_dual_stack,
|
103
|
+
:endpoint,
|
104
|
+
:force_path_style,
|
105
|
+
:accelerate,
|
106
|
+
:use_global_endpoint,
|
107
|
+
:use_object_lambda_endpoint,
|
108
|
+
:key,
|
109
|
+
:prefix,
|
110
|
+
:copy_source,
|
111
|
+
:disable_access_points,
|
112
|
+
:disable_multi_region_access_points,
|
113
|
+
:use_arn_region,
|
114
|
+
:use_s3_express_control_endpoint,
|
115
|
+
:disable_s3_express_session_auth,
|
116
|
+
) do
|
117
|
+
include Aws::Structure
|
118
|
+
|
119
|
+
# @api private
|
120
|
+
class << self
|
121
|
+
PARAM_MAP = {
|
122
|
+
'Bucket' => :bucket,
|
123
|
+
'Region' => :region,
|
124
|
+
'UseFIPS' => :use_fips,
|
125
|
+
'UseDualStack' => :use_dual_stack,
|
126
|
+
'Endpoint' => :endpoint,
|
127
|
+
'ForcePathStyle' => :force_path_style,
|
128
|
+
'Accelerate' => :accelerate,
|
129
|
+
'UseGlobalEndpoint' => :use_global_endpoint,
|
130
|
+
'UseObjectLambdaEndpoint' => :use_object_lambda_endpoint,
|
131
|
+
'Key' => :key,
|
132
|
+
'Prefix' => :prefix,
|
133
|
+
'CopySource' => :copy_source,
|
134
|
+
'DisableAccessPoints' => :disable_access_points,
|
135
|
+
'DisableMultiRegionAccessPoints' => :disable_multi_region_access_points,
|
136
|
+
'UseArnRegion' => :use_arn_region,
|
137
|
+
'UseS3ExpressControlEndpoint' => :use_s3_express_control_endpoint,
|
138
|
+
'DisableS3ExpressSessionAuth' => :disable_s3_express_session_auth,
|
139
|
+
}.freeze
|
140
|
+
end
|
141
|
+
|
142
|
+
def initialize(options = {})
|
143
|
+
self[:bucket] = options[:bucket]
|
144
|
+
self[:region] = options[:region]
|
145
|
+
self[:use_fips] = options[:use_fips]
|
146
|
+
self[:use_fips] = false if self[:use_fips].nil?
|
147
|
+
self[:use_dual_stack] = options[:use_dual_stack]
|
148
|
+
self[:use_dual_stack] = false if self[:use_dual_stack].nil?
|
149
|
+
self[:endpoint] = options[:endpoint]
|
150
|
+
self[:force_path_style] = options[:force_path_style]
|
151
|
+
self[:force_path_style] = false if self[:force_path_style].nil?
|
152
|
+
self[:accelerate] = options[:accelerate]
|
153
|
+
self[:accelerate] = false if self[:accelerate].nil?
|
154
|
+
self[:use_global_endpoint] = options[:use_global_endpoint]
|
155
|
+
self[:use_global_endpoint] = false if self[:use_global_endpoint].nil?
|
156
|
+
self[:use_object_lambda_endpoint] = options[:use_object_lambda_endpoint]
|
157
|
+
self[:key] = options[:key]
|
158
|
+
self[:prefix] = options[:prefix]
|
159
|
+
self[:copy_source] = options[:copy_source]
|
160
|
+
self[:disable_access_points] = options[:disable_access_points]
|
161
|
+
self[:disable_multi_region_access_points] = options[:disable_multi_region_access_points]
|
162
|
+
self[:disable_multi_region_access_points] = false if self[:disable_multi_region_access_points].nil?
|
163
|
+
self[:use_arn_region] = options[:use_arn_region]
|
164
|
+
self[:use_s3_express_control_endpoint] = options[:use_s3_express_control_endpoint]
|
165
|
+
self[:disable_s3_express_session_auth] = options[:disable_s3_express_session_auth]
|
166
|
+
end
|
167
|
+
|
168
|
+
def self.create(config, options={})
|
169
|
+
new({
|
170
|
+
region: config.region,
|
171
|
+
use_fips: config.use_fips_endpoint,
|
172
|
+
endpoint: (config.endpoint.to_s unless config.regional_endpoint),
|
173
|
+
force_path_style: config.force_path_style,
|
174
|
+
use_global_endpoint: config.s3_us_east_1_regional_endpoint == 'legacy',
|
175
|
+
disable_multi_region_access_points: config.s3_disable_multiregion_access_points,
|
176
|
+
use_arn_region: config.s3_use_arn_region,
|
177
|
+
disable_s3_express_session_auth: config.disable_s3_express_session_auth,
|
178
|
+
}.merge(options))
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|