symmetric-encryption 0.2.0 → 0.3.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/README.md +201 -54
- data/examples/symmetric-encryption.yml +64 -19
- data/lib/symmetric/cipher.rb +184 -0
- data/lib/symmetric/encryption.rb +197 -182
- data/lib/symmetric/version.rb +1 -1
- data/lib/symmetric-encryption.rb +1 -0
- data/test/attr_encrypted_test.rb +6 -18
- data/test/cipher_test.rb +71 -0
- data/test/config/symmetric-encryption.yml +13 -9
- data/test/config/{test.iv → test_new.iv} +0 -0
- data/test/config/{test.key → test_new.key} +0 -0
- data/test/config/test_secondary_1.iv +1 -0
- data/test/config/test_secondary_1.key +2 -0
- data/test/encryption_test.rb +16 -6
- metadata +9 -6
- data/symmetric-encryption-0.1.2.gem +0 -0
data/lib/symmetric/encryption.rb
CHANGED
@@ -10,141 +10,62 @@ module Symmetric
|
|
10
10
|
# be distributed separately from the application
|
11
11
|
class Encryption
|
12
12
|
|
13
|
-
#
|
14
|
-
|
15
|
-
|
16
|
-
MAGIC_HEADER = '@EnC'
|
17
|
-
MAGIC_HEADER_SIZE = MAGIC_HEADER.size
|
18
|
-
end
|
19
|
-
|
20
|
-
# The minimum length for an encrypted string
|
21
|
-
def self.min_encrypted_length
|
22
|
-
@@min_encrypted_length ||= encrypt('1').length
|
23
|
-
end
|
24
|
-
|
25
|
-
# Returns [true|false] a best effort determination as to whether the supplied
|
26
|
-
# string is encrypted or not, without incurring the penalty of actually
|
27
|
-
# decrypting the supplied data
|
28
|
-
# Parameters:
|
29
|
-
# encrypted_data: Encrypted string
|
30
|
-
def self.encrypted?(encrypted_data)
|
31
|
-
# Simple checks first
|
32
|
-
return false if (encrypted_data.length < min_encrypted_length) || (!encrypted_data.end_with?("\n"))
|
33
|
-
# For now have to decrypt it fully
|
34
|
-
begin
|
35
|
-
decrypt(encrypted_data) ? true : false
|
36
|
-
rescue
|
37
|
-
false
|
38
|
-
end
|
39
|
-
end
|
13
|
+
# Defaults
|
14
|
+
@@cipher = nil
|
15
|
+
@@secondary_ciphers = []
|
40
16
|
|
41
|
-
# Set the Symmetric Cipher to be used
|
17
|
+
# Set the Primary Symmetric Cipher to be used
|
42
18
|
def self.cipher=(cipher)
|
19
|
+
raise "Cipher must be similar to Symmetric::Ciphers" unless cipher.respond_to?(:encrypt) && cipher.respond_to?(:decrypt) && cipher.respond_to?(:encrypted?)
|
43
20
|
@@cipher = cipher
|
44
21
|
end
|
45
22
|
|
46
|
-
# Returns the Symmetric Cipher being used
|
23
|
+
# Returns the Primary Symmetric Cipher being used
|
47
24
|
def self.cipher
|
48
25
|
@@cipher
|
49
26
|
end
|
50
27
|
|
51
|
-
# Set the Symmetric
|
52
|
-
def self.
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
# Set the Initialization Vector to use with Symmetric Key
|
57
|
-
def self.iv=(iv)
|
58
|
-
@@iv = iv
|
59
|
-
end
|
60
|
-
|
61
|
-
# Defaults
|
62
|
-
@@key = nil
|
63
|
-
@@iv = nil
|
64
|
-
|
65
|
-
# Load the Encryption Configuration from a YAML file
|
66
|
-
# filename: Name of file to read.
|
67
|
-
# Mandatory for non-Rails apps
|
68
|
-
# Default: Rails.root/config/symmetry.yml
|
69
|
-
def self.load!(filename=nil, environment=nil)
|
70
|
-
config = YAML.load_file(filename || File.join(Rails.root, "config", "symmetric-encryption.yml"))[environment || Rails.env]
|
71
|
-
self.cipher = config['cipher'] || 'aes-256-cbc'
|
72
|
-
symmetric_key = config['symmetric_key']
|
73
|
-
symmetric_iv = config['symmetric_iv']
|
74
|
-
|
75
|
-
# Hard coded symmetric_key?
|
76
|
-
if symmetric_key
|
77
|
-
self.key = symmetric_key
|
78
|
-
self.iv = symmetric_iv
|
79
|
-
else
|
80
|
-
load_keys(config['symmetric_key_filename'], config['symmetric_iv_filename'], config['private_rsa_key'])
|
28
|
+
# Set the Secondary Symmetric Ciphers Array to be used
|
29
|
+
def self.secondary_ciphers=(secondary_ciphers)
|
30
|
+
raise "secondary_ciphers must be a collection" unless secondary_ciphers.respond_to? :each
|
31
|
+
secondary_ciphers.each do |cipher|
|
32
|
+
raise "secondary_ciphers can only consist of Symmetric::Ciphers" unless cipher.respond_to?(:encrypt) && cipher.respond_to?(:decrypt) && cipher.respond_to?(:encrypted?)
|
81
33
|
end
|
82
|
-
|
34
|
+
@@secondary_ciphers = secondary_ciphers
|
83
35
|
end
|
84
36
|
|
85
|
-
#
|
86
|
-
|
87
|
-
|
88
|
-
# private_key: Key used to unlock file containing the actual symmetric key
|
89
|
-
def self.load_keys(key_filename, iv_filename, private_key)
|
90
|
-
# Load Encrypted Symmetric keys
|
91
|
-
encrypted_key = File.read(key_filename)
|
92
|
-
encrypted_iv = File.read(iv_filename)
|
93
|
-
|
94
|
-
# Decrypt Symmetric Key
|
95
|
-
rsa = OpenSSL::PKey::RSA.new(private_key)
|
96
|
-
@@key = rsa.private_decrypt(encrypted_key)
|
97
|
-
@@iv = rsa.private_decrypt(encrypted_iv)
|
98
|
-
nil
|
99
|
-
end
|
100
|
-
|
101
|
-
# Generate new random symmetric keys for use with this Encryption library
|
102
|
-
#
|
103
|
-
# Creates Symmetric Key .key
|
104
|
-
# and initilization vector .iv
|
105
|
-
# which is encrypted with the above Public key
|
106
|
-
#
|
107
|
-
# Note: Existing files will be overwritten
|
108
|
-
def self.generate_symmetric_key_files(filename=nil, environment=nil)
|
109
|
-
# Temporary: Generate private key manually for now. Will automate soon.
|
110
|
-
#new_key = OpenSSL::PKey::RSA.generate(2048)
|
111
|
-
|
112
|
-
filename ||= File.join(Rails.root, "config", "symmetric-encryption.yml")
|
113
|
-
environment ||= (Rails.env || ENV['RAILS'])
|
114
|
-
config = YAML.load_file(filename)[environment]
|
115
|
-
|
116
|
-
raise "Missing mandatory 'key_filename' for environment:#{environment} in #{filename}" unless key_filename = config['symmetric_key_filename']
|
117
|
-
iv_filename = config['symmetric_iv_filename']
|
118
|
-
raise "Missing mandatory 'private_key' for environment:#{environment} in #{filename}" unless private_key = config['private_rsa_key']
|
119
|
-
rsa_key = OpenSSL::PKey::RSA.new(private_key)
|
120
|
-
|
121
|
-
# To ensure compatibility with C openssl code, remove RSA from pub file headers
|
122
|
-
#File.open(File.join(rsa_keys_path, 'private.key'), 'w') {|file| file.write(new_key.to_pem)}
|
123
|
-
|
124
|
-
# Generate Symmetric Key
|
125
|
-
openssl_cipher = OpenSSL::Cipher::Cipher.new(config['cipher'] || 'aes-256-cbc')
|
126
|
-
openssl_cipher.encrypt
|
127
|
-
@@key = openssl_cipher.random_key
|
128
|
-
@@iv = openssl_cipher.random_iv if iv_filename
|
129
|
-
|
130
|
-
# Save symmetric key after encrypting it with the private asymmetric key
|
131
|
-
File.open(key_filename, 'wb') {|file| file.write( rsa_key.public_encrypt(@@key) ) }
|
132
|
-
File.open(iv_filename, 'wb') {|file| file.write( rsa_key.public_encrypt(@@iv) ) } if iv_filename
|
133
|
-
puts("Generated new Symmetric Key for encryption. Please copy #{key_filename} and #{iv_filename} to the other web servers in #{environment}.")
|
134
|
-
end
|
135
|
-
|
136
|
-
# Generate a 22 character random password
|
137
|
-
def self.random_password
|
138
|
-
Base64.encode64(OpenSSL::Cipher::Cipher.new('aes-128-cbc').random_key)[0..-4]
|
37
|
+
# Returns the Primary Symmetric Cipher being used
|
38
|
+
def self.secondary_ciphers
|
39
|
+
@@secondary_ciphers
|
139
40
|
end
|
140
41
|
|
141
42
|
# AES Symmetric Decryption of supplied string
|
142
43
|
# Returns decrypted string
|
143
44
|
# Returns nil if the supplied str is nil
|
144
45
|
# Returns "" if it is a string and it is empty
|
46
|
+
#
|
47
|
+
# Note: If secondary ciphers are supplied in the configuration file the
|
48
|
+
# first key will be used to decrypt 'str'. If it fails each cipher in the
|
49
|
+
# order supplied will be tried.
|
50
|
+
# It is slow to try each cipher in turn, so should be used during migrations
|
51
|
+
# only
|
52
|
+
#
|
53
|
+
# Raises: OpenSSL::Cipher::CipherError when 'str' was not encrypted using
|
54
|
+
# the supplied key and iv
|
55
|
+
#
|
145
56
|
def self.decrypt(str)
|
146
|
-
|
147
|
-
|
57
|
+
raise "Call Symmetric::Encryption.load! or Symmetric::Encryption.cipher= prior to encrypting or decrypting data" unless @@cipher
|
58
|
+
begin
|
59
|
+
@@cipher.decrypt(str)
|
60
|
+
rescue OpenSSL::Cipher::CipherError => exc
|
61
|
+
@@secondary_ciphers.each do |cipher|
|
62
|
+
begin
|
63
|
+
return cipher.decrypt(str)
|
64
|
+
rescue OpenSSL::Cipher::CipherError
|
65
|
+
end
|
66
|
+
end
|
67
|
+
raise exc
|
68
|
+
end
|
148
69
|
end
|
149
70
|
|
150
71
|
# AES Symmetric Encryption of supplied string
|
@@ -152,8 +73,8 @@ module Symmetric
|
|
152
73
|
# Returns nil if the supplied str is nil
|
153
74
|
# Returns "" if it is a string and it is empty
|
154
75
|
def self.encrypt(str)
|
155
|
-
|
156
|
-
|
76
|
+
raise "Call Symmetric::Encryption.load! or Symmetric::Encryption.cipher= prior to encrypting or decrypting data" unless @@cipher
|
77
|
+
@@cipher.encrypt(str)
|
157
78
|
end
|
158
79
|
|
159
80
|
# Invokes decrypt
|
@@ -165,82 +86,176 @@ module Symmetric
|
|
165
86
|
# in the test or development environments but still need to be able to load
|
166
87
|
# YAML config files that contain encrypted development and production passwords
|
167
88
|
def self.try_decrypt(str)
|
168
|
-
|
89
|
+
raise "Call Symmetric::Encryption.load! or Symmetric::Encryption.cipher= prior to encrypting or decrypting data" unless @@cipher
|
90
|
+
begin
|
91
|
+
decrypt(str)
|
92
|
+
rescue OpenSSL::Cipher::CipherError
|
93
|
+
nil
|
94
|
+
end
|
169
95
|
end
|
170
96
|
|
171
|
-
#
|
172
|
-
#
|
173
|
-
#
|
174
|
-
#
|
175
|
-
#
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
97
|
+
# Returns [true|false] a best effort determination as to whether the supplied
|
98
|
+
# string is encrypted or not, without incurring the penalty of actually
|
99
|
+
# decrypting the supplied data
|
100
|
+
# Parameters:
|
101
|
+
# encrypted_data: Encrypted string
|
102
|
+
def self.encrypted?(encrypted_data)
|
103
|
+
raise "Call Symmetric::Encryption.load! or Symmetric::Encryption.cipher= prior to encrypting or decrypting data" unless @@cipher
|
104
|
+
@@cipher.encrypted?(encrypted_data)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Load the Encryption Configuration from a YAML file
|
108
|
+
# filename:
|
109
|
+
# Name of file to read.
|
110
|
+
# Mandatory for non-Rails apps
|
111
|
+
# Default: Rails.root/config/symmetric-encryption.yml
|
112
|
+
# environment:
|
113
|
+
# Which environments config to load. Usually: production, development, etc.
|
114
|
+
# Default: Rails.env
|
115
|
+
def self.load!(filename=nil, environment=nil)
|
116
|
+
config = read_config(filename, environment)
|
117
|
+
|
118
|
+
# Check for hard coded key, iv and cipher
|
119
|
+
if config[:key]
|
120
|
+
@@cipher = Cipher.new(config)
|
121
|
+
@@secondary_ciphers = []
|
122
|
+
else
|
123
|
+
private_rsa_key = config[:private_rsa_key]
|
124
|
+
@@cipher, *@@secondary_ciphers = config[:ciphers].collect do |cipher_conf|
|
125
|
+
cipher_from_encrypted_files(
|
126
|
+
private_rsa_key,
|
127
|
+
cipher_conf[:cipher],
|
128
|
+
cipher_conf[:key_filename],
|
129
|
+
cipher_conf[:iv_filename])
|
197
130
|
end
|
198
131
|
end
|
199
|
-
|
200
|
-
|
201
|
-
# '@EnC'
|
202
|
-
# unsigned short (32 bits) in little endian format for flags above
|
203
|
-
# 'actual encrypted buffer data'
|
204
|
-
"#{MAGIC_HEADER}#{[flags].pack('v')}#{encrypted}"
|
132
|
+
|
133
|
+
true
|
205
134
|
end
|
206
135
|
|
207
|
-
#
|
208
|
-
#
|
209
|
-
|
210
|
-
#
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
136
|
+
# Future: Generate private key in config file generator
|
137
|
+
#new_key = OpenSSL::PKey::RSA.generate(2048)
|
138
|
+
|
139
|
+
# Generate new random symmetric keys for use with this Encryption library
|
140
|
+
#
|
141
|
+
# Note: Only the current Encryption key settings are used
|
142
|
+
#
|
143
|
+
# Creates Symmetric Key .key
|
144
|
+
# and initilization vector .iv
|
145
|
+
# which is encrypted with the above Public key
|
146
|
+
#
|
147
|
+
# Warning: Existing files will be overwritten
|
148
|
+
def self.generate_symmetric_key_files(filename=nil, environment=nil)
|
149
|
+
config = read_config(filename, environment)
|
150
|
+
cipher_cfg = config[:ciphers].first
|
151
|
+
key_filename = cipher_cfg[:key_filename]
|
152
|
+
iv_filename = cipher_cfg[:iv_filename]
|
153
|
+
cipher = cipher_cfg[:cipher]
|
154
|
+
|
155
|
+
raise "The configuration file must contain a 'private_rsa_key' parameter to generate symmetric keys" unless config[:private_rsa_key]
|
156
|
+
rsa_key = OpenSSL::PKey::RSA.new(config[:private_rsa_key])
|
157
|
+
|
158
|
+
# Generate a new Symmetric Key pair
|
159
|
+
key_pair = Symmetric::Cipher.random_key_pair(cipher || 'aes-256-cbc', !iv_filename.nil?)
|
160
|
+
|
161
|
+
# Save symmetric key after encrypting it with the private RSA key, backing up existing files if present
|
162
|
+
File.rename(key_filename, "#{key_filename}.#{Time.now.to_i}") if File.exist?(key_filename)
|
163
|
+
File.open(key_filename, 'wb') {|file| file.write( rsa_key.public_encrypt(key_pair[:key]) ) }
|
164
|
+
|
165
|
+
if iv_filename
|
166
|
+
File.rename(iv_filename, "#{iv_filename}.#{Time.now.to_i}") if File.exist?(iv_filename)
|
167
|
+
File.open(iv_filename, 'wb') {|file| file.write( rsa_key.public_encrypt(key_pair[:iv]) ) }
|
168
|
+
end
|
169
|
+
puts("Generated new Symmetric Key for encryption. Please copy #{key_filename} and #{iv_filename} to the other web servers in #{environment}.")
|
170
|
+
end
|
171
|
+
|
172
|
+
# Generate a 22 character random password
|
173
|
+
def self.random_password
|
174
|
+
Base64.encode64(OpenSSL::Cipher.new('aes-128-cbc').random_key)[0..-4]
|
175
|
+
end
|
176
|
+
|
177
|
+
protected
|
178
|
+
|
179
|
+
# Returns the Encryption Configuration
|
180
|
+
#
|
181
|
+
# Read the configuration from the YAML file and return in the latest format
|
182
|
+
#
|
183
|
+
# filename:
|
184
|
+
# Name of file to read.
|
185
|
+
# Mandatory for non-Rails apps
|
186
|
+
# Default: Rails.root/config/symmetric-encryption.yml
|
187
|
+
# environment:
|
188
|
+
# Which environments config to load. Usually: production, development, etc.
|
189
|
+
def self.read_config(filename=nil, environment=nil)
|
190
|
+
config = YAML.load_file(filename || File.join(Rails.root, "config", "symmetric-encryption.yml"))[environment || Rails.env]
|
191
|
+
|
192
|
+
# Default cipher
|
193
|
+
default_cipher = config['cipher'] || 'aes-256-cbc'
|
194
|
+
cfg = {}
|
195
|
+
|
196
|
+
# Hard coded symmetric_key? - Dev / Testing use only!
|
197
|
+
if symmetric_key = (config['key'] || config['symmetric_key'])
|
198
|
+
raise "Symmetric::Encryption Cannot hard code Production encryption keys in #{filename}" if (environment || Rails.env) == 'production'
|
199
|
+
cfg[:key] = symmetric_key
|
200
|
+
cfg[:iv] = config['iv'] || config['symmetric_iv']
|
201
|
+
cfg[:cipher] = default_cipher
|
202
|
+
|
203
|
+
elsif ciphers = config['ciphers']
|
204
|
+
raise "Missing mandatory config parameter 'private_rsa_key'" unless cfg[:private_rsa_key] = config['private_rsa_key']
|
205
|
+
|
206
|
+
cfg[:ciphers] = ciphers.collect do |cipher_cfg|
|
207
|
+
key_filename = cipher_cfg['key_filename'] || cipher_cfg['symmetric_key_filename']
|
208
|
+
raise "Missing mandatory 'key_filename' for environment:#{environment} in #{filename}" unless key_filename
|
209
|
+
iv_filename = cipher_cfg['iv_filename'] || cipher_cfg['symmetric_iv_filename']
|
210
|
+
{
|
211
|
+
:cipher => cipher_cfg['cipher'] || default_cipher,
|
212
|
+
:key_filename => key_filename,
|
213
|
+
:iv_filename => iv_filename,
|
214
|
+
}
|
227
215
|
end
|
216
|
+
|
228
217
|
else
|
229
|
-
|
218
|
+
# Migrate old format config
|
219
|
+
raise "Missing mandatory config parameter 'private_rsa_key'" unless cfg['private_rsa_key'] = config['private_rsa_key']
|
220
|
+
cfg[:ciphers] = [ {
|
221
|
+
:cipher => cipher['cipher'] || default_cipher,
|
222
|
+
:key_filename => config['symmetric_key_filename'],
|
223
|
+
:iv_filename => config['symmetric_iv_filename'],
|
224
|
+
} ]
|
230
225
|
end
|
231
|
-
|
226
|
+
|
227
|
+
cfg
|
232
228
|
end
|
233
229
|
|
234
|
-
|
230
|
+
# Returns an instance of Symmetric::Cipher initialized from keys
|
231
|
+
# stored in files
|
232
|
+
#
|
233
|
+
# Raises an Exception on failure
|
234
|
+
#
|
235
|
+
# Parameters:
|
236
|
+
# cipher
|
237
|
+
# Encryption cipher for the symmetric encryption key
|
238
|
+
# private_key
|
239
|
+
# Key used to unlock file containing the actual symmetric key
|
240
|
+
# key_filename
|
241
|
+
# Name of file containing symmetric key encrypted using the public
|
242
|
+
# key matching the supplied private_key
|
243
|
+
# iv_filename
|
244
|
+
# Optional. Name of file containing symmetric key initialization vector
|
245
|
+
# encrypted using the public key matching the supplied private_key
|
246
|
+
def self.cipher_from_encrypted_files(private_rsa_key, cipher, key_filename, iv_filename = nil)
|
247
|
+
# Load Encrypted Symmetric keys
|
248
|
+
encrypted_key = File.read(key_filename)
|
249
|
+
encrypted_iv = File.read(iv_filename) if iv_filename
|
235
250
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
251
|
+
# Decrypt Symmetric Keys
|
252
|
+
rsa = OpenSSL::PKey::RSA.new(private_rsa_key)
|
253
|
+
iv = rsa.private_decrypt(encrypted_iv) if iv_filename
|
254
|
+
Cipher.new(
|
255
|
+
:key => rsa.private_decrypt(encrypted_key),
|
256
|
+
:iv => iv,
|
257
|
+
:cipher => cipher
|
258
|
+
)
|
244
259
|
end
|
245
260
|
|
246
261
|
end
|
data/lib/symmetric/version.rb
CHANGED
data/lib/symmetric-encryption.rb
CHANGED
data/test/attr_encrypted_test.rb
CHANGED
@@ -9,26 +9,10 @@ require 'shoulda'
|
|
9
9
|
require 'active_record'
|
10
10
|
require 'symmetric-encryption'
|
11
11
|
|
12
|
-
# #TODO Need to supply the model and migrations for this test
|
13
|
-
# Adding to existing AR model User
|
14
|
-
# Unit Test for Symmetric::Encryption
|
15
|
-
#
|
16
|
-
|
17
|
-
#ROOT = File.join(File.dirname(__FILE__), '..')
|
18
|
-
|
19
|
-
#['/lib', '/db'].each do |folder|
|
20
|
-
# $:.unshift File.join(ROOT, folder)
|
21
|
-
#end
|
22
|
-
|
23
12
|
ActiveRecord::Base.logger = Logger.new($stderr)
|
24
13
|
ActiveRecord::Base.configurations = YAML::load(ERB.new(IO.read('test/config/database.yml')).result)
|
25
14
|
ActiveRecord::Base.establish_connection('test')
|
26
15
|
|
27
|
-
#ActiveRecord::Base.connection.create_database 'symmetric_encryption_test', :charset => :utf8
|
28
|
-
#require 'db/schema'
|
29
|
-
|
30
|
-
#The file db/schema.rb contains, for example:
|
31
|
-
|
32
16
|
ActiveRecord::Schema.define :version => 0 do
|
33
17
|
create_table :users, :force => true do |t|
|
34
18
|
t.string :encrypted_bank_account_number
|
@@ -44,6 +28,10 @@ class User < ActiveRecord::Base
|
|
44
28
|
validates :encrypted_social_security_number, :symmetric_encrypted => true
|
45
29
|
end
|
46
30
|
|
31
|
+
#
|
32
|
+
# Unit Test for attr_encrypted and validation aspects of Symmetric::Encryption
|
33
|
+
#
|
34
|
+
|
47
35
|
class AttrEncryptedTest < Test::Unit::TestCase
|
48
36
|
context 'initialized' do
|
49
37
|
|
@@ -66,10 +54,10 @@ class AttrEncryptedTest < Test::Unit::TestCase
|
|
66
54
|
|
67
55
|
setup do
|
68
56
|
@bank_account_number = "1234567890"
|
69
|
-
@bank_account_number_encrypted = "
|
57
|
+
@bank_account_number_encrypted = "L94ArJeFlJrZp6SYsvoOGA==\n"
|
70
58
|
|
71
59
|
@social_security_number = "987654321"
|
72
|
-
@social_security_number_encrypted = "
|
60
|
+
@social_security_number_encrypted = "S+8X1NRrqdfEIQyFHVPuVA==\n"
|
73
61
|
|
74
62
|
@user = User.new(
|
75
63
|
# Encrypted Attribute
|
data/test/cipher_test.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# Allow examples to be run in-place without requiring a gem install
|
2
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'test/unit'
|
6
|
+
require 'shoulda'
|
7
|
+
require 'symmetric/cipher'
|
8
|
+
|
9
|
+
# Unit Test for Symmetric::Cipher
|
10
|
+
#
|
11
|
+
class CipherTest < Test::Unit::TestCase
|
12
|
+
context 'standalone' do
|
13
|
+
|
14
|
+
should "allow setting the cipher" do
|
15
|
+
cipher = Symmetric::Cipher.new(
|
16
|
+
:cipher => 'aes-128-cbc',
|
17
|
+
:key => '1234567890ABCDEF1234567890ABCDEF',
|
18
|
+
:iv => '1234567890ABCDEF'
|
19
|
+
)
|
20
|
+
assert_equal 'aes-128-cbc', cipher.cipher
|
21
|
+
end
|
22
|
+
|
23
|
+
should "not require an iv" do
|
24
|
+
cipher = Symmetric::Cipher.new(
|
25
|
+
:key => '1234567890ABCDEF1234567890ABCDEF'
|
26
|
+
)
|
27
|
+
assert_equal "wjzpl29q+tsxyLBWAQsn5g==\n", cipher.encrypt('Hello World')
|
28
|
+
end
|
29
|
+
|
30
|
+
should "throw an exception on bad data" do
|
31
|
+
cipher = Symmetric::Cipher.new(
|
32
|
+
:cipher => 'aes-128-cbc',
|
33
|
+
:key => '1234567890ABCDEF1234567890ABCDEF',
|
34
|
+
:iv => '1234567890ABCDEF'
|
35
|
+
)
|
36
|
+
assert_raise OpenSSL::Cipher::CipherError do
|
37
|
+
cipher.decrypt('bad data')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'with configuration' do
|
44
|
+
setup do
|
45
|
+
@cipher = Symmetric::Cipher.new(
|
46
|
+
:key => '1234567890ABCDEF1234567890ABCDEF',
|
47
|
+
:iv => '1234567890ABCDEF'
|
48
|
+
)
|
49
|
+
@social_security_number = "987654321"
|
50
|
+
@social_security_number_encrypted = "Qd0qzN6oVuATJQBTf8X6tg==\n"
|
51
|
+
end
|
52
|
+
|
53
|
+
should "default to 'aes-256-cbc'" do
|
54
|
+
assert_equal 'aes-256-cbc', @cipher.cipher
|
55
|
+
end
|
56
|
+
|
57
|
+
should "encrypt simple string" do
|
58
|
+
assert_equal @social_security_number_encrypted, @cipher.encrypt(@social_security_number)
|
59
|
+
end
|
60
|
+
|
61
|
+
should "decrypt string" do
|
62
|
+
assert_equal @social_security_number, @cipher.decrypt(@social_security_number_encrypted)
|
63
|
+
end
|
64
|
+
|
65
|
+
should "determine if string is encrypted" do
|
66
|
+
assert_equal true, @cipher.encrypted?(@social_security_number_encrypted)
|
67
|
+
assert_equal false, @cipher.encrypted?(@social_security_number)
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
#
|
2
|
-
# Test Config
|
2
|
+
# Test Config with multiple keys
|
3
3
|
#
|
4
4
|
---
|
5
5
|
test:
|
6
|
-
#
|
6
|
+
# Test RSA Key, DO NOT use this RSA key, generate a new one using
|
7
|
+
# openssl genrsa 2048
|
7
8
|
private_rsa_key: |
|
8
9
|
-----BEGIN RSA PRIVATE KEY-----
|
9
10
|
MIIEpAIBAAKCAQEAxIL9H/jYUGpA38v6PowRSRJEo3aNVXULNM/QNRpx2DTf++KH
|
@@ -33,11 +34,14 @@ test:
|
|
33
34
|
r1URaMAun2PfAB4g2N/kEZTExgeOGqXjFhvvjdzl97ux2cTyZhaTXg==
|
34
35
|
-----END RSA PRIVATE KEY-----
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
ciphers:
|
38
|
+
# Current / Newest Symmetric Encryption Key
|
39
|
+
- key_filename: /Users/rmorrison/Sandbox/symmetric-encryption/test/config/test_new.key
|
40
|
+
iv_filename: /Users/rmorrison/Sandbox/symmetric-encryption/test/config/test_new.iv
|
41
|
+
cipher: aes-128-cbc
|
42
|
+
|
43
|
+
# Previous Symmetric Encryption Key
|
44
|
+
- key_filename: /Users/rmorrison/Sandbox/symmetric-encryption/test/config/test_secondary_1.key
|
45
|
+
iv_filename: /Users/rmorrison/Sandbox/symmetric-encryption/test/config/test_secondary_1.iv
|
46
|
+
cipher: aes-128-cbc
|
41
47
|
|
42
|
-
# Use aes-256-cbc encryption
|
43
|
-
cipher: aes-256-cbc
|
File without changes
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
N�ʤd�X0��Vܝ���5]��$�y؎��=���Mq>�pP����gY���}+_�0�)�6���{�F�gN���#��Gρ'�۪���Q�I-�+f���S��~�x|t����C~�h�t8l��V簤�z+�ĺO�MKz"7N��?<�?ր�5B��D�<mq!ۺcHwHcH��?�]/_�s���[��iH^:��ٰ�{V�|�C~y\�B�y Fc,8i�5��r�ƍQ<첀
|
data/test/encryption_test.rb
CHANGED
@@ -5,22 +5,28 @@ require 'rubygems'
|
|
5
5
|
require 'test/unit'
|
6
6
|
require 'shoulda'
|
7
7
|
|
8
|
+
Symmetric::Encryption.load!(File.join(File.dirname(__FILE__), 'config', 'symmetric-encryption.yml'), 'test')
|
9
|
+
|
8
10
|
# Unit Test for Symmetric::Encryption
|
9
11
|
#
|
10
12
|
class EncryptionTest < Test::Unit::TestCase
|
11
13
|
context 'initialized' do
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
+
context 'Symmetric::Encryption configuration' do
|
16
|
+
setup do
|
17
|
+
@config = Symmetric::Encryption.send(:read_config, File.join(File.dirname(__FILE__), 'config', 'symmetric-encryption.yml'), 'test')
|
18
|
+
end
|
19
|
+
|
20
|
+
should "match config file" do
|
21
|
+
assert_equal @config[:ciphers][0][:cipher], Symmetric::Encryption.cipher.cipher
|
22
|
+
end
|
15
23
|
end
|
16
24
|
|
17
25
|
context 'Symmetric::Encryption tests' do
|
18
26
|
setup do
|
19
|
-
@bank_account_number = "1234567890"
|
20
|
-
@bank_account_number_encrypted = "QUxoUU8O/mi0o9ykgXNBFg==\n"
|
21
|
-
|
22
27
|
@social_security_number = "987654321"
|
23
|
-
@social_security_number_encrypted = "
|
28
|
+
@social_security_number_encrypted = "S+8X1NRrqdfEIQyFHVPuVA==\n"
|
29
|
+
@social_security_number_encrypted_with_secondary_1 = "D1UCu38pqJ3jc0GvwJHiow==\n"
|
24
30
|
end
|
25
31
|
|
26
32
|
should "encrypt simple string" do
|
@@ -35,6 +41,10 @@ class EncryptionTest < Test::Unit::TestCase
|
|
35
41
|
assert_equal true, Symmetric::Encryption.encrypted?(@social_security_number_encrypted)
|
36
42
|
assert_equal false, Symmetric::Encryption.encrypted?(@social_security_number)
|
37
43
|
end
|
44
|
+
|
45
|
+
should "decrypt with secondary key when first one fails" do
|
46
|
+
assert_equal @social_security_number, Symmetric::Encryption.decrypt(@social_security_number_encrypted)
|
47
|
+
end
|
38
48
|
end
|
39
49
|
end
|
40
50
|
|