attr_keyring 0.5.4 → 0.6.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
  SHA256:
3
- metadata.gz: 2af6cd51b5b53137780ac69ddf709a3e0b6f31c4381e0425be63579fce0be85d
4
- data.tar.gz: 6c3ac6b0b56ecceeecce9e098d8424f5659d8c5d75d26201f87cc669accfe478
3
+ metadata.gz: ee86bffa6e27d8523d423c8e8bfefa90a15733fdda28a60155a71fd67a794917
4
+ data.tar.gz: 340174053331dc2447f2d980ec97d3a4b429f09ed8052ef991908a2de90ad872
5
5
  SHA512:
6
- metadata.gz: 151441f7e84eb4145206b96635b07b2e55f83eabe7f855fe03a0e880d65030c9165a5bf09646066ecfd6ef28e0de6b90be8449831858893cf325b6d8b2068027
7
- data.tar.gz: 4b7bb84692b69a794ac42ca1e5674f6c00659de1c77dcfa0f00111f6d6537719e9cf2ed93122562b79d8f9b8a47ff572b0330410c724065c4d5cfab22d8efa28
6
+ metadata.gz: 3f4d6593813ab7f4e2e8672108d40b44f068c70fc8ccc59c83eae84bba0851ba3e6996633ef66b87b83f2f5c93038b573062b446e93045902623aba5dbcd653c
7
+ data.tar.gz: e2379acd02af797c2dcf900957404eb6f8552fe1b11f472c4ea8b5a140b06fbda868eec13e65fa998cc27d11274435e329aec3a12b59f516ecc1dddf779b4f4e
@@ -8,7 +8,7 @@ AllCops:
8
8
  Metrics/AbcSize:
9
9
  Enabled: false
10
10
 
11
- Metrics/LineLength:
11
+ Layout/LineLength:
12
12
  Exclude:
13
13
  - test/**/*
14
14
 
data/README.md CHANGED
@@ -10,7 +10,10 @@
10
10
 
11
11
  N.B.: attr_keyring is *not* for encrypting passwords--for that, you should use something like [bcrypt](https://github.com/codahale/bcrypt-ruby). It's meant for encrypting sensitive data you will need to access in plain text (e.g. storing OAuth token from users). Passwords do not fall in that category.
12
12
 
13
- This library is heavily inspired by [attr_vault](https://github.com/uhoh-itsmaciek/attr_vault), and can read encrypted messages if you encode them in base64 (e.g. `Base64.strict_encode64(encrypted_by_attr_vault)`).
13
+ This library is heavily inspired by
14
+ [attr_vault](https://github.com/uhoh-itsmaciek/attr_vault), and can read
15
+ encrypted messages if you encode them in base64
16
+ (e.g. `Base64.strict_encode64(encrypted_by_attr_vault)`).
14
17
 
15
18
  ## Installation
16
19
 
@@ -36,7 +39,10 @@ Or install it yourself as:
36
39
  gem "attr_keyring"
37
40
  require "keyring"
38
41
 
39
- keyring = Keyring.new("1" => "uDiMcWVNTuz//naQ88sOcN+E40CyBRGzGTT7OkoBS6M=")
42
+ keyring = Keyring.new(
43
+ {"1" => "uDiMcWVNTuz//naQ88sOcN+E40CyBRGzGTT7OkoBS6M="},
44
+ digest_salt: "<custom salt>"
45
+ )
40
46
 
41
47
  # STEP 1: Encrypt message using latest encryption key.
42
48
  encrypted, keyring_id, digest = keyring.encrypt("super secret")
@@ -52,30 +58,37 @@ puts "✉️ #{decrypted}"
52
58
 
53
59
  #### Change encryption algorithm
54
60
 
55
- You can choose between `AES-128-CBC`, `AES-192-CBC` and `AES-256-CBC`. By default, `AES-128-CBC` will be used.
56
-
57
- To specify the encryption algorithm, set the `encryption` option. The following example uses `AES-256-CBC`.
61
+ You can choose between `AES-128-CBC`, `AES-192-CBC` and `AES-256-CBC`. By
62
+ default, `AES-128-CBC` will be used.
58
63
 
59
- ```js
60
- import { keyring } from "@fnando/keyring";
64
+ To specify the encryption algorithm, set the `encryption` option. The following
65
+ example uses `AES-256-CBC`.
61
66
 
62
- const keys = {"1": "uDiMcWVNTuz//naQ88sOcN+E40CyBRGzGTT7OkoBS6M="};
63
- const encryptor = keyring(keys, {encryption: "aes-256-cbc"});
67
+ ```ruby
68
+ keyring = Keyring.new(
69
+ "1" => "uDiMcWVNTuz//naQ88sOcN+E40CyBRGzGTT7OkoBS6M=",
70
+ encryptor: Keyring::Encryptor::AES256CBC,
71
+ digest_salt: "<custom salt>"
72
+ )
64
73
  ```
65
74
 
66
75
  ### Configuration
67
76
 
68
77
  As far as database schema goes:
69
78
 
70
- 1. You'll need a column to track the key that was used for encryption; by default it's called `keyring_id`.
71
- 2. Every encrypted columns must follow the name `encrypted_<column name>`.
72
- 3. Optionally, you can also have a `<column name>_digest` to help with searching (see Lookup section below).
79
+ 1. You'll need a column to track the key that was used for encryption; by
80
+ default it's called `keyring_id`.
81
+ 2. Every encrypted column must follow the name `encrypted_<column name>`.
82
+ 3. Optionally, you can also have a `<column name>_digest` to help with searching
83
+ (see Lookup section below).
73
84
 
74
- As far as model configuration goes, they're pretty similar, as you can see below:
85
+ As far as model configuration goes, they're pretty similar, as you can see
86
+ below:
75
87
 
76
88
  #### ActiveRecord
77
89
 
78
- From Rails 5+, ActiveRecord models now inherit from `ApplicationRecord` instead. This is how you set it up:
90
+ From Rails 5+, ActiveRecord models now inherit from `ApplicationRecord` instead.
91
+ This is how you set it up:
79
92
 
80
93
  ```ruby
81
94
  class ApplicationRecord < ActiveRecord::Base
@@ -86,7 +99,8 @@ end
86
99
 
87
100
  #### Sequel
88
101
 
89
- Sequel doesn't have an abstract model class (but it could), so you can set up the model class directly like the following:
102
+ Sequel doesn't have an abstract model class (but it could), so you can set up
103
+ the model class directly like the following:
90
104
 
91
105
  ```ruby
92
106
  class User < Sequel::Model
@@ -96,16 +110,20 @@ end
96
110
 
97
111
  ### Defining encrypted attributes
98
112
 
99
- To set up your model, you have to define the keyring (set of encryption keys) and the attributes that will be encrypted. Both ActiveRecord and Sequel have the same API, so the examples below work for both ORMs.
113
+ To set up your model, you have to define the keyring (set of encryption keys)
114
+ and the attributes that will be encrypted. Both ActiveRecord and Sequel have the
115
+ same API, so the examples below work for both ORMs.
100
116
 
101
117
  ```ruby
102
118
  class User < ApplicationRecord
103
- attr_keyring ENV["USER_KEYRING"]
119
+ attr_keyring ENV["USER_KEYRING"],
120
+ digest_salt: "<custom salt>"
104
121
  attr_encrypt :twitter_oauth_token, :social_security_number
105
122
  end
106
123
  ```
107
124
 
108
- The code above will encrypt your columns with the current key. If you're updating a record, then the column will be migrated to the latest key available.
125
+ The code above will encrypt your columns with the current key. If you're
126
+ updating a record, then the column will be migrated to the latest key available.
109
127
 
110
128
  You can use the model as you would normally do.
111
129
 
@@ -126,20 +144,27 @@ user.encrypted_email
126
144
 
127
145
  ### Encryption
128
146
 
129
- By default, AES-128-CBC is the algorithm used for encryption. This algorithm uses 16 bytes keys, but you're required to use a key that's double the size because half of that keys will be used to generate the HMAC. The first 16 bytes will be used as the encryption key, and the last 16 bytes will be used to generate the HMAC.
147
+ By default, AES-128-CBC is the algorithm used for encryption. This algorithm
148
+ uses 16 bytes keys, but you're required to use a key that's double the size
149
+ because half of that keys will be used to generate the HMAC. The first 16 bytes
150
+ will be used as the encryption key, and the last 16 bytes will be used to
151
+ generate the HMAC.
130
152
 
131
- Using random data base64-encoded is the recommended way. You can easily generate keys by using the following command:
153
+ Using random data base64-encoded is the recommended way. You can easily generate
154
+ keys by using the following command:
132
155
 
133
156
  ```console
134
157
  $ dd if=/dev/urandom bs=32 count=1 2>/dev/null | openssl base64 -A
135
158
  qUjOJFgZsZbTICsN0TMkKqUvSgObYxnkHDsazTqE5tM=
136
159
  ```
137
160
 
138
- Include the result of this command in the `value` section of the key description in the keyring. Half this key is used for encryption, and half for the HMAC.
161
+ Include the result of this command in the `value` section of the key description
162
+ in the keyring. Half this key is used for encryption, and half for the HMAC.
139
163
 
140
164
  #### Key size
141
165
 
142
- The key size depends on the algorithm being used. The key size should be double the size as half of it is used for HMAC computation.
166
+ The key size depends on the algorithm being used. The key size should be double
167
+ the size as half of it is used for HMAC computation.
143
168
 
144
169
  - `aes-128-cbc`: 16 bytes (encryption) + 16 bytes (HMAC).
145
170
  - `aes-192-cbc`: 24 bytes (encryption) + 24 bytes (HMAC).
@@ -147,13 +172,24 @@ The key size depends on the algorithm being used. The key size should be double
147
172
 
148
173
  #### About the encrypted message
149
174
 
150
- Initialization vectors (IV) should be unpredictable and unique; ideally, they will be cryptographically random. They do not have to be secret: IVs are typically just added to ciphertext messages unencrypted. It may sound contradictory that something has to be unpredictable and unique, but does not have to be secret; it is important to remember that an attacker must not be able to predict ahead of time what a given IV will be.
175
+ Initialization vectors (IV) should be unpredictable and unique; ideally, they
176
+ will be cryptographically random. They do not have to be secret: IVs are
177
+ typically just added to ciphertext messages unencrypted. It may sound
178
+ contradictory that something has to be unpredictable and unique, but does not
179
+ have to be secret; it is important to remember that an attacker must not be able
180
+ to predict ahead of time what a given IV will be.
151
181
 
152
- With that in mind, _attr_keyring_ uses `base64(hmac(unencrypted iv + encrypted message) + unencrypted iv + encrypted message)` as the final message. If you're planning to migrate from other encryption mechanisms or read encrypted values from the database without using _attr_keyring_, make sure you account for this. The HMAC is 32-bytes long and the IV is 16-bytes long.
182
+ With that in mind, _attr_keyring_ uses `base64(hmac(unencrypted iv + encrypted
183
+ message) + unencrypted iv + encrypted message)` as the final message. If you're
184
+ planning to migrate from other encryption mechanisms or read encrypted values
185
+ from the database without using _attr_keyring_, make sure you account for this.
186
+ The HMAC is 32-bytes long and the IV is 16-bytes long.
153
187
 
154
188
  ### Keyring
155
189
 
156
- Keys are managed through a keyring--a short JSON document describing your encryption keys. The keyring must be a JSON object mapping numeric ids of the keys to the key values. A keyring must have at least one key. For example:
190
+ Keys are managed through a keyring--a short JSON document describing your
191
+ encryption keys. The keyring must be a JSON object mapping numeric ids of the
192
+ keys to the key values. A keyring must have at least one key. For example:
157
193
 
158
194
  ```json
159
195
  {
@@ -162,11 +198,14 @@ Keys are managed through a keyring--a short JSON document describing your encryp
162
198
  }
163
199
  ```
164
200
 
165
- The `id` is used to track which key encrypted which piece of data; a key with a larger id is assumed to be newer. The value is the actual bytes of the encryption key.
201
+ The `id` is used to track which key encrypted which piece of data; a key with a
202
+ larger id is assumed to be newer. The value is the actual bytes of the
203
+ encryption key.
166
204
 
167
205
  #### Dynamically loading keyring
168
206
 
169
- If you're using Rails 5.2+, you can use credentials to define your keyring. Your `credentials.yml` must be define like the following:
207
+ If you're using Rails 5.2+, you can use credentials to define your keyring.
208
+ Your `credentials.yml` must be define like the following:
170
209
 
171
210
  ```yaml
172
211
  user_keyring:
@@ -174,12 +213,14 @@ user_keyring:
174
213
  2: "r6AfOeilPDJomFsiOXLdfQ=="
175
214
  ```
176
215
 
177
- Then you can setup your model by using `attr_keyring Rails.application.credentials.user_keyring`.
216
+ Then you can setup your model by using
217
+ `attr_keyring Rails.application.credentials.user_keyring`.
178
218
 
179
- Other possibilities (e.g. the keyring file is provided by configuration management):
219
+ Other possibilities (e.g. the keyring file is provided by configuration
220
+ management):
180
221
 
181
- - `attr_keyring YAML.load_file(keyring_file)`
182
- - `attr_keyring JSON.parse(File.read(keyring_file))`.
222
+ - `attr_keyring YAML.load_file(keyring_file), digest_salt: "<custom salt>"`
223
+ - `attr_keyring JSON.parse(File.read(keyring_file)), digest_salt: "<custom salt>"`.
183
224
 
184
225
  ### Lookup
185
226
 
@@ -189,17 +230,25 @@ One tricky aspect of encryption is looking up records by known secret. E.g.,
189
230
  User.where(email: "john@example.com")
190
231
  ```
191
232
 
192
- is trivial with plain text fields, but impossible with the model defined as above.
233
+ is trivial with plain text fields, but impossible with the model defined as
234
+ above.
193
235
 
194
- If a column `<attribute>_digest` exists, then a SHA1 digest from the value will be saved. This will allow you to lookup by that value instead and add unique indexes.
236
+ If a column `<attribute>_digest` exists, then a SHA1 digest from the value will
237
+ be saved. This will allow you to lookup by that value instead and add unique
238
+ indexes. You don't have to use a hashing salt, but it's highly recommended; this
239
+ way you can avoid leaking your users' info via rainbow tables.
195
240
 
196
241
  ```ruby
197
- User.where(email: Digest::SHA1.hexdigest("john@example.com"))
242
+ User.where(email: User.keyring.digest("john@example.com")).first
198
243
  ```
199
244
 
200
245
  ### Key Rotation
201
246
 
202
- Because attr_keyring uses a keyring, with access to multiple keys at once, key rotation is fairly straightforward: if you add a key to the keyring with a higher id than any other key, that key will automatically be used for encryption when records are either created or updated. Any keys that are no longer in use can be safely removed from the keyring.
247
+ Because attr_keyring uses a keyring, with access to multiple keys at once, key
248
+ rotation is fairly straightforward: if you add a key to the keyring with a
249
+ higher id than any other key, that key will automatically be used for encryption
250
+ when records are either created or updated. Any keys that are no longer in use
251
+ can be safely removed from the keyring.
203
252
 
204
253
  To check if an existing key with id `123` is still in use, run:
205
254
 
@@ -208,7 +257,8 @@ To check if an existing key with id `123` is still in use, run:
208
257
  User.where(keyring_id: 123).empty?
209
258
  ```
210
259
 
211
- You may not want to wait for records to be updated (e.g. key leaking). In that case, you can rollout a key rotation:
260
+ You may not want to wait for records to be updated (e.g. key leaking). In that
261
+ case, you can rollout a key rotation:
212
262
 
213
263
  ```ruby
214
264
  User.where(keyring_id: 1234).find_each do |user|
@@ -218,12 +268,18 @@ end
218
268
 
219
269
  ### What if I don't use ActiveRecord/Sequel?
220
270
 
221
- You can also leverage the encryption mechanism of `attr_keyring` totally decoupled from ActiveRecord/Sequel. First, make sure you load `keyring` instead. Then you can create a keyring to encrypt/decrypt strings, without even touching the database.
271
+ You can also leverage the encryption mechanism of `attr_keyring` totally
272
+ decoupled from ActiveRecord/Sequel. First, make sure you load `keyring` instead.
273
+ Then you can create a keyring to encrypt/decrypt strings, without even touching
274
+ the database.
222
275
 
223
276
  ```ruby
224
277
  require "keyring"
225
278
 
226
- keyring = Keyring.new("1" => "QSXyoiRDPoJmfkJUZ4hJeQ==")
279
+ keyring = Keyring.new(
280
+ {"1" => "QSXyoiRDPoJmfkJUZ4hJeQ=="},
281
+ digest_salt: "<custom salt>"
282
+ )
227
283
 
228
284
  encrypted, keyring_id, digest = keyring.encrypt("super secret")
229
285
 
@@ -244,26 +300,42 @@ puts decrypted
244
300
 
245
301
  ### Exchange data with Node.js
246
302
 
247
- If you use Node.js, you may be interested in <https://github.com/fnando/keyring-node>, which is able to read and write messages using the same format.
303
+ If you use Node.js, you may be interested in
304
+ <https://github.com/fnando/keyring-node>, which is able to read and write
305
+ messages using the same format.
248
306
 
249
307
  ## Development
250
308
 
251
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
309
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
310
+ `rake test` to run the tests. You can also run `bin/console` for an interactive
311
+ prompt that will allow you to experiment.
252
312
 
253
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
313
+ To install this gem onto your local machine, run `bundle exec rake install`. To
314
+ release a new version, update the version number in `version.rb`, and then run
315
+ `bundle exec rake release`, which will create a git tag for the version, push
316
+ git commits and tags, and push the `.gem` file to
317
+ [rubygems.org](https://rubygems.org).
254
318
 
255
319
  ## Contributing
256
320
 
257
- Bug reports and pull requests are welcome on GitHub at https://github.com/fnando/attr_keyring. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
321
+ Bug reports and pull requests are welcome on GitHub at
322
+ https://github.com/fnando/attr_keyring. This project is intended to be a safe,
323
+ welcoming space for collaboration, and contributors are expected to adhere to
324
+ the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
258
325
 
259
326
  ## License
260
327
 
261
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
328
+ The gem is available as open source under the terms of the
329
+ [MIT License](https://opensource.org/licenses/MIT).
262
330
 
263
331
  ## Icon
264
332
 
265
- Icon made by [Icongeek26](https://www.flaticon.com/authors/icongeek26) from [Flaticon](https://www.flaticon.com/) is licensed by Creative Commons BY 3.0.
333
+ Icon made by [Icongeek26](https://www.flaticon.com/authors/icongeek26)
334
+ from [Flaticon](https://www.flaticon.com/) is licensed by Creative Commons BY
335
+ 3.0.
266
336
 
267
337
  ## Code of Conduct
268
338
 
269
- Everyone interacting in the attr_keyring project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/fnando/attr_keyring/blob/master/CODE_OF_CONDUCT.md).
339
+ Everyone interacting in the attr_keyring project’s codebases, issue trackers,
340
+ chat rooms and mailing lists is expected to follow the
341
+ [code of conduct](https://github.com/fnando/attr_keyring/blob/master/CODE_OF_CONDUCT.md).
@@ -26,7 +26,7 @@ module AttrKeyring
26
26
  end
27
27
 
28
28
  self.encrypted_attributes = []
29
- self.keyring = Keyring.new({})
29
+ self.keyring = Keyring.new({}, digest_salt: "")
30
30
  self.keyring_column_name = :keyring_id
31
31
  end
32
32
  end
@@ -40,8 +40,8 @@ module AttrKeyring
40
40
  subclass.keyring_column_name = keyring_column_name
41
41
  end
42
42
 
43
- def attr_keyring(keyring, encryptor: Keyring::Encryptor::AES::AES128CBC)
44
- self.keyring = Keyring.new(keyring, encryptor)
43
+ def attr_keyring(keyring, options = {})
44
+ self.keyring = Keyring.new(keyring, options)
45
45
  end
46
46
 
47
47
  def attr_encrypt(*attributes)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AttrKeyring
4
- VERSION = "0.5.4"
4
+ VERSION = "0.6.0"
5
5
  end
@@ -12,10 +12,19 @@ module Keyring
12
12
  InvalidSecret = Class.new(StandardError)
13
13
  EmptyKeyring = Class.new(StandardError)
14
14
  InvalidAuthentication = Class.new(StandardError)
15
+ MissingDigestSalt = Class.new(StandardError) do
16
+ def message
17
+ %w[
18
+ Please provide :digest_salt;
19
+ you can disable this error by explicitly passing an empty string.
20
+ ].join(" ")
21
+ end
22
+ end
15
23
 
16
24
  class Base
17
- def initialize(keyring, encryptor)
18
- @encryptor = encryptor
25
+ def initialize(keyring, options)
26
+ @encryptor = options[:encryptor]
27
+ @digest_salt = options[:digest_salt]
19
28
  @keyring = keyring.map do |id, value|
20
29
  Key.new(id, value, @encryptor.key_size)
21
30
  end
@@ -44,13 +53,12 @@ module Keyring
44
53
 
45
54
  def encrypt(message, keyring_id = nil)
46
55
  keyring_id ||= current_key&.id
47
- digest = Digest::SHA1.hexdigest(message)
48
56
  key = self[keyring_id]
49
57
 
50
58
  [
51
59
  @encryptor.encrypt(key, message),
52
60
  keyring_id,
53
- digest
61
+ digest(message)
54
62
  ]
55
63
  end
56
64
 
@@ -58,9 +66,19 @@ module Keyring
58
66
  key = self[keyring_id]
59
67
  @encryptor.decrypt(key, message)
60
68
  end
69
+
70
+ def digest(message)
71
+ Digest::SHA1.hexdigest("#{message}#{@digest_salt}")
72
+ end
61
73
  end
62
74
 
63
- def self.new(keyring, encryptor = Encryptor::AES::AES128CBC)
64
- Base.new(keyring, encryptor)
75
+ def self.new(keyring, options = {})
76
+ options = {
77
+ encryptor: Encryptor::AES::AES128CBC
78
+ }.merge(options)
79
+
80
+ raise MissingDigestSalt if options[:digest_salt].nil?
81
+
82
+ Base.new(keyring, options)
65
83
  end
66
84
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: attr_keyring
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.4
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nando Vieira
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-11-27 00:00:00.000000000 Z
11
+ date: 2020-02-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -229,7 +229,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
229
229
  - !ruby/object:Gem::Version
230
230
  version: '0'
231
231
  requirements: []
232
- rubygems_version: 3.0.3
232
+ rubygems_version: 3.1.2
233
233
  signing_key:
234
234
  specification_version: 4
235
235
  summary: Simple encryption-at-rest plugin for ActiveRecord.