aws-sdk-resources 2.1.8 → 2.1.9

Sign up to get free protection for your applications and to get access to all the features.
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