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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d4bf60bca21cbcbf37397b2431401a56191b2a8a2b311ddd12bbe8f94d43a259
4
- data.tar.gz: '09969a1e9c7904fe69e017dd6aa8a0d5e1a50573fc5937994ad2c0eab63f499b'
3
+ metadata.gz: 33199b663aaa289ff221bd83278063e366501185112f9aea61ea52997c6646fb
4
+ data.tar.gz: 3d8556b252b574c69cd50244cc752302825cae423daf6bcbac5a0f07b6a83d9d
5
5
  SHA512:
6
- metadata.gz: 5b513fb92e0074f10a5044acbcd9a06bba8f90af473cdc52ae7d981e5432e77afedca887372246afbbb16df7c72cc099ad12a59312e909ef36203c9678b2d987
7
- data.tar.gz: 1ab527b1cad1a112b1ce43a3e2745faff13289dfa9dabcd15d7e0ca856cfec801b41112faa09ca312dd57537f3ac5a32d5882fb369d2bb0e262f8bd2bd20d8b4
6
+ metadata.gz: 9246245fe128a27292ee9a365ab04fcb0acfd3336b838aeef5756353d35add2248911916e6dec4ed46cb0adf01576ac8fb98066cb6a35aec7827144d5d208d40
7
+ data.tar.gz: 98273c362d05eca1b66cc578264c18db1a66007fd461ae0d3d8919305f9c34b9af70450a5fe3b318357fe296fc0455b52f52d7b2313a04269101fdf50b408df4
data/CHANGELOG.md CHANGED
@@ -1,4 +1,10 @@
1
- ## 0.2.4
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[5.2]
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 16.04, use:
414
+ For Ubuntu 18.04, use:
388
415
 
389
416
  ```sh
390
- sudo apt-get install libsodium18
417
+ sudo apt-get install libsodium23
391
418
  ```
392
419
 
393
- For Ubuntu 18.04, use:
420
+ For Ubuntu 16.04, use:
394
421
 
395
422
  ```sh
396
- sudo apt-get install libsodium23
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[5.2]
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[5.2]
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
@@ -141,6 +141,11 @@ class Lockbox
141
141
  new_io
142
142
  end
143
143
 
144
+ def decrypt_str(ciphertext, **options)
145
+ message = decrypt(ciphertext, **options)
146
+ message.force_encoding(Encoding::UTF_8)
147
+ end
148
+
144
149
  def self.generate_key
145
150
  SecureRandom.hex(32)
146
151
  end
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.values.flat_map { |v| [v[:attribute], v[:encrypted_attribute]] }
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
- if message.nil? || (message == "" && !options[:padding])
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
- ciphertext = Lockbox::Utils.build_box(opts[:context], options, table, encrypted_attribute).encrypt(message)
309
- ciphertext = Base64.strict_encode64(ciphertext) if encode
310
- ciphertext
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
@@ -1,3 +1,3 @@
1
1
  class Lockbox
2
- VERSION = "0.2.4"
2
+ VERSION = "0.2.5"
3
3
  end
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
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-08-16 00:00:00.000000000 Z
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.4
221
+ rubygems_version: 3.0.3
222
222
  signing_key:
223
223
  specification_version: 4
224
224
  summary: Modern encryption for Rails