aws-sdk-s3 1.65.0 → 1.69.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.
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 +5 -3
  4. data/lib/aws-sdk-s3/bucket_acl.rb +2 -0
  5. data/lib/aws-sdk-s3/bucket_cors.rb +3 -1
  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 +6 -2
  16. data/lib/aws-sdk-s3/client.rb +530 -649
  17. data/lib/aws-sdk-s3/client_api.rb +40 -18
  18. data/lib/aws-sdk-s3/customizations.rb +3 -0
  19. data/lib/aws-sdk-s3/customizations/bucket.rb +4 -0
  20. data/lib/aws-sdk-s3/customizations/multipart_upload.rb +2 -0
  21. data/lib/aws-sdk-s3/customizations/object.rb +5 -0
  22. data/lib/aws-sdk-s3/customizations/object_summary.rb +5 -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 +378 -0
  39. data/lib/aws-sdk-s3/encryptionV2/decrypt_handler.rb +194 -0
  40. data/lib/aws-sdk-s3/encryptionV2/default_cipher_provider.rb +104 -0
  41. data/lib/aws-sdk-s3/encryptionV2/default_key_provider.rb +38 -0
  42. data/lib/aws-sdk-s3/encryptionV2/encrypt_handler.rb +63 -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 +84 -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 +3 -1
  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 +16 -4
  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 +14 -3
  68. data/lib/aws-sdk-s3/object_version.rb +17 -1
  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 +3 -4
  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 +3 -1
  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 +3 -1
  88. data/lib/aws-sdk-s3/types.rb +140 -37
  89. data/lib/aws-sdk-s3/waiters.rb +2 -0
  90. metadata +18 -4
@@ -0,0 +1,194 @@
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
+ [context[:encryption][:cipher_provider].decryption_cipher(envelope),
76
+ envelope]
77
+ else
78
+ raise Errors::DecryptionError, "unable to locate encryption envelope"
79
+ end
80
+ end
81
+
82
+ def get_encryption_envelope(context)
83
+ if context[:encryption][:envelope_location] == :metadata
84
+ envelope_from_metadata(context) || envelope_from_instr_file(context)
85
+ else
86
+ envelope_from_instr_file(context) || envelope_from_metadata(context)
87
+ end
88
+ end
89
+
90
+ def envelope_from_metadata(context)
91
+ possible_envelope = {}
92
+ POSSIBLE_ENVELOPE_KEYS.each do |suffix|
93
+ if value = context.http_response.headers["x-amz-meta-#{suffix}"]
94
+ possible_envelope[suffix] = value
95
+ end
96
+ end
97
+ extract_envelope(possible_envelope)
98
+ end
99
+
100
+ def envelope_from_instr_file(context)
101
+ suffix = context[:encryption][:instruction_file_suffix]
102
+ possible_envelope = Json.load(context.client.get_object(
103
+ bucket: context.params[:bucket],
104
+ key: context.params[:key] + suffix
105
+ ).body.read)
106
+ extract_envelope(possible_envelope)
107
+ rescue S3::Errors::ServiceError, Json::ParseError
108
+ nil
109
+ end
110
+
111
+ def extract_envelope(hash)
112
+ return nil unless hash
113
+ return v1_envelope(hash) if hash.key?('x-amz-key')
114
+ return v2_envelope(hash) if hash.key?('x-amz-key-v2')
115
+ if hash.keys.any? { |key| key.match(/^x-amz-key-(.+)$/) }
116
+ msg = "unsupported envelope encryption version #{$1}"
117
+ raise Errors::DecryptionError, msg
118
+ end
119
+ end
120
+
121
+ def v1_envelope(envelope)
122
+ envelope
123
+ end
124
+
125
+ def v2_envelope(envelope)
126
+ unless POSSIBLE_ENCRYPTION_FORMATS.include? envelope['x-amz-cek-alg']
127
+ alg = envelope['x-amz-cek-alg'].inspect
128
+ msg = "unsupported content encrypting key (cek) format: #{alg}"
129
+ raise Errors::DecryptionError, msg
130
+ end
131
+ unless POSSIBLE_WRAPPING_FORMATS.include? envelope['x-amz-wrap-alg']
132
+ alg = envelope['x-amz-wrap-alg'].inspect
133
+ msg = "unsupported key wrapping algorithm: #{alg}"
134
+ raise Errors::DecryptionError, msg
135
+ end
136
+ unless (missing_keys = V2_ENVELOPE_KEYS - envelope.keys).empty?
137
+ msg = "incomplete v2 encryption envelope:\n"
138
+ msg += " missing: #{missing_keys.join(',')}\n"
139
+ raise Errors::DecryptionError, msg
140
+ end
141
+ envelope
142
+ end
143
+
144
+ # When the x-amz-meta-x-amz-tag-len header is present, it indicates
145
+ # that the body of this object has a trailing auth tag. The header
146
+ # indicates the length of that tag.
147
+ #
148
+ # This method fetches the tag from the end of the object by
149
+ # making a GET Object w/range request. This auth tag is used
150
+ # to initialize the cipher, and the decrypter truncates the
151
+ # auth tag from the body when writing the final bytes.
152
+ def authenticated_decrypter(context, cipher, envelope)
153
+ if RUBY_VERSION.match(/1.9/)
154
+ raise "authenticated decryption not supported by OpenSSL in Ruby version ~> 1.9"
155
+ raise Aws::Errors::NonSupportedRubyVersionError, msg
156
+ end
157
+ http_resp = context.http_response
158
+ content_length = http_resp.headers['content-length'].to_i
159
+ auth_tag_length = envelope['x-amz-tag-len']
160
+ auth_tag_length = auth_tag_length.to_i / 8
161
+
162
+ auth_tag = context.client.get_object(
163
+ bucket: context.params[:bucket],
164
+ key: context.params[:key],
165
+ range: "bytes=-#{auth_tag_length}"
166
+ ).body.read
167
+
168
+ cipher.auth_tag = auth_tag
169
+ cipher.auth_data = ''
170
+
171
+ # The encrypted object contains both the cipher text
172
+ # plus a trailing auth tag.
173
+ IOAuthDecrypter.new(
174
+ io: http_resp.body,
175
+ encrypted_content_length: content_length - auth_tag_length,
176
+ cipher: cipher)
177
+ end
178
+
179
+ def body_contains_auth_tag?(envelope)
180
+ envelope.include? 'x-amz-tag-len'
181
+ end
182
+
183
+ def apply_cse_user_agent(context)
184
+ if context.config.user_agent_suffix.nil?
185
+ context.config.user_agent_suffix = 'CSE_V2'
186
+ elsif !context.config.user_agent_suffix.include? 'CSE_V2'
187
+ context.config.user_agent_suffix += ' CSE_V2'
188
+ end
189
+ end
190
+
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,104 @@
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
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)
40
+ if envelope.key? 'x-amz-key'
41
+ # Support for decryption of legacy objects
42
+ master_key = @key_provider.key_for(envelope['x-amz-matdesc'])
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
+ master_key = @key_provider.key_for(envelope['x-amz-matdesc'])
48
+ if envelope['x-amz-cek-alg'] != 'AES/GCM/NoPadding'
49
+ raise ArgumentError, 'Unsupported cek-alg: ' \
50
+ "#{envelope['x-amz-cek-alg']}"
51
+ end
52
+ key =
53
+ case envelope['x-amz-wrap-alg']
54
+ when 'AES/GCM'
55
+ Utils.decrypt_aes_gcm(master_key,
56
+ decode64(envelope['x-amz-key-v2']),
57
+ envelope['x-amz-cek-alg'])
58
+ when 'RSA-OAEP-SHA1'
59
+ key, cek_alg = Utils.decrypt_rsa(master_key, decode64(envelope['x-amz-key-v2']))
60
+ raise Errors::DecryptionError unless cek_alg == envelope['x-amz-cek-alg']
61
+ key
62
+ else
63
+ raise ArgumentError, 'Unsupported wrap-alg: ' \
64
+ "#{envelope['x-amz-wrap-alg']}"
65
+ end
66
+ iv = decode64(envelope['x-amz-iv'])
67
+ Utils.aes_decryption_cipher(:GCM, key, iv)
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def envelope_key(cipher)
74
+ cipher.key = cipher.random_key
75
+ end
76
+
77
+ def envelope_iv(cipher)
78
+ cipher.iv = cipher.random_iv
79
+ end
80
+
81
+ def encrypt_aes_gcm(data, auth_data)
82
+ Utils.encrypt_aes_gcm(@key_provider.encryption_materials.key, data, auth_data)
83
+ end
84
+
85
+ def encrypt_rsa(data, auth_data)
86
+ Utils.encrypt_rsa(@key_provider.encryption_materials.key, data, auth_data)
87
+ end
88
+
89
+ def materials_description
90
+ @key_provider.encryption_materials.description
91
+ end
92
+
93
+ def encode64(str)
94
+ Base64.encode64(str).split("\n") * ''
95
+ end
96
+
97
+ def decode64(str)
98
+ Base64.decode64(str)
99
+ end
100
+
101
+ end
102
+ end
103
+ end
104
+ 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,63 @@
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].encryption_cipher
15
+ context[:encryption][:cipher] = cipher
16
+ apply_encryption_envelope(context, envelope)
17
+ apply_encryption_cipher(context, cipher)
18
+ apply_cse_user_agent(context)
19
+ @handler.call(context)
20
+ end
21
+
22
+ private
23
+
24
+ def apply_encryption_envelope(context, envelope)
25
+ if context[:encryption][:envelope_location] == :instruction_file
26
+ suffix = context[:encryption][:instruction_file_suffix]
27
+ context.client.put_object(
28
+ bucket: context.params[:bucket],
29
+ key: context.params[:key] + suffix,
30
+ body: Json.dump(envelope)
31
+ )
32
+ else # :metadata
33
+ context.params[:metadata] ||= {}
34
+ context.params[:metadata].update(envelope)
35
+ end
36
+ end
37
+
38
+ def apply_encryption_cipher(context, cipher)
39
+ io = context.params[:body] || ''
40
+ io = StringIO.new(io) if io.is_a? String
41
+ context.params[:body] = IOEncrypter.new(cipher, io)
42
+ context.params[:metadata] ||= {}
43
+ context.params[:metadata]['x-amz-unencrypted-content-length'] = io.size
44
+ if context.params.delete(:content_md5)
45
+ raise ArgumentError, 'content_md5 is not supported'
46
+ end
47
+ context.http_response.on_headers do
48
+ context.params[:body].close
49
+ end
50
+ end
51
+
52
+ def apply_cse_user_agent(context)
53
+ if context.config.user_agent_suffix.nil?
54
+ context.config.user_agent_suffix = 'CSE_V2'
55
+ elsif !context.config.user_agent_suffix.include? 'CSE_V2'
56
+ context.config.user_agent_suffix += ' CSE_V2'
57
+ end
58
+ end
59
+
60
+ end
61
+ end
62
+ end
63
+ 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