crypt_keeper 0.4.2 → 0.5.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.
data/.gitignore CHANGED
@@ -16,3 +16,4 @@ test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
18
  spec/debug.log
19
+ spec/database.yml
@@ -4,6 +4,11 @@ gemfile:
4
4
  - gemfiles/activerecord_3_0.gemfile
5
5
  - gemfiles/activerecord_3_1.gemfile
6
6
  - gemfiles/activerecord_3_2.gemfile
7
+ before_script:
8
+ - cp spec/default.database.yml spec/database.yml
9
+ - psql -c 'CREATE DATABASE crypt_keeper_providers;' -U postgres
10
+ - psql crypt_keeper_providers -c 'CREATE EXTENSION IF NOT EXISTS pgcrypto;' -U postgres
11
+ - mysql -e 'CREATE DATABASE crypt_keeper_providers'
7
12
  notifications:
8
13
  email:
9
14
  recipients:
@@ -13,3 +18,9 @@ notifications:
13
18
  rvm:
14
19
  - 1.9.2
15
20
  - 1.9.3
21
+ - jruby
22
+ matrix:
23
+ allow_failures:
24
+ - rvm: jruby
25
+ env:
26
+ - JRUBY_OPTS=--1.9
data/README.md CHANGED
@@ -15,7 +15,7 @@ is a simple class that does 3 things.
15
15
  Note: Any options defined using `crypt_keeper` will be passed to `new` as a
16
16
  hash.
17
17
 
18
- You can see an AES example [here](https://github.com/jmazzi/crypt_keeper_providers/blob/master/lib/crypt_keeper_providers/aes.rb).
18
+ You can see an AES example [here](/jmazzi/crypt_keeper/blob/master/lib/crypt_keeper/provider/aes.rb).
19
19
 
20
20
  ## Why?
21
21
 
@@ -50,18 +50,20 @@ update the content without going through the current encryptor.
50
50
  ## Creating your own encryptor
51
51
 
52
52
  Creating your own encryptor is easy. All you have to do is create a class
53
- under the `CryptKeeperProviders` namespace, like this:
53
+ under the `CryptKeeper::Provider` namespace, like this:
54
54
 
55
55
  ```ruby
56
- module CryptKeeperProviders
57
- class MyEncryptor
58
- def initialize(options = {})
59
- end
56
+ module CryptKeeper
57
+ module Provider
58
+ class MyEncryptor
59
+ def initialize(options = {})
60
+ end
60
61
 
61
- def encrypt(value)
62
- end
62
+ def encrypt(value)
63
+ end
63
64
 
64
- def decrypt(value)
65
+ def decrypt(value)
66
+ end
65
67
  end
66
68
  end
67
69
  end
@@ -81,19 +83,21 @@ end
81
83
 
82
84
  There are two included encryptors.
83
85
 
84
- * [AES](https://github.com/jmazzi/crypt_keeper_providers/blob/master/lib/crypt_keeper_providers/aes.rb)
86
+ * [AES](https://github.com/jmazzi/crypt_keeper/blob/master/lib/crypt_keeper/provider/aes.rb)
85
87
  * Encryption is peformed using AES-256 via OpenSSL.
86
88
 
87
89
 
88
- * [MySQL AES](https://github.com/jmazzi/crypt_keeper_providers/blob/master/lib/crypt_keeper_providers/mysql_aes.rb)
90
+ * [MySQL AES](https://github.com/jmazzi/crypt_keeper/blob/master/lib/crypt_keeper/provider/mysql_aes.rb)
89
91
  * Encryption is peformed MySQL's native AES functions.
92
+ * ActiveRecord logs are [automatically](https://github.com/jmazzi/crypt_keeper/blob/master/lib/crypt_keeper/log_subscriber/mysql_aes.rb)
93
+ filtered for you to protect senitive data from being logged.
90
94
 
91
95
 
92
- * [PostgreSQL PGP](https://github.com/jmazzi/crypt_keeper_providers/blob/master/lib/crypt_keeper_providers/postgres_pgp.rb).
96
+ * [PostgreSQL PGP](https://github.com/jmazzi/crypt_keeper/blob/master/lib/crypt_keeper/provider/postgres_pgp.rb).
93
97
  * Encryption is performed using PostgresSQL's native [PGP functions](http://www.postgresql.org/docs/9.1/static/pgcrypto.html).
94
98
  * It requires the `pgcrypto` PostgresSQL extension:
95
99
  `CREATE EXTENSION IF NOT EXISTS pgcrypto`
96
- * ActiveRecord logs are [automatically](https://github.com/jmazzi/crypt_keeper_providers/blob/master/lib/crypt_keeper_providers/postgres_pgp/log_subscriber.rb)
100
+ * ActiveRecord logs are [automatically](https://github.com/jmazzi/crypt_keeper/blob/master/lib/crypt_keeper/log_subscriber/postgres_pgp.rb)
97
101
  filtered for you to protect senitive data from being logged.
98
102
 
99
103
  ## Requirements
@@ -17,7 +17,6 @@ Gem::Specification.new do |gem|
17
17
 
18
18
  gem.add_runtime_dependency 'activerecord', '>= 3.0'
19
19
  gem.add_runtime_dependency 'activesupport', '>= 3.0'
20
- gem.add_runtime_dependency 'crypt_keeper_providers', '0.5.2'
21
20
  gem.add_runtime_dependency 'appraisal', '~> 0.4.1'
22
21
 
23
22
  gem.add_development_dependency 'rspec', '~> 2.11.0'
@@ -28,7 +27,11 @@ Gem::Specification.new do |gem|
28
27
  if RUBY_PLATFORM == 'java'
29
28
  gem.add_development_dependency 'jruby-openssl', '~> 0.7.7'
30
29
  gem.add_development_dependency 'activerecord-jdbcsqlite3-adapter'
30
+ gem.add_development_dependency 'activerecord-jdbcpostgresql-adapter'
31
+ gem.add_development_dependency 'activerecord-jdbcmysql-adapter'
31
32
  else
32
33
  gem.add_development_dependency 'sqlite3'
34
+ gem.add_development_dependency 'pg', '~> 0.14.0'
35
+ gem.add_development_dependency 'mysql2', '~> 0.3.11'
33
36
  end
34
37
  end
@@ -5,7 +5,6 @@ PATH
5
5
  activerecord (>= 3.0)
6
6
  activesupport (>= 3.0)
7
7
  appraisal (~> 0.4.1)
8
- crypt_keeper_providers (= 0.5.2)
9
8
 
10
9
  GEM
11
10
  remote: https://rubygems.org/
@@ -26,7 +25,6 @@ GEM
26
25
  rake
27
26
  arel (3.0.2)
28
27
  builder (3.0.0)
29
- crypt_keeper_providers (0.5.2)
30
28
  diff-lcs (1.1.3)
31
29
  ffi (1.1.5)
32
30
  guard (1.3.2)
@@ -40,6 +38,8 @@ GEM
40
38
  rb-fsevent (~> 0.9.1)
41
39
  rb-inotify (~> 0.8.8)
42
40
  multi_json (1.3.6)
41
+ mysql2 (0.3.11)
42
+ pg (0.14.0)
43
43
  rake (0.9.2.2)
44
44
  rb-fchange (0.0.5)
45
45
  ffi
@@ -67,6 +67,8 @@ DEPENDENCIES
67
67
  crypt_keeper!
68
68
  guard (~> 1.3.0)
69
69
  guard-rspec (~> 1.2.0)
70
+ mysql2 (~> 0.3.11)
71
+ pg (~> 0.14.0)
70
72
  rake (~> 0.9.2.2)
71
73
  rspec (~> 2.11.0)
72
74
  sqlite3
@@ -5,7 +5,6 @@ PATH
5
5
  activerecord (>= 3.0)
6
6
  activesupport (>= 3.0)
7
7
  appraisal (~> 0.4.1)
8
- crypt_keeper_providers (= 0.5.2)
9
8
 
10
9
  GEM
11
10
  remote: https://rubygems.org/
@@ -26,7 +25,6 @@ GEM
26
25
  rake
27
26
  arel (3.0.2)
28
27
  builder (3.0.0)
29
- crypt_keeper_providers (0.5.2)
30
28
  diff-lcs (1.1.3)
31
29
  ffi (1.1.5)
32
30
  guard (1.3.2)
@@ -40,6 +38,8 @@ GEM
40
38
  rb-fsevent (~> 0.9.1)
41
39
  rb-inotify (~> 0.8.8)
42
40
  multi_json (1.3.6)
41
+ mysql2 (0.3.11)
42
+ pg (0.14.0)
43
43
  rake (0.9.2.2)
44
44
  rb-fchange (0.0.5)
45
45
  ffi
@@ -67,6 +67,8 @@ DEPENDENCIES
67
67
  crypt_keeper!
68
68
  guard (~> 1.3.0)
69
69
  guard-rspec (~> 1.2.0)
70
+ mysql2 (~> 0.3.11)
71
+ pg (~> 0.14.0)
70
72
  rake (~> 0.9.2.2)
71
73
  rspec (~> 2.11.0)
72
74
  sqlite3
@@ -5,7 +5,6 @@ PATH
5
5
  activerecord (>= 3.0)
6
6
  activesupport (>= 3.0)
7
7
  appraisal (~> 0.4.1)
8
- crypt_keeper_providers (= 0.5.2)
9
8
 
10
9
  GEM
11
10
  remote: https://rubygems.org/
@@ -26,7 +25,6 @@ GEM
26
25
  rake
27
26
  arel (3.0.2)
28
27
  builder (3.0.0)
29
- crypt_keeper_providers (0.5.2)
30
28
  diff-lcs (1.1.3)
31
29
  ffi (1.1.5)
32
30
  guard (1.3.2)
@@ -40,6 +38,8 @@ GEM
40
38
  rb-fsevent (~> 0.9.1)
41
39
  rb-inotify (~> 0.8.8)
42
40
  multi_json (1.3.6)
41
+ mysql2 (0.3.11)
42
+ pg (0.14.0)
43
43
  rake (0.9.2.2)
44
44
  rb-fchange (0.0.5)
45
45
  ffi
@@ -67,6 +67,8 @@ DEPENDENCIES
67
67
  crypt_keeper!
68
68
  guard (~> 1.3.0)
69
69
  guard-rspec (~> 1.2.0)
70
+ mysql2 (~> 0.3.11)
71
+ pg (~> 0.14.0)
70
72
  rake (~> 0.9.2.2)
71
73
  rspec (~> 2.11.0)
72
74
  sqlite3
@@ -1,10 +1,15 @@
1
+ require 'active_record'
2
+
1
3
  require 'crypt_keeper/version'
2
4
  require 'crypt_keeper/model'
3
- require 'active_record'
5
+
6
+ require 'crypt_keeper/provider/aes'
7
+ require 'crypt_keeper/provider/mysql_aes'
8
+ require 'crypt_keeper/provider/postgres_pgp'
4
9
 
5
10
  module CryptKeeper
6
11
  end
7
12
 
8
- ActiveSupport.on_load(:active_record) do
13
+ ActiveSupport.on_load :active_record do
9
14
  include CryptKeeper::Model
10
15
  end
@@ -0,0 +1,29 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/lazy_load_hooks'
3
+
4
+ module CryptKeeper
5
+ module LogSubscriber
6
+ module MysqlAes
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ alias_method_chain :sql, :mysql_aes
11
+ end
12
+
13
+ # Public: Prevents sensitive data from being logged
14
+ def sql_with_mysql_aes(event)
15
+ filter = /(aes_(encrypt|decrypt))\(((.|\n)*?)\)/i
16
+
17
+ event.payload[:sql] = event.payload[:sql].gsub(filter) do |_|
18
+ "#{$1}([FILTERED])"
19
+ end
20
+
21
+ sql_without_mysql_aes(event)
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ ActiveSupport.on_load :active_record do
28
+ ActiveRecord::LogSubscriber.send :include, CryptKeeper::LogSubscriber::MysqlAes
29
+ end
@@ -0,0 +1,29 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/lazy_load_hooks'
3
+
4
+ module CryptKeeper
5
+ module LogSubscriber
6
+ module PostgresPgp
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ alias_method_chain :sql, :postgres_pgp
11
+ end
12
+
13
+ # Public: Prevents sensitive data from being logged
14
+ def sql_with_postgres_pgp(event)
15
+ filter = /(pgp_sym_(encrypt|decrypt))\(((.|\n)*?)\)/i
16
+
17
+ event.payload[:sql] = event.payload[:sql].gsub(filter) do |_|
18
+ "#{$1}([FILTERED])"
19
+ end
20
+
21
+ sql_without_postgres_pgp(event)
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ ActiveSupport.on_load :active_record do
28
+ ActiveRecord::LogSubscriber.send :include, CryptKeeper::LogSubscriber::PostgresPgp
29
+ end
@@ -1,6 +1,5 @@
1
1
  require 'active_support/concern'
2
2
  require 'active_support/core_ext/array/extract_options'
3
- require 'crypt_keeper_providers'
4
3
 
5
4
  module CryptKeeper
6
5
  module Model
@@ -11,14 +10,18 @@ module CryptKeeper
11
10
  # Private: Encrypt each crypt_keeper_fields
12
11
  def encrypt_callback
13
12
  crypt_keeper_fields.each do |field|
14
- self[field] = self.class.encrypt read_attribute(field)
13
+ if !self[field].nil?
14
+ self[field] = self.class.encrypt read_attribute(field)
15
+ end
15
16
  end
16
17
  end
17
18
 
18
19
  # Private: Decrypt each crypt_keeper_fields
19
20
  def decrypt_callback
20
21
  crypt_keeper_fields.each do |field|
21
- self[field] = self.class.decrypt read_attribute(field)
22
+ if !self[field].nil?
23
+ self[field] = self.class.decrypt read_attribute(field)
24
+ end
22
25
  end
23
26
  end
24
27
 
@@ -68,7 +71,7 @@ module CryptKeeper
68
71
 
69
72
  # Private: The encryptor class
70
73
  def encryptor_klass
71
- @encryptor_klass ||= "CryptKeeperProviders::#{crypt_keeper_encryptor.to_s.camelize}".constantize
74
+ @encryptor_klass ||= "CryptKeeper::Provider::#{crypt_keeper_encryptor.to_s.camelize}".constantize
72
75
  end
73
76
 
74
77
  # Private: Ensure that the encryptor responds to new
@@ -0,0 +1,53 @@
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
+ @aes = ::OpenSSL::Cipher::Cipher.new("AES-256-CBC")
21
+ @aes.padding = 1
22
+
23
+ key = options.fetch(:key) do
24
+ raise ArgumentError, "Missing :key"
25
+ end
26
+
27
+ @key = Digest::SHA256.digest(key)
28
+ end
29
+
30
+ # Public: Encrypt a string
31
+ #
32
+ # Returns a string
33
+ def encrypt(value)
34
+ aes.encrypt
35
+ aes.key = key
36
+ iv = rand.to_s
37
+ aes.iv = iv
38
+ Base64::encode64("#{iv}#{SEPARATOR}#{aes.update(value.to_s) + aes.final}")
39
+ end
40
+
41
+ # Public: Decrypt a string
42
+ #
43
+ # Returns a string
44
+ def decrypt(value)
45
+ iv, value = Base64::decode64(value.to_s).split(SEPARATOR)
46
+ aes.decrypt
47
+ aes.key = key
48
+ aes.iv = iv
49
+ aes.update(value) + aes.final
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,40 @@
1
+ require 'crypt_keeper/log_subscriber/mysql_aes'
2
+
3
+ module CryptKeeper
4
+ module Provider
5
+ class MysqlAes
6
+ attr_accessor :key
7
+
8
+ # Public: Initializes the encryptor
9
+ #
10
+ # options - A hash, :key is required
11
+ def initialize(options = {})
12
+ @key = options.fetch(:key) do
13
+ raise ArgumentError, "Missing :key"
14
+ end
15
+ end
16
+
17
+ # Public: Encrypts a string
18
+ #
19
+ # Returns an encrypted string
20
+ def encrypt(value)
21
+ escape_and_execute_sql(["SELECT AES_ENCRYPT(?, ?)", value, key]).first
22
+ end
23
+
24
+ # Public: Decrypts a string
25
+ #
26
+ # Returns a plaintext string
27
+ def decrypt(value)
28
+ escape_and_execute_sql(["SELECT AES_DECRYPT(?, ?)", value, key]).first
29
+ end
30
+
31
+ private
32
+
33
+ # Private: Sanitize an sql query and then execute it
34
+ def escape_and_execute_sql(query)
35
+ query = ::ActiveRecord::Base.send :sanitize_sql_array, query
36
+ ::ActiveRecord::Base.connection.execute(query).first
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,40 @@
1
+ require 'crypt_keeper/log_subscriber/postgres_pgp'
2
+
3
+ module CryptKeeper
4
+ module Provider
5
+ class PostgresPgp
6
+ attr_accessor :key
7
+
8
+ # Public: Initializes the encryptor
9
+ #
10
+ # options - A hash, :key is required
11
+ def initialize(options = {})
12
+ @key = options.fetch(:key) do
13
+ raise ArgumentError, "Missing :key"
14
+ end
15
+ end
16
+
17
+ # Public: Encrypts a string
18
+ #
19
+ # Returns an encrypted string
20
+ def encrypt(value)
21
+ escape_and_execute_sql(["SELECT pgp_sym_encrypt(?, ?)", value, key])['pgp_sym_encrypt']
22
+ end
23
+
24
+ # Public: Decrypts a string
25
+ #
26
+ # Returns a plaintext string
27
+ def decrypt(value)
28
+ escape_and_execute_sql(["SELECT pgp_sym_decrypt(?, ?)", value, key])['pgp_sym_decrypt']
29
+ end
30
+
31
+ private
32
+
33
+ # Private: Sanitize an sql query and then execute it
34
+ def escape_and_execute_sql(query)
35
+ query = ::ActiveRecord::Base.send :sanitize_sql_array, query
36
+ ::ActiveRecord::Base.connection.execute(query).first
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,3 +1,3 @@
1
1
  module CryptKeeper
2
- VERSION = "0.4.2"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -0,0 +1,16 @@
1
+ postgres:
2
+ adapter: postgresql
3
+ encoding: utf8
4
+ reconnect: false
5
+ database: crypt_keeper_providers
6
+ pool: 5
7
+ username: postgres
8
+ password:
9
+ mysql:
10
+ adapter: mysql2
11
+ encoding: utf8
12
+ reconnect: false
13
+ database: crypt_keeper_providers
14
+ pool: 5
15
+ username: root
16
+ password:
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ module CryptKeeperProviders
4
+ describe MysqlAesLogSubscriber do
5
+ use_postgres
6
+
7
+ subject { ::ActiveRecord::LogSubscriber.new }
8
+
9
+ let(:input_query) do
10
+ "SELECT AES_ENCRYPT('encrypt_value', 'encrypt_key'), AES_ENCRYPT('decrypt_value', 'decrypt_key') FROM DUAL;"
11
+ end
12
+
13
+ let(:output_query) do
14
+ "SELECT AES_ENCRYPT([FILTERED]), AES_DECRYPT([FILTERED]) FROM DUAL;"
15
+ end
16
+
17
+ it "filters mysql aes functions" do
18
+ subject.should_receive(:sql_without_mysql_aes) do |event|
19
+ event.payload[:sql].should == output_query
20
+ end
21
+
22
+ subject.sql(ActiveSupport::Notifications::Event.new(:sql, 1, 1, 1, { sql: output_query }))
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ module CryptKeeperProviders
4
+ describe PostgresPgpLogSubscriber do
5
+ use_postgres
6
+
7
+ subject { ::ActiveRecord::LogSubscriber.new }
8
+
9
+ let(:input_query) do
10
+ "SELECT pgp_sym_encrypt('encrypt_value', 'encrypt_key'), pgp_sym_decrypt('decrypt_value', 'decrypt_key') FROM DUAL;"
11
+ end
12
+
13
+ let(:output_query) do
14
+ "SELECT pgp_sym_encrypt([FILTERED]), pgp_sym_decrypt([FILTERED]) FROM DUAL;"
15
+ end
16
+
17
+ it "filters pgp functions" do
18
+ subject.should_receive(:sql_without_postgres_pgp) do |event|
19
+ event.payload[:sql].should == output_query
20
+ end
21
+
22
+ subject.sql(ActiveSupport::Notifications::Event.new(:sql, 1, 1, 1, { sql: output_query }))
23
+ end
24
+ end
25
+ end
@@ -2,7 +2,10 @@ require 'spec_helper'
2
2
 
3
3
  module CryptKeeper
4
4
  describe Model do
5
+ use_sqlite
6
+
5
7
  subject { SensitiveData }
8
+
6
9
  describe "#crypt_keeper" do
7
10
  after(:each) do
8
11
  subject.instance_variable_set(:@encryptor_klass, nil)
@@ -29,7 +32,7 @@ module CryptKeeper
29
32
 
30
33
  it "should accept class name (as string) for encryptor option" do
31
34
  subject.crypt_keeper :storage, :secret, key1: 1, key2: 2, encryptor: "FakeEncryptor"
32
- subject.send(:encryptor_klass).should == CryptKeeperProviders::FakeEncryptor
35
+ subject.send(:encryptor_klass).should == CryptKeeper::Provider::FakeEncryptor
33
36
 
34
37
  subject.instance_variable_set(:@encryptor_klass, nil)
35
38
  end
@@ -53,6 +56,13 @@ module CryptKeeper
53
56
  subject.save!
54
57
  subject.storage.should == cipher_text
55
58
  end
59
+
60
+ it "should not encrypt nil" do
61
+ subject.storage = nil
62
+ subject.stub :decrypt_callback
63
+ subject.save!
64
+ subject.storage.should be_nil
65
+ end
56
66
  end
57
67
 
58
68
  describe "#decrypt" do
@@ -62,6 +72,13 @@ module CryptKeeper
62
72
  subject.save!
63
73
  subject.storage.should == plain_text
64
74
  end
75
+
76
+ it "should not decrypt nil" do
77
+ subject.storage = nil
78
+ subject.stub :encrypt_callback
79
+ subject.save!
80
+ subject.storage.should be_nil
81
+ end
65
82
  end
66
83
 
67
84
  describe "Encrypt & Decrypt" do
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ module CryptKeeper
4
+ module Provider
5
+ describe Aes do
6
+ subject { Aes.new(key: 'cake') }
7
+
8
+ describe "#initialize" do
9
+ let(:hexed_key) do
10
+ Digest::SHA256.digest('cake')
11
+ end
12
+
13
+ it "should extract the key and digest it" do
14
+ subject.key.should == hexed_key
15
+ end
16
+
17
+ it "should raise an exception with a missing key" do
18
+ expect { Aes.new }.to raise_error(ArgumentError, "Missing :key")
19
+ end
20
+ end
21
+
22
+ describe "#encrypt" do
23
+ let(:encrypted) do
24
+ subject.encrypt 'string'
25
+ end
26
+
27
+ it "should encrypt the string" do
28
+ encrypted.should_not == 'string'
29
+ encrypted.should_not be_nil
30
+ encrypted.should_not be_empty
31
+ end
32
+ end
33
+
34
+ describe "#decrypt" do
35
+ let(:decrypted) do
36
+ subject.decrypt "MC41MDk5MjI2NjgxMDI1MDI2OmNyeXB0X2tlZXBlcjpPI/8dCqWXDMVj7Jqs\nuwf/\n"
37
+ end
38
+
39
+ it "should decrypt the string" do
40
+ decrypted.should == 'string'
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ module CryptKeeper
4
+ module Provider
5
+ describe MysqlAes do
6
+ use_mysql
7
+
8
+ let(:plain_text) { 'test' }
9
+
10
+ # MySQL stores AES encrypted strings in binary which you can't paste
11
+ # into a spec :). This is a Base64 encoded string of 'test' AES encrypted
12
+ # by AES_ENCRYPT()
13
+ let(:cipher_text) do
14
+ Base64.decode64 "nbKOoWn8kvAw9k/C2Mex6Q==\n"
15
+ end
16
+
17
+ subject { MysqlAes.new key: 'candy' }
18
+
19
+ its(:key) { should == 'candy' }
20
+
21
+ describe "#initialize" do
22
+ it "should raise an exception with a missing key" do
23
+ expect { MysqlAes.new }.to raise_error(ArgumentError, "Missing :key")
24
+ end
25
+ end
26
+
27
+ describe "#encrypt" do
28
+ it "should encrypt the string" do
29
+ subject.encrypt(plain_text).should_not == plain_text
30
+ subject.encrypt(plain_text).should_not be_empty
31
+ end
32
+ end
33
+
34
+ describe "#decrypt" do
35
+ it "should decrypt the string" do
36
+ subject.decrypt(cipher_text).should == plain_text
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ module CryptKeeper
4
+ module Provider
5
+ describe PostgresPgp do
6
+ use_postgres
7
+
8
+ let(:cipher_text) { '\\xc30d0407030283b15f71b6a7d0296cd23501bd2c8fe3c7a56005ff4619527c4291509a78c77a6758cddd2a14acbde589fa10b3e0686865182d3beadaf237b9f928e7ba1810b8' }
9
+ let(:plain_text) { 'test' }
10
+
11
+ subject { PostgresPgp.new key: 'candy' }
12
+
13
+ its(:key) { should == 'candy' }
14
+
15
+ describe "#initialize" do
16
+ it "should raise an exception with a missing key" do
17
+ expect { PostgresPgp.new }.to raise_error(ArgumentError, "Missing :key")
18
+ end
19
+ end
20
+
21
+ describe "#encrypt" do
22
+ it "should encrypt the string" do
23
+ subject.encrypt(plain_text).should_not == plain_text
24
+ subject.encrypt(plain_text).should_not be_empty
25
+ end
26
+ end
27
+
28
+ describe "#decrypt" do
29
+ it "should decrypt the string" do
30
+ subject.decrypt(cipher_text).should == plain_text
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,16 +1,50 @@
1
1
  require 'active_record'
2
2
  require 'logger'
3
3
 
4
- ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
5
- ActiveRecord::Base.logger = Logger.new SPEC_ROOT.join('debug.log').to_s
6
- ActiveRecord::Migration.verbose = false
7
-
8
- ActiveRecord::Schema.define do
9
- create_table :sensitive_data, :force => true do |t|
10
- t.column :name, :string
11
- t.column :storage, :text
12
- t.column :secret, :text
4
+ ::ActiveRecord::Base.logger = Logger.new SPEC_ROOT.join('debug.log').to_s
5
+ ::ActiveRecord::Migration.verbose = false
6
+
7
+ module CryptKeeper
8
+ class SensitiveData < ActiveRecord::Base; end
9
+
10
+ module ConnectionHelpers
11
+
12
+ def use_postgres
13
+ before :all do
14
+ ::ActiveRecord::Base.clear_active_connections!
15
+ config = YAML.load_file SPEC_ROOT.join('database.yml')
16
+ ::ActiveRecord::Base.establish_connection(config['postgres'])
17
+ end
18
+ end
19
+
20
+ def use_mysql
21
+ before :all do
22
+ ::ActiveRecord::Base.clear_active_connections!
23
+ config = YAML.load_file SPEC_ROOT.join('database.yml')
24
+ ::ActiveRecord::Base.establish_connection(config['mysql'])
25
+ end
26
+ end
27
+
28
+ def use_sqlite
29
+ before :all do
30
+ ::ActiveRecord::Base.clear_active_connections!
31
+ ::ActiveRecord::Base.establish_connection(:adapter => 'sqlite3',
32
+ :database => ':memory:')
33
+
34
+ ::ActiveRecord::Schema.define do
35
+ create_table :sensitive_data, :force => true do |t|
36
+ t.column :name, :string
37
+ t.column :storage, :text
38
+ t.column :secret, :text
39
+ end
40
+ end
41
+ end
42
+ end
43
+
13
44
  end
14
45
  end
15
46
 
16
- class SensitiveData < ActiveRecord::Base; end
47
+
48
+ RSpec.configure do |config|
49
+ config.extend CryptKeeper::ConnectionHelpers
50
+ end
@@ -1,26 +1,29 @@
1
1
  # A fake class that does no encryption
2
- module CryptKeeperProviders
3
- class FakeEncryptor
4
- def initialize(*args)
2
+ module CryptKeeper
3
+ module Provider
4
+ class FakeEncryptor
5
+ def initialize(*args)
6
+ end
5
7
  end
6
8
  end
7
9
  end
8
10
 
9
11
  # This class embeds the passphrase in the beginning of the string
10
12
  # and then reverses the 'plaintext'
11
- module CryptKeeperProviders
12
- class Encryptor
13
- def initialize(options = {})
14
- @passphrase = options[:passphrase]
15
- end
13
+ module CryptKeeper
14
+ module Provider
15
+ class Encryptor
16
+ def initialize(options = {})
17
+ @passphrase = options[:passphrase]
18
+ end
16
19
 
17
- def encrypt(data)
18
- @passphrase + data.reverse
19
- end
20
+ def encrypt(data)
21
+ @passphrase + data.reverse
22
+ end
20
23
 
21
- def decrypt(data)
22
- data.sub(/^#{@passphrase}/, '').reverse
24
+ def decrypt(data)
25
+ data.sub(/^#{@passphrase}/, '').reverse
26
+ end
23
27
  end
24
28
  end
25
29
  end
26
-
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: crypt_keeper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-08-31 00:00:00.000000000 Z
12
+ date: 2012-09-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -43,22 +43,6 @@ dependencies:
43
43
  - - ! '>='
44
44
  - !ruby/object:Gem::Version
45
45
  version: '3.0'
46
- - !ruby/object:Gem::Dependency
47
- name: crypt_keeper_providers
48
- requirement: !ruby/object:Gem::Requirement
49
- none: false
50
- requirements:
51
- - - '='
52
- - !ruby/object:Gem::Version
53
- version: 0.5.2
54
- type: :runtime
55
- prerelease: false
56
- version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
- requirements:
59
- - - '='
60
- - !ruby/object:Gem::Version
61
- version: 0.5.2
62
46
  - !ruby/object:Gem::Dependency
63
47
  name: appraisal
64
48
  requirement: !ruby/object:Gem::Requirement
@@ -155,6 +139,38 @@ dependencies:
155
139
  - - ! '>='
156
140
  - !ruby/object:Gem::Version
157
141
  version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: pg
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ~>
148
+ - !ruby/object:Gem::Version
149
+ version: 0.14.0
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ~>
156
+ - !ruby/object:Gem::Version
157
+ version: 0.14.0
158
+ - !ruby/object:Gem::Dependency
159
+ name: mysql2
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ~>
164
+ - !ruby/object:Gem::Version
165
+ version: 0.3.11
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ~>
172
+ - !ruby/object:Gem::Version
173
+ version: 0.3.11
158
174
  description: Transparent encryption for ActiveRecord that isn't over-engineered
159
175
  email:
160
176
  - jmazzi@gmail.com
@@ -179,9 +195,20 @@ files:
179
195
  - gemfiles/activerecord_3_2.gemfile
180
196
  - gemfiles/activerecord_3_2.gemfile.lock
181
197
  - lib/crypt_keeper.rb
198
+ - lib/crypt_keeper/log_subscriber/mysql_aes.rb
199
+ - lib/crypt_keeper/log_subscriber/postgres_pgp.rb
182
200
  - lib/crypt_keeper/model.rb
201
+ - lib/crypt_keeper/provider/aes.rb
202
+ - lib/crypt_keeper/provider/mysql_aes.rb
203
+ - lib/crypt_keeper/provider/postgres_pgp.rb
183
204
  - lib/crypt_keeper/version.rb
205
+ - spec/default.database.yml
206
+ - spec/log_subscriber/mysql_aes.rb
207
+ - spec/log_subscriber/postgres_pgp.rb
184
208
  - spec/model_spec.rb
209
+ - spec/provider/aes_spec.rb
210
+ - spec/provider/mysql_aes_spec.rb
211
+ - spec/provider/postgres_pgp_spec.rb
185
212
  - spec/spec_helper.rb
186
213
  - spec/support/active_record.rb
187
214
  - spec/support/encryptors.rb
@@ -199,7 +226,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
199
226
  version: '0'
200
227
  segments:
201
228
  - 0
202
- hash: 3079739714190142337
229
+ hash: 777875145635862709
203
230
  required_rubygems_version: !ruby/object:Gem::Requirement
204
231
  none: false
205
232
  requirements:
@@ -208,7 +235,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
208
235
  version: '0'
209
236
  segments:
210
237
  - 0
211
- hash: 3079739714190142337
238
+ hash: 777875145635862709
212
239
  requirements: []
213
240
  rubyforge_project:
214
241
  rubygems_version: 1.8.23
@@ -216,7 +243,13 @@ signing_key:
216
243
  specification_version: 3
217
244
  summary: Transparent encryption for ActiveRecord that isn't over-engineered
218
245
  test_files:
246
+ - spec/default.database.yml
247
+ - spec/log_subscriber/mysql_aes.rb
248
+ - spec/log_subscriber/postgres_pgp.rb
219
249
  - spec/model_spec.rb
250
+ - spec/provider/aes_spec.rb
251
+ - spec/provider/mysql_aes_spec.rb
252
+ - spec/provider/postgres_pgp_spec.rb
220
253
  - spec/spec_helper.rb
221
254
  - spec/support/active_record.rb
222
255
  - spec/support/encryptors.rb