aws-sdk-s3 1.68.1 → 1.93.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +748 -0
  3. data/LICENSE.txt +202 -0
  4. data/VERSION +1 -0
  5. data/lib/aws-sdk-s3.rb +6 -3
  6. data/lib/aws-sdk-s3/arn/access_point_arn.rb +64 -0
  7. data/lib/aws-sdk-s3/arn/object_lambda_arn.rb +64 -0
  8. data/lib/aws-sdk-s3/arn/outpost_access_point_arn.rb +73 -0
  9. data/lib/aws-sdk-s3/bucket.rb +71 -10
  10. data/lib/aws-sdk-s3/bucket_acl.rb +11 -1
  11. data/lib/aws-sdk-s3/bucket_cors.rb +20 -4
  12. data/lib/aws-sdk-s3/bucket_lifecycle.rb +17 -2
  13. data/lib/aws-sdk-s3/bucket_lifecycle_configuration.rb +15 -2
  14. data/lib/aws-sdk-s3/bucket_logging.rb +11 -1
  15. data/lib/aws-sdk-s3/bucket_notification.rb +8 -1
  16. data/lib/aws-sdk-s3/bucket_policy.rb +18 -2
  17. data/lib/aws-sdk-s3/bucket_region_cache.rb +2 -0
  18. data/lib/aws-sdk-s3/bucket_request_payment.rb +14 -5
  19. data/lib/aws-sdk-s3/bucket_tagging.rb +18 -2
  20. data/lib/aws-sdk-s3/bucket_versioning.rb +27 -1
  21. data/lib/aws-sdk-s3/bucket_website.rb +18 -2
  22. data/lib/aws-sdk-s3/client.rb +3425 -1183
  23. data/lib/aws-sdk-s3/client_api.rb +361 -21
  24. data/lib/aws-sdk-s3/customizations.rb +4 -1
  25. data/lib/aws-sdk-s3/customizations/bucket.rb +17 -7
  26. data/lib/aws-sdk-s3/customizations/multipart_upload.rb +2 -0
  27. data/lib/aws-sdk-s3/customizations/object.rb +27 -7
  28. data/lib/aws-sdk-s3/customizations/object_summary.rb +2 -0
  29. data/lib/aws-sdk-s3/customizations/types/list_object_versions_output.rb +2 -0
  30. data/lib/aws-sdk-s3/encryption.rb +4 -0
  31. data/lib/aws-sdk-s3/encryption/client.rb +13 -0
  32. data/lib/aws-sdk-s3/encryption/decrypt_handler.rb +72 -26
  33. data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +43 -5
  34. data/lib/aws-sdk-s3/encryption/default_key_provider.rb +2 -0
  35. data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +13 -2
  36. data/lib/aws-sdk-s3/encryption/errors.rb +2 -0
  37. data/lib/aws-sdk-s3/encryption/io_auth_decrypter.rb +2 -0
  38. data/lib/aws-sdk-s3/encryption/io_decrypter.rb +11 -3
  39. data/lib/aws-sdk-s3/encryption/io_encrypter.rb +2 -0
  40. data/lib/aws-sdk-s3/encryption/key_provider.rb +2 -0
  41. data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +34 -3
  42. data/lib/aws-sdk-s3/encryption/materials.rb +2 -0
  43. data/lib/aws-sdk-s3/encryption/utils.rb +25 -0
  44. data/lib/aws-sdk-s3/encryptionV2/client.rb +566 -0
  45. data/lib/aws-sdk-s3/encryptionV2/decrypt_handler.rb +226 -0
  46. data/lib/aws-sdk-s3/encryptionV2/default_cipher_provider.rb +170 -0
  47. data/lib/aws-sdk-s3/encryptionV2/default_key_provider.rb +40 -0
  48. data/lib/aws-sdk-s3/encryptionV2/encrypt_handler.rb +69 -0
  49. data/lib/aws-sdk-s3/encryptionV2/errors.rb +37 -0
  50. data/lib/aws-sdk-s3/encryptionV2/io_auth_decrypter.rb +58 -0
  51. data/lib/aws-sdk-s3/encryptionV2/io_decrypter.rb +37 -0
  52. data/lib/aws-sdk-s3/encryptionV2/io_encrypter.rb +73 -0
  53. data/lib/aws-sdk-s3/encryptionV2/key_provider.rb +31 -0
  54. data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +169 -0
  55. data/lib/aws-sdk-s3/encryptionV2/materials.rb +60 -0
  56. data/lib/aws-sdk-s3/encryptionV2/utils.rb +103 -0
  57. data/lib/aws-sdk-s3/encryption_v2.rb +23 -0
  58. data/lib/aws-sdk-s3/errors.rb +24 -1
  59. data/lib/aws-sdk-s3/event_streams.rb +3 -1
  60. data/lib/aws-sdk-s3/file_downloader.rb +2 -0
  61. data/lib/aws-sdk-s3/file_part.rb +2 -0
  62. data/lib/aws-sdk-s3/file_uploader.rb +16 -3
  63. data/lib/aws-sdk-s3/legacy_signer.rb +17 -25
  64. data/lib/aws-sdk-s3/multipart_file_uploader.rb +39 -2
  65. data/lib/aws-sdk-s3/multipart_stream_uploader.rb +2 -0
  66. data/lib/aws-sdk-s3/multipart_upload.rb +18 -1
  67. data/lib/aws-sdk-s3/multipart_upload_error.rb +2 -0
  68. data/lib/aws-sdk-s3/multipart_upload_part.rb +67 -8
  69. data/lib/aws-sdk-s3/object.rb +310 -34
  70. data/lib/aws-sdk-s3/object_acl.rb +19 -1
  71. data/lib/aws-sdk-s3/object_copier.rb +2 -0
  72. data/lib/aws-sdk-s3/object_multipart_copier.rb +2 -0
  73. data/lib/aws-sdk-s3/object_summary.rb +208 -25
  74. data/lib/aws-sdk-s3/object_version.rb +35 -9
  75. data/lib/aws-sdk-s3/plugins/accelerate.rb +32 -38
  76. data/lib/aws-sdk-s3/plugins/arn.rb +228 -0
  77. data/lib/aws-sdk-s3/plugins/bucket_dns.rb +2 -2
  78. data/lib/aws-sdk-s3/plugins/bucket_name_restrictions.rb +3 -1
  79. data/lib/aws-sdk-s3/plugins/dualstack.rb +15 -4
  80. data/lib/aws-sdk-s3/plugins/expect_100_continue.rb +4 -1
  81. data/lib/aws-sdk-s3/plugins/get_bucket_location_fix.rb +2 -0
  82. data/lib/aws-sdk-s3/plugins/http_200_errors.rb +4 -1
  83. data/lib/aws-sdk-s3/plugins/iad_regional_endpoint.rb +14 -7
  84. data/lib/aws-sdk-s3/plugins/location_constraint.rb +2 -0
  85. data/lib/aws-sdk-s3/plugins/md5s.rb +3 -1
  86. data/lib/aws-sdk-s3/plugins/object_lambda_endpoint.rb +25 -0
  87. data/lib/aws-sdk-s3/plugins/redirects.rb +2 -0
  88. data/lib/aws-sdk-s3/plugins/s3_host_id.rb +2 -0
  89. data/lib/aws-sdk-s3/plugins/s3_signer.rb +36 -14
  90. data/lib/aws-sdk-s3/plugins/sse_cpk.rb +3 -1
  91. data/lib/aws-sdk-s3/plugins/streaming_retry.rb +118 -0
  92. data/lib/aws-sdk-s3/plugins/url_encoded_keys.rb +2 -0
  93. data/lib/aws-sdk-s3/presigned_post.rb +64 -28
  94. data/lib/aws-sdk-s3/presigner.rb +20 -16
  95. data/lib/aws-sdk-s3/resource.rb +4 -2
  96. data/lib/aws-sdk-s3/types.rb +3300 -542
  97. data/lib/aws-sdk-s3/waiters.rb +3 -1
  98. metadata +30 -8
  99. data/lib/aws-sdk-s3/plugins/bucket_arn.rb +0 -210
@@ -0,0 +1,226 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+
5
+ module Aws
6
+ module S3
7
+ module EncryptionV2
8
+ # @api private
9
+ class DecryptHandler < Seahorse::Client::Handler
10
+ @@warned_response_target_proc = false
11
+
12
+ V1_ENVELOPE_KEYS = %w(
13
+ x-amz-key
14
+ x-amz-iv
15
+ x-amz-matdesc
16
+ )
17
+
18
+ V2_ENVELOPE_KEYS = %w(
19
+ x-amz-key-v2
20
+ x-amz-iv
21
+ x-amz-cek-alg
22
+ x-amz-wrap-alg
23
+ x-amz-matdesc
24
+ )
25
+
26
+ V2_OPTIONAL_KEYS = %w(x-amz-tag-len)
27
+
28
+ POSSIBLE_ENVELOPE_KEYS = (V1_ENVELOPE_KEYS +
29
+ V2_ENVELOPE_KEYS + V2_OPTIONAL_KEYS).uniq
30
+
31
+ POSSIBLE_WRAPPING_FORMATS = %w(
32
+ AES/GCM
33
+ kms
34
+ kms+context
35
+ RSA-OAEP-SHA1
36
+ )
37
+
38
+ POSSIBLE_ENCRYPTION_FORMATS = %w(
39
+ AES/GCM/NoPadding
40
+ AES/CBC/PKCS5Padding
41
+ AES/CBC/PKCS7Padding
42
+ )
43
+
44
+ AUTH_REQUIRED_CEK_ALGS = %w(AES/GCM/NoPadding)
45
+
46
+ def call(context)
47
+ attach_http_event_listeners(context)
48
+ apply_cse_user_agent(context)
49
+
50
+ if context[:response_target].is_a?(Proc) && !@@warned_response_target_proc
51
+ @@warned_response_target_proc = true
52
+ warn(':response_target is a Proc, or a block was provided. ' \
53
+ 'Read the entire object to the ' \
54
+ 'end before you start using the decrypted data. This is to ' \
55
+ 'verify that the object has not been modified since it ' \
56
+ 'was encrypted.')
57
+
58
+ end
59
+
60
+ @handler.call(context)
61
+ end
62
+
63
+ private
64
+
65
+ def attach_http_event_listeners(context)
66
+
67
+ context.http_response.on_headers(200) do
68
+ cipher, envelope = decryption_cipher(context)
69
+ decrypter = body_contains_auth_tag?(envelope) ?
70
+ authenticated_decrypter(context, cipher, envelope) :
71
+ IODecrypter.new(cipher, context.http_response.body)
72
+ context.http_response.body = decrypter
73
+ end
74
+
75
+ context.http_response.on_success(200) do
76
+ decrypter = context.http_response.body
77
+ decrypter.finalize
78
+ decrypter.io.rewind if decrypter.io.respond_to?(:rewind)
79
+ context.http_response.body = decrypter.io
80
+ end
81
+
82
+ context.http_response.on_error do
83
+ if context.http_response.body.respond_to?(:io)
84
+ context.http_response.body = context.http_response.body.io
85
+ end
86
+ end
87
+ end
88
+
89
+ def decryption_cipher(context)
90
+ if (envelope = get_encryption_envelope(context))
91
+ cipher = context[:encryption][:cipher_provider]
92
+ .decryption_cipher(
93
+ envelope,
94
+ context[:encryption]
95
+ )
96
+ [cipher, envelope]
97
+ else
98
+ raise Errors::DecryptionError, "unable to locate encryption envelope"
99
+ end
100
+ end
101
+
102
+ def get_encryption_envelope(context)
103
+ if context[:encryption][:envelope_location] == :metadata
104
+ envelope_from_metadata(context) || envelope_from_instr_file(context)
105
+ else
106
+ envelope_from_instr_file(context) || envelope_from_metadata(context)
107
+ end
108
+ end
109
+
110
+ def envelope_from_metadata(context)
111
+ possible_envelope = {}
112
+ POSSIBLE_ENVELOPE_KEYS.each do |suffix|
113
+ if value = context.http_response.headers["x-amz-meta-#{suffix}"]
114
+ possible_envelope[suffix] = value
115
+ end
116
+ end
117
+ extract_envelope(possible_envelope)
118
+ end
119
+
120
+ def envelope_from_instr_file(context)
121
+ suffix = context[:encryption][:instruction_file_suffix]
122
+ possible_envelope = Json.load(context.client.get_object(
123
+ bucket: context.params[:bucket],
124
+ key: context.params[:key] + suffix
125
+ ).body.read)
126
+ extract_envelope(possible_envelope)
127
+ rescue S3::Errors::ServiceError, Json::ParseError
128
+ nil
129
+ end
130
+
131
+ def extract_envelope(hash)
132
+ return nil unless hash
133
+ return v1_envelope(hash) if hash.key?('x-amz-key')
134
+ return v2_envelope(hash) if hash.key?('x-amz-key-v2')
135
+ if hash.keys.any? { |key| key.match(/^x-amz-key-(.+)$/) }
136
+ msg = "unsupported envelope encryption version #{$1}"
137
+ raise Errors::DecryptionError, msg
138
+ end
139
+ end
140
+
141
+ def v1_envelope(envelope)
142
+ envelope
143
+ end
144
+
145
+ def v2_envelope(envelope)
146
+ unless POSSIBLE_ENCRYPTION_FORMATS.include? envelope['x-amz-cek-alg']
147
+ alg = envelope['x-amz-cek-alg'].inspect
148
+ msg = "unsupported content encrypting key (cek) format: #{alg}"
149
+ raise Errors::DecryptionError, msg
150
+ end
151
+ unless POSSIBLE_WRAPPING_FORMATS.include? envelope['x-amz-wrap-alg']
152
+ alg = envelope['x-amz-wrap-alg'].inspect
153
+ msg = "unsupported key wrapping algorithm: #{alg}"
154
+ raise Errors::DecryptionError, msg
155
+ end
156
+ unless (missing_keys = V2_ENVELOPE_KEYS - envelope.keys).empty?
157
+ msg = "incomplete v2 encryption envelope:\n"
158
+ msg += " missing: #{missing_keys.join(',')}\n"
159
+ raise Errors::DecryptionError, msg
160
+ end
161
+ envelope
162
+ end
163
+
164
+ # This method fetches the tag from the end of the object by
165
+ # making a GET Object w/range request. This auth tag is used
166
+ # to initialize the cipher, and the decrypter truncates the
167
+ # auth tag from the body when writing the final bytes.
168
+ def authenticated_decrypter(context, cipher, envelope)
169
+ if RUBY_VERSION.match(/1.9/)
170
+ raise "authenticated decryption not supported by OpenSSL in Ruby version ~> 1.9"
171
+ raise Aws::Errors::NonSupportedRubyVersionError, msg
172
+ end
173
+ http_resp = context.http_response
174
+ content_length = http_resp.headers['content-length'].to_i
175
+ auth_tag_length = auth_tag_length(envelope)
176
+
177
+ auth_tag = context.client.get_object(
178
+ bucket: context.params[:bucket],
179
+ key: context.params[:key],
180
+ range: "bytes=-#{auth_tag_length}"
181
+ ).body.read
182
+
183
+ cipher.auth_tag = auth_tag
184
+ cipher.auth_data = ''
185
+
186
+ # The encrypted object contains both the cipher text
187
+ # plus a trailing auth tag.
188
+ IOAuthDecrypter.new(
189
+ io: http_resp.body,
190
+ encrypted_content_length: content_length - auth_tag_length,
191
+ cipher: cipher)
192
+ end
193
+
194
+ def body_contains_auth_tag?(envelope)
195
+ AUTH_REQUIRED_CEK_ALGS.include?(envelope['x-amz-cek-alg'])
196
+ end
197
+
198
+ # Determine the auth tag length from the algorithm
199
+ # Validate it against the value provided in the x-amz-tag-len
200
+ # Return the tag length in bytes
201
+ def auth_tag_length(envelope)
202
+ tag_length =
203
+ case envelope['x-amz-cek-alg']
204
+ when 'AES/GCM/NoPadding' then AES_GCM_TAG_LEN_BYTES
205
+ else
206
+ raise ArgumentError, 'Unsupported cek-alg: ' \
207
+ "#{envelope['x-amz-cek-alg']}"
208
+ end
209
+ if (tag_length * 8) != envelope['x-amz-tag-len'].to_i
210
+ raise Errors::DecryptionError, 'x-amz-tag-len does not match expected'
211
+ end
212
+ tag_length
213
+ end
214
+
215
+ def apply_cse_user_agent(context)
216
+ if context.config.user_agent_suffix.nil?
217
+ context.config.user_agent_suffix = EC_USER_AGENT
218
+ elsif !context.config.user_agent_suffix.include? EC_USER_AGENT
219
+ context.config.user_agent_suffix += " #{EC_USER_AGENT}"
220
+ end
221
+ end
222
+
223
+ end
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,170 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+
5
+ module Aws
6
+ module S3
7
+ module EncryptionV2
8
+ # @api private
9
+ class DefaultCipherProvider
10
+
11
+ def initialize(options = {})
12
+ @key_provider = options[:key_provider]
13
+ @key_wrap_schema = validate_key_wrap(
14
+ options[:key_wrap_schema],
15
+ @key_provider.encryption_materials.key
16
+ )
17
+ @content_encryption_schema = validate_cek(
18
+ options[:content_encryption_schema]
19
+ )
20
+ end
21
+
22
+ # @return [Array<Hash,Cipher>] Creates an returns a new encryption
23
+ # envelope and encryption cipher.
24
+ def encryption_cipher(options = {})
25
+ validate_options(options)
26
+ cipher = Utils.aes_encryption_cipher(:GCM)
27
+ if @key_provider.encryption_materials.key.is_a? OpenSSL::PKey::RSA
28
+ enc_key = encode64(
29
+ encrypt_rsa(envelope_key(cipher), @content_encryption_schema)
30
+ )
31
+ else
32
+ enc_key = encode64(
33
+ encrypt_aes_gcm(envelope_key(cipher), @content_encryption_schema)
34
+ )
35
+ end
36
+ envelope = {
37
+ 'x-amz-key-v2' => enc_key,
38
+ 'x-amz-cek-alg' => @content_encryption_schema,
39
+ 'x-amz-tag-len' => (AES_GCM_TAG_LEN_BYTES * 8).to_s,
40
+ 'x-amz-wrap-alg' => @key_wrap_schema,
41
+ 'x-amz-iv' => encode64(envelope_iv(cipher)),
42
+ 'x-amz-matdesc' => materials_description
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
+ validate_options(options)
52
+ master_key = @key_provider.key_for(envelope['x-amz-matdesc'])
53
+ if envelope.key? 'x-amz-key'
54
+ unless options[:security_profile] == :v2_and_legacy
55
+ raise Errors::LegacyDecryptionError
56
+ end
57
+ # Support for decryption of legacy objects
58
+ key = Utils.decrypt(master_key, decode64(envelope['x-amz-key']))
59
+ iv = decode64(envelope['x-amz-iv'])
60
+ Utils.aes_decryption_cipher(:CBC, key, iv)
61
+ else
62
+ if envelope['x-amz-cek-alg'] != 'AES/GCM/NoPadding'
63
+ raise ArgumentError, 'Unsupported cek-alg: ' \
64
+ "#{envelope['x-amz-cek-alg']}"
65
+ end
66
+ key =
67
+ case envelope['x-amz-wrap-alg']
68
+ when 'AES/GCM'
69
+ if master_key.is_a? OpenSSL::PKey::RSA
70
+ raise ArgumentError, 'Key mismatch - Client is configured' \
71
+ ' with an RSA key and the x-amz-wrap-alg is AES/GCM.'
72
+ end
73
+ Utils.decrypt_aes_gcm(master_key,
74
+ decode64(envelope['x-amz-key-v2']),
75
+ envelope['x-amz-cek-alg'])
76
+ when 'RSA-OAEP-SHA1'
77
+ unless master_key.is_a? OpenSSL::PKey::RSA
78
+ raise ArgumentError, 'Key mismatch - Client is configured' \
79
+ ' with an AES key and the x-amz-wrap-alg is RSA-OAEP-SHA1.'
80
+ end
81
+ key, cek_alg = Utils.decrypt_rsa(master_key, decode64(envelope['x-amz-key-v2']))
82
+ raise Errors::CEKAlgMismatchError unless cek_alg == envelope['x-amz-cek-alg']
83
+ key
84
+ when 'kms+context'
85
+ raise ArgumentError, 'Key mismatch - Client is configured' \
86
+ ' with a user provided key and the x-amz-wrap-alg is' \
87
+ ' kms+context. Please configure the client with the' \
88
+ ' required kms_key_id'
89
+ else
90
+ raise ArgumentError, 'Unsupported wrap-alg: ' \
91
+ "#{envelope['x-amz-wrap-alg']}"
92
+ end
93
+ iv = decode64(envelope['x-amz-iv'])
94
+ Utils.aes_decryption_cipher(:GCM, key, iv)
95
+ end
96
+ end
97
+
98
+ private
99
+
100
+ # Validate that the key_wrap_schema
101
+ # is valid, supported and matches the provided key.
102
+ # Returns the string version for the x-amz-key-wrap-alg
103
+ def validate_key_wrap(key_wrap_schema, key)
104
+ if key.is_a? OpenSSL::PKey::RSA
105
+ unless key_wrap_schema == :rsa_oaep_sha1
106
+ raise ArgumentError, ':key_wrap_schema must be set to :rsa_oaep_sha1 for RSA keys.'
107
+ end
108
+ else
109
+ unless key_wrap_schema == :aes_gcm
110
+ raise ArgumentError, ':key_wrap_schema must be set to :aes_gcm for AES keys.'
111
+ end
112
+ end
113
+
114
+ case key_wrap_schema
115
+ when :rsa_oaep_sha1 then 'RSA-OAEP-SHA1'
116
+ when :aes_gcm then 'AES/GCM'
117
+ when :kms_context
118
+ raise ArgumentError, 'A kms_key_id is required when using :kms_context.'
119
+ else
120
+ raise ArgumentError, "Unsupported key_wrap_schema: #{key_wrap_schema}"
121
+ end
122
+ end
123
+
124
+ def validate_cek(content_encryption_schema)
125
+ case content_encryption_schema
126
+ when :aes_gcm_no_padding
127
+ "AES/GCM/NoPadding"
128
+ else
129
+ raise ArgumentError, "Unsupported content_encryption_schema: #{content_encryption_schema}"
130
+ end
131
+ end
132
+
133
+ def envelope_key(cipher)
134
+ cipher.key = cipher.random_key
135
+ end
136
+
137
+ def envelope_iv(cipher)
138
+ cipher.iv = cipher.random_iv
139
+ end
140
+
141
+ def encrypt_aes_gcm(data, auth_data)
142
+ Utils.encrypt_aes_gcm(@key_provider.encryption_materials.key, data, auth_data)
143
+ end
144
+
145
+ def encrypt_rsa(data, auth_data)
146
+ Utils.encrypt_rsa(@key_provider.encryption_materials.key, data, auth_data)
147
+ end
148
+
149
+ def materials_description
150
+ @key_provider.encryption_materials.description
151
+ end
152
+
153
+ def encode64(str)
154
+ Base64.encode64(str).split("\n") * ''
155
+ end
156
+
157
+ def decode64(str)
158
+ Base64.decode64(str)
159
+ end
160
+
161
+ def validate_options(options)
162
+ if !options[:kms_encryption_context].nil?
163
+ raise ArgumentError, 'Cannot provide :kms_encryption_context ' \
164
+ 'with non KMS client.'
165
+ end
166
+ end
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ module S3
5
+ module EncryptionV2
6
+
7
+ # The default key provider is constructed with a single key
8
+ # that is used for both encryption and decryption, ignoring
9
+ # the possible per-object envelope encryption materials description.
10
+ # @api private
11
+ class DefaultKeyProvider
12
+
13
+ include KeyProvider
14
+
15
+ # @option options [required, OpenSSL::PKey::RSA, String] :encryption_key
16
+ # The master key to use for encrypting objects.
17
+ # @option options [String<JSON>] :materials_description ('{}')
18
+ # A description of the encryption key.
19
+ def initialize(options = {})
20
+ @encryption_materials = Materials.new(
21
+ key: options[:encryption_key],
22
+ description: options[:materials_description] || '{}'
23
+ )
24
+ end
25
+
26
+ # @return [Materials]
27
+ def encryption_materials
28
+ @encryption_materials
29
+ end
30
+
31
+ # @param [String<JSON>] materials_description
32
+ # @return Returns the key given in the constructor.
33
+ def key_for(materials_description)
34
+ @encryption_materials.key
35
+ end
36
+
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+
5
+ module Aws
6
+ module S3
7
+ module EncryptionV2
8
+ # @api private
9
+ class EncryptHandler < Seahorse::Client::Handler
10
+
11
+ def call(context)
12
+ if RUBY_VERSION.match(/1.9/)
13
+ raise "authenticated encryption not supported by OpenSSL in Ruby version ~> 1.9"
14
+ raise Aws::Errors::NonSupportedRubyVersionError, msg
15
+ end
16
+ envelope, cipher = context[:encryption][:cipher_provider]
17
+ .encryption_cipher(
18
+ kms_encryption_context: context[:encryption][:kms_encryption_context]
19
+ )
20
+ context[:encryption][:cipher] = cipher
21
+ apply_encryption_envelope(context, envelope)
22
+ apply_encryption_cipher(context, cipher)
23
+ apply_cse_user_agent(context)
24
+ @handler.call(context)
25
+ end
26
+
27
+ private
28
+
29
+ def apply_encryption_envelope(context, envelope)
30
+ if context[:encryption][:envelope_location] == :instruction_file
31
+ suffix = context[:encryption][:instruction_file_suffix]
32
+ context.client.put_object(
33
+ bucket: context.params[:bucket],
34
+ key: context.params[:key] + suffix,
35
+ body: Json.dump(envelope)
36
+ )
37
+ else # :metadata
38
+ context.params[:metadata] ||= {}
39
+ context.params[:metadata].update(envelope)
40
+ end
41
+ end
42
+
43
+ def apply_encryption_cipher(context, cipher)
44
+ io = context.params[:body] || ''
45
+ io = StringIO.new(io) if io.is_a? String
46
+ context.params[:body] = IOEncrypter.new(cipher, io)
47
+ context.params[:metadata] ||= {}
48
+ context.params[:metadata]['x-amz-unencrypted-content-length'] = io.size
49
+ if context.params.delete(:content_md5)
50
+ raise ArgumentError, 'Setting content_md5 on client side '\
51
+ 'encrypted objects is deprecated.'
52
+ end
53
+ context.http_response.on_headers do
54
+ context.params[:body].close
55
+ end
56
+ end
57
+
58
+ def apply_cse_user_agent(context)
59
+ if context.config.user_agent_suffix.nil?
60
+ context.config.user_agent_suffix = EC_USER_AGENT
61
+ elsif !context.config.user_agent_suffix.include? EC_USER_AGENT
62
+ context.config.user_agent_suffix += " #{EC_USER_AGENT}"
63
+ end
64
+ end
65
+
66
+ end
67
+ end
68
+ end
69
+ end