symmetric-encryption 1.1.1 → 2.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.
- checksums.yaml +4 -4
- data/README.md +75 -23
- data/lib/rails/generators/symmetric_encryption/config/templates/symmetric-encryption.yml +7 -6
- data/lib/symmetric_encryption/cipher.rb +161 -126
- data/lib/symmetric_encryption/extensions/active_record/base.rb +36 -13
- data/lib/symmetric_encryption/extensions/mongoid/fields.rb +23 -12
- data/lib/symmetric_encryption/railtie.rb +4 -4
- data/lib/symmetric_encryption/reader.rb +7 -5
- data/lib/symmetric_encryption/symmetric_encryption.rb +54 -24
- data/lib/symmetric_encryption/version.rb +1 -1
- data/lib/symmetric_encryption/writer.rb +61 -15
- data/test/attr_encrypted_test.rb +30 -0
- data/test/cipher_test.rb +14 -13
- data/test/config/symmetric-encryption.yml +2 -2
- data/test/field_encrypted_test.rb +28 -0
- data/test/reader_test.rb +72 -38
- data/test/symmetric_encryption_test.rb +25 -5
- data/test/test_db.sqlite3 +0 -0
- data/test/writer_test.rb +3 -3
- metadata +2 -2
data/test/attr_encrypted_test.rb
CHANGED
@@ -19,6 +19,8 @@ ActiveRecord::Schema.define :version => 0 do
|
|
19
19
|
create_table :users, :force => true do |t|
|
20
20
|
t.string :encrypted_bank_account_number
|
21
21
|
t.string :encrypted_social_security_number
|
22
|
+
t.string :encrypted_string
|
23
|
+
t.text :encrypted_long_string
|
22
24
|
t.string :name
|
23
25
|
end
|
24
26
|
end
|
@@ -26,6 +28,8 @@ end
|
|
26
28
|
class User < ActiveRecord::Base
|
27
29
|
attr_encrypted :bank_account_number
|
28
30
|
attr_encrypted :social_security_number
|
31
|
+
attr_encrypted :string, :random_iv => true
|
32
|
+
attr_encrypted :long_string, :random_iv => true, :compress => true
|
29
33
|
|
30
34
|
validates :encrypted_bank_account_number, :symmetric_encryption => true
|
31
35
|
validates :encrypted_social_security_number, :symmetric_encryption => true
|
@@ -56,6 +60,9 @@ class AttrEncryptedTest < Test::Unit::TestCase
|
|
56
60
|
@social_security_number = "987654321"
|
57
61
|
@social_security_number_encrypted = "S+8X1NRrqdfEIQyFHVPuVA=="
|
58
62
|
|
63
|
+
@string = "A string containing some data to be encrypted with a random initialization vector"
|
64
|
+
@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"
|
65
|
+
|
59
66
|
@user = User.new(
|
60
67
|
# Encrypted Attribute
|
61
68
|
:bank_account_number => @bank_account_number,
|
@@ -82,6 +89,29 @@ class AttrEncryptedTest < Test::Unit::TestCase
|
|
82
89
|
assert_equal @social_security_number_encrypted, @user.encrypted_social_security_number
|
83
90
|
end
|
84
91
|
|
92
|
+
should "support same iv" do
|
93
|
+
@user.social_security_number = @social_security_number
|
94
|
+
assert first_value = @user.social_security_number
|
95
|
+
# Assign the same value
|
96
|
+
@user.social_security_number = @social_security_number
|
97
|
+
assert_equal first_value, @user.social_security_number
|
98
|
+
end
|
99
|
+
|
100
|
+
should "support a random iv" do
|
101
|
+
@user.string = @string
|
102
|
+
assert first_value = @user.encrypted_string
|
103
|
+
# Assign the same value
|
104
|
+
@user.string = @string.dup
|
105
|
+
assert_equal true, first_value != @user.encrypted_string
|
106
|
+
end
|
107
|
+
|
108
|
+
should "support a random iv and compress" do
|
109
|
+
@user.string = @long_string
|
110
|
+
@user.long_string = @long_string
|
111
|
+
|
112
|
+
assert_equal true, (@user.encrypted_long_string.length.to_f / @user.encrypted_string.length) < 0.8
|
113
|
+
end
|
114
|
+
|
85
115
|
should "encrypt" do
|
86
116
|
user = User.new
|
87
117
|
user.bank_account_number = @bank_account_number
|
data/test/cipher_test.rb
CHANGED
@@ -14,14 +14,14 @@ SymmetricEncryption.load!(File.join(File.dirname(__FILE__), 'config', 'symmetric
|
|
14
14
|
class CipherTest < Test::Unit::TestCase
|
15
15
|
context 'standalone' do
|
16
16
|
|
17
|
-
should "allow setting the
|
17
|
+
should "allow setting the cipher_name" do
|
18
18
|
cipher = SymmetricEncryption::Cipher.new(
|
19
|
-
:
|
19
|
+
:cipher_name => 'aes-128-cbc',
|
20
20
|
:key => '1234567890ABCDEF1234567890ABCDEF',
|
21
21
|
:iv => '1234567890ABCDEF',
|
22
22
|
:encoding => :none
|
23
23
|
)
|
24
|
-
assert_equal 'aes-128-cbc', cipher.
|
24
|
+
assert_equal 'aes-128-cbc', cipher.cipher_name
|
25
25
|
end
|
26
26
|
|
27
27
|
should "not require an iv" do
|
@@ -40,7 +40,7 @@ class CipherTest < Test::Unit::TestCase
|
|
40
40
|
|
41
41
|
should "throw an exception on bad data" do
|
42
42
|
cipher = SymmetricEncryption::Cipher.new(
|
43
|
-
:
|
43
|
+
:cipher_name => 'aes-128-cbc',
|
44
44
|
:key => '1234567890ABCDEF1234567890ABCDEF',
|
45
45
|
:iv => '1234567890ABCDEF',
|
46
46
|
:encoding => :none
|
@@ -70,7 +70,7 @@ class CipherTest < Test::Unit::TestCase
|
|
70
70
|
end
|
71
71
|
|
72
72
|
should "default to 'aes-256-cbc'" do
|
73
|
-
assert_equal 'aes-256-cbc', @cipher.
|
73
|
+
assert_equal 'aes-256-cbc', @cipher.cipher_name
|
74
74
|
end
|
75
75
|
|
76
76
|
should "encrypt simple string" do
|
@@ -96,17 +96,18 @@ class CipherTest < Test::Unit::TestCase
|
|
96
96
|
context "magic header" do
|
97
97
|
|
98
98
|
should "create and parse magic header" do
|
99
|
-
random_cipher = SymmetricEncryption::Cipher.
|
100
|
-
header =
|
101
|
-
|
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, decryption_cipher = SymmetricEncryption::Cipher.parse_magic_header!(header)
|
102
102
|
assert_equal true, compressed
|
103
|
-
assert_equal random_cipher.
|
104
|
-
assert_equal random_cipher.send(:key),
|
105
|
-
assert_equal random_cipher.send(:iv),
|
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"
|
106
106
|
|
107
|
-
string = "
|
107
|
+
string = "Hello World"
|
108
|
+
cipher = SymmetricEncryption::Cipher.new(:key => key, :iv => iv, :cipher_name => cipher_name)
|
108
109
|
# Test Encryption
|
109
|
-
assert_equal random_cipher.encrypt(string, false), cipher.encrypt(string, false), "Encrypted values differ"
|
110
|
+
assert_equal random_cipher.encrypt(string, false, false), cipher.encrypt(string, false, false), "Encrypted values differ"
|
110
111
|
end
|
111
112
|
end
|
112
113
|
|
@@ -38,7 +38,7 @@ test:
|
|
38
38
|
# Current / Newest Symmetric Encryption Key
|
39
39
|
- key_filename: /Users/rmorrison/Sandbox/symmetric-encryption/test/config/test_new.key
|
40
40
|
iv_filename: /Users/rmorrison/Sandbox/symmetric-encryption/test/config/test_new.iv
|
41
|
-
|
41
|
+
cipher_name: aes-128-cbc
|
42
42
|
# Base64 encode encrypted data without newlines
|
43
43
|
encoding: base64strict
|
44
44
|
version: 1
|
@@ -46,7 +46,7 @@ test:
|
|
46
46
|
# Previous Symmetric Encryption Key
|
47
47
|
- key_filename: /Users/rmorrison/Sandbox/symmetric-encryption/test/config/test_secondary_1.key
|
48
48
|
iv_filename: /Users/rmorrison/Sandbox/symmetric-encryption/test/config/test_secondary_1.iv
|
49
|
-
|
49
|
+
cipher_name: aes-128-cbc
|
50
50
|
# Base64 encode encrypted data without newlines
|
51
51
|
encoding: base64
|
52
52
|
version: 0
|
@@ -22,6 +22,8 @@ class MongoidUser
|
|
22
22
|
field :name, :type => String
|
23
23
|
field :encrypted_bank_account_number, :type => String, :encrypted => true
|
24
24
|
field :encrypted_social_security_number, :type => String, :encrypted => true
|
25
|
+
field :encrypted_string, :type => String, :encrypted => true, :random_iv => true
|
26
|
+
field :encrypted_long_string, :type => String, :encrypted => true, :random_iv => true, :compress => true
|
25
27
|
# field :encrypted_integer, :type => Integer, :encrypted => true
|
26
28
|
# field :encrypted_float, :type => Float, :encrypted => true
|
27
29
|
# field :encrypted_date, :type => Date, :encrypted => true
|
@@ -55,6 +57,9 @@ class FieldEncryptedTest < Test::Unit::TestCase
|
|
55
57
|
@date = Date.parse('20120320')
|
56
58
|
@date_encrypted = "WTkSPHo5ApSSHBJMxxWt2A=="
|
57
59
|
|
60
|
+
@string = "A string containing some data to be encrypted with a random initialization vector"
|
61
|
+
@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"
|
62
|
+
|
58
63
|
# #TODO Intercept passing in attributes to create etc.
|
59
64
|
@user = MongoidUser.new(
|
60
65
|
:encrypted_bank_account_number => @bank_account_number_encrypted,
|
@@ -84,6 +89,29 @@ class FieldEncryptedTest < Test::Unit::TestCase
|
|
84
89
|
assert_equal @social_security_number_encrypted, @user.encrypted_social_security_number
|
85
90
|
end
|
86
91
|
|
92
|
+
should "support same iv" do
|
93
|
+
@user.social_security_number = @social_security_number
|
94
|
+
assert first_value = @user.social_security_number
|
95
|
+
# Assign the same value
|
96
|
+
@user.social_security_number = @social_security_number
|
97
|
+
assert_equal first_value, @user.social_security_number
|
98
|
+
end
|
99
|
+
|
100
|
+
should "support a random iv" do
|
101
|
+
@user.string = @string
|
102
|
+
assert first_value = @user.encrypted_string
|
103
|
+
# Assign the same value
|
104
|
+
@user.string = @string.dup
|
105
|
+
assert_equal true, first_value != @user.encrypted_string
|
106
|
+
end
|
107
|
+
|
108
|
+
should "support a random iv and compress" do
|
109
|
+
@user.string = @long_string
|
110
|
+
@user.long_string = @long_string
|
111
|
+
|
112
|
+
assert_equal true, (@user.encrypted_long_string.length.to_f / @user.encrypted_string.length) < 0.8
|
113
|
+
end
|
114
|
+
|
87
115
|
should "encrypt" do
|
88
116
|
user = MongoidUser.new
|
89
117
|
user.bank_account_number = @bank_account_number
|
data/test/reader_test.rb
CHANGED
@@ -22,56 +22,90 @@ class ReaderTest < Test::Unit::TestCase
|
|
22
22
|
]
|
23
23
|
@data_str = @data.inject('') {|sum,str| sum << str}
|
24
24
|
@data_len = @data_str.length
|
25
|
-
@
|
25
|
+
@data_encrypted_without_header = SymmetricEncryption.cipher.binary_encrypt(@data_str)
|
26
|
+
|
27
|
+
@data_encrypted_with_header = SymmetricEncryption::Cipher.magic_header(
|
28
|
+
SymmetricEncryption.cipher.version,
|
29
|
+
compress = false,
|
30
|
+
SymmetricEncryption.cipher.send(:iv),
|
31
|
+
SymmetricEncryption.cipher.send(:key),
|
32
|
+
SymmetricEncryption.cipher.cipher_name)
|
33
|
+
@data_encrypted_with_header << SymmetricEncryption.cipher.binary_encrypt(@data_str)
|
34
|
+
|
35
|
+
# Verify regular decrypt can decrypt this string
|
36
|
+
SymmetricEncryption.cipher.binary_decrypt(@data_encrypted_without_header)
|
37
|
+
SymmetricEncryption.cipher.binary_decrypt(@data_encrypted_with_header)
|
26
38
|
end
|
27
39
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
40
|
+
[true, false].each do |header|
|
41
|
+
context header do
|
42
|
+
setup do
|
43
|
+
@data_encrypted = header ? @data_encrypted_with_header : @data_encrypted_without_header
|
44
|
+
end
|
33
45
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
40
|
-
assert_equal @data_str[10..-1], decrypted
|
41
|
-
end
|
46
|
+
should "decrypt from string stream as a single read" do
|
47
|
+
stream = StringIO.new(@data_encrypted)
|
48
|
+
decrypted = SymmetricEncryption::Reader.open(stream) {|file| file.read}
|
49
|
+
assert_equal @data_str, decrypted
|
50
|
+
end
|
42
51
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
52
|
+
should "decrypt from string stream as a single read, after a partial read" do
|
53
|
+
stream = StringIO.new(@data_encrypted)
|
54
|
+
decrypted = SymmetricEncryption::Reader.open(stream) do |file|
|
55
|
+
file.read(10)
|
56
|
+
file.read
|
57
|
+
end
|
58
|
+
assert_equal @data_str[10..-1], decrypted
|
50
59
|
end
|
51
|
-
end
|
52
|
-
end
|
53
60
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
61
|
+
should "decrypt lines from string stream" do
|
62
|
+
stream = StringIO.new(@data_encrypted)
|
63
|
+
i = 0
|
64
|
+
decrypted = SymmetricEncryption::Reader.open(stream) do |file|
|
65
|
+
file.each_line do |line|
|
66
|
+
assert_equal @data[i], line
|
67
|
+
i += 1
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
should "decrypt fixed lengths from string stream" do
|
73
|
+
stream = StringIO.new(@data_encrypted)
|
74
|
+
i = 0
|
75
|
+
SymmetricEncryption::Reader.open(stream) do |file|
|
76
|
+
index = 0
|
77
|
+
[0,10,5,5000].each do |size|
|
78
|
+
buf = file.read(size)
|
79
|
+
if size == 0
|
80
|
+
assert_equal '', buf
|
81
|
+
else
|
82
|
+
assert_equal @data_str[index..index+size-1], buf
|
83
|
+
end
|
84
|
+
index += size
|
85
|
+
end
|
65
86
|
end
|
66
|
-
index += size
|
67
87
|
end
|
68
88
|
end
|
69
89
|
end
|
70
90
|
|
71
91
|
context "reading from file" do
|
72
|
-
|
73
|
-
|
74
|
-
|
92
|
+
[
|
93
|
+
# No Header
|
94
|
+
{:header => false, :random_key => false, :random_iv => false},
|
95
|
+
# Default Header with random key and iv
|
96
|
+
{},
|
97
|
+
# Header with no compression ( default anyway )
|
98
|
+
{:compress => false},
|
99
|
+
# Compress and use Random key, iv
|
100
|
+
{:compress => true},
|
101
|
+
# Header but not random key or iv
|
102
|
+
{:random_key => false},
|
103
|
+
# Random iv only
|
104
|
+
{:random_key => false, :random_iv => true},
|
105
|
+
# Random iv only with compression
|
106
|
+
{:random_iv => true, :compress => true},
|
107
|
+
].each do |options|
|
108
|
+
context "with options: #{options.inspect}" do
|
75
109
|
setup do
|
76
110
|
@filename = '._test'
|
77
111
|
# Create encrypted file
|
@@ -23,25 +23,25 @@ class SymmetricEncryptionTest < Test::Unit::TestCase
|
|
23
23
|
|
24
24
|
should "match config file for first cipher" do
|
25
25
|
cipher = SymmetricEncryption.cipher
|
26
|
-
assert_equal @cipher_v1[:
|
26
|
+
assert_equal @cipher_v1[:cipher_name], cipher.cipher_name
|
27
27
|
assert_equal @cipher_v1[:version], cipher.version
|
28
28
|
assert_equal false, SymmetricEncryption.secondary_ciphers.include?(cipher)
|
29
29
|
end
|
30
30
|
|
31
31
|
should "match config file for v1 cipher" do
|
32
32
|
cipher = SymmetricEncryption.cipher(1)
|
33
|
-
assert @cipher_v1[:
|
33
|
+
assert @cipher_v1[:cipher_name]
|
34
34
|
assert @cipher_v1[:version]
|
35
|
-
assert_equal @cipher_v1[:
|
35
|
+
assert_equal @cipher_v1[:cipher_name], cipher.cipher_name
|
36
36
|
assert_equal @cipher_v1[:version], cipher.version
|
37
37
|
assert_equal false, SymmetricEncryption.secondary_ciphers.include?(cipher)
|
38
38
|
end
|
39
39
|
|
40
40
|
should "match config file for v0 cipher" do
|
41
41
|
cipher = SymmetricEncryption.cipher(0)
|
42
|
-
assert @cipher_v0[:
|
42
|
+
assert @cipher_v0[:cipher_name]
|
43
43
|
assert @cipher_v0[:version]
|
44
|
-
assert_equal @cipher_v0[:
|
44
|
+
assert_equal @cipher_v0[:cipher_name], cipher.cipher_name
|
45
45
|
assert_equal @cipher_v0[:version], cipher.version
|
46
46
|
assert_equal true, SymmetricEncryption.secondary_ciphers.include?(cipher)
|
47
47
|
end
|
@@ -95,6 +95,26 @@ class SymmetricEncryptionTest < Test::Unit::TestCase
|
|
95
95
|
end
|
96
96
|
end
|
97
97
|
|
98
|
+
context "random iv" do
|
99
|
+
setup do
|
100
|
+
@social_security_number = "987654321"
|
101
|
+
end
|
102
|
+
|
103
|
+
should "encrypt and then decrypt using random iv" do
|
104
|
+
# Encrypt with random iv
|
105
|
+
assert encrypted = SymmetricEncryption.encrypt(@social_security_number, true)
|
106
|
+
assert_equal true, SymmetricEncryption.encrypted?(encrypted)
|
107
|
+
assert_equal @social_security_number, SymmetricEncryption.decrypt(encrypted)
|
108
|
+
end
|
109
|
+
|
110
|
+
should "encrypt and then decrypt using random iv" do
|
111
|
+
# Encrypt with random iv and compress
|
112
|
+
assert encrypted = SymmetricEncryption.encrypt(@social_security_number, true, true)
|
113
|
+
assert_equal true, SymmetricEncryption.encrypted?(encrypted)
|
114
|
+
assert_equal @social_security_number, SymmetricEncryption.decrypt(encrypted)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
98
118
|
end
|
99
119
|
|
100
120
|
end
|
data/test/test_db.sqlite3
CHANGED
Binary file
|
data/test/writer_test.rb
CHANGED
@@ -22,7 +22,7 @@ class WriterTest < Test::Unit::TestCase
|
|
22
22
|
]
|
23
23
|
@data_str = @data.inject('') {|sum,str| sum << str}
|
24
24
|
@data_len = @data_str.length
|
25
|
-
@data_encrypted = SymmetricEncryption.cipher.
|
25
|
+
@data_encrypted = SymmetricEncryption.cipher.binary_encrypt(@data_str, false, false)
|
26
26
|
@filename = '._test'
|
27
27
|
end
|
28
28
|
|
@@ -32,7 +32,7 @@ class WriterTest < Test::Unit::TestCase
|
|
32
32
|
|
33
33
|
should "encrypt to string stream" do
|
34
34
|
stream = StringIO.new
|
35
|
-
file = SymmetricEncryption::Writer.new(stream, :header => false, :random_key => false)
|
35
|
+
file = SymmetricEncryption::Writer.new(stream, :header => false, :random_key => false, :random_iv => false)
|
36
36
|
written_len = @data.inject(0) {|sum,str| sum + file.write(str)}
|
37
37
|
file.close
|
38
38
|
|
@@ -53,7 +53,7 @@ class WriterTest < Test::Unit::TestCase
|
|
53
53
|
|
54
54
|
should "encrypt to file using .open" do
|
55
55
|
written_len = nil
|
56
|
-
SymmetricEncryption::Writer.open(@filename, :header => false, :random_key => false) do |file|
|
56
|
+
SymmetricEncryption::Writer.open(@filename, :header => false, :random_key => false, :random_iv => false) do |file|
|
57
57
|
written_len = @data.inject(0) {|sum,str| sum + file.write(str)}
|
58
58
|
end
|
59
59
|
assert_equal @data_len, written_len
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: symmetric-encryption
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Reid Morrison
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-04-
|
11
|
+
date: 2013-04-16 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: SymmetricEncryption supports encrypting ActiveRecord data, Mongoid data,
|
14
14
|
passwords in configuration files, encrypting and decrypting of large files through
|