aws-sdk-s3 1.75.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 +7 -0
- data/lib/aws-sdk-s3.rb +73 -0
- data/lib/aws-sdk-s3/bucket.rb +861 -0
- data/lib/aws-sdk-s3/bucket_acl.rb +277 -0
- data/lib/aws-sdk-s3/bucket_cors.rb +262 -0
- data/lib/aws-sdk-s3/bucket_lifecycle.rb +264 -0
- data/lib/aws-sdk-s3/bucket_lifecycle_configuration.rb +283 -0
- data/lib/aws-sdk-s3/bucket_logging.rb +251 -0
- data/lib/aws-sdk-s3/bucket_notification.rb +293 -0
- data/lib/aws-sdk-s3/bucket_policy.rb +242 -0
- data/lib/aws-sdk-s3/bucket_region_cache.rb +81 -0
- data/lib/aws-sdk-s3/bucket_request_payment.rb +236 -0
- data/lib/aws-sdk-s3/bucket_tagging.rb +251 -0
- data/lib/aws-sdk-s3/bucket_versioning.rb +312 -0
- data/lib/aws-sdk-s3/bucket_website.rb +292 -0
- data/lib/aws-sdk-s3/client.rb +11818 -0
- data/lib/aws-sdk-s3/client_api.rb +3014 -0
- data/lib/aws-sdk-s3/customizations.rb +34 -0
- data/lib/aws-sdk-s3/customizations/bucket.rb +162 -0
- data/lib/aws-sdk-s3/customizations/multipart_upload.rb +44 -0
- data/lib/aws-sdk-s3/customizations/object.rb +389 -0
- data/lib/aws-sdk-s3/customizations/object_summary.rb +85 -0
- data/lib/aws-sdk-s3/customizations/types/list_object_versions_output.rb +13 -0
- data/lib/aws-sdk-s3/encryption.rb +21 -0
- data/lib/aws-sdk-s3/encryption/client.rb +375 -0
- data/lib/aws-sdk-s3/encryption/decrypt_handler.rb +190 -0
- data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +65 -0
- data/lib/aws-sdk-s3/encryption/default_key_provider.rb +40 -0
- data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +61 -0
- data/lib/aws-sdk-s3/encryption/errors.rb +15 -0
- data/lib/aws-sdk-s3/encryption/io_auth_decrypter.rb +58 -0
- data/lib/aws-sdk-s3/encryption/io_decrypter.rb +36 -0
- data/lib/aws-sdk-s3/encryption/io_encrypter.rb +71 -0
- data/lib/aws-sdk-s3/encryption/key_provider.rb +31 -0
- data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +75 -0
- data/lib/aws-sdk-s3/encryption/materials.rb +60 -0
- data/lib/aws-sdk-s3/encryption/utils.rb +81 -0
- data/lib/aws-sdk-s3/encryptionV2/client.rb +388 -0
- data/lib/aws-sdk-s3/encryptionV2/decrypt_handler.rb +198 -0
- data/lib/aws-sdk-s3/encryptionV2/default_cipher_provider.rb +103 -0
- data/lib/aws-sdk-s3/encryptionV2/default_key_provider.rb +38 -0
- data/lib/aws-sdk-s3/encryptionV2/encrypt_handler.rb +66 -0
- data/lib/aws-sdk-s3/encryptionV2/errors.rb +13 -0
- data/lib/aws-sdk-s3/encryptionV2/io_auth_decrypter.rb +56 -0
- data/lib/aws-sdk-s3/encryptionV2/io_decrypter.rb +35 -0
- data/lib/aws-sdk-s3/encryptionV2/io_encrypter.rb +71 -0
- data/lib/aws-sdk-s3/encryptionV2/key_provider.rb +29 -0
- data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +99 -0
- data/lib/aws-sdk-s3/encryptionV2/materials.rb +58 -0
- data/lib/aws-sdk-s3/encryptionV2/utils.rb +116 -0
- data/lib/aws-sdk-s3/encryption_v2.rb +20 -0
- data/lib/aws-sdk-s3/errors.rb +115 -0
- data/lib/aws-sdk-s3/event_streams.rb +69 -0
- data/lib/aws-sdk-s3/file_downloader.rb +142 -0
- data/lib/aws-sdk-s3/file_part.rb +78 -0
- data/lib/aws-sdk-s3/file_uploader.rb +70 -0
- data/lib/aws-sdk-s3/legacy_signer.rb +189 -0
- data/lib/aws-sdk-s3/multipart_file_uploader.rb +227 -0
- data/lib/aws-sdk-s3/multipart_stream_uploader.rb +173 -0
- data/lib/aws-sdk-s3/multipart_upload.rb +401 -0
- data/lib/aws-sdk-s3/multipart_upload_error.rb +18 -0
- data/lib/aws-sdk-s3/multipart_upload_part.rb +423 -0
- data/lib/aws-sdk-s3/object.rb +1422 -0
- data/lib/aws-sdk-s3/object_acl.rb +333 -0
- data/lib/aws-sdk-s3/object_copier.rb +101 -0
- data/lib/aws-sdk-s3/object_multipart_copier.rb +182 -0
- data/lib/aws-sdk-s3/object_summary.rb +1181 -0
- data/lib/aws-sdk-s3/object_version.rb +550 -0
- data/lib/aws-sdk-s3/plugins/accelerate.rb +87 -0
- data/lib/aws-sdk-s3/plugins/bucket_arn.rb +212 -0
- data/lib/aws-sdk-s3/plugins/bucket_dns.rb +91 -0
- data/lib/aws-sdk-s3/plugins/bucket_name_restrictions.rb +45 -0
- data/lib/aws-sdk-s3/plugins/dualstack.rb +74 -0
- data/lib/aws-sdk-s3/plugins/expect_100_continue.rb +28 -0
- data/lib/aws-sdk-s3/plugins/get_bucket_location_fix.rb +25 -0
- data/lib/aws-sdk-s3/plugins/http_200_errors.rb +55 -0
- data/lib/aws-sdk-s3/plugins/iad_regional_endpoint.rb +62 -0
- data/lib/aws-sdk-s3/plugins/location_constraint.rb +35 -0
- data/lib/aws-sdk-s3/plugins/md5s.rb +84 -0
- data/lib/aws-sdk-s3/plugins/redirects.rb +45 -0
- data/lib/aws-sdk-s3/plugins/s3_host_id.rb +30 -0
- data/lib/aws-sdk-s3/plugins/s3_signer.rb +222 -0
- data/lib/aws-sdk-s3/plugins/sse_cpk.rb +70 -0
- data/lib/aws-sdk-s3/plugins/streaming_retry.rb +118 -0
- data/lib/aws-sdk-s3/plugins/url_encoded_keys.rb +97 -0
- data/lib/aws-sdk-s3/presigned_post.rb +686 -0
- data/lib/aws-sdk-s3/presigner.rb +253 -0
- data/lib/aws-sdk-s3/resource.rb +117 -0
- data/lib/aws-sdk-s3/types.rb +13154 -0
- data/lib/aws-sdk-s3/waiters.rb +243 -0
- metadata +184 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
require 'base64'
|
|
2
|
+
|
|
3
|
+
module Aws
|
|
4
|
+
module S3
|
|
5
|
+
module EncryptionV2
|
|
6
|
+
# @api private
|
|
7
|
+
class DecryptHandler < Seahorse::Client::Handler
|
|
8
|
+
|
|
9
|
+
V1_ENVELOPE_KEYS = %w(
|
|
10
|
+
x-amz-key
|
|
11
|
+
x-amz-iv
|
|
12
|
+
x-amz-matdesc
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
V2_ENVELOPE_KEYS = %w(
|
|
16
|
+
x-amz-key-v2
|
|
17
|
+
x-amz-iv
|
|
18
|
+
x-amz-cek-alg
|
|
19
|
+
x-amz-wrap-alg
|
|
20
|
+
x-amz-matdesc
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
V2_OPTIONAL_KEYS = %w(x-amz-tag-len)
|
|
24
|
+
|
|
25
|
+
POSSIBLE_ENVELOPE_KEYS = (V1_ENVELOPE_KEYS +
|
|
26
|
+
V2_ENVELOPE_KEYS + V2_OPTIONAL_KEYS).uniq
|
|
27
|
+
|
|
28
|
+
POSSIBLE_WRAPPING_FORMATS = %w(
|
|
29
|
+
AES/GCM
|
|
30
|
+
kms
|
|
31
|
+
kms+context
|
|
32
|
+
RSA-OAEP-SHA1
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
POSSIBLE_ENCRYPTION_FORMATS = %w(
|
|
36
|
+
AES/GCM/NoPadding
|
|
37
|
+
AES/CBC/PKCS5Padding
|
|
38
|
+
AES/CBC/PKCS7Padding
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def call(context)
|
|
42
|
+
attach_http_event_listeners(context)
|
|
43
|
+
apply_cse_user_agent(context)
|
|
44
|
+
@handler.call(context)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def attach_http_event_listeners(context)
|
|
50
|
+
|
|
51
|
+
context.http_response.on_headers(200) do
|
|
52
|
+
cipher, envelope = decryption_cipher(context)
|
|
53
|
+
decrypter = body_contains_auth_tag?(envelope) ?
|
|
54
|
+
authenticated_decrypter(context, cipher, envelope) :
|
|
55
|
+
IODecrypter.new(cipher, context.http_response.body)
|
|
56
|
+
context.http_response.body = decrypter
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
context.http_response.on_success(200) do
|
|
60
|
+
decrypter = context.http_response.body
|
|
61
|
+
decrypter.finalize
|
|
62
|
+
decrypter.io.rewind if decrypter.io.respond_to?(:rewind)
|
|
63
|
+
context.http_response.body = decrypter.io
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
context.http_response.on_error do
|
|
67
|
+
if context.http_response.body.respond_to?(:io)
|
|
68
|
+
context.http_response.body = context.http_response.body.io
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def decryption_cipher(context)
|
|
74
|
+
if envelope = get_encryption_envelope(context)
|
|
75
|
+
cipher = context[:encryption][:cipher_provider]
|
|
76
|
+
.decryption_cipher(
|
|
77
|
+
envelope,
|
|
78
|
+
kms_encryption_context: context[:encryption][:kms_encryption_context]
|
|
79
|
+
)
|
|
80
|
+
[cipher, envelope]
|
|
81
|
+
else
|
|
82
|
+
raise Errors::DecryptionError, "unable to locate encryption envelope"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def get_encryption_envelope(context)
|
|
87
|
+
if context[:encryption][:envelope_location] == :metadata
|
|
88
|
+
envelope_from_metadata(context) || envelope_from_instr_file(context)
|
|
89
|
+
else
|
|
90
|
+
envelope_from_instr_file(context) || envelope_from_metadata(context)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def envelope_from_metadata(context)
|
|
95
|
+
possible_envelope = {}
|
|
96
|
+
POSSIBLE_ENVELOPE_KEYS.each do |suffix|
|
|
97
|
+
if value = context.http_response.headers["x-amz-meta-#{suffix}"]
|
|
98
|
+
possible_envelope[suffix] = value
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
extract_envelope(possible_envelope)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def envelope_from_instr_file(context)
|
|
105
|
+
suffix = context[:encryption][:instruction_file_suffix]
|
|
106
|
+
possible_envelope = Json.load(context.client.get_object(
|
|
107
|
+
bucket: context.params[:bucket],
|
|
108
|
+
key: context.params[:key] + suffix
|
|
109
|
+
).body.read)
|
|
110
|
+
extract_envelope(possible_envelope)
|
|
111
|
+
rescue S3::Errors::ServiceError, Json::ParseError
|
|
112
|
+
nil
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def extract_envelope(hash)
|
|
116
|
+
return nil unless hash
|
|
117
|
+
return v1_envelope(hash) if hash.key?('x-amz-key')
|
|
118
|
+
return v2_envelope(hash) if hash.key?('x-amz-key-v2')
|
|
119
|
+
if hash.keys.any? { |key| key.match(/^x-amz-key-(.+)$/) }
|
|
120
|
+
msg = "unsupported envelope encryption version #{$1}"
|
|
121
|
+
raise Errors::DecryptionError, msg
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def v1_envelope(envelope)
|
|
126
|
+
envelope
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def v2_envelope(envelope)
|
|
130
|
+
unless POSSIBLE_ENCRYPTION_FORMATS.include? envelope['x-amz-cek-alg']
|
|
131
|
+
alg = envelope['x-amz-cek-alg'].inspect
|
|
132
|
+
msg = "unsupported content encrypting key (cek) format: #{alg}"
|
|
133
|
+
raise Errors::DecryptionError, msg
|
|
134
|
+
end
|
|
135
|
+
unless POSSIBLE_WRAPPING_FORMATS.include? envelope['x-amz-wrap-alg']
|
|
136
|
+
alg = envelope['x-amz-wrap-alg'].inspect
|
|
137
|
+
msg = "unsupported key wrapping algorithm: #{alg}"
|
|
138
|
+
raise Errors::DecryptionError, msg
|
|
139
|
+
end
|
|
140
|
+
unless (missing_keys = V2_ENVELOPE_KEYS - envelope.keys).empty?
|
|
141
|
+
msg = "incomplete v2 encryption envelope:\n"
|
|
142
|
+
msg += " missing: #{missing_keys.join(',')}\n"
|
|
143
|
+
raise Errors::DecryptionError, msg
|
|
144
|
+
end
|
|
145
|
+
envelope
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# When the x-amz-meta-x-amz-tag-len header is present, it indicates
|
|
149
|
+
# that the body of this object has a trailing auth tag. The header
|
|
150
|
+
# indicates the length of that tag.
|
|
151
|
+
#
|
|
152
|
+
# This method fetches the tag from the end of the object by
|
|
153
|
+
# making a GET Object w/range request. This auth tag is used
|
|
154
|
+
# to initialize the cipher, and the decrypter truncates the
|
|
155
|
+
# auth tag from the body when writing the final bytes.
|
|
156
|
+
def authenticated_decrypter(context, cipher, envelope)
|
|
157
|
+
if RUBY_VERSION.match(/1.9/)
|
|
158
|
+
raise "authenticated decryption not supported by OpenSSL in Ruby version ~> 1.9"
|
|
159
|
+
raise Aws::Errors::NonSupportedRubyVersionError, msg
|
|
160
|
+
end
|
|
161
|
+
http_resp = context.http_response
|
|
162
|
+
content_length = http_resp.headers['content-length'].to_i
|
|
163
|
+
auth_tag_length = envelope['x-amz-tag-len']
|
|
164
|
+
auth_tag_length = auth_tag_length.to_i / 8
|
|
165
|
+
|
|
166
|
+
auth_tag = context.client.get_object(
|
|
167
|
+
bucket: context.params[:bucket],
|
|
168
|
+
key: context.params[:key],
|
|
169
|
+
range: "bytes=-#{auth_tag_length}"
|
|
170
|
+
).body.read
|
|
171
|
+
|
|
172
|
+
cipher.auth_tag = auth_tag
|
|
173
|
+
cipher.auth_data = ''
|
|
174
|
+
|
|
175
|
+
# The encrypted object contains both the cipher text
|
|
176
|
+
# plus a trailing auth tag.
|
|
177
|
+
IOAuthDecrypter.new(
|
|
178
|
+
io: http_resp.body,
|
|
179
|
+
encrypted_content_length: content_length - auth_tag_length,
|
|
180
|
+
cipher: cipher)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def body_contains_auth_tag?(envelope)
|
|
184
|
+
envelope.include? 'x-amz-tag-len'
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def apply_cse_user_agent(context)
|
|
188
|
+
if context.config.user_agent_suffix.nil?
|
|
189
|
+
context.config.user_agent_suffix = 'CSE_V2'
|
|
190
|
+
elsif !context.config.user_agent_suffix.include? 'CSE_V2'
|
|
191
|
+
context.config.user_agent_suffix += ' CSE_V2'
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
require 'base64'
|
|
2
|
+
|
|
3
|
+
module Aws
|
|
4
|
+
module S3
|
|
5
|
+
module EncryptionV2
|
|
6
|
+
# @api private
|
|
7
|
+
class DefaultCipherProvider
|
|
8
|
+
|
|
9
|
+
def initialize(options = {})
|
|
10
|
+
@key_provider = options[:key_provider]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @return [Array<Hash,Cipher>] Creates an returns a new encryption
|
|
14
|
+
# envelope and encryption cipher.
|
|
15
|
+
def encryption_cipher(options = {})
|
|
16
|
+
cipher = Utils.aes_encryption_cipher(:GCM)
|
|
17
|
+
cek_alg = 'AES/GCM/NoPadding'
|
|
18
|
+
if @key_provider.encryption_materials.key.is_a? OpenSSL::PKey::RSA
|
|
19
|
+
wrap_alg = 'RSA-OAEP-SHA1'
|
|
20
|
+
enc_key = encode64(encrypt_rsa(envelope_key(cipher), cek_alg))
|
|
21
|
+
else
|
|
22
|
+
wrap_alg = 'AES/GCM'
|
|
23
|
+
enc_key = encode64(encrypt_aes_gcm(envelope_key(cipher), cek_alg))
|
|
24
|
+
end
|
|
25
|
+
envelope = {
|
|
26
|
+
'x-amz-key-v2' => enc_key,
|
|
27
|
+
'x-amz-cek-alg' => cek_alg,
|
|
28
|
+
'x-amz-tag-len' => 16 * 8,
|
|
29
|
+
'x-amz-wrap-alg' => wrap_alg,
|
|
30
|
+
'x-amz-iv' => encode64(envelope_iv(cipher)),
|
|
31
|
+
'x-amz-matdesc' => materials_description,
|
|
32
|
+
}
|
|
33
|
+
cipher.auth_data = '' # auth_data must be set after key and iv
|
|
34
|
+
[envelope, cipher]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @return [Cipher] Given an encryption envelope, returns a
|
|
38
|
+
# decryption cipher.
|
|
39
|
+
def decryption_cipher(envelope, options = {})
|
|
40
|
+
master_key = @key_provider.key_for(envelope['x-amz-matdesc'])
|
|
41
|
+
if envelope.key? 'x-amz-key'
|
|
42
|
+
# Support for decryption of legacy objects
|
|
43
|
+
key = Utils.decrypt(master_key, decode64(envelope['x-amz-key']))
|
|
44
|
+
iv = decode64(envelope['x-amz-iv'])
|
|
45
|
+
Utils.aes_decryption_cipher(:CBC, key, iv)
|
|
46
|
+
else
|
|
47
|
+
if envelope['x-amz-cek-alg'] != 'AES/GCM/NoPadding'
|
|
48
|
+
raise ArgumentError, 'Unsupported cek-alg: ' \
|
|
49
|
+
"#{envelope['x-amz-cek-alg']}"
|
|
50
|
+
end
|
|
51
|
+
key =
|
|
52
|
+
case envelope['x-amz-wrap-alg']
|
|
53
|
+
when 'AES/GCM'
|
|
54
|
+
Utils.decrypt_aes_gcm(master_key,
|
|
55
|
+
decode64(envelope['x-amz-key-v2']),
|
|
56
|
+
envelope['x-amz-cek-alg'])
|
|
57
|
+
when 'RSA-OAEP-SHA1'
|
|
58
|
+
key, cek_alg = Utils.decrypt_rsa(master_key, decode64(envelope['x-amz-key-v2']))
|
|
59
|
+
raise Errors::DecryptionError unless cek_alg == envelope['x-amz-cek-alg']
|
|
60
|
+
key
|
|
61
|
+
else
|
|
62
|
+
raise ArgumentError, 'Unsupported wrap-alg: ' \
|
|
63
|
+
"#{envelope['x-amz-wrap-alg']}"
|
|
64
|
+
end
|
|
65
|
+
iv = decode64(envelope['x-amz-iv'])
|
|
66
|
+
Utils.aes_decryption_cipher(:GCM, key, iv)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def envelope_key(cipher)
|
|
73
|
+
cipher.key = cipher.random_key
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def envelope_iv(cipher)
|
|
77
|
+
cipher.iv = cipher.random_iv
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def encrypt_aes_gcm(data, auth_data)
|
|
81
|
+
Utils.encrypt_aes_gcm(@key_provider.encryption_materials.key, data, auth_data)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def encrypt_rsa(data, auth_data)
|
|
85
|
+
Utils.encrypt_rsa(@key_provider.encryption_materials.key, data, auth_data)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def materials_description
|
|
89
|
+
@key_provider.encryption_materials.description
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def encode64(str)
|
|
93
|
+
Base64.encode64(str).split("\n") * ''
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def decode64(str)
|
|
97
|
+
Base64.decode64(str)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Aws
|
|
2
|
+
module S3
|
|
3
|
+
module EncryptionV2
|
|
4
|
+
|
|
5
|
+
# The default key provider is constructed with a single key
|
|
6
|
+
# that is used for both encryption and decryption, ignoring
|
|
7
|
+
# the possible per-object envelope encryption materials description.
|
|
8
|
+
# @api private
|
|
9
|
+
class DefaultKeyProvider
|
|
10
|
+
|
|
11
|
+
include KeyProvider
|
|
12
|
+
|
|
13
|
+
# @option options [required, OpenSSL::PKey::RSA, String] :encryption_key
|
|
14
|
+
# The master key to use for encrypting objects.
|
|
15
|
+
# @option options [String<JSON>] :materials_description ('{}')
|
|
16
|
+
# A description of the encryption key.
|
|
17
|
+
def initialize(options = {})
|
|
18
|
+
@encryption_materials = Materials.new(
|
|
19
|
+
key: options[:encryption_key],
|
|
20
|
+
description: options[:materials_description] || '{}'
|
|
21
|
+
)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @return [Materials]
|
|
25
|
+
def encryption_materials
|
|
26
|
+
@encryption_materials
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @param [String<JSON>] materials_description
|
|
30
|
+
# @return Returns the key given in the constructor.
|
|
31
|
+
def key_for(materials_description)
|
|
32
|
+
@encryption_materials.key
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require 'base64'
|
|
2
|
+
|
|
3
|
+
module Aws
|
|
4
|
+
module S3
|
|
5
|
+
module EncryptionV2
|
|
6
|
+
# @api private
|
|
7
|
+
class EncryptHandler < Seahorse::Client::Handler
|
|
8
|
+
|
|
9
|
+
def call(context)
|
|
10
|
+
if RUBY_VERSION.match(/1.9/)
|
|
11
|
+
raise "authenticated encryption not supported by OpenSSL in Ruby version ~> 1.9"
|
|
12
|
+
raise Aws::Errors::NonSupportedRubyVersionError, msg
|
|
13
|
+
end
|
|
14
|
+
envelope, cipher = context[:encryption][:cipher_provider]
|
|
15
|
+
.encryption_cipher(
|
|
16
|
+
kms_encryption_context: context[:encryption][:kms_encryption_context]
|
|
17
|
+
)
|
|
18
|
+
context[:encryption][:cipher] = cipher
|
|
19
|
+
apply_encryption_envelope(context, envelope)
|
|
20
|
+
apply_encryption_cipher(context, cipher)
|
|
21
|
+
apply_cse_user_agent(context)
|
|
22
|
+
@handler.call(context)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def apply_encryption_envelope(context, envelope)
|
|
28
|
+
if context[:encryption][:envelope_location] == :instruction_file
|
|
29
|
+
suffix = context[:encryption][:instruction_file_suffix]
|
|
30
|
+
context.client.put_object(
|
|
31
|
+
bucket: context.params[:bucket],
|
|
32
|
+
key: context.params[:key] + suffix,
|
|
33
|
+
body: Json.dump(envelope)
|
|
34
|
+
)
|
|
35
|
+
else # :metadata
|
|
36
|
+
context.params[:metadata] ||= {}
|
|
37
|
+
context.params[:metadata].update(envelope)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def apply_encryption_cipher(context, cipher)
|
|
42
|
+
io = context.params[:body] || ''
|
|
43
|
+
io = StringIO.new(io) if io.is_a? String
|
|
44
|
+
context.params[:body] = IOEncrypter.new(cipher, io)
|
|
45
|
+
context.params[:metadata] ||= {}
|
|
46
|
+
context.params[:metadata]['x-amz-unencrypted-content-length'] = io.size
|
|
47
|
+
if context.params.delete(:content_md5)
|
|
48
|
+
raise ArgumentError, 'content_md5 is not supported'
|
|
49
|
+
end
|
|
50
|
+
context.http_response.on_headers do
|
|
51
|
+
context.params[:body].close
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def apply_cse_user_agent(context)
|
|
56
|
+
if context.config.user_agent_suffix.nil?
|
|
57
|
+
context.config.user_agent_suffix = 'CSE_V2'
|
|
58
|
+
elsif !context.config.user_agent_suffix.include? 'CSE_V2'
|
|
59
|
+
context.config.user_agent_suffix += ' CSE_V2'
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module Aws
|
|
2
|
+
module S3
|
|
3
|
+
module EncryptionV2
|
|
4
|
+
# @api private
|
|
5
|
+
class IOAuthDecrypter
|
|
6
|
+
|
|
7
|
+
# @option options [required, IO#write] :io
|
|
8
|
+
# An IO-like object that responds to {#write}.
|
|
9
|
+
# @option options [required, Integer] :encrypted_content_length
|
|
10
|
+
# The number of bytes to decrypt from the `:io` object.
|
|
11
|
+
# This should be the total size of `:io` minus the length of
|
|
12
|
+
# the cipher auth tag.
|
|
13
|
+
# @option options [required, OpenSSL::Cipher] :cipher An initialized
|
|
14
|
+
# cipher that can be used to decrypt the bytes as they are
|
|
15
|
+
# written to the `:io` object. The cipher should already have
|
|
16
|
+
# its `#auth_tag` set.
|
|
17
|
+
def initialize(options = {})
|
|
18
|
+
@decrypter = IODecrypter.new(options[:cipher], options[:io])
|
|
19
|
+
@max_bytes = options[:encrypted_content_length]
|
|
20
|
+
@bytes_written = 0
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def write(chunk)
|
|
24
|
+
chunk = truncate_chunk(chunk)
|
|
25
|
+
if chunk.bytesize > 0
|
|
26
|
+
@bytes_written += chunk.bytesize
|
|
27
|
+
@decrypter.write(chunk)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def finalize
|
|
32
|
+
@decrypter.finalize
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def io
|
|
36
|
+
@decrypter.io
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def truncate_chunk(chunk)
|
|
42
|
+
if chunk.bytesize + @bytes_written <= @max_bytes
|
|
43
|
+
chunk
|
|
44
|
+
elsif @bytes_written < @max_bytes
|
|
45
|
+
chunk[0..(@max_bytes - @bytes_written - 1)]
|
|
46
|
+
else
|
|
47
|
+
# If the tag was sent over after the full body has been read,
|
|
48
|
+
# we don't want to accidentally append it.
|
|
49
|
+
""
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|