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 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