encrypted_strings 0.2.1 → 0.3.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.
- data/CHANGELOG.rdoc +4 -0
- data/README.rdoc +2 -2
- data/Rakefile +1 -1
- data/lib/encrypted_strings/asymmetric_cipher.rb +162 -164
- data/lib/encrypted_strings/cipher.rb +14 -16
- data/lib/encrypted_strings/extensions/string.rb +186 -188
- data/lib/encrypted_strings/sha_cipher.rb +57 -59
- data/lib/encrypted_strings/symmetric_cipher.rb +91 -93
- data/test/asymmetric_cipher_test.rb +24 -24
- data/test/cipher_test.rb +1 -1
- data/test/sha_cipher_test.rb +11 -11
- data/test/string_test.rb +4 -4
- data/test/symmetric_cipher_test.rb +13 -13
- metadata +2 -2
@@ -1,68 +1,66 @@
|
|
1
1
|
require 'digest/sha1'
|
2
2
|
|
3
|
-
module
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
3
|
+
module EncryptedStrings
|
4
|
+
# Encrypts a string using a Secure Hash Algorithm (SHA), specifically SHA-1.
|
5
|
+
#
|
6
|
+
# == Encrypting
|
7
|
+
#
|
8
|
+
# To encrypt a string using an SHA cipher, the salt used to seed the
|
9
|
+
# algorithm must be specified. You can define the default for this value
|
10
|
+
# like so:
|
11
|
+
#
|
12
|
+
# EncryptedStrings::ShaCipher.default_salt = 'secret'
|
13
|
+
#
|
14
|
+
# If these configuration options are not passed in to #encrypt, then the
|
15
|
+
# default values will be used. You can override the default values like so:
|
16
|
+
#
|
17
|
+
# password = 'shhhh'
|
18
|
+
# password.encrypt(:sha, :salt => 'secret') # => "ae645b35bb5dfea6c9133ac872e6adfa92a3c2bd"
|
19
|
+
#
|
20
|
+
# == Decrypting
|
21
|
+
#
|
22
|
+
# SHA-encrypted strings cannot be decrypted. The only way to determine
|
23
|
+
# whether an unencrypted value is equal to an SHA-encrypted string is to
|
24
|
+
# encrypt the value with the same salt. For example,
|
25
|
+
#
|
26
|
+
# password = 'shhhh'.encrypt(:sha, :salt => 'secret') # => "3b22cbe4acde873c3efc82681096f3ae69aff828"
|
27
|
+
# input = 'shhhh'.encrypt(:sha, :salt => 'secret') # => "3b22cbe4acde873c3efc82681096f3ae69aff828"
|
28
|
+
# password == input # => true
|
29
|
+
class ShaCipher < Cipher
|
30
|
+
class << self
|
31
|
+
# The default salt value to use during encryption
|
32
|
+
attr_accessor :default_salt
|
33
|
+
end
|
34
|
+
|
35
|
+
# Set defaults
|
36
|
+
@default_salt = 'salt'
|
37
|
+
|
38
|
+
# The salt value to use for encryption
|
39
|
+
attr_accessor :salt
|
40
|
+
|
41
|
+
# Creates a new cipher that uses an SHA encryption strategy.
|
26
42
|
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
# The default salt value to use during encryption
|
33
|
-
attr_accessor :default_salt
|
34
|
-
end
|
35
|
-
|
36
|
-
# Set defaults
|
37
|
-
@default_salt = 'salt'
|
43
|
+
# Configuration options:
|
44
|
+
# * +salt+ - Random bytes used as one of the inputs for generating the encrypted string
|
45
|
+
def initialize(options = {})
|
46
|
+
invalid_options = options.keys - [:salt]
|
47
|
+
raise ArgumentError, "Unknown key(s): #{invalid_options.join(", ")}" unless invalid_options.empty?
|
38
48
|
|
39
|
-
|
40
|
-
attr_accessor :salt
|
49
|
+
options = {:salt => ShaCipher.default_salt}.merge(options)
|
41
50
|
|
42
|
-
|
43
|
-
#
|
44
|
-
# Configuration options:
|
45
|
-
# * +salt+ - Random bytes used as one of the inputs for generating the encrypted string
|
46
|
-
def initialize(options = {})
|
47
|
-
invalid_options = options.keys - [:salt]
|
48
|
-
raise ArgumentError, "Unknown key(s): #{invalid_options.join(", ")}" unless invalid_options.empty?
|
49
|
-
|
50
|
-
options = {:salt => ShaCipher.default_salt}.merge(options)
|
51
|
-
|
52
|
-
self.salt = options[:salt].to_s
|
53
|
-
|
54
|
-
super()
|
55
|
-
end
|
51
|
+
self.salt = options[:salt].to_s
|
56
52
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
53
|
+
super()
|
54
|
+
end
|
55
|
+
|
56
|
+
# Decryption is not supported
|
57
|
+
def can_decrypt?
|
58
|
+
false
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns the encrypted value of the data
|
62
|
+
def encrypt(data)
|
63
|
+
Digest::SHA1.hexdigest(data + salt)
|
66
64
|
end
|
67
65
|
end
|
68
66
|
end
|
@@ -1,102 +1,100 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
module EncryptedStrings
|
2
|
+
# Indicates no password was specified for the symmetric cipher
|
3
|
+
class NoPasswordError < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
# Symmetric encryption uses a specific algorithm and password to encrypt
|
7
|
+
# the string. As long as the algorithm and password are known, the string
|
8
|
+
# can be decrypted.
|
9
|
+
#
|
10
|
+
# Source: http://support.microsoft.com/kb/246071
|
11
|
+
#
|
12
|
+
# == Encrypting
|
13
|
+
#
|
14
|
+
# To encrypt a string using a symmetric cipher, the algorithm and password
|
15
|
+
# must be specified. You can define the defaults for these values like so:
|
16
|
+
#
|
17
|
+
# EncryptedStrings::SymmetricCipher.default_algorithm = 'des-ecb'
|
18
|
+
# EncryptedStrings::SymmetricCipher.default_password = 'secret'
|
19
|
+
#
|
20
|
+
# If these configuration options are not passed in to #encrypt, then the
|
21
|
+
# default values will be used. You can override the default values like so:
|
22
|
+
#
|
23
|
+
# password = 'shhhh'
|
24
|
+
# password.encrypt(:symmetric, :algorithm => 'des-ecb', :password => 'secret') # => "S/sEkViX3v4=\n"
|
25
|
+
#
|
26
|
+
# An exception will be raised if no password is specified.
|
27
|
+
#
|
28
|
+
# == Decrypting
|
29
|
+
#
|
30
|
+
# To decrypt a string using an symmetric cipher, the algorithm and password
|
31
|
+
# must be specified. Defaults for these values can be defined as show above.
|
32
|
+
#
|
33
|
+
# If these configuration options are not passed in to #decrypt, then the
|
34
|
+
# default values will be used. You can override the default values like so:
|
35
|
+
#
|
36
|
+
# password = "S/sEkViX3v4=\n"
|
37
|
+
# password.decrypt(:symmetric, :algorithm => 'des-ecb', :password => 'secret') # => "shhhh"
|
38
|
+
#
|
39
|
+
# An exception will be raised if no password is specified.
|
40
|
+
class SymmetricCipher < Cipher
|
41
|
+
class << self
|
42
|
+
# The default algorithm to use for encryption. Default is DES-EDE3-CBC.
|
43
|
+
attr_accessor :default_algorithm
|
44
|
+
|
45
|
+
# The default password to use for generating the key and initialization
|
46
|
+
# vector. Default is nil.
|
47
|
+
attr_accessor :default_password
|
5
48
|
end
|
6
49
|
|
7
|
-
#
|
8
|
-
|
9
|
-
|
10
|
-
#
|
11
|
-
|
12
|
-
|
13
|
-
#
|
14
|
-
#
|
15
|
-
|
16
|
-
|
17
|
-
#
|
18
|
-
# PluginAWeek::EncryptedStrings::SymmetricCipher.default_algorithm = 'des-ecb'
|
19
|
-
# PluginAWeek::EncryptedStrings::SymmetricCipher.default_password = 'secret'
|
20
|
-
#
|
21
|
-
# If these configuration options are not passed in to #encrypt, then the
|
22
|
-
# default values will be used. You can override the default values like so:
|
23
|
-
#
|
24
|
-
# password = 'shhhh'
|
25
|
-
# password.encrypt(:symmetric, :algorithm => 'des-ecb', :password => 'secret') # => "S/sEkViX3v4=\n"
|
26
|
-
#
|
27
|
-
# An exception will be raised if no password is specified.
|
28
|
-
#
|
29
|
-
# == Decrypting
|
30
|
-
#
|
31
|
-
# To decrypt a string using an symmetric cipher, the algorithm and password
|
32
|
-
# must be specified. Defaults for these values can be defined as show above.
|
33
|
-
#
|
34
|
-
# If these configuration options are not passed in to #decrypt, then the
|
35
|
-
# default values will be used. You can override the default values like so:
|
36
|
-
#
|
37
|
-
# password = "S/sEkViX3v4=\n"
|
38
|
-
# password.decrypt(:symmetric, :algorithm => 'des-ecb', :password => 'secret') # => "shhhh"
|
50
|
+
# Set default values
|
51
|
+
@default_algorithm = 'DES-EDE3-CBC'
|
52
|
+
|
53
|
+
# The algorithm to use for encryption/decryption
|
54
|
+
attr_accessor :algorithm
|
55
|
+
|
56
|
+
# The password that generates the key/initialization vector for the
|
57
|
+
# algorithm
|
58
|
+
attr_accessor :password
|
59
|
+
|
60
|
+
# Creates a new cipher that uses a symmetric encryption strategy.
|
39
61
|
#
|
40
|
-
#
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
# The default password to use for generating the key and initialization
|
47
|
-
# vector. Default is nil.
|
48
|
-
attr_accessor :default_password
|
49
|
-
end
|
50
|
-
|
51
|
-
# Set default values
|
52
|
-
@default_algorithm = 'DES-EDE3-CBC'
|
53
|
-
|
54
|
-
# The algorithm to use for encryption/decryption
|
55
|
-
attr_accessor :algorithm
|
62
|
+
# Configuration options:
|
63
|
+
# * +algorithm+ - The algorithm to use for generating the encrypted string
|
64
|
+
# * +password+ - The secret value to use for generating the key/initialization vector for the algorithm
|
65
|
+
def initialize(options = {})
|
66
|
+
invalid_options = options.keys - [:algorithm, :password]
|
67
|
+
raise ArgumentError, "Unknown key(s): #{invalid_options.join(", ")}" unless invalid_options.empty?
|
56
68
|
|
57
|
-
|
58
|
-
|
59
|
-
|
69
|
+
options = {
|
70
|
+
:algorithm => SymmetricCipher.default_algorithm,
|
71
|
+
:password => SymmetricCipher.default_password
|
72
|
+
}.merge(options)
|
60
73
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
# * +algorithm+ - The algorithm to use for generating the encrypted string
|
65
|
-
# * +password+ - The secret value to use for generating the key/initialization vector for the algorithm
|
66
|
-
def initialize(options = {})
|
67
|
-
invalid_options = options.keys - [:algorithm, :password]
|
68
|
-
raise ArgumentError, "Unknown key(s): #{invalid_options.join(", ")}" unless invalid_options.empty?
|
69
|
-
|
70
|
-
options = {
|
71
|
-
:algorithm => SymmetricCipher.default_algorithm,
|
72
|
-
:password => SymmetricCipher.default_password
|
73
|
-
}.merge(options)
|
74
|
-
|
75
|
-
self.algorithm = options[:algorithm]
|
76
|
-
self.password = options[:password]
|
77
|
-
raise NoPasswordError if password.nil?
|
78
|
-
|
79
|
-
super()
|
80
|
-
end
|
81
|
-
|
82
|
-
# Decrypts the current string using the current key and algorithm specified
|
83
|
-
def decrypt(data)
|
84
|
-
cipher = build_cipher(:decrypt)
|
85
|
-
cipher.update(Base64.decode64(data)) + cipher.final
|
86
|
-
end
|
74
|
+
self.algorithm = options[:algorithm]
|
75
|
+
self.password = options[:password]
|
76
|
+
raise NoPasswordError if password.nil?
|
87
77
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
def build_cipher(type) #:nodoc:
|
96
|
-
cipher = OpenSSL::Cipher.new(algorithm).send(type)
|
97
|
-
cipher.pkcs5_keyivgen(password)
|
98
|
-
cipher
|
99
|
-
end
|
78
|
+
super()
|
79
|
+
end
|
80
|
+
|
81
|
+
# Decrypts the current string using the current key and algorithm specified
|
82
|
+
def decrypt(data)
|
83
|
+
cipher = build_cipher(:decrypt)
|
84
|
+
cipher.update(Base64.decode64(data)) + cipher.final
|
100
85
|
end
|
86
|
+
|
87
|
+
# Encrypts the current string using the current key and algorithm specified
|
88
|
+
def encrypt(data)
|
89
|
+
cipher = build_cipher(:encrypt)
|
90
|
+
Base64.encode64(cipher.update(data) + cipher.final)
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
def build_cipher(type) #:nodoc:
|
95
|
+
cipher = OpenSSL::Cipher.new(algorithm).send(type)
|
96
|
+
cipher.pkcs5_keyivgen(password)
|
97
|
+
cipher
|
98
|
+
end
|
101
99
|
end
|
102
100
|
end
|
@@ -2,27 +2,27 @@ require File.dirname(__FILE__) + '/test_helper'
|
|
2
2
|
|
3
3
|
class NoPrivateKeyErrorTest < Test::Unit::TestCase
|
4
4
|
def test_should_exist
|
5
|
-
assert_not_nil
|
5
|
+
assert_not_nil EncryptedStrings::NoPrivateKeyError
|
6
6
|
end
|
7
7
|
end
|
8
8
|
|
9
9
|
class NoPublicKeyErrorTest < Test::Unit::TestCase
|
10
10
|
def test_should_exist
|
11
|
-
assert_not_nil
|
11
|
+
assert_not_nil EncryptedStrings::NoPublicKeyError
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
15
|
class AsymmetricCipherByDefaultTest < Test::Unit::TestCase
|
16
16
|
def setup
|
17
|
-
@asymmetric_cipher =
|
17
|
+
@asymmetric_cipher = EncryptedStrings::AsymmetricCipher.new(:public_key_file => File.dirname(__FILE__) + '/keys/public')
|
18
18
|
end
|
19
19
|
|
20
20
|
def test_should_raise_an_exception
|
21
|
-
assert_raise(ArgumentError) {
|
21
|
+
assert_raise(ArgumentError) {EncryptedStrings::AsymmetricCipher.new}
|
22
22
|
end
|
23
23
|
|
24
24
|
def test_should_not_have_a_public_key_file
|
25
|
-
@asymmetric_cipher =
|
25
|
+
@asymmetric_cipher = EncryptedStrings::AsymmetricCipher.new(:private_key_file => File.dirname(__FILE__) + '/keys/private')
|
26
26
|
assert_nil @asymmetric_cipher.public_key_file
|
27
27
|
end
|
28
28
|
|
@@ -41,13 +41,13 @@ end
|
|
41
41
|
|
42
42
|
class AsymmetricCipherWithCustomDefaultsTest < Test::Unit::TestCase
|
43
43
|
def setup
|
44
|
-
@original_default_public_key_file =
|
45
|
-
@original_default_private_key_file =
|
44
|
+
@original_default_public_key_file = EncryptedStrings::AsymmetricCipher.default_public_key_file
|
45
|
+
@original_default_private_key_file = EncryptedStrings::AsymmetricCipher.default_private_key_file
|
46
46
|
|
47
|
-
|
48
|
-
|
47
|
+
EncryptedStrings::AsymmetricCipher.default_public_key_file = File.dirname(__FILE__) + '/keys/public'
|
48
|
+
EncryptedStrings::AsymmetricCipher.default_private_key_file = File.dirname(__FILE__) + '/keys/private'
|
49
49
|
|
50
|
-
@asymmetric_cipher =
|
50
|
+
@asymmetric_cipher = EncryptedStrings::AsymmetricCipher.new
|
51
51
|
end
|
52
52
|
|
53
53
|
def test_should_use_default_public_key_file
|
@@ -67,20 +67,20 @@ class AsymmetricCipherWithCustomDefaultsTest < Test::Unit::TestCase
|
|
67
67
|
end
|
68
68
|
|
69
69
|
def teardown
|
70
|
-
|
71
|
-
|
70
|
+
EncryptedStrings::AsymmetricCipher.default_public_key_file = @original_default_public_key_file
|
71
|
+
EncryptedStrings::AsymmetricCipher.default_private_key_file = @original_default_private_key_file
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
75
|
class AsymmetricCipherWithInvalidOptionsTest < Test::Unit::TestCase
|
76
76
|
def test_should_throw_an_exception
|
77
|
-
assert_raise(ArgumentError) {
|
77
|
+
assert_raise(ArgumentError) {EncryptedStrings::AsymmetricCipher.new(:invalid => true)}
|
78
78
|
end
|
79
79
|
end
|
80
80
|
|
81
81
|
class AsymmetricCipherTest < Test::Unit::TestCase
|
82
82
|
def setup
|
83
|
-
@asymmetric_cipher =
|
83
|
+
@asymmetric_cipher = EncryptedStrings::AsymmetricCipher.new(:public_key_file => File.dirname(__FILE__) + '/keys/public')
|
84
84
|
end
|
85
85
|
|
86
86
|
def test_should_be_able_to_decrypt
|
@@ -90,7 +90,7 @@ end
|
|
90
90
|
|
91
91
|
class AsymmetricCipherWithoutPublicKeyTest < Test::Unit::TestCase
|
92
92
|
def setup
|
93
|
-
@asymmetric_cipher =
|
93
|
+
@asymmetric_cipher = EncryptedStrings::AsymmetricCipher.new(:public_key_file => nil, :private_key_file => File.dirname(__FILE__) + '/keys/private')
|
94
94
|
end
|
95
95
|
|
96
96
|
def test_should_not_be_public
|
@@ -98,13 +98,13 @@ class AsymmetricCipherWithoutPublicKeyTest < Test::Unit::TestCase
|
|
98
98
|
end
|
99
99
|
|
100
100
|
def test_should_not_be_able_to_encrypt
|
101
|
-
assert_raise(
|
101
|
+
assert_raise(EncryptedStrings::NoPublicKeyError) {@asymmetric_cipher.encrypt('test')}
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
105
|
class AsymmetricCipherWithPublicKeyTest < Test::Unit::TestCase
|
106
106
|
def setup
|
107
|
-
@asymmetric_cipher =
|
107
|
+
@asymmetric_cipher = EncryptedStrings::AsymmetricCipher.new(:public_key_file => File.dirname(__FILE__) + '/keys/public')
|
108
108
|
end
|
109
109
|
|
110
110
|
def test_should_be_public
|
@@ -120,13 +120,13 @@ class AsymmetricCipherWithPublicKeyTest < Test::Unit::TestCase
|
|
120
120
|
end
|
121
121
|
|
122
122
|
def test_should_not_be_able_to_decrypt
|
123
|
-
assert_raise(
|
123
|
+
assert_raise(EncryptedStrings::NoPrivateKeyError) {@asymmetric_cipher.decrypt("HbEh0Hwri26S7SWYqO26DBbzfhR1h/0pXYLjSKUpxF5DOaOCtD9oRN748+Na\nrfNaVN5Eg7RUhbRFZE+UnNHo6Q==\n")}
|
124
124
|
end
|
125
125
|
end
|
126
126
|
|
127
127
|
class AsymmetricCipherWithoutPrivateKeyTest < Test::Unit::TestCase
|
128
128
|
def setup
|
129
|
-
@asymmetric_cipher =
|
129
|
+
@asymmetric_cipher = EncryptedStrings::AsymmetricCipher.new(:private_key_file => nil, :public_key_file => File.dirname(__FILE__) + '/keys/public')
|
130
130
|
end
|
131
131
|
|
132
132
|
def test_should_not_be_private
|
@@ -134,13 +134,13 @@ class AsymmetricCipherWithoutPrivateKeyTest < Test::Unit::TestCase
|
|
134
134
|
end
|
135
135
|
|
136
136
|
def test_should_not_be_able_to_decrypt
|
137
|
-
assert_raise(
|
137
|
+
assert_raise(EncryptedStrings::NoPrivateKeyError) {@asymmetric_cipher.decrypt("HbEh0Hwri26S7SWYqO26DBbzfhR1h/0pXYLjSKUpxF5DOaOCtD9oRN748+Na\nrfNaVN5Eg7RUhbRFZE+UnNHo6Q==\n")}
|
138
138
|
end
|
139
139
|
end
|
140
140
|
|
141
141
|
class AsymmetricCipherWithPrivateKeyTest < Test::Unit::TestCase
|
142
142
|
def setup
|
143
|
-
@asymmetric_cipher =
|
143
|
+
@asymmetric_cipher = EncryptedStrings::AsymmetricCipher.new(:private_key_file => File.dirname(__FILE__) + '/keys/private')
|
144
144
|
end
|
145
145
|
|
146
146
|
def test_should_not_be_public
|
@@ -152,7 +152,7 @@ class AsymmetricCipherWithPrivateKeyTest < Test::Unit::TestCase
|
|
152
152
|
end
|
153
153
|
|
154
154
|
def test_not_should_be_able_to_encrypt
|
155
|
-
assert_raise(
|
155
|
+
assert_raise(EncryptedStrings::NoPublicKeyError) {@asymmetric_cipher.encrypt('test')}
|
156
156
|
end
|
157
157
|
|
158
158
|
def test_should_be_able_to_decrypt
|
@@ -162,7 +162,7 @@ end
|
|
162
162
|
|
163
163
|
class AsymmetricCipherWithEncryptedPrivateKeyTest < Test::Unit::TestCase
|
164
164
|
def setup
|
165
|
-
@asymmetric_cipher =
|
165
|
+
@asymmetric_cipher = EncryptedStrings::AsymmetricCipher.new(:private_key_file => File.dirname(__FILE__) + '/keys/encrypted_private', :algorithm => 'DES-EDE3-CBC', :password => 'secret')
|
166
166
|
end
|
167
167
|
|
168
168
|
def test_should_not_be_public
|
@@ -174,7 +174,7 @@ class AsymmetricCipherWithEncryptedPrivateKeyTest < Test::Unit::TestCase
|
|
174
174
|
end
|
175
175
|
|
176
176
|
def test_should_not_be_able_to_encrypt
|
177
|
-
assert_raise(
|
177
|
+
assert_raise(EncryptedStrings::NoPublicKeyError) {@asymmetric_cipher.encrypt('test')}
|
178
178
|
end
|
179
179
|
|
180
180
|
def test_should_be_able_to_decrypt
|