lockbox 0.2.4 → 0.2.5

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: 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