chef-encrypted-attributes 0.1.1 → 0.2.0

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: 17b97315e38f6e17547fb5686b485d628c84b40e
4
- data.tar.gz: c475bd6e9f5631ffa0727cc2685938fa3263c611
3
+ metadata.gz: e1bb2baddf0f4e35d5b5fb127fd7ce2df1c28612
4
+ data.tar.gz: 92504e332a6b3ae5a15746f727149fa7e6c9fcc2
5
5
  SHA512:
6
- metadata.gz: a24fde54be4cd23dd3215095a7d72f54c25589b3282dbf364569f11cb8e4f98dd92a41219d3962968456f0d5213984bbde841fca33025f404e426f7f9a8a1d42
7
- data.tar.gz: ddd03f1b5e9b6a61b6c6d4dbde9239410094130d146b72a19a6024f7d7639159303b7337733bb2999e4d27211c5e8696f14862bfd67eb83253fe650682046f5d
6
+ metadata.gz: 919dff2768d33583bdedcf661865091c494b0e6a9a5823fe0aebed15bfa2db812c9c82c5647084f811f93c6b688cb37d314b2686c949f7a6a38fe9685934dfef
7
+ data.tar.gz: f802b87b39e85e74b402acb3ffbae41f5d3c3e51e0eb862b3b09c4692dbc1551b0a37f57a9fced0f03f6b8007c0a56f028ccd17d3a043cb7e3fac34ee7953c82
@@ -0,0 +1,2 @@
1
+ �πw������Q11 3I/�s�<<�1e�3e�����B���H���r}̏��ࣖxcrqB}E��(�ɵ�-+F�6b����M©zҼߖ)g8�x�TeX̨1�Z�+�TM�^�{G_�!�5��x� Gl������&4n�sZⰭ�� ����7d�o��b����^]�jJ�9�mrm���S���X�)
2
+ N�[ِ�ƥOd�P]!P.�
Binary file
data/API.md CHANGED
@@ -41,7 +41,7 @@ Returns `true` if the encrypted attribute has been updated, `false` if not.
41
41
 
42
42
  An exception is thrown if there is any error in the updating process.
43
43
 
44
- ### Chef::EncryptedAttribute.exists?(hs)
44
+ ### Chef::EncryptedAttribute.exist?(hs)
45
45
 
46
46
  Checks whether an encrypted attribute exists.
47
47
 
@@ -86,7 +86,7 @@ An exception is thrown if there is any error in the updating process.
86
86
 
87
87
  This method **requires admin privileges**. So in most cases, cannot be used from cookbooks.
88
88
 
89
- ### Chef::EncryptedAttribute.exists_on_node?(name, attr_ary [, config])
89
+ ### Chef::EncryptedAttribute.exist_on_node?(name, attr_ary [, config])
90
90
 
91
91
  Checks whether an encrypted attribute exists in a remote node.
92
92
 
@@ -102,9 +102,11 @@ All the methods read the default configuration from the `Chef::Config[:encrypted
102
102
 
103
103
  If the configuration value to be merged is an array or a hash (for example `keys`), the method argument configuration value has preference over the global configuration. Arrays and hashes are not merged.
104
104
 
105
- Both `Chef::Config[:encrypted_attributes]` and methods `config` parameter should be a hash which may have any of the following keys:
105
+ Both `Chef::Config[:encrypted_attributes]` and method's `config` parameter should be a hash which may have any of the following keys:
106
106
 
107
- * `:version` - `EncryptedMash` format version to use, by default `1` is used which is considered best.
107
+ * `:version` - `EncryptedMash` format version to use, by default `1` is used which is recommended. The version `2` uses [GCM](http://en.wikipedia.org/wiki/Galois/Counter_Mode) and probably should be considered the most secure, but it is disabled by default because it has some more requirements:
108
+ * Ruby `>= 2`.
109
+ * OpenSSL `>= 1.0.1`.
108
110
  * `:partial_search` - Whether to use Chef Server partial search, enabled by default. It may not work in some old versions of Chef Server.
109
111
  * `:client_search` - Search query for clients allowed to read the encrypted attribute. Can be a simple string or an array of queries to be *OR*-ed.
110
112
  * `:users` - Array of user names to be allowed to read the encrypted attribute(s). `"*"` to allow access to all users. Keep in mind that only admin clients or admin users are allowed to read user public keys. It is **not recommended** to use this from cookbooks unless you know what you are doing.
@@ -125,6 +127,13 @@ To disable Partial Search locally:
125
127
  ftp_pass = Chef::EncryptedAttribute.load(node["myapp"]["ftp_password"], { :partial_search => false })
126
128
  ```
127
129
 
130
+ To use protocol version 2 globally, which uses [GCM](http://en.wikipedia.org/wiki/Galois/Counter_Mode):
131
+
132
+ ```ruby
133
+ Chef::Config[:encrypted_attributes][:version] = 2
134
+ # ...
135
+ ```
136
+
128
137
  If you want to use knife to work with encrypted attributes, surely you will need to save your Chef User public keys in a Data Bag (there is no need to encrypt them because they are public) and add them to the `:keys` configuration option. See the [Example Using User Keys Data Bag](README.md#example-using-user-keys-data-bag) in the README for more information on this.
129
138
 
130
139
  ## Caches
@@ -2,6 +2,18 @@
2
2
 
3
3
  This file is used to list changes made in each version of `chef-encrypted-attributes`.
4
4
 
5
+ ## 0.2.0:
6
+
7
+ * Deprecate `#exists?` methods in favor of `#exist?` methods
8
+ * Fixed all RSpec deprecation warnings
9
+ * Added Protocol Version 2 (*disabled by default*): uses [GCM](http://en.wikipedia.org/wiki/Galois/Counter_Mode) as in [Chef 12 Encrypted Data Bags Version 3](https://github.com/opscode/chef/pull/1591).
10
+ * Added `RequirementsFailure` exception
11
+ * README, CONTRIBUTING, TODO: multiple documentation improvements
12
+ * Added some security related sections to the README
13
+ * Added email GPG key
14
+ * Added gem signing certificate
15
+ * gemspec: added dev dependency versions with pessimistic operator
16
+
5
17
  ## 0.1.1:
6
18
 
7
19
  * gemspec: replaced open-ended chef dependency by `~> 11.4`
@@ -110,3 +110,57 @@ Data to calculate the HMAC from
110
110
  * The data used to calculate the HMAC is the encrypted data, not the clear text data (**Encrypt-then-MAC**).
111
111
  * The secret used to calculate the HMAC is not the same as the secret used to encrypt the data.
112
112
  * The secret used to calculate the HMAC is shared inside `encrypted_secret` field with the data secret.
113
+
114
+ ### EncryptedMash::Version2
115
+
116
+ Uses public key cryptography (PKI) to encrypt a shared secret. Then this shared secret is used to encrypt the data using [GCM](http://en.wikipedia.org/wiki/Galois/Counter_Mode).
117
+
118
+ * This protocol version is based on the [Chef 12 Encrypted Data Bags Version 3 implementation](https://github.com/opscode/chef/pull/1591).
119
+ * To use it, the following **special requirements** must be met:
120
+ * Ruby `>= 2`.
121
+ * OpenSSL `>= 1.0.1`.
122
+ * This implementation can be improved, is not optimized either for performance or for space.
123
+ * Every time the `EncryptedAttribute` is updated, all the shared secrets are regenerated.
124
+
125
+ #### EncryptedMash::Version2 Structure
126
+
127
+ If you try to read this encrypted attribute structure, you can see a *Mash* attribute with the following content:
128
+
129
+ ```
130
+ EncryptedMash
131
+ ├── chef_type: "encrypted_attribute" (string).
132
+ ├── x_json_class: The used `EncryptedMash` version class name (string).
133
+ ├── encrypted_data
134
+ │ ├── cipher: The used PKI algorithm, "aes-256-gcm" (string).
135
+ │ ├── data: PKI encrypted data (base64).
136
+ │ ├── auth_tag: GCM authentication tag (base64).
137
+ │ └── iv: Initialization vector (in base64).
138
+ └── encrypted_secret
139
+ ├── pub_key_hash1: The shared secret encrypted for the public key 1 (base64).
140
+ ├── pub_key_hash2: The shared secret encrypted for the public key 2 (base64).
141
+ └── ...
142
+ ```
143
+
144
+ * `x_json_class` field is used, with the `x_` prefix, to be easily integrated with Chef in the future.
145
+
146
+ ##### EncryptedMash[encrypted_data][data]
147
+
148
+ The data inside `encrypted_data` is symmetrically encrypted using the secret shared key. The data is converted to *JSON* before the encryption, then encrypted and finally encoded in *base64*. By default, the `"aes-256-gcm"` algorithm is used for encryption.
149
+
150
+ After decryption, the *JSON* has the following structure:
151
+
152
+ ```
153
+ └── encrypted_data
154
+ └── data (symmetrically encrypted JSON in base64)
155
+ └── content: attribute content as a Mash.
156
+ ```
157
+
158
+ * In the future, this structure may contain some metadata like default configuration values.
159
+
160
+ ##### EncryptedMash[encrypted_secret][pub_key_hash1]
161
+
162
+ The `public_key_hash1` key value is the *SHA1* of the public key used for encryption.
163
+
164
+ Its content is the encrypted shared secret in *raw*. The encryption is done using the *RSA* algorithm (PKI).
165
+
166
+ After decryption, you find the the shared secret in *raw* (in *Version1* this is a *JSON* in *base64*).
data/README.md CHANGED
@@ -19,6 +19,9 @@ Node clients with read access can be specified using a `client_search` query. In
19
19
  * Ruby `>= 1.9`
20
20
  * Chef Client `~> 11.4`
21
21
  * yajl-ruby `~> 1.1` (included with Chef)
22
+ * If you want to use protocol version 2 to use [GCM](http://en.wikipedia.org/wiki/Galois/Counter_Mode) (disabled by default):
23
+ * Ruby `>= 2`.
24
+ * OpenSSL `>= 1.0.1`.
22
25
 
23
26
  ## Usage in Recipes
24
27
 
@@ -41,7 +44,7 @@ require "chef-encrypted-attributes"
41
44
 
42
45
  Chef::Recipe.send(:include, Opscode::OpenSSL::Password) # include the #secure_password method
43
46
 
44
- if Chef::EncryptedAttribute.exists?(node["myapp"]["ftp_password"])
47
+ if Chef::EncryptedAttribute.exist?(node["myapp"]["ftp_password"])
45
48
  # update with the new keys
46
49
  Chef::EncryptedAttribute.update(node.set["myapp"]["ftp_password"])
47
50
 
@@ -69,7 +72,7 @@ require "chef-encrypted-attributes"
69
72
  # Allow all webapp nodes to read the attributes encrypted by me
70
73
  Chef::Config[:encrypted_attributes][:client_search] = "role:webapp"
71
74
 
72
- if Chef::EncryptedAttribute.exists?(node["myapp"]["encrypted_data"])
75
+ if Chef::EncryptedAttribute.exist?(node["myapp"]["encrypted_data"])
73
76
  # when can used #load here as above if we need the `encrypted_data` outside this `if`
74
77
 
75
78
  # update with the new keys
@@ -87,14 +90,14 @@ Then we can read this attribute from another allowed node (a `"role:webapp"` nod
87
90
  chef_gem "chef-encrypted-attributes"
88
91
  require "chef-encrypted-attributes"
89
92
 
90
- if Chef::EncryptedAttribute.exists_on_node?("random.example.com", ["myapp", "encrypted_data"])
93
+ if Chef::EncryptedAttribute.exist_on_node?("random.example.com", ["myapp", "encrypted_data"])
91
94
  data = Chef::EncryptedAttribute.load_from_node("random.example.com", ["myapp", "encrypted_data"])
92
95
 
93
96
  # use `data` for something here ...
94
97
  end
95
98
  ```
96
99
 
97
- **Note:** Be careful when using `#exists_on_node?` and `#load_from_node` and remember passing the attribute path to read as **Array of Strings** ~~instead of using `node[...]` (which points to the local node)~~.
100
+ **Note:** Be careful when using `#exist_on_node?` and `#load_from_node` and remember passing the attribute path to read as **Array of Strings** ~~instead of using `node[...]` (which points to the local node)~~.
98
101
 
99
102
  ### Example Using User Keys Data Bag
100
103
 
@@ -124,7 +127,7 @@ chef_users.delete("id") # remove the data bag "id" to avoid to confuse it with a
124
127
  Chef::Log.debug("Admin users able to read the Encrypted Attributes: #{chef_users.keys.inspect}")
125
128
  Chef::Config[:encrypted_attributes][:keys] = chef_users.values
126
129
 
127
- # if Chef::EncryptedAttribute.exists_on_node?(...)
130
+ # if Chef::EncryptedAttribute.exist_on_node?(...)
128
131
  # Chef::EncryptedAttribute.update(...)
129
132
  # else
130
133
  # node.set[...][...] = Chef::EncryptedAttribute.create(...)
@@ -254,7 +257,7 @@ For example:
254
257
  <td>&nbsp;</td>
255
258
  <td>--encrypted-attribute-version</td>
256
259
  <td>Encrypted Attribute protocol version to use</td>
257
- <td>"0", "1" <em>(default)</em></td>
260
+ <td>"0", "1" <em>(default)</em>, "2"</td>
258
261
  <td>create, edit, update</td>
259
262
  </tr>
260
263
  <tr>
@@ -298,18 +301,52 @@ For example:
298
301
 
299
302
  See the [INTERNAL.md](INTERNAL.md) file for a more low level documentation.
300
303
 
304
+ ## Using Signed Gems
305
+
306
+ The `chef-encrypted-attributes` gem is cryptographically signed by Onddo Labs's certificate, which identifies as *team@onddo.com*. You can obtain the official signature here:
307
+
308
+ https://raw.github.com/onddo/chef-encrypted-attributes/master/certs/team_onddo.crt
309
+
310
+ To be sure the gem you install has not been tampered with:
311
+
312
+ $ gem cert --add <(curl -Ls https://raw.github.com/onddo/chef-encrypted-attributes/master/certs/team_onddo.crt)
313
+ $ gem install chef-encrypted-attributes -P MediumSecurity
314
+
315
+ The *MediumSecurity* trust profile will verify signed gems, but allow the installation of unsigned dependencies. This is necessary because not all of `chef-encrypted-attributes`'s dependencies are signed, so we cannot use *HighSecurity*.
316
+
317
+ We recommend to remove our certificate after the gem has been successfully verified and installed:
318
+
319
+ $ gem cert --remove '/cn=team/dc=onddo/dc=com'
320
+
321
+ ## Security Notes
322
+
323
+ All the cryptographic systems and algorithms used by `chef-encrypted-attributes` are carefully described in the [internal documentation](INTERNAL.md) for public review. The code was originally based on *Encrypted Data Bags* and [chef-vault](https://github.com/Nordstrom/chef-vault) implementations, then improved.
324
+
325
+ Still, this gem should be considered experimental until audited by professional cryptographers.
326
+
327
+ ## Reporting Security Problems
328
+
329
+ If you have discovered a bug in `chef-encrypted-attributes` of a sensitive nature, i.e. one which can compromise the security of `chef-encrypted-attributes` users, you can report it securely by sending a GPG encrypted message. Please use the following key:
330
+
331
+ https://raw.github.com/onddo/chef-encrypted-attributes/master/zuazo.gpg
332
+
333
+ The key fingerprint is (or should be):
334
+
335
+ 8EFA 5B17 7275 5F1F 42B2 26B4 8E18 8B67 9DE1 9468
336
+
337
+ ## Testing
338
+
339
+ See [TESTING.md](TESTING.md).
340
+
301
341
  ## Contributing
302
342
 
303
- 1. Fork the repository on Github.
304
- 2. Create a named feature branch (like `add_component_x`).
305
- 3. Write tests for your change.
306
- 4. Write your change.
307
- 5. Run the tests, ensuring they all pass (try as much as possible not to reduce coverage).
308
- 6. Submit a Pull Request using Github.
343
+ Please do not hesitate to [open an issue](https://github.com/onddo/chef-encrypted-attributes/issues/new) with any questions or problems.
344
+
345
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
309
346
 
310
- See the [TESTING.md](TESTING.md) file to know how to run the tests properly.
347
+ ## TODO
311
348
 
312
- You can also see the [TODO.md](TODO.md) file if you're looking for inspiration.
349
+ See [TODO.md](TODO.md).
313
350
 
314
351
  ## License and Author
315
352
 
data/TESTING.md CHANGED
@@ -17,28 +17,34 @@
17
17
  You can run some simple benchmarks, not at all realistic:
18
18
 
19
19
  $ rspec spec/benchmark/*
20
- user system total real
21
- Local EncryptedAttribute read (v=0) 0.430000 0.000000 0.430000 ( 0.428674)
22
- Local EncryptedAttribute read (v=1) 0.420000 0.010000 0.430000 ( 0.433974)
23
- Local EncryptedDataBag read 0.010000 0.000000 0.010000 ( 0.011308)
24
- Remote EncryptedAttribute read (v=0) 1.510000 0.050000 1.560000 ( 1.567265)
25
- Remote EncryptedAttribute read (v=1) 1.500000 0.060000 1.560000 ( 1.570522)
26
- Remote EncryptedDataBag read 0.930000 0.050000 0.980000 ( 0.989036)
27
- Local EncryptedAttribute write (v=0) 0.060000 0.000000 0.060000 ( 0.059326)
28
- Local EncryptedAttribute write (v=1) 0.090000 0.000000 0.090000 ( 0.090731)
29
- Local EncryptedDataBag write 0.020000 0.000000 0.020000 ( 0.016910)
30
- Remote EncryptedAttribute write (v=0) 1.140000 0.080000 1.220000 ( 1.218726)
31
- Remote EncryptedAttribute write (v=1) 1.180000 0.070000 1.250000 ( 1.259015)
32
- Remote EncryptedDataBag write 2.050000 0.150000 2.200000 ( 2.208485)
33
- Local EncryptedAttribute read/write (v=0) 0.560000 0.010000 0.570000 ( 0.569158)
34
- Local EncryptedAttribute read/write (v=1) 0.570000 0.010000 0.580000 ( 0.589196)
35
- Local EncryptedDataBag read/write 0.980000 0.050000 1.030000 ( 1.044585)
36
- Remote EncryptedAttribute read/write (v=0) 2.720000 0.130000 2.850000 ( 2.859289)
37
- Remote EncryptedAttribute read/write (v=1) 2.660000 0.130000 2.790000 ( 2.813521)
38
- Remote EncryptedDataBag read/write 3.110000 0.190000 3.300000 ( 3.301053)
20
+ user system total real
21
+ Local EncryptedAttribute read (v=0) 0.410000 0.000000 0.410000 ( 0.417956)
22
+ Local EncryptedAttribute read (v=1) 0.390000 0.010000 0.400000 ( 0.398934)
23
+ Local EncryptedAttribute read (v=2) 0.420000 0.000000 0.420000 ( 0.420211)
24
+ Local EncryptedDataBag read 0.010000 0.000000 0.010000 ( 0.011614)
25
+ Remote EncryptedAttribute read (v=0) 1.480000 0.070000 1.550000 ( 1.549856)
26
+ Remote EncryptedAttribute read (v=1) 1.440000 0.060000 1.500000 ( 1.486179)
27
+ Remote EncryptedAttribute read (v=2) 1.510000 0.060000 1.570000 ( 1.561124)
28
+ Remote EncryptedDataBag read 0.970000 0.060000 1.030000 ( 1.012260)
29
+ Local EncryptedAttribute write (v=0) 0.090000 0.000000 0.090000 ( 0.089210)
30
+ Local EncryptedAttribute write (v=1) 0.090000 0.000000 0.090000 ( 0.090442)
31
+ Local EncryptedAttribute write (v=2) 0.060000 0.000000 0.060000 ( 0.055671)
32
+ Local EncryptedDataBag write 0.000000 0.000000 0.000000 ( 0.012315)
33
+ Remote EncryptedAttribute write (v=0) 1.140000 0.050000 1.190000 ( 1.179739)
34
+ Remote EncryptedAttribute write (v=1) 1.090000 0.090000 1.180000 ( 1.161603)
35
+ Remote EncryptedAttribute write (v=2) 1.120000 0.060000 1.180000 ( 1.159668)
36
+ Remote EncryptedDataBag write 2.080000 0.090000 2.170000 ( 2.146914)
37
+ Local EncryptedAttribute read/write (v=0) 0.550000 0.000000 0.550000 ( 0.555362)
38
+ Local EncryptedAttribute read/write (v=1) 0.540000 0.010000 0.550000 ( 0.550447)
39
+ Local EncryptedAttribute read/write (v=2) 0.570000 0.000000 0.570000 ( 0.576107)
40
+ Local EncryptedDataBag read/write 0.950000 0.050000 1.000000 ( 0.979758)
41
+ Remote EncryptedAttribute read/write (v=0) 2.670000 0.100000 2.770000 ( 2.746405)
42
+ Remote EncryptedAttribute read/write (v=1) 2.700000 0.090000 2.790000 ( 2.758583)
43
+ Remote EncryptedAttribute read/write (v=2) 2.660000 0.110000 2.770000 ( 2.752359)
44
+ Remote EncryptedDataBag read/write 3.030000 0.140000 3.170000 ( 3.125538)
39
45
 
40
- Finished in 22 seconds
41
- 18 examples, 0 failures
46
+ Finished in 28.01 seconds
47
+ 24 examples, 0 failures
42
48
 
43
49
  These benchmarks run 100 passes for each test.
44
50
 
data/TODO.md CHANGED
@@ -9,9 +9,9 @@ TODO
9
9
  * Add Ruby `1.8` support?
10
10
  * Document the Ruby code.
11
11
  * Add more info/debug prints.
12
- * Space-optimized `EncryptedMash::Version2` class.
12
+ * Space-optimized `EncryptedMash::Version3` class.
13
13
  * Tests: Add test helper functions (key generation, ApiClients including priv keys, Node creation...).
14
- * Tests: Add more tests for `EncryptedMash::Version1`.
14
+ * Tests: Add more tests for `EncryptedMash::Version1` and `EncryptedMash::Version2`.
15
15
  * Tests: Add unit tests for `EncryptedAttribute`.
16
16
  * Tests: Add unit tests for all knife commands.
17
17
  * Tests: Tests `raise_error` always include regex.
@@ -28,6 +28,7 @@ require 'chef/encrypted_attribute/remote_clients'
28
28
  require 'chef/encrypted_attribute/remote_users'
29
29
  require 'chef/encrypted_attribute/encrypted_mash/version0'
30
30
  require 'chef/encrypted_attribute/encrypted_mash/version1'
31
+ require 'chef/encrypted_attribute/encrypted_mash/version2'
31
32
 
32
33
  Chef::Config[:encrypted_attributes] = Mash.new unless Chef::Config[:encrypted_attributes].kind_of?(Hash)
33
34
 
@@ -196,9 +197,9 @@ class Chef
196
197
  result
197
198
  end
198
199
 
199
- def self.exists?(hs)
200
+ def self.exist?(hs)
200
201
  Chef::Log.debug("#{self.class.name}: Checking if Encrypted Attribute exists here: #{hs.to_s}")
201
- result = EncryptedMash.exists?(hs)
202
+ result = EncryptedMash.exist?(hs)
202
203
  if result
203
204
  Chef::Log.debug("#{self.class.name}: Encrypted Attribute found.")
204
205
  else
@@ -207,11 +208,21 @@ class Chef
207
208
  result
208
209
  end
209
210
 
210
- def self.exists_on_node?(name, attr_ary, c={})
211
+ def self.exists?(*args)
212
+ Chef::Log.warn("#{self.name}.exists? is deprecated in favor of #{self.name}.exist?.")
213
+ exist?(*args)
214
+ end
215
+
216
+ def self.exist_on_node?(name, attr_ary, c={})
211
217
  Chef::Log.debug("#{self.class.name}: Checking if Remote Encrypted Attribute exists on #{name}")
212
218
  remote_node = RemoteNode.new(name)
213
219
  node_attr = remote_node.load_attribute(attr_ary, self.config(c).partial_search)
214
- Chef::EncryptedAttribute.exists?(node_attr)
220
+ Chef::EncryptedAttribute.exist?(node_attr)
221
+ end
222
+
223
+ def self.exists_on_node?(*args)
224
+ Chef::Log.warn("#{self.name}.exists_on_node? is deprecated in favor of #{self.name}.exist_on_node?.")
225
+ exist_on_node?(*args)
215
226
  end
216
227
 
217
228
  end
@@ -0,0 +1,36 @@
1
+ #
2
+ # Author:: Xabier de Zuazo (<xabier@onddo.com>)
3
+ # Copyright:: Copyright (c) 2014 Onddo Labs, SL. (www.onddo.com)
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/encrypted_attribute/exceptions'
20
+
21
+ class Chef
22
+ class EncryptedAttribute
23
+ module Assertions
24
+
25
+ def assert_aead_requirements_met!(algorithm)
26
+ unless OpenSSL::Cipher.method_defined?(:auth_data=)
27
+ raise RequirementsFailure, 'The used Encrypted Attributes protocol version requires Ruby >= 1.9'
28
+ end
29
+ unless OpenSSL::Cipher.ciphers.include?(algorithm)
30
+ raise RequirementsFailure, "The used Encrypted Attributes protocol version requires an OpenSSL version with \"#{algorithm}\" algorithm support"
31
+ end
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -45,13 +45,18 @@ class Chef
45
45
  end
46
46
  end
47
47
 
48
- def self.exists?(enc_hs)
48
+ def self.exist?(enc_hs)
49
49
  enc_hs.kind_of?(Hash) and
50
50
  enc_hs.has_key?(JSON_CLASS) and
51
51
  enc_hs[JSON_CLASS] =~ /^#{Regexp.escape(Module.nesting[1].name)}/ and
52
52
  enc_hs.has_key?(CHEF_TYPE) and enc_hs[CHEF_TYPE] == CHEF_TYPE_VALUE
53
53
  end
54
54
 
55
+ def self.exists?(*args)
56
+ Chef::Log.warn("#{self.name}.exists? is deprecated in favor of #{self.name}.exist?.")
57
+ exist?(*args)
58
+ end
59
+
55
60
  def self.create(version)
56
61
  klass = version_klass(version)
57
62
  klass.send(:new)
@@ -69,7 +74,7 @@ class Chef
69
74
 
70
75
  # Update the EncryptedMash from Hash
71
76
  def update_from!(enc_hs)
72
- unless self.class.exists?(enc_hs)
77
+ unless self.class.exist?(enc_hs)
73
78
  raise UnacceptableEncryptedAttributeFormat, 'Trying to construct invalid encrypted attribute. Maybe it is not encrypted?'
74
79
  end
75
80
  enc_hs = enc_hs.dup
@@ -92,8 +92,8 @@ class Chef
92
92
  begin
93
93
  cipher = OpenSSL::Cipher.new(algo)
94
94
  cipher.encrypt
95
- enc_value['iv'] = Base64.encode64(cipher.iv = cipher.random_iv)
96
95
  enc_value['secret'] = Base64.encode64(cipher.key = cipher.random_key)
96
+ enc_value['iv'] = Base64.encode64(cipher.iv = cipher.random_iv)
97
97
  enc_data = cipher.update(value) + cipher.final
98
98
  rescue OpenSSL::Cipher::CipherError => e
99
99
  raise EncryptionFailure, "#{e.class.name}: #{e.to_s}"
@@ -105,8 +105,9 @@ class Chef
105
105
  def symmetric_decrypt_value(enc_value, algo=SYMM_ALGORITHM)
106
106
  cipher = OpenSSL::Cipher.new(enc_value['cipher'] || algo) # TODO maybe it's better to ignore [cipher] ?
107
107
  cipher.decrypt
108
- cipher.iv = Base64.decode64(enc_value['iv'])
108
+ # We must set key before iv: https://bugs.ruby-lang.org/issues/8221
109
109
  cipher.key = Base64.decode64(enc_value['secret'])
110
+ cipher.iv = Base64.decode64(enc_value['iv'])
110
111
  cipher.update(Base64.decode64(enc_value['data'])) + cipher.final
111
112
  rescue OpenSSL::Cipher::CipherError => e
112
113
  raise DecryptionFailure, "#{e.class.name}: #{e.to_s}"
@@ -0,0 +1,104 @@
1
+ #
2
+ # Author:: Xabier de Zuazo (<xabier@onddo.com>)
3
+ # Copyright:: Copyright (c) 2014 Onddo Labs, SL. (www.onddo.com)
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'chef/encrypted_attribute/encrypted_mash/version0'
20
+ require 'chef/encrypted_attribute/encrypted_mash/version1'
21
+ require 'chef/encrypted_attribute/assertions'
22
+ require 'chef/encrypted_attribute/exceptions'
23
+
24
+ # Version2 format: using RSA with a shared secret and GCM
25
+ class Chef
26
+ class EncryptedAttribute
27
+ class EncryptedMash
28
+ class Version2 < Chef::EncryptedAttribute::EncryptedMash::Version1
29
+ include Chef::EncryptedAttribute::Assertions
30
+
31
+ ALGORITHM = 'aes-256-gcm'
32
+
33
+ def initialize(enc_hs=nil)
34
+ assert_aead_requirements_met!(ALGORITHM)
35
+ super
36
+ end
37
+
38
+ def encrypt(value, public_keys)
39
+ value_json = json_encode(value)
40
+ public_keys = parse_public_keys(public_keys)
41
+ # encrypt the data
42
+ encrypted_data = symmetric_encrypt_value(value_json)
43
+ secret = encrypted_data.delete('secret') # should no include the secret in clear
44
+ self['encrypted_data'] = encrypted_data
45
+ # encrypt the shared secret
46
+ self['encrypted_secret'] = rsa_encrypt_multi_key(secret, public_keys)
47
+ self
48
+ end
49
+
50
+ def decrypt(key)
51
+ key = parse_decryption_key(key)
52
+ enc_value = self['encrypted_data'].dup
53
+ # decrypt the shared secret
54
+ enc_value['secret'] = rsa_decrypt_multi_key(self['encrypted_secret'], key)
55
+ # decrypt the data
56
+ value_json = symmetric_decrypt_value(enc_value)
57
+ json_decode(value_json)
58
+ end
59
+
60
+ protected
61
+
62
+ def encrypted?
63
+ Version0.instance_method(:encrypted?).bind(self).call and
64
+ self['encrypted_data'].has_key?('iv') and
65
+ self['encrypted_data']['iv'].kind_of?(String) and
66
+ self['encrypted_data'].has_key?('auth_tag') and
67
+ self['encrypted_data']['auth_tag'].kind_of?(String) and
68
+ self['encrypted_data'].has_key?('data') and
69
+ self['encrypted_data']['data'].kind_of?(String) and
70
+ self['encrypted_secret'].kind_of?(Hash)
71
+ end
72
+
73
+ def symmetric_encrypt_value(value, algo=ALGORITHM)
74
+ enc_value = Mash.new({ 'cipher' => algo })
75
+ begin
76
+ cipher = OpenSSL::Cipher.new(algo)
77
+ cipher.encrypt
78
+ enc_value['secret'] = cipher.key = cipher.random_key
79
+ enc_value['iv'] = Base64.encode64(cipher.iv = cipher.random_iv)
80
+ enc_data = cipher.update(value) + cipher.final
81
+ enc_value['auth_tag'] = Base64.encode64(cipher.auth_tag)
82
+ rescue OpenSSL::Cipher::CipherError => e
83
+ raise EncryptionFailure, "#{e.class.name}: #{e.to_s}"
84
+ end
85
+ enc_value['data'] = Base64.encode64(enc_data)
86
+ enc_value
87
+ end
88
+
89
+ def symmetric_decrypt_value(enc_value, algo=ALGORITHM)
90
+ cipher = OpenSSL::Cipher.new(enc_value['cipher'] || algo) # TODO maybe it's better to ignore [cipher] ?
91
+ cipher.decrypt
92
+ # We must set key before iv: https://bugs.ruby-lang.org/issues/8221
93
+ cipher.key = enc_value['secret']
94
+ cipher.iv = Base64.decode64(enc_value['iv'])
95
+ cipher.auth_tag = Base64.decode64(enc_value['auth_tag'])
96
+ cipher.update(Base64.decode64(enc_value['data'])) + cipher.final
97
+ rescue OpenSSL::Cipher::CipherError => e
98
+ raise DecryptionFailure, "#{e.class.name}: #{e.to_s}"
99
+ end
100
+
101
+ end
102
+ end
103
+ end
104
+ end
@@ -19,6 +19,7 @@
19
19
  class Chef
20
20
  class EncryptedAttribute
21
21
 
22
+ class RequirementsFailure < StandardError; end
22
23
  class UnsupportedEncryptedAttributeFormat < StandardError; end
23
24
  class UnacceptableEncryptedAttributeFormat < StandardError; end
24
25
  class DecryptionFailure < StandardError; end
@@ -18,6 +18,6 @@
18
18
 
19
19
  class Chef
20
20
  class EncryptedAttribute
21
- VERSION = '0.1.1'
21
+ VERSION = '0.2.0'
22
22
  end
23
23
  end
@@ -51,7 +51,7 @@ class Chef
51
51
  attr_ary = attribute_path_to_ary(attr_path)
52
52
 
53
53
  # check if the encrypted attribute already exists
54
- if Chef::EncryptedAttribute.exists_on_node?(node_name, attr_ary)
54
+ if Chef::EncryptedAttribute.exist_on_node?(node_name, attr_ary)
55
55
  ui.fatal('Encrypted attribute already exists')
56
56
  exit 1
57
57
  end
@@ -54,7 +54,7 @@ class Chef
54
54
  end
55
55
 
56
56
  attr_ary = attribute_path_to_ary(attr_path)
57
- if Chef::EncryptedAttribute.exists_on_node?(node_name, attr_ary)
57
+ if Chef::EncryptedAttribute.exist_on_node?(node_name, attr_ary)
58
58
  # TODO move this to lib/EncryptedAttribute
59
59
  unless config[:force] # try to read the attribute
60
60
  Chef::EncryptedAttribute.load_from_node(node_name, attr_ary)
@@ -51,7 +51,7 @@ class Chef
51
51
  attr_ary = attribute_path_to_ary(attr_path)
52
52
 
53
53
  # check if the encrypted attribute already exists
54
- unless Chef::EncryptedAttribute.exists_on_node?(node_name, attr_ary)
54
+ unless Chef::EncryptedAttribute.exist_on_node?(node_name, attr_ary)
55
55
  ui.fatal('Encrypted attribute not found')
56
56
  exit 1
57
57
  end
@@ -47,7 +47,7 @@ class Chef
47
47
 
48
48
  attr_ary = attribute_path_to_ary(attr_path)
49
49
 
50
- unless Chef::EncryptedAttribute.exists_on_node?(node_name, attr_ary)
50
+ unless Chef::EncryptedAttribute.exist_on_node?(node_name, attr_ary)
51
51
  ui.fatal('Encrypted attribute not found')
52
52
  exit 1
53
53
  end
@@ -46,7 +46,7 @@ class Chef
46
46
  attr_ary = attribute_path_to_ary(attr_path)
47
47
 
48
48
  # check if the encrypted attribute already exists
49
- unless Chef::EncryptedAttribute.exists_on_node?(node_name, attr_ary)
49
+ unless Chef::EncryptedAttribute.exist_on_node?(node_name, attr_ary)
50
50
  ui.fatal('Encrypted attribute not found')
51
51
  exit 1
52
52
  end
@@ -18,7 +18,7 @@
18
18
 
19
19
  require 'rspec/autorun'
20
20
  require 'chef_zero/rspec'
21
- require 'chef_encrypted_attributes'
21
+ require 'chef-encrypted-attributes'
22
22
 
23
23
  require 'support/silent_formatter'
24
24
  RSpec.configure do |config|
@@ -17,7 +17,7 @@
17
17
  #
18
18
 
19
19
  require 'simplecov'
20
- if ENV['TRAVIS'] and RUBY_VERSION >= '1.9.3'
20
+ if ENV['TRAVIS'] and RUBY_VERSION >= '2.0'
21
21
  require 'coveralls'
22
22
  SimpleCov.formatter = Coveralls::SimpleCov::Formatter
23
23
  end
@@ -30,9 +30,17 @@ require 'chef/exceptions'
30
30
 
31
31
  require 'rspec/autorun'
32
32
 
33
+ require 'support/platform_helpers'
34
+
33
35
  RSpec.configure do |config|
34
36
  config.order = 'random'
35
37
 
36
- config.color_enabled = true
38
+ config.color = true
37
39
  config.tty = true
40
+
41
+ config.filter_run_excluding :ruby_gte_19 => true unless ruby_gte_19?
42
+ config.filter_run_excluding :ruby_gte_20 => true unless ruby_gte_20?
43
+ config.filter_run_excluding :ruby_gte_20_and_openssl_gte_101 => true unless (ruby_gte_20? && openssl_gte_101?)
44
+ config.filter_run_excluding :openssl_lt_101 => true unless openssl_lt_101?
45
+ config.filter_run_excluding :ruby_lt_20 => true unless ruby_lt_20?
38
46
  end
metadata CHANGED
@@ -1,14 +1,36 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chef-encrypted-attributes
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Onddo Labs, SL.
8
8
  autorequire:
9
9
  bindir: bin
10
- cert_chain: []
11
- date: 2014-05-23 00:00:00.000000000 Z
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIDYDCCAkigAwIBAgIBATANBgkqhkiG9w0BAQUFADA7MQ0wCwYDVQQDDAR0ZWFt
14
+ MRUwEwYKCZImiZPyLGQBGRYFb25kZG8xEzARBgoJkiaJk/IsZAEZFgNjb20wHhcN
15
+ MTQwODExMDkxNDAzWhcNMTUwODExMDkxNDAzWjA7MQ0wCwYDVQQDDAR0ZWFtMRUw
16
+ EwYKCZImiZPyLGQBGRYFb25kZG8xEzARBgoJkiaJk/IsZAEZFgNjb20wggEiMA0G
17
+ CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCrbcM9ZsRca5gEkpftR/J/Rem9JTAK
18
+ 0cKHuEAKgp0QvHAceDRJNJ6LoXfeXP86fPL3kpHNQdbHFW/MGsynjeWg/c/8VbAR
19
+ W0L6MBUoBuSBN91GBoqHpka1qhMHA6QnUpUHFCOUhoA5oweZ8kDsHh/W2n//vZwr
20
+ DBcnf4wRzQeZl/I+Ral6gKZSImtMyo0W7TYQT2rMbvms0LoNMKZQ6Gpw9B2NoYtH
21
+ FYBt78LB771jRc/j5Sk9O2rm/Tnvl6HW2Pb6arwk9pd0A7KVYjJzmaBF5aqOMTml
22
+ ZDgelv7lLJyIQSASKnusmTlFehppplTHWQKfev6jfKXCepS/cD5EH1wfAgMBAAGj
23
+ bzBtMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBQzPM4ZVw+kEtQm
24
+ 4eA7AyE6s0gIUDAZBgNVHREEEjAQgQ50ZWFtQG9uZGRvLmNvbTAZBgNVHRIEEjAQ
25
+ gQ50ZWFtQG9uZGRvLmNvbTANBgkqhkiG9w0BAQUFAAOCAQEAZNdmm2OCr8wVxtj8
26
+ z9gnGVBWpHqE0+mPBsgsueOAhNzoqphOUGMn9JWvOQU3TwiUPHJN9QiOT92OpD0Z
27
+ UKN6ixyaqPhegRqGQQGTOC7d+l+E+mab3m6f96MyCxNuts8vEFqubs5rd+1E/NDb
28
+ ZpX7/1KENUo06s8Rpro8pPXh+CL8+177gcfkXzXBElXR6r5Tf8VSxrFPeLIRrdzs
29
+ AkPvQPEiZbg78ZKmVjcuokhXi1bzvjGVI0lMzW/JBO8GvjUyh4hl6HAfOgXMXpaz
30
+ cYe8PqNEkky7ugvF4zU3sB6TW+96XasuwDv1uJmyr35LF15U6Cs83+osMbAKJTmG
31
+ /vqKzw==
32
+ -----END CERTIFICATE-----
33
+ date: 2014-08-12 00:00:00.000000000 Z
12
34
  dependencies:
13
35
  - !ruby/object:Gem::Dependency
14
36
  name: yajl-ruby
@@ -42,30 +64,30 @@ dependencies:
42
64
  name: rake
43
65
  requirement: !ruby/object:Gem::Requirement
44
66
  requirements:
45
- - - '>='
67
+ - - ~>
46
68
  - !ruby/object:Gem::Version
47
- version: '0'
69
+ version: '10.0'
48
70
  type: :development
49
71
  prerelease: false
50
72
  version_requirements: !ruby/object:Gem::Requirement
51
73
  requirements:
52
- - - '>='
74
+ - - ~>
53
75
  - !ruby/object:Gem::Version
54
- version: '0'
76
+ version: '10.0'
55
77
  - !ruby/object:Gem::Dependency
56
78
  name: chef-zero
57
79
  requirement: !ruby/object:Gem::Requirement
58
80
  requirements:
59
- - - '>='
81
+ - - ~>
60
82
  - !ruby/object:Gem::Version
61
- version: '0'
83
+ version: '2.0'
62
84
  type: :development
63
85
  prerelease: false
64
86
  version_requirements: !ruby/object:Gem::Requirement
65
87
  requirements:
66
- - - '>='
88
+ - - ~>
67
89
  - !ruby/object:Gem::Version
68
- version: '0'
90
+ version: '2.0'
69
91
  - !ruby/object:Gem::Dependency
70
92
  name: rspec-core
71
93
  requirement: !ruby/object:Gem::Requirement
@@ -112,67 +134,69 @@ dependencies:
112
134
  name: coveralls
113
135
  requirement: !ruby/object:Gem::Requirement
114
136
  requirements:
115
- - - '>='
137
+ - - ~>
116
138
  - !ruby/object:Gem::Version
117
- version: '0'
139
+ version: '0.7'
118
140
  type: :development
119
141
  prerelease: false
120
142
  version_requirements: !ruby/object:Gem::Requirement
121
143
  requirements:
122
- - - '>='
144
+ - - ~>
123
145
  - !ruby/object:Gem::Version
124
- version: '0'
146
+ version: '0.7'
125
147
  - !ruby/object:Gem::Dependency
126
148
  name: simplecov
127
149
  requirement: !ruby/object:Gem::Requirement
128
150
  requirements:
129
- - - '>='
151
+ - - ~>
130
152
  - !ruby/object:Gem::Version
131
- version: '0'
153
+ version: '0.9'
132
154
  type: :development
133
155
  prerelease: false
134
156
  version_requirements: !ruby/object:Gem::Requirement
135
157
  requirements:
136
- - - '>='
158
+ - - ~>
137
159
  - !ruby/object:Gem::Version
138
- version: '0'
160
+ version: '0.9'
139
161
  description: Chef plugin to add Node encrypted attributes support using client keys
140
162
  email: team@onddo.com
141
163
  executables: []
142
164
  extensions: []
143
165
  extra_rdoc_files: []
144
166
  files:
145
- - Rakefile
146
- - LICENSE
147
- - README.md
148
167
  - API.md
168
+ - CHANGELOG.md
149
169
  - INTERNAL.md
170
+ - LICENSE
171
+ - README.md
172
+ - Rakefile
150
173
  - TESTING.md
151
174
  - TODO.md
152
- - CHANGELOG.md
153
175
  - lib/chef-encrypted-attributes.rb
154
- - lib/chef/knife/core/encrypted_attribute_editor_options.rb
155
- - lib/chef/knife/core/config.rb
156
- - lib/chef/knife/encrypted_attribute_create.rb
157
- - lib/chef/knife/encrypted_attribute_show.rb
158
- - lib/chef/knife/encrypted_attribute_update.rb
159
- - lib/chef/knife/encrypted_attribute_delete.rb
160
- - lib/chef/knife/encrypted_attribute_edit.rb
161
- - lib/chef/encrypted_attribute/remote_users.rb
176
+ - lib/chef/encrypted_attribute.rb
177
+ - lib/chef/encrypted_attribute/assertions.rb
162
178
  - lib/chef/encrypted_attribute/cache_lru.rb
163
- - lib/chef/encrypted_attribute/search_helper.rb
164
179
  - lib/chef/encrypted_attribute/config.rb
165
- - lib/chef/encrypted_attribute/exceptions.rb
180
+ - lib/chef/encrypted_attribute/encrypted_mash.rb
166
181
  - lib/chef/encrypted_attribute/encrypted_mash/version0.rb
167
182
  - lib/chef/encrypted_attribute/encrypted_mash/version1.rb
183
+ - lib/chef/encrypted_attribute/encrypted_mash/version2.rb
184
+ - lib/chef/encrypted_attribute/exceptions.rb
168
185
  - lib/chef/encrypted_attribute/local_node.rb
169
- - lib/chef/encrypted_attribute/encrypted_mash.rb
170
- - lib/chef/encrypted_attribute/version.rb
171
186
  - lib/chef/encrypted_attribute/remote_clients.rb
172
187
  - lib/chef/encrypted_attribute/remote_node.rb
173
- - lib/chef/encrypted_attribute.rb
174
- - spec/integration_helper.rb
188
+ - lib/chef/encrypted_attribute/remote_users.rb
189
+ - lib/chef/encrypted_attribute/search_helper.rb
190
+ - lib/chef/encrypted_attribute/version.rb
191
+ - lib/chef/knife/core/config.rb
192
+ - lib/chef/knife/core/encrypted_attribute_editor_options.rb
193
+ - lib/chef/knife/encrypted_attribute_create.rb
194
+ - lib/chef/knife/encrypted_attribute_delete.rb
195
+ - lib/chef/knife/encrypted_attribute_edit.rb
196
+ - lib/chef/knife/encrypted_attribute_show.rb
197
+ - lib/chef/knife/encrypted_attribute_update.rb
175
198
  - spec/benchmark_helper.rb
199
+ - spec/integration_helper.rb
176
200
  - spec/spec_helper.rb
177
201
  homepage: http://onddo.github.io/chef-encrypted-attributes
178
202
  licenses:
@@ -194,7 +218,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
194
218
  version: '0'
195
219
  requirements: []
196
220
  rubyforge_project:
197
- rubygems_version: 2.0.6
221
+ rubygems_version: 2.2.2
198
222
  signing_key:
199
223
  specification_version: 4
200
224
  summary: Chef Encrypted Attributes
Binary file