symmetric-encryption 3.9.1 → 4.0.0.beta3

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +72 -0
  3. data/bin/symmetric-encryption +5 -0
  4. data/lib/symmetric_encryption/cipher.rb +162 -419
  5. data/lib/symmetric_encryption/cli.rb +343 -0
  6. data/lib/symmetric_encryption/coerce.rb +5 -20
  7. data/lib/symmetric_encryption/config.rb +128 -50
  8. data/lib/symmetric_encryption/extensions/mongo_mapper/plugins/encrypted_key.rb +2 -2
  9. data/lib/symmetric_encryption/generator.rb +3 -2
  10. data/lib/symmetric_encryption/header.rb +260 -0
  11. data/lib/symmetric_encryption/key.rb +106 -0
  12. data/lib/symmetric_encryption/keystore/environment.rb +90 -0
  13. data/lib/symmetric_encryption/keystore/file.rb +102 -0
  14. data/lib/symmetric_encryption/keystore/memory.rb +53 -0
  15. data/lib/symmetric_encryption/keystore.rb +124 -0
  16. data/lib/symmetric_encryption/railtie.rb +5 -7
  17. data/lib/symmetric_encryption/reader.rb +74 -55
  18. data/lib/symmetric_encryption/rsa_key.rb +24 -0
  19. data/lib/symmetric_encryption/symmetric_encryption.rb +64 -102
  20. data/lib/symmetric_encryption/utils/re_encrypt_files.rb +140 -0
  21. data/lib/symmetric_encryption/version.rb +1 -1
  22. data/lib/symmetric_encryption/writer.rb +104 -117
  23. data/lib/symmetric_encryption.rb +9 -4
  24. data/test/active_record_test.rb +61 -40
  25. data/test/cipher_test.rb +179 -236
  26. data/test/config/symmetric-encryption.yml +140 -82
  27. data/test/header_test.rb +218 -0
  28. data/test/key_test.rb +231 -0
  29. data/test/keystore/environment_test.rb +119 -0
  30. data/test/keystore/file_test.rb +125 -0
  31. data/test/keystore_test.rb +59 -0
  32. data/test/mongoid_test.rb +13 -13
  33. data/test/reader_test.rb +52 -53
  34. data/test/symmetric_encryption_test.rb +50 -135
  35. data/test/test_db.sqlite3 +0 -0
  36. data/test/writer_test.rb +52 -31
  37. metadata +26 -14
  38. data/examples/symmetric-encryption.yml +0 -108
  39. data/lib/rails/generators/symmetric_encryption/config/config_generator.rb +0 -22
  40. data/lib/rails/generators/symmetric_encryption/config/templates/symmetric-encryption.yml +0 -50
  41. data/lib/rails/generators/symmetric_encryption/heroku_config/heroku_config_generator.rb +0 -20
  42. data/lib/rails/generators/symmetric_encryption/heroku_config/templates/symmetric-encryption.yml +0 -78
  43. data/lib/rails/generators/symmetric_encryption/new_keys/new_keys_generator.rb +0 -14
  44. data/lib/symmetric_encryption/key_encryption_key.rb +0 -32
  45. data/lib/symmetric_encryption/railties/symmetric_encryption.rake +0 -84
  46. data/lib/symmetric_encryption/utils/re_encrypt_config_files.rb +0 -82
data/test/cipher_test.rb CHANGED
@@ -1,267 +1,210 @@
1
1
  require_relative 'test_helper'
2
2
 
3
- # Unit Test for SymmetricEncryption::Cipher
4
- #
3
+ # Tests for SymmetricEncryption::Cipher
5
4
  class CipherTest < Minitest::Test
6
- describe 'standalone' do
7
-
8
- it 'allow setting the cipher_name' do
9
- cipher = SymmetricEncryption::Cipher.new(
10
- cipher_name: 'aes-128-cbc',
11
- key: '1234567890ABCDEF',
12
- iv: '1234567890ABCDEF',
13
- encoding: :none
14
- )
15
- assert_equal 'aes-128-cbc', cipher.cipher_name
16
- end
17
-
18
- it 'not require an iv' do
19
- cipher = SymmetricEncryption::Cipher.new(
20
- key: '1234567890ABCDEF1234567890ABCDEF',
21
- encoding: :none
22
- )
23
- result = "\302<\351\227oj\372\3331\310\260V\001\v'\346"
24
- # Note: This test fails on JRuby 1.7 RC1 since it's OpenSSL
25
- # behaves differently when no IV is supplied.
26
- # It instead encrypts to the following value:
27
- # result = "0h\x92\x88\xA1\xFE\x8D\xF5\xF3v\x82\xAF(P\x83Y"
28
- result.force_encoding('binary') if defined?(Encoding)
29
- assert_equal result, cipher.encrypt('Hello World')
30
- end
31
-
32
- it 'throw an exception on bad data' do
33
- cipher = SymmetricEncryption::Cipher.new(
34
- cipher_name: 'aes-128-cbc',
35
- key: '1234567890ABCDEF',
36
- iv: '1234567890ABCDEF',
37
- encoding: :none
38
- )
39
- assert_raises OpenSSL::Cipher::CipherError do
40
- cipher.decrypt('bad data')
41
- end
42
- end
43
-
44
- end
45
-
46
- [false, true].each do |always_add_header|
47
- [:none, :base64, :base64strict, :base16].each do |encoding|
48
- describe "encoding: #{encoding} with#{'out' unless always_add_header} header" do
49
- before do
50
- @social_security_number = '987654321'
51
- @social_security_number_encrypted =
52
- case encoding
53
- when :base64
54
- always_add_header ? "QEVuQwAAyTeLjsHTa8ykoO95K0KQmg==\n" : "yTeLjsHTa8ykoO95K0KQmg==\n"
55
- when :base64strict
56
- always_add_header ? 'QEVuQwAAyTeLjsHTa8ykoO95K0KQmg==' : 'yTeLjsHTa8ykoO95K0KQmg=='
57
- when :base16
58
- always_add_header ? '40456e430000c9378b8ec1d36bcca4a0ef792b42909a' : 'c9378b8ec1d36bcca4a0ef792b42909a'
59
- when :none
60
- bin = always_add_header ? "@EnC\x00\x00\xC97\x8B\x8E\xC1\xD3k\xCC\xA4\xA0\xEFy+B\x90\x9A" : "\xC97\x8B\x8E\xC1\xD3k\xCC\xA4\xA0\xEFy+B\x90\x9A"
61
- bin.force_encoding(Encoding.find('binary'))
62
- else
63
- raise "Add test for encoding: #{encoding}"
64
- end
65
- @social_security_number_encrypted_with_secondary_1 = "D1UCu38pqJ3jc0GvwJHiow==\n"
66
- @non_utf8 = "\xc2".force_encoding('binary')
67
- @cipher = SymmetricEncryption::Cipher.new(
68
- key: 'ABCDEF1234567890',
69
- iv: 'ABCDEF1234567890',
70
- cipher_name: 'aes-128-cbc',
71
- encoding: encoding,
72
- always_add_header: always_add_header
5
+ ['aes-128-cbc'].each do |cipher_name|
6
+ #['aes-128-cbc', 'aes-128-gcm'].each do |cipher_name|
7
+ describe "Cipher: #{cipher_name}" do
8
+ describe 'standalone' do
9
+ it 'allows setting the cipher_name' do
10
+ cipher = SymmetricEncryption::Cipher.new(
11
+ cipher_name: cipher_name,
12
+ key: '1234567890ABCDEF',
13
+ iv: '1234567890ABCDEF',
14
+ encoding: :none
73
15
  )
16
+ assert_equal cipher_name, cipher.cipher_name
74
17
  end
75
18
 
76
- it 'encrypt simple string' do
77
- assert_equal @social_security_number_encrypted, @cipher.encrypt(@social_security_number)
78
- end
79
-
80
- it 'decrypt string' do
81
- assert decrypted = @cipher.decrypt(@social_security_number_encrypted)
82
- assert_equal @social_security_number, decrypted
83
- assert_equal Encoding.find('utf-8'), decrypted.encoding, decrypted
84
- end
85
-
86
- it 'return BINARY encoding for non-UTF-8 encrypted data' do
87
- assert_equal Encoding.find('binary'), @non_utf8.encoding
88
- assert_equal true, @non_utf8.valid_encoding?
89
- assert encrypted = @cipher.encrypt(@non_utf8)
90
- assert decrypted = @cipher.decrypt(encrypted)
91
- assert_equal true, decrypted.valid_encoding?
92
- assert_equal Encoding.find('binary'), decrypted.encoding, decrypted
93
- assert_equal @non_utf8, decrypted
94
- end
95
-
96
- it 'return nil when encrypting nil' do
97
- assert_nil @cipher.encrypt(nil)
98
- end
99
-
100
- it "return '' when encrypting ''" do
101
- assert_equal '', @cipher.encrypt('')
102
- end
103
-
104
- it 'return nil when decrypting nil' do
105
- assert_nil @cipher.decrypt(nil)
19
+ it 'does not require an iv' do
20
+ cipher = SymmetricEncryption::Cipher.new(
21
+ key: '1234567890ABCDEF',
22
+ cipher_name: cipher_name,
23
+ encoding: :none,
24
+ always_add_header: false
25
+ )
26
+ assert result = cipher.encrypt('Hello World')
27
+ assert_equal 'Hello World', cipher.decrypt(result)
106
28
  end
107
29
 
108
- it "return '' when decrypting ''" do
109
- assert_equal '', @cipher.decrypt('')
30
+ it 'throw an exception on bad data' do
31
+ cipher = SymmetricEncryption::Cipher.new(
32
+ cipher_name: cipher_name,
33
+ key: '1234567890ABCDEF',
34
+ iv: '1234567890ABCDEF',
35
+ encoding: :none
36
+ )
37
+ assert_raises OpenSSL::Cipher::CipherError do
38
+ cipher.decrypt('bad data')
39
+ end
110
40
  end
111
41
  end
112
- end
113
- end
114
-
115
- describe 'with configuration' do
116
- before do
117
- @cipher = SymmetricEncryption::Cipher.new(
118
- key: '1234567890ABCDEF1234567890ABCDEF',
119
- iv: '1234567890ABCDEF',
120
- encoding: :none
121
- )
122
- @social_security_number = '987654321'
123
42
 
124
- @social_security_number_encrypted = "A\335*\314\336\250V\340\023%\000S\177\305\372\266"
125
- @social_security_number_encrypted.force_encoding('binary') if defined?(Encoding)
126
-
127
- @sample_data = [
128
- {text: '555052345', encrypted: ''}
129
- ]
130
- end
131
-
132
- it "default to 'aes-256-cbc'" do
133
- assert_equal 'aes-256-cbc', @cipher.cipher_name
134
- end
135
-
136
- describe 'with header' do
137
- before do
138
- @social_security_number = '987654321'
139
- end
43
+ [false, true].each do |always_add_header|
44
+ [:none, :base64, :base64strict, :base16].each do |encoding|
45
+ describe "encoding: #{encoding} with#{'out' unless always_add_header} header" do
46
+ before do
47
+ @social_security_number = '987654321'
48
+ @encrypted_values = {
49
+ 'aes-128-cbc' => {
50
+ base64: {
51
+ header: "QEVuQwAAyTeLjsHTa8ykoO95K0KQmg==\n",
52
+ no_header: "yTeLjsHTa8ykoO95K0KQmg==\n"
53
+ },
54
+ base64strict: {
55
+ header: 'QEVuQwAAyTeLjsHTa8ykoO95K0KQmg==',
56
+ no_header: 'yTeLjsHTa8ykoO95K0KQmg=='
57
+ },
58
+ base16: {
59
+ header: '40456e430000c9378b8ec1d36bcca4a0ef792b42909a',
60
+ no_header: 'c9378b8ec1d36bcca4a0ef792b42909a'
61
+ },
62
+ none: {
63
+ header: "@EnC\x00\x00\xC97\x8B\x8E\xC1\xD3k\xCC\xA4\xA0\xEFy+B\x90\x9A",
64
+ no_header: "\xC97\x8B\x8E\xC1\xD3k\xCC\xA4\xA0\xEFy+B\x90\x9A"
65
+ },
66
+ },
67
+ # 'aes-128-gcm' => {
68
+ # base64: {
69
+ # header: "QEVuQwAAOcqz9UDbd1Sn\n",
70
+ # no_header: "Ocqz9UDbd1Sn\n"
71
+ # },
72
+ # base64strict: {
73
+ # header: 'QEVuQwAAOcqz9UDbd1Sn',
74
+ # no_header: 'Ocqz9UDbd1Sn'
75
+ # },
76
+ # base16: {
77
+ # header: '40456e43000039cab3f540db7754a7',
78
+ # no_header: '39cab3f540db7754a7'
79
+ # },
80
+ # none: {
81
+ # header: "@EnC\x00\x009\xCA\xB3\xF5@\xDBwT\xA7",
82
+ # no_header: "9\xCA\xB3\xF5@\xDBwT\xA7"
83
+ # },
84
+ # }
85
+ }
86
+
87
+ @non_utf8 = "\xc2".force_encoding('binary')
88
+ @cipher = SymmetricEncryption::Cipher.new(
89
+ key: 'ABCDEF1234567890',
90
+ iv: 'ABCDEF1234567890',
91
+ cipher_name: cipher_name,
92
+ encoding: encoding,
93
+ always_add_header: always_add_header
94
+ )
95
+
96
+ h = @encrypted_values[cipher_name][encoding] if @encrypted_values[cipher_name]
97
+ skip "Add @encrypted_values for cipher_name: #{cipher_name} and encoding: #{encoding}, value: #{@cipher.encrypt(@social_security_number).inspect}" unless h
98
+ @social_security_number_encrypted = h[always_add_header ? :header : :no_header]
99
+
100
+ @social_security_number_encrypted.force_encoding(Encoding.find('binary')) if encoding == :none
101
+ end
140
102
 
141
- it 'build and parse header' do
142
- assert random_key_pair = SymmetricEncryption::Cipher.random_key_pair('aes-128-cbc')
143
- assert binary_header = SymmetricEncryption::Cipher.build_header(SymmetricEncryption.cipher.version, true, random_key_pair[:iv], random_key_pair[:key], random_key_pair[:cipher_name])
144
- header = SymmetricEncryption::Cipher.parse_header!(binary_header)
145
- assert_equal true, header.compressed
146
- assert random_cipher = SymmetricEncryption::Cipher.new(random_key_pair)
147
- assert_equal random_cipher.cipher_name, header.cipher_name, 'Ciphers differ'
148
- assert_equal random_cipher.send(:key), header.key, 'Keys differ'
149
- assert_equal random_cipher.send(:iv), header.iv, 'IVs differ'
103
+ it 'encrypt simple string' do
104
+ assert encrypted = @cipher.encrypt(@social_security_number)
105
+ assert_equal @social_security_number_encrypted, encrypted
106
+ end
150
107
 
151
- string = 'Hello World'
152
- cipher = SymmetricEncryption::Cipher.new(key: header.key, iv: header.iv, cipher_name: header.cipher_name)
153
- # Test Encryption
154
- assert_equal random_cipher.encrypt(string, false, false), cipher.encrypt(string, false, false), 'Encrypted values differ'
155
- end
108
+ it 'decrypt string' do
109
+ assert decrypted = @cipher.decrypt(@social_security_number_encrypted)
110
+ assert_equal @social_security_number, decrypted
111
+ assert_equal Encoding.find('utf-8'), decrypted.encoding, decrypted
112
+ end
156
113
 
157
- it 'encrypt and then decrypt without a header' do
158
- assert encrypted = @cipher.binary_encrypt(@social_security_number, false, false, false)
159
- assert_equal @social_security_number, @cipher.decrypt(encrypted)
160
- end
114
+ it 'encrypt and decrypt string' do
115
+ assert encrypted = @cipher.encrypt(@social_security_number)
116
+ assert_equal @social_security_number_encrypted, encrypted
117
+ assert decrypted = @cipher.decrypt(encrypted)
118
+ assert_equal @social_security_number, decrypted
119
+ assert_equal Encoding.find('utf-8'), decrypted.encoding, decrypted
120
+ end
161
121
 
162
- it 'encrypt and then decrypt using random iv' do
163
- assert encrypted = @cipher.encrypt(@social_security_number, true)
164
- assert_equal @social_security_number, @cipher.decrypt(encrypted)
165
- end
122
+ it 'return BINARY encoding for non-UTF-8 encrypted data' do
123
+ assert_equal Encoding.find('binary'), @non_utf8.encoding
124
+ assert_equal true, @non_utf8.valid_encoding?
125
+ assert encrypted = @cipher.encrypt(@non_utf8)
126
+ assert decrypted = @cipher.decrypt(encrypted)
127
+ assert_equal true, decrypted.valid_encoding?
128
+ assert_equal Encoding.find('binary'), decrypted.encoding, decrypted
129
+ assert_equal @non_utf8, decrypted
130
+ end
166
131
 
167
- it 'encrypt and then decrypt using random iv with compression' do
168
- assert encrypted = @cipher.encrypt(@social_security_number, true, true)
169
- assert_equal @social_security_number, @cipher.decrypt(encrypted)
170
- end
132
+ it 'return nil when encrypting nil' do
133
+ assert_nil @cipher.encrypt(nil)
134
+ end
171
135
 
172
- end
136
+ it "return '' when encrypting ''" do
137
+ assert_equal '', @cipher.encrypt('')
138
+ end
173
139
 
174
- end
140
+ it 'return nil when decrypting nil' do
141
+ assert_nil @cipher.decrypt(nil)
142
+ end
175
143
 
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: '')
144
+ it "return '' when decrypting ''" do
145
+ assert_equal '', @cipher.decrypt('')
146
+ end
147
+ end
181
148
  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
149
  end
195
- end
196
150
 
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: ''
151
+ describe 'with configuration' do
152
+ before do
153
+ @cipher = SymmetricEncryption::Cipher.new(
154
+ key: '1234567890ABCDEF',
155
+ iv: '1234567890ABCDEF',
156
+ cipher_name: 'aes-128-cbc',
157
+ encoding: :none
226
158
  )
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
159
+ @social_security_number = '987654321'
236
160
 
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
161
+ @social_security_number_encrypted = "A\335*\314\336\250V\340\023%\000S\177\305\372\266"
162
+ @social_security_number_encrypted.force_encoding('binary')
241
163
 
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
164
+ @sample_data = [
165
+ {text: '555052345', encrypted: ''}
166
+ ]
167
+ end
256
168
 
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
- )
169
+ describe 'with header' do
170
+ before do
171
+ @social_security_number = '987654321'
172
+ end
173
+
174
+ it 'build and parse header' do
175
+ key = SymmetricEncryption::Key.new(cipher_name: 'aes-128-cbc')
176
+ assert binary_header = SymmetricEncryption::Cipher.build_header(SymmetricEncryption.cipher.version, true, key.iv, key.key, key.cipher_name)
177
+ header = SymmetricEncryption::Header.new
178
+ header.parse(binary_header)
179
+ assert_equal true, header.compressed?
180
+ assert random_cipher = SymmetricEncryption::Cipher.new(iv: key.iv, key: key.key, cipher_name: key.cipher_name)
181
+ assert_equal random_cipher.cipher_name, header.cipher_name, 'Ciphers differ'
182
+ assert_equal random_cipher.send(:key), header.key, 'Keys differ'
183
+ assert_equal random_cipher.send(:iv), header.iv, 'IVs differ'
184
+
185
+ string = 'Hello World'
186
+ cipher = SymmetricEncryption::Cipher.new(key: header.key, iv: header.iv, cipher_name: header.cipher_name)
187
+ # Test Encryption
188
+ assert_equal random_cipher.encrypt(string), cipher.encrypt(string), 'Encrypted values differ'
189
+ end
190
+
191
+ it 'encrypt and then decrypt without a header' do
192
+ assert encrypted = @cipher.binary_encrypt(@social_security_number, header: false)
193
+ assert_equal @social_security_number, @cipher.decrypt(encrypted)
194
+ end
195
+
196
+ it 'encrypt and then decrypt using random iv' do
197
+ assert encrypted = @cipher.encrypt(@social_security_number, random_iv: true)
198
+ assert_equal @social_security_number, @cipher.decrypt(encrypted)
199
+ end
200
+
201
+ it 'encrypt and then decrypt using random iv with compression' do
202
+ assert encrypted = @cipher.encrypt(@social_security_number, random_iv: true, compress: true)
203
+ assert_equal @social_security_number, @cipher.decrypt(encrypted)
204
+ end
263
205
  end
264
206
  end
207
+
265
208
  end
266
209
  end
267
210
  end