blind_index 1.0.2 → 2.0.0
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 +7 -0
- data/LICENSE.txt +1 -1
- data/README.md +57 -30
- data/lib/blind_index.rb +5 -10
- data/lib/blind_index/extensions.rb +0 -32
- data/lib/blind_index/model.rb +18 -13
- data/lib/blind_index/version.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f656fa1765df9bf2bcfa9d994ccb9b5cf0504f27f7524d159323126462d973e0
|
4
|
+
data.tar.gz: 88d5f2cd786f840e75540a204dbe7fbb74794de954978d90cdc69297078cf752
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 139d3d8f3aca413d0ee5045fe3212e6ed3327cdb6d0c60cb7eb2b314b4eb849abb42dcf17e386e23fb5521db2875b6c502b143fc46dc3305ad38151688b610be
|
7
|
+
data.tar.gz: 0f5df7f99a1b79f2eab0bc9ff959b9fd21c238e772e09265ef88690678134f539968d9aebe508b88e9881657563013cd53031947d6a6322e08c7e29d95f45902
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
## 2.0.0 (2019-02-10)
|
2
|
+
|
3
|
+
- Blind indexes are updated immediately instead of in a `before_validation` callback
|
4
|
+
- Better Lockbox integration - no need to generate a separate key
|
5
|
+
- The `argon2` gem has been replaced with `argon2-kdf` for less dependencies and Windows support
|
6
|
+
- Removed deprecated `compute_email_bidx`
|
7
|
+
|
1
8
|
## 1.0.2 (2019-12-26)
|
2
9
|
|
3
10
|
- Fixed `OpenSSL::KDF` error on some platforms
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -26,37 +26,13 @@ Add this line to your application’s Gemfile:
|
|
26
26
|
gem 'blind_index'
|
27
27
|
```
|
28
28
|
|
29
|
-
On Windows, also add:
|
30
|
-
|
31
|
-
```ruby
|
32
|
-
gem 'argon2', git: 'https://github.com/technion/ruby-argon2.git', submodules: true
|
33
|
-
```
|
34
|
-
|
35
|
-
Until `argon2 > 2.0.2` is released.
|
36
|
-
|
37
29
|
## Getting Started
|
38
30
|
|
39
|
-
|
40
|
-
|
41
|
-
First, generate a key
|
42
|
-
|
43
|
-
```ruby
|
44
|
-
BlindIndex.generate_key
|
45
|
-
```
|
46
|
-
|
47
|
-
Store the key with your other secrets. This is typically Rails credentials or an environment variable ([dotenv](https://github.com/bkeepers/dotenv) is great for this). Be sure to use different keys in development and production. Keys don’t need to be hex-encoded, but it’s often easier to store them this way.
|
48
|
-
|
49
|
-
Set the following environment variable with your key (you can use this one in development)
|
50
|
-
|
51
|
-
```sh
|
52
|
-
BLIND_INDEX_MASTER_KEY=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
53
|
-
```
|
31
|
+
Your model should already be set up with Lockbox or attr_encrypted. The examples are for a `User` model with `encrypts :email` or `attr_encrypted :email`. See the full examples for [Lockbox](https://ankane.org/securing-user-emails-lockbox) and [attr_encrypted](https://ankane.org/securing-user-emails-in-rails) if needed.
|
54
32
|
|
55
|
-
|
33
|
+
Also, if you use attr_encrypted, [generate a key](#key-generation).
|
56
34
|
|
57
|
-
|
58
|
-
BlindIndex.master_key = Rails.application.credentials.blind_index_master_key
|
59
|
-
```
|
35
|
+
---
|
60
36
|
|
61
37
|
Create a migration to add a column for the blind index
|
62
38
|
|
@@ -169,18 +145,37 @@ You can also use virtual attributes to index data from multiple columns:
|
|
169
145
|
```ruby
|
170
146
|
class User < ApplicationRecord
|
171
147
|
attribute :initials, :string
|
148
|
+
blind_index :initials
|
172
149
|
|
173
|
-
# must come before the blind_index method so it runs first
|
174
150
|
before_validation :set_initials, if: -> { changes.key?(:first_name) || changes.key?(:last_name) }
|
175
151
|
|
176
|
-
blind_index :initials
|
177
|
-
|
178
152
|
def set_initials
|
179
153
|
self.initials = "#{first_name[0]}#{last_name[0]}"
|
180
154
|
end
|
181
155
|
end
|
182
156
|
```
|
183
157
|
|
158
|
+
## Migrating Data
|
159
|
+
|
160
|
+
If you’re encrypting a column and adding a blind index at the same time, use the `migrating` option.
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
class User < ApplicationRecord
|
164
|
+
blind_index :email, migrating: true
|
165
|
+
end
|
166
|
+
```
|
167
|
+
|
168
|
+
This allows you to backfill records while still querying the unencrypted field.
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
User.unscoped.where(email_bidx: nil).find_each do |user|
|
172
|
+
user.compute_migrated_email_bidx
|
173
|
+
user.save(validate: false)
|
174
|
+
end
|
175
|
+
```
|
176
|
+
|
177
|
+
Once that completes, you can remove the `migrating` option.
|
178
|
+
|
184
179
|
## Key Rotation
|
185
180
|
|
186
181
|
To rotate keys without downtime, add a new column:
|
@@ -260,6 +255,30 @@ class User
|
|
260
255
|
end
|
261
256
|
```
|
262
257
|
|
258
|
+
## Key Generation
|
259
|
+
|
260
|
+
This is optional for Lockbox, as its master key is used by default.
|
261
|
+
|
262
|
+
Generate a key with:
|
263
|
+
|
264
|
+
```ruby
|
265
|
+
BlindIndex.generate_key
|
266
|
+
```
|
267
|
+
|
268
|
+
Store the key with your other secrets. This is typically Rails credentials or an environment variable ([dotenv](https://github.com/bkeepers/dotenv) is great for this). Be sure to use different keys in development and production. Keys don’t need to be hex-encoded, but it’s often easier to store them this way.
|
269
|
+
|
270
|
+
Set the following environment variable with your key (you can use this one in development)
|
271
|
+
|
272
|
+
```sh
|
273
|
+
BLIND_INDEX_MASTER_KEY=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
274
|
+
```
|
275
|
+
|
276
|
+
or create `config/initializers/blind_index.rb` with something like
|
277
|
+
|
278
|
+
```ruby
|
279
|
+
BlindIndex.master_key = Rails.application.credentials.blind_index_master_key
|
280
|
+
```
|
281
|
+
|
263
282
|
## Reference
|
264
283
|
|
265
284
|
Set default options in an initializer with:
|
@@ -301,6 +320,14 @@ One alternative to blind indexing is to use a deterministic encryption scheme, l
|
|
301
320
|
|
302
321
|
## Upgrading
|
303
322
|
|
323
|
+
### 2.0.0
|
324
|
+
|
325
|
+
2.0.0 brings a number of improvements.
|
326
|
+
|
327
|
+
- Blind indexes are updated immediately instead of in a `before_validation` callback
|
328
|
+
- Better Lockbox integration - no need to generate a separate key
|
329
|
+
- There’s a new gem for Argon2 that has no dependencies and (officially) supports Windows
|
330
|
+
|
304
331
|
### 1.0.0
|
305
332
|
|
306
333
|
1.0.0 brings a number of improvements. Here are a few to be aware of:
|
data/lib/blind_index.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# dependencies
|
2
2
|
require "active_support"
|
3
3
|
require "openssl"
|
4
|
-
require "argon2"
|
4
|
+
require "argon2/kdf"
|
5
5
|
|
6
6
|
# modules
|
7
7
|
require "blind_index/key_generator"
|
@@ -18,7 +18,7 @@ module BlindIndex
|
|
18
18
|
self.default_options = {}
|
19
19
|
|
20
20
|
def self.master_key
|
21
|
-
@master_key ||= ENV["BLIND_INDEX_MASTER_KEY"]
|
21
|
+
@master_key ||= ENV["BLIND_INDEX_MASTER_KEY"] || (defined?(Lockbox.master_key) && Lockbox.master_key)
|
22
22
|
end
|
23
23
|
|
24
24
|
def self.generate_bidx(value, key:, **options)
|
@@ -64,7 +64,7 @@ module BlindIndex
|
|
64
64
|
# use same bounds as rbnacl
|
65
65
|
raise BlindIndex::Error, "m must be between 3 and 22" if m < 3 || m > 22
|
66
66
|
|
67
|
-
|
67
|
+
Argon2::KDF.argon2id(value, salt: key, t: t, m: m, p: 1, length: size)
|
68
68
|
when :pbkdf2_sha256
|
69
69
|
iterations = cost_options[:iterations] || options[:iterations] || (options[:slow] ? 100000 : 10000)
|
70
70
|
OpenSSL::PKCS5.pbkdf2_hmac(value, key, iterations, size, "sha256")
|
@@ -78,7 +78,7 @@ module BlindIndex
|
|
78
78
|
# use same bounds as rbnacl
|
79
79
|
raise BlindIndex::Error, "m must be between 3 and 22" if m < 3 || m > 22
|
80
80
|
|
81
|
-
|
81
|
+
Argon2::KDF.argon2i(value, salt: key, t: t, m: m, p: 1, length: size)
|
82
82
|
when :scrypt
|
83
83
|
n = cost_options[:n] || 4096
|
84
84
|
r = cost_options[:r] || 8
|
@@ -133,12 +133,7 @@ ActiveSupport.on_load(:active_record) do
|
|
133
133
|
require "blind_index/extensions"
|
134
134
|
extend BlindIndex::Model
|
135
135
|
|
136
|
-
|
137
|
-
ActiveRecord::TableMetadata.prepend(BlindIndex::Extensions::TableMetadata)
|
138
|
-
else
|
139
|
-
ActiveRecord::PredicateBuilder.singleton_class.prepend(BlindIndex::Extensions::PredicateBuilder)
|
140
|
-
end
|
141
|
-
|
136
|
+
ActiveRecord::TableMetadata.prepend(BlindIndex::Extensions::TableMetadata)
|
142
137
|
ActiveRecord::DynamicMatchers::Method.prepend(BlindIndex::Extensions::DynamicMatchers)
|
143
138
|
|
144
139
|
unless ActiveRecord::VERSION::STRING.start_with?("5.1.")
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module BlindIndex
|
2
2
|
module Extensions
|
3
|
-
# ActiveRecord 5.0+
|
4
3
|
module TableMetadata
|
5
4
|
def resolve_column_aliases(hash)
|
6
5
|
new_hash = super
|
@@ -29,37 +28,6 @@ module BlindIndex
|
|
29
28
|
end
|
30
29
|
end
|
31
30
|
|
32
|
-
# ActiveRecord 4.2
|
33
|
-
module PredicateBuilder
|
34
|
-
def resolve_column_aliases(klass, hash)
|
35
|
-
new_hash = super
|
36
|
-
if has_blind_indexes?(klass)
|
37
|
-
hash.each do |key, _|
|
38
|
-
if key.respond_to?(:to_sym) && (bi = klass.blind_indexes[key.to_sym]) && !new_hash[key].is_a?(ActiveRecord::StatementCache::Substitute)
|
39
|
-
value = new_hash.delete(key)
|
40
|
-
new_hash[bi[:bidx_attribute]] =
|
41
|
-
if value.is_a?(Array)
|
42
|
-
value.map { |v| BlindIndex.generate_bidx(v, bi) }
|
43
|
-
else
|
44
|
-
BlindIndex.generate_bidx(value, bi)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
new_hash
|
50
|
-
end
|
51
|
-
|
52
|
-
@@blind_index_cache = {}
|
53
|
-
|
54
|
-
# memoize for performance
|
55
|
-
def has_blind_indexes?(klass)
|
56
|
-
if @@blind_index_cache[klass].nil?
|
57
|
-
@@blind_index_cache[klass] = klass.respond_to?(:blind_indexes)
|
58
|
-
end
|
59
|
-
@@blind_index_cache[klass]
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
31
|
module UniquenessValidator
|
64
32
|
if ActiveRecord::VERSION::STRING >= "5.2"
|
65
33
|
def build_relation(klass, attribute, value)
|
data/lib/blind_index/model.rb
CHANGED
@@ -64,25 +64,30 @@ module BlindIndex
|
|
64
64
|
BlindIndex.generate_bidx(value, **blind_indexes[name])
|
65
65
|
end
|
66
66
|
|
67
|
-
define_singleton_method method_name do |value|
|
68
|
-
ActiveSupport::Deprecation.warn("Use #{class_method_name} instead")
|
69
|
-
send(class_method_name, value)
|
70
|
-
end
|
71
|
-
|
72
67
|
define_method method_name do
|
73
68
|
self.send("#{bidx_attribute}=", self.class.send(class_method_name, send(attribute)))
|
74
69
|
end
|
75
70
|
|
76
71
|
if callback
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
72
|
+
activerecord = defined?(ActiveRecord) && self < ActiveRecord::Base
|
73
|
+
|
74
|
+
# TODO reuse module
|
75
|
+
m = Module.new do
|
76
|
+
define_method "#{attribute}=" do |value|
|
77
|
+
result = super(value)
|
78
|
+
send(method_name)
|
79
|
+
result
|
80
|
+
end
|
81
|
+
|
82
|
+
unless activerecord
|
83
|
+
define_method "reset_#{attribute}!" do
|
84
|
+
result = super()
|
85
|
+
send(method_name)
|
86
|
+
result
|
87
|
+
end
|
88
|
+
end
|
85
89
|
end
|
90
|
+
prepend m
|
86
91
|
end
|
87
92
|
|
88
93
|
# use include so user can override
|
data/lib/blind_index/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blind_index
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Kane
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-02-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -25,19 +25,19 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '5'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name: argon2
|
28
|
+
name: argon2-kdf
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 0.1.1
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 0.1.1
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|