lockbox 0.2.4 → 0.2.5
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 +4 -4
- data/CHANGELOG.md +13 -7
- data/README.md +54 -9
- data/lib/lockbox.rb +5 -0
- data/lib/lockbox/model.rb +57 -50
- data/lib/lockbox/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 33199b663aaa289ff221bd83278063e366501185112f9aea61ea52997c6646fb
|
4
|
+
data.tar.gz: 3d8556b252b574c69cd50244cc752302825cae423daf6bcbac5a0f07b6a83d9d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9246245fe128a27292ee9a365ab04fcb0acfd3336b838aeef5756353d35add2248911916e6dec4ed46cb0adf01576ac8fb98066cb6a35aec7827144d5d208d40
|
7
|
+
data.tar.gz: 98273c362d05eca1b66cc578264c18db1a66007fd461ae0d3d8919305f9c34b9af70450a5fe3b318357fe296fc0455b52f52d7b2313a04269101fdf50b408df4
|
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,10 @@
|
|
1
|
-
## 0.2.
|
1
|
+
## 0.2.5 (2019-12-14)
|
2
|
+
|
3
|
+
- Made `model.attribute?` consistent with unencrypted columns
|
4
|
+
- Added `decrypt_str` method
|
5
|
+
- Improved fixtures for attributes with `type` option
|
6
|
+
|
7
|
+
## 0.2.4 (2019-08-16)
|
2
8
|
|
3
9
|
- Added support for Mongoid
|
4
10
|
- Added `encrypt_io` and `decrypt_io` methods
|
@@ -6,36 +12,36 @@
|
|
6
12
|
- Fixed error with migrate and default scope
|
7
13
|
- Fixed encryption with Active Storage 6 and `record.create!`
|
8
14
|
|
9
|
-
## 0.2.3
|
15
|
+
## 0.2.3 (2019-07-31)
|
10
16
|
|
11
17
|
- Added time type
|
12
18
|
- Added support for rotating padding with same key
|
13
19
|
- Fixed `OpenSSL::KDF` error on some platforms
|
14
20
|
- Fixed UTF-8 error
|
15
21
|
|
16
|
-
## 0.2.2
|
22
|
+
## 0.2.2 (2019-07-24)
|
17
23
|
|
18
24
|
- Fixed error with models that have attachments but no encrypted attachments
|
19
25
|
|
20
|
-
## 0.2.1
|
26
|
+
## 0.2.1 (2019-07-22)
|
21
27
|
|
22
28
|
- Added support for types
|
23
29
|
- Added support for serialized attributes
|
24
30
|
- Added support for padding
|
25
31
|
- Added `encode` option for binary columns
|
26
32
|
|
27
|
-
## 0.2.0
|
33
|
+
## 0.2.0 (2019-07-08)
|
28
34
|
|
29
35
|
- Added `encrypts` method for database fields
|
30
36
|
- Added `encrypts_attached` method
|
31
37
|
- Added `generate_key` method
|
32
38
|
- Added support for XSalsa20
|
33
39
|
|
34
|
-
## 0.1.1
|
40
|
+
## 0.1.1 (2019-02-28)
|
35
41
|
|
36
42
|
- Added support for hybrid cryptography
|
37
43
|
- Added support for database fields
|
38
44
|
|
39
|
-
## 0.1.0
|
45
|
+
## 0.1.0 (2019-01-02)
|
40
46
|
|
41
47
|
- First release
|
data/README.md
CHANGED
@@ -44,6 +44,24 @@ Lockbox.master_key = Rails.application.credentials.lockbox_master_key
|
|
44
44
|
|
45
45
|
Alternatively, you can use a [key management service](#key-management) to manage your keys.
|
46
46
|
|
47
|
+
## Instructions
|
48
|
+
|
49
|
+
Database fields
|
50
|
+
|
51
|
+
- [Active Record](#active-record)
|
52
|
+
- [Mongoid](#mongoid)
|
53
|
+
|
54
|
+
Files
|
55
|
+
|
56
|
+
- [Active Storage](#active-storage)
|
57
|
+
- [CarrierWave](#carrierwave)
|
58
|
+
- [Shrine](#shrine)
|
59
|
+
- [Local Files](#local-files)
|
60
|
+
|
61
|
+
Other
|
62
|
+
|
63
|
+
- [Strings](#strings)
|
64
|
+
|
47
65
|
## Database Fields
|
48
66
|
|
49
67
|
### Active Record
|
@@ -51,7 +69,7 @@ Alternatively, you can use a [key management service](#key-management) to manage
|
|
51
69
|
Create a migration with:
|
52
70
|
|
53
71
|
```ruby
|
54
|
-
class AddEmailCiphertextToUsers < ActiveRecord::Migration[
|
72
|
+
class AddEmailCiphertextToUsers < ActiveRecord::Migration[6.0]
|
55
73
|
def change
|
56
74
|
add_column :users, :email_ciphertext, :text
|
57
75
|
end
|
@@ -92,7 +110,7 @@ class User < ApplicationRecord
|
|
92
110
|
end
|
93
111
|
```
|
94
112
|
|
95
|
-
**Note:** Always use a `text` or `binary` column in migrations, regardless of the type
|
113
|
+
**Note:** Always use a `text` or `binary` column for the ciphertext in migrations, regardless of the type
|
96
114
|
|
97
115
|
Lockbox automatically works with serialized fields for maximum compatibility with existing code and libraries.
|
98
116
|
|
@@ -100,6 +118,9 @@ Lockbox automatically works with serialized fields for maximum compatibility wit
|
|
100
118
|
class User < ApplicationRecord
|
101
119
|
serialize :properties, JSON
|
102
120
|
encrypts :properties
|
121
|
+
|
122
|
+
store :settings, accessors: [:color, :homepage]
|
123
|
+
encrypts :settings
|
103
124
|
end
|
104
125
|
```
|
105
126
|
|
@@ -247,6 +268,12 @@ Decrypt
|
|
247
268
|
box.decrypt(ciphertext)
|
248
269
|
```
|
249
270
|
|
271
|
+
Decrypt and return UTF-8 instead of binary
|
272
|
+
|
273
|
+
```ruby
|
274
|
+
box.decrypt_str(ciphertext)
|
275
|
+
```
|
276
|
+
|
250
277
|
## Migrating Existing Data
|
251
278
|
|
252
279
|
Lockbox makes it easy to encrypt an existing column. Add a new column for the ciphertext, then add to your model:
|
@@ -384,20 +411,29 @@ Heroku [comes with libsodium](https://devcenter.heroku.com/articles/stack-packag
|
|
384
411
|
|
385
412
|
##### Ubuntu
|
386
413
|
|
387
|
-
For Ubuntu
|
414
|
+
For Ubuntu 18.04, use:
|
388
415
|
|
389
416
|
```sh
|
390
|
-
sudo apt-get install
|
417
|
+
sudo apt-get install libsodium23
|
391
418
|
```
|
392
419
|
|
393
|
-
For Ubuntu
|
420
|
+
For Ubuntu 16.04, use:
|
394
421
|
|
395
422
|
```sh
|
396
|
-
sudo apt-get install
|
423
|
+
sudo apt-get install libsodium18
|
397
424
|
```
|
398
425
|
|
399
426
|
##### Travis CI
|
400
427
|
|
428
|
+
On Bionic, add to `.travis.yml`:
|
429
|
+
|
430
|
+
```yml
|
431
|
+
addons:
|
432
|
+
apt:
|
433
|
+
packages:
|
434
|
+
- libsodium23
|
435
|
+
```
|
436
|
+
|
401
437
|
On Xenial, add to `.travis.yml`:
|
402
438
|
|
403
439
|
```yml
|
@@ -524,7 +560,7 @@ For XSalsa20, use the appropriate [Libsodium library](https://libsodium.gitbook.
|
|
524
560
|
|
525
561
|
## Migrating from Another Library
|
526
562
|
|
527
|
-
Lockbox makes it easy to migrate from another library. The example below uses `attr_encrypted` but the same approach should work for any library.
|
563
|
+
Lockbox makes it easy to migrate from another library without downtime. The example below uses `attr_encrypted` but the same approach should work for any library.
|
528
564
|
|
529
565
|
Let’s suppose your model looks like this:
|
530
566
|
|
@@ -538,7 +574,7 @@ end
|
|
538
574
|
Create a migration with:
|
539
575
|
|
540
576
|
```ruby
|
541
|
-
class MigrateToLockbox < ActiveRecord::Migration[
|
577
|
+
class MigrateToLockbox < ActiveRecord::Migration[6.0]
|
542
578
|
def change
|
543
579
|
add_column :users, :name_ciphertext, :text
|
544
580
|
add_column :users, :email_ciphertext, :text
|
@@ -571,7 +607,7 @@ end
|
|
571
607
|
Then remove the previous gem from your Gemfile and drop its columns.
|
572
608
|
|
573
609
|
```ruby
|
574
|
-
class RemovePreviousEncryptedColumns < ActiveRecord::Migration[
|
610
|
+
class RemovePreviousEncryptedColumns < ActiveRecord::Migration[6.0]
|
575
611
|
def change
|
576
612
|
remove_column :users, :encrypted_name, :text
|
577
613
|
remove_column :users, :encrypted_name_iv, :text
|
@@ -639,3 +675,12 @@ Everyone is encouraged to help improve this project. Here are a few ways you can
|
|
639
675
|
- Fix bugs and [submit pull requests](https://github.com/ankane/lockbox/pulls)
|
640
676
|
- Write, clarify, or fix documentation
|
641
677
|
- Suggest or add new features
|
678
|
+
|
679
|
+
To get started with development and testing:
|
680
|
+
|
681
|
+
```sh
|
682
|
+
git clone https://github.com/ankane/lockbox.git
|
683
|
+
cd lockbox
|
684
|
+
bundle install
|
685
|
+
bundle exec rake test
|
686
|
+
```
|
data/lib/lockbox.rb
CHANGED
data/lib/lockbox/model.rb
CHANGED
@@ -104,10 +104,13 @@ class Lockbox
|
|
104
104
|
@lockbox_attributes[original_name] = options.merge(encode: encode)
|
105
105
|
|
106
106
|
if @lockbox_attributes.size == 1
|
107
|
+
# use same approach as activerecord serialization
|
107
108
|
def serializable_hash(options = nil)
|
108
109
|
options = options.try(:dup) || {}
|
110
|
+
|
109
111
|
options[:except] = Array(options[:except])
|
110
|
-
options[:except] += self.class.lockbox_attributes.
|
112
|
+
options[:except] += self.class.lockbox_attributes.flat_map { |_, v| [v[:attribute], v[:encrypted_attribute]] }
|
113
|
+
|
111
114
|
super(options)
|
112
115
|
end
|
113
116
|
|
@@ -151,6 +154,10 @@ class Lockbox
|
|
151
154
|
|
152
155
|
if respond_to?(:attribute)
|
153
156
|
attribute name, attribute_type
|
157
|
+
|
158
|
+
define_method("#{name}?") do
|
159
|
+
send("#{encrypted_attribute}?")
|
160
|
+
end
|
154
161
|
else
|
155
162
|
m = Module.new do
|
156
163
|
define_method("#{name}=") do |val|
|
@@ -183,45 +190,6 @@ class Lockbox
|
|
183
190
|
define_method("#{name}=") do |message|
|
184
191
|
original_message = message
|
185
192
|
|
186
|
-
unless message.nil?
|
187
|
-
case options[:type]
|
188
|
-
when :boolean
|
189
|
-
message = ActiveRecord::Type::Boolean.new.serialize(message)
|
190
|
-
message = nil if message == "" # for Active Record < 5.2
|
191
|
-
message = message ? "t" : "f" unless message.nil?
|
192
|
-
when :date
|
193
|
-
message = ActiveRecord::Type::Date.new.serialize(message)
|
194
|
-
# strftime should be more stable than to_s(:db)
|
195
|
-
message = message.strftime("%Y-%m-%d") unless message.nil?
|
196
|
-
when :datetime
|
197
|
-
message = ActiveRecord::Type::DateTime.new.serialize(message)
|
198
|
-
message = nil unless message.respond_to?(:iso8601) # for Active Record < 5.2
|
199
|
-
message = message.iso8601(9) unless message.nil?
|
200
|
-
when :time
|
201
|
-
message = ActiveRecord::Type::Time.new.serialize(message)
|
202
|
-
message = nil unless message.respond_to?(:strftime)
|
203
|
-
message = message.strftime("%H:%M:%S.%N") unless message.nil?
|
204
|
-
message
|
205
|
-
when :integer
|
206
|
-
message = ActiveRecord::Type::Integer.new(limit: 8).serialize(message)
|
207
|
-
message = 0 if message.nil?
|
208
|
-
# signed 64-bit integer, big endian
|
209
|
-
message = [message].pack("q>")
|
210
|
-
when :float
|
211
|
-
message = ActiveRecord::Type::Float.new.serialize(message)
|
212
|
-
# double precision, big endian
|
213
|
-
message = [message].pack("G") unless message.nil?
|
214
|
-
when :string, :binary
|
215
|
-
# do nothing
|
216
|
-
# encrypt will convert to binary
|
217
|
-
else
|
218
|
-
type = (self.class.try(:attribute_types) || {})[name.to_s]
|
219
|
-
if type && type.is_a?(ActiveRecord::Type::Serialized)
|
220
|
-
message = type.serialize(message)
|
221
|
-
end
|
222
|
-
end
|
223
|
-
end
|
224
|
-
|
225
193
|
# decrypt first for dirty tracking
|
226
194
|
# don't raise error if can't decrypt previous
|
227
195
|
begin
|
@@ -230,13 +198,8 @@ class Lockbox
|
|
230
198
|
nil
|
231
199
|
end
|
232
200
|
|
233
|
-
ciphertext
|
234
|
-
|
235
|
-
message
|
236
|
-
else
|
237
|
-
self.class.send(class_method_name, message, context: self)
|
238
|
-
end
|
239
|
-
|
201
|
+
# set ciphertext
|
202
|
+
ciphertext = self.class.send(class_method_name, message, context: self)
|
240
203
|
send("#{encrypted_attribute}=", ciphertext)
|
241
204
|
|
242
205
|
super(original_message)
|
@@ -305,9 +268,53 @@ class Lockbox
|
|
305
268
|
# for fixtures
|
306
269
|
define_singleton_method class_method_name do |message, **opts|
|
307
270
|
table = respond_to?(:table_name) ? table_name : collection_name.to_s
|
308
|
-
|
309
|
-
|
310
|
-
|
271
|
+
|
272
|
+
unless message.nil?
|
273
|
+
case options[:type]
|
274
|
+
when :boolean
|
275
|
+
message = ActiveRecord::Type::Boolean.new.serialize(message)
|
276
|
+
message = nil if message == "" # for Active Record < 5.2
|
277
|
+
message = message ? "t" : "f" unless message.nil?
|
278
|
+
when :date
|
279
|
+
message = ActiveRecord::Type::Date.new.serialize(message)
|
280
|
+
# strftime should be more stable than to_s(:db)
|
281
|
+
message = message.strftime("%Y-%m-%d") unless message.nil?
|
282
|
+
when :datetime
|
283
|
+
message = ActiveRecord::Type::DateTime.new.serialize(message)
|
284
|
+
message = nil unless message.respond_to?(:iso8601) # for Active Record < 5.2
|
285
|
+
message = message.iso8601(9) unless message.nil?
|
286
|
+
when :time
|
287
|
+
message = ActiveRecord::Type::Time.new.serialize(message)
|
288
|
+
message = nil unless message.respond_to?(:strftime)
|
289
|
+
message = message.strftime("%H:%M:%S.%N") unless message.nil?
|
290
|
+
message
|
291
|
+
when :integer
|
292
|
+
message = ActiveRecord::Type::Integer.new(limit: 8).serialize(message)
|
293
|
+
message = 0 if message.nil?
|
294
|
+
# signed 64-bit integer, big endian
|
295
|
+
message = [message].pack("q>")
|
296
|
+
when :float
|
297
|
+
message = ActiveRecord::Type::Float.new.serialize(message)
|
298
|
+
# double precision, big endian
|
299
|
+
message = [message].pack("G") unless message.nil?
|
300
|
+
when :string, :binary
|
301
|
+
# do nothing
|
302
|
+
# encrypt will convert to binary
|
303
|
+
else
|
304
|
+
type = (try(:attribute_types) || {})[name.to_s]
|
305
|
+
if type && type.is_a?(ActiveRecord::Type::Serialized)
|
306
|
+
message = type.serialize(message)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
if message.nil? || (message == "" && !options[:padding])
|
312
|
+
message
|
313
|
+
else
|
314
|
+
ciphertext = Lockbox::Utils.build_box(opts[:context], options, table, encrypted_attribute).encrypt(message)
|
315
|
+
ciphertext = Base64.strict_encode64(ciphertext) if encode
|
316
|
+
ciphertext
|
317
|
+
end
|
311
318
|
end
|
312
319
|
end
|
313
320
|
end
|
data/lib/lockbox/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lockbox
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-12-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -218,7 +218,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
218
218
|
- !ruby/object:Gem::Version
|
219
219
|
version: '0'
|
220
220
|
requirements: []
|
221
|
-
rubygems_version: 3.0.
|
221
|
+
rubygems_version: 3.0.3
|
222
222
|
signing_key:
|
223
223
|
specification_version: 4
|
224
224
|
summary: Modern encryption for Rails
|