symmetric-encryption 2.2.0 → 3.0.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.
@@ -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=="