strongbox 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/README.md +2 -0
- data/lib/strongbox.rb +13 -8
- data/lib/strongbox/lock.rb +21 -3
- data/test/method_key_test.rb +68 -0
- metadata +5 -17
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MGZhNGI2ODYzOWFjYTk3MTYyNDdjNjIzMDJmMzc4NDc2ZGMyMGEzOQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
Yzk0MDNlNWNjYzlmMjkyMDYwYjE2YjUwNjYzNWJhOGIxNzNkMWU4MQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
OTZlY2M1NmMwZjkzMjMyYTlmY2IzODc3MzM4NzIzMmYxYjllMjMxNTczZWYz
|
10
|
+
ZTc2ZDc0ZjQyNTVmZDQwYzE0NmIxOWE4Y2FmOTFlY2I4OTlmOWZhN2MyMjE5
|
11
|
+
ZGE2YTlhYTI0NGRmZDI3NzM3YjU0NTg0YWY1OGQyNTJlNzBmMjM=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ODZhNGYzZmJiMWNhNmVhMDU3MzdhNDVjNDI4MDgwMzE0OGI4YzRjZjEyYjQy
|
14
|
+
Njc5NzkwMjliZDNhY2JjY2NkYzQ4Yzc5MjA1ODcwZTM2Y2U2NDA4YWE4M2M5
|
15
|
+
ZjY0YzVjZDNiNzE2NzIyYjE2YzAyYzQwNGVmMmY5Yzg3ZmM0OWE=
|
data/README.md
CHANGED
@@ -126,6 +126,8 @@ Options to `encrypt_with_public_key` are:
|
|
126
126
|
|
127
127
|
* `:ensure_required_columns` - Make sure the required database column(s) exist. Defaults to `true`, set to `false` if you want to encrypt/decrypt data stored outside of the database.
|
128
128
|
|
129
|
+
* `:deferred_encryption` - Defer the encryption to happen before saving the object, instead of on the assignment of the encrypted attribute. Solves issues when using [dynamic keys](http://stuff-things.net/2012/04/18/dynamic-keys-for-strongbox/). Defaults to `false`.
|
130
|
+
|
129
131
|
For example, encrypting a small attribute, providing only the public
|
130
132
|
key for extra security, and Base64 encoding the encrypted data:
|
131
133
|
|
data/lib/strongbox.rb
CHANGED
@@ -5,7 +5,7 @@ require 'strongbox/lock'
|
|
5
5
|
|
6
6
|
module Strongbox
|
7
7
|
|
8
|
-
VERSION = "0.7.
|
8
|
+
VERSION = "0.7.1"
|
9
9
|
|
10
10
|
RSA_PKCS1_PADDING = OpenSSL::PKey::RSA::PKCS1_PADDING
|
11
11
|
RSA_SSLV23_PADDING = OpenSSL::PKey::RSA::SSLV23_PADDING
|
@@ -20,7 +20,8 @@ module Strongbox
|
|
20
20
|
:symmetric => :always,
|
21
21
|
:padding => RSA_PKCS1_PADDING,
|
22
22
|
:symmetric_cipher => 'aes-256-cbc',
|
23
|
-
:ensure_required_columns => true
|
23
|
+
:ensure_required_columns => true,
|
24
|
+
:deferred_encryption => false
|
24
25
|
}
|
25
26
|
end
|
26
27
|
|
@@ -50,20 +51,20 @@ module Strongbox
|
|
50
51
|
# Argument 0..-2 contains columns to be encrypted
|
51
52
|
def encrypt_with_public_key(*args)
|
52
53
|
include InstanceMethods
|
53
|
-
|
54
|
+
|
54
55
|
options = args.delete_at(-1) || {}
|
55
|
-
|
56
|
+
|
56
57
|
unless options.is_a?(Hash)
|
57
58
|
args.push(options)
|
58
59
|
options = {}
|
59
60
|
end
|
60
|
-
|
61
|
+
|
61
62
|
if args.one?
|
62
63
|
name = args.first
|
63
64
|
else
|
64
65
|
return args.each { |name| encrypt_with_public_key(name, options) }
|
65
66
|
end
|
66
|
-
|
67
|
+
|
67
68
|
if respond_to?(:class_attribute)
|
68
69
|
self.lock_options = {} if lock_options.nil?
|
69
70
|
else
|
@@ -77,9 +78,14 @@ module Strongbox
|
|
77
78
|
end
|
78
79
|
|
79
80
|
define_method "#{name}=" do | plaintext |
|
80
|
-
lock_for(name).
|
81
|
+
lock_for(name).content plaintext
|
81
82
|
end
|
82
83
|
|
84
|
+
if lock_options[name][:deferred_encryption]
|
85
|
+
before_save do
|
86
|
+
lock_for(name).encrypt!
|
87
|
+
end
|
88
|
+
end
|
83
89
|
end
|
84
90
|
end
|
85
91
|
|
@@ -94,4 +100,3 @@ end
|
|
94
100
|
if Object.const_defined?("ActiveRecord")
|
95
101
|
ActiveRecord::Base.send(:include, Strongbox)
|
96
102
|
end
|
97
|
-
|
data/lib/strongbox/lock.rb
CHANGED
@@ -21,6 +21,20 @@ module Strongbox
|
|
21
21
|
@symmetric_key = options[:symmetric_key] || "#{name}_key"
|
22
22
|
@symmetric_iv = options[:symmetric_iv] || "#{name}_iv"
|
23
23
|
@ensure_required_columns = options[:ensure_required_columns]
|
24
|
+
@deferred_encryption = options[:deferred_encryption]
|
25
|
+
end
|
26
|
+
|
27
|
+
def content plaintext
|
28
|
+
if @deferred_encryption
|
29
|
+
@raw_content = plaintext
|
30
|
+
else
|
31
|
+
encrypt plaintext
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def encrypt!
|
36
|
+
encrypt @raw_content
|
37
|
+
@raw_content = nil
|
24
38
|
end
|
25
39
|
|
26
40
|
def encrypt plaintext
|
@@ -61,11 +75,15 @@ module Strongbox
|
|
61
75
|
# OpenSSL::PKey::RSAError if the password is wrong.
|
62
76
|
|
63
77
|
def decrypt password = nil, ciphertext = nil
|
78
|
+
return @raw_content if @deferred_encryption && @raw_content
|
79
|
+
|
64
80
|
# Given a private key and a nil password OpenSSL::PKey::RSA.new() will
|
65
81
|
# *prompt* for a password, we default to an empty string to avoid that.
|
66
82
|
ciphertext ||= @instance[@name]
|
67
|
-
|
68
|
-
|
83
|
+
unless @deferred_encryption
|
84
|
+
return nil if ciphertext.nil?
|
85
|
+
return "" if ciphertext.empty?
|
86
|
+
end
|
69
87
|
|
70
88
|
return "*encrypted*" if password.nil?
|
71
89
|
unless @private_key
|
@@ -97,7 +115,7 @@ module Strongbox
|
|
97
115
|
end
|
98
116
|
|
99
117
|
def to_s
|
100
|
-
decrypt
|
118
|
+
@raw_content || decrypt
|
101
119
|
end
|
102
120
|
|
103
121
|
def to_json(options = nil)
|
data/test/method_key_test.rb
CHANGED
@@ -74,4 +74,72 @@ class MethodKeyTest < Test::Unit::TestCase
|
|
74
74
|
|
75
75
|
should_encypted_and_decrypt
|
76
76
|
end
|
77
|
+
|
78
|
+
context "With dynamic keys" do
|
79
|
+
setup do
|
80
|
+
ActiveRecord::Base.connection.create_table :dummies, :force => true do |table|
|
81
|
+
table.string :in_the_clear
|
82
|
+
table.binary :secret
|
83
|
+
table.binary :secret_key
|
84
|
+
table.binary :secret_iv
|
85
|
+
table.binary :segreto
|
86
|
+
|
87
|
+
table.string :key_pair
|
88
|
+
end
|
89
|
+
rebuild_class :public_key => :key_pair,
|
90
|
+
:private_key => :key_pair,
|
91
|
+
:deferred_encryption => true
|
92
|
+
|
93
|
+
Dummy.class_eval do
|
94
|
+
attr_accessor :password
|
95
|
+
|
96
|
+
def key_pair
|
97
|
+
unless self['key_pair']
|
98
|
+
raise if self.password.blank?
|
99
|
+
self['key_pair'] = generate_key_pair(self.password)
|
100
|
+
end
|
101
|
+
self['key_pair']
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
@password = 'letmein'
|
106
|
+
end
|
107
|
+
|
108
|
+
context 'When just initialized' do
|
109
|
+
setup do
|
110
|
+
@dummy = Dummy.new
|
111
|
+
@dummy.secret = 'Shhhh'
|
112
|
+
@dummy.password = @password
|
113
|
+
end
|
114
|
+
|
115
|
+
should 'return secret when not yet locked' do
|
116
|
+
assert_equal 'Shhhh', @dummy.secret.decrypt
|
117
|
+
end
|
118
|
+
|
119
|
+
should 'return secret when unlocked' do
|
120
|
+
assert_equal 'Shhhh', @dummy.secret.decrypt(@password)
|
121
|
+
end
|
122
|
+
|
123
|
+
context 'Then locked' do
|
124
|
+
setup do
|
125
|
+
@dummy.secret.encrypt!
|
126
|
+
end
|
127
|
+
should_encypted_and_decrypt
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'After saving the model, and then loading it from the database' do
|
132
|
+
setup do
|
133
|
+
Dummy.create!(:secret => 'Shhhh', :password => @password)
|
134
|
+
@dummy = Dummy.first
|
135
|
+
p 'XXXXXXXXXXXX'
|
136
|
+
end
|
137
|
+
|
138
|
+
should_encypted_and_decrypt
|
139
|
+
end
|
140
|
+
|
141
|
+
teardown do
|
142
|
+
rebuild_model
|
143
|
+
end
|
144
|
+
end
|
77
145
|
end
|
metadata
CHANGED
@@ -1,20 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: strongbox
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
5
|
-
prerelease:
|
4
|
+
version: 0.7.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Spike Ilacqua
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2015-01-19 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: activerecord
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
17
|
- - ! '>='
|
20
18
|
- !ruby/object:Gem::Version
|
@@ -22,7 +20,6 @@ dependencies:
|
|
22
20
|
type: :runtime
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
24
|
- - ! '>='
|
28
25
|
- !ruby/object:Gem::Version
|
@@ -30,7 +27,6 @@ dependencies:
|
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: thoughtbot-shoulda
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
31
|
- - ! '>='
|
36
32
|
- !ruby/object:Gem::Version
|
@@ -38,7 +34,6 @@ dependencies:
|
|
38
34
|
type: :development
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
38
|
- - ! '>='
|
44
39
|
- !ruby/object:Gem::Version
|
@@ -46,7 +41,6 @@ dependencies:
|
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: sqlite3
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
45
|
- - ~>
|
52
46
|
- !ruby/object:Gem::Version
|
@@ -54,7 +48,6 @@ dependencies:
|
|
54
48
|
type: :development
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
52
|
- - ~>
|
60
53
|
- !ruby/object:Gem::Version
|
@@ -62,7 +55,6 @@ dependencies:
|
|
62
55
|
- !ruby/object:Gem::Dependency
|
63
56
|
name: rake
|
64
57
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
58
|
requirements:
|
67
59
|
- - ! '>='
|
68
60
|
- !ruby/object:Gem::Version
|
@@ -70,7 +62,6 @@ dependencies:
|
|
70
62
|
type: :development
|
71
63
|
prerelease: false
|
72
64
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
65
|
requirements:
|
75
66
|
- - ! '>='
|
76
67
|
- !ruby/object:Gem::Version
|
@@ -78,7 +69,6 @@ dependencies:
|
|
78
69
|
- !ruby/object:Gem::Dependency
|
79
70
|
name: rdoc
|
80
71
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
72
|
requirements:
|
83
73
|
- - ! '>='
|
84
74
|
- !ruby/object:Gem::Version
|
@@ -86,7 +76,6 @@ dependencies:
|
|
86
76
|
type: :development
|
87
77
|
prerelease: false
|
88
78
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
79
|
requirements:
|
91
80
|
- - ! '>='
|
92
81
|
- !ruby/object:Gem::Version
|
@@ -128,27 +117,26 @@ files:
|
|
128
117
|
- test/validations_test.rb
|
129
118
|
homepage: http://stuff-things.net/strongbox
|
130
119
|
licenses: []
|
120
|
+
metadata: {}
|
131
121
|
post_install_message:
|
132
122
|
rdoc_options: []
|
133
123
|
require_paths:
|
134
124
|
- lib
|
135
125
|
required_ruby_version: !ruby/object:Gem::Requirement
|
136
|
-
none: false
|
137
126
|
requirements:
|
138
127
|
- - ! '>='
|
139
128
|
- !ruby/object:Gem::Version
|
140
129
|
version: '0'
|
141
130
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
142
|
-
none: false
|
143
131
|
requirements:
|
144
132
|
- - ! '>='
|
145
133
|
- !ruby/object:Gem::Version
|
146
134
|
version: '0'
|
147
135
|
requirements: []
|
148
136
|
rubyforge_project:
|
149
|
-
rubygems_version:
|
137
|
+
rubygems_version: 2.4.5
|
150
138
|
signing_key:
|
151
|
-
specification_version:
|
139
|
+
specification_version: 4
|
152
140
|
summary: Secures ActiveRecord fields with public key encryption.
|
153
141
|
test_files:
|
154
142
|
- test/database.yml
|