crypt_keeper 2.0.0.rc2 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +48 -7
- data/Appraisals +23 -0
- data/README.md +2 -2
- data/crypt_keeper.gemspec +4 -4
- data/gemfiles/activerecord_4_2.gemfile +2 -1
- data/gemfiles/activerecord_5_0.gemfile +1 -1
- data/gemfiles/activerecord_5_1.gemfile +0 -1
- data/gemfiles/activerecord_5_2.gemfile +0 -1
- data/gemfiles/activerecord_6_0.gemfile +8 -0
- data/gemfiles/activerecord_6_1.gemfile +9 -0
- data/gemfiles/activerecord_7_0.gemfile +9 -0
- data/lib/crypt_keeper/helper.rb +4 -0
- data/lib/crypt_keeper/provider/postgres_pgp.rb +8 -2
- data/lib/crypt_keeper/version.rb +1 -1
- data/spec/crypt_keeper/model_spec.rb +17 -2
- data/spec/crypt_keeper/provider/postgres_pgp_spec.rb +36 -0
- data/spec/spec_helper.rb +5 -1
- data/spec/support/active_record.rb +1 -0
- metadata +30 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a090653644f2d7aba77cd684309d1a720f3a956a8568d5c03f06985ca8b608ff
|
4
|
+
data.tar.gz: a61221ebb211944e2f2695f223da199c905e6ae70ce681aa3d4c909e61801e6c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2eb493363aad423a7ea2131056b48e0c5be36ee4c61f46e1bc3dda20dba6cd70ac72e2f2a1741ed004a5e35c6b2526faaeaab5b777fee9f768a9ddd5e4a97db2
|
7
|
+
data.tar.gz: dd4f889755d15483c9777199a3e332d0d66b8845355179d9fbd4b0dc26e8524ad54cbe4df6a88882d7669d0f27c9bb37fc2473379ed2ada777a897ff41f5ca57
|
data/.travis.yml
CHANGED
@@ -1,20 +1,60 @@
|
|
1
1
|
language: ruby
|
2
2
|
|
3
3
|
rvm:
|
4
|
-
- 2.2.
|
5
|
-
- 2.3.
|
6
|
-
- 2.4.
|
4
|
+
- 2.2.10
|
5
|
+
- 2.3.8
|
6
|
+
- 2.4.5
|
7
|
+
- 2.5.3
|
8
|
+
- 2.6.4
|
9
|
+
- 2.7.2
|
7
10
|
|
8
11
|
gemfile:
|
9
12
|
- gemfiles/activerecord_4_2.gemfile
|
10
13
|
- gemfiles/activerecord_5_0.gemfile
|
11
14
|
- gemfiles/activerecord_5_1.gemfile
|
12
15
|
- gemfiles/activerecord_5_2.gemfile
|
16
|
+
- gemfiles/activerecord_6_0.gemfile
|
17
|
+
- gemfiles/activerecord_6_1.gemfile
|
18
|
+
|
13
19
|
|
14
|
-
|
15
|
-
|
20
|
+
matrix:
|
21
|
+
exclude:
|
22
|
+
- rvm: 2.2.10
|
23
|
+
gemfile: gemfiles/activerecord_5_0.gemfile
|
24
|
+
- rvm: 2.2.10
|
25
|
+
gemfile: gemfiles/activerecord_5_1.gemfile
|
26
|
+
- rvm: 2.2.10
|
27
|
+
gemfile: gemfiles/activerecord_6_0.gemfile
|
28
|
+
- rvm: 2.2.10
|
29
|
+
gemfile: gemfiles/activerecord_6_1.gemfile
|
30
|
+
- rvm: 2.3.8
|
31
|
+
gemfile: gemfiles/activerecord_5_0.gemfile
|
32
|
+
- rvm: 2.3.8
|
33
|
+
gemfile: gemfiles/activerecord_5_1.gemfile
|
34
|
+
- rvm: 2.3.8
|
35
|
+
gemfile: gemfiles/activerecord_6_0.gemfile
|
36
|
+
- rvm: 2.3.8
|
37
|
+
gemfile: gemfiles/activerecord_6_1.gemfile
|
38
|
+
- rvm: 2.4.5
|
39
|
+
gemfile: gemfiles/activerecord_5_0.gemfile
|
40
|
+
- rvm: 2.4.5
|
41
|
+
gemfile: gemfiles/activerecord_5_1.gemfile
|
42
|
+
- rvm: 2.4.5
|
43
|
+
gemfile: gemfiles/activerecord_6_0.gemfile
|
44
|
+
- rvm: 2.4.5
|
45
|
+
gemfile: gemfiles/activerecord_6_1.gemfile
|
46
|
+
- rvm: 2.5.3
|
47
|
+
gemfile: gemfiles/activerecord_5_0.gemfile
|
48
|
+
- rvm: 2.5.3
|
49
|
+
gemfile: gemfiles/activerecord_5_1.gemfile
|
50
|
+
- rvm: 2.6.4
|
51
|
+
gemfile: gemfiles/activerecord_5_0.gemfile
|
52
|
+
- rvm: 2.6.4
|
53
|
+
gemfile: gemfiles/activerecord_5_1.gemfile
|
16
54
|
|
17
|
-
|
55
|
+
services:
|
56
|
+
- postgresql
|
57
|
+
- mysql
|
18
58
|
|
19
59
|
before_script:
|
20
60
|
- cp spec/default.database.yml spec/database.yml
|
@@ -22,7 +62,8 @@ before_script:
|
|
22
62
|
- psql crypt_keeper_providers -c 'CREATE EXTENSION IF NOT EXISTS pgcrypto;' -U postgres
|
23
63
|
- mysql -e 'CREATE DATABASE crypt_keeper_providers'
|
24
64
|
|
25
|
-
branches:
|
65
|
+
branches:
|
66
|
+
- master
|
26
67
|
|
27
68
|
notifications:
|
28
69
|
email:
|
data/Appraisals
CHANGED
@@ -1,11 +1,17 @@
|
|
1
1
|
appraise "activerecord_4_2" do
|
2
2
|
gem "activerecord", "~> 4.2.0"
|
3
3
|
gem "activesupport", "~> 4.2.0"
|
4
|
+
gem "sqlite3", "~> 1.3.0"
|
5
|
+
|
6
|
+
# otherwise you get "undefined method `new' for BigDecimal:Class" in Ruby 2.7
|
7
|
+
gem "bigdecimal", "1.3.5"
|
4
8
|
end
|
5
9
|
|
6
10
|
appraise "activerecord_5_0" do
|
7
11
|
gem "activerecord", "~> 5.0.0"
|
8
12
|
gem "activesupport", "~> 5.0.0"
|
13
|
+
|
14
|
+
gem "sqlite3", "~> 1.3.6"
|
9
15
|
end
|
10
16
|
|
11
17
|
appraise "activerecord_5_1" do
|
@@ -17,3 +23,20 @@ appraise "activerecord_5_2" do
|
|
17
23
|
gem "activerecord", "~> 5.2.0"
|
18
24
|
gem "activesupport", "~> 5.2.0"
|
19
25
|
end
|
26
|
+
|
27
|
+
appraise "activerecord_6_0" do
|
28
|
+
gem "activerecord", "~> 6.0.0"
|
29
|
+
gem "activesupport", "~> 6.0.0"
|
30
|
+
end
|
31
|
+
|
32
|
+
appraise "activerecord_6_1" do
|
33
|
+
gem "activerecord", "~> 6.1.0"
|
34
|
+
gem "activesupport", "~> 6.1.0"
|
35
|
+
gem "pg", "~> 1.1"
|
36
|
+
end
|
37
|
+
|
38
|
+
appraise "activerecord_7_0" do
|
39
|
+
gem "activerecord", "~> 7.0"
|
40
|
+
gem "activesupport", "~> 7.0"
|
41
|
+
gem "pg", "~> 1.1"
|
42
|
+
end
|
data/README.md
CHANGED
@@ -151,10 +151,10 @@ Searching ciphertext is a complex problem that varies depending on the encryptio
|
|
151
151
|
* ActiveSupport's MessageEncryptor uses a random initialization vector when generating keys. The same plaintext encrypted multiple times will have different output each time for the ciphertext. Since this is the case, it is not possible to search leveraging the database. Database rows will need to be filtered in memory. It is suggested that you use a scope or ActiveRecord batches to narrow the results before seaching them.
|
152
152
|
|
153
153
|
* Mysql AES
|
154
|
-
|
154
|
+
* Surprisingly, MySQL's implementation of AES does not use a random initialization vector. The column containing the ciphertext can be indexed and searched quickly.
|
155
155
|
|
156
156
|
* PostgresSQL PGP
|
157
|
-
|
157
|
+
* PGP also uses a random initialization vector which means it generates unique output each time you encrypt plaintext. Although the database can be searched by performing row level decryption and comparing the plaintext, it will not be able to use an index. A scope or batch is suggested when searching.
|
158
158
|
|
159
159
|
## How the search interface is used
|
160
160
|
|
data/crypt_keeper.gemspec
CHANGED
@@ -18,13 +18,13 @@ Gem::Specification.new do |gem|
|
|
18
18
|
|
19
19
|
gem.post_install_message = "WARNING: CryptKeeper 2.0 contains breaking changes and may require you to reencrypt your data! Please view the README at https://github.com/jmazzi/crypt_keeper for more information."
|
20
20
|
|
21
|
-
gem.add_runtime_dependency 'activerecord', '>= 4.2', '
|
22
|
-
gem.add_runtime_dependency 'activesupport', '>= 4.2', '
|
21
|
+
gem.add_runtime_dependency 'activerecord', '>= 4.2', '<= 7'
|
22
|
+
gem.add_runtime_dependency 'activesupport', '>= 4.2', '<= 7'
|
23
23
|
|
24
24
|
gem.add_development_dependency 'rspec', '~> 3.5.0'
|
25
25
|
gem.add_development_dependency 'guard', '~> 2.6.1'
|
26
26
|
gem.add_development_dependency 'guard-rspec', '~> 4.2.9'
|
27
|
-
gem.add_development_dependency 'rake'
|
27
|
+
gem.add_development_dependency 'rake'
|
28
28
|
gem.add_development_dependency 'rb-fsevent', '~> 0.9.1'
|
29
29
|
gem.add_development_dependency 'coveralls'
|
30
30
|
gem.add_development_dependency 'appraisal', '~> 2.1.0'
|
@@ -35,7 +35,7 @@ Gem::Specification.new do |gem|
|
|
35
35
|
gem.add_development_dependency 'activerecord-jdbcpostgresql-adapter'
|
36
36
|
gem.add_development_dependency 'activerecord-jdbcmysql-adapter'
|
37
37
|
else
|
38
|
-
gem.add_development_dependency 'sqlite3'
|
38
|
+
gem.add_development_dependency 'sqlite3', '>= 1.3.3', '< 1.5'
|
39
39
|
gem.add_development_dependency 'pg', '~> 0.18.0'
|
40
40
|
gem.add_development_dependency 'mysql2', '>= 0.3.13', '< 0.6.0'
|
41
41
|
end
|
data/lib/crypt_keeper/helper.rb
CHANGED
@@ -12,6 +12,10 @@ module CryptKeeper
|
|
12
12
|
def escape_and_execute_sql(query, new_transaction: false)
|
13
13
|
query = ::ActiveRecord::Base.send :sanitize_sql_array, query
|
14
14
|
|
15
|
+
# force binary encoding to avoid "invalid byte sequence in UTF-8" errors
|
16
|
+
# when we send binary AES keys (f.ex) to the database
|
17
|
+
query = query.b if query.respond_to?(:b)
|
18
|
+
|
15
19
|
if CryptKeeper.silence_logs?
|
16
20
|
::ActiveRecord::Base.logger.silence do
|
17
21
|
execute_sql(query, new_transaction: new_transaction)
|
@@ -42,8 +42,14 @@ module CryptKeeper
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def search(records, field, criteria)
|
45
|
-
|
46
|
-
|
45
|
+
if criteria.present?
|
46
|
+
records
|
47
|
+
.where.not("TRIM(BOTH FROM \"#{field}\") = ?", "")
|
48
|
+
.where("(pgp_sym_decrypt(cast(\"#{field}\" AS bytea), ?) = ?)",
|
49
|
+
key, criteria)
|
50
|
+
else
|
51
|
+
records.where(field => criteria)
|
52
|
+
end
|
47
53
|
end
|
48
54
|
|
49
55
|
private
|
data/lib/crypt_keeper/version.rb
CHANGED
@@ -25,8 +25,7 @@ describe CryptKeeper::Model do
|
|
25
25
|
end
|
26
26
|
|
27
27
|
it "allows binary as a valid type" do
|
28
|
-
subject.crypt_keeper :
|
29
|
-
allow(subject.columns_hash['storage']).to receive(:type).and_return(:binary)
|
28
|
+
subject.crypt_keeper :storage_binary, encryptor: :fake_encryptor
|
30
29
|
expect(subject.new.save).to be_truthy
|
31
30
|
end
|
32
31
|
|
@@ -55,6 +54,7 @@ describe CryptKeeper::Model do
|
|
55
54
|
end
|
56
55
|
end
|
57
56
|
|
57
|
+
|
58
58
|
context "Encryption and Decryption" do
|
59
59
|
let(:plain_text) { 'plain_text' }
|
60
60
|
let(:cipher_text) { 'tooltxet_nialp' }
|
@@ -105,6 +105,21 @@ describe CryptKeeper::Model do
|
|
105
105
|
expect_any_instance_of(CryptKeeper::Provider::Encryptor).to_not receive(:decrypt)
|
106
106
|
subject.find(record.id).storage
|
107
107
|
end
|
108
|
+
|
109
|
+
context "with a binary database field" do
|
110
|
+
subject { create_encrypted_model :storage_binary, passphrase: 'tool', encryptor: :encryptor }
|
111
|
+
|
112
|
+
it "encrypts the data" do
|
113
|
+
expect_any_instance_of(CryptKeeper::Provider::Encryptor).to receive(:encrypt).with('testing')
|
114
|
+
subject.create!(storage_binary: 'testing')
|
115
|
+
end
|
116
|
+
|
117
|
+
it "decrypts the data" do
|
118
|
+
record = subject.create!(storage_binary: 'testing')
|
119
|
+
expect_any_instance_of(CryptKeeper::Provider::Encryptor).to receive(:decrypt).at_least(1).times.with('toolgnitset')
|
120
|
+
subject.find(record.id).storage_binary
|
121
|
+
end
|
122
|
+
end
|
108
123
|
end
|
109
124
|
|
110
125
|
context "Search" do
|
@@ -64,6 +64,42 @@ describe CryptKeeper::Provider::PostgresPgp do
|
|
64
64
|
match = subject.create!(storage: 'blah')
|
65
65
|
expect(subject.search_by_plaintext(:storage, 'blah').first).to eq(match)
|
66
66
|
end
|
67
|
+
|
68
|
+
it "successfully accesses result when the data has empty strings and nil" do
|
69
|
+
subject.create!(storage: nil)
|
70
|
+
subject.create!(storage: '')
|
71
|
+
subject.create!(storage: ' ')
|
72
|
+
match = subject.create!(storage: 'blah')
|
73
|
+
|
74
|
+
results = subject.search_by_plaintext(:storage, 'blah')
|
75
|
+
|
76
|
+
expect(results).to match_array([match])
|
77
|
+
end
|
78
|
+
|
79
|
+
it "finds an empty string" do
|
80
|
+
subject.create!(storage: nil)
|
81
|
+
subject.create!(storage: 'blah')
|
82
|
+
match = subject.create!(storage: '')
|
83
|
+
|
84
|
+
expect(subject.search_by_plaintext(:storage, '')).to match_array([match])
|
85
|
+
end
|
86
|
+
|
87
|
+
it "finds the empty string with the right number of spaces" do
|
88
|
+
subject.create!(storage: '')
|
89
|
+
match = subject.create!(storage: ' ')
|
90
|
+
|
91
|
+
results = subject.search_by_plaintext(:storage, ' ')
|
92
|
+
|
93
|
+
expect(results).to match_array([match])
|
94
|
+
end
|
95
|
+
|
96
|
+
it "finds nil results" do
|
97
|
+
subject.create!(storage: '')
|
98
|
+
subject.create!(storage: 'blah')
|
99
|
+
match = subject.create!(storage: nil)
|
100
|
+
|
101
|
+
expect(subject.search_by_plaintext(:storage, nil)).to match_array([match])
|
102
|
+
end
|
67
103
|
end
|
68
104
|
|
69
105
|
describe "Custom pgcrypto options" do
|
data/spec/spec_helper.rb
CHANGED
@@ -14,7 +14,11 @@ RSpec.configure do |config|
|
|
14
14
|
config.filter_run :focus
|
15
15
|
|
16
16
|
config.after :each do
|
17
|
-
ActiveRecord::Base.descendants.
|
17
|
+
descendants = ActiveRecord::Base.descendants.delete_if do |descendant|
|
18
|
+
descendant.to_s.include?("SchemaMigration")
|
19
|
+
end
|
20
|
+
|
21
|
+
descendants.each do |model|
|
18
22
|
model.method(:delete_all).call
|
19
23
|
end
|
20
24
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: crypt_keeper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Mazzi
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-12-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -17,9 +17,9 @@ dependencies:
|
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '4.2'
|
20
|
-
- - "
|
20
|
+
- - "<="
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: '
|
22
|
+
version: '7'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -27,9 +27,9 @@ dependencies:
|
|
27
27
|
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '4.2'
|
30
|
-
- - "
|
30
|
+
- - "<="
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: '
|
32
|
+
version: '7'
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: activesupport
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -37,9 +37,9 @@ dependencies:
|
|
37
37
|
- - ">="
|
38
38
|
- !ruby/object:Gem::Version
|
39
39
|
version: '4.2'
|
40
|
-
- - "
|
40
|
+
- - "<="
|
41
41
|
- !ruby/object:Gem::Version
|
42
|
-
version: '
|
42
|
+
version: '7'
|
43
43
|
type: :runtime
|
44
44
|
prerelease: false
|
45
45
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -47,9 +47,9 @@ dependencies:
|
|
47
47
|
- - ">="
|
48
48
|
- !ruby/object:Gem::Version
|
49
49
|
version: '4.2'
|
50
|
-
- - "
|
50
|
+
- - "<="
|
51
51
|
- !ruby/object:Gem::Version
|
52
|
-
version: '
|
52
|
+
version: '7'
|
53
53
|
- !ruby/object:Gem::Dependency
|
54
54
|
name: rspec
|
55
55
|
requirement: !ruby/object:Gem::Requirement
|
@@ -96,16 +96,16 @@ dependencies:
|
|
96
96
|
name: rake
|
97
97
|
requirement: !ruby/object:Gem::Requirement
|
98
98
|
requirements:
|
99
|
-
- - "
|
99
|
+
- - ">="
|
100
100
|
- !ruby/object:Gem::Version
|
101
|
-
version:
|
101
|
+
version: '0'
|
102
102
|
type: :development
|
103
103
|
prerelease: false
|
104
104
|
version_requirements: !ruby/object:Gem::Requirement
|
105
105
|
requirements:
|
106
|
-
- - "
|
106
|
+
- - ">="
|
107
107
|
- !ruby/object:Gem::Version
|
108
|
-
version:
|
108
|
+
version: '0'
|
109
109
|
- !ruby/object:Gem::Dependency
|
110
110
|
name: rb-fsevent
|
111
111
|
requirement: !ruby/object:Gem::Requirement
|
@@ -154,14 +154,20 @@ dependencies:
|
|
154
154
|
requirements:
|
155
155
|
- - ">="
|
156
156
|
- !ruby/object:Gem::Version
|
157
|
-
version:
|
157
|
+
version: 1.3.3
|
158
|
+
- - "<"
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '1.5'
|
158
161
|
type: :development
|
159
162
|
prerelease: false
|
160
163
|
version_requirements: !ruby/object:Gem::Requirement
|
161
164
|
requirements:
|
162
165
|
- - ">="
|
163
166
|
- !ruby/object:Gem::Version
|
164
|
-
version:
|
167
|
+
version: 1.3.3
|
168
|
+
- - "<"
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
version: '1.5'
|
165
171
|
- !ruby/object:Gem::Dependency
|
166
172
|
name: pg
|
167
173
|
requirement: !ruby/object:Gem::Requirement
|
@@ -217,6 +223,9 @@ files:
|
|
217
223
|
- gemfiles/activerecord_5_0.gemfile
|
218
224
|
- gemfiles/activerecord_5_1.gemfile
|
219
225
|
- gemfiles/activerecord_5_2.gemfile
|
226
|
+
- gemfiles/activerecord_6_0.gemfile
|
227
|
+
- gemfiles/activerecord_6_1.gemfile
|
228
|
+
- gemfiles/activerecord_7_0.gemfile
|
220
229
|
- lib/crypt_keeper.rb
|
221
230
|
- lib/crypt_keeper/helper.rb
|
222
231
|
- lib/crypt_keeper/log_subscriber/mysql_aes.rb
|
@@ -261,13 +270,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
261
270
|
version: '0'
|
262
271
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
263
272
|
requirements:
|
264
|
-
- - "
|
273
|
+
- - ">="
|
265
274
|
- !ruby/object:Gem::Version
|
266
|
-
version:
|
275
|
+
version: '0'
|
267
276
|
requirements: []
|
268
|
-
|
269
|
-
|
270
|
-
signing_key:
|
277
|
+
rubygems_version: 3.1.4
|
278
|
+
signing_key:
|
271
279
|
specification_version: 4
|
272
280
|
summary: Transparent ActiveRecord encryption
|
273
281
|
test_files:
|