chef-encrypted-attributes 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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