symmetric-encryption 3.9.1 → 4.0.0.beta3

Sign up to get free protection for your applications and to get access to all the features.
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