encrypted_strings 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,17 +6,17 @@ module PluginAWeek #:nodoc:
6
6
  #
7
7
  # == Encrypting
8
8
  #
9
- # To encrypt a string using an SHA algorithm, the salt used to seed the
10
- # encrypting must be specified. You can define the default for this
11
- # value like so:
9
+ # To encrypt a string using an SHA cipher, the salt used to seed the
10
+ # algorithm must be specified. You can define the default for this value
11
+ # like so:
12
12
  #
13
- # PluginAWeek::EncryptedStrings::ShaEncryptor.default_salt = "secret"
13
+ # PluginAWeek::EncryptedStrings::ShaCipher.default_salt = 'secret'
14
14
  #
15
15
  # If these configuration options are not passed in to #encrypt, then the
16
16
  # default values will be used. You can override the default values like so:
17
17
  #
18
- # password = "shhhh"
19
- # password.encrypt(:sha, :salt => "my_salt") # => "ae645b35bb5dfea6c9133ac872e6adfa92a3c2bd"
18
+ # password = 'shhhh'
19
+ # password.encrypt(:sha, :salt => 'secret') # => "ae645b35bb5dfea6c9133ac872e6adfa92a3c2bd"
20
20
  #
21
21
  # == Decrypting
22
22
  #
@@ -24,10 +24,10 @@ module PluginAWeek #:nodoc:
24
24
  # whether an unencrypted value is equal to an SHA-encrypted string is to
25
25
  # encrypt the value with the same salt. For example,
26
26
  #
27
- # password = "shhhh".encrypt(:sha, :salt => "secret") # => "3b22cbe4acde873c3efc82681096f3ae69aff828"
28
- # input = "shhhh".encrypt(:sha, :salt => "secret") # => "3b22cbe4acde873c3efc82681096f3ae69aff828"
27
+ # password = 'shhhh'.encrypt(:sha, :salt => 'secret') # => "3b22cbe4acde873c3efc82681096f3ae69aff828"
28
+ # input = 'shhhh'.encrypt(:sha, :salt => 'secret') # => "3b22cbe4acde873c3efc82681096f3ae69aff828"
29
29
  # password == input # => true
30
- class ShaEncryptor < Encryptor
30
+ class ShaCipher < Cipher
31
31
  class << self
32
32
  # The default salt value to use during encryption
33
33
  attr_accessor :default_salt
@@ -39,13 +39,15 @@ module PluginAWeek #:nodoc:
39
39
  # The salt value to use for encryption
40
40
  attr_accessor :salt
41
41
 
42
+ # Creates a new cipher that uses an SHA encryption strategy.
43
+ #
42
44
  # Configuration options:
43
- # * +salt+ - Salt value to use for encryption
45
+ # * +salt+ - Random bytes used as one of the inputs for generating the encrypted string
44
46
  def initialize(options = {})
45
47
  invalid_options = options.keys - [:salt]
46
48
  raise ArgumentError, "Unknown key(s): #{invalid_options.join(", ")}" unless invalid_options.empty?
47
49
 
48
- options = {:salt => ShaEncryptor.default_salt}.merge(options)
50
+ options = {:salt => self.class.default_salt}.merge(options)
49
51
 
50
52
  self.salt = options[:salt].to_s
51
53
 
@@ -0,0 +1,102 @@
1
+ module PluginAWeek #:nodoc:
2
+ module EncryptedStrings
3
+ # Indicates no password was specified for the symmetric cipher
4
+ class NoPasswordError < StandardError
5
+ end
6
+
7
+ # Symmetric encryption uses a specific algorithm and password to encrypt
8
+ # the string. As long as the algorithm and password are known, the string
9
+ # can be decrypted.
10
+ #
11
+ # Source: http://support.microsoft.com/kb/246071
12
+ #
13
+ # == Encrypting
14
+ #
15
+ # To encrypt a string using a symmetric cipher, the algorithm and password
16
+ # must be specified. You can define the defaults for these values like so:
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"
39
+ #
40
+ # An exception will be raised if no password is specified.
41
+ class SymmetricCipher < Cipher
42
+ class << self
43
+ # The default algorithm to use for encryption. Default is DES-EDE3-CBC.
44
+ attr_accessor :default_algorithm
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
56
+
57
+ # The password that generates the key/initialization vector for the
58
+ # algorithm
59
+ attr_accessor :password
60
+
61
+ # Creates a new cipher that uses a symmetric encryption strategy.
62
+ #
63
+ # Configuration options:
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 => self.class.default_algorithm,
72
+ :password => self.class.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
87
+
88
+ # Encrypts the current string using the current key and algorithm specified
89
+ def encrypt(data)
90
+ cipher = build_cipher(:encrypt)
91
+ Base64.encode64(cipher.update(data) + cipher.final)
92
+ end
93
+
94
+ private
95
+ def build_cipher(type) #:nodoc:
96
+ cipher = OpenSSL::Cipher.new(algorithm).send(type)
97
+ cipher.pkcs5_keyivgen(password)
98
+ cipher
99
+ end
100
+ end
101
+ end
102
+ end
@@ -1,5 +1,5 @@
1
1
  require 'encrypted_strings/extensions/string'
2
- require 'encrypted_strings/encryptor'
3
- require 'encrypted_strings/symmetric_encryptor'
4
- require 'encrypted_strings/asymmetric_encryptor'
5
- require 'encrypted_strings/sha_encryptor'
2
+ require 'encrypted_strings/cipher'
3
+ require 'encrypted_strings/symmetric_cipher'
4
+ require 'encrypted_strings/asymmetric_cipher'
5
+ require 'encrypted_strings/sha_cipher'
@@ -0,0 +1,183 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class NoPrivateKeyErrorTest < Test::Unit::TestCase
4
+ def test_should_exist
5
+ assert_not_nil PluginAWeek::EncryptedStrings::NoPrivateKeyError
6
+ end
7
+ end
8
+
9
+ class NoPublicKeyErrorTest < Test::Unit::TestCase
10
+ def test_should_exist
11
+ assert_not_nil PluginAWeek::EncryptedStrings::NoPublicKeyError
12
+ end
13
+ end
14
+
15
+ class AsymmetricCipherByDefaultTest < Test::Unit::TestCase
16
+ def setup
17
+ @asymmetric_cipher = PluginAWeek::EncryptedStrings::AsymmetricCipher.new(:public_key_file => File.dirname(__FILE__) + '/keys/public')
18
+ end
19
+
20
+ def test_should_raise_an_exception
21
+ assert_raise(ArgumentError) {PluginAWeek::EncryptedStrings::AsymmetricCipher.new}
22
+ end
23
+
24
+ def test_should_not_have_a_public_key_file
25
+ @asymmetric_cipher = PluginAWeek::EncryptedStrings::AsymmetricCipher.new(:private_key_file => File.dirname(__FILE__) + '/keys/private')
26
+ assert_nil @asymmetric_cipher.public_key_file
27
+ end
28
+
29
+ def test_should_not_have_a_private_key_file
30
+ assert_nil @asymmetric_cipher.private_key_file
31
+ end
32
+
33
+ def test_should_not_have_an_algorithm
34
+ assert_nil @asymmetric_cipher.algorithm
35
+ end
36
+
37
+ def test_should_not_have_a_password
38
+ assert_nil @asymmetric_cipher.password
39
+ end
40
+ end
41
+
42
+ class AsymmetricCipherWithCustomDefaultsTest < Test::Unit::TestCase
43
+ def setup
44
+ @original_default_public_key_file = PluginAWeek::EncryptedStrings::AsymmetricCipher.default_public_key_file
45
+ @original_default_private_key_file = PluginAWeek::EncryptedStrings::AsymmetricCipher.default_private_key_file
46
+
47
+ PluginAWeek::EncryptedStrings::AsymmetricCipher.default_public_key_file = File.dirname(__FILE__) + '/keys/public'
48
+ PluginAWeek::EncryptedStrings::AsymmetricCipher.default_private_key_file = File.dirname(__FILE__) + '/keys/private'
49
+
50
+ @asymmetric_cipher = PluginAWeek::EncryptedStrings::AsymmetricCipher.new
51
+ end
52
+
53
+ def test_should_use_default_public_key_file
54
+ assert_equal File.dirname(__FILE__) + '/keys/public', @asymmetric_cipher.public_key_file
55
+ end
56
+
57
+ def test_should_use_default_private_key_file
58
+ assert_equal File.dirname(__FILE__) + '/keys/private', @asymmetric_cipher.private_key_file
59
+ end
60
+
61
+ def test_should_not_have_an_algorithm
62
+ assert_nil @asymmetric_cipher.algorithm
63
+ end
64
+
65
+ def test_should_not_have_a_password
66
+ assert_nil @asymmetric_cipher.password
67
+ end
68
+
69
+ def teardown
70
+ PluginAWeek::EncryptedStrings::AsymmetricCipher.default_public_key_file = @original_default_public_key_file
71
+ PluginAWeek::EncryptedStrings::AsymmetricCipher.default_private_key_file = @original_default_private_key_file
72
+ end
73
+ end
74
+
75
+ class AsymmetricCipherWithInvalidOptionsTest < Test::Unit::TestCase
76
+ def test_should_throw_an_exception
77
+ assert_raise(ArgumentError) {PluginAWeek::EncryptedStrings::AsymmetricCipher.new(:invalid => true)}
78
+ end
79
+ end
80
+
81
+ class AsymmetricCipherTest < Test::Unit::TestCase
82
+ def setup
83
+ @asymmetric_cipher = PluginAWeek::EncryptedStrings::AsymmetricCipher.new(:public_key_file => File.dirname(__FILE__) + '/keys/public')
84
+ end
85
+
86
+ def test_should_be_able_to_decrypt
87
+ assert @asymmetric_cipher.can_decrypt?
88
+ end
89
+ end
90
+
91
+ class AsymmetricCipherWithoutPublicKeyTest < Test::Unit::TestCase
92
+ def setup
93
+ @asymmetric_cipher = PluginAWeek::EncryptedStrings::AsymmetricCipher.new(:public_key_file => nil, :private_key_file => File.dirname(__FILE__) + '/keys/private')
94
+ end
95
+
96
+ def test_should_not_be_public
97
+ assert !@asymmetric_cipher.public?
98
+ end
99
+
100
+ def test_should_not_be_able_to_encrypt
101
+ assert_raise(PluginAWeek::EncryptedStrings::NoPublicKeyError) {@asymmetric_cipher.encrypt('test')}
102
+ end
103
+ end
104
+
105
+ class AsymmetricCipherWithPublicKeyTest < Test::Unit::TestCase
106
+ def setup
107
+ @asymmetric_cipher = PluginAWeek::EncryptedStrings::AsymmetricCipher.new(:public_key_file => File.dirname(__FILE__) + '/keys/public')
108
+ end
109
+
110
+ def test_should_be_public
111
+ assert @asymmetric_cipher.public?
112
+ end
113
+
114
+ def test_should_not_be_private
115
+ assert !@asymmetric_cipher.private?
116
+ end
117
+
118
+ def test_should_be_able_to_encrypt
119
+ assert_equal 90, @asymmetric_cipher.encrypt('test').length
120
+ end
121
+
122
+ def test_should_not_be_able_to_decrypt
123
+ assert_raise(PluginAWeek::EncryptedStrings::NoPrivateKeyError) {@asymmetric_cipher.decrypt("HbEh0Hwri26S7SWYqO26DBbzfhR1h/0pXYLjSKUpxF5DOaOCtD9oRN748+Na\nrfNaVN5Eg7RUhbRFZE+UnNHo6Q==\n")}
124
+ end
125
+ end
126
+
127
+ class AsymmetricCipherWithoutPrivateKeyTest < Test::Unit::TestCase
128
+ def setup
129
+ @asymmetric_cipher = PluginAWeek::EncryptedStrings::AsymmetricCipher.new(:private_key_file => nil, :public_key_file => File.dirname(__FILE__) + '/keys/public')
130
+ end
131
+
132
+ def test_should_not_be_private
133
+ assert !@asymmetric_cipher.private?
134
+ end
135
+
136
+ def test_should_not_be_able_to_decrypt
137
+ assert_raise(PluginAWeek::EncryptedStrings::NoPrivateKeyError) {@asymmetric_cipher.decrypt("HbEh0Hwri26S7SWYqO26DBbzfhR1h/0pXYLjSKUpxF5DOaOCtD9oRN748+Na\nrfNaVN5Eg7RUhbRFZE+UnNHo6Q==\n")}
138
+ end
139
+ end
140
+
141
+ class AsymmetricCipherWithPrivateKeyTest < Test::Unit::TestCase
142
+ def setup
143
+ @asymmetric_cipher = PluginAWeek::EncryptedStrings::AsymmetricCipher.new(:private_key_file => File.dirname(__FILE__) + '/keys/private')
144
+ end
145
+
146
+ def test_should_not_be_public
147
+ assert !@asymmetric_cipher.public?
148
+ end
149
+
150
+ def test_should_be_private
151
+ assert @asymmetric_cipher.private?
152
+ end
153
+
154
+ def test_not_should_be_able_to_encrypt
155
+ assert_raise(PluginAWeek::EncryptedStrings::NoPublicKeyError) {@asymmetric_cipher.encrypt('test')}
156
+ end
157
+
158
+ def test_should_be_able_to_decrypt
159
+ assert_equal 'test', @asymmetric_cipher.decrypt("HbEh0Hwri26S7SWYqO26DBbzfhR1h/0pXYLjSKUpxF5DOaOCtD9oRN748+Na\nrfNaVN5Eg7RUhbRFZE+UnNHo6Q==\n")
160
+ end
161
+ end
162
+
163
+ class AsymmetricCipherWithEncryptedPrivateKeyTest < Test::Unit::TestCase
164
+ def setup
165
+ @asymmetric_cipher = PluginAWeek::EncryptedStrings::AsymmetricCipher.new(:private_key_file => File.dirname(__FILE__) + '/keys/encrypted_private', :algorithm => 'DES-EDE3-CBC', :password => 'secret')
166
+ end
167
+
168
+ def test_should_not_be_public
169
+ assert !@asymmetric_cipher.public?
170
+ end
171
+
172
+ def test_should_be_private
173
+ assert @asymmetric_cipher.private?
174
+ end
175
+
176
+ def test_should_not_be_able_to_encrypt
177
+ assert_raise(PluginAWeek::EncryptedStrings::NoPublicKeyError) {@asymmetric_cipher.encrypt('test')}
178
+ end
179
+
180
+ def test_should_be_able_to_decrypt
181
+ assert_equal 'test', @asymmetric_cipher.decrypt("HbEh0Hwri26S7SWYqO26DBbzfhR1h/0pXYLjSKUpxF5DOaOCtD9oRN748+Na\nrfNaVN5Eg7RUhbRFZE+UnNHo6Q==\n")
182
+ end
183
+ end
@@ -0,0 +1,15 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class CipherByDefaultTest < Test::Unit::TestCase
4
+ def setup
5
+ @cipher = PluginAWeek::EncryptedStrings::Cipher.new
6
+ end
7
+
8
+ def test_should_be_able_to_decrypt_by_default
9
+ assert @cipher.can_decrypt?
10
+ end
11
+
12
+ def test_should_raise_exception_if_decrypt_not_implemented
13
+ assert_raises(NotImplementedError) {@cipher.decrypt('test')}
14
+ end
15
+ end
@@ -1,12 +1,12 @@
1
- OBNa1q8kbx8pyZZjIpr/pZV0oulE2czh5JlPW/13XsDKYjIwiLkCTZI5Vhv5
2
- cCF+wQuNvrbnp/QvJGzc5aJK1GwxqEb9hAwCzxw69DVjIhUowXDk/rYWUaTq
3
- YwpieO1v6gQrOVxerOubR7LT6A4I6k1CTECX6zeJeGgT2jOfXzIii0T1gmYD
4
- tJxLlNclpGB+eS+tuwrfyNmE4CkYa5vjuWdxHAeMRXPlm+o3ErE0QZ36Sn51
5
- TScPDsZHo7Ppit87sPXpubM/5JltN0QwV4Hj9dJoSm0QNWDb3BoyT1uuJ0wm
6
- b/adzINN1oUjdgMB9g3kmkErS3MRJtuPRNuU7/GvAuv78pQ5tAdkvk8gu7Cd
7
- 5cFgnzvE7l96oZB6TXgsnpjVLide61Di+mX8K2dvUdoCNgrQ1iPklTbEfJeH
8
- U/GoUV1ZyVnEfHkeWBEmC8YVBeri+lFL923g7z29luhxiBRkhows8TQsQ4hX
9
- SMlFCKCKWKW2/58aSlvj0INa4NiBwkY987/X7VYvnSm747vOcD8Xmnd0Qwvt
10
- weC2/NK11NFTNBIIodqsbvtlt1Ff0SXrO5PfbDU1yY+xzbx7SAwqhImXwScS
11
- 4b0nFpArdfeVKNp5teSiu050m4B8kb2EjPW7NISui5NXxWtU5Mu41EnenGES
12
- CkHsLI3plvL/
1
+ TGr8Jm/7zh1dfjjoNJtOncvX/BdUL47b8k2rQbMTY7VD6ZpD317NTuVX1hHV
2
+ 54Nm6pnCId5wcga3sI3TqolTxkqKBy7Rb1mPVBeJyd7ZuoD7zp65+ws+o4Lr
3
+ Xn/3WculfuThUnDESqD53vnKPCfRK6hoMDygEV7urmuide5ogpZp52lidUku
4
+ cXOHDqfVETX7NNnHHghg/6qDUX7+0qf+XeKe8uiI0cPoE5YFnOHyF7oOBGtR
5
+ IqJG97q1InCJMeAbMSxcjO71Te51Z098yI+XN4rGmXbmzSrVKHMUk0tdsVxi
6
+ CE5OmnzLeXK1xomxkRmXZzl10WDBn9e4knoLJTlDdNR3fA4gLRfy3r6RBlDl
7
+ j7HI53o5gi9PTshSXwHr6Q8SV9fty2Nz3/yRT/ZPLUJC1GUqErLl2j6zYbVR
8
+ 8YMaLt1Zqi79ycPZxZV4Zh57YE86nUqepS1pzVpcS8dHZMne846lTyaOyxZ8
9
+ dxW+18s7E7KqpHj17QYrF6c7R5ZHgoNAbLFeGSUCXqkW8YdyidLqQTzWN7hF
10
+ roVcZWFe8YfSUKmgndVBOHGHxMGr+OYgVdAStOEwmRHaNGgSBE4FCkKXYJUj
11
+ ec3zNpiOCb8zxfNku5nT5nIHnGqO4JKWDvDGVioV+ffHwXpAw3OFE5n+M/uo
12
+ 4ZZbVSh1qZxd
@@ -0,0 +1,81 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class ShaCipherByDefaulTest < Test::Unit::TestCase
4
+ def setup
5
+ @sha_cipher = PluginAWeek::EncryptedStrings::ShaCipher.new
6
+ end
7
+
8
+ def test_should_use_default_salt
9
+ assert_equal 'salt', @sha_cipher.salt
10
+ end
11
+
12
+ def test_should_encrypt_using_default_salt
13
+ assert_equal 'f438229716cab43569496f3a3630b3727524b81b', @sha_cipher.encrypt('test')
14
+ end
15
+ end
16
+
17
+ class ShaCipherWithCustomDefaultsTest < Test::Unit::TestCase
18
+ def setup
19
+ @original_default_salt = PluginAWeek::EncryptedStrings::ShaCipher.default_salt
20
+ PluginAWeek::EncryptedStrings::ShaCipher.default_salt = 'custom_salt'
21
+ @sha_cipher = PluginAWeek::EncryptedStrings::ShaCipher.new
22
+ end
23
+
24
+ def test_should_use_custom_default_salt
25
+ assert_equal 'custom_salt', @sha_cipher.salt
26
+ end
27
+
28
+ def test_should_encrypt_using_custom_default_salt
29
+ assert_equal '280f3c516070b09aa3eb755378509c725a9c6561', @sha_cipher.encrypt('test')
30
+ end
31
+
32
+ def teardown
33
+ PluginAWeek::EncryptedStrings::ShaCipher.default_salt = @original_default_salt
34
+ end
35
+ end
36
+
37
+ class ShaCipherWithInvalidOptionsTest < Test::Unit::TestCase
38
+ def test_should_throw_an_exception
39
+ assert_raise(ArgumentError) {PluginAWeek::EncryptedStrings::ShaCipher.new(:invalid => true)}
40
+ end
41
+ end
42
+
43
+ class ShaCipherTest < Test::Unit::TestCase
44
+ def setup
45
+ @sha_cipher = PluginAWeek::EncryptedStrings::ShaCipher.new
46
+ end
47
+
48
+ def test_should_not_be_able_to_decrypt
49
+ assert !PluginAWeek::EncryptedStrings::ShaCipher.new.can_decrypt?
50
+ end
51
+
52
+ def test_should_raise_exception_if_trying_to_decrypt
53
+ assert_raises(NotImplementedError) {PluginAWeek::EncryptedStrings::ShaCipher.new.decrypt('test')}
54
+ end
55
+ end
56
+
57
+ class ShaCipherWithCustomOptionsTest < Test::Unit::TestCase
58
+ def setup
59
+ @sha_cipher = PluginAWeek::EncryptedStrings::ShaCipher.new(:salt => 'different salt')
60
+ end
61
+
62
+ def test_should_use_custom_salt
63
+ assert_equal 'different salt', @sha_cipher.salt
64
+ end
65
+
66
+ def test_should_encrypt_using_custom_salt
67
+ assert_equal '18e3256d71529db8fa65b2eef24a69ddad7070f3', @sha_cipher.encrypt('test')
68
+ end
69
+ end
70
+
71
+ class ShaCipherWithNonStringSaltTest < Test::Unit::TestCase
72
+ require 'time'
73
+
74
+ def setup
75
+ @sha_cipher = PluginAWeek::EncryptedStrings::ShaCipher.new(:salt => Time.parse('Tue Jan 01 00:00:00 UTC 2008'))
76
+ end
77
+
78
+ def test_should_stringify_salt
79
+ assert_equal 'Tue Jan 01 00:00:00 UTC 2008', @sha_cipher.salt
80
+ end
81
+ end