crypt_keeper 0.22.0 → 1.0.0.beta1
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/.gitignore +1 -1
- data/.travis.yml +5 -1
- data/Appraisals +6 -6
- data/README.md +8 -20
- data/Rakefile +7 -3
- data/crypt_keeper.gemspec +5 -5
- data/gemfiles/{activerecord_4_1.gemfile → activerecord_5_0.gemfile} +2 -2
- data/lib/crypt_keeper.rb +1 -2
- data/lib/crypt_keeper/helper.rb +0 -18
- data/lib/crypt_keeper/log_subscriber/mysql_aes.rb +7 -9
- data/lib/crypt_keeper/log_subscriber/postgres_pgp.rb +7 -9
- data/lib/crypt_keeper/model.rb +14 -20
- data/lib/crypt_keeper/provider/aes_new.rb +1 -1
- data/lib/crypt_keeper/provider/base.rb +21 -0
- data/lib/crypt_keeper/provider/mysql_aes_new.rb +1 -1
- data/lib/crypt_keeper/provider/postgres_pgp.rb +2 -2
- data/lib/crypt_keeper/provider/postgres_pgp_public_key.rb +1 -1
- data/lib/crypt_keeper/version.rb +1 -1
- data/spec/crypt_keeper/log_subscriber/mysql_aes_spec.rb +56 -0
- data/spec/crypt_keeper/log_subscriber/postgres_pgp_spec.rb +94 -0
- data/spec/crypt_keeper/model_spec.rb +172 -0
- data/spec/crypt_keeper/provider/aes_new_spec.rb +41 -0
- data/spec/crypt_keeper/provider/mysql_aes_new_spec.rb +50 -0
- data/spec/crypt_keeper/provider/postgres_pgp_public_key_spec.rb +66 -0
- data/spec/crypt_keeper/provider/postgres_pgp_spec.rb +66 -0
- data/spec/spec_helper.rb +0 -1
- data/spec/support/encryptors.rb +9 -3
- data/spec/support/logging.rb +92 -0
- metadata +37 -44
- data/gemfiles/activerecord_4_1.gemfile.lock +0 -120
- data/gemfiles/activerecord_4_2.gemfile.lock +0 -120
- data/lib/crypt_keeper/provider/aes.rb +0 -66
- data/lib/crypt_keeper/provider/mysql_aes.rb +0 -47
- data/spec/log_subscriber/mysql_aes_spec.rb +0 -73
- data/spec/log_subscriber/postgres_pgp_spec.rb +0 -123
- data/spec/model_spec.rb +0 -169
- data/spec/provider/aes_new_spec.rb +0 -45
- data/spec/provider/aes_spec.rb +0 -67
- data/spec/provider/mysql_aes_new_spec.rb +0 -54
- data/spec/provider/mysql_aes_spec.rb +0 -35
- data/spec/provider/postgres_pgp_public_key_spec.rb +0 -70
- data/spec/provider/postgres_pgp_spec.rb +0 -70
@@ -1,120 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: ../
|
3
|
-
specs:
|
4
|
-
crypt_keeper (0.21.0)
|
5
|
-
activerecord (>= 3.1, < 4.3)
|
6
|
-
activesupport (>= 3.1, < 4.3)
|
7
|
-
aes (~> 0.5.0)
|
8
|
-
armor (~> 0.0.2)
|
9
|
-
|
10
|
-
GEM
|
11
|
-
remote: https://rubygems.org/
|
12
|
-
specs:
|
13
|
-
activemodel (4.2.6)
|
14
|
-
activesupport (= 4.2.6)
|
15
|
-
builder (~> 3.1)
|
16
|
-
activerecord (4.2.6)
|
17
|
-
activemodel (= 4.2.6)
|
18
|
-
activesupport (= 4.2.6)
|
19
|
-
arel (~> 6.0)
|
20
|
-
activesupport (4.2.6)
|
21
|
-
i18n (~> 0.7)
|
22
|
-
json (~> 1.7, >= 1.7.7)
|
23
|
-
minitest (~> 5.1)
|
24
|
-
thread_safe (~> 0.3, >= 0.3.4)
|
25
|
-
tzinfo (~> 1.1)
|
26
|
-
aes (0.5.0)
|
27
|
-
appraisal (1.0.3)
|
28
|
-
bundler
|
29
|
-
rake
|
30
|
-
thor (>= 0.14.0)
|
31
|
-
arel (6.0.3)
|
32
|
-
armor (0.0.3)
|
33
|
-
builder (3.2.2)
|
34
|
-
celluloid (0.16.0)
|
35
|
-
timers (~> 4.0.0)
|
36
|
-
coderay (1.1.1)
|
37
|
-
coveralls (0.8.13)
|
38
|
-
json (~> 1.8)
|
39
|
-
simplecov (~> 0.11.0)
|
40
|
-
term-ansicolor (~> 1.3)
|
41
|
-
thor (~> 0.19.1)
|
42
|
-
tins (~> 1.6.0)
|
43
|
-
diff-lcs (1.2.5)
|
44
|
-
docile (1.1.5)
|
45
|
-
ffi (1.9.10)
|
46
|
-
formatador (0.2.5)
|
47
|
-
guard (2.6.1)
|
48
|
-
formatador (>= 0.2.4)
|
49
|
-
listen (~> 2.7)
|
50
|
-
lumberjack (~> 1.0)
|
51
|
-
pry (>= 0.9.12)
|
52
|
-
thor (>= 0.18.1)
|
53
|
-
guard-rspec (4.2.10)
|
54
|
-
guard (~> 2.1)
|
55
|
-
rspec (>= 2.14, < 4.0)
|
56
|
-
hitimes (1.2.4)
|
57
|
-
i18n (0.7.0)
|
58
|
-
json (1.8.3)
|
59
|
-
listen (2.10.1)
|
60
|
-
celluloid (~> 0.16.0)
|
61
|
-
rb-fsevent (>= 0.9.3)
|
62
|
-
rb-inotify (>= 0.9)
|
63
|
-
lumberjack (1.0.10)
|
64
|
-
method_source (0.8.2)
|
65
|
-
minitest (5.9.0)
|
66
|
-
mysql2 (0.3.21)
|
67
|
-
pg (0.17.1)
|
68
|
-
pry (0.10.3)
|
69
|
-
coderay (~> 1.1.0)
|
70
|
-
method_source (~> 0.8.1)
|
71
|
-
slop (~> 3.4)
|
72
|
-
rake (10.3.2)
|
73
|
-
rb-fsevent (0.9.7)
|
74
|
-
rb-inotify (0.9.7)
|
75
|
-
ffi (>= 0.5.0)
|
76
|
-
rspec (2.14.1)
|
77
|
-
rspec-core (~> 2.14.0)
|
78
|
-
rspec-expectations (~> 2.14.0)
|
79
|
-
rspec-mocks (~> 2.14.0)
|
80
|
-
rspec-core (2.14.8)
|
81
|
-
rspec-expectations (2.14.5)
|
82
|
-
diff-lcs (>= 1.1.3, < 2.0)
|
83
|
-
rspec-mocks (2.14.6)
|
84
|
-
simplecov (0.11.2)
|
85
|
-
docile (~> 1.1.0)
|
86
|
-
json (~> 1.8)
|
87
|
-
simplecov-html (~> 0.10.0)
|
88
|
-
simplecov-html (0.10.0)
|
89
|
-
slop (3.6.0)
|
90
|
-
sqlite3 (1.3.11)
|
91
|
-
term-ansicolor (1.3.2)
|
92
|
-
tins (~> 1.0)
|
93
|
-
thor (0.19.1)
|
94
|
-
thread_safe (0.3.5)
|
95
|
-
timers (4.0.4)
|
96
|
-
hitimes
|
97
|
-
tins (1.6.0)
|
98
|
-
tzinfo (1.2.2)
|
99
|
-
thread_safe (~> 0.1)
|
100
|
-
|
101
|
-
PLATFORMS
|
102
|
-
ruby
|
103
|
-
|
104
|
-
DEPENDENCIES
|
105
|
-
activerecord (~> 4.2.0)
|
106
|
-
activesupport (~> 4.2.0)
|
107
|
-
appraisal (~> 1.0.0)
|
108
|
-
coveralls
|
109
|
-
crypt_keeper!
|
110
|
-
guard (~> 2.6.1)
|
111
|
-
guard-rspec (~> 4.2.9)
|
112
|
-
mysql2 (~> 0.3.11)
|
113
|
-
pg (~> 0.17.1)
|
114
|
-
rake (~> 10.3.1)
|
115
|
-
rb-fsevent (~> 0.9.1)
|
116
|
-
rspec (~> 2.14.0)
|
117
|
-
sqlite3
|
118
|
-
|
119
|
-
BUNDLED WITH
|
120
|
-
1.12.5
|
@@ -1,66 +0,0 @@
|
|
1
|
-
require 'digest/sha2'
|
2
|
-
require 'openssl'
|
3
|
-
require 'base64'
|
4
|
-
|
5
|
-
module CryptKeeper
|
6
|
-
module Provider
|
7
|
-
class Aes
|
8
|
-
SEPARATOR = ":crypt_keeper:"
|
9
|
-
|
10
|
-
# Public: The encryption key
|
11
|
-
attr_accessor :key
|
12
|
-
|
13
|
-
# Public: An instance of OpenSSL::Cipher::Cipher
|
14
|
-
attr_accessor :aes
|
15
|
-
|
16
|
-
# Public: Initializes the class
|
17
|
-
#
|
18
|
-
# options - A hash of options. :key is required
|
19
|
-
def initialize(options = {})
|
20
|
-
legacy
|
21
|
-
@aes = ::OpenSSL::Cipher::Cipher.new("AES-256-CBC")
|
22
|
-
@aes.padding = 1
|
23
|
-
|
24
|
-
key = options.fetch(:key) do
|
25
|
-
raise ArgumentError, "Missing :key"
|
26
|
-
end
|
27
|
-
|
28
|
-
@key = Digest::SHA256.digest(key)
|
29
|
-
end
|
30
|
-
|
31
|
-
# Public: Encrypt a string
|
32
|
-
#
|
33
|
-
# Note: nil and empty strings are not encryptable with AES.
|
34
|
-
# When they are encountered, the orignal value is returned.
|
35
|
-
# Otherwise, returns the encrypted string
|
36
|
-
def encrypt(value)
|
37
|
-
return value if value == '' || value.nil?
|
38
|
-
aes.encrypt
|
39
|
-
aes.key = key
|
40
|
-
Base64::encode64("#{aes.random_iv}#{SEPARATOR}#{aes.update(value.to_s) + aes.final}")
|
41
|
-
end
|
42
|
-
|
43
|
-
# Public: Decrypt a string
|
44
|
-
#
|
45
|
-
# Note: nil and empty strings are not encryptable with AES (and thus cannot be decrypted).
|
46
|
-
# When they are encountered, the orignal value is returned.
|
47
|
-
# Otherwise, returns the decrypted string
|
48
|
-
def decrypt(value)
|
49
|
-
return value if value == '' || value.nil?
|
50
|
-
iv, value = Base64::decode64(value.to_s).split(SEPARATOR)
|
51
|
-
aes.decrypt
|
52
|
-
aes.key = key
|
53
|
-
aes.iv = iv
|
54
|
-
aes.update(value) + aes.final
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
|
59
|
-
def legacy
|
60
|
-
unless ENV['CRYPT_KEEPER_IGNORE_LEGACY_DEPRECATION']
|
61
|
-
warn "[DEPRECATION] AES Legacy is now deprecated. Please see http://git.io/uYcp2A"
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
@@ -1,47 +0,0 @@
|
|
1
|
-
require 'crypt_keeper/log_subscriber/mysql_aes'
|
2
|
-
|
3
|
-
module CryptKeeper
|
4
|
-
module Provider
|
5
|
-
class MysqlAes
|
6
|
-
include CryptKeeper::Helper::SQL
|
7
|
-
|
8
|
-
attr_accessor :key
|
9
|
-
|
10
|
-
# Public: Initializes the encryptor
|
11
|
-
#
|
12
|
-
# options - A hash, :key is required
|
13
|
-
def initialize(options = {})
|
14
|
-
legacy
|
15
|
-
ActiveSupport.run_load_hooks(:crypt_keeper_mysql_aes_log, self)
|
16
|
-
|
17
|
-
@key = options.fetch(:key) do
|
18
|
-
raise ArgumentError, "Missing :key"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
# Public: Encrypts a string
|
23
|
-
#
|
24
|
-
# Returns an encrypted string
|
25
|
-
def encrypt(value)
|
26
|
-
Base64.encode64 escape_and_execute_sql(
|
27
|
-
["SELECT AES_ENCRYPT(?, ?)", value, key]).first
|
28
|
-
end
|
29
|
-
|
30
|
-
# Public: Decrypts a string
|
31
|
-
#
|
32
|
-
# Returns a plaintext string
|
33
|
-
def decrypt(value)
|
34
|
-
escape_and_execute_sql(
|
35
|
-
["SELECT AES_DECRYPT(?, ?)", Base64.decode64(value), key]).first
|
36
|
-
end
|
37
|
-
|
38
|
-
private
|
39
|
-
|
40
|
-
def legacy
|
41
|
-
unless ENV['CRYPT_KEEPER_IGNORE_LEGACY_DEPRECATION']
|
42
|
-
warn "[DEPRECATION] MySqlAes Legacy is now deprecated. Please see http://git.io/nXXOlg"
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
@@ -1,73 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module CryptKeeper::LogSubscriber
|
4
|
-
describe MysqlAes do
|
5
|
-
before do
|
6
|
-
CryptKeeper.silence_logs = false
|
7
|
-
end
|
8
|
-
|
9
|
-
use_mysql
|
10
|
-
|
11
|
-
context "AES encryption" do
|
12
|
-
# Fire the ActiveSupport.on_load
|
13
|
-
before do
|
14
|
-
CryptKeeper::Provider::MysqlAesNew.new key: 'secret', salt: 'salt'
|
15
|
-
end
|
16
|
-
|
17
|
-
subject { ::ActiveRecord::LogSubscriber.new }
|
18
|
-
|
19
|
-
let(:input_query) do
|
20
|
-
"SELECT aes_encrypt('encrypt_value', 'encrypt_key'), aes_decrypt('decrypt_value', 'decrypt_key') FROM DUAL;"
|
21
|
-
end
|
22
|
-
|
23
|
-
let(:output_query) do
|
24
|
-
"SELECT aes_encrypt([FILTERED]) FROM DUAL;"
|
25
|
-
end
|
26
|
-
|
27
|
-
let(:input_search_query) do
|
28
|
-
"SELECT \"sensitive_data\".* FROM \"sensitive_data\" WHERE ((aes_decrypt('f'), 'tool') = 'blah')) AND secret = 'testing'"
|
29
|
-
end
|
30
|
-
|
31
|
-
let(:output_search_query) do
|
32
|
-
"SELECT \"sensitive_data\".* FROM \"sensitive_data\" WHERE ((aes_decrypt([FILTERED]) AND secret = 'testing'"
|
33
|
-
end
|
34
|
-
|
35
|
-
it "filters aes functions" do
|
36
|
-
subject.should_receive(:sql_without_mysql_aes) do |event|
|
37
|
-
event.payload[:sql].should == output_query
|
38
|
-
end
|
39
|
-
|
40
|
-
subject.sql(ActiveSupport::Notifications::Event.new(:sql, 1, 1, 1, { sql: input_query }))
|
41
|
-
end
|
42
|
-
|
43
|
-
it "filters aes functions in lowercase" do
|
44
|
-
subject.should_receive(:sql_without_mysql_aes) do |event|
|
45
|
-
event.payload[:sql].should == output_query.downcase.gsub(/filtered/, 'FILTERED')
|
46
|
-
end
|
47
|
-
|
48
|
-
subject.sql(ActiveSupport::Notifications::Event.new(:sql, 1, 1, 1, { sql: input_query.downcase }))
|
49
|
-
end
|
50
|
-
|
51
|
-
it "filters aes functions when searching" do
|
52
|
-
subject.should_receive(:sql_without_mysql_aes) do |event|
|
53
|
-
event.payload[:sql].should == output_search_query
|
54
|
-
end
|
55
|
-
|
56
|
-
subject.sql(ActiveSupport::Notifications::Event.new(:sql, 1, 1, 1, { sql: input_search_query }))
|
57
|
-
end
|
58
|
-
|
59
|
-
it "forces string encodings" do
|
60
|
-
string_encoding_query = "SELECT aes_encrypt('hi \255', 'test')"
|
61
|
-
subject.sql(ActiveSupport::Notifications::Event.new(:sql, 1, 1, 1, { sql: string_encoding_query }))
|
62
|
-
end
|
63
|
-
|
64
|
-
it "skips logging if CryptKeeper.silence_logs is set" do
|
65
|
-
CryptKeeper.silence_logs = true
|
66
|
-
|
67
|
-
subject.should_not_receive(:sql_without_mysql_aes)
|
68
|
-
|
69
|
-
subject.sql(ActiveSupport::Notifications::Event.new(:sql, 1, 1, 1, { sql: input_query }))
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
@@ -1,123 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module CryptKeeper::LogSubscriber
|
4
|
-
describe PostgresPgp do
|
5
|
-
before do
|
6
|
-
CryptKeeper.silence_logs = false
|
7
|
-
end
|
8
|
-
|
9
|
-
use_postgres
|
10
|
-
|
11
|
-
context "Symmetric encryption" do
|
12
|
-
# Fire the ActiveSupport.on_load
|
13
|
-
before do
|
14
|
-
CryptKeeper::Provider::PostgresPgp.new key: 'secret'
|
15
|
-
end
|
16
|
-
|
17
|
-
subject { ::ActiveRecord::LogSubscriber.new }
|
18
|
-
|
19
|
-
let(:input_query) do
|
20
|
-
"SELECT pgp_sym_encrypt('encrypt_value', 'encrypt_key'), pgp_sym_decrypt('decrypt_value', 'decrypt_key') FROM DUAL;"
|
21
|
-
end
|
22
|
-
|
23
|
-
let(:output_query) do
|
24
|
-
"SELECT encrypt([FILTERED]) FROM DUAL;"
|
25
|
-
end
|
26
|
-
|
27
|
-
let(:input_search_query) do
|
28
|
-
"SELECT \"sensitive_data\".* FROM \"sensitive_data\" WHERE ((pgp_sym_decrypt('f'), 'tool') = 'blah')) AND secret = 'testing'"
|
29
|
-
end
|
30
|
-
|
31
|
-
let(:output_search_query) do
|
32
|
-
"SELECT \"sensitive_data\".* FROM \"sensitive_data\" WHERE decrypt([FILTERED]) AND secret = 'testing'"
|
33
|
-
end
|
34
|
-
|
35
|
-
it "filters pgp functions" do
|
36
|
-
subject.should_receive(:sql_without_postgres_pgp) do |event|
|
37
|
-
event.payload[:sql].should == output_query
|
38
|
-
end
|
39
|
-
|
40
|
-
subject.sql(ActiveSupport::Notifications::Event.new(:sql, 1, 1, 1, { sql: input_query }))
|
41
|
-
end
|
42
|
-
|
43
|
-
it "filters pgp functions in lowercase" do
|
44
|
-
subject.should_receive(:sql_without_postgres_pgp) do |event|
|
45
|
-
event.payload[:sql].should == output_query.downcase.gsub(/filtered/, 'FILTERED')
|
46
|
-
end
|
47
|
-
|
48
|
-
subject.sql(ActiveSupport::Notifications::Event.new(:sql, 1, 1, 1, { sql: input_query.downcase }))
|
49
|
-
end
|
50
|
-
|
51
|
-
it "filters pgp functions when searching" do
|
52
|
-
subject.should_receive(:sql_without_postgres_pgp) do |event|
|
53
|
-
event.payload[:sql].should == output_search_query
|
54
|
-
end
|
55
|
-
|
56
|
-
subject.sql(ActiveSupport::Notifications::Event.new(:sql, 1, 1, 1, { sql: input_search_query }))
|
57
|
-
end
|
58
|
-
|
59
|
-
it "forces string encodings" do
|
60
|
-
string_encoding_query = "SELECT pgp_sym_encrypt('hi \255', 'test')"
|
61
|
-
subject.sql(ActiveSupport::Notifications::Event.new(:sql, 1, 1, 1, { sql: string_encoding_query }))
|
62
|
-
end
|
63
|
-
|
64
|
-
it "skips logging if CryptKeeper.silence_logs is set" do
|
65
|
-
CryptKeeper.silence_logs = true
|
66
|
-
|
67
|
-
subject.should_not_receive(:sql_without_postgres_pgp)
|
68
|
-
|
69
|
-
subject.sql(ActiveSupport::Notifications::Event.new(:sql, 1, 1, 1, { sql: input_query }))
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
context "Public key encryption" do
|
74
|
-
let(:public_key) do
|
75
|
-
IO.read(File.join(SPEC_ROOT, 'fixtures', 'public.asc'))
|
76
|
-
end
|
77
|
-
|
78
|
-
let(:private_key) do
|
79
|
-
IO.read(File.join(SPEC_ROOT, 'fixtures', 'private.asc'))
|
80
|
-
end
|
81
|
-
|
82
|
-
# Fire the ActiveSupport.on_load
|
83
|
-
before do
|
84
|
-
CryptKeeper::Provider::PostgresPgpPublicKey.new key: 'secret', public_key: public_key, private_key: private_key
|
85
|
-
end
|
86
|
-
|
87
|
-
subject { ::ActiveRecord::LogSubscriber.new }
|
88
|
-
|
89
|
-
let(:input_query) do
|
90
|
-
"SELECT pgp_pub_encrypt('test', dearmor('#{public_key}
|
91
|
-
'))"
|
92
|
-
end
|
93
|
-
|
94
|
-
let(:output_query) do
|
95
|
-
"SELECT encrypt([FILTERED])"
|
96
|
-
end
|
97
|
-
|
98
|
-
it "filters pgp functions" do
|
99
|
-
subject.should_receive(:sql_without_postgres_pgp) do |event|
|
100
|
-
event.payload[:sql].should == output_query
|
101
|
-
end
|
102
|
-
|
103
|
-
subject.sql(ActiveSupport::Notifications::Event.new(:sql, 1, 1, 1, { sql: input_query }))
|
104
|
-
end
|
105
|
-
|
106
|
-
it "filters pgp functions in lowercase" do
|
107
|
-
subject.should_receive(:sql_without_postgres_pgp) do |event|
|
108
|
-
event.payload[:sql].should == output_query.downcase.gsub(/filtered/, 'FILTERED')
|
109
|
-
end
|
110
|
-
|
111
|
-
subject.sql(ActiveSupport::Notifications::Event.new(:sql, 1, 1, 1, { sql: input_query.downcase }))
|
112
|
-
end
|
113
|
-
|
114
|
-
it "skips logging if CryptKeeper.silence_logs is set" do
|
115
|
-
CryptKeeper.silence_logs = true
|
116
|
-
|
117
|
-
subject.should_not_receive(:sql_without_postgres_pgp)
|
118
|
-
|
119
|
-
subject.sql(ActiveSupport::Notifications::Event.new(:sql, 1, 1, 1, { sql: input_query }))
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|
data/spec/model_spec.rb
DELETED
@@ -1,169 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require 'spec_helper'
|
4
|
-
|
5
|
-
module CryptKeeper
|
6
|
-
describe Model do
|
7
|
-
use_sqlite
|
8
|
-
|
9
|
-
subject { create_model }
|
10
|
-
|
11
|
-
after do
|
12
|
-
CryptKeeper.stub_encryption = false
|
13
|
-
end
|
14
|
-
|
15
|
-
describe "#crypt_keeper" do
|
16
|
-
context "Fields" do
|
17
|
-
it "enables encryption for the given fields" do
|
18
|
-
subject.crypt_keeper :storage, :secret, encryptor: :fake_encryptor
|
19
|
-
subject.crypt_keeper_fields.should == [:storage, :secret]
|
20
|
-
end
|
21
|
-
|
22
|
-
it "raises an exception for missing field" do
|
23
|
-
msg = "Column :none does not exist"
|
24
|
-
subject.crypt_keeper :none, encryptor: :fake_encryptor
|
25
|
-
expect { subject.new.save }.to raise_error(ArgumentError, msg)
|
26
|
-
end
|
27
|
-
|
28
|
-
it "raises an exception for non text fields" do
|
29
|
-
msg = "Column :name must be of type 'text' to be used for encryption"
|
30
|
-
subject.crypt_keeper :name, encryptor: :fake_encryptor
|
31
|
-
expect { subject.new.save }.to raise_error(ArgumentError, msg)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
context "Options" do
|
36
|
-
it "accepts the class name as a string" do
|
37
|
-
subject.crypt_keeper :storage, :secret, key1: 1, key2: 2, encryptor: "FakeEncryptor"
|
38
|
-
subject.send(:encryptor_klass).should == CryptKeeper::Provider::FakeEncryptor
|
39
|
-
end
|
40
|
-
|
41
|
-
it "raises an error on missing encryptor" do
|
42
|
-
expect { subject.crypt_keeper :storage, :secret }.
|
43
|
-
to raise_error(RuntimeError, /You must specify a valid encryptor/)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
context "Encryption and Decryption" do
|
49
|
-
let(:plain_text) { 'plain_text' }
|
50
|
-
let(:cipher_text) { 'tooltxet_nialp' }
|
51
|
-
|
52
|
-
subject { create_encrypted_model :storage, passphrase: 'tool', encryptor: :encryptor }
|
53
|
-
|
54
|
-
it "encrypts the data" do
|
55
|
-
CryptKeeper::Provider::Encryptor.any_instance.should_receive(:encrypt).with('testing')
|
56
|
-
subject.create!(storage: 'testing')
|
57
|
-
end
|
58
|
-
|
59
|
-
it "decrypts the data" do
|
60
|
-
record = subject.create!(storage: 'testing')
|
61
|
-
CryptKeeper::Provider::Encryptor.any_instance.should_receive(:decrypt).at_least(1).times.with('toolgnitset')
|
62
|
-
subject.find(record.id).storage
|
63
|
-
end
|
64
|
-
|
65
|
-
it "returns the plaintext on decrypt" do
|
66
|
-
record = subject.create!(storage: 'testing')
|
67
|
-
subject.find(record.id).storage.should == 'testing'
|
68
|
-
end
|
69
|
-
|
70
|
-
it "does not encrypt or decrypt nil" do
|
71
|
-
data = subject.create!(storage: nil)
|
72
|
-
data.storage.should be_nil
|
73
|
-
end
|
74
|
-
|
75
|
-
it "does not encrypt or decrypt empty strings" do
|
76
|
-
data = subject.create!(storage: "")
|
77
|
-
data.storage.should be_empty
|
78
|
-
end
|
79
|
-
|
80
|
-
it "converts numbers to strings" do
|
81
|
-
data = subject.create!(storage: 1)
|
82
|
-
data.reload.storage.should == "1"
|
83
|
-
end
|
84
|
-
|
85
|
-
it "does not decrypt when stubbing is enabled" do
|
86
|
-
CryptKeeper.stub_encryption = true
|
87
|
-
record = subject.create!(storage: "testing")
|
88
|
-
CryptKeeper::Provider::Encryptor.any_instance.should_not_receive(:decrypt)
|
89
|
-
subject.find(record.id).storage
|
90
|
-
end
|
91
|
-
|
92
|
-
it "does not decrypt when stubbing is enabled after model is created" do
|
93
|
-
record = subject.create!(storage: "testing")
|
94
|
-
CryptKeeper.stub_encryption = true
|
95
|
-
CryptKeeper::Provider::Encryptor.any_instance.should_not_receive(:decrypt)
|
96
|
-
subject.find(record.id).storage
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
|
-
context "Search" do
|
101
|
-
subject { create_encrypted_model :storage, passphrase: 'tool', encryptor: :search_encryptor }
|
102
|
-
|
103
|
-
it "searches if supported" do
|
104
|
-
expect { subject.search_by_plaintext(:storage, 'test1') }.to_not raise_error
|
105
|
-
end
|
106
|
-
|
107
|
-
it "complains about bad columns" do
|
108
|
-
expect { subject.search_by_plaintext(:what, 'test1') }.to raise_error(/what is not a crypt_keeper field/)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
context "Encodings" do
|
113
|
-
subject { create_encrypted_model :storage, key: 'tool', salt: 'salt', encryptor: :aes_new, encoding: 'utf-8' }
|
114
|
-
|
115
|
-
it "forces the encoding on decrypt" do
|
116
|
-
record = subject.create!(storage: 'Tromsø')
|
117
|
-
record.reload
|
118
|
-
expect(record.storage).to eql('Tromsø')
|
119
|
-
end
|
120
|
-
|
121
|
-
it "converts from other encodings" do
|
122
|
-
plaintext = "\xC2\xA92011 AACR".force_encoding('ASCII-8BIT')
|
123
|
-
record = subject.create!(storage: plaintext)
|
124
|
-
record.reload
|
125
|
-
expect(record.storage.encoding.name).to eql('UTF-8')
|
126
|
-
end
|
127
|
-
end
|
128
|
-
|
129
|
-
context "Initial Table Encryption" do
|
130
|
-
subject { create_encrypted_model :storage, key: 'tool', salt: 'salt', encryptor: :aes_new }
|
131
|
-
|
132
|
-
before do
|
133
|
-
subject.delete_all
|
134
|
-
c = create_model
|
135
|
-
5.times { |i| c.create! storage: "testing#{i}" }
|
136
|
-
end
|
137
|
-
|
138
|
-
it "encrypts the table" do
|
139
|
-
expect { subject.first(5).map(&:storage) }.to raise_error(OpenSSL::Cipher::CipherError)
|
140
|
-
subject.encrypt_table!
|
141
|
-
expect { subject.first(5).map(&:storage) }.not_to raise_error
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
context "Table Decryption (Reverse of Initial Table Encryption)" do
|
146
|
-
subject { create_encrypted_model :storage, key: 'tool', salt: 'salt', encryptor: :aes_new }
|
147
|
-
let!(:storage_entries) { 5.times.map { |i| "testing#{i}" } }
|
148
|
-
|
149
|
-
before do
|
150
|
-
subject.delete_all
|
151
|
-
storage_entries.each { |entry| subject.create! storage: entry}
|
152
|
-
end
|
153
|
-
|
154
|
-
it "decrypts the table" do
|
155
|
-
subject.decrypt_table!
|
156
|
-
expect( create_model.first(5).map(&:storage) ).to eq( storage_entries )
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
context "Missing Attributes" do
|
161
|
-
subject { create_encrypted_model :storage, key: 'tool', salt: 'salt', encryptor: :aes_new, encoding: 'utf-8' }
|
162
|
-
|
163
|
-
it "doesn't attempt decryption of missing attributes" do
|
164
|
-
subject.create!(storage: 'blah')
|
165
|
-
expect { subject.select(:id).first }.to_not raise_error
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|