aws-sdk-s3 1.10.0 → 1.208.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 (153) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1517 -0
  3. data/LICENSE.txt +202 -0
  4. data/VERSION +1 -0
  5. data/lib/aws-sdk-s3/access_grants_credentials.rb +57 -0
  6. data/lib/aws-sdk-s3/access_grants_credentials_provider.rb +250 -0
  7. data/lib/aws-sdk-s3/bucket.rb +1062 -99
  8. data/lib/aws-sdk-s3/bucket_acl.rb +67 -17
  9. data/lib/aws-sdk-s3/bucket_cors.rb +80 -17
  10. data/lib/aws-sdk-s3/bucket_lifecycle.rb +71 -19
  11. data/lib/aws-sdk-s3/bucket_lifecycle_configuration.rb +126 -20
  12. data/lib/aws-sdk-s3/bucket_logging.rb +68 -18
  13. data/lib/aws-sdk-s3/bucket_notification.rb +56 -20
  14. data/lib/aws-sdk-s3/bucket_policy.rb +108 -17
  15. data/lib/aws-sdk-s3/bucket_region_cache.rb +11 -5
  16. data/lib/aws-sdk-s3/bucket_request_payment.rb +60 -15
  17. data/lib/aws-sdk-s3/bucket_tagging.rb +71 -17
  18. data/lib/aws-sdk-s3/bucket_versioning.rb +166 -17
  19. data/lib/aws-sdk-s3/bucket_website.rb +78 -17
  20. data/lib/aws-sdk-s3/client.rb +20068 -3879
  21. data/lib/aws-sdk-s3/client_api.rb +1957 -209
  22. data/lib/aws-sdk-s3/customizations/bucket.rb +57 -38
  23. data/lib/aws-sdk-s3/customizations/errors.rb +40 -0
  24. data/lib/aws-sdk-s3/customizations/multipart_upload.rb +2 -0
  25. data/lib/aws-sdk-s3/customizations/object.rb +338 -68
  26. data/lib/aws-sdk-s3/customizations/object_summary.rb +17 -0
  27. data/lib/aws-sdk-s3/customizations/object_version.rb +13 -0
  28. data/lib/aws-sdk-s3/customizations/types/list_object_versions_output.rb +2 -0
  29. data/lib/aws-sdk-s3/customizations/types/permanent_redirect.rb +26 -0
  30. data/lib/aws-sdk-s3/customizations.rb +30 -27
  31. data/lib/aws-sdk-s3/default_executor.rb +103 -0
  32. data/lib/aws-sdk-s3/encryption/client.rb +29 -8
  33. data/lib/aws-sdk-s3/encryption/decrypt_handler.rb +71 -29
  34. data/lib/aws-sdk-s3/encryption/default_cipher_provider.rb +45 -5
  35. data/lib/aws-sdk-s3/encryption/default_key_provider.rb +2 -0
  36. data/lib/aws-sdk-s3/encryption/encrypt_handler.rb +15 -2
  37. data/lib/aws-sdk-s3/encryption/errors.rb +2 -0
  38. data/lib/aws-sdk-s3/encryption/io_auth_decrypter.rb +11 -3
  39. data/lib/aws-sdk-s3/encryption/io_decrypter.rb +11 -3
  40. data/lib/aws-sdk-s3/encryption/io_encrypter.rb +2 -0
  41. data/lib/aws-sdk-s3/encryption/key_provider.rb +2 -0
  42. data/lib/aws-sdk-s3/encryption/kms_cipher_provider.rb +48 -11
  43. data/lib/aws-sdk-s3/encryption/materials.rb +8 -6
  44. data/lib/aws-sdk-s3/encryption/utils.rb +25 -0
  45. data/lib/aws-sdk-s3/encryption.rb +4 -0
  46. data/lib/aws-sdk-s3/encryptionV2/client.rb +645 -0
  47. data/lib/aws-sdk-s3/encryptionV2/decrypt_handler.rb +68 -0
  48. data/lib/aws-sdk-s3/encryptionV2/decryption.rb +205 -0
  49. data/lib/aws-sdk-s3/encryptionV2/default_cipher_provider.rb +187 -0
  50. data/lib/aws-sdk-s3/encryptionV2/default_key_provider.rb +40 -0
  51. data/lib/aws-sdk-s3/encryptionV2/encrypt_handler.rb +67 -0
  52. data/lib/aws-sdk-s3/encryptionV2/errors.rb +37 -0
  53. data/lib/aws-sdk-s3/encryptionV2/io_auth_decrypter.rb +58 -0
  54. data/lib/aws-sdk-s3/encryptionV2/io_decrypter.rb +37 -0
  55. data/lib/aws-sdk-s3/encryptionV2/io_encrypter.rb +75 -0
  56. data/lib/aws-sdk-s3/encryptionV2/key_provider.rb +31 -0
  57. data/lib/aws-sdk-s3/encryptionV2/kms_cipher_provider.rb +181 -0
  58. data/lib/aws-sdk-s3/encryptionV2/materials.rb +60 -0
  59. data/lib/aws-sdk-s3/encryptionV2/utils.rb +108 -0
  60. data/lib/aws-sdk-s3/encryptionV3/client.rb +885 -0
  61. data/lib/aws-sdk-s3/encryptionV3/decrypt_handler.rb +98 -0
  62. data/lib/aws-sdk-s3/encryptionV3/decryption.rb +244 -0
  63. data/lib/aws-sdk-s3/encryptionV3/default_cipher_provider.rb +159 -0
  64. data/lib/aws-sdk-s3/encryptionV3/default_key_provider.rb +35 -0
  65. data/lib/aws-sdk-s3/encryptionV3/encrypt_handler.rb +98 -0
  66. data/lib/aws-sdk-s3/encryptionV3/errors.rb +47 -0
  67. data/lib/aws-sdk-s3/encryptionV3/io_auth_decrypter.rb +60 -0
  68. data/lib/aws-sdk-s3/encryptionV3/io_decrypter.rb +35 -0
  69. data/lib/aws-sdk-s3/encryptionV3/io_encrypter.rb +84 -0
  70. data/lib/aws-sdk-s3/encryptionV3/key_provider.rb +28 -0
  71. data/lib/aws-sdk-s3/encryptionV3/kms_cipher_provider.rb +159 -0
  72. data/lib/aws-sdk-s3/encryptionV3/materials.rb +58 -0
  73. data/lib/aws-sdk-s3/encryptionV3/utils.rb +321 -0
  74. data/lib/aws-sdk-s3/encryption_v2.rb +24 -0
  75. data/lib/aws-sdk-s3/encryption_v3.rb +24 -0
  76. data/lib/aws-sdk-s3/endpoint_parameters.rb +181 -0
  77. data/lib/aws-sdk-s3/endpoint_provider.rb +886 -0
  78. data/lib/aws-sdk-s3/endpoints.rb +1544 -0
  79. data/lib/aws-sdk-s3/errors.rb +181 -1
  80. data/lib/aws-sdk-s3/event_streams.rb +69 -0
  81. data/lib/aws-sdk-s3/express_credentials.rb +55 -0
  82. data/lib/aws-sdk-s3/express_credentials_provider.rb +59 -0
  83. data/lib/aws-sdk-s3/file_downloader.rb +261 -82
  84. data/lib/aws-sdk-s3/file_part.rb +16 -13
  85. data/lib/aws-sdk-s3/file_uploader.rb +37 -22
  86. data/lib/aws-sdk-s3/legacy_signer.rb +19 -26
  87. data/lib/aws-sdk-s3/multipart_download_error.rb +8 -0
  88. data/lib/aws-sdk-s3/multipart_file_uploader.rb +142 -80
  89. data/lib/aws-sdk-s3/multipart_stream_uploader.rb +191 -0
  90. data/lib/aws-sdk-s3/multipart_upload.rb +342 -31
  91. data/lib/aws-sdk-s3/multipart_upload_error.rb +5 -4
  92. data/lib/aws-sdk-s3/multipart_upload_part.rb +387 -47
  93. data/lib/aws-sdk-s3/object.rb +2733 -204
  94. data/lib/aws-sdk-s3/object_acl.rb +112 -25
  95. data/lib/aws-sdk-s3/object_copier.rb +9 -5
  96. data/lib/aws-sdk-s3/object_multipart_copier.rb +50 -23
  97. data/lib/aws-sdk-s3/object_summary.rb +2265 -181
  98. data/lib/aws-sdk-s3/object_version.rb +542 -74
  99. data/lib/aws-sdk-s3/plugins/accelerate.rb +17 -64
  100. data/lib/aws-sdk-s3/plugins/access_grants.rb +178 -0
  101. data/lib/aws-sdk-s3/plugins/arn.rb +70 -0
  102. data/lib/aws-sdk-s3/plugins/bucket_dns.rb +7 -43
  103. data/lib/aws-sdk-s3/plugins/bucket_name_restrictions.rb +20 -3
  104. data/lib/aws-sdk-s3/plugins/checksum_algorithm.rb +31 -0
  105. data/lib/aws-sdk-s3/plugins/dualstack.rb +7 -50
  106. data/lib/aws-sdk-s3/plugins/endpoints.rb +86 -0
  107. data/lib/aws-sdk-s3/plugins/expect_100_continue.rb +5 -4
  108. data/lib/aws-sdk-s3/plugins/express_session_auth.rb +88 -0
  109. data/lib/aws-sdk-s3/plugins/get_bucket_location_fix.rb +3 -1
  110. data/lib/aws-sdk-s3/plugins/http_200_errors.rb +62 -17
  111. data/lib/aws-sdk-s3/plugins/iad_regional_endpoint.rb +44 -0
  112. data/lib/aws-sdk-s3/plugins/location_constraint.rb +5 -1
  113. data/lib/aws-sdk-s3/plugins/md5s.rb +14 -67
  114. data/lib/aws-sdk-s3/plugins/redirects.rb +5 -1
  115. data/lib/aws-sdk-s3/plugins/s3_host_id.rb +2 -0
  116. data/lib/aws-sdk-s3/plugins/s3_signer.rb +67 -93
  117. data/lib/aws-sdk-s3/plugins/sse_cpk.rb +3 -1
  118. data/lib/aws-sdk-s3/plugins/streaming_retry.rb +137 -0
  119. data/lib/aws-sdk-s3/plugins/url_encoded_keys.rb +4 -1
  120. data/lib/aws-sdk-s3/presigned_post.rb +160 -99
  121. data/lib/aws-sdk-s3/presigner.rb +178 -81
  122. data/lib/aws-sdk-s3/resource.rb +164 -15
  123. data/lib/aws-sdk-s3/transfer_manager.rb +303 -0
  124. data/lib/aws-sdk-s3/types.rb +15981 -4168
  125. data/lib/aws-sdk-s3/waiters.rb +67 -1
  126. data/lib/aws-sdk-s3.rb +46 -31
  127. data/sig/bucket.rbs +231 -0
  128. data/sig/bucket_acl.rbs +78 -0
  129. data/sig/bucket_cors.rbs +69 -0
  130. data/sig/bucket_lifecycle.rbs +88 -0
  131. data/sig/bucket_lifecycle_configuration.rbs +115 -0
  132. data/sig/bucket_logging.rbs +76 -0
  133. data/sig/bucket_notification.rbs +114 -0
  134. data/sig/bucket_policy.rbs +59 -0
  135. data/sig/bucket_request_payment.rbs +54 -0
  136. data/sig/bucket_tagging.rbs +65 -0
  137. data/sig/bucket_versioning.rbs +77 -0
  138. data/sig/bucket_website.rbs +93 -0
  139. data/sig/client.rbs +2612 -0
  140. data/sig/customizations/bucket.rbs +19 -0
  141. data/sig/customizations/object.rbs +38 -0
  142. data/sig/customizations/object_summary.rbs +35 -0
  143. data/sig/errors.rbs +44 -0
  144. data/sig/multipart_upload.rbs +120 -0
  145. data/sig/multipart_upload_part.rbs +109 -0
  146. data/sig/object.rbs +464 -0
  147. data/sig/object_acl.rbs +86 -0
  148. data/sig/object_summary.rbs +347 -0
  149. data/sig/object_version.rbs +143 -0
  150. data/sig/resource.rbs +141 -0
  151. data/sig/types.rbs +2899 -0
  152. data/sig/waiters.rbs +95 -0
  153. metadata +97 -14
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aws
4
+ module S3
5
+ module EncryptionV2
6
+ # @api private
7
+ class IODecrypter
8
+
9
+ # @param [OpenSSL::Cipher] cipher
10
+ # @param [IO#write] io An IO-like object that responds to `#write`.
11
+ def initialize(cipher, io)
12
+ @cipher = cipher
13
+ # Ensure that IO is reset between retries
14
+ @io = io.tap { |io| io.truncate(0) if io.respond_to?(:truncate) }
15
+ @cipher_buffer = String.new
16
+ end
17
+
18
+ # @return [#write]
19
+ attr_reader :io
20
+
21
+ def write(chunk)
22
+ # decrypt and write
23
+ if @cipher.method(:update).arity == 1
24
+ @io.write(@cipher.update(chunk))
25
+ else
26
+ @io.write(@cipher.update(chunk, @cipher_buffer))
27
+ end
28
+ end
29
+
30
+ def finalize
31
+ @io.write(@cipher.final)
32
+ end
33
+
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,75 @@
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
+ ##= ../specification/s3-encryption/encryption.md#alg-aes-256-gcm-iv12-tag16-no-kdf
48
+ ##% The client MUST append the GCM auth tag to the ciphertext if the underlying crypto provider does not do so automatically.
49
+ if plain_text.empty?
50
+ StringIO.new(cipher.final + cipher.auth_tag)
51
+ else
52
+ StringIO.new(cipher.update(plain_text) + cipher.final + cipher.auth_tag)
53
+ end
54
+ end
55
+
56
+ def encrypt_to_tempfile(cipher, io)
57
+ encrypted = Tempfile.new(self.object_id.to_s)
58
+ encrypted.binmode
59
+ while chunk = io.read(ONE_MEGABYTE, read_buffer ||= String.new)
60
+ if cipher.method(:update).arity == 1
61
+ encrypted.write(cipher.update(chunk))
62
+ else
63
+ encrypted.write(cipher.update(chunk, cipher_buffer ||= String.new))
64
+ end
65
+ end
66
+ encrypted.write(cipher.final)
67
+ encrypted.write(cipher.auth_tag)
68
+ encrypted.rewind
69
+ encrypted
70
+ end
71
+
72
+ end
73
+ end
74
+ end
75
+ 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,181 @@
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
+ ##= ../specification/s3-encryption/data-format/content-metadata.md#algorithm-suite-and-message-format-version-compatibility
37
+ ##% Objects encrypted with ALG_AES_256_GCM_IV12_TAG16_NO_KDF MUST use the V2 message format version only.
38
+ envelope = {
39
+ 'x-amz-key-v2' => encode64(key_data.ciphertext_blob),
40
+ 'x-amz-iv' => encode64(cipher.iv = cipher.random_iv),
41
+ 'x-amz-cek-alg' => @content_encryption_schema,
42
+ 'x-amz-tag-len' => (AES_GCM_TAG_LEN_BYTES * 8).to_s,
43
+ 'x-amz-wrap-alg' => @key_wrap_schema,
44
+ 'x-amz-matdesc' => Json.dump(encryption_context)
45
+ }
46
+ cipher.auth_data = '' # auth_data must be set after key and iv
47
+ [envelope, cipher]
48
+ end
49
+
50
+ # @return [Cipher] Given an encryption envelope, returns a
51
+ # decryption cipher.
52
+ def decryption_cipher(envelope, options = {})
53
+ encryption_context = Json.load(envelope['x-amz-matdesc'])
54
+ cek_alg = envelope['x-amz-cek-alg']
55
+
56
+ case envelope['x-amz-wrap-alg']
57
+ when 'kms'
58
+ ##= ../specification/s3-encryption/client.md#enable-legacy-wrapping-algorithms
59
+ ##% The S3EC MUST support the option to enable or disable legacy wrapping algorithms.
60
+ unless options[:security_profile] == :v2_and_legacy
61
+ ##= ../specification/s3-encryption/client.md#enable-legacy-wrapping-algorithms
62
+ ##% When disabled, the S3EC MUST NOT decrypt objects encrypted using legacy wrapping algorithms; it MUST throw an exception when attempting to decrypt an object encrypted with a legacy wrapping algorithm.
63
+ raise Errors::LegacyDecryptionError
64
+ end
65
+ ##= ../specification/s3-encryption/client.md#enable-legacy-wrapping-algorithms
66
+ ##% When enabled, the S3EC MUST be able to decrypt objects encrypted with all supported wrapping algorithms (both legacy and fully supported).
67
+ when 'kms+context'
68
+ if cek_alg != encryption_context['aws:x-amz-cek-alg']
69
+ raise Errors::CEKAlgMismatchError
70
+ end
71
+
72
+ if encryption_context != build_encryption_context(cek_alg, options)
73
+ raise Errors::DecryptionError, 'Value of encryption context from'\
74
+ ' envelope does not match the provided encryption context'
75
+ end
76
+ when 'AES/GCM'
77
+ raise ArgumentError, 'Key mismatch - Client is configured' \
78
+ ' with a KMS key and the x-amz-wrap-alg is AES/GCM.'
79
+ when 'RSA-OAEP-SHA1'
80
+ raise ArgumentError, 'Key mismatch - Client is configured' \
81
+ ' with a KMS key and the x-amz-wrap-alg is RSA-OAEP-SHA1.'
82
+ else
83
+ raise ArgumentError, 'Unsupported wrap-alg: ' \
84
+ "#{envelope['x-amz-wrap-alg']}"
85
+ end
86
+
87
+ any_cmk_mode = false || options[:kms_allow_decrypt_with_any_cmk]
88
+ decrypt_options = {
89
+ ciphertext_blob: decode64(envelope['x-amz-key-v2']),
90
+ encryption_context: encryption_context
91
+ }
92
+ unless any_cmk_mode
93
+ decrypt_options[:key_id] = @kms_key_id
94
+ end
95
+
96
+ key = Aws::Plugins::UserAgent.metric('S3_CRYPTO_V2') do
97
+ @kms_client.decrypt(decrypt_options).plaintext
98
+ end
99
+ iv = decode64(envelope['x-amz-iv'])
100
+ block_mode =
101
+ case cek_alg
102
+ when 'AES/CBC/PKCS5Padding'
103
+ :CBC
104
+ when 'AES/CBC/PKCS7Padding'
105
+ :CBC
106
+ when 'AES/GCM/NoPadding'
107
+ :GCM
108
+ else
109
+ type = envelope['x-amz-cek-alg'].inspect
110
+ msg = "unsupported content encrypting key (cek) format: #{type}"
111
+ raise Errors::DecryptionError, msg
112
+ end
113
+ Utils.aes_decryption_cipher(block_mode, key, iv)
114
+ end
115
+
116
+ private
117
+
118
+ def validate_key_wrap(key_wrap_schema)
119
+ case key_wrap_schema
120
+ when :kms_context then 'kms+context'
121
+ else
122
+ raise ArgumentError, "Unsupported key_wrap_schema: #{key_wrap_schema}"
123
+ end
124
+ end
125
+
126
+ def validate_cek(content_encryption_schema)
127
+ case content_encryption_schema
128
+ when :aes_gcm_no_padding
129
+ "AES/GCM/NoPadding"
130
+ else
131
+ raise ArgumentError, "Unsupported content_encryption_schema: #{content_encryption_schema}"
132
+ end
133
+ end
134
+
135
+ def validate_kms_key(kms_key_id)
136
+ if kms_key_id.nil? || kms_key_id.length.zero?
137
+ raise ArgumentError, 'KMS CMK ID was not specified. ' \
138
+ 'Please specify a CMK ID, ' \
139
+ 'or set kms_key_id: :kms_allow_decrypt_with_any_cmk to use ' \
140
+ 'any valid CMK from the object.'
141
+ end
142
+
143
+ if kms_key_id.is_a?(Symbol) && kms_key_id != :kms_allow_decrypt_with_any_cmk
144
+ raise ArgumentError, 'kms_key_id must be a valid KMS CMK or be ' \
145
+ 'set to :kms_allow_decrypt_with_any_cmk'
146
+ end
147
+ kms_key_id
148
+ end
149
+
150
+ def build_encryption_context(cek_alg, options = {})
151
+ kms_context = (options[:kms_encryption_context] || {})
152
+ .each_with_object({}) { |(k, v), h| h[k.to_s] = v }
153
+ if kms_context.include? 'aws:x-amz-cek-alg'
154
+ raise ArgumentError, 'Conflict in reserved KMS Encryption Context ' \
155
+ 'key aws:x-amz-cek-alg. This value is reserved for the S3 ' \
156
+ 'Encryption Client and cannot be set by the user.'
157
+ end
158
+ {
159
+ 'aws:x-amz-cek-alg' => cek_alg
160
+ }.merge(kms_context)
161
+ end
162
+
163
+ def encode64(str)
164
+ Base64.encode64(str).split("\n") * ""
165
+ end
166
+
167
+ def decode64(str)
168
+ Base64.decode64(str)
169
+ end
170
+
171
+ def validate_key_for_encryption
172
+ if @kms_key_id == :kms_allow_decrypt_with_any_cmk
173
+ raise ArgumentError, 'Unable to encrypt/write objects with '\
174
+ 'kms_key_id = :kms_allow_decrypt_with_any_cmk. Provide ' \
175
+ 'a valid kms_key_id on client construction.'
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
181
+ 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,108 @@
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
+ ##= ../specification/s3-encryption/encryption.md#alg-aes-256-gcm-iv12-tag16-no-kdf
84
+ ##% The client MUST initialize the cipher,
85
+ ##% or call an AES-GCM encryption API, with the plaintext data key, the generated IV,
86
+ ##% and the tag length defined in the Algorithm Suite
87
+ ##% when encrypting with ALG_AES_256_GCM_IV12_TAG16_NO_KDF.
88
+ cipher = key ?
89
+ OpenSSL::Cipher.new("aes-#{cipher_size(key)}-#{block_mode.downcase}") :
90
+ OpenSSL::Cipher.new("aes-256-#{block_mode.downcase}")
91
+ cipher.send(mode) # encrypt or decrypt
92
+ cipher.key = key if key
93
+ cipher.iv = iv if iv
94
+ cipher
95
+ end
96
+
97
+ # @param [String] key
98
+ # @return [Integer]
99
+ # @raise ArgumentError
100
+ def cipher_size(key)
101
+ key.bytesize * 8
102
+ end
103
+
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end