symmetric-encryption 3.8.3 → 3.9.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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/Rakefile +16 -16
  4. data/examples/symmetric-encryption.yml +33 -38
  5. data/lib/rails/generators/symmetric_encryption/config/templates/symmetric-encryption.yml +10 -14
  6. data/lib/rails/generators/symmetric_encryption/heroku_config/templates/symmetric-encryption.yml +28 -25
  7. data/lib/symmetric_encryption.rb +14 -6
  8. data/lib/symmetric_encryption/cipher.rb +151 -130
  9. data/lib/symmetric_encryption/config.rb +0 -1
  10. data/lib/symmetric_encryption/encoder.rb +79 -0
  11. data/lib/symmetric_encryption/extensions/active_record/base.rb +94 -134
  12. data/lib/symmetric_encryption/extensions/mongo_mapper/plugins/encrypted_key.rb +3 -89
  13. data/lib/symmetric_encryption/key_encryption_key.rb +32 -0
  14. data/lib/symmetric_encryption/railtie.rb +3 -3
  15. data/lib/symmetric_encryption/symmetric_encryption.rb +41 -8
  16. data/lib/symmetric_encryption/utils/re_encrypt_config_files.rb +82 -0
  17. data/lib/symmetric_encryption/version.rb +1 -1
  18. data/test/active_record_test.rb +149 -140
  19. data/test/cipher_test.rb +98 -6
  20. data/test/config/{mongoid_v5.yml → mongoid.yml} +0 -0
  21. data/test/config/symmetric-encryption.yml +4 -10
  22. data/test/config/test_new.key +2 -2
  23. data/test/encoder_test.rb +61 -0
  24. data/test/mongoid_test.rb +12 -22
  25. data/test/reader_test.rb +16 -11
  26. data/test/symmetric_encryption_test.rb +23 -3
  27. data/test/test_db.sqlite3 +0 -0
  28. data/test/test_helper.rb +2 -16
  29. data/test/writer_test.rb +1 -5
  30. metadata +11 -12
  31. data/test/config/mongoid_v2.yml +0 -6
  32. data/test/config/mongoid_v3.yml +0 -9
  33. data/test/mongo_mapper_test.rb +0 -599
data/test/cipher_test.rb CHANGED
@@ -8,7 +8,7 @@ class CipherTest < Minitest::Test
8
8
  it 'allow setting the cipher_name' do
9
9
  cipher = SymmetricEncryption::Cipher.new(
10
10
  cipher_name: 'aes-128-cbc',
11
- key: '1234567890ABCDEF1234567890ABCDEF',
11
+ key: '1234567890ABCDEF',
12
12
  iv: '1234567890ABCDEF',
13
13
  encoding: :none
14
14
  )
@@ -32,7 +32,7 @@ class CipherTest < Minitest::Test
32
32
  it 'throw an exception on bad data' do
33
33
  cipher = SymmetricEncryption::Cipher.new(
34
34
  cipher_name: 'aes-128-cbc',
35
- key: '1234567890ABCDEF1234567890ABCDEF',
35
+ key: '1234567890ABCDEF',
36
36
  iv: '1234567890ABCDEF',
37
37
  encoding: :none
38
38
  )
@@ -44,7 +44,7 @@ class CipherTest < Minitest::Test
44
44
  end
45
45
 
46
46
  [false, true].each do |always_add_header|
47
- SymmetricEncryption::Cipher::ENCODINGS.each do |encoding|
47
+ [:none, :base64, :base64strict, :base16].each do |encoding|
48
48
  describe "encoding: #{encoding} with#{'out' unless always_add_header} header" do
49
49
  before do
50
50
  @social_security_number = '987654321'
@@ -65,7 +65,7 @@ class CipherTest < Minitest::Test
65
65
  @social_security_number_encrypted_with_secondary_1 = "D1UCu38pqJ3jc0GvwJHiow==\n"
66
66
  @non_utf8 = "\xc2".force_encoding('binary')
67
67
  @cipher = SymmetricEncryption::Cipher.new(
68
- key: 'ABCDEF1234567890ABCDEF1234567890',
68
+ key: 'ABCDEF1234567890',
69
69
  iv: 'ABCDEF1234567890',
70
70
  cipher_name: 'aes-128-cbc',
71
71
  encoding: encoding,
@@ -94,7 +94,7 @@ class CipherTest < Minitest::Test
94
94
  end
95
95
 
96
96
  it 'return nil when encrypting nil' do
97
- assert_equal nil, @cipher.encrypt(nil)
97
+ assert_nil @cipher.encrypt(nil)
98
98
  end
99
99
 
100
100
  it "return '' when encrypting ''" do
@@ -102,7 +102,7 @@ class CipherTest < Minitest::Test
102
102
  end
103
103
 
104
104
  it 'return nil when decrypting nil' do
105
- assert_equal nil, @cipher.decrypt(nil)
105
+ assert_nil @cipher.decrypt(nil)
106
106
  end
107
107
 
108
108
  it "return '' when decrypting ''" do
@@ -172,4 +172,96 @@ class CipherTest < Minitest::Test
172
172
  end
173
173
 
174
174
  end
175
+
176
+ describe '.generate_random_keys' do
177
+ describe 'with wrong params' do
178
+ it 'raises ArgumentError' do
179
+ error = assert_raises ArgumentError do
180
+ SymmetricEncryption::Cipher.generate_random_keys(wrong_params: '')
181
+ end
182
+
183
+ assert_equal "SymmetricEncryption::Cipher Invalid options {:wrong_params=>\"\"}", error.message
184
+ end
185
+ end
186
+
187
+ describe 'without keys' do
188
+ it 'creates new keys' do
189
+ h = SymmetricEncryption::Cipher.generate_random_keys
190
+ assert_equal 'aes-256-cbc', h[:cipher_name]
191
+ assert_equal :base64strict, h[:encoding]
192
+ assert h.has_key?(:key), h
193
+ assert h.has_key?(:iv), h
194
+ end
195
+ end
196
+
197
+ describe 'with keys' do
198
+ it 'creates new keys' do
199
+ h = SymmetricEncryption::Cipher.generate_random_keys(key: '', iv: '')
200
+ assert_equal 'aes-256-cbc', h[:cipher_name]
201
+ assert_equal :base64strict, h[:encoding]
202
+ assert h.has_key?(:key), h
203
+ assert h.has_key?(:iv), h
204
+ end
205
+ end
206
+
207
+ describe 'with encrypted keys' do
208
+ it 'creates new encrypted keys' do
209
+ key_encryption_key = SymmetricEncryption::KeyEncryptionKey.generate
210
+ h = SymmetricEncryption::Cipher.generate_random_keys(
211
+ encrypted_key: '',
212
+ encrypted_iv: '',
213
+ private_rsa_key: key_encryption_key
214
+ )
215
+ assert_equal 'aes-256-cbc', h[:cipher_name]
216
+ assert_equal :base64strict, h[:encoding]
217
+ assert h.has_key?(:encrypted_key), h
218
+ assert h.has_key?(:encrypted_iv), h
219
+ end
220
+
221
+ it 'exception on missing rsa key' do
222
+ assert_raises SymmetricEncryption::ConfigError do
223
+ SymmetricEncryption::Cipher.generate_random_keys(
224
+ encrypted_key: '',
225
+ encrypted_iv: ''
226
+ )
227
+ end
228
+ end
229
+ end
230
+
231
+ describe 'with files' do
232
+ before do
233
+ @key_filename = 'blah.key'
234
+ @iv_filename = 'blah.iv'
235
+ end
236
+
237
+ after do
238
+ File.delete(@key_filename) if File.exist?(@key_filename)
239
+ File.delete(@iv_filename) if File.exist?(@iv_filename)
240
+ end
241
+
242
+ it 'creates new files' do
243
+ key_encryption_key = SymmetricEncryption::KeyEncryptionKey.generate
244
+ h = SymmetricEncryption::Cipher.generate_random_keys(
245
+ key_filename: @key_filename,
246
+ iv_filename: @iv_filename,
247
+ private_rsa_key: key_encryption_key
248
+ )
249
+ assert_equal 'aes-256-cbc', h[:cipher_name]
250
+ assert_equal :base64strict, h[:encoding]
251
+ assert h.has_key?(:key_filename), h
252
+ assert h.has_key?(:iv_filename), h
253
+ assert File.exist?(@key_filename)
254
+ assert File.exist?(@iv_filename)
255
+ end
256
+
257
+ it 'exception on missing rsa key' do
258
+ assert_raises SymmetricEncryption::ConfigError do
259
+ SymmetricEncryption::Cipher.generate_random_keys(
260
+ key_filename: @key_filename,
261
+ iv_filename: @iv_filename
262
+ )
263
+ end
264
+ end
265
+ end
266
+ end
175
267
  end
File without changes
@@ -3,8 +3,8 @@
3
3
  #
4
4
  ---
5
5
  test:
6
- # Test RSA Key, DO NOT use this RSA key, generate a new one using
7
- # openssl genrsa 2048
6
+ # Test Key encryption key, DO NOT use this key, generate a new one using
7
+ # SymmetricEncryption::KeyEncryptionKey.generate
8
8
  # Or use the rails generator to create a new config file as described in the readme
9
9
  private_rsa_key: |
10
10
  -----BEGIN RSA PRIVATE KEY-----
@@ -37,10 +37,6 @@ test:
37
37
 
38
38
  ciphers:
39
39
  # Current / Newest Symmetric Encryption Key
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
40
  -
45
41
  key_filename: test/config/test_new.key
46
42
  iv_filename: test/config/test_new.iv
@@ -52,9 +48,7 @@ test:
52
48
 
53
49
  # Prior Symmetric Encryption Key specified in environment variable
54
50
  -
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.
51
+ # Encryption Key
58
52
  #
59
53
  # Example:
60
54
  # # An environment variable:
@@ -63,7 +57,7 @@ test:
63
57
  # NOTE: Do not put the encrypted key directly in this file. It is only here
64
58
  # for testing purposes
65
59
  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:
60
+ # For testing purposes only, the above key is just:
67
61
  # key: ABCDEF1234567890
68
62
  iv: 1234567890ABCDEF
69
63
  cipher_name: aes-128-cbc
@@ -1,2 +1,2 @@
1
- e@ ���D/�t4��k��"z��Qmg
2
- m��r��Ն��۪�ӃqE-�}��]��=���8�WzVܮ+�R�nt���R2����.��Iu�l
1
+ ��|A|1M[g� �C��� 8w���D$K/�o
2
+ -�VLӠ�~$���d����+�<����э|�zORbA�*b�Έ�'L_nj�� ˎ�|��껰������Ptq<���a�|�lo���&ݯI
@@ -0,0 +1,61 @@
1
+ # encoding: utf-8
2
+ require_relative 'test_helper'
3
+
4
+ # Unit Test for SymmetricEncryption
5
+ #
6
+ class EncoderTest < Minitest::Test
7
+ describe SymmetricEncryption::Encoder do
8
+ [:none, :base64, :base64strict, :base16].each do |encoding|
9
+ describe "encoding: #{encoding}" do
10
+ before do
11
+ @data = '987654321'
12
+ @data_encoded =
13
+ case encoding
14
+ when :base64
15
+ "OTg3NjU0MzIx\n"
16
+ when :base64strict
17
+ 'OTg3NjU0MzIx'
18
+ when :base16
19
+ '393837363534333231'
20
+ when :none
21
+ @data
22
+ end
23
+ @encoder = SymmetricEncryption::Encoder[encoding]
24
+ @non_utf8 = "\xc2".force_encoding('binary')
25
+ end
26
+
27
+ it 'correctly encodes' do
28
+ assert_equal @data_encoded, @encoder.encode(@data)
29
+ assert_equal Encoding.find('UTF-8'), @data_encoded.encoding
30
+ end
31
+
32
+ it 'return BINARY encoding for non-UTF-8 data' do
33
+ assert_equal Encoding.find('binary'), @non_utf8.encoding
34
+ assert @non_utf8.valid_encoding?
35
+ assert encoded = @encoder.encode(@non_utf8)
36
+ assert decoded = @encoder.decode(encoded)
37
+ assert decoded.valid_encoding?
38
+ assert_equal Encoding.find('binary'), decoded.encoding, decoded
39
+ assert_equal @non_utf8, decoded
40
+ end
41
+
42
+ it 'return nil when encoding nil' do
43
+ assert_nil @encoder.encode(nil)
44
+ end
45
+
46
+ it "return '' when encoding ''" do
47
+ assert_equal '', @encoder.encode('')
48
+ end
49
+
50
+ it 'return nil when decoding nil' do
51
+ assert_nil @encoder.decode(nil)
52
+ end
53
+
54
+ it "return '' when decoding ''" do
55
+ assert_equal '', @encoder.decode('')
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ end
data/test/mongoid_test.rb CHANGED
@@ -4,27 +4,17 @@ begin
4
4
  require_relative '../lib/symmetric_encryption/extensions/mongoid/encrypted'
5
5
  ENV['RACK_ENV'] = 'test'
6
6
 
7
- Mongoid.logger = SemanticLogger[Mongoid]
8
- filename =
9
- case Mongoid::VERSION.to_i
10
- when 3, 4
11
- 'test/config/mongoid_v3.yml'
12
- when 1, 2
13
- 'test/config/mongoid_v2.yml'
14
- else
15
- 'test/config/mongoid_v5.yml'
16
- end
17
- Mongoid.load!(filename)
7
+ Mongoid.load!('test/config/mongoid.yml')
18
8
 
19
9
  #@formatter:off
20
10
  class MongoidUser
21
11
  include Mongoid::Document
22
12
 
23
13
  field :name, type: String
24
- field :encrypted_bank_account_number, type: String, encrypted: true
25
- field :encrypted_social_security_number, type: String, encrypted: true
26
- field :encrypted_string, type: String, encrypted: {random_iv: true}
27
- field :encrypted_long_string, type: String, encrypted: {random_iv: true, compress: true}
14
+ field :encrypted_bank_account_number, type: String, encrypted: true
15
+ field :encrypted_social_security_number, type: String, encrypted: true
16
+ field :encrypted_string, type: String, encrypted: {random_iv: true}
17
+ field :encrypted_long_string, type: String, encrypted: {random_iv: true, compress: true}
28
18
 
29
19
  field :encrypted_integer_value, type: String, encrypted: {type: :integer}
30
20
  field :aiv, type: String, encrypted: {type: :integer, decrypt_as: :aliased_integer_value}
@@ -194,14 +184,14 @@ begin
194
184
 
195
185
  it "all paths it lead to the same result, check uninitialized" do
196
186
  user = MongoidUser.new
197
- assert_equal nil, user.social_security_number
187
+ assert_nil user.social_security_number
198
188
  assert_equal @bank_account_number, (user.social_security_number = @bank_account_number)
199
189
  assert_equal @bank_account_number, user.social_security_number
200
190
  assert_equal @bank_account_number_encrypted, user.encrypted_social_security_number
201
191
 
202
- assert_equal nil, (user.social_security_number = nil)
203
- assert_equal nil, user.social_security_number
204
- assert_equal nil, user.encrypted_social_security_number
192
+ assert_nil (user.social_security_number = nil)
193
+ assert_nil user.social_security_number
194
+ assert_nil user.encrypted_social_security_number
205
195
  end
206
196
 
207
197
  it 'allow unencrypted values to be passed to the constructor' do
@@ -385,14 +375,14 @@ begin
385
375
 
386
376
  describe 'time values' do
387
377
  it 'return correct data type' do
388
- assert_equal @time_value, @user_clone.time_value
378
+ assert_equal @time_value, @user_clone.time_value.dup
389
379
  assert @user.clone.time_value.kind_of?(Time)
390
380
  end
391
381
 
392
382
  it 'coerce data type before save' do
393
383
  now = Time.now
394
384
  u = MongoidUser.new(time_value: now)
395
- assert_equal now, u.time_value
385
+ assert_equal now, u.time_value.dup
396
386
  assert u.time_value.kind_of?(Time)
397
387
  end
398
388
 
@@ -411,7 +401,7 @@ begin
411
401
  @user_clone.save!
412
402
 
413
403
  @user.reload
414
- assert_equal new_time_value, @user.time_value
404
+ assert_equal new_time_value, @user.time_value.dup
415
405
  end
416
406
  end
417
407
 
data/test/reader_test.rb CHANGED
@@ -140,14 +140,14 @@ class ReaderTest < Minitest::Test
140
140
  File.delete(@filename) if File.exist?(@filename) && !@filename.end_with?('empty.csv')
141
141
  end
142
142
 
143
- it ".empty?" do
143
+ it '.empty?' do
144
144
  assert_equal (@data_size==0), SymmetricEncryption::Reader.empty?(@filename)
145
145
  assert_raises Errno::ENOENT do
146
146
  SymmetricEncryption::Reader.empty?('missing_file')
147
147
  end
148
148
  end
149
149
 
150
- it ".header_present?" do
150
+ it '.header_present?' do
151
151
  assert_equal @header, SymmetricEncryption::Reader.header_present?(@filename)
152
152
  assert_raises Errno::ENOENT do
153
153
  SymmetricEncryption::Reader.header_present?('missing_file')
@@ -160,7 +160,7 @@ class ReaderTest < Minitest::Test
160
160
  file.close
161
161
  end
162
162
 
163
- it "#read()" do
163
+ it '#read' do
164
164
  data = nil
165
165
  eof = nil
166
166
  result = SymmetricEncryption::Reader.open(@filename) do |file|
@@ -172,17 +172,22 @@ class ReaderTest < Minitest::Test
172
172
  assert_equal @data_str, result
173
173
  end
174
174
 
175
- it "#read(size)" do
175
+ it '#read(size)' do
176
176
  file = SymmetricEncryption::Reader.open(@filename)
177
177
  eof = file.eof?
178
178
  data = file.read(4096)
179
179
  file.close
180
180
 
181
181
  assert_equal @eof, eof
182
- assert_equal (@data_size > 0 ? @data_str : nil), data
182
+ if @data_size > 0
183
+ assert_equal @data_str, data
184
+ else
185
+ assert_nil data
186
+ end
187
+
183
188
  end
184
189
 
185
- it "#each_line" do
190
+ it '#each_line' do
186
191
  SymmetricEncryption::Reader.open(@filename) do |file|
187
192
  i = 0
188
193
  file.each_line do |line|
@@ -192,7 +197,7 @@ class ReaderTest < Minitest::Test
192
197
  end
193
198
  end
194
199
 
195
- it "#rewind" do
200
+ it '#rewind' do
196
201
  decrypted = SymmetricEncryption::Reader.open(@filename) do |file|
197
202
  file.read
198
203
  file.rewind
@@ -201,7 +206,7 @@ class ReaderTest < Minitest::Test
201
206
  assert_equal @data_str, decrypted
202
207
  end
203
208
 
204
- it "#gets(nil,size)" do
209
+ it '#gets(nil,size)' do
205
210
  file = SymmetricEncryption::Reader.open(@filename)
206
211
  eof = file.eof?
207
212
  data = file.gets(nil, 4096)
@@ -216,12 +221,12 @@ class ReaderTest < Minitest::Test
216
221
  if defined?(JRuby) && options[:compress] && (usecase == :empty)
217
222
  assert_equal '', data
218
223
  else
219
- assert_equal nil, data
224
+ assert_nil data
220
225
  end
221
226
  end
222
227
  end
223
228
 
224
- it "#gets(delim)" do
229
+ it '#gets(delim)' do
225
230
  SymmetricEncryption::Reader.open(@filename) do |file|
226
231
  i = 0
227
232
  while line = file.gets("\n")
@@ -232,7 +237,7 @@ class ReaderTest < Minitest::Test
232
237
  end
233
238
  end
234
239
 
235
- it "#gets(delim,size)" do
240
+ it '#gets(delim,size)' do
236
241
  SymmetricEncryption::Reader.open(@filename) do |file|
237
242
  i = 0
238
243
  while file.gets("\n", 128)
@@ -41,7 +41,7 @@ class SymmetricEncryptionTest < Minitest::Test
41
41
  end
42
42
  end
43
43
 
44
- SymmetricEncryption::Cipher::ENCODINGS.each do |encoding|
44
+ [:none, :base64, :base64strict, :base16].each do |encoding|
45
45
  describe "encoding: #{encoding}" do
46
46
  before do
47
47
  @social_security_number = '987654321'
@@ -89,7 +89,7 @@ class SymmetricEncryptionTest < Minitest::Test
89
89
  end
90
90
 
91
91
  it 'return nil when encrypting nil' do
92
- assert_equal nil, SymmetricEncryption.encrypt(nil)
92
+ assert_nil SymmetricEncryption.encrypt(nil)
93
93
  end
94
94
 
95
95
  it "return '' when encrypting ''" do
@@ -97,7 +97,7 @@ class SymmetricEncryptionTest < Minitest::Test
97
97
  end
98
98
 
99
99
  it 'return nil when decrypting nil' do
100
- assert_equal nil, SymmetricEncryption.decrypt(nil)
100
+ assert_nil SymmetricEncryption.decrypt(nil)
101
101
  end
102
102
 
103
103
  it "return '' when decrypting ''" do
@@ -293,6 +293,26 @@ class SymmetricEncryptionTest < Minitest::Test
293
293
  end
294
294
  end
295
295
 
296
+ describe '.generate_symmetric_key_files' do
297
+ let(:params) { {private_rsa_key: 'rsa_key', key: 'key', iv: 'iv'} }
298
+ let(:file_path) { File.join(File.dirname(__FILE__), 'config', 'symmetric-encryption.yml') }
299
+ let(:cipher_config) { {encrypted_key: 'encrypted_key', encrypted_iv: 'encrypted_iv'} }
300
+
301
+ let(:config) do
302
+ {
303
+ private_rsa_key: 'rsa_key',
304
+ ciphers: [{version: 1, always_add_header: true, key: 'key', iv: 'iv'}]
305
+ }
306
+ end
307
+
308
+ it 'removes unused config keys before generate the random keys' do
309
+ SymmetricEncryption::Config.stub(:read_config, config) do
310
+ SymmetricEncryption::Cipher.stub(:generate_random_keys, cipher_config) do
311
+ SymmetricEncryption.generate_symmetric_key_files(file_path, 'test')
312
+ end
313
+ end
314
+ end
315
+ end
296
316
  end
297
317
 
298
318
  end