lockbox 0.4.8 → 0.4.9

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: a560c020c3adf21952f81767ffc9b5b4586784f62d748f484e7bacbd4076a64a
4
- data.tar.gz: 59d05b405b4cd46da679ef4f03a53fae03cc78d7cdfe89bab13cd6981b76a4da
3
+ metadata.gz: 5ad5a754772ecb9d5f0a480cab88a63de9ed2fcfd973eef25f286fcf13da7694
4
+ data.tar.gz: d38646c9d1aedee2bf12419a24064fa5b01e4ef4cb619e8cb48903c343ec67b1
5
5
  SHA512:
6
- metadata.gz: 8d6217f47cc9c38ad8cf3db11b2a3a2936950b97f91ea168c5f2e4f8a1d9a5916c832286f08156869fbecf89d05dfc9bd7c4ecade9b9b4384488c936a292a1a6
7
- data.tar.gz: 3ddf36244c68b6b0bebad62801366d9827e6bee520717f1d544cfc6a18e798c644a158b68ac295fa87cef45ce5b922f37e89c5e39a5882ccb9fe512e725e778b
6
+ metadata.gz: 5f6e78e05cb6788ad8188314694846f70c438e3e43f48bdf1e8e6356ac94e64226a3790ebaab6369121d1083d551a7203281979731443cfdb1c611d52a617493
7
+ data.tar.gz: 30fc406d323dda8abdc0d5138880dc32b862abdd479e623180cf99c2531b466ef5a3be10620c975d0f2aee4b10e17b413cdc1cf21e03aa49ef5c6f0a7757cd82
@@ -1,3 +1,10 @@
1
+ ## 0.4.9 (2020-10-01)
2
+
3
+ - Added `key_table` and `key_attribute` options to `previous_versions`
4
+ - Added `encrypted_attribute` option
5
+ - Added support for encrypting empty string
6
+ - Improved `inspect` for models with encrypted attributes
7
+
1
8
  ## 0.4.8 (2020-08-30)
2
9
 
3
10
  - Added `key_table` and `key_attribute` options
data/README.md CHANGED
@@ -773,7 +773,9 @@ Lockbox supports a few different ways to set keys for database fields and files.
773
773
 
774
774
  ### Master Key
775
775
 
776
- By default, the master key is used to generate unique keys for each field/uploader. This technique comes from [CipherSweet](https://ciphersweet.paragonie.com/internals/key-hierarchy). The table name and column/uploader name are both used in this process. You can get an individual key with:
776
+ By default, the master key is used to generate unique keys for each field/uploader. This technique comes from [CipherSweet](https://ciphersweet.paragonie.com/internals/key-hierarchy). The table name and column/uploader name are both used in this process.
777
+
778
+ You can get an individual key with:
777
779
 
778
780
  ```ruby
779
781
  Lockbox.attribute_key(table: "users", attribute: "email_ciphertext")
@@ -820,10 +822,14 @@ To use a different key for each record, use a symbol:
820
822
  ```ruby
821
823
  class User < ApplicationRecord
822
824
  encrypts :email, key: :some_method
825
+ end
826
+ ```
823
827
 
824
- def some_method
825
- # code to get key
826
- end
828
+ Or a proc:
829
+
830
+ ```ruby
831
+ class User < ApplicationRecord
832
+ encrypts :email, key: -> { some_method }
827
833
  end
828
834
  ```
829
835
 
@@ -938,12 +944,6 @@ class User < ApplicationRecord
938
944
  end
939
945
  ```
940
946
 
941
- or set it globally:
942
-
943
- ```ruby
944
- Lockbox.default_options = {encode: false}
945
- ```
946
-
947
947
  ## Compatibility
948
948
 
949
949
  It’s easy to read encrypted data in another language if needed.
@@ -18,9 +18,10 @@ module Lockbox
18
18
  # In encryption mode, it must be set after calling #encrypt and setting #key= and #iv=
19
19
  cipher.auth_data = associated_data || ""
20
20
 
21
- ciphertext = cipher.update(message) + cipher.final
21
+ ciphertext = String.new
22
+ ciphertext << cipher.update(message) unless message.empty?
23
+ ciphertext << cipher.final
22
24
  ciphertext << cipher.auth_tag
23
-
24
25
  ciphertext
25
26
  end
26
27
 
@@ -29,7 +30,6 @@ module Lockbox
29
30
 
30
31
  fail_decryption if nonce.to_s.bytesize != nonce_bytes
31
32
  fail_decryption if auth_tag.to_s.bytesize != auth_tag_bytes
32
- fail_decryption if ciphertext.to_s.bytesize == 0
33
33
 
34
34
  cipher = OpenSSL::Cipher.new("aes-256-gcm")
35
35
  # do not change order of operations
@@ -43,7 +43,11 @@ module Lockbox
43
43
  cipher.auth_data = associated_data || ""
44
44
 
45
45
  begin
46
- cipher.update(ciphertext) + cipher.final
46
+ if ciphertext.to_s.empty?
47
+ cipher.final
48
+ else
49
+ cipher.update(ciphertext) + cipher.final
50
+ end
47
51
  rescue OpenSSL::Cipher::CipherError
48
52
  fail_decryption
49
53
  end
@@ -32,11 +32,15 @@ module Lockbox
32
32
 
33
33
  raise ArgumentError, "Cannot use key_attribute with multiple attributes" if options[:key_attribute] && attributes.size > 1
34
34
 
35
+ original_options = options.dup
36
+
35
37
  attributes.each do |name|
36
- # add default options
37
- encrypted_attribute = "#{name}_ciphertext"
38
+ # per attribute options
39
+ # TODO use a different name
40
+ options = original_options.dup
38
41
 
39
- options = options.dup
42
+ # add default options
43
+ encrypted_attribute = options.delete(:encrypted_attribute) || "#{name}_ciphertext"
40
44
 
41
45
  # migrating
42
46
  original_name = name.to_sym
@@ -82,6 +86,11 @@ module Lockbox
82
86
  serializable_hash.map do |k,v|
83
87
  "#{k}: #{respond_to?(:attribute_for_inspect) ? attribute_for_inspect(k) : v.inspect}"
84
88
  end
89
+
90
+ self.class.lockbox_attributes.map do |_, lockbox_attribute|
91
+ inspection << "#{lockbox_attribute[:attribute]}: [FILTERED]" if has_attribute?(lockbox_attribute[:encrypted_attribute])
92
+ end
93
+
85
94
  "#<#{self.class} #{inspection.join(", ")}>"
86
95
  end
87
96
 
@@ -169,6 +178,7 @@ module Lockbox
169
178
  end
170
179
 
171
180
  raise "Duplicate encrypted attribute: #{original_name}" if lockbox_attributes[original_name]
181
+ raise "Multiple encrypted attributes use the same column: #{encrypted_attribute}" if lockbox_attributes.any? { |_, v| v[:encrypted_attribute] == encrypted_attribute }
172
182
  @lockbox_attributes[original_name] = options
173
183
 
174
184
  if activerecord
@@ -29,7 +29,17 @@ module Lockbox
29
29
  options[:previous_versions] = options[:previous_versions].dup
30
30
  options[:previous_versions].each_with_index do |version, i|
31
31
  if !(version[:key] || version[:encryption_key] || version[:decryption_key]) && version[:master_key]
32
- options[:previous_versions][i] = version.merge(key: Lockbox.attribute_key(table: table, attribute: attribute, master_key: version.delete(:master_key), encode: false))
32
+ # could also use key_table and key_attribute from options
33
+ # when specified, but keep simple for now
34
+ # also, this change isn't backward compatible
35
+ key =
36
+ Lockbox.attribute_key(
37
+ table: version.delete(:key_table) || table,
38
+ attribute: version.delete(:key_attribute) || attribute,
39
+ master_key: version.delete(:master_key),
40
+ encode: false
41
+ )
42
+ options[:previous_versions][i] = version.merge(key: key)
33
43
  end
34
44
  end
35
45
  end
@@ -1,3 +1,3 @@
1
1
  module Lockbox
2
- VERSION = "0.4.8"
2
+ VERSION = "0.4.9"
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.4.8
4
+ version: 0.4.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Kane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-31 00:00:00.000000000 Z
11
+ date: 2020-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler