crypt_keeper 0.15.0.pre → 0.16.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -0
  3. data/Appraisals +5 -0
  4. data/README.md +26 -7
  5. data/bin/crypt_keeper +99 -0
  6. data/crypt_keeper.gemspec +4 -2
  7. data/gemfiles/activerecord_3_1.gemfile.lock +10 -6
  8. data/gemfiles/activerecord_3_2.gemfile.lock +10 -6
  9. data/gemfiles/activerecord_4_0.gemfile.lock +10 -6
  10. data/gemfiles/activerecord_4_1.gemfile +8 -0
  11. data/gemfiles/activerecord_4_1.gemfile.lock +117 -0
  12. data/lib/crypt_keeper/helper.rb +8 -0
  13. data/lib/crypt_keeper/log_subscriber/mysql_aes.rb +1 -1
  14. data/lib/crypt_keeper/log_subscriber/postgres_pgp.rb +3 -3
  15. data/lib/crypt_keeper/provider/aes.rb +9 -8
  16. data/lib/crypt_keeper/provider/aes_new.rb +53 -0
  17. data/lib/crypt_keeper/provider/mysql_aes.rb +7 -2
  18. data/lib/crypt_keeper/provider/mysql_aes_new.rb +43 -0
  19. data/lib/crypt_keeper/provider/postgres_pgp.rb +1 -1
  20. data/lib/crypt_keeper/provider/postgres_pgp_public_key.rb +57 -0
  21. data/lib/crypt_keeper/version.rb +1 -1
  22. data/lib/crypt_keeper.rb +3 -0
  23. data/spec/fixtures/private.asc +60 -0
  24. data/spec/fixtures/public.asc +31 -0
  25. data/spec/log_subscriber/postgres_pgp_spec.rb +82 -14
  26. data/spec/provider/aes_new_spec.rb +45 -0
  27. data/spec/provider/aes_spec.rb +29 -7
  28. data/spec/provider/mysql_aes_new_spec.rb +52 -0
  29. data/spec/provider/mysql_aes_spec.rb +0 -16
  30. data/spec/provider/postgres_pgp_public_key_spec.rb +70 -0
  31. data/spec/provider/postgres_pgp_spec.rb +6 -4
  32. data/spec/spec_helper.rb +16 -1
  33. data/spec/support/active_record.rb +3 -2
  34. metadata +53 -10
  35. data/spec/log_subscriber/mysql_aes_spec.rb +0 -30
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: aa70dc82c3b6dac6568cf2c5b7a5c291d9af6238
4
- data.tar.gz: f834bf7497a5dc1f6e26361092bbd8b9adedf57f
3
+ metadata.gz: 742dc47f65ba6f9c21e39dad6b34a25196e74978
4
+ data.tar.gz: 4e5eaafa1b9876217c739a4c3649fc52308dc0ff
5
5
  SHA512:
6
- metadata.gz: 16a5a01696fd83cac4d8a7db9efc1bf3a4e56b81a77e6ebcac01ae7d58bfcefbda041866fb18c3485813c69c1fadd3f2bdb5c69f2a66b4c4a382b989a82cfcd7
7
- data.tar.gz: 990e7f447a55d5c91bbbaa2865aef249bc7b3d3723bb5367044731e9e5bafacbb0a401ca1df027ad1686d1ed07013b5bf4c3091c557d0003474e3f223be4d297
6
+ metadata.gz: f43ec8ae9564d08e804d5539ee04fff90b072502e65128a983c4727953ad930a00cfc2f72bb50d5e6b6585c7bd43bb83199f75bd3cabffb27e317a3f56539dd6
7
+ data.tar.gz: 097e420adcb718391b14476dff3a8a1c00279bcd211df9cef3f163a424bd1267d5be85b1cfa15fe1773a77cd930cc9fea1c4c7ff0d4a406ca4c7146a570f5d61
data/.travis.yml CHANGED
@@ -4,6 +4,7 @@ gemfile:
4
4
  - gemfiles/activerecord_3_1.gemfile
5
5
  - gemfiles/activerecord_3_2.gemfile
6
6
  - gemfiles/activerecord_4_0.gemfile
7
+ - gemfiles/activerecord_4_1.gemfile
7
8
  before_install:
8
9
  - bundle install
9
10
  before_script:
@@ -20,3 +21,6 @@ notifications:
20
21
  rvm:
21
22
  - 1.9.3
22
23
  - 2.0.0
24
+ - 2.1.1
25
+ addons:
26
+ postgresql: 9.3
data/Appraisals CHANGED
@@ -12,3 +12,8 @@ appraise "activerecord_4_0" do
12
12
  gem 'activerecord', '~> 4.0.0'
13
13
  gem 'activesupport', '~> 4.0.0'
14
14
  end
15
+
16
+ appraise "activerecord_4_1" do
17
+ gem 'activerecord', '~> 4.1.0'
18
+ gem 'activesupport', '~> 4.1.0'
19
+ end
data/README.md CHANGED
@@ -27,7 +27,7 @@ simple that *just works*.
27
27
 
28
28
  ```ruby
29
29
  class MyModel < ActiveRecord::Base
30
- crypt_keeper :field, :other_field, :encryptor => :aes, :key => 'super_good_password'
30
+ crypt_keeper :field, :other_field, :encryptor => :aes, :key => 'super_good_password', salt: 'salt'
31
31
  end
32
32
 
33
33
  model = MyModel.new(field: 'sometext')
@@ -51,15 +51,23 @@ update the content without going through the current encryptor.
51
51
 
52
52
  There are three included encryptors.
53
53
 
54
- * [AES](https://github.com/jmazzi/crypt_keeper/blob/master/lib/crypt_keeper/provider/aes.rb)
54
+ * [AES](https://github.com/jmazzi/crypt_keeper/blob/master/lib/crypt_keeper/provider/aes_new.rb)
55
55
  * Encryption is peformed using AES-256 via OpenSSL.
56
+ * Passphrases are derived using [PBKDF2](http://en.wikipedia.org/wiki/PBKDF2)
56
57
 
58
+ * [AES Legacy](https://github.com/jmazzi/crypt_keeper/blob/master/lib/crypt_keeper/provider/aes.rb) *DEPRECATED*
59
+ * Encryption is peformed using AES-256 via OpenSSL.
57
60
 
58
- * [MySQL AES](https://github.com/jmazzi/crypt_keeper/blob/master/lib/crypt_keeper/provider/mysql_aes.rb)
61
+ * [MySQL AES](https://github.com/jmazzi/crypt_keeper/blob/master/lib/crypt_keeper/provider/mysql_aes_new.rb)
59
62
  * Encryption is peformed MySQL's native AES functions.
60
63
  * ActiveRecord logs are [automatically](https://github.com/jmazzi/crypt_keeper/blob/master/lib/crypt_keeper/log_subscriber/mysql_aes.rb)
61
64
  filtered for you to protect sensitive data from being logged.
65
+ * Passphrases are derived using [PBKDF2](http://en.wikipedia.org/wiki/PBKDF2)
62
66
 
67
+ * [MySQL AES](https://github.com/jmazzi/crypt_keeper/blob/master/lib/crypt_keeper/provider/mysql_aes.rb) *DEPRECATED*
68
+ * Encryption is peformed MySQL's native AES functions.
69
+ * ActiveRecord logs are [automatically](https://github.com/jmazzi/crypt_keeper/blob/master/lib/crypt_keeper/log_subscriber/mysql_aes.rb)
70
+ filtered for you to protect senitive data from being logged.
63
71
 
64
72
  * [PostgreSQL PGP](https://github.com/jmazzi/crypt_keeper/blob/master/lib/crypt_keeper/provider/postgres_pgp.rb).
65
73
  * Encryption is performed using PostgresSQL's native [PGP functions](http://www.postgresql.org/docs/9.1/static/pgcrypto.html).
@@ -68,15 +76,25 @@ There are three included encryptors.
68
76
  * ActiveRecord logs are [automatically](https://github.com/jmazzi/crypt_keeper/blob/master/lib/crypt_keeper/log_subscriber/postgres_pgp.rb)
69
77
  filtered for you to protect senitive data from being logged.
70
78
  * Custom options can be set through the `:pgcrypto_options`. E.g. `crypt_keeper :field, encryptor: :postgres_pgp, pgcrypto_options: 'compress-level=9'
79
+ * Passphrases are hashed by PostgresSQL itself using a [String2Key (S2K)](http://www.postgresql.org/docs/9.2/static/pgcrypto.html) algorithm. This is rather similar to crypt() algorithms — purposefully slow and with random salt — but it produces a full-length binary key.
80
+
81
+ * [PostgreSQL PGP Public Key](https://github.com/jmazzi/crypt_keeper/blob/master/lib/crypt_keeper/provider/postgres_pgp_public_key.rb).
82
+ * Encryption is performed using PostgresSQL's native [PGP functions](http://www.postgresql.org/docs/9.1/static/pgcrypto.html).
83
+ * It requires the `pgcrypto` PostgresSQL extension:
84
+ `CREATE EXTENSION IF NOT EXISTS pgcrypto`
85
+ * ActiveRecord logs are [automatically](https://github.com/jmazzi/crypt_keeper/blob/master/lib/crypt_keeper/log_subscriber/postgres_pgp.rb)
86
+ filtered for you to protect senitive data from being logged.
87
+ * Accepts a public and private_key. The private key is optional. If the private key is not present the ciphertext value is returned instead of the plaintext. This allows you to keep the private key off certain servers. Encryption is possible with only a public key. Any server that needs access to the plaintext will need the private key.
88
+ * Passphrases are hashed by PostgresSQL itself using a [String2Key (S2K)](http://www.postgresql.org/docs/9.2/static/pgcrypto.html) algorithm. This is rather similar to crypt() algorithms — purposefully slow and with random salt — but it produces a full-length binary key.
71
89
 
72
90
  ## Searching
73
- Searching ciphertext is a complex problem that varies depending on the encryption algorithm you choose. All of the bundled providers include search support, but they have some caveats.
91
+ Searching ciphertext is a complex problem that varies depending on the encryption algorithm you choose. All of the bundled providers include search support, but they have some caveats.
74
92
 
75
93
  * AES
76
94
  * The Ruby implementation of AES uses a random initialization vector. 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.
77
95
 
78
96
  * Mysql AES
79
- * Surprisingly, MySQL's implementation of AES does not use a random initialization vector. The column containing the ciphertext can be indexed and searched quickly.
97
+ * Surprisingly, MySQL's implementation of AES does not use a random initialization vector. The column containing the ciphertext can be indexed and searched quickly.
80
98
 
81
99
  * PostgresSQL PGP
82
100
  * 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.
@@ -123,10 +141,11 @@ end
123
141
 
124
142
  ## Requirements
125
143
 
126
- CryptKeeper has been tested against ActiveRecord 3.1, 3.2, 4.0 using ruby
127
- 1.9.3 and 2.0.0
144
+ CryptKeeper has been tested against ActiveRecord 3.1, 3.2, 4.0, 4.1 using ruby
145
+ 1.9.3, 2.0.0 and 2.1.1
128
146
 
129
147
  ActiveRecord 4.0 is supported starting with v0.11.0.
148
+ ActiveRecord 4.1 is supported starting with v0.16.0. (unreleased, use master branch)
130
149
 
131
150
  ## Installation
132
151
 
data/bin/crypt_keeper ADDED
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.join(File.dirname(__FILE__), "/../lib"))
3
+ require 'crypt_keeper'
4
+ require 'yaml'
5
+ require 'active_support/core_ext'
6
+ require 'active_record/log_subscriber'
7
+ require 'optparse'
8
+
9
+ if !File.exists?('config/database.yml')
10
+ puts "Please run this from the root of the rails project"
11
+ exit 1
12
+ else
13
+ config = YAML::load_file('./config/database.yml')
14
+ end
15
+
16
+ options = {}
17
+ OptionParser.new do |opts|
18
+ opts.banner = "Usage: crypt_keeper [options]"
19
+
20
+ opts.on("--old-key [key]", "The old encryption key") do |v|
21
+ options[:old_key] = v
22
+ end
23
+
24
+ opts.on("--new-key [key]", "The new encryption key") do |v|
25
+ options[:new_key] = v
26
+ end
27
+
28
+ opts.on("--salt [salt]", "The salt for the new encryption") do |v|
29
+ options[:salt] = v
30
+ end
31
+
32
+ opts.on("--table-name [table_name]", "The name of the table") do |v|
33
+ options[:table_name] = v
34
+ end
35
+
36
+ opts.on("--columns [field1,field2]", "A comma separated list of column names which are encrypted") do |v|
37
+ options[:columns] = v
38
+ end
39
+
40
+ opts.on("--old-encryptor [old encryptor]", "The old encryptor") do |v|
41
+ options[:old_encryptor] = v
42
+ end
43
+
44
+ opts.on("--query-log [file]", "ActiveRecord query log") do |v|
45
+ options[:query_log] = v
46
+ end
47
+ end.parse!
48
+
49
+ def get_required_arg(arg, options)
50
+ options.fetch(arg) { raise ArgumentError, "--#{arg.to_s.sub('_', '-')} is a required option"}
51
+ end
52
+
53
+ old_key = get_required_arg(:old_key, options)
54
+ new_key = get_required_arg(:new_key, options)
55
+ salt = get_required_arg(:salt, options)
56
+ table_name = get_required_arg(:table_name, options)
57
+ columns = get_required_arg(:columns, options).split(',')
58
+ encryptor = get_required_arg(:old_encryptor, options)
59
+ env = ENV.fetch('RAILS_ENV', 'development')
60
+
61
+ if options[:query_log].present?
62
+ ActiveRecord::Base.logger = Logger.new(options[:query_log])
63
+ end
64
+
65
+ if encryptor == "aes"
66
+ old_encryptor = CryptKeeper::Provider::Aes.new(key: old_key)
67
+ new_encryptor = CryptKeeper::Provider::AesNew.new(key: new_key, salt: salt)
68
+ elsif encryptor == "mysql_aes"
69
+ old_encryptor = CryptKeeper::Provider::MysqlAes.new(key: old_key)
70
+ new_encryptor = CryptKeeper::Provider::MysqlAesNew.new(key: new_key, salt: salt)
71
+ else
72
+ puts "#{encryptor} is not a known encryptor that can be migrated"
73
+ exit 1
74
+ end
75
+
76
+ class MigrateData < ActiveRecord::Base
77
+ def self.reencrypt_all(encrypted_fields, old_encryptor, new_encryptor)
78
+ ActiveRecord::Base.transaction do
79
+ find_each do |record|
80
+ new_hash = {}
81
+ encrypted_fields.each do |f|
82
+ plaintext = old_encryptor.decrypt(record[f])
83
+ ciphertext = new_encryptor.encrypt(plaintext)
84
+ new_hash[f] = ciphertext
85
+ end
86
+
87
+ record.update_attributes! new_hash
88
+ yield record if block_given?
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ ActiveRecord::Base.establish_connection(config.fetch(env))
95
+ MigrateData.table_name = table_name
96
+
97
+ MigrateData.reencrypt_all(columns, old_encryptor, new_encryptor) do |r|
98
+ puts "#{r.id} is now re-encrypted"
99
+ end
data/crypt_keeper.gemspec CHANGED
@@ -16,8 +16,10 @@ Gem::Specification.new do |gem|
16
16
  gem.require_paths = ["lib"]
17
17
  gem.version = CryptKeeper::VERSION
18
18
 
19
- gem.add_runtime_dependency 'activerecord', '>= 3.1', '< 4.1'
20
- gem.add_runtime_dependency 'activesupport', '>= 3.1', '< 4.1'
19
+ gem.add_runtime_dependency 'activerecord', '>= 3.1', '< 4.2'
20
+ gem.add_runtime_dependency 'activesupport', '>= 3.1', '< 4.2'
21
+ gem.add_runtime_dependency 'aes', '~> 0.5.0'
22
+ gem.add_runtime_dependency 'armor', '~> 0.0.2'
21
23
 
22
24
  gem.add_development_dependency 'rspec', '~> 2.13.0'
23
25
  gem.add_development_dependency 'guard', '~> 1.8.0'
@@ -1,9 +1,11 @@
1
1
  PATH
2
2
  remote: /Users/justin/work/jmazzi/crypt_keeper
3
3
  specs:
4
- crypt_keeper (0.14.0.pre)
5
- activerecord (>= 3.1, < 4.1)
6
- activesupport (>= 3.1, < 4.1)
4
+ crypt_keeper (0.16.0.pre)
5
+ activerecord (>= 3.1, < 4.2)
6
+ activesupport (>= 3.1, < 4.2)
7
+ aes (~> 0.5.0)
8
+ armor (~> 0.0.2)
7
9
 
8
10
  GEM
9
11
  remote: https://rubygems.org/
@@ -19,10 +21,12 @@ GEM
19
21
  tzinfo (~> 0.3.29)
20
22
  activesupport (3.1.12)
21
23
  multi_json (~> 1.0)
24
+ aes (0.5.0)
22
25
  appraisal (0.5.2)
23
26
  bundler
24
27
  rake
25
28
  arel (2.2.3)
29
+ armor (0.0.2)
26
30
  builder (3.0.4)
27
31
  coderay (1.0.9)
28
32
  coveralls (0.7.0)
@@ -51,7 +55,7 @@ GEM
51
55
  lumberjack (1.0.4)
52
56
  method_source (0.8.2)
53
57
  mime-types (1.25)
54
- multi_json (1.8.0)
58
+ multi_json (1.8.2)
55
59
  mysql2 (0.3.13)
56
60
  pg (0.15.1)
57
61
  pry (0.9.12.2)
@@ -83,8 +87,8 @@ GEM
83
87
  term-ansicolor (1.2.2)
84
88
  tins (~> 0.8)
85
89
  thor (0.18.1)
86
- tins (0.11.0)
87
- tzinfo (0.3.37)
90
+ tins (0.12.0)
91
+ tzinfo (0.3.38)
88
92
 
89
93
  PLATFORMS
90
94
  ruby
@@ -1,9 +1,11 @@
1
1
  PATH
2
2
  remote: /Users/justin/work/jmazzi/crypt_keeper
3
3
  specs:
4
- crypt_keeper (0.14.0.pre)
5
- activerecord (>= 3.1, < 4.1)
6
- activesupport (>= 3.1, < 4.1)
4
+ crypt_keeper (0.16.0.pre)
5
+ activerecord (>= 3.1, < 4.2)
6
+ activesupport (>= 3.1, < 4.2)
7
+ aes (~> 0.5.0)
8
+ armor (~> 0.0.2)
7
9
 
8
10
  GEM
9
11
  remote: https://rubygems.org/
@@ -19,10 +21,12 @@ GEM
19
21
  activesupport (3.2.14)
20
22
  i18n (~> 0.6, >= 0.6.4)
21
23
  multi_json (~> 1.0)
24
+ aes (0.5.0)
22
25
  appraisal (0.5.2)
23
26
  bundler
24
27
  rake
25
28
  arel (3.0.2)
29
+ armor (0.0.2)
26
30
  builder (3.0.4)
27
31
  coderay (1.0.9)
28
32
  coveralls (0.7.0)
@@ -51,7 +55,7 @@ GEM
51
55
  lumberjack (1.0.4)
52
56
  method_source (0.8.2)
53
57
  mime-types (1.25)
54
- multi_json (1.8.0)
58
+ multi_json (1.8.2)
55
59
  mysql2 (0.3.13)
56
60
  pg (0.15.1)
57
61
  pry (0.9.12.2)
@@ -83,8 +87,8 @@ GEM
83
87
  term-ansicolor (1.2.2)
84
88
  tins (~> 0.8)
85
89
  thor (0.18.1)
86
- tins (0.11.0)
87
- tzinfo (0.3.37)
90
+ tins (0.12.0)
91
+ tzinfo (0.3.38)
88
92
 
89
93
  PLATFORMS
90
94
  ruby
@@ -1,9 +1,11 @@
1
1
  PATH
2
2
  remote: /Users/justin/work/jmazzi/crypt_keeper
3
3
  specs:
4
- crypt_keeper (0.14.0.pre)
5
- activerecord (>= 3.1, < 4.1)
6
- activesupport (>= 3.1, < 4.1)
4
+ crypt_keeper (0.16.0.pre)
5
+ activerecord (>= 3.1, < 4.2)
6
+ activesupport (>= 3.1, < 4.2)
7
+ aes (~> 0.5.0)
8
+ armor (~> 0.0.2)
7
9
 
8
10
  GEM
9
11
  remote: https://rubygems.org/
@@ -23,10 +25,12 @@ GEM
23
25
  multi_json (~> 1.3)
24
26
  thread_safe (~> 0.1)
25
27
  tzinfo (~> 0.3.37)
28
+ aes (0.5.0)
26
29
  appraisal (0.5.2)
27
30
  bundler
28
31
  rake
29
32
  arel (4.0.0)
33
+ armor (0.0.2)
30
34
  atomic (1.1.14)
31
35
  builder (3.1.4)
32
36
  coderay (1.0.9)
@@ -57,7 +61,7 @@ GEM
57
61
  method_source (0.8.2)
58
62
  mime-types (1.25)
59
63
  minitest (4.7.5)
60
- multi_json (1.8.0)
64
+ multi_json (1.8.2)
61
65
  mysql2 (0.3.13)
62
66
  pg (0.15.1)
63
67
  pry (0.9.12.2)
@@ -91,8 +95,8 @@ GEM
91
95
  thor (0.18.1)
92
96
  thread_safe (0.1.3)
93
97
  atomic
94
- tins (0.11.0)
95
- tzinfo (0.3.37)
98
+ tins (0.12.0)
99
+ tzinfo (0.3.38)
96
100
 
97
101
  PLATFORMS
98
102
  ruby
@@ -0,0 +1,8 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 4.1.0"
6
+ gem "activesupport", "~> 4.1.0"
7
+
8
+ gemspec :path=>"../"
@@ -0,0 +1,117 @@
1
+ PATH
2
+ remote: /Users/justin/work/jmazzi/crypt_keeper
3
+ specs:
4
+ crypt_keeper (0.16.0.pre)
5
+ activerecord (>= 3.1, < 4.2)
6
+ activesupport (>= 3.1, < 4.2)
7
+ aes (~> 0.5.0)
8
+ armor (~> 0.0.2)
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ activemodel (4.1.0)
14
+ activesupport (= 4.1.0)
15
+ builder (~> 3.1)
16
+ activerecord (4.1.0)
17
+ activemodel (= 4.1.0)
18
+ activesupport (= 4.1.0)
19
+ arel (~> 5.0.0)
20
+ activesupport (4.1.0)
21
+ i18n (~> 0.6, >= 0.6.9)
22
+ json (~> 1.7, >= 1.7.7)
23
+ minitest (~> 5.1)
24
+ thread_safe (~> 0.1)
25
+ tzinfo (~> 1.1)
26
+ aes (0.5.0)
27
+ appraisal (0.5.2)
28
+ bundler
29
+ rake
30
+ arel (5.0.1.20140414130214)
31
+ armor (0.0.3)
32
+ builder (3.2.2)
33
+ coderay (1.1.0)
34
+ coveralls (0.7.0)
35
+ multi_json (~> 1.3)
36
+ rest-client
37
+ simplecov (>= 0.7)
38
+ term-ansicolor
39
+ thor
40
+ diff-lcs (1.2.5)
41
+ docile (1.1.3)
42
+ ffi (1.9.3)
43
+ formatador (0.2.4)
44
+ guard (1.8.3)
45
+ formatador (>= 0.2.4)
46
+ listen (~> 1.3)
47
+ lumberjack (>= 1.0.2)
48
+ pry (>= 0.9.10)
49
+ thor (>= 0.14.6)
50
+ guard-rspec (2.5.4)
51
+ guard (>= 1.1)
52
+ rspec (~> 2.11)
53
+ i18n (0.6.9)
54
+ json (1.8.1)
55
+ listen (1.3.1)
56
+ rb-fsevent (>= 0.9.3)
57
+ rb-inotify (>= 0.9)
58
+ rb-kqueue (>= 0.2)
59
+ lumberjack (1.0.5)
60
+ method_source (0.8.2)
61
+ mime-types (2.2)
62
+ minitest (5.3.3)
63
+ multi_json (1.9.2)
64
+ mysql2 (0.3.15)
65
+ pg (0.15.1)
66
+ pry (0.9.12.6)
67
+ coderay (~> 1.0)
68
+ method_source (~> 0.8)
69
+ slop (~> 3.4)
70
+ rake (10.0.4)
71
+ rb-fsevent (0.9.4)
72
+ rb-inotify (0.9.3)
73
+ ffi (>= 0.5.0)
74
+ rb-kqueue (0.2.2)
75
+ ffi (>= 0.5.0)
76
+ rest-client (1.6.7)
77
+ mime-types (>= 1.16)
78
+ rspec (2.13.0)
79
+ rspec-core (~> 2.13.0)
80
+ rspec-expectations (~> 2.13.0)
81
+ rspec-mocks (~> 2.13.0)
82
+ rspec-core (2.13.1)
83
+ rspec-expectations (2.13.0)
84
+ diff-lcs (>= 1.1.3, < 2.0)
85
+ rspec-mocks (2.13.1)
86
+ simplecov (0.8.2)
87
+ docile (~> 1.1.0)
88
+ multi_json
89
+ simplecov-html (~> 0.8.0)
90
+ simplecov-html (0.8.0)
91
+ slop (3.5.0)
92
+ sqlite3 (1.3.9)
93
+ term-ansicolor (1.3.0)
94
+ tins (~> 1.0)
95
+ thor (0.19.1)
96
+ thread_safe (0.3.3)
97
+ tins (1.1.0)
98
+ tzinfo (1.1.0)
99
+ thread_safe (~> 0.1)
100
+
101
+ PLATFORMS
102
+ ruby
103
+
104
+ DEPENDENCIES
105
+ activerecord (~> 4.1.0)
106
+ activesupport (~> 4.1.0)
107
+ appraisal (~> 0.5.2)
108
+ coveralls
109
+ crypt_keeper!
110
+ guard (~> 1.8.0)
111
+ guard-rspec (~> 2.5.4)
112
+ mysql2 (~> 0.3.11)
113
+ pg (~> 0.15.1)
114
+ rake (~> 10.0.3)
115
+ rb-fsevent (~> 0.9.1)
116
+ rspec (~> 2.13.0)
117
+ sqlite3
@@ -10,6 +10,14 @@ module CryptKeeper
10
10
  end
11
11
  end
12
12
 
13
+ module DigestPassphrase
14
+ def digest_passphrase(key, salt)
15
+ raise ArgumentError.new("Missing :key") if key.blank?
16
+ raise ArgumentError.new("Missing :salt") if salt.blank?
17
+ ::Armor.digest(key, salt)
18
+ end
19
+ end
20
+
13
21
  module Serializer
14
22
  def dump(value)
15
23
  if value.blank?
@@ -12,7 +12,7 @@ module CryptKeeper
12
12
 
13
13
  # Public: Prevents sensitive data from being logged
14
14
  def sql_with_mysql_aes(event)
15
- filter = /(aes_(encrypt|decrypt))\(((.|\n)*?)\)/i
15
+ filter = /(aes_(encrypt|decrypt))\(.*\)/i
16
16
 
17
17
  event.payload[:sql] = event.payload[:sql].gsub(filter) do |_|
18
18
  "#{$1}([FILTERED])"
@@ -12,10 +12,10 @@ module CryptKeeper
12
12
 
13
13
  # Public: Prevents sensitive data from being logged
14
14
  def sql_with_postgres_pgp(event)
15
- filter = /(pgp_sym_(encrypt|decrypt))\(((.|\n)*?)\)/i
15
+ filter = /(\(*)pgp_(sym|pub)_(?<operation>decrypt|encrypt)(\(+.*\)+)/im
16
16
 
17
17
  event.payload[:sql] = event.payload[:sql].gsub(filter) do |_|
18
- "#{$1}([FILTERED])"
18
+ "#{$~[:operation]}([FILTERED])"
19
19
  end
20
20
 
21
21
  sql_without_postgres_pgp(event)
@@ -24,6 +24,6 @@ module CryptKeeper
24
24
  end
25
25
  end
26
26
 
27
- ActiveSupport.on_load :crypt_keeper_posgres_pgp_log do
27
+ ActiveSupport.on_load :crypt_keeper_postgres_pgp_log do
28
28
  ActiveRecord::LogSubscriber.send :include, CryptKeeper::LogSubscriber::PostgresPgp
29
29
  end
@@ -5,7 +5,6 @@ require 'base64'
5
5
  module CryptKeeper
6
6
  module Provider
7
7
  class Aes
8
- # A value to split the iv and cipher text with
9
8
  SEPARATOR = ":crypt_keeper:"
10
9
 
11
10
  # Public: The encryption key
@@ -18,6 +17,7 @@ module CryptKeeper
18
17
  #
19
18
  # options - A hash of options. :key is required
20
19
  def initialize(options = {})
20
+ legacy
21
21
  @aes = ::OpenSSL::Cipher::Cipher.new("AES-256-CBC")
22
22
  @aes.padding = 1
23
23
 
@@ -34,6 +34,7 @@ module CryptKeeper
34
34
  # When they are encountered, the orignal value is returned.
35
35
  # Otherwise, returns the encrypted string
36
36
  def encrypt(value)
37
+ return value if value == '' || value.nil?
37
38
  aes.encrypt
38
39
  aes.key = key
39
40
  Base64::encode64("#{aes.random_iv}#{SEPARATOR}#{aes.update(value.to_s) + aes.final}")
@@ -45,6 +46,7 @@ module CryptKeeper
45
46
  # When they are encountered, the orignal value is returned.
46
47
  # Otherwise, returns the decrypted string
47
48
  def decrypt(value)
49
+ return value if value == '' || value.nil?
48
50
  iv, value = Base64::decode64(value.to_s).split(SEPARATOR)
49
51
  aes.decrypt
50
52
  aes.key = key
@@ -52,13 +54,12 @@ module CryptKeeper
52
54
  aes.update(value) + aes.final
53
55
  end
54
56
 
55
- # Public: Search for a record
56
- #
57
- # record - An ActiveRecord collection
58
- # field - The field to search
59
- # criteria - A string to search with
60
- def search(records, field, criteria)
61
- records.select { |record| record[field] == criteria }
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
62
63
  end
63
64
  end
64
65
  end
@@ -0,0 +1,53 @@
1
+ require 'aes'
2
+ require 'armor'
3
+
4
+ module CryptKeeper
5
+ module Provider
6
+ class AesNew
7
+ include CryptKeeper::Helper::DigestPassphrase
8
+
9
+ # Public: The encryption key
10
+ attr_accessor :key
11
+
12
+ # Public: Initializes the class
13
+ #
14
+ # options - A hash of options. :key and :salt are required
15
+ def initialize(options = {})
16
+ @key = digest_passphrase(options[:key], options[:salt])
17
+ end
18
+
19
+ # Public: Encrypt a string
20
+ #
21
+ # Note: nil and empty strings are not encryptable with AES.
22
+ # When they are encountered, the orignal value is returned.
23
+ # Otherwise, returns the encrypted string
24
+ #
25
+ # Returns a String
26
+ def encrypt(value)
27
+ AES.encrypt(value, key)
28
+ end
29
+
30
+ # Public: Decrypt a string
31
+ #
32
+ # Note: nil and empty strings are not encryptable with AES (and thus cannot be decrypted).
33
+ # When they are encountered, the orignal value is returned.
34
+ # Otherwise, returns the decrypted string
35
+ #
36
+ # Returns a String
37
+ def decrypt(value)
38
+ AES.decrypt(value, key)
39
+ end
40
+
41
+ # Public: Search for a record
42
+ #
43
+ # record - An ActiveRecord collection
44
+ # field - The field to search
45
+ # criteria - A string to search with
46
+ #
47
+ # Returns an Enumerable
48
+ def search(records, field, criteria)
49
+ records.select { |record| record[field] == criteria }
50
+ end
51
+ end
52
+ end
53
+ end
@@ -11,6 +11,7 @@ module CryptKeeper
11
11
  #
12
12
  # options - A hash, :key is required
13
13
  def initialize(options = {})
14
+ legacy
14
15
  ActiveSupport.run_load_hooks(:crypt_keeper_mysql_aes_log, self)
15
16
 
16
17
  @key = options.fetch(:key) do
@@ -34,8 +35,12 @@ module CryptKeeper
34
35
  ["SELECT AES_DECRYPT(?, ?)", Base64.decode64(value), key]).first
35
36
  end
36
37
 
37
- def search(records, field, criteria)
38
- records.where(field.to_sym => encrypt(criteria))
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
39
44
  end
40
45
  end
41
46
  end