aws-sdk-s3 1.67.0 → 1.70.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/lib/aws-sdk-s3.rb +3 -1
  3. data/lib/aws-sdk-s3/bucket.rb +2 -0
  4. data/lib/aws-sdk-s3/bucket_acl.rb +2 -0
  5. data/lib/aws-sdk-s3/bucket_cors.rb +2 -0
  6. data/lib/aws-sdk-s3/bucket_lifecycle.rb +2 -0
  7. data/lib/aws-sdk-s3/bucket_lifecycle_configuration.rb +2 -0
  8. data/lib/aws-sdk-s3/bucket_logging.rb +2 -0
  9. data/lib/aws-sdk-s3/bucket_notification.rb +2 -0
  10. data/lib/aws-sdk-s3/bucket_policy.rb +2 -0
  11. data/lib/aws-sdk-s3/bucket_region_cache.rb +2 -0
  12. data/lib/aws-sdk-s3/bucket_request_payment.rb +2 -0
  13. data/lib/aws-sdk-s3/bucket_tagging.rb +2 -0
  14. data/lib/aws-sdk-s3/bucket_versioning.rb +2 -0
  15. data/lib/aws-sdk-s3/bucket_website.rb +2 -0
  16. data/lib/aws-sdk-s3/client.rb +11 -6
  17. data/lib/aws-sdk-s3/client_api.rb +22 -0
  18. data/lib/aws-sdk-s3/customizations.rb +3 -0
  19. data/lib/aws-sdk-s3/customizations/bucket.rb +2 -0
  20. data/lib/aws-sdk-s3/customizations/multipart_upload.rb +2 -0
  21. data/lib/aws-sdk-s3/customizations/object.rb +2 -0
  22. data/lib/aws-sdk-s3/customizations/object_summary.rb +2 -0
  23. data/lib/aws-sdk-s3/customizations/types/list_object_versions_output.rb +2 -0
  24. data/lib/aws-sdk-s3/encryption.rb +2 -0
  25. data/lib/aws-sdk-s3/encryption/client.rb +3 -1
  26. data/lib/aws-sdk-s3/encryption/decrypt_handler.rb +11 -0
  27. data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +2 -0
  28. data/lib/aws-sdk-s3/encryption/default_key_provider.rb +2 -0
  29. data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +11 -0
  30. data/lib/aws-sdk-s3/encryption/errors.rb +2 -0
  31. data/lib/aws-sdk-s3/encryption/io_auth_decrypter.rb +2 -0
  32. data/lib/aws-sdk-s3/encryption/io_decrypter.rb +8 -1
  33. data/lib/aws-sdk-s3/encryption/io_encrypter.rb +2 -0
  34. data/lib/aws-sdk-s3/encryption/key_provider.rb +2 -0
  35. data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +2 -0
  36. data/lib/aws-sdk-s3/encryption/materials.rb +2 -0
  37. data/lib/aws-sdk-s3/encryption/utils.rb +2 -0
  38. data/lib/aws-sdk-s3/encryptionV2/client.rb +388 -0
  39. data/lib/aws-sdk-s3/encryptionV2/decrypt_handler.rb +198 -0
  40. data/lib/aws-sdk-s3/encryptionV2/default_cipher_provider.rb +103 -0
  41. data/lib/aws-sdk-s3/encryptionV2/default_key_provider.rb +38 -0
  42. data/lib/aws-sdk-s3/encryptionV2/encrypt_handler.rb +66 -0
  43. data/lib/aws-sdk-s3/encryptionV2/errors.rb +13 -0
  44. data/lib/aws-sdk-s3/encryptionV2/io_auth_decrypter.rb +56 -0
  45. data/lib/aws-sdk-s3/encryptionV2/io_decrypter.rb +30 -0
  46. data/lib/aws-sdk-s3/encryptionV2/io_encrypter.rb +67 -0
  47. data/lib/aws-sdk-s3/encryptionV2/key_provider.rb +29 -0
  48. data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +99 -0
  49. data/lib/aws-sdk-s3/encryptionV2/materials.rb +58 -0
  50. data/lib/aws-sdk-s3/encryptionV2/utils.rb +116 -0
  51. data/lib/aws-sdk-s3/encryption_v2.rb +20 -0
  52. data/lib/aws-sdk-s3/errors.rb +2 -0
  53. data/lib/aws-sdk-s3/event_streams.rb +7 -0
  54. data/lib/aws-sdk-s3/file_downloader.rb +2 -0
  55. data/lib/aws-sdk-s3/file_part.rb +2 -0
  56. data/lib/aws-sdk-s3/file_uploader.rb +2 -0
  57. data/lib/aws-sdk-s3/legacy_signer.rb +2 -0
  58. data/lib/aws-sdk-s3/multipart_file_uploader.rb +2 -0
  59. data/lib/aws-sdk-s3/multipart_stream_uploader.rb +2 -0
  60. data/lib/aws-sdk-s3/multipart_upload.rb +2 -0
  61. data/lib/aws-sdk-s3/multipart_upload_error.rb +2 -0
  62. data/lib/aws-sdk-s3/multipart_upload_part.rb +2 -0
  63. data/lib/aws-sdk-s3/object.rb +2 -0
  64. data/lib/aws-sdk-s3/object_acl.rb +2 -0
  65. data/lib/aws-sdk-s3/object_copier.rb +2 -0
  66. data/lib/aws-sdk-s3/object_multipart_copier.rb +2 -0
  67. data/lib/aws-sdk-s3/object_summary.rb +2 -0
  68. data/lib/aws-sdk-s3/object_version.rb +2 -0
  69. data/lib/aws-sdk-s3/plugins/accelerate.rb +2 -0
  70. data/lib/aws-sdk-s3/plugins/bucket_arn.rb +2 -0
  71. data/lib/aws-sdk-s3/plugins/bucket_dns.rb +2 -0
  72. data/lib/aws-sdk-s3/plugins/bucket_name_restrictions.rb +2 -0
  73. data/lib/aws-sdk-s3/plugins/dualstack.rb +2 -0
  74. data/lib/aws-sdk-s3/plugins/expect_100_continue.rb +2 -0
  75. data/lib/aws-sdk-s3/plugins/get_bucket_location_fix.rb +2 -0
  76. data/lib/aws-sdk-s3/plugins/http_200_errors.rb +2 -0
  77. data/lib/aws-sdk-s3/plugins/iad_regional_endpoint.rb +2 -0
  78. data/lib/aws-sdk-s3/plugins/location_constraint.rb +2 -0
  79. data/lib/aws-sdk-s3/plugins/md5s.rb +23 -24
  80. data/lib/aws-sdk-s3/plugins/redirects.rb +2 -0
  81. data/lib/aws-sdk-s3/plugins/s3_host_id.rb +2 -0
  82. data/lib/aws-sdk-s3/plugins/s3_signer.rb +2 -0
  83. data/lib/aws-sdk-s3/plugins/sse_cpk.rb +2 -0
  84. data/lib/aws-sdk-s3/plugins/url_encoded_keys.rb +2 -0
  85. data/lib/aws-sdk-s3/presigned_post.rb +2 -0
  86. data/lib/aws-sdk-s3/presigner.rb +2 -0
  87. data/lib/aws-sdk-s3/resource.rb +2 -0
  88. data/lib/aws-sdk-s3/types.rb +265 -0
  89. data/lib/aws-sdk-s3/waiters.rb +2 -0
  90. metadata +18 -4
@@ -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,13 @@
1
+ module Aws
2
+ module S3
3
+ module EncryptionV2
4
+ module Errors
5
+
6
+ class DecryptionError < RuntimeError; end
7
+
8
+ class EncryptionError < RuntimeError; end
9
+
10
+ end
11
+ end
12
+ end
13
+ 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