aws-sdk-resources 2.11.632 → 3.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +5 -5
  2. data/lib/aws-sdk-resources.rb +1 -91
  3. metadata +14 -85
  4. data/lib/aws-sdk-resources/batch.rb +0 -143
  5. data/lib/aws-sdk-resources/builder.rb +0 -85
  6. data/lib/aws-sdk-resources/builder_sources.rb +0 -105
  7. data/lib/aws-sdk-resources/collection.rb +0 -107
  8. data/lib/aws-sdk-resources/definition.rb +0 -331
  9. data/lib/aws-sdk-resources/documenter.rb +0 -70
  10. data/lib/aws-sdk-resources/documenter/base_operation_documenter.rb +0 -279
  11. data/lib/aws-sdk-resources/documenter/data_operation_documenter.rb +0 -25
  12. data/lib/aws-sdk-resources/documenter/has_many_operation_documenter.rb +0 -69
  13. data/lib/aws-sdk-resources/documenter/has_operation_documenter.rb +0 -66
  14. data/lib/aws-sdk-resources/documenter/operation_documenter.rb +0 -20
  15. data/lib/aws-sdk-resources/documenter/resource_operation_documenter.rb +0 -53
  16. data/lib/aws-sdk-resources/documenter/waiter_operation_documenter.rb +0 -77
  17. data/lib/aws-sdk-resources/errors.rb +0 -15
  18. data/lib/aws-sdk-resources/operation_methods.rb +0 -83
  19. data/lib/aws-sdk-resources/operations.rb +0 -280
  20. data/lib/aws-sdk-resources/options.rb +0 -17
  21. data/lib/aws-sdk-resources/request.rb +0 -39
  22. data/lib/aws-sdk-resources/request_params.rb +0 -140
  23. data/lib/aws-sdk-resources/resource.rb +0 -243
  24. data/lib/aws-sdk-resources/services/ec2.rb +0 -21
  25. data/lib/aws-sdk-resources/services/ec2/instance.rb +0 -29
  26. data/lib/aws-sdk-resources/services/iam.rb +0 -19
  27. data/lib/aws-sdk-resources/services/s3.rb +0 -21
  28. data/lib/aws-sdk-resources/services/s3/bucket.rb +0 -131
  29. data/lib/aws-sdk-resources/services/s3/encryption.rb +0 -24
  30. data/lib/aws-sdk-resources/services/s3/encryption/client.rb +0 -386
  31. data/lib/aws-sdk-resources/services/s3/encryption/decrypt_handler.rb +0 -225
  32. data/lib/aws-sdk-resources/services/s3/encryption/default_cipher_provider.rb +0 -101
  33. data/lib/aws-sdk-resources/services/s3/encryption/default_key_provider.rb +0 -40
  34. data/lib/aws-sdk-resources/services/s3/encryption/encrypt_handler.rb +0 -61
  35. data/lib/aws-sdk-resources/services/s3/encryption/errors.rb +0 -15
  36. data/lib/aws-sdk-resources/services/s3/encryption/io_auth_decrypter.rb +0 -58
  37. data/lib/aws-sdk-resources/services/s3/encryption/io_decrypter.rb +0 -37
  38. data/lib/aws-sdk-resources/services/s3/encryption/io_encrypter.rb +0 -71
  39. data/lib/aws-sdk-resources/services/s3/encryption/key_provider.rb +0 -31
  40. data/lib/aws-sdk-resources/services/s3/encryption/kms_cipher_provider.rb +0 -104
  41. data/lib/aws-sdk-resources/services/s3/encryption/materials.rb +0 -60
  42. data/lib/aws-sdk-resources/services/s3/encryption/utils.rb +0 -104
  43. data/lib/aws-sdk-resources/services/s3/encryptionV2/client.rb +0 -561
  44. data/lib/aws-sdk-resources/services/s3/encryptionV2/decrypt_handler.rb +0 -214
  45. data/lib/aws-sdk-resources/services/s3/encryptionV2/default_cipher_provider.rb +0 -170
  46. data/lib/aws-sdk-resources/services/s3/encryptionV2/default_key_provider.rb +0 -40
  47. data/lib/aws-sdk-resources/services/s3/encryptionV2/encrypt_handler.rb +0 -69
  48. data/lib/aws-sdk-resources/services/s3/encryptionV2/errors.rb +0 -37
  49. data/lib/aws-sdk-resources/services/s3/encryptionV2/io_auth_decrypter.rb +0 -58
  50. data/lib/aws-sdk-resources/services/s3/encryptionV2/io_decrypter.rb +0 -37
  51. data/lib/aws-sdk-resources/services/s3/encryptionV2/io_encrypter.rb +0 -73
  52. data/lib/aws-sdk-resources/services/s3/encryptionV2/key_provider.rb +0 -31
  53. data/lib/aws-sdk-resources/services/s3/encryptionV2/kms_cipher_provider.rb +0 -169
  54. data/lib/aws-sdk-resources/services/s3/encryptionV2/materials.rb +0 -60
  55. data/lib/aws-sdk-resources/services/s3/encryptionV2/utils.rb +0 -103
  56. data/lib/aws-sdk-resources/services/s3/encryption_v2.rb +0 -24
  57. data/lib/aws-sdk-resources/services/s3/file_downloader.rb +0 -169
  58. data/lib/aws-sdk-resources/services/s3/file_part.rb +0 -75
  59. data/lib/aws-sdk-resources/services/s3/file_uploader.rb +0 -58
  60. data/lib/aws-sdk-resources/services/s3/multipart_file_uploader.rb +0 -187
  61. data/lib/aws-sdk-resources/services/s3/multipart_upload.rb +0 -42
  62. data/lib/aws-sdk-resources/services/s3/multipart_upload_error.rb +0 -16
  63. data/lib/aws-sdk-resources/services/s3/object.rb +0 -290
  64. data/lib/aws-sdk-resources/services/s3/object_copier.rb +0 -99
  65. data/lib/aws-sdk-resources/services/s3/object_multipart_copier.rb +0 -180
  66. data/lib/aws-sdk-resources/services/s3/object_summary.rb +0 -73
  67. data/lib/aws-sdk-resources/services/s3/presigned_post.rb +0 -651
  68. data/lib/aws-sdk-resources/services/sns.rb +0 -7
  69. data/lib/aws-sdk-resources/services/sns/message_verifier.rb +0 -171
  70. data/lib/aws-sdk-resources/services/sqs.rb +0 -7
  71. data/lib/aws-sdk-resources/services/sqs/queue_poller.rb +0 -521
  72. data/lib/aws-sdk-resources/source.rb +0 -39
@@ -1,37 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Aws
4
- module S3
5
- module Encryption
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
@@ -1,71 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'stringio'
4
- require 'tempfile'
5
-
6
- module Aws
7
- module S3
8
- module Encryption
9
-
10
- # Provides an IO wrapper encrpyting a stream of data.
11
- # It is possible to use this same object for decrypting. You must
12
- # initialize it with a decryptiion cipher in that case and the
13
- # IO object must contain cipher text instead of plain text.
14
- # @api private
15
- class IOEncrypter
16
-
17
- # @api private
18
- ONE_MEGABYTE = 1024 * 1024
19
-
20
- def initialize(cipher, io)
21
- @encrypted = io.size <= ONE_MEGABYTE ?
22
- encrypt_to_stringio(cipher, io.read) :
23
- encrypt_to_tempfile(cipher, io)
24
- @size = @encrypted.size
25
- end
26
-
27
- # @return [Integer]
28
- attr_reader :size
29
-
30
- def read(bytes = nil, output_buffer = nil)
31
- if Tempfile === @encrypted && @encrypted.closed?
32
- @encrypted.open
33
- @encrypted.binmode
34
- end
35
- @encrypted.read(bytes, output_buffer)
36
- end
37
-
38
- def rewind
39
- @encrypted.rewind
40
- end
41
-
42
- # @api private
43
- def close
44
- @encrypted.close if Tempfile === @encrypted
45
- end
46
-
47
- private
48
-
49
- def encrypt_to_stringio(cipher, plain_text)
50
- if plain_text.empty?
51
- StringIO.new(cipher.final)
52
- else
53
- StringIO.new(cipher.update(plain_text) + cipher.final)
54
- end
55
- end
56
-
57
- def encrypt_to_tempfile(cipher, io)
58
- encrypted = Tempfile.new(self.object_id.to_s)
59
- encrypted.binmode
60
- while chunk = io.read(ONE_MEGABYTE)
61
- encrypted.write(cipher.update(chunk))
62
- end
63
- encrypted.write(cipher.final)
64
- encrypted.rewind
65
- encrypted
66
- end
67
-
68
- end
69
- end
70
- end
71
- end
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Aws
4
- module S3
5
- module Encryption
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
@@ -1,104 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'base64'
4
-
5
- module Aws
6
- module S3
7
- module Encryption
8
- # @api private
9
- class KmsCipherProvider
10
-
11
- def initialize(options = {})
12
- @kms_key_id = options[:kms_key_id]
13
- @kms_client = options[:kms_client]
14
- end
15
-
16
- # @return [Array<Hash,Cipher>] Creates an returns a new encryption
17
- # envelope and encryption cipher.
18
- def encryption_cipher
19
- encryption_context = { "kms_cmk_id" => @kms_key_id }
20
- key_data = @kms_client.generate_data_key(
21
- key_id: @kms_key_id,
22
- encryption_context: encryption_context,
23
- key_spec: 'AES_256',
24
- )
25
- cipher = Utils.aes_encryption_cipher(:CBC)
26
- cipher.key = key_data.plaintext
27
- envelope = {
28
- 'x-amz-key-v2' => encode64(key_data.ciphertext_blob),
29
- 'x-amz-iv' => encode64(cipher.iv = cipher.random_iv),
30
- 'x-amz-cek-alg' => 'AES/CBC/PKCS5Padding',
31
- 'x-amz-wrap-alg' => 'kms',
32
- 'x-amz-matdesc' => Json.dump(encryption_context)
33
- }
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
- encryption_context = Json.load(envelope['x-amz-matdesc'])
41
- cek_alg = envelope['x-amz-cek-alg']
42
-
43
- case envelope['x-amz-wrap-alg']
44
- when 'kms'; # NO OP
45
- when 'kms+context'
46
- if cek_alg != encryption_context['aws:x-amz-cek-alg']
47
- raise Errors::DecryptionError, 'Value of cek-alg from envelope'\
48
- ' does not match the value in the encryption context'
49
- end
50
- when 'AES/GCM'
51
- raise ArgumentError, 'Key mismatch - Client is configured' \
52
- ' with a KMS key and the x-amz-wrap-alg is AES/GCM.'
53
- when 'RSA-OAEP-SHA1'
54
- raise ArgumentError, 'Key mismatch - Client is configured' \
55
- ' with a KMS key and the x-amz-wrap-alg is RSA-OAEP-SHA1.'
56
- else
57
- raise ArgumentError, 'Unsupported wrap-alg: ' \
58
- "#{envelope['x-amz-wrap-alg']}"
59
- end
60
-
61
- key = @kms_client.decrypt(
62
- ciphertext_blob: decode64(envelope['x-amz-key-v2']),
63
- encryption_context: encryption_context
64
- ).plaintext
65
-
66
- iv = decode64(envelope['x-amz-iv'])
67
- block_mode =
68
- case cek_alg
69
- when 'AES/CBC/PKCS5Padding'
70
- :CBC
71
- when 'AES/CBC/PKCS7Padding'
72
- :CBC
73
- when 'AES/GCM/NoPadding'
74
- :GCM
75
- else
76
- type = envelope['x-amz-cek-alg'].inspect
77
- msg = "unsupported content encrypting key (cek) format: #{type}"
78
- raise Errors::DecryptionError, msg
79
- end
80
- Utils.aes_decryption_cipher(block_mode, key, iv)
81
- end
82
-
83
- private
84
-
85
- def build_encryption_context(cek_alg, options = {})
86
- kms_context = (options[:kms_encryption_context] || {})
87
- .each_with_object({}) { |(k, v), h| h[k.to_s] = v }
88
- {
89
- 'aws:x-amz-cek-alg' => cek_alg
90
- }.merge(kms_context)
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
@@ -1,60 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'base64'
4
-
5
- module Aws
6
- module S3
7
- module Encryption
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
@@ -1,104 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'openssl'
4
-
5
- module Aws
6
- module S3
7
- module Encryption
8
- # @api private
9
- module Utils
10
-
11
- UNSAFE_MSG = "unsafe encryption, data is longer than key length"
12
-
13
- class << self
14
-
15
- def encrypt(key, data)
16
- case key
17
- when OpenSSL::PKey::RSA # asymmetric encryption
18
- warn(UNSAFE_MSG) if key.public_key.n.num_bits < cipher_size(data)
19
- key.public_encrypt(data)
20
- when String # symmetric encryption
21
- warn(UNSAFE_MSG) if cipher_size(key) < cipher_size(data)
22
- cipher = aes_encryption_cipher(:ECB, key)
23
- cipher.update(data) + cipher.final
24
- end
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
-
43
- def decrypt_aes_gcm(key, data, auth_data)
44
- # data is iv (12B) + key + tag (16B)
45
- buf = data.unpack('C*')
46
- iv = buf[0,12].pack('C*') # iv will always be 12 bytes
47
- tag = buf[-16, 16].pack('C*') # tag is 16 bytes
48
- enc_key = buf[12, buf.size - (12+16)].pack('C*')
49
- cipher = aes_cipher(:decrypt, :GCM, key, iv)
50
- cipher.auth_tag = tag
51
- cipher.auth_data = auth_data
52
- cipher.update(enc_key) + cipher.final
53
- end
54
-
55
- # returns the decrypted data + auth_data
56
- def decrypt_rsa(key, enc_data)
57
- # Plaintext must be KeyLengthInBytes (1 Byte) + DataKey + AuthData
58
- buf = key.private_decrypt(enc_data, OpenSSL::PKey::RSA::PKCS1_OAEP_PADDING).unpack('C*')
59
- key_length = buf[0]
60
- data = buf[1, key_length].pack('C*')
61
- auth_data = buf[key_length+1, buf.length - key_length].pack('C*')
62
- [data, auth_data]
63
- end
64
-
65
- # @param [String] block_mode "CBC" or "ECB"
66
- # @param [OpenSSL::PKey::RSA, String, nil] key
67
- # @param [String, nil] iv The initialization vector
68
- def aes_encryption_cipher(block_mode, key = nil, iv = nil)
69
- aes_cipher(:encrypt, block_mode, key, iv)
70
- end
71
-
72
- # @param [String] block_mode "CBC" or "ECB"
73
- # @param [OpenSSL::PKey::RSA, String, nil] key
74
- # @param [String, nil] iv The initialization vector
75
- def aes_decryption_cipher(block_mode, key = nil, iv = nil)
76
- aes_cipher(:decrypt, block_mode, key, iv)
77
- end
78
-
79
- # @param [String] mode "encrypt" or "decrypt"
80
- # @param [String] block_mode "CBC" or "ECB"
81
- # @param [OpenSSL::PKey::RSA, String, nil] key
82
- # @param [String, nil] iv The initialization vector
83
- def aes_cipher(mode, block_mode, key, iv)
84
- cipher = key ?
85
- OpenSSL::Cipher.new("aes-#{cipher_size(key)}-#{block_mode.downcase}") :
86
- OpenSSL::Cipher.new("aes-256-#{block_mode.downcase}")
87
- cipher.send(mode) # encrypt or decrypt
88
- cipher.key = key if key
89
- cipher.iv = iv if iv
90
- cipher
91
- end
92
-
93
- # @param [String] key
94
- # @return [Integer]
95
- # @raise ArgumentError
96
- def cipher_size(key)
97
- key.bytesize * 8
98
- end
99
-
100
- end
101
- end
102
- end
103
- end
104
- end
@@ -1,561 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'forwardable'
4
-
5
- module Aws
6
- module S3
7
-
8
- REQUIRED_PARAMS = [:key_wrap_schema, :content_encryption_schema, :security_profile]
9
- SUPPORTED_SECURITY_PROFILES = [:v2, :v2_and_legacy]
10
-
11
- # Provides an encryption client that encrypts and decrypts data client-side,
12
- # storing the encrypted data in Amazon S3. The `EncryptionV2::Client` (V2 Client)
13
- # provides improved security over the `Encryption::Client` (V1 Client)
14
- # by using more modern and secure algorithms. You can use the V2 Client
15
- # to continue decrypting objects encrypted using deprecated algorithms
16
- # by setting security_profile: :v2_and_legacy. The latest V1 Client also
17
- # supports reading and decrypting objects encrypted by the V2 Client.
18
- #
19
- # This client uses a process called "envelope encryption". Your private
20
- # encryption keys and your data's plain-text are **never** sent to
21
- # Amazon S3. **If you lose you encryption keys, you will not be able to
22
- # decrypt your data.**
23
- #
24
- # ## Envelope Encryption Overview
25
- #
26
- # The goal of envelope encryption is to combine the performance of
27
- # fast symmetric encryption while maintaining the secure key management
28
- # that asymmetric keys provide.
29
- #
30
- # A one-time-use symmetric key (envelope key) is generated client-side.
31
- # This is used to encrypt the data client-side. This key is then
32
- # encrypted by your master key and stored alongside your data in Amazon
33
- # S3.
34
- #
35
- # When accessing your encrypted data with the encryption client,
36
- # the encrypted envelope key is retrieved and decrypted client-side
37
- # with your master key. The envelope key is then used to decrypt the
38
- # data client-side.
39
- #
40
- # One of the benefits of envelope encryption is that if your master key
41
- # is compromised, you have the option of just re-encrypting the stored
42
- # envelope symmetric keys, instead of re-encrypting all of the
43
- # data in your account.
44
- #
45
- # ## Basic Usage
46
- #
47
- # The encryption client requires an {Aws::S3::Client}. If you do not
48
- # provide a `:client`, then a client will be constructed for you.
49
- #
50
- # require 'openssl'
51
- # key = OpenSSL::PKey::RSA.new(1024)
52
- #
53
- # # encryption client
54
- # s3 = Aws::S3::EncryptionV2::Client.new(
55
- # encryption_key: key,
56
- # key_wrap_schema: :rsa_oaep_sha1, # the key_wrap_schema must be rsa_oaep_sha1 for asymmetric keys
57
- # content_encryption_schema: :aes_gcm_no_padding,
58
- # security_profile: :v2 # use :v2_and_legacy to allow reading/decrypting objects encrypted by the V1 encryption client
59
- # )
60
- #
61
- # # round-trip an object, encrypted/decrypted locally
62
- # s3.put_object(bucket:'aws-sdk', key:'secret', body:'handshake')
63
- # s3.get_object(bucket:'aws-sdk', key:'secret').body.read
64
- # #=> 'handshake'
65
- #
66
- # # reading encrypted object without the encryption client
67
- # # results in the getting the cipher text
68
- # Aws::S3::Client.new.get_object(bucket:'aws-sdk', key:'secret').body.read
69
- # #=> "... cipher text ..."
70
- #
71
- # ## Required Configuration
72
- #
73
- # You must configure all of the following:
74
- #
75
- # * a key or key provider - See the Keys section below. The key provided determines
76
- # the key wrapping schema(s) supported for both encryption and decryption.
77
- # * `key_wrap_schema` - The key wrapping schema. It must match the type of key configured.
78
- # * `content_encryption_schema` - The only supported value currently is `:aes_gcm_no_padding`.
79
- # More options will be added in future releases.
80
- # * `security_profile` - Determines the support for reading objects written
81
- # using older key wrap or content encryption schemas. If you need to read
82
- # legacy objects encrypted by an existing V1 Client, then set this to `:v2_and_legacy`.
83
- # Otherwise, set it to `:v2`
84
- #
85
- # ## Keys
86
- #
87
- # For client-side encryption to work, you must provide one of the following:
88
- #
89
- # * An encryption key
90
- # * A {KeyProvider}
91
- # * A KMS encryption key id
92
- #
93
- # Additionally, the key wrapping schema must agree with the type of the key:
94
- # * :aes_gcm: An AES encryption key or a key provider.
95
- # * :rsa_oaep_sha1: An RSA encryption key or key provider.
96
- # * :kms_context: A KMS encryption key id
97
- #
98
- # ### An Encryption Key
99
- #
100
- # You can pass a single encryption key. This is used as a master key
101
- # encrypting and decrypting all object keys.
102
- #
103
- # key = OpenSSL::Cipher.new("AES-256-ECB").random_key # symmetric key - used with `key_wrap_schema: :aes_gcm`
104
- # key = OpenSSL::PKey::RSA.new(1024) # asymmetric key pair - used with `key_wrap_schema: :rsa_oaep_sha1`
105
- #
106
- # s3 = Aws::S3::EncryptionV2::Client.new(
107
- # encryption_key: key,
108
- # key_wrap_schema: :aes_gcm, # or :rsa_oaep_sha1 if using RSA
109
- # content_encryption_schema: :aes_gcm_no_padding,
110
- # security_profile: :v2
111
- # )
112
- #
113
- # ### Key Provider
114
- #
115
- # Alternatively, you can use a {KeyProvider}. A key provider makes
116
- # it easy to work with multiple keys and simplifies key rotation.
117
- #
118
- # ### KMS Encryption Key Id
119
- #
120
- # If you pass the id of an AWS Key Management Service (KMS) key and
121
- # use :kms_content for the key_wrap_schema, then KMS will be used to
122
- # generate, encrypt and decrypt object keys.
123
- #
124
- # # keep track of the kms key id
125
- # kms = Aws::KMS::Client.new
126
- # key_id = kms.create_key.key_metadata.key_id
127
- #
128
- # Aws::S3::EncryptionV2::Client.new(
129
- # kms_key_id: key_id,
130
- # kms_client: kms,
131
- # key_wrap_schema: :kms_context,
132
- # content_encryption_schema: :aes_gcm_no_padding,
133
- # security_profile: :v2
134
- # )
135
- #
136
- # ## Custom Key Providers
137
- #
138
- # A {KeyProvider} is any object that responds to:
139
- #
140
- # * `#encryption_materials`
141
- # * `#key_for(materials_description)`
142
- #
143
- # Here is a trivial implementation of an in-memory key provider.
144
- # This is provided as a demonstration of the key provider interface,
145
- # and should not be used in production:
146
- #
147
- # class KeyProvider
148
- #
149
- # def initialize(default_key_name, keys)
150
- # @keys = keys
151
- # @encryption_materials = Aws::S3::EncryptionV2::Materials.new(
152
- # key: @keys[default_key_name],
153
- # description: JSON.dump(key: default_key_name),
154
- # )
155
- # end
156
- #
157
- # attr_reader :encryption_materials
158
- #
159
- # def key_for(matdesc)
160
- # key_name = JSON.load(matdesc)['key']
161
- # if key = @keys[key_name]
162
- # key
163
- # else
164
- # raise "encryption key not found for: #{matdesc.inspect}"
165
- # end
166
- # end
167
- # end
168
- #
169
- # Given the above key provider, you can create an encryption client that
170
- # chooses the key to use based on the materials description stored with
171
- # the encrypted object. This makes it possible to use multiple keys
172
- # and simplifies key rotation.
173
- #
174
- # # uses "new-key" for encrypting objects, uses either for decrypting
175
- # keys = KeyProvider.new('new-key', {
176
- # "old-key" => Base64.decode64("kM5UVbhE/4rtMZJfsadYEdm2vaKFsmV2f5+URSeUCV4="),
177
- # "new-key" => Base64.decode64("w1WLio3agRWRTSJK/Ouh8NHoqRQ6fn5WbSXDTHjXMSo="),
178
- # }),
179
- #
180
- # # chooses the key based on the materials description stored
181
- # # with the encrypted object
182
- # s3 = Aws::S3::EncryptionV2::Client.new(
183
- # key_provider: keys,
184
- # key_wrap_schema: ...,
185
- # content_encryption_schema: :aes_gcm_no_padding,
186
- # security_profile: :v2
187
- # )
188
- #
189
- # ## Materials Description
190
- #
191
- # A materials description is JSON document string that is stored
192
- # in the metadata (or instruction file) of an encrypted object.
193
- # The {DefaultKeyProvider} uses the empty JSON document `"{}"`.
194
- #
195
- # When building a key provider, you are free to store whatever
196
- # information you need to identify the master key that was used
197
- # to encrypt the object.
198
- #
199
- # ## Envelope Location
200
- #
201
- # By default, the encryption client store the encryption envelope
202
- # with the object, as metadata. You can choose to have the envelope
203
- # stored in a separate "instruction file". An instruction file
204
- # is an object, with the key of the encrypted object, suffixed with
205
- # `".instruction"`.
206
- #
207
- # Specify the `:envelope_location` option as `:instruction_file` to
208
- # use an instruction file for storing the envelope.
209
- #
210
- # # default behavior
211
- # s3 = Aws::S3::EncryptionV2::Client.new(
212
- # key_provider: ...,
213
- # envelope_location: :metadata,
214
- # )
215
- #
216
- # # store envelope in a separate object
217
- # s3 = Aws::S3::EncryptionV2::Client.new(
218
- # key_provider: ...,
219
- # envelope_location: :instruction_file,
220
- # instruction_file_suffix: '.instruction' # default
221
- # key_wrap_schema: ...,
222
- # content_encryption_schema: :aes_gcm_no_padding,
223
- # security_profile: :v2
224
- # )
225
- #
226
- # When using an instruction file, multiple requests are made when
227
- # putting and getting the object. **This may cause issues if you are
228
- # issuing concurrent PUT and GET requests to an encrypted object.**
229
- #
230
- module EncryptionV2
231
- class Client
232
-
233
- extend Deprecations
234
- extend Forwardable
235
- def_delegators :@client, :config, :delete_object, :head_object, :build_request
236
-
237
- # Creates a new encryption client. You must configure all of the following:
238
- #
239
- # * a key or key provider - The key provided also determines the key wrapping
240
- # schema(s) supported for both encryption and decryption.
241
- # * `key_wrap_schema` - The key wrapping schema. It must match the type of key configured.
242
- # * `content_encryption_schema` - The only supported value currently is `:aes_gcm_no_padding`
243
- # More options will be added in future releases.
244
- # * `security_profile` - Determines the support for reading objects written
245
- # using older key wrap or content encryption schemas. If you need to read
246
- # legacy objects encrypted by an existing V1 Client, then set this to `:v2_and_legacy`.
247
- # Otherwise, set it to `:v2`
248
- #
249
- # To configure the key you must provide one of the following set of options:
250
- #
251
- # * `:encryption_key`
252
- # * `:kms_key_id`
253
- # * `:key_provider`
254
- #
255
- # You may also pass any other options accepted by `Client#initialize`.
256
- #
257
- # @option options [S3::Client] :client A basic S3 client that is used
258
- # to make api calls. If a `:client` is not provided, a new {S3::Client}
259
- # will be constructed.
260
- #
261
- # @option options [OpenSSL::PKey::RSA, String] :encryption_key The master
262
- # key to use for encrypting/decrypting all objects.
263
- #
264
- # @option options [String] :kms_key_id When you provide a `:kms_key_id`,
265
- # then AWS Key Management Service (KMS) will be used to manage the
266
- # object encryption keys. By default a {KMS::Client} will be
267
- # constructed for KMS API calls. Alternatively, you can provide
268
- # your own via `:kms_client`. To only support decryption/reads, you may
269
- # provide `:allow_decrypt_with_any_cmk` which will use
270
- # the implicit CMK associated with the data during reads but will
271
- # not allow you to encrypt/write objects with this client.
272
- #
273
- # @option options [#key_for] :key_provider Any object that responds
274
- # to `#key_for`. This method should accept a materials description
275
- # JSON document string and return return an encryption key.
276
- #
277
- # @option options [required, Symbol] :key_wrap_schema The Key wrapping
278
- # schema to be used. It must match the type of key configured.
279
- # Must be one of the following:
280
- #
281
- # * :kms_context (Must provide kms_key_id)
282
- # * :aes_gcm (Must provide an AES (string) key)
283
- # * :rsa_oaep_sha1 (Must provide an RSA key)
284
- #
285
- # @option options [required, Symbol] :content_encryption_schema
286
- # Must be one of the following:
287
- #
288
- # * :aes_gcm_no_padding
289
- #
290
- # @option options [Required, Symbol] :security_profile
291
- # Determines the support for reading objects written using older
292
- # key wrap or content encryption schemas.
293
- # Must be one of the following:
294
- #
295
- # * :v2 - Reads of legacy (v1) objects are NOT allowed
296
- # * :v2_and_legacy - Enables reading of legacy (V1) schemas.
297
- #
298
- # @option options [Symbol] :envelope_location (:metadata) Where to
299
- # store the envelope encryption keys. By default, the envelope is
300
- # stored with the encrypted object. If you pass `:instruction_file`,
301
- # then the envelope is stored in a separate object in Amazon S3.
302
- #
303
- # @option options [String] :instruction_file_suffix ('.instruction')
304
- # When `:envelope_location` is `:instruction_file` then the
305
- # instruction file uses the object key with this suffix appended.
306
- #
307
- # @option options [KMS::Client] :kms_client A default {KMS::Client}
308
- # is constructed when using KMS to manage encryption keys.
309
- #
310
- def initialize(options = {})
311
- validate_params(options)
312
- @client = extract_client(options)
313
- @cipher_provider = cipher_provider(options)
314
- @envelope_location = extract_location(options)
315
- @instruction_file_suffix = extract_suffix(options)
316
- @kms_allow_decrypt_with_any_cmk =
317
- options[:kms_key_id] == :kms_allow_decrypt_with_any_cmk
318
- @security_profile = extract_security_profile(options)
319
- end
320
-
321
- # @return [S3::Client]
322
- attr_reader :client
323
-
324
- # @return [KeyProvider, nil] Returns `nil` if you are using
325
- # AWS Key Management Service (KMS).
326
- attr_reader :key_provider
327
-
328
- # @return [Symbol] Determines the support for reading objects written
329
- # using older key wrap or content encryption schemas.
330
- attr_reader :security_profile
331
-
332
- # @return [Boolean] If true the provided KMS key_id will not be used
333
- # during decrypt, allowing decryption with the key_id from the object.
334
- attr_reader :kms_allow_decrypt_with_any_cmk
335
-
336
- # @return [Symbol<:metadata, :instruction_file>]
337
- attr_reader :envelope_location
338
-
339
- # @return [String] When {#envelope_location} is `:instruction_file`,
340
- # the envelope is stored in the object with the object key suffixed
341
- # by this string.
342
- attr_reader :instruction_file_suffix
343
-
344
- # Uploads an object to Amazon S3, encrypting data client-side.
345
- # See {S3::Client#put_object} for documentation on accepted
346
- # request parameters.
347
- # @option params [Hash] :kms_encryption_context Additional encryption
348
- # context to use with KMS. Applies only when KMS is used. In order
349
- # to decrypt the object you will need to provide the identical
350
- # :kms_encryption_context to `get_object`.
351
- # @option (see S3::Client#put_object)
352
- # @return (see S3::Client#put_object)
353
- # @see S3::Client#put_object
354
- def put_object(params = {})
355
- kms_encryption_context = params.delete(:kms_encryption_context)
356
- req = @client.build_request(:put_object, params)
357
- req.handlers.add(EncryptHandler, priority: 95)
358
- req.context[:encryption] = {
359
- cipher_provider: @cipher_provider,
360
- envelope_location: @envelope_location,
361
- instruction_file_suffix: @instruction_file_suffix,
362
- kms_encryption_context: kms_encryption_context
363
- }
364
- req.send_request
365
- end
366
-
367
- # Gets an object from Amazon S3, decrypting data locally.
368
- # See {S3::Client#get_object} for documentation on accepted
369
- # request parameters.
370
- # @option options [Symbol] :security_profile
371
- # Determines the support for reading objects written using older
372
- # key wrap or content encryption schemas. Overrides the value set
373
- # on client construction if provided.
374
- # Must be one of the following:
375
- #
376
- # * :v2 - Reads of legacy (v1) objects are NOT allowed
377
- # * :v2_and_legacy - Enables reading of legacy (V1) schemas.
378
- # @option params [String] :instruction_file_suffix The suffix
379
- # used to find the instruction file containing the encryption
380
- # envelope. You should not set this option when the envelope
381
- # is stored in the object metadata. Defaults to
382
- # {#instruction_file_suffix}.
383
- # @option params [Hash] :kms_encryption_context Additional encryption
384
- # context to use with KMS. Applies only when KMS is used.
385
- # @option options [Boolean] :kms_allow_decrypt_with_any_cmk (false)
386
- # By default the KMS CMK ID (kms_key_id) will be used during decrypt
387
- # and will fail if there is a mismatch. Setting this to true
388
- # will use the implicit CMK associated with the data.
389
- # @option (see S3::Client#get_object)
390
- # @return (see S3::Client#get_object)
391
- # @see S3::Client#get_object
392
- # @note The `:range` request parameter is not supported.
393
- def get_object(params = {}, &block)
394
- if params[:range]
395
- raise NotImplementedError, '#get_object with :range not supported'
396
- end
397
- envelope_location, instruction_file_suffix = envelope_options(params)
398
- kms_encryption_context = params.delete(:kms_encryption_context)
399
- kms_any_cmk_mode = kms_any_cmk_mode(params)
400
- security_profile = security_profile_from_params(params)
401
-
402
- req = @client.build_request(:get_object, params)
403
- req.handlers.add(DecryptHandler)
404
- req.context[:encryption] = {
405
- cipher_provider: @cipher_provider,
406
- envelope_location: envelope_location,
407
- instruction_file_suffix: instruction_file_suffix,
408
- kms_encryption_context: kms_encryption_context,
409
- kms_allow_decrypt_with_any_cmk: kms_any_cmk_mode,
410
- security_profile: security_profile
411
- }
412
- req.send_request(target: block)
413
- end
414
-
415
- private
416
-
417
- # Validate required parameters exist and don't conflict.
418
- # The cek_alg and wrap_alg are passed on to the CipherProviders
419
- # and further validated there
420
- def validate_params(options)
421
- unless (missing_params = REQUIRED_PARAMS - options.keys).empty?
422
- raise ArgumentError, "Missing required parameter(s): "\
423
- "#{missing_params.map{ |s| ":#{s}" }.join(', ')}"
424
- end
425
-
426
- wrap_alg = options[:key_wrap_schema]
427
-
428
- # validate that the wrap alg matches the type of key given
429
- case wrap_alg
430
- when :kms_context
431
- unless options[:kms_key_id]
432
- raise ArgumentError, 'You must provide :kms_key_id to use :kms_context'
433
- end
434
- end
435
- end
436
-
437
- def extract_client(options)
438
- options[:client] || begin
439
- options = options.dup
440
- options.delete(:kms_key_id)
441
- options.delete(:kms_client)
442
- options.delete(:key_provider)
443
- options.delete(:encryption_key)
444
- options.delete(:envelope_location)
445
- options.delete(:instruction_file_suffix)
446
- REQUIRED_PARAMS.each { |p| options.delete(p) }
447
- S3::Client.new(options)
448
- end
449
- end
450
-
451
- def kms_client(options)
452
- options[:kms_client] || begin
453
- KMS::Client.new(
454
- region: @client.config.region,
455
- credentials: @client.config.credentials,
456
- )
457
- end
458
- end
459
-
460
- def cipher_provider(options)
461
- if options[:kms_key_id]
462
- KmsCipherProvider.new(
463
- kms_key_id: options[:kms_key_id],
464
- kms_client: kms_client(options),
465
- key_wrap_schema: options[:key_wrap_schema],
466
- content_encryption_schema: options[:content_encryption_schema]
467
- )
468
- else
469
- @key_provider = extract_key_provider(options)
470
- DefaultCipherProvider.new(
471
- key_provider: @key_provider,
472
- key_wrap_schema: options[:key_wrap_schema],
473
- content_encryption_schema: options[:content_encryption_schema]
474
- )
475
- end
476
- end
477
-
478
- def extract_key_provider(options)
479
- if options[:key_provider]
480
- options[:key_provider]
481
- elsif options[:encryption_key]
482
- DefaultKeyProvider.new(options)
483
- else
484
- msg = 'you must pass a :kms_key_id, :key_provider, or :encryption_key'
485
- raise ArgumentError, msg
486
- end
487
- end
488
-
489
- def envelope_options(params)
490
- location = params.delete(:envelope_location) || @envelope_location
491
- suffix = params.delete(:instruction_file_suffix)
492
- if suffix
493
- [:instruction_file, suffix]
494
- else
495
- [location, @instruction_file_suffix]
496
- end
497
- end
498
-
499
- def extract_location(options)
500
- location = options[:envelope_location] || :metadata
501
- if [:metadata, :instruction_file].include?(location)
502
- location
503
- else
504
- msg = ':envelope_location must be :metadata or :instruction_file '\
505
- "got #{location.inspect}"
506
- raise ArgumentError, msg
507
- end
508
- end
509
-
510
- def extract_suffix(options)
511
- suffix = options[:instruction_file_suffix] || '.instruction'
512
- if suffix.is_a? String
513
- suffix
514
- else
515
- msg = ':instruction_file_suffix must be a String'
516
- raise ArgumentError, msg
517
- end
518
- end
519
-
520
- def kms_any_cmk_mode(params)
521
- if !params[:kms_allow_decrypt_with_any_cmk].nil?
522
- params.delete(:kms_allow_decrypt_with_any_cmk)
523
- else
524
- @kms_allow_decrypt_with_any_cmk
525
- end
526
- end
527
-
528
- def extract_security_profile(options)
529
- validate_security_profile(options[:security_profile])
530
- end
531
-
532
- def security_profile_from_params(params)
533
- security_profile =
534
- if !params[:security_profile].nil?
535
- params.delete(:security_profile)
536
- else
537
- @security_profile
538
- end
539
- validate_security_profile(security_profile)
540
- end
541
-
542
- def validate_security_profile(security_profile)
543
- unless SUPPORTED_SECURITY_PROFILES.include? security_profile
544
- raise ArgumentError, "Unsupported security profile: :#{security_profile}. " \
545
- "Please provide one of: #{SUPPORTED_SECURITY_PROFILES.map { |s| ":#{s}" }.join(', ')}"
546
- end
547
- if security_profile == :v2_and_legacy && !@warned_about_legacy
548
- @warned_about_legacy = true
549
- warn(
550
- 'The S3 Encryption Client is configured to read encrypted objects ' \
551
- "with legacy encryption modes. If you don't have objects " \
552
- 'encrypted with these legacy modes, you should disable support ' \
553
- 'for them to enhance security.'
554
- )
555
- end
556
- security_profile
557
- end
558
- end
559
- end
560
- end
561
- end