encrypted_strings 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc CHANGED
@@ -1,5 +1,9 @@
1
1
  == master
2
2
 
3
+ == 0.3.0 / 2008-12-14
4
+
5
+ * Remove the PluginAWeek namespace
6
+
3
7
  == 0.2.1 / 2008-12-04
4
8
 
5
9
  * Fix class-level defaults not working when inherited
data/README.rdoc CHANGED
@@ -38,11 +38,11 @@ Asymmetric ciphers.
38
38
  >> encrypted_password.class
39
39
  => String
40
40
  >> encrypted_password.cipher
41
- => #<PluginAWeek::EncryptedStrings::ShaCipher:0x2b9238889460 @salt="salt">
41
+ => #<EncryptedStrings::ShaCipher:0x2b9238889460 @salt="salt">
42
42
  >> encrypted_password == 'shhhh'
43
43
  => true
44
44
  >> encrypted_password.decrypt
45
- NotImplementedError: Decryption is not supported using a(n) PluginAWeek::EncryptedStrings::ShaCipher
45
+ NotImplementedError: Decryption is not supported using a(n) EncryptedStrings::ShaCipher
46
46
  from ./script/../config/../config/../vendor/plugins/encrypted_strings/lib/encrypted_strings/cipher.rb:13:in `decrypt'
47
47
  from ./script/../config/../config/../vendor/plugins/encrypted_strings/lib/encrypted_strings/extensions/string.rb:52:in `decrypt'
48
48
  from (irb):40
data/Rakefile CHANGED
@@ -5,7 +5,7 @@ require 'rake/contrib/sshpublisher'
5
5
 
6
6
  spec = Gem::Specification.new do |s|
7
7
  s.name = 'encrypted_strings'
8
- s.version = '0.2.1'
8
+ s.version = '0.3.0'
9
9
  s.platform = Gem::Platform::RUBY
10
10
  s.summary = 'Dead-simple string encryption/decryption syntax.'
11
11
 
@@ -1,187 +1,185 @@
1
- module PluginAWeek #:nodoc:
2
- module EncryptedStrings
3
- # Indicates no public key was found
4
- class NoPublicKeyError < StandardError
1
+ module EncryptedStrings
2
+ # Indicates no public key was found
3
+ class NoPublicKeyError < StandardError
4
+ end
5
+
6
+ # Indicates no private key was found
7
+ class NoPrivateKeyError < StandardError
8
+ end
9
+
10
+ # Encryption in which the keys used to encrypt/decrypt come in pairs. Also
11
+ # known as public key encryption. Anything that's encrypted using the
12
+ # public key can only be decrypted with the same algorithm and a matching
13
+ # private key. Any message that is encrypted with the private key can only
14
+ # be decrypted with the matching public key.
15
+ #
16
+ # Source: http://support.microsoft.com/kb/246071
17
+ #
18
+ # == Encrypting
19
+ #
20
+ # To encrypt a string using an asymmetric cipher, the location of the
21
+ # public key file must be specified. You can define the default for this
22
+ # value like so:
23
+ #
24
+ # EncryptedStrings::AsymmetricCipher.default_public_key_file = './public.key'
25
+ #
26
+ # If these configuration options are not passed in to #encrypt, then the
27
+ # default values will be used. You can override the default values like so:
28
+ #
29
+ # password = 'shhhh'
30
+ # password.encrypt(:asymmetric, :public_key_file => './encrypted_public.key') # => "INy95irZ8AlHmvc6ZAF/ARsTpbqPIB/4bEAKKOebjsayB7NYWtIzpswvzxqf\nNJ5yyuvxfMODrcg7RimEMFkFlg==\n"
31
+ #
32
+ # An exception will be raised if either the public key file could not be
33
+ # found or the key could not decrypt the public key file.
34
+ #
35
+ # == Decrypting
36
+ #
37
+ # To decrypt a string using an asymmetric cipher, the location of the
38
+ # private key file must be specified. If this file is itself encrypted, you
39
+ # must also specify the algorithm and password used to seed the symmetric
40
+ # algorithm that will decrypt the plublic key file. You can define defaults
41
+ # for these values like so:
42
+ #
43
+ # EncryptedStrings::AsymmetricCipher.default_private_key_file = './private.key'
44
+ # EncryptedStrings::SymmetricCipher.default_algorithm = 'DES-EDE3-CBC'
45
+ # EncryptedStrings::SymmetricCipher.default_password = 'secret'
46
+ #
47
+ # If these configuration options are not passed in to #decrypt, then the
48
+ # default values will be used. You can override the default values like so:
49
+ #
50
+ # password = "INy95irZ8AlHmvc6ZAF/ARsTpbqPIB/4bEAKKOebjsayB7NYWtIzpswvzxqf\nNJ5yyuvxfMODrcg7RimEMFkFlg==\n"
51
+ # password.decrypt(:asymmetric, :public_key_file => './encrypted_public.key', :password => 'secret') # => "shhhh"
52
+ #
53
+ # An exception will be raised if either the private key file could not be
54
+ # found or the password could not decrypt the private key file.
55
+ class AsymmetricCipher < Cipher
56
+ class << self
57
+ # The default private key to use during encryption. Default is nil.
58
+ attr_accessor :default_private_key_file
59
+
60
+ # The default public key to use during encryption. Default is nil.
61
+ attr_accessor :default_public_key_file
5
62
  end
6
63
 
7
- # Indicates no private key was found
8
- class NoPrivateKeyError < StandardError
9
- end
64
+ # Private key used for decrypting data
65
+ attr_reader :private_key_file
10
66
 
11
- # Encryption in which the keys used to encrypt/decrypt come in pairs. Also
12
- # known as public key encryption. Anything that's encrypted using the
13
- # public key can only be decrypted with the same algorithm and a matching
14
- # private key. Any message that is encrypted with the private key can only
15
- # be decrypted with the matching public key.
16
- #
17
- # Source: http://support.microsoft.com/kb/246071
18
- #
19
- # == Encrypting
20
- #
21
- # To encrypt a string using an asymmetric cipher, the location of the
22
- # public key file must be specified. You can define the default for this
23
- # value like so:
24
- #
25
- # PluginAWeek::EncryptedStrings::AsymmetricCipher.default_public_key_file = './public.key'
26
- #
27
- # If these configuration options are not passed in to #encrypt, then the
28
- # default values will be used. You can override the default values like so:
29
- #
30
- # password = 'shhhh'
31
- # password.encrypt(:asymmetric, :public_key_file => './encrypted_public.key') # => "INy95irZ8AlHmvc6ZAF/ARsTpbqPIB/4bEAKKOebjsayB7NYWtIzpswvzxqf\nNJ5yyuvxfMODrcg7RimEMFkFlg==\n"
32
- #
33
- # An exception will be raised if either the public key file could not be
34
- # found or the key could not decrypt the public key file.
35
- #
36
- # == Decrypting
37
- #
38
- # To decrypt a string using an asymmetric cipher, the location of the
39
- # private key file must be specified. If this file is itself encrypted, you
40
- # must also specify the algorithm and password used to seed the symmetric
41
- # algorithm that will decrypt the plublic key file. You can define defaults
42
- # for these values like so:
43
- #
44
- # PluginAWeek::EncryptedStrings::AsymmetricCipher.default_private_key_file = './private.key'
45
- # PluginAWeek::EncryptedStrings::SymmetricCipher.default_algorithm = 'DES-EDE3-CBC'
46
- # PluginAWeek::EncryptedStrings::SymmetricCipher.default_password = 'secret'
47
- #
48
- # If these configuration options are not passed in to #decrypt, then the
49
- # default values will be used. You can override the default values like so:
50
- #
51
- # password = "INy95irZ8AlHmvc6ZAF/ARsTpbqPIB/4bEAKKOebjsayB7NYWtIzpswvzxqf\nNJ5yyuvxfMODrcg7RimEMFkFlg==\n"
52
- # password.decrypt(:asymmetric, :public_key_file => './encrypted_public.key', :password => 'secret') # => "shhhh"
67
+ # Public key used for encrypting data
68
+ attr_reader :public_key_file
69
+
70
+ # The algorithm to use if the key files are encrypted themselves
71
+ attr_accessor :algorithm
72
+
73
+ # The password used during symmetric decryption of the key files
74
+ attr_accessor :password
75
+
76
+ # Creates a new cipher that uses an asymmetric encryption strategy.
53
77
  #
54
- # An exception will be raised if either the private key file could not be
55
- # found or the password could not decrypt the private key file.
56
- class AsymmetricCipher < Cipher
57
- class << self
58
- # The default private key to use during encryption. Default is nil.
59
- attr_accessor :default_private_key_file
60
-
61
- # The default public key to use during encryption. Default is nil.
62
- attr_accessor :default_public_key_file
63
- end
78
+ # Configuration options:
79
+ # * +private_key_file+ - Encrypted private key file
80
+ # * +public_key_file+ - Public key file
81
+ # * +password+ - The password to use in the symmetric cipher
82
+ # * +algorithm+ - Algorithm to use symmetrically encrypted strings
83
+ def initialize(options = {})
84
+ invalid_options = options.keys - [:private_key_file, :public_key_file, :algorithm, :password]
85
+ raise ArgumentError, "Unknown key(s): #{invalid_options.join(", ")}" unless invalid_options.empty?
64
86
 
65
- # Private key used for decrypting data
66
- attr_reader :private_key_file
87
+ options = {
88
+ :private_key_file => AsymmetricCipher.default_private_key_file,
89
+ :public_key_file => AsymmetricCipher.default_public_key_file
90
+ }.merge(options)
67
91
 
68
- # Public key used for encrypting data
69
- attr_reader :public_key_file
92
+ @public_key = @private_key = nil
70
93
 
71
- # The algorithm to use if the key files are encrypted themselves
72
- attr_accessor :algorithm
94
+ self.private_key_file = options[:private_key_file]
95
+ self.public_key_file = options[:public_key_file]
96
+ raise ArgumentError, 'At least one key file must be specified (:private_key_file or :public_key_file)' unless private_key_file || public_key_file
73
97
 
74
- # The password used during symmetric decryption of the key files
75
- attr_accessor :password
98
+ self.algorithm = options[:algorithm]
99
+ self.password = options[:password]
76
100
 
77
- # Creates a new cipher that uses an asymmetric encryption strategy.
78
- #
79
- # Configuration options:
80
- # * +private_key_file+ - Encrypted private key file
81
- # * +public_key_file+ - Public key file
82
- # * +password+ - The password to use in the symmetric cipher
83
- # * +algorithm+ - Algorithm to use symmetrically encrypted strings
84
- def initialize(options = {})
85
- invalid_options = options.keys - [:private_key_file, :public_key_file, :algorithm, :password]
86
- raise ArgumentError, "Unknown key(s): #{invalid_options.join(", ")}" unless invalid_options.empty?
87
-
88
- options = {
89
- :private_key_file => AsymmetricCipher.default_private_key_file,
90
- :public_key_file => AsymmetricCipher.default_public_key_file
91
- }.merge(options)
92
-
93
- @public_key = @private_key = nil
94
-
95
- self.private_key_file = options[:private_key_file]
96
- self.public_key_file = options[:public_key_file]
97
- raise ArgumentError, 'At least one key file must be specified (:private_key_file or :public_key_file)' unless private_key_file || public_key_file
98
-
99
- self.algorithm = options[:algorithm]
100
- self.password = options[:password]
101
-
102
- super()
103
- end
104
-
105
- # Encrypts the given data. If no public key file has been specified, then
106
- # a NoPublicKeyError will be raised.
107
- def encrypt(data)
108
- raise NoPublicKeyError, "Public key file: #{public_key_file}" unless public?
109
-
110
- encrypted_data = public_rsa.public_encrypt(data)
111
- Base64.encode64(encrypted_data)
112
- end
101
+ super()
102
+ end
103
+
104
+ # Encrypts the given data. If no public key file has been specified, then
105
+ # a NoPublicKeyError will be raised.
106
+ def encrypt(data)
107
+ raise NoPublicKeyError, "Public key file: #{public_key_file}" unless public?
113
108
 
114
- # Decrypts the given data. If no private key file has been specified, then
115
- # a NoPrivateKeyError will be raised.
116
- def decrypt(data)
117
- raise NoPrivateKeyError, "Private key file: #{private_key_file}" unless private?
118
-
119
- decrypted_data = Base64.decode64(data)
120
- private_rsa.private_decrypt(decrypted_data)
121
- end
109
+ encrypted_data = public_rsa.public_encrypt(data)
110
+ Base64.encode64(encrypted_data)
111
+ end
112
+
113
+ # Decrypts the given data. If no private key file has been specified, then
114
+ # a NoPrivateKeyError will be raised.
115
+ def decrypt(data)
116
+ raise NoPrivateKeyError, "Private key file: #{private_key_file}" unless private?
122
117
 
123
- # Sets the location of the private key and loads it
124
- def private_key_file=(file)
125
- @private_key_file = file and load_private_key
126
- end
118
+ decrypted_data = Base64.decode64(data)
119
+ private_rsa.private_decrypt(decrypted_data)
120
+ end
121
+
122
+ # Sets the location of the private key and loads it
123
+ def private_key_file=(file)
124
+ @private_key_file = file and load_private_key
125
+ end
126
+
127
+ # Sets the location of the public key and loads it
128
+ def public_key_file=(file)
129
+ @public_key_file = file and load_public_key
130
+ end
131
+
132
+ # Does this cipher have a public key available?
133
+ def public?
134
+ return true if @public_key
127
135
 
128
- # Sets the location of the public key and loads it
129
- def public_key_file=(file)
130
- @public_key_file = file and load_public_key
131
- end
136
+ load_public_key
137
+ !@public_key.nil?
138
+ end
139
+
140
+ # Does this cipher have a private key available?
141
+ def private?
142
+ return true if @private_key
132
143
 
133
- # Does this cipher have a public key available?
134
- def public?
135
- return true if @public_key
144
+ load_private_key
145
+ !@private_key.nil?
146
+ end
147
+
148
+ private
149
+ # Loads the private key from the configured file
150
+ def load_private_key
151
+ @private_rsa = nil
136
152
 
137
- load_public_key
138
- !@public_key.nil?
153
+ if private_key_file && File.file?(private_key_file)
154
+ @private_key = File.read(private_key_file)
155
+ end
139
156
  end
140
157
 
141
- # Does this cipher have a private key available?
142
- def private?
143
- return true if @private_key
158
+ # Loads the public key from the configured file
159
+ def load_public_key
160
+ @public_rsa = nil
144
161
 
145
- load_private_key
146
- !@private_key.nil?
162
+ if public_key_file && File.file?(public_key_file)
163
+ @public_key = File.read(public_key_file)
164
+ end
147
165
  end
148
166
 
149
- private
150
- # Loads the private key from the configured file
151
- def load_private_key
152
- @private_rsa = nil
167
+ # Retrieves the private RSA from the private key
168
+ def private_rsa
169
+ if password
170
+ options = {:password => password}
171
+ options[:algorithm] = algorithm if algorithm
153
172
 
154
- if private_key_file && File.file?(private_key_file)
155
- @private_key = File.read(private_key_file)
156
- end
173
+ private_key = @private_key.decrypt(:symmetric, options)
174
+ OpenSSL::PKey::RSA.new(private_key)
175
+ else
176
+ @private_rsa ||= OpenSSL::PKey::RSA.new(@private_key)
157
177
  end
158
-
159
- # Loads the public key from the configured file
160
- def load_public_key
161
- @public_rsa = nil
162
-
163
- if public_key_file && File.file?(public_key_file)
164
- @public_key = File.read(public_key_file)
165
- end
166
- end
167
-
168
- # Retrieves the private RSA from the private key
169
- def private_rsa
170
- if password
171
- options = {:password => password}
172
- options[:algorithm] = algorithm if algorithm
173
-
174
- private_key = @private_key.decrypt(:symmetric, options)
175
- OpenSSL::PKey::RSA.new(private_key)
176
- else
177
- @private_rsa ||= OpenSSL::PKey::RSA.new(@private_key)
178
- end
179
- end
180
-
181
- # Retrieves the public RSA
182
- def public_rsa
183
- @public_rsa ||= OpenSSL::PKey::RSA.new(@public_key)
184
- end
185
- end
178
+ end
179
+
180
+ # Retrieves the public RSA
181
+ def public_rsa
182
+ @public_rsa ||= OpenSSL::PKey::RSA.new(@public_key)
183
+ end
186
184
  end
187
185
  end
@@ -1,19 +1,17 @@
1
- module PluginAWeek #:nodoc:
2
- module EncryptedStrings
3
- # Represents the base class for all ciphers. By default, all ciphers are
4
- # assumed to be able to decrypt strings. Note, however, that certain
5
- # encryption algorithms do not allow decryption.
6
- class Cipher
7
- # Can this string be decrypted? Default is true.
8
- def can_decrypt?
9
- true
10
- end
11
-
12
- # Attempts to decrypt the given data using the current configuration. By
13
- # default, decryption is not implemented.
14
- def decrypt(data)
15
- raise NotImplementedError, "Decryption is not supported using a(n) #{self.class.name}"
16
- end
1
+ module EncryptedStrings
2
+ # Represents the base class for all ciphers. By default, all ciphers are
3
+ # assumed to be able to decrypt strings. Note, however, that certain
4
+ # encryption algorithms do not allow decryption.
5
+ class Cipher
6
+ # Can this string be decrypted? Default is true.
7
+ def can_decrypt?
8
+ true
9
+ end
10
+
11
+ # Attempts to decrypt the given data using the current configuration. By
12
+ # default, decryption is not implemented.
13
+ def decrypt(data)
14
+ raise NotImplementedError, "Decryption is not supported using a(n) #{self.class.name}"
17
15
  end
18
16
  end
19
17
  end
@@ -1,210 +1,208 @@
1
1
  require 'openssl'
2
2
  require 'base64'
3
3
 
4
- module PluginAWeek #:nodoc:
5
- module EncryptedStrings
6
- module Extensions #:nodoc:
7
- # Adds support for in-place encryption/decryption of strings
8
- module String
9
- def self.included(base) #:nodoc:
10
- base.class_eval do
11
- attr_accessor :cipher
12
-
13
- alias_method :equals_without_encryption, :==
14
- alias_method :==, :equals_with_encryption
15
- end
16
- end
17
-
18
- # Encrypts the current string using the specified cipher. The default
19
- # cipher is sha.
20
- #
21
- # Configuration options are cipher-specific. See each individual cipher
22
- # class to find out the options available.
23
- #
24
- # == Example
25
- #
26
- # The following uses an SHA cipher to encrypt the string:
27
- #
28
- # password = 'shhhh'
29
- # password.encrypt # => "66c85d26dadde7e1db27e15a0776c921e27143bd"
30
- #
31
- # == Custom encryption mode
32
- #
33
- # The following uses Symmetric cipher (with a default password) to
34
- # encrypt the string:
35
- #
36
- # PluginAWeek::EncryptedStrings::SymmetricCipher.default_password = 'secret'
37
- # password = 'shhhh'
38
- # password.encrypt(:symmetric) # => "jDACXI5hMPI=\n"
39
- #
40
- # == Custom encryption options
41
- #
42
- # Some encryption modes also support additional configuration options
43
- # that determine how to encrypt the string. For example, SHA supports
44
- # a salt which seeds the algorithm:
45
- #
46
- # password = 'shhhh'
47
- # password.encrypt(:sha, :salt => 'secret') # => "3b22cbe4acde873c3efc82681096f3ae69aff828"
48
- def encrypt(*args)
49
- cipher = cipher_from_args(*args)
50
- encrypted_string = cipher.encrypt(self)
51
- encrypted_string.cipher = cipher
52
-
53
- encrypted_string
54
- end
55
-
56
- # Encrypts this string and replaces it with the encrypted value. This
57
- # takes the same parameters as #encrypt, but returns the same string
58
- # instead of a different one.
59
- #
60
- # == Example
61
- #
62
- # password = 'shhhh'
63
- # password.encrypt!(:symmetric, :password => 'secret') # => "qSg8vOo6QfU=\n"
64
- # password # => "qSg8vOo6QfU=\n"
65
- def encrypt!(*args)
66
- encrypted_string = encrypt(*args)
67
- self.cipher = encrypted_string.cipher
4
+ module EncryptedStrings
5
+ module Extensions #:nodoc:
6
+ # Adds support for in-place encryption/decryption of strings
7
+ module String
8
+ def self.included(base) #:nodoc:
9
+ base.class_eval do
10
+ attr_accessor :cipher
68
11
 
69
- replace(encrypted_string)
12
+ alias_method :equals_without_encryption, :==
13
+ alias_method :==, :equals_with_encryption
70
14
  end
15
+ end
16
+
17
+ # Encrypts the current string using the specified cipher. The default
18
+ # cipher is sha.
19
+ #
20
+ # Configuration options are cipher-specific. See each individual cipher
21
+ # class to find out the options available.
22
+ #
23
+ # == Example
24
+ #
25
+ # The following uses an SHA cipher to encrypt the string:
26
+ #
27
+ # password = 'shhhh'
28
+ # password.encrypt # => "66c85d26dadde7e1db27e15a0776c921e27143bd"
29
+ #
30
+ # == Custom encryption mode
31
+ #
32
+ # The following uses Symmetric cipher (with a default password) to
33
+ # encrypt the string:
34
+ #
35
+ # EncryptedStrings::SymmetricCipher.default_password = 'secret'
36
+ # password = 'shhhh'
37
+ # password.encrypt(:symmetric) # => "jDACXI5hMPI=\n"
38
+ #
39
+ # == Custom encryption options
40
+ #
41
+ # Some encryption modes also support additional configuration options
42
+ # that determine how to encrypt the string. For example, SHA supports
43
+ # a salt which seeds the algorithm:
44
+ #
45
+ # password = 'shhhh'
46
+ # password.encrypt(:sha, :salt => 'secret') # => "3b22cbe4acde873c3efc82681096f3ae69aff828"
47
+ def encrypt(*args)
48
+ cipher = cipher_from_args(*args)
49
+ encrypted_string = cipher.encrypt(self)
50
+ encrypted_string.cipher = cipher
71
51
 
72
- # Is this string encrypted? This will return true if the string is the
73
- # result of a call to #encrypt or #encrypt!.
74
- #
75
- # == Example
76
- #
77
- # password = 'shhhh'
78
- # password.encrypted? # => false
79
- # password.encrypt! # => "66c85d26dadde7e1db27e15a0776c921e27143bd"
80
- # password.encrypted? # => true
81
- def encrypted?
82
- !cipher.nil?
83
- end
52
+ encrypted_string
53
+ end
54
+
55
+ # Encrypts this string and replaces it with the encrypted value. This
56
+ # takes the same parameters as #encrypt, but returns the same string
57
+ # instead of a different one.
58
+ #
59
+ # == Example
60
+ #
61
+ # password = 'shhhh'
62
+ # password.encrypt!(:symmetric, :password => 'secret') # => "qSg8vOo6QfU=\n"
63
+ # password # => "qSg8vOo6QfU=\n"
64
+ def encrypt!(*args)
65
+ encrypted_string = encrypt(*args)
66
+ self.cipher = encrypted_string.cipher
84
67
 
85
- # Decrypts this string. If this is not a string that was previously
86
- # encrypted, the cipher must be specified in the same way that it is
87
- # when encrypting a string.
88
- #
89
- # == Example
90
- #
91
- # Without being previously encrypted:
92
- #
93
- # password = "qSg8vOo6QfU=\n"
94
- # password.decrypt(:symmetric, :password => 'secret') # => "shhhh"
95
- #
96
- # After being previously encrypted:
97
- #
98
- # password = 'shhhh'
99
- # password.encrypt!(:symmetric, :password => 'secret') # => "qSg8vOo6QfU=\n"
100
- # password.decrypt # => "shhhh"
101
- def decrypt(*args)
102
- raise ArgumentError, 'Cipher cannot be inferred: must specify it as an argument' if args.empty? && !encrypted?
103
-
104
- cipher = args.empty? && self.cipher || cipher_from_args(*args)
105
- encrypted_string = cipher.decrypt(self)
106
- encrypted_string.cipher = nil
107
-
108
- encrypted_string
109
- end
68
+ replace(encrypted_string)
69
+ end
70
+
71
+ # Is this string encrypted? This will return true if the string is the
72
+ # result of a call to #encrypt or #encrypt!.
73
+ #
74
+ # == Example
75
+ #
76
+ # password = 'shhhh'
77
+ # password.encrypted? # => false
78
+ # password.encrypt! # => "66c85d26dadde7e1db27e15a0776c921e27143bd"
79
+ # password.encrypted? # => true
80
+ def encrypted?
81
+ !cipher.nil?
82
+ end
83
+
84
+ # Decrypts this string. If this is not a string that was previously
85
+ # encrypted, the cipher must be specified in the same way that it is
86
+ # when encrypting a string.
87
+ #
88
+ # == Example
89
+ #
90
+ # Without being previously encrypted:
91
+ #
92
+ # password = "qSg8vOo6QfU=\n"
93
+ # password.decrypt(:symmetric, :password => 'secret') # => "shhhh"
94
+ #
95
+ # After being previously encrypted:
96
+ #
97
+ # password = 'shhhh'
98
+ # password.encrypt!(:symmetric, :password => 'secret') # => "qSg8vOo6QfU=\n"
99
+ # password.decrypt # => "shhhh"
100
+ def decrypt(*args)
101
+ raise ArgumentError, 'Cipher cannot be inferred: must specify it as an argument' if args.empty? && !encrypted?
110
102
 
111
- # Decrypts this string and replaces it with the decrypted value This
112
- # takes the same parameters as #decrypt, but returns the same string
113
- # instead of a different one.
114
- #
115
- # For example,
116
- #
117
- # password = "qSg8vOo6QfU=\n"
118
- # password.decrypt!(:symmetric, :password => 'secret') # => "shhhh"
119
- # password # => "shhhh"
120
- def decrypt!(*args)
121
- value = replace(decrypt(*args))
122
- self.cipher = nil
123
- value
124
- end
103
+ cipher = args.empty? && self.cipher || cipher_from_args(*args)
104
+ encrypted_string = cipher.decrypt(self)
105
+ encrypted_string.cipher = nil
125
106
 
126
- # Can this string be decrypted? Strings can only be decrypted if they
127
- # have previously been decrypted *and* the cipher supports decryption.
128
- # To determine whether or not the cipher supports decryption, see the
129
- # api for the cipher.
130
- def can_decrypt?
131
- encrypted? && cipher.can_decrypt?
132
- end
133
-
134
- # Tests whether the other object is equal to this one. Encrypted strings
135
- # will be tested not only on their encrypted strings, but also by
136
- # decrypting them and running tests against the decrypted value.
137
- #
138
- # == Equality with strings
139
- #
140
- # password = 'shhhh'
141
- # password.encrypt! # => "66c85d26dadde7e1db27e15a0776c921e27143bd"
142
- # password # => "66c85d26dadde7e1db27e15a0776c921e27143bd"
143
- # password == "shhhh" # => true
144
- #
145
- # == Equality with encrypted strings
146
- #
147
- # password = 'shhhh'
148
- # password.encrypt! # => "66c85d26dadde7e1db27e15a0776c921e27143bd"
149
- # password # => "66c85d26dadde7e1db27e15a0776c921e27143bd"
150
- # password == 'shhhh' # => true
151
- #
152
- # another_password = 'shhhh'
153
- # another_password.encrypt! # => "66c85d26dadde7e1db27e15a0776c921e27143bd"
154
- # password == another_password # => true
155
- def equals_with_encryption(other)
156
- if !(is_equal = equals_without_encryption(other)) && String === other
157
- if encrypted?
158
- if other.encrypted?
159
- # We're both encrypted, so check if:
160
- # (1) The other string is the encrypted value of this string
161
- # (2) This string is the encrypted value of the other string
162
- # (3) The other string is the encrypted value of this string, decrypted
163
- # (4) This string is the encrypted value of the other string, decrypted
164
- is_string_equal?(self, other) || is_string_equal?(other, self) || self.can_decrypt? && is_string_equal?(self.decrypt, other) || other.can_decrypt? && is_string_equal?(other.decrypt, self)
165
- else
166
- # Only we're encrypted
167
- is_string_equal?(other, self)
168
- end
107
+ encrypted_string
108
+ end
109
+
110
+ # Decrypts this string and replaces it with the decrypted value This
111
+ # takes the same parameters as #decrypt, but returns the same string
112
+ # instead of a different one.
113
+ #
114
+ # For example,
115
+ #
116
+ # password = "qSg8vOo6QfU=\n"
117
+ # password.decrypt!(:symmetric, :password => 'secret') # => "shhhh"
118
+ # password # => "shhhh"
119
+ def decrypt!(*args)
120
+ value = replace(decrypt(*args))
121
+ self.cipher = nil
122
+ value
123
+ end
124
+
125
+ # Can this string be decrypted? Strings can only be decrypted if they
126
+ # have previously been decrypted *and* the cipher supports decryption.
127
+ # To determine whether or not the cipher supports decryption, see the
128
+ # api for the cipher.
129
+ def can_decrypt?
130
+ encrypted? && cipher.can_decrypt?
131
+ end
132
+
133
+ # Tests whether the other object is equal to this one. Encrypted strings
134
+ # will be tested not only on their encrypted strings, but also by
135
+ # decrypting them and running tests against the decrypted value.
136
+ #
137
+ # == Equality with strings
138
+ #
139
+ # password = 'shhhh'
140
+ # password.encrypt! # => "66c85d26dadde7e1db27e15a0776c921e27143bd"
141
+ # password # => "66c85d26dadde7e1db27e15a0776c921e27143bd"
142
+ # password == "shhhh" # => true
143
+ #
144
+ # == Equality with encrypted strings
145
+ #
146
+ # password = 'shhhh'
147
+ # password.encrypt! # => "66c85d26dadde7e1db27e15a0776c921e27143bd"
148
+ # password # => "66c85d26dadde7e1db27e15a0776c921e27143bd"
149
+ # password == 'shhhh' # => true
150
+ #
151
+ # another_password = 'shhhh'
152
+ # another_password.encrypt! # => "66c85d26dadde7e1db27e15a0776c921e27143bd"
153
+ # password == another_password # => true
154
+ def equals_with_encryption(other)
155
+ if !(is_equal = equals_without_encryption(other)) && String === other
156
+ if encrypted?
157
+ if other.encrypted?
158
+ # We're both encrypted, so check if:
159
+ # (1) The other string is the encrypted value of this string
160
+ # (2) This string is the encrypted value of the other string
161
+ # (3) The other string is the encrypted value of this string, decrypted
162
+ # (4) This string is the encrypted value of the other string, decrypted
163
+ is_string_equal?(self, other) || is_string_equal?(other, self) || self.can_decrypt? && is_string_equal?(self.decrypt, other) || other.can_decrypt? && is_string_equal?(other.decrypt, self)
169
164
  else
170
- if other.encrypted?
171
- # Only the other string is encrypted
172
- is_string_equal?(self, other)
173
- else
174
- # Neither are encrypted and equality test didn't work before, so
175
- # they can't be equal
176
- false
177
- end
165
+ # Only we're encrypted
166
+ is_string_equal?(other, self)
178
167
  end
179
168
  else
180
- # The other value wasn't a string, so we can't check encryption equality
181
- is_equal
182
- end
183
- end
184
-
185
- private
186
- def is_string_equal?(value, encrypted_value) #:nodoc:
187
- # If the encrypted value can be decrypted, then test against the decrypted value
188
- if encrypted_value.can_decrypt?
189
- encrypted_value.decrypt.equals_without_encryption(value)
169
+ if other.encrypted?
170
+ # Only the other string is encrypted
171
+ is_string_equal?(self, other)
190
172
  else
191
- # Otherwise encrypt this value based on the cipher used on the encrypted value
192
- # and test the equality of those strings
193
- encrypted_value.equals_without_encryption(encrypted_value.cipher.encrypt(value))
173
+ # Neither are encrypted and equality test didn't work before, so
174
+ # they can't be equal
175
+ false
194
176
  end
195
177
  end
196
-
197
- # Builds the cipher to use from the given arguments
198
- def cipher_from_args(*args) #:nodoc:
199
- options = args.last.is_a?(Hash) ? args.pop : {}
200
- name = (args.first || :sha).to_s.gsub(/(?:^|_)(.)/) {$1.upcase}
201
- PluginAWeek::EncryptedStrings.const_get("#{name}Cipher").new(options)
202
- end
178
+ else
179
+ # The other value wasn't a string, so we can't check encryption equality
180
+ is_equal
181
+ end
203
182
  end
183
+
184
+ private
185
+ def is_string_equal?(value, encrypted_value) #:nodoc:
186
+ # If the encrypted value can be decrypted, then test against the decrypted value
187
+ if encrypted_value.can_decrypt?
188
+ encrypted_value.decrypt.equals_without_encryption(value)
189
+ else
190
+ # Otherwise encrypt this value based on the cipher used on the encrypted value
191
+ # and test the equality of those strings
192
+ encrypted_value.equals_without_encryption(encrypted_value.cipher.encrypt(value))
193
+ end
194
+ end
195
+
196
+ # Builds the cipher to use from the given arguments
197
+ def cipher_from_args(*args) #:nodoc:
198
+ options = args.last.is_a?(Hash) ? args.pop : {}
199
+ name = (args.first || :sha).to_s.gsub(/(?:^|_)(.)/) {$1.upcase}
200
+ EncryptedStrings.const_get("#{name}Cipher").new(options)
201
+ end
204
202
  end
205
203
  end
206
204
  end
207
205
 
208
206
  ::String.class_eval do
209
- include PluginAWeek::EncryptedStrings::Extensions::String
207
+ include EncryptedStrings::Extensions::String
210
208
  end