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.
@@ -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
- # Note: If secondary ciphers are supplied in the configuration file the
59
- # first key will be used to decrypt 'str'. If it fails each cipher in the
60
- # order supplied will be tried.
61
- # It is slow to try each cipher in turn, so should be used during migrations
62
- # only
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 supplied key and iv
75
+ # the primary key and iv
66
76
  #
67
- def self.decrypt(str)
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
- # Decode and then decrypt supplied string
71
- begin
72
- @@cipher.decrypt(str)
73
- rescue OpenSSL::Cipher::CipherError => exc
74
- @@secondary_ciphers.each do |cipher|
75
- begin
76
- return cipher.decrypt(str)
77
- rescue OpenSSL::Cipher::CipherError
78
- end
79
- end
80
- raise exc
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
- config = read_config(filename, environment)
162
-
163
- # Check for hard coded key, iv and cipher
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
- config = read_config(filename, environment)
188
- cipher_cfg = config[:ciphers].first
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
- raise "The configuration file must contain a 'private_rsa_key' parameter to generate symmetric keys" unless config[:private_rsa_key]
194
- rsa_key = OpenSSL::PKey::RSA.new(config[:private_rsa_key])
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
- # Save symmetric key after encrypting it with the private RSA key, backing up existing files if present
200
- File.rename(key_filename, "#{key_filename}.#{Time.now.to_i}") if File.exist?(key_filename)
201
- File.open(key_filename, 'wb') {|file| file.write( rsa_key.public_encrypt(key_pair[:key]) ) }
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 Encryption Configuration
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
- config = YAML.load_file(filename || File.join(Rails.root, "config", "symmetric-encryption.yml"))[environment || Rails.env]
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
- # Migrate old format config
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 initialized from keys
280
- # stored in files
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
- # key_filename
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 matching the supplied private_key
293
- # iv_filename
294
- # Optional. Name of file containing symmetric key initialization vector
295
- # encrypted using the public key matching the supplied private_key
296
- def self.cipher_from_encrypted_files(private_rsa_key, cipher_conf)
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 = cipher_conf[:key_filename]
299
- encrypted_key = begin
300
- File.read(key_filename, :open_args => ['rb'])
301
- rescue Errno::ENOENT
302
- puts "\nSymmetric Encryption key file: '#{key_filename}' not found or readable."
303
- puts "To generate the keys for the first time run: rails generate symmetric_encryption:new_keys\n\n"
304
- return
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
- iv_filename = cipher_conf[:iv_filename]
308
- encrypted_iv = begin
309
- File.read(iv_filename, :open_args => ['rb']) if iv_filename
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
- rsa = OpenSSL::PKey::RSA.new(private_rsa_key)
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
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module SymmetricEncryption #:nodoc
3
- VERSION = "2.2.0"
3
+ VERSION = "3.0.0"
4
4
  end
@@ -144,7 +144,7 @@ module SymmetricEncryption
144
144
 
145
145
  # Write the Encryption header including the random iv, key, and cipher
146
146
  if header
147
- @ios.write(Cipher.magic_header(
147
+ @ios.write(Cipher.build_header(
148
148
  cipher.version,
149
149
  compress,
150
150
  random_iv ? iv : nil,
@@ -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 = "L94ArJeFlJrZp6SYsvoOGA=="
59
+ @bank_account_number_encrypted = "QEVuQwIAL94ArJeFlJrZp6SYsvoOGA=="
60
60
 
61
61
  @social_security_number = "987654321"
62
- @social_security_number_encrypted = "S+8X1NRrqdfEIQyFHVPuVA=="
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 "magic header" do
96
+ context "with header" do
97
+ setup do
98
+ @social_security_number = "987654321"
99
+ end
97
100
 
98
- should "create and parse magic header" do
99
- random_cipher = SymmetricEncryption::Cipher.new(SymmetricEncryption::Cipher.random_key_pair)
100
- header = SymmetricEncryption::Cipher.magic_header(1, compressed=true, random_cipher.send(:iv), random_cipher.send(:key), random_cipher.cipher_name)
101
- compressed, iv, key, cipher_name, version, decryption_cipher = SymmetricEncryption::Cipher.parse_magic_header!(header)
102
- assert_equal true, compressed
103
- assert_equal random_cipher.cipher_name, cipher_name, "Ciphers differ"
104
- assert_equal random_cipher.send(:key), key, "Keys differ"
105
- assert_equal random_cipher.send(:iv), iv, "IVs differ"
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
- - key_filename: test/config/test_new.key
40
- iv_filename: test/config/test_new.iv
41
- cipher_name: aes-128-cbc
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: base64strict
44
- version: 1
49
+ encoding: base64strict
50
+ version: 2
51
+ always_add_header: true
45
52
 
46
- # Previous Symmetric Encryption Key
47
- - key_filename: test/config/test_secondary_1.key
48
- iv_filename: test/config/test_secondary_1.iv
49
- cipher_name: aes-128-cbc
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: base64
52
- version: 0
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 = "L94ArJeFlJrZp6SYsvoOGA=="
41
+ @bank_account_number_encrypted = "QEVuQwIAL94ArJeFlJrZp6SYsvoOGA=="
42
42
 
43
43
  @social_security_number = "987654321"
44
- @social_security_number_encrypted = "S+8X1NRrqdfEIQyFHVPuVA=="
44
+ @social_security_number_encrypted = "QEVuQwIAS+8X1NRrqdfEIQyFHVPuVA=="
45
45
 
46
46
  @integer = 32768
47
47
  @integer_encrypted = "FA3smFQEKqB/ITv+A0xACg=="