symmetric-encryption 2.2.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +39 -1
- data/Rakefile +4 -16
- data/lib/symmetric_encryption/cipher.rb +189 -103
- data/lib/symmetric_encryption/reader.rb +16 -5
- data/lib/symmetric_encryption/symmetric_encryption.rb +203 -116
- data/lib/symmetric_encryption/version.rb +1 -1
- data/lib/symmetric_encryption/writer.rb +1 -1
- data/test/attr_encrypted_test.rb +2 -2
- data/test/cipher_test.rb +30 -10
- data/test/config/symmetric-encryption.yml +41 -11
- data/test/field_encrypted_test.rb +2 -2
- data/test/reader_test.rb +23 -14
- data/test/symmetric_encryption_test.rb +58 -26
- data/test/test_db.sqlite3 +0 -0
- data/test/writer_test.rb +4 -0
- metadata +24 -16
- data/Gemfile +0 -19
- data/Gemfile.lock +0 -61
- data/nbproject/private/config.properties +0 -0
- data/nbproject/private/private.properties +0 -1
- data/nbproject/private/private.xml +0 -4
- data/nbproject/private/rake-d.txt +0 -4
- data/nbproject/project.properties +0 -9
- data/nbproject/project.xml +0 -16
@@ -2,6 +2,7 @@ require 'base64'
|
|
2
2
|
require 'openssl'
|
3
3
|
require 'zlib'
|
4
4
|
require 'yaml'
|
5
|
+
require 'erb'
|
5
6
|
|
6
7
|
# Encrypt using 256 Bit AES CBC symmetric key and initialization vector
|
7
8
|
# The symmetric key is protected using the private key below and must
|
@@ -11,6 +12,7 @@ module SymmetricEncryption
|
|
11
12
|
# Defaults
|
12
13
|
@@cipher = nil
|
13
14
|
@@secondary_ciphers = []
|
15
|
+
@@select_cipher = nil
|
14
16
|
|
15
17
|
# Set the Primary Symmetric Cipher to be used
|
16
18
|
#
|
@@ -55,29 +57,45 @@ module SymmetricEncryption
|
|
55
57
|
# Returns nil if the supplied str is nil
|
56
58
|
# Returns "" if it is a string and it is empty
|
57
59
|
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
60
|
+
# Parameters
|
61
|
+
# str
|
62
|
+
# Encrypted string to decrypt
|
63
|
+
# version
|
64
|
+
# Specify which cipher version to use if no header is present on the
|
65
|
+
# encrypted string
|
66
|
+
#
|
67
|
+
# If the supplied string has an encryption header then the cipher matching
|
68
|
+
# the version number in the header will be used to decrypt the string
|
69
|
+
#
|
70
|
+
# When no header is present in the encrypted data, a custom Block/Proc can
|
71
|
+
# be supplied to determine which cipher to use to decrypt the data.
|
72
|
+
# see #cipher_selector=
|
63
73
|
#
|
64
74
|
# Raises: OpenSSL::Cipher::CipherError when 'str' was not encrypted using
|
65
|
-
# the
|
75
|
+
# the primary key and iv
|
66
76
|
#
|
67
|
-
|
77
|
+
# NOTE: #decrypt will _not_ attempt to use a secondary cipher if it fails
|
78
|
+
# to decrypt the current string. This is because in a very small
|
79
|
+
# yet significant number of cases it is possible to decrypt data using
|
80
|
+
# the incorrect key. Clearly the data returned is garbage, but it still
|
81
|
+
# successfully returns a string of data
|
82
|
+
def self.decrypt(encrypted_and_encoded_string, version=nil)
|
68
83
|
raise "Call SymmetricEncryption.load! or SymmetricEncryption.cipher= prior to encrypting or decrypting data" unless @@cipher
|
84
|
+
return if encrypted_and_encoded_string.nil? || (encrypted_and_encoded_string == '')
|
69
85
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
86
|
+
str = encrypted_and_encoded_string.to_s
|
87
|
+
|
88
|
+
# Decode before decrypting supplied string
|
89
|
+
decoded = @@cipher.decode(str)
|
90
|
+
return unless decoded
|
91
|
+
return decoded if decoded.empty?
|
92
|
+
|
93
|
+
if header = Cipher.parse_header!(decoded)
|
94
|
+
header.decryption_cipher.binary_decrypt(decoded, header)
|
95
|
+
else
|
96
|
+
# Use cipher_selector if present to decide which cipher to use
|
97
|
+
c = @@select_cipher.nil? ? cipher(version) : @@select_cipher.call(str, decoded)
|
98
|
+
c.binary_decrypt(decoded)
|
81
99
|
end
|
82
100
|
end
|
83
101
|
|
@@ -129,6 +147,9 @@ module SymmetricEncryption
|
|
129
147
|
# different environment. I.e. We cannot decode production passwords
|
130
148
|
# in the test or development environments but still need to be able to load
|
131
149
|
# YAML config files that contain encrypted development and production passwords
|
150
|
+
#
|
151
|
+
# WARNING: It is possible to decrypt data using the wrong key, so the value
|
152
|
+
# returned should not be relied upon
|
132
153
|
def self.try_decrypt(str)
|
133
154
|
raise "Call SymmetricEncryption.load! or SymmetricEncryption.cipher= prior to encrypting or decrypting data" unless @@cipher
|
134
155
|
begin
|
@@ -141,6 +162,10 @@ module SymmetricEncryption
|
|
141
162
|
# Returns [true|false] as to whether the data could be decrypted
|
142
163
|
# Parameters:
|
143
164
|
# encrypted_data: Encrypted string
|
165
|
+
#
|
166
|
+
# WARNING: This method can only be relied upon if the encrypted data includes the
|
167
|
+
# symmetric encryption header. In some cases data decrypted using the
|
168
|
+
# wrong key will decrypt and return garbage
|
144
169
|
def self.encrypted?(encrypted_data)
|
145
170
|
raise "Call SymmetricEncryption.load! or SymmetricEncryption.cipher= prior to encrypting or decrypting data" unless @@cipher
|
146
171
|
|
@@ -149,6 +174,35 @@ module SymmetricEncryption
|
|
149
174
|
!(result.nil? || result == '')
|
150
175
|
end
|
151
176
|
|
177
|
+
# When no header is present in the encrypted data, this custom Block/Proc is
|
178
|
+
# used to determine which cipher to use to decrypt the data.
|
179
|
+
#
|
180
|
+
# The Block must return a valid cipher
|
181
|
+
#
|
182
|
+
# Parameters
|
183
|
+
# encoded_str
|
184
|
+
# The original encoded string
|
185
|
+
#
|
186
|
+
# decoded_str
|
187
|
+
# The string after being decoded using the global encoding
|
188
|
+
#
|
189
|
+
# NOTE: Do _not_ attempt to use a secondary cipher if the previous fails
|
190
|
+
# to decrypt due to an OpenSSL::Cipher::CipherError exception.
|
191
|
+
# This is because in a very small, yet significant number of cases it is
|
192
|
+
# possible to decrypt data using the incorrect key.
|
193
|
+
# Clearly the data returned is garbage, but it still successfully
|
194
|
+
# returns a string of data
|
195
|
+
#
|
196
|
+
# Example:
|
197
|
+
# SymmetricEncryption.select_cipher do |encoded_str, decoded_str|
|
198
|
+
# # Use cipher version 0 if the encoded string ends with "\n" otherwise
|
199
|
+
# # use the current default cipher
|
200
|
+
# encoded_str.end_with?("\n") ? SymmetricEncryption.cipher(0) : SymmetricEncryption.cipher
|
201
|
+
# end
|
202
|
+
def self.select_cipher(&block)
|
203
|
+
@@select_cipher = block ? block : nil
|
204
|
+
end
|
205
|
+
|
152
206
|
# Load the Encryption Configuration from a YAML file
|
153
207
|
# filename:
|
154
208
|
# Name of file to read.
|
@@ -158,19 +212,9 @@ module SymmetricEncryption
|
|
158
212
|
# Which environments config to load. Usually: production, development, etc.
|
159
213
|
# Default: Rails.env
|
160
214
|
def self.load!(filename=nil, environment=nil)
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
if config[:key]
|
165
|
-
@@cipher = Cipher.new(config)
|
166
|
-
@@secondary_ciphers = []
|
167
|
-
else
|
168
|
-
private_rsa_key = config[:private_rsa_key]
|
169
|
-
@@cipher, *@@secondary_ciphers = config[:ciphers].collect do |cipher_conf|
|
170
|
-
cipher_from_encrypted_files(private_rsa_key, cipher_conf)
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
215
|
+
ciphers = read_config(filename, environment)
|
216
|
+
@@cipher = ciphers.shift
|
217
|
+
@@secondary_ciphers = ciphers
|
174
218
|
true
|
175
219
|
end
|
176
220
|
|
@@ -184,32 +228,53 @@ module SymmetricEncryption
|
|
184
228
|
#
|
185
229
|
# Existing key files will be renamed if present
|
186
230
|
def self.generate_symmetric_key_files(filename=nil, environment=nil)
|
187
|
-
|
188
|
-
|
189
|
-
key_filename = cipher_cfg[:key_filename]
|
190
|
-
iv_filename = cipher_cfg[:iv_filename]
|
191
|
-
cipher_name = cipher_cfg[:cipher_name] || cipher_cfg[:cipher]
|
231
|
+
config_filename = filename || File.join(Rails.root, "config", "symmetric-encryption.yml")
|
232
|
+
config = YAML.load(ERB.new(File.new(config_filename).read).result)[environment || Rails.env]
|
192
233
|
|
193
|
-
|
194
|
-
|
234
|
+
# RSA key to decrypt key files
|
235
|
+
private_rsa_key = config.delete('private_rsa_key')
|
236
|
+
raise "The configuration file must contain a 'private_rsa_key' parameter to generate symmetric keys" unless private_rsa_key
|
237
|
+
rsa_key = OpenSSL::PKey::RSA.new(private_rsa_key)
|
238
|
+
|
239
|
+
# Check if config file contains 1 or multiple ciphers
|
240
|
+
ciphers = config.delete('ciphers')
|
241
|
+
cfg = ciphers.nil? ? config : ciphers.first
|
242
|
+
|
243
|
+
# Convert keys to symbols
|
244
|
+
cipher_cfg = {}
|
245
|
+
cfg.each_pair{|k,v| cipher_cfg[k.to_sym] = v}
|
246
|
+
|
247
|
+
cipher_name = cipher_cfg[:cipher_name] || cipher_cfg[:cipher]
|
195
248
|
|
196
249
|
# Generate a new Symmetric Key pair
|
250
|
+
iv_filename = cipher_cfg[:iv_filename]
|
197
251
|
key_pair = SymmetricEncryption::Cipher.random_key_pair(cipher_name || 'aes-256-cbc', !iv_filename.nil?)
|
198
252
|
|
199
|
-
|
200
|
-
|
201
|
-
|
253
|
+
if key_filename = cipher_cfg[:key_filename]
|
254
|
+
# Save symmetric key after encrypting it with the private RSA key, backing up existing files if present
|
255
|
+
File.rename(key_filename, "#{key_filename}.#{Time.now.to_i}") if File.exist?(key_filename)
|
256
|
+
File.open(key_filename, 'wb') {|file| file.write( rsa_key.public_encrypt(key_pair[:key]) ) }
|
257
|
+
puts("Generated new Symmetric Key for encryption. Please copy #{key_filename} to the other web servers in #{environment}.")
|
258
|
+
elsif !cipher_cfg[:key]
|
259
|
+
key = rsa_key.public_encrypt(key_pair[:key])
|
260
|
+
puts "Generated new Symmetric Key for encryption. Set the KEY environment variable in #{environment} to:"
|
261
|
+
puts ::Base64.encode64(key)
|
262
|
+
end
|
202
263
|
|
203
264
|
if iv_filename
|
204
265
|
File.rename(iv_filename, "#{iv_filename}.#{Time.now.to_i}") if File.exist?(iv_filename)
|
205
266
|
File.open(iv_filename, 'wb') {|file| file.write( rsa_key.public_encrypt(key_pair[:iv]) ) }
|
267
|
+
puts("Generated new Symmetric Key for encryption. Please copy #{iv_filename} to the other web servers in #{environment}.")
|
268
|
+
elsif !cipher_cfg[:iv]
|
269
|
+
iv = rsa_key.public_encrypt(key_pair[:iv])
|
270
|
+
puts "Generated new Symmetric Key for encryption. Set the IV environment variable in #{environment} to:"
|
271
|
+
puts ::Base64.encode64(key)
|
206
272
|
end
|
207
|
-
puts("Generated new Symmetric Key for encryption. Please copy #{key_filename} and #{iv_filename} to the other web servers in #{environment}.")
|
208
273
|
end
|
209
274
|
|
210
275
|
# Generate a 22 character random password
|
211
276
|
def self.random_password
|
212
|
-
Base64.encode64(OpenSSL::Cipher.new('aes-128-cbc').random_key)[0..-4]
|
277
|
+
Base64.encode64(OpenSSL::Cipher.new('aes-128-cbc').random_key)[0..-4].strip
|
213
278
|
end
|
214
279
|
|
215
280
|
# Binary encrypted data includes this magic header so that we can quickly
|
@@ -222,7 +287,7 @@ module SymmetricEncryption
|
|
222
287
|
|
223
288
|
protected
|
224
289
|
|
225
|
-
# Returns the
|
290
|
+
# Returns [Array(SymmetricEncrytion::Cipher)] ciphers specified in the configuration file
|
226
291
|
#
|
227
292
|
# Read the configuration from the YAML file and return in the latest format
|
228
293
|
#
|
@@ -233,96 +298,118 @@ module SymmetricEncryption
|
|
233
298
|
# environment:
|
234
299
|
# Which environments config to load. Usually: production, development, etc.
|
235
300
|
def self.read_config(filename=nil, environment=nil)
|
236
|
-
|
237
|
-
|
238
|
-
# Default cipher
|
239
|
-
default_cipher = config['cipher_name'] || config['cipher'] || 'aes-256-cbc'
|
240
|
-
cfg = {}
|
241
|
-
|
242
|
-
# Hard coded symmetric_key? - Dev / Testing use only!
|
243
|
-
if symmetric_key = (config['key'] || config['symmetric_key'])
|
244
|
-
raise "SymmetricEncryption Cannot hard code Production encryption keys in #{filename}" if (environment || Rails.env) == 'production'
|
245
|
-
cfg[:key] = symmetric_key
|
246
|
-
cfg[:iv] = config['iv'] || config['symmetric_iv']
|
247
|
-
cfg[:cipher_name] = default_cipher
|
248
|
-
cfg[:version] = config['version']
|
249
|
-
|
250
|
-
elsif ciphers = config['ciphers']
|
251
|
-
raise "Missing mandatory config parameter 'private_rsa_key'" unless cfg[:private_rsa_key] = config['private_rsa_key']
|
252
|
-
|
253
|
-
cfg[:ciphers] = ciphers.collect do |cipher_cfg|
|
254
|
-
key_filename = cipher_cfg['key_filename'] || cipher_cfg['symmetric_key_filename']
|
255
|
-
raise "Missing mandatory 'key_filename' for environment:#{environment} in #{filename}" unless key_filename
|
256
|
-
iv_filename = cipher_cfg['iv_filename'] || cipher_cfg['symmetric_iv_filename']
|
257
|
-
{
|
258
|
-
:cipher_name => cipher_cfg['cipher_name'] || cipher_cfg['cipher'] || default_cipher,
|
259
|
-
:key_filename => key_filename,
|
260
|
-
:iv_filename => iv_filename,
|
261
|
-
:encoding => cipher_cfg['encoding'],
|
262
|
-
:version => cipher_cfg['version']
|
263
|
-
}
|
264
|
-
end
|
301
|
+
config_filename = filename || File.join(Rails.root, "config", "symmetric-encryption.yml")
|
302
|
+
config = YAML.load(ERB.new(File.new(config_filename).read).result)[environment || Rails.env]
|
265
303
|
|
304
|
+
# RSA key to decrypt key files
|
305
|
+
private_rsa_key = config.delete('private_rsa_key')
|
306
|
+
|
307
|
+
if ciphers = config.delete('ciphers')
|
308
|
+
ciphers.collect {|cipher_conf| cipher_from_config(cipher_conf, private_rsa_key)}
|
266
309
|
else
|
267
|
-
|
268
|
-
raise "Missing mandatory config parameter 'private_rsa_key'" unless cfg[:private_rsa_key] = config['private_rsa_key']
|
269
|
-
cfg[:ciphers] = [ {
|
270
|
-
:cipher_name => default_cipher,
|
271
|
-
:key_filename => config['symmetric_key_filename'],
|
272
|
-
:iv_filename => config['symmetric_iv_filename'],
|
273
|
-
} ]
|
310
|
+
[cipher_from_config(config, private_rsa_key)]
|
274
311
|
end
|
275
|
-
|
276
|
-
cfg
|
277
312
|
end
|
278
313
|
|
279
|
-
# Returns an instance of SymmetricEncryption::Cipher
|
280
|
-
#
|
314
|
+
# Returns an instance of SymmetricEncryption::Cipher created from
|
315
|
+
# the supplied configuration and optional rsa_encryption_key
|
281
316
|
#
|
282
317
|
# Raises an Exception on failure
|
283
318
|
#
|
284
319
|
# Parameters:
|
285
|
-
# private_rsa_key
|
286
|
-
# Key used to unlock file containing the actual symmetric key
|
287
320
|
# cipher_conf Hash:
|
288
|
-
# cipher_name
|
321
|
+
# :cipher_name
|
289
322
|
# Encryption cipher name for the symmetric encryption key
|
290
|
-
#
|
323
|
+
#
|
324
|
+
# :version
|
325
|
+
# The version number of this cipher
|
326
|
+
# Default: 0
|
327
|
+
#
|
328
|
+
# :encoding [Symbol]
|
329
|
+
# Encoding to use after encrypting with this cipher
|
330
|
+
#
|
331
|
+
# :always_add_header
|
332
|
+
# Whether to always include the header when encrypting data.
|
333
|
+
# Highly recommended to set this value to true.
|
334
|
+
# Increases the length of the encrypted data by 6 bytes, but makes
|
335
|
+
# migration to a new key trivial
|
336
|
+
# Default: false
|
337
|
+
#
|
338
|
+
# :key
|
339
|
+
# The actual key to use for encryption/decryption purposes
|
340
|
+
#
|
341
|
+
# :key_filename
|
291
342
|
# Name of file containing symmetric key encrypted using the public
|
292
|
-
# key
|
293
|
-
#
|
294
|
-
#
|
295
|
-
# encrypted using the public key
|
296
|
-
|
343
|
+
# key from the private_rsa_key
|
344
|
+
#
|
345
|
+
# :encrypted_key
|
346
|
+
# Symmetric key encrypted using the public key from the private_rsa_key
|
347
|
+
#
|
348
|
+
# :iv
|
349
|
+
# Optional: The actual iv to use for encryption/decryption purposes
|
350
|
+
#
|
351
|
+
# :encrypted_iv
|
352
|
+
# Initialization vector encrypted using the public key from the private_rsa_key
|
353
|
+
#
|
354
|
+
# :iv_filename
|
355
|
+
# Optional: Name of file containing symmetric key initialization vector
|
356
|
+
# encrypted using the public key from the private_rsa_key
|
357
|
+
#
|
358
|
+
# private_rsa_key [String]
|
359
|
+
# RSA Key used to decrypt key and iv as applicable
|
360
|
+
def self.cipher_from_config(cipher_conf, private_rsa_key=nil)
|
361
|
+
config = {}
|
362
|
+
cipher_conf.each_pair{|k,v| config[k.to_sym] = v}
|
363
|
+
|
364
|
+
# To decrypt encrypted key or iv files
|
365
|
+
rsa = OpenSSL::PKey::RSA.new(private_rsa_key) if private_rsa_key
|
366
|
+
|
297
367
|
# Load Encrypted Symmetric keys
|
298
|
-
key_filename =
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
368
|
+
if key_filename = config.delete(:key_filename)
|
369
|
+
raise "Missing mandatory config parameter :private_rsa_key when :key_filename is supplied" unless rsa
|
370
|
+
encrypted_key = begin
|
371
|
+
File.read(key_filename, :open_args => ['rb'])
|
372
|
+
rescue Errno::ENOENT
|
373
|
+
puts "\nSymmetric Encryption key file: '#{key_filename}' not found or readable."
|
374
|
+
puts "To generate the keys for the first time run: rails generate symmetric_encryption:new_keys\n\n"
|
375
|
+
return
|
376
|
+
end
|
377
|
+
config[:key] = rsa.private_decrypt(encrypted_key)
|
378
|
+
end
|
379
|
+
|
380
|
+
if iv_filename = config.delete(:iv_filename)
|
381
|
+
raise "Missing mandatory config parameter :private_rsa_key when :iv_filename is supplied" unless rsa
|
382
|
+
encrypted_iv = begin
|
383
|
+
File.read(iv_filename, :open_args => ['rb']) if iv_filename
|
384
|
+
rescue Errno::ENOENT
|
385
|
+
puts "\nSymmetric Encryption initialization vector file: '#{iv_filename}' not found or readable."
|
386
|
+
puts "To generate the keys for the first time run: rails generate symmetric_encryption:new_keys\n\n"
|
387
|
+
return
|
388
|
+
end
|
389
|
+
config[:iv] = rsa.private_decrypt(encrypted_iv)
|
390
|
+
end
|
391
|
+
|
392
|
+
if encrypted_key = config.delete(:encrypted_key)
|
393
|
+
raise "Missing mandatory config parameter :private_rsa_key when :encrypted_key is supplied" unless rsa
|
394
|
+
# Decode value first using encoding specified
|
395
|
+
encrypted_key = ::Base64.decode64(encrypted_key)
|
396
|
+
config[:key] = rsa.private_decrypt(encrypted_key)
|
397
|
+
end
|
398
|
+
|
399
|
+
if encrypted_iv = config.delete(:encrypted_iv)
|
400
|
+
raise "Missing mandatory config parameter :private_rsa_key when :encrypted_iv is supplied" unless rsa
|
401
|
+
# Decode value first using encoding specified
|
402
|
+
encrypted_iv = ::Base64.decode64(encrypted_iv)
|
403
|
+
config[:iv] = rsa.private_decrypt(encrypted_iv)
|
305
404
|
end
|
306
405
|
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
rescue Errno::ENOENT
|
311
|
-
puts "\nSymmetric Encryption initialization vector file: '#{iv_filename}' not found or readable."
|
312
|
-
puts "To generate the keys for the first time run: rails generate symmetric_encryption:new_keys\n\n"
|
313
|
-
return
|
406
|
+
# Backward compatibility
|
407
|
+
if old_key_name_cipher = config.delete(:cipher)
|
408
|
+
config[:cipher_name] = old_key_name_cipher
|
314
409
|
end
|
315
410
|
|
316
411
|
# Decrypt Symmetric Keys
|
317
|
-
|
318
|
-
iv = rsa.private_decrypt(encrypted_iv) if iv_filename
|
319
|
-
Cipher.new(
|
320
|
-
:key => rsa.private_decrypt(encrypted_key),
|
321
|
-
:iv => iv,
|
322
|
-
:cipher_name => cipher_conf[:cipher_name],
|
323
|
-
:encoding => cipher_conf[:encoding],
|
324
|
-
:version => cipher_conf[:version]
|
325
|
-
)
|
412
|
+
Cipher.new(config)
|
326
413
|
end
|
327
414
|
|
328
415
|
# With Ruby 1.9 strings have encodings
|
data/test/attr_encrypted_test.rb
CHANGED
@@ -56,10 +56,10 @@ class AttrEncryptedTest < Test::Unit::TestCase
|
|
56
56
|
|
57
57
|
setup do
|
58
58
|
@bank_account_number = "1234567890"
|
59
|
-
@bank_account_number_encrypted = "
|
59
|
+
@bank_account_number_encrypted = "QEVuQwIAL94ArJeFlJrZp6SYsvoOGA=="
|
60
60
|
|
61
61
|
@social_security_number = "987654321"
|
62
|
-
@social_security_number_encrypted = "
|
62
|
+
@social_security_number_encrypted = "QEVuQwIAS+8X1NRrqdfEIQyFHVPuVA=="
|
63
63
|
|
64
64
|
@string = "A string containing some data to be encrypted with a random initialization vector"
|
65
65
|
@long_string = "A string containing some data to be encrypted with a random initialization vector and compressed since it takes up so much space in plain text form"
|
data/test/cipher_test.rb
CHANGED
@@ -93,22 +93,42 @@ class CipherTest < Test::Unit::TestCase
|
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
96
|
-
context "
|
96
|
+
context "with header" do
|
97
|
+
setup do
|
98
|
+
@social_security_number = "987654321"
|
99
|
+
end
|
97
100
|
|
98
|
-
should "
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
assert_equal true, compressed
|
103
|
-
|
104
|
-
assert_equal random_cipher.
|
105
|
-
assert_equal random_cipher.send(:
|
101
|
+
should "build and parse header" do
|
102
|
+
assert random_key_pair = SymmetricEncryption::Cipher.random_key_pair('aes-128-cbc')
|
103
|
+
assert binary_header = SymmetricEncryption::Cipher.build_header(SymmetricEncryption.cipher.version, compressed=true, random_key_pair[:iv], random_key_pair[:key], random_key_pair[:cipher_name], binary=true)
|
104
|
+
header = SymmetricEncryption::Cipher.parse_header!(binary_header)
|
105
|
+
assert_equal true, header.compressed
|
106
|
+
assert random_cipher = SymmetricEncryption::Cipher.new(random_key_pair)
|
107
|
+
assert_equal random_cipher.cipher_name, header.cipher_name, "Ciphers differ"
|
108
|
+
assert_equal random_cipher.send(:key), header.key, "Keys differ"
|
109
|
+
assert_equal random_cipher.send(:iv), header.iv, "IVs differ"
|
106
110
|
|
107
111
|
string = "Hello World"
|
108
|
-
cipher = SymmetricEncryption::Cipher.new(:key => key, :iv => iv, :cipher_name => cipher_name)
|
112
|
+
cipher = SymmetricEncryption::Cipher.new(:key => header.key, :iv => header.iv, :cipher_name => header.cipher_name)
|
109
113
|
# Test Encryption
|
110
114
|
assert_equal random_cipher.encrypt(string, false, false), cipher.encrypt(string, false, false), "Encrypted values differ"
|
111
115
|
end
|
116
|
+
|
117
|
+
should "encrypt and then decrypt without a header" do
|
118
|
+
assert encrypted = @cipher.binary_encrypt(@social_security_number,false,false,false)
|
119
|
+
assert_equal @social_security_number, @cipher.decrypt(encrypted)
|
120
|
+
end
|
121
|
+
|
122
|
+
should "encrypt and then decrypt using random iv" do
|
123
|
+
assert encrypted = @cipher.encrypt(@social_security_number, random_iv=true)
|
124
|
+
assert_equal @social_security_number, @cipher.decrypt(encrypted)
|
125
|
+
end
|
126
|
+
|
127
|
+
should "encrypt and then decrypt using random iv with compression" do
|
128
|
+
assert encrypted = @cipher.encrypt(@social_security_number, random_iv=true, compress=true)
|
129
|
+
assert_equal @social_security_number, @cipher.decrypt(encrypted)
|
130
|
+
end
|
131
|
+
|
112
132
|
end
|
113
133
|
|
114
134
|
end
|
@@ -5,6 +5,7 @@
|
|
5
5
|
test:
|
6
6
|
# Test RSA Key, DO NOT use this RSA key, generate a new one using
|
7
7
|
# openssl genrsa 2048
|
8
|
+
# Or use the rails generator to create a new config file as described in the readme
|
8
9
|
private_rsa_key: |
|
9
10
|
-----BEGIN RSA PRIVATE KEY-----
|
10
11
|
MIIEpAIBAAKCAQEAxIL9H/jYUGpA38v6PowRSRJEo3aNVXULNM/QNRpx2DTf++KH
|
@@ -36,18 +37,47 @@ test:
|
|
36
37
|
|
37
38
|
ciphers:
|
38
39
|
# Current / Newest Symmetric Encryption Key
|
39
|
-
|
40
|
-
|
41
|
-
|
40
|
+
#
|
41
|
+
# To manually generate new keys once this file has been generated:
|
42
|
+
# require 'symmetric_encryption'
|
43
|
+
# SymmetricEncryption.generate_symmetric_key_files('this_file_name.yml', 'production')
|
44
|
+
-
|
45
|
+
key_filename: test/config/test_new.key
|
46
|
+
iv_filename: test/config/test_new.iv
|
47
|
+
cipher_name: aes-128-cbc
|
42
48
|
# Base64 encode encrypted data without newlines
|
43
|
-
encoding:
|
44
|
-
version:
|
49
|
+
encoding: base64strict
|
50
|
+
version: 2
|
51
|
+
always_add_header: true
|
45
52
|
|
46
|
-
#
|
47
|
-
-
|
48
|
-
|
49
|
-
|
53
|
+
# Prior Symmetric Encryption Key specified in environment variable
|
54
|
+
-
|
55
|
+
# Base64 encoded and RSA encrypted, encryption key
|
56
|
+
# encrypted_key can be used for retrieving the encrypted key from a source
|
57
|
+
# other than a local file.
|
58
|
+
#
|
59
|
+
# Example:
|
60
|
+
# # An environment variable:
|
61
|
+
# encrypted_key: <%= ENV['KEY'] %>
|
62
|
+
#
|
63
|
+
# NOTE: Do not put the encrypted key directly in this file. It is only here
|
64
|
+
# for testing purposes
|
65
|
+
encrypted_key: <%= 'xFAsZ73PThktyo76PoNQGYnjCJUAd4+Yaz71bO5FajshXsbjkfZjjvbK9hxzWLr+C7X67hcrTypVHB1Rw0De8lRDqexlc87sTx1wtlz70lOvTBXt9Lv4sbJNLxacuqk545LIJpgK02Dq7FGzACV3jb3Yk+QQngiscETYM6PyiuFpReFB0qFOgCSLeBJsXAdNdqkEZggl8PL+lGDueDGeKUng+Ic/AFWPhJGYkk3xV++AGwUFXdDQeuHllxmV9WlzriHnDwzbfugkfGaRjWn808VXrv9Jgf2yRy++gOYUvRnjZ1ltOgXUEEmBVF2Uvhu+zs6C/D4cb1mkR7911M5naA==' %>
|
66
|
+
# For testing purposes only, the above RSA encrypted key is just:
|
67
|
+
# key: ABCDEF1234567890
|
68
|
+
iv: 1234567890ABCDEF
|
69
|
+
cipher_name: aes-128-cbc
|
50
70
|
# Base64 encode encrypted data without newlines
|
51
|
-
encoding:
|
52
|
-
version:
|
71
|
+
encoding: base64strict
|
72
|
+
version: 1
|
73
|
+
always_add_header: false
|
53
74
|
|
75
|
+
# First Symmetric Encryption Key
|
76
|
+
-
|
77
|
+
key_filename: test/config/test_secondary_1.key
|
78
|
+
iv_filename: test/config/test_secondary_1.iv
|
79
|
+
cipher_name: aes-128-cbc
|
80
|
+
# Base64 encode encrypted data
|
81
|
+
encoding: base64
|
82
|
+
version: 0
|
83
|
+
always_add_header: false
|
@@ -38,10 +38,10 @@ class FieldEncryptedTest < Test::Unit::TestCase
|
|
38
38
|
context 'the SymmetricEncryption Library' do
|
39
39
|
setup do
|
40
40
|
@bank_account_number = "1234567890"
|
41
|
-
@bank_account_number_encrypted = "
|
41
|
+
@bank_account_number_encrypted = "QEVuQwIAL94ArJeFlJrZp6SYsvoOGA=="
|
42
42
|
|
43
43
|
@social_security_number = "987654321"
|
44
|
-
@social_security_number_encrypted = "
|
44
|
+
@social_security_number_encrypted = "QEVuQwIAS+8X1NRrqdfEIQyFHVPuVA=="
|
45
45
|
|
46
46
|
@integer = 32768
|
47
47
|
@integer_encrypted = "FA3smFQEKqB/ITv+A0xACg=="
|