crypt_keeper 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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