aws-sdk-resources 2.1.8 → 2.1.9

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a2cca81f17c1740c1dba2ccf904b439999dc6f73
4
- data.tar.gz: 39b85f20fc0ca3e1bb74bb131a5e7824f29d0b3d
3
+ metadata.gz: f766ecda91727cde733f041b28aac6a40f7a52bd
4
+ data.tar.gz: 3686c5198e513ff7161b9dd81c9b6fd62b49479f
5
5
  SHA512:
6
- metadata.gz: cd3c1b4b6c3efa37be65e615d6f5cb486a91dcbc95ada6400fa4931b1aab1caa98982e26ceae48434da434e14641ac8c053247e7721b7425842ffbd9b4efcd1d
7
- data.tar.gz: e2b5472781093b043a3b15a73991a41f666e02ef2a52642989944ccf4281a1e93f8c1ece682a996a96d8032fb37f79de99a84639953559505a492f3567165069
6
+ metadata.gz: 0382c58d6ac0af454eb4a85ca4829ff9c2c6d319b564dee88d8fc3f3c86512a005cbf4910aeabf9b3b07fbd88b433de20aa69ac247f77a8e50b58841c28ccdb9
7
+ data.tar.gz: 416e650362af08992aa5e51bcb76b4bee40d7fb220c41c4aa1e4c86aa7fe43d925aba829dd7de7d316ef1ccd05167300cc5cc9236aa3e6ee9f0dad3538c90c86
@@ -4,12 +4,14 @@ module Aws
4
4
 
5
5
  autoload :Client, 'aws-sdk-resources/services/s3/encryption/client'
6
6
  autoload :DecryptHandler, 'aws-sdk-resources/services/s3/encryption/decrypt_handler'
7
+ autoload :DefaultCipherProvider, 'aws-sdk-resources/services/s3/encryption/default_cipher_provider'
7
8
  autoload :DefaultKeyProvider, 'aws-sdk-resources/services/s3/encryption/default_key_provider'
8
9
  autoload :EncryptHandler, 'aws-sdk-resources/services/s3/encryption/encrypt_handler'
9
10
  autoload :Errors, 'aws-sdk-resources/services/s3/encryption/errors'
10
11
  autoload :IOEncrypter, 'aws-sdk-resources/services/s3/encryption/io_encrypter'
11
12
  autoload :IODecrypter, 'aws-sdk-resources/services/s3/encryption/io_decrypter'
12
13
  autoload :KeyProvider, 'aws-sdk-resources/services/s3/encryption/key_provider'
14
+ autoload :KmsCipherProvider, 'aws-sdk-resources/services/s3/encryption/kms_cipher_provider'
13
15
  autoload :Materials, 'aws-sdk-resources/services/s3/encryption/materials'
14
16
  autoload :Utils, 'aws-sdk-resources/services/s3/encryption/utils'
15
17
 
@@ -53,17 +53,42 @@ module Aws
53
53
  #
54
54
  # ## Keys
55
55
  #
56
- # For client-side encryption to work, you must provide an encryption key:
56
+ # For client-side encryption to work, you must provide one of the following:
57
+ #
58
+ # * An encryption key
59
+ # * A {KeyProvider}
60
+ # * A KMS encryption key id
61
+ #
62
+ # ### An Encryption Key
63
+ #
64
+ # You can pass a single encryption key. This is used as a master key
65
+ # encrypting and decrypting all object keys.
57
66
  #
58
67
  # key = OpenSSL::Cipher.new("AES-256-ECB").random_key # symmetric key
59
68
  # key = OpenSSL::PKey::RSA.new(1024) # asymmetric key pair
60
69
  #
61
70
  # s3 = Aws::S3::Encryption::Client.new(encryption_key: key)
62
71
  #
72
+ # ### Key Provider
73
+ #
63
74
  # Alternatively, you can use a {KeyProvider}. A key provider makes
64
75
  # it easy to work with multiple keys and simplifies key rotation.
65
76
  #
66
- # ### Key Provider
77
+ # ### KMS Encryption Key Id
78
+ #
79
+ # If you pass the id to an AWS Key Management Service (KMS) key,
80
+ # then KMS will be used to generate, encrypt and decrypt object keys.
81
+ #
82
+ # # keep track of the kms key id
83
+ # kms = Aws::KMS::Client.new
84
+ # key_id = kms.create_key.key_metadata.key_id
85
+ #
86
+ # Aws::S3::Encryption::Client.new(
87
+ # kms_key_id: key_id,
88
+ # kms_client: kms,
89
+ # )
90
+ #
91
+ # ## Custom Key Providers
67
92
  #
68
93
  # A {KeyProvider} is any object that responds to:
69
94
  #
@@ -150,14 +175,16 @@ module Aws
150
175
  # issuing concurrent PUT and GET requests to an encrypted object.**
151
176
  #
152
177
  module Encryption
153
-
154
178
  class Client
155
179
 
180
+ extend Deprecations
181
+
156
182
  # Creates a new encryption client. You must provide on of the following
157
183
  # options:
158
184
  #
159
- # * `:key_provider`
160
185
  # * `:encryption_key`
186
+ # * `:kms_key_id`
187
+ # * `:key_provider`
161
188
  #
162
189
  # You may also pass any other options accepted by {S3::Client#initialize}.
163
190
  #
@@ -165,13 +192,19 @@ module Aws
165
192
  # to make api calls. If a `:client` is not provided, a new {S3::Client}
166
193
  # will be constructed.
167
194
  #
195
+ # @option options [OpenSSL::PKey::RSA, String] :encryption_key The master
196
+ # key to use for encrypting/decrypting all objects.
197
+ #
198
+ # @option options [String] :kms_key_id When you provide a `:kms_key_id`,
199
+ # then AWS Key Management Service (KMS) will be used to manage the
200
+ # object encryption keys. By default a {KMS::Client} will be
201
+ # constructed for KMS API calls. Alternatively, you can provide
202
+ # your own via `:kms_client`.
203
+ #
168
204
  # @option options [#key_for] :key_provider Any object that responds
169
205
  # to `#key_for`. This method should accept a materials description
170
206
  # JSON document string and return return an encryption key.
171
207
  #
172
- # @option options [OpenSSL::PKey::RSA, String] :encryption_key The master
173
- # key to use for encrypting/decrypting all objects.
174
- #
175
208
  # @option options [Symbol] :envelope_location (:metadata) Where to
176
209
  # store the envelope encryption keys. By default, the envelope is
177
210
  # stored with the encrypted object. If you pass `:instruction_file`,
@@ -181,9 +214,12 @@ module Aws
181
214
  # When `:envelope_location` is `:instruction_file` then the
182
215
  # instruction file uses the object key with this suffix appended.
183
216
  #
217
+ # @option options [KMS::Client] :kms_client A default {KMS::Client}
218
+ # is constructed when using KMS to manage encryption keys.
219
+ #
184
220
  def initialize(options = {})
185
221
  @client = extract_client(options)
186
- @key_provider = extract_key_provider(options)
222
+ @cipher_provider = cipher_provider(options)
187
223
  @envelope_location = extract_location(options)
188
224
  @instruction_file_suffix = extract_suffix(options)
189
225
  end
@@ -191,7 +227,8 @@ module Aws
191
227
  # @return [S3::Client]
192
228
  attr_reader :client
193
229
 
194
- # @return [KeyProvider]
230
+ # @return [KeyProvider, nil] Returns `nil` if you are using
231
+ # AWS Key Management Service (KMS).
195
232
  attr_reader :key_provider
196
233
 
197
234
  # @return [Symbol<:metadata, :instruction_file>]
@@ -212,7 +249,7 @@ module Aws
212
249
  req = @client.build_request(:put_object, params)
213
250
  req.handlers.add(EncryptHandler, priority: 95)
214
251
  req.context[:encryption] = {
215
- materials: @key_provider.encryption_materials,
252
+ cipher_provider: @cipher_provider,
216
253
  envelope_location: @envelope_location,
217
254
  instruction_file_suffix: @instruction_file_suffix,
218
255
  }
@@ -240,7 +277,7 @@ module Aws
240
277
  req = @client.build_request(:get_object, params)
241
278
  req.handlers.add(DecryptHandler)
242
279
  req.context[:encryption] = {
243
- key_provider: @key_provider,
280
+ cipher_provider: @cipher_provider,
244
281
  envelope_location: envelope_location,
245
282
  instruction_file_suffix: instruction_file_suffix,
246
283
  }
@@ -252,6 +289,8 @@ module Aws
252
289
  def extract_client(options)
253
290
  options[:client] || begin
254
291
  options = options.dup
292
+ options.delete(:kms_key_id)
293
+ options.delete(:kms_client)
255
294
  options.delete(:key_provider)
256
295
  options.delete(:encryption_key)
257
296
  options.delete(:envelope_location)
@@ -260,13 +299,25 @@ module Aws
260
299
  end
261
300
  end
262
301
 
263
- def envelope_options(params)
264
- location = params.delete(:envelope_location) || @envelope_location
265
- suffix = params.delete(:instruction_file_suffix)
266
- if suffix
267
- [:instruction_file, suffix]
302
+ def kms_client(options)
303
+ options[:kms_client] || begin
304
+ KMS::Client.new(
305
+ region: @client.config.region,
306
+ credentials: @client.config.credentials,
307
+ )
308
+ end
309
+ end
310
+
311
+ def cipher_provider(options)
312
+ if options[:kms_key_id]
313
+ KmsCipherProvider.new(
314
+ kms_key_id: options[:kms_key_id],
315
+ kms_client: kms_client(options),
316
+ )
268
317
  else
269
- [location, @instruction_file_suffix]
318
+ # kept here for backwards compatability, {#key_provider} is deprecated
319
+ @key_provider = extract_key_provider(options)
320
+ DefaultCipherProvider.new(key_provider: @key_provider)
270
321
  end
271
322
  end
272
323
 
@@ -276,11 +327,21 @@ module Aws
276
327
  elsif options[:encryption_key]
277
328
  DefaultKeyProvider.new(options)
278
329
  else
279
- msg = "you must pass a :key_provider or :encryption_key"
330
+ msg = "you must pass a :kms_key_id, :key_provider, or :encryption_key"
280
331
  raise ArgumentError, msg
281
332
  end
282
333
  end
283
334
 
335
+ def envelope_options(params)
336
+ location = params.delete(:envelope_location) || @envelope_location
337
+ suffix = params.delete(:instruction_file_suffix)
338
+ if suffix
339
+ [:instruction_file, suffix]
340
+ else
341
+ [location, @instruction_file_suffix]
342
+ end
343
+ end
344
+
284
345
  def extract_location(options)
285
346
  location = options[:envelope_location] || :metadata
286
347
  if [:metadata, :instruction_file].include?(location)
@@ -6,6 +6,22 @@ module Aws
6
6
  # @api private
7
7
  class DecryptHandler < Seahorse::Client::Handler
8
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
+ POSSIBLE_ENVELOPE_KEYS = (V1_ENVELOPE_KEYS + V2_ENVELOPE_KEYS).uniq
24
+
9
25
  def call(context)
10
26
  attach_http_event_listeners(context)
11
27
  @handler.call(context)
@@ -36,11 +52,7 @@ module Aws
36
52
 
37
53
  def decryption_cipher(context)
38
54
  if envelope = get_encryption_envelope(context)
39
- key_provider = context[:encryption][:key_provider]
40
- key = key_provider.key_for(envelope['x-amz-matdesc'])
41
- envelope_key = Utils.decrypt(key, decode64(envelope['x-amz-key']))
42
- envelope_iv = decode64(envelope['x-amz-iv'])
43
- Utils.aes_decryption_cipher(:CBC, envelope_key, envelope_iv)
55
+ context[:encryption][:cipher_provider].decryption_cipher(envelope)
44
56
  else
45
57
  raise Errors::DecryptionError, "unable to locate encyrption envelope"
46
58
  end
@@ -55,27 +67,61 @@ module Aws
55
67
  end
56
68
 
57
69
  def envelope_from_metadata(context)
58
- keys = %w(x-amz-key x-amz-iv x-amz-matdesc)
59
- envelope = keys.each.with_object({}) do |key, hash|
60
- if value = context.http_response.headers["x-amz-meta-#{key}"]
61
- hash[key] = value
70
+ possible_envelope = {}
71
+ POSSIBLE_ENVELOPE_KEYS.each do |suffix|
72
+ if value = context.http_response.headers["x-amz-meta-#{suffix}"]
73
+ possible_envelope[suffix] = value
62
74
  end
63
75
  end
64
- envelope.keys == keys ? envelope : nil
76
+ extract_envelope(possible_envelope)
65
77
  end
66
78
 
67
79
  def envelope_from_instr_file(context)
68
80
  suffix = context[:encryption][:instruction_file_suffix]
69
- Json.load(context.client.get_object(
81
+ possible_envelope = Json.load(context.client.get_object(
70
82
  bucket: context.params[:bucket],
71
83
  key: context.params[:key] + suffix
72
84
  ).body.read)
85
+ extract_envelope(possible_envelope)
73
86
  rescue S3::Errors::ServiceError, Json::ParseError
74
87
  nil
75
88
  end
76
89
 
77
- def decode64(str)
78
- Base64.decode64(str)
90
+ def extract_envelope(hash)
91
+ return v1_envelope(hash) if hash.key?('x-amz-key')
92
+ return v2_envelope(hash) if hash.key?('x-amz-key-v2')
93
+ if hash.keys.any? { |key| key.match(/^x-amz-key-(.+)$/) }
94
+ msg = "unsupported envelope encryption version #{$1}"
95
+ raise Errors::DecryptionError, msg
96
+ else
97
+ nil # no envelope found
98
+ end
99
+ end
100
+
101
+ def v1_envelope(envelope)
102
+ envelope
103
+ end
104
+
105
+ def v2_envelope(envelope)
106
+ unless envelope['x-amz-cek-alg'] == 'AES/CBC/PKCS5Padding'
107
+ alg = envelope['x-amz-cek-alg'].inspect
108
+ msg = "unsupported content encrypting key (cek) format: #{alg}"
109
+ raise Errors::DecryptionError, msg
110
+ end
111
+ unless envelope['x-amz-wrap-alg'] == 'kms'
112
+ # possible to support
113
+ # RSA/ECB/OAEPWithSHA-256AndMGF1Padding
114
+ alg = envelope['x-amz-wrap-alg'].inspect
115
+ msg = "unsupported key wrapping algorithm: #{alg}"
116
+ raise Errors::DecryptionError, msg
117
+ end
118
+ unless V2_ENVELOPE_KEYS.sort == envelope.keys.sort
119
+ msg = "incomplete v2 encryption envelope:\n"
120
+ msg += " expected: #{V2_ENVELOPE_KEYS.join(',')}\n"
121
+ msg += " got: #{envelope_keys.join(', ')}"
122
+ raise Errors::DecryptionError, msg
123
+ end
124
+ envelope
79
125
  end
80
126
 
81
127
  end
@@ -0,0 +1,63 @@
1
+ require 'base64'
2
+
3
+ module Aws
4
+ module S3
5
+ module Encryption
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(:CBC)
17
+ envelope = {
18
+ 'x-amz-key' => encode64(encrypt(envelope_key(cipher))),
19
+ 'x-amz-iv' => encode64(envelope_iv(cipher)),
20
+ 'x-amz-matdesc' => materials_description,
21
+ }
22
+ [envelope, cipher]
23
+ end
24
+
25
+ # @return [Cipher] Given an encryption envelope, returns a
26
+ # decryption cipher.
27
+ def decryption_cipher(envelope)
28
+ master_key = @key_provider.key_for(envelope['x-amz-matdesc'])
29
+ key = Utils.decrypt(master_key, decode64(envelope['x-amz-key']))
30
+ iv = decode64(envelope['x-amz-iv'])
31
+ Utils.aes_decryption_cipher(:CBC, key, iv)
32
+ end
33
+
34
+ private
35
+
36
+ def envelope_key(cipher)
37
+ cipher.key = cipher.random_key
38
+ end
39
+
40
+ def envelope_iv(cipher)
41
+ cipher.iv = cipher.random_iv
42
+ end
43
+
44
+ def encrypt(data)
45
+ Utils.encrypt(@key_provider.encryption_materials.key, data)
46
+ end
47
+
48
+ def materials_description
49
+ @key_provider.encryption_materials.description
50
+ end
51
+
52
+ def encode64(str)
53
+ Base64.encode64(str).split("\n") * ""
54
+ end
55
+
56
+ def decode64(str)
57
+ Base64.decode64(str)
58
+ end
59
+
60
+ end
61
+ end
62
+ end
63
+ end
@@ -7,21 +7,16 @@ module Aws
7
7
  class EncryptHandler < Seahorse::Client::Handler
8
8
 
9
9
  def call(context)
10
- cipher = Utils.aes_encryption_cipher(:CBC)
11
- context[:encryption][:cipher] = cipher
12
- envelope = {
13
- 'x-amz-key' => encode64(encrypt(context, envelope_key(cipher))),
14
- 'x-amz-iv' => encode64(envelope_iv(cipher)),
15
- 'x-amz-matdesc' => context[:encryption][:materials].description,
16
- }
17
- apply_encryption_envelope(context, envelope)
10
+ envelope, cipher = context[:encryption][:cipher_provider].encryption_cipher
11
+ apply_encryption_envelope(context, envelope, cipher)
18
12
  apply_encryption_cipher(context, cipher)
19
13
  @handler.call(context)
20
14
  end
21
15
 
22
16
  private
23
17
 
24
- def apply_encryption_envelope(context, envelope)
18
+ def apply_encryption_envelope(context, envelope, cipher)
19
+ context[:encryption][:cipher] = cipher
25
20
  if context[:encryption][:envelope_location] == :metadata
26
21
  context.params[:metadata] ||= {}
27
22
  context.params[:metadata].update(envelope)
@@ -49,22 +44,6 @@ module Aws
49
44
  end
50
45
  end
51
46
 
52
- def envelope_key(cipher)
53
- cipher.key = cipher.random_key
54
- end
55
-
56
- def envelope_iv(cipher)
57
- cipher.iv = cipher.random_iv
58
- end
59
-
60
- def encode64(str)
61
- Base64.encode64(str).split("\n") * ""
62
- end
63
-
64
- def encrypt(context, data)
65
- Utils.encrypt(context[:encryption][:materials].key, data)
66
- end
67
-
68
47
  end
69
48
  end
70
49
  end
@@ -0,0 +1,60 @@
1
+ require 'base64'
2
+
3
+ module Aws
4
+ module S3
5
+ module Encryption
6
+ # @api private
7
+ class KmsCipherProvider
8
+
9
+ def initialize(options = {})
10
+ @kms_key_id = options[:kms_key_id]
11
+ @kms_client = options[:kms_client]
12
+ end
13
+
14
+ # @return [Array<Hash,Cipher>] Creates an returns a new encryption
15
+ # envelope and encryption cipher.
16
+ def encryption_cipher
17
+ encryption_context = { "kms_cmk_id" => @kms_key_id }
18
+ key_data = @kms_client.generate_data_key(
19
+ key_id: @kms_key_id,
20
+ encryption_context: encryption_context,
21
+ key_spec: 'AES_256',
22
+ )
23
+ cipher = Utils.aes_encryption_cipher(:CBC)
24
+ cipher.key = key_data.plaintext
25
+ envelope = {
26
+ 'x-amz-key-v2' => encode64(key_data.ciphertext_blob),
27
+ 'x-amz-iv' => encode64(cipher.iv = cipher.random_iv),
28
+ 'x-amz-cek-alg' => 'AES/CBC/PKCS5Padding',
29
+ 'x-amz-wrap-alg' => 'kms',
30
+ 'x-amz-matdesc' => Json.dump(encryption_context)
31
+ }
32
+ [envelope, cipher]
33
+ end
34
+
35
+ # @return [Cipher] Given an encryption envelope, returns a
36
+ # decryption cipher.
37
+ def decryption_cipher(envelope)
38
+ encryption_context = Json.load(envelope['x-amz-matdesc'])
39
+ key = @kms_client.decrypt(
40
+ ciphertext_blob: decode64(envelope['x-amz-key-v2']),
41
+ encryption_context: encryption_context,
42
+ ).plaintext
43
+ iv = decode64(envelope['x-amz-iv'])
44
+ Utils.aes_decryption_cipher(:CBC, key, iv)
45
+ end
46
+
47
+ private
48
+
49
+ def encode64(str)
50
+ Base64.encode64(str).split("\n") * ""
51
+ end
52
+
53
+ def decode64(str)
54
+ Base64.decode64(str)
55
+ end
56
+
57
+ end
58
+ end
59
+ end
60
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aws-sdk-resources
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.8
4
+ version: 2.1.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Amazon Web Services
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-07-23 00:00:00.000000000 Z
11
+ date: 2015-07-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk-core
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 2.1.8
19
+ version: 2.1.9
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 2.1.8
26
+ version: 2.1.9
27
27
  description: Provides resource oriented interfaces and other higher-level abstractions
28
28
  for many AWS services. This gem is part of the official AWS SDK for Ruby.
29
29
  email:
@@ -61,12 +61,14 @@ files:
61
61
  - lib/aws-sdk-resources/services/s3/encryption.rb
62
62
  - lib/aws-sdk-resources/services/s3/encryption/client.rb
63
63
  - lib/aws-sdk-resources/services/s3/encryption/decrypt_handler.rb
64
+ - lib/aws-sdk-resources/services/s3/encryption/default_cipher_provider.rb
64
65
  - lib/aws-sdk-resources/services/s3/encryption/default_key_provider.rb
65
66
  - lib/aws-sdk-resources/services/s3/encryption/encrypt_handler.rb
66
67
  - lib/aws-sdk-resources/services/s3/encryption/errors.rb
67
68
  - lib/aws-sdk-resources/services/s3/encryption/io_decrypter.rb
68
69
  - lib/aws-sdk-resources/services/s3/encryption/io_encrypter.rb
69
70
  - lib/aws-sdk-resources/services/s3/encryption/key_provider.rb
71
+ - lib/aws-sdk-resources/services/s3/encryption/kms_cipher_provider.rb
70
72
  - lib/aws-sdk-resources/services/s3/encryption/materials.rb
71
73
  - lib/aws-sdk-resources/services/s3/encryption/utils.rb
72
74
  - lib/aws-sdk-resources/services/s3/file_part.rb