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
data/CHANGELOG.rdoc
CHANGED
data/README.rdoc
CHANGED
@@ -38,11 +38,11 @@ Asymmetric ciphers.
|
|
38
38
|
>> encrypted_password.class
|
39
39
|
=> String
|
40
40
|
>> encrypted_password.cipher
|
41
|
-
=> #<
|
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)
|
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
@@ -1,187 +1,185 @@
|
|
1
|
-
module
|
2
|
-
|
3
|
-
|
4
|
-
|
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
|
-
#
|
8
|
-
|
9
|
-
end
|
64
|
+
# Private key used for decrypting data
|
65
|
+
attr_reader :private_key_file
|
10
66
|
|
11
|
-
#
|
12
|
-
|
13
|
-
|
14
|
-
#
|
15
|
-
|
16
|
-
|
17
|
-
#
|
18
|
-
|
19
|
-
|
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
|
-
#
|
55
|
-
#
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
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
|
-
|
66
|
-
|
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
|
-
|
69
|
-
attr_reader :public_key_file
|
92
|
+
@public_key = @private_key = nil
|
70
93
|
|
71
|
-
|
72
|
-
|
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
|
-
|
75
|
-
|
98
|
+
self.algorithm = options[:algorithm]
|
99
|
+
self.password = options[:password]
|
76
100
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
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
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
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
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
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
|
-
|
134
|
-
|
135
|
-
|
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
|
-
|
138
|
-
|
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
|
-
#
|
142
|
-
def
|
143
|
-
|
158
|
+
# Loads the public key from the configured file
|
159
|
+
def load_public_key
|
160
|
+
@public_rsa = nil
|
144
161
|
|
145
|
-
|
146
|
-
|
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
|
-
|
151
|
-
|
152
|
-
|
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
|
-
|
155
|
-
|
156
|
-
|
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
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
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
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
5
|
-
module
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
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
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
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
|
-
|
112
|
-
|
113
|
-
|
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
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
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
|
-
|
171
|
-
|
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
|
-
|
181
|
-
|
182
|
-
|
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
|
-
#
|
192
|
-
#
|
193
|
-
|
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
|
-
#
|
198
|
-
|
199
|
-
|
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
|
207
|
+
include EncryptedStrings::Extensions::String
|
210
208
|
end
|