mongoid-encrypted-fields 1.0.0 → 1.1.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/CHANGLOG.md +22 -0
- data/README.md +11 -5
- data/examples/encrypted_strings_asymmetric_cipher.rb +20 -0
- data/examples/encrypted_strings_symmetric_cipher.rb +20 -0
- data/examples/gibberish_cipher.rb +19 -0
- data/lib/mongoid-encrypted-fields.rb +0 -5
- data/lib/mongoid-encrypted-fields/version.rb +1 -1
- data/mongoid-encrypted-fields.gemspec +2 -1
- data/spec/mongoid-encrypted-fields/fields/encrypted_date_spec.rb +1 -1
- data/spec/mongoid-encrypted-fields/fields/encrypted_datetime_spec.rb +1 -1
- data/spec/mongoid-encrypted-fields/fields/encrypted_field_spec.rb +1 -1
- data/spec/mongoid-encrypted-fields/fields/encrypted_string_spec.rb +1 -1
- data/spec/mongoid-encrypted-fields/fields/encrypted_time_spec.rb +1 -1
- data/spec/mongoid-encrypted-fields/fields/model_spec.rb +4 -4
- data/spec/spec_helper.rb +1 -1
- metadata +34 -17
- data/lib/mongoid-encrypted-fields/ciphers/asymmetric_cipher.rb +0 -22
- data/lib/mongoid-encrypted-fields/ciphers/cipher.rb +0 -15
- data/lib/mongoid-encrypted-fields/ciphers/symmetric_cipher.rb +0 -22
data/CHANGLOG.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Revision history
|
2
|
+
|
3
|
+
## 1.1 - Breaking changes - PLEASE READ
|
4
|
+
|
5
|
+
### encrypted-strings gem
|
6
|
+
During performance testing, we've found the [encrypted-strings](https://github.com/pluginaweek/encrypted_strings) gem
|
7
|
+
to be very slow under load, and it's patching the == method on String so it has been removed as the default cipher option.
|
8
|
+
|
9
|
+
### Gibberish
|
10
|
+
[Gibberish](https://github.com/mdp/gibberish) was found to be very fast, but uses a unique salt for each encryption.
|
11
|
+
This is normally great for security, but becomes problematic for searching for encrypted fields in Mongoid because each
|
12
|
+
search would generate a unique value that wouldn't match what's stored in the database.
|
13
|
+
|
14
|
+
We've included classes in the **examples** folder to show implementations of using either of these Gems, but remember that
|
15
|
+
we only require a class that implements **encrypt** and **decrypt** so you can use any gem of your choice.
|
16
|
+
|
17
|
+
### Modifications
|
18
|
+
|
19
|
+
* Removed included cipher implementations
|
20
|
+
* Removed [encrypted-strings](https://github.com/pluginaweek/encrypted_strings) gem dependency
|
21
|
+
|
22
|
+
## 1.0 - Initial release
|
data/README.md
CHANGED
@@ -10,16 +10,19 @@ Queries encrypt data before searching the database, so equality matches work aut
|
|
10
10
|
## Prerequisites
|
11
11
|
* Ruby 1.9.3
|
12
12
|
* [Mongoid](http://mongoid.org) 3.0
|
13
|
-
*
|
13
|
+
* "Bring your own" encryption, see below
|
14
14
|
|
15
15
|
## Install
|
16
16
|
gem 'mongoid-encrypted-fields'
|
17
17
|
|
18
18
|
## Usage
|
19
19
|
* Configure the cipher to be used for encrypting field values:
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
|
21
|
+
GibberishCipher can be found in examples - uses the [Gibberish](https://github.com/mdp/gibberish) gem:
|
22
|
+
```Ruby
|
23
|
+
Mongoid::EncryptedFields.cipher = GibberishCipher.new(ENV['MY_PASSWORD'])
|
24
|
+
```
|
25
|
+
|
23
26
|
* Use encrypted types for fields in your models:
|
24
27
|
```Ruby
|
25
28
|
class Person
|
@@ -37,6 +40,9 @@ Queries encrypt data before searching the database, so equality matches work aut
|
|
37
40
|
* The encrypted value is accessible with the "encrypted" attribute
|
38
41
|
```Ruby
|
39
42
|
person.ssn.encrypted # => <encrypted string>
|
43
|
+
|
44
|
+
# It can also be accessed using the hash syntax supported by Mongoid
|
45
|
+
person[:ssn] # => <encrypted string>
|
40
46
|
```
|
41
47
|
* Finding a model by an encrypted field works automatically (equality only):
|
42
48
|
```Ruby
|
@@ -47,4 +53,4 @@ Queries encrypt data before searching the database, so equality matches work aut
|
|
47
53
|
* Single cipher for all encrypted fields
|
48
54
|
|
49
55
|
## Copyright
|
50
|
-
(c) 2012 Koan Health. See LICENSE.txt for further details.
|
56
|
+
(c) 2012 Koan Health. See LICENSE.txt for further details.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'encrypted_strings'
|
2
|
+
|
3
|
+
class EncryptedStringsAsymmetricCipher
|
4
|
+
|
5
|
+
attr_reader :algorithm, :password, :public_key_file, :private_key_file
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@options = options
|
9
|
+
@options.each { |key, value| instance_variable_set "@#{key}", value }
|
10
|
+
end
|
11
|
+
|
12
|
+
def encrypt(data)
|
13
|
+
data.encrypt(:asymmetric, @options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def decrypt(data)
|
17
|
+
data.decrypt(:asymmetric, @options)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'encrypted_strings'
|
2
|
+
|
3
|
+
class EncryptedStringsSymmetricCipher
|
4
|
+
|
5
|
+
attr_reader :algorithm, :password
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@options = options
|
9
|
+
@options.each { |key, value| instance_variable_set "@#{key}", value }
|
10
|
+
end
|
11
|
+
|
12
|
+
def encrypt(data)
|
13
|
+
data.encrypt(:symmetric, @options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def decrypt(data)
|
17
|
+
data.decrypt(:symmetric, @options)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'gibberish/aes'
|
2
|
+
|
3
|
+
# Gibberish uses a unique salt for every encryption, but we need the same text to return the same ciphertext
|
4
|
+
# so Searching for encrypted field will work
|
5
|
+
class GibberishCipher < Gibberish::AES
|
6
|
+
|
7
|
+
SALT = "OU812FTW" # TODO: If you use this example class, make a unique 8-byte salt for your project
|
8
|
+
|
9
|
+
def initialize(password, options = {})
|
10
|
+
super password, options[:size] || 256
|
11
|
+
@salt = options[:salt] || SALT
|
12
|
+
end
|
13
|
+
|
14
|
+
def generate_salt
|
15
|
+
return @salt if @salt
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -1,10 +1,5 @@
|
|
1
|
-
require 'encrypted_strings'
|
2
|
-
|
3
1
|
require 'mongoid-encrypted-fields/version'
|
4
2
|
require 'mongoid-encrypted-fields/logging'
|
5
|
-
require 'mongoid-encrypted-fields/ciphers/cipher'
|
6
|
-
require 'mongoid-encrypted-fields/ciphers/asymmetric_cipher'
|
7
|
-
require 'mongoid-encrypted-fields/ciphers/symmetric_cipher'
|
8
3
|
require 'mongoid-encrypted-fields/fields/encrypted_field'
|
9
4
|
require 'mongoid-encrypted-fields/fields/encrypted_string'
|
10
5
|
require 'mongoid-encrypted-fields/fields/encrypted_date'
|
@@ -19,7 +19,8 @@ Gem::Specification.new do |gem|
|
|
19
19
|
|
20
20
|
gem.add_dependency "rake"
|
21
21
|
gem.add_dependency "mongoid", "~> 3"
|
22
|
-
gem.add_dependency "encrypted_strings", "~> 0.3"
|
23
22
|
|
24
23
|
gem.add_development_dependency "rspec"
|
24
|
+
gem.add_development_dependency "gibberish", "~> 1.2"
|
25
|
+
gem.add_development_dependency "encrypted_strings", "~> 0.3"
|
25
26
|
end
|
@@ -4,7 +4,7 @@ module Mongoid
|
|
4
4
|
describe EncryptedDate do
|
5
5
|
|
6
6
|
before(:all) do
|
7
|
-
Mongoid::EncryptedFields.cipher =
|
7
|
+
Mongoid::EncryptedFields.cipher = GibberishCipher.new('my test password')
|
8
8
|
end
|
9
9
|
|
10
10
|
subject { Mongoid::EncryptedDate }
|
@@ -4,7 +4,7 @@ module Mongoid
|
|
4
4
|
describe EncryptedDateTime do
|
5
5
|
|
6
6
|
before(:all) do
|
7
|
-
Mongoid::EncryptedFields.cipher =
|
7
|
+
Mongoid::EncryptedFields.cipher = GibberishCipher.new('my test password')
|
8
8
|
end
|
9
9
|
|
10
10
|
subject { Mongoid::EncryptedDateTime }
|
@@ -4,7 +4,7 @@ module Mongoid
|
|
4
4
|
describe EncryptedField do
|
5
5
|
|
6
6
|
before(:all) do
|
7
|
-
Mongoid::EncryptedFields.cipher =
|
7
|
+
Mongoid::EncryptedFields.cipher = GibberishCipher.new('my test password')
|
8
8
|
end
|
9
9
|
|
10
10
|
it "should encrypt and decrypt a string" do
|
@@ -4,7 +4,7 @@ module Mongoid
|
|
4
4
|
describe EncryptedString do
|
5
5
|
|
6
6
|
before(:all) do
|
7
|
-
Mongoid::EncryptedFields.cipher =
|
7
|
+
Mongoid::EncryptedFields.cipher = GibberishCipher.new('my test password')
|
8
8
|
end
|
9
9
|
|
10
10
|
let(:raw) { "abc123" }
|
@@ -4,7 +4,7 @@ module Mongoid
|
|
4
4
|
describe EncryptedTime do
|
5
5
|
|
6
6
|
before(:all) do
|
7
|
-
Mongoid::EncryptedFields.cipher =
|
7
|
+
Mongoid::EncryptedFields.cipher = GibberishCipher.new('my test password')
|
8
8
|
end
|
9
9
|
|
10
10
|
subject { Mongoid::EncryptedTime }
|
@@ -3,14 +3,14 @@ require 'spec_helper'
|
|
3
3
|
describe 'Single model' do
|
4
4
|
|
5
5
|
before(:all) do
|
6
|
-
Mongoid::EncryptedFields.cipher =
|
6
|
+
Mongoid::EncryptedFields.cipher = GibberishCipher.new('my test password')
|
7
7
|
end
|
8
8
|
|
9
9
|
let (:ssn) { '123456789' }
|
10
|
-
let (:ssn_encrypted) { Mongoid::EncryptedString.mongoize(
|
10
|
+
let (:ssn_encrypted) { Mongoid::EncryptedString.mongoize(ssn) }
|
11
11
|
let (:birth_date) { 20.years.ago.to_date }
|
12
|
-
let (:birth_date_encrypted) { Mongoid::EncryptedDate.mongoize(
|
13
|
-
let (:person) { Person.new(name: 'John Doe', ssn:
|
12
|
+
let (:birth_date_encrypted) { Mongoid::EncryptedDate.mongoize(birth_date) }
|
13
|
+
let (:person) { Person.new(name: 'John Doe', ssn: ssn, birth_date: birth_date) }
|
14
14
|
|
15
15
|
it "model stores encrypted ssn" do
|
16
16
|
person.attributes['ssn'].should eq(ssn_encrypted)
|
data/spec/spec_helper.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'bundler/setup'
|
3
3
|
require 'mongoid'
|
4
|
-
require 'encrypted_strings'
|
5
4
|
require 'rspec'
|
6
5
|
|
7
6
|
require 'mongoid-encrypted-fields'
|
8
7
|
|
8
|
+
Dir["#{File.dirname(__FILE__)}/../examples/**/*.rb"].each {|f| require f}
|
9
9
|
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
10
10
|
|
11
11
|
ENV['MONGOID_ENV'] ||= 'test'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongoid-encrypted-fields
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.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:
|
12
|
+
date: 2013-02-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -44,37 +44,53 @@ dependencies:
|
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '3'
|
46
46
|
- !ruby/object:Gem::Dependency
|
47
|
-
name:
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: gibberish
|
48
64
|
requirement: !ruby/object:Gem::Requirement
|
49
65
|
none: false
|
50
66
|
requirements:
|
51
67
|
- - ~>
|
52
68
|
- !ruby/object:Gem::Version
|
53
|
-
version: '
|
54
|
-
type: :
|
69
|
+
version: '1.2'
|
70
|
+
type: :development
|
55
71
|
prerelease: false
|
56
72
|
version_requirements: !ruby/object:Gem::Requirement
|
57
73
|
none: false
|
58
74
|
requirements:
|
59
75
|
- - ~>
|
60
76
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
77
|
+
version: '1.2'
|
62
78
|
- !ruby/object:Gem::Dependency
|
63
|
-
name:
|
79
|
+
name: encrypted_strings
|
64
80
|
requirement: !ruby/object:Gem::Requirement
|
65
81
|
none: false
|
66
82
|
requirements:
|
67
|
-
- -
|
83
|
+
- - ~>
|
68
84
|
- !ruby/object:Gem::Version
|
69
|
-
version: '0'
|
85
|
+
version: '0.3'
|
70
86
|
type: :development
|
71
87
|
prerelease: false
|
72
88
|
version_requirements: !ruby/object:Gem::Requirement
|
73
89
|
none: false
|
74
90
|
requirements:
|
75
|
-
- -
|
91
|
+
- - ~>
|
76
92
|
- !ruby/object:Gem::Version
|
77
|
-
version: '0'
|
93
|
+
version: '0.3'
|
78
94
|
description: A library for storing encrypted data in Mongo
|
79
95
|
email:
|
80
96
|
- development@koanhealth.com
|
@@ -85,14 +101,15 @@ files:
|
|
85
101
|
- .gitignore
|
86
102
|
- .rspec
|
87
103
|
- .rvmrc
|
104
|
+
- CHANGLOG.md
|
88
105
|
- Gemfile
|
89
106
|
- LICENSE.txt
|
90
107
|
- README.md
|
91
108
|
- Rakefile
|
109
|
+
- examples/encrypted_strings_asymmetric_cipher.rb
|
110
|
+
- examples/encrypted_strings_symmetric_cipher.rb
|
111
|
+
- examples/gibberish_cipher.rb
|
92
112
|
- lib/mongoid-encrypted-fields.rb
|
93
|
-
- lib/mongoid-encrypted-fields/ciphers/asymmetric_cipher.rb
|
94
|
-
- lib/mongoid-encrypted-fields/ciphers/cipher.rb
|
95
|
-
- lib/mongoid-encrypted-fields/ciphers/symmetric_cipher.rb
|
96
113
|
- lib/mongoid-encrypted-fields/fields/encrypted_date.rb
|
97
114
|
- lib/mongoid-encrypted-fields/fields/encrypted_date_time.rb
|
98
115
|
- lib/mongoid-encrypted-fields/fields/encrypted_field.rb
|
@@ -124,7 +141,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
124
141
|
version: '0'
|
125
142
|
segments:
|
126
143
|
- 0
|
127
|
-
hash:
|
144
|
+
hash: -323082810856718661
|
128
145
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
129
146
|
none: false
|
130
147
|
requirements:
|
@@ -133,10 +150,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
133
150
|
version: '0'
|
134
151
|
segments:
|
135
152
|
- 0
|
136
|
-
hash:
|
153
|
+
hash: -323082810856718661
|
137
154
|
requirements: []
|
138
155
|
rubyforge_project:
|
139
|
-
rubygems_version: 1.8.
|
156
|
+
rubygems_version: 1.8.25
|
140
157
|
signing_key:
|
141
158
|
specification_version: 3
|
142
159
|
summary: Custom types for storing encrypted data
|
@@ -1,22 +0,0 @@
|
|
1
|
-
module Mongoid
|
2
|
-
module Ciphers
|
3
|
-
class AsymmetricCipher < Cipher
|
4
|
-
|
5
|
-
attr_reader :algorithm, :password, :public_key_file, :private_key_file
|
6
|
-
|
7
|
-
def initialize(options = {})
|
8
|
-
@options = options
|
9
|
-
@options.each { |key, value| instance_variable_set "@#{key}", value }
|
10
|
-
end
|
11
|
-
|
12
|
-
def encrypt(data)
|
13
|
-
data.encrypt(:asymmetric, @options)
|
14
|
-
end
|
15
|
-
|
16
|
-
def decrypt(data)
|
17
|
-
data.decrypt(:asymmetric, @options)
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
module Mongoid
|
2
|
-
module Ciphers
|
3
|
-
class Cipher
|
4
|
-
|
5
|
-
def encrypt(data)
|
6
|
-
raise NotImplementedError.new('encrypt must be implemented')
|
7
|
-
end
|
8
|
-
|
9
|
-
def decrypt(data)
|
10
|
-
raise NotImplementedError.new('decrypt must be implemented')
|
11
|
-
end
|
12
|
-
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
module Mongoid
|
2
|
-
module Ciphers
|
3
|
-
class SymmetricCipher < Cipher
|
4
|
-
|
5
|
-
attr_reader :algorithm, :password
|
6
|
-
|
7
|
-
def initialize(options = { })
|
8
|
-
@options = options
|
9
|
-
@options.each { |key, value| instance_variable_set "@#{key}", value }
|
10
|
-
end
|
11
|
-
|
12
|
-
def encrypt(data)
|
13
|
-
data.encrypt(:symmetric, @options)
|
14
|
-
end
|
15
|
-
|
16
|
-
def decrypt(data)
|
17
|
-
data.decrypt(:symmetric, @options)
|
18
|
-
end
|
19
|
-
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|