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.
data/CHANGELOG.rdoc CHANGED
@@ -1,5 +1,13 @@
1
1
  == master
2
2
 
3
+ == 0.2.0 / 2008-12-01
4
+
5
+ * Remove AsymmetricEncryptor.default_algorithm, instead relying on SymmetricEncryptor.default_algorithm
6
+ * Rename NoKeyError to NoPasswordError
7
+ * Rename Encryptors to Ciphers
8
+ * Remove deprecated SymmetricEncryptor#key option
9
+ * Require that symmetric encryption be PKCS #5 compliant
10
+
3
11
  == 0.1.1 / 2008-12-01
4
12
 
5
13
  * Fix non-compliant PKCS #5 algorithm being used for symmetric encryption. Use :pkcs5_compliant => true for better security.
data/README.rdoc CHANGED
@@ -23,58 +23,58 @@ Source
23
23
  == Description
24
24
 
25
25
  Encrypting and decrypting data is not exactly the most straightforward and DRY
26
- way. encrypted_strings greatly improves upon this syntax and adds
26
+ way. encrypted_strings improves the syntax and reduces the complexity, adding
27
27
  straightforward support for encrypting values using SHA-1, Symmetric, and
28
- Asymmetric modes.
28
+ Asymmetric ciphers.
29
29
 
30
30
  == Usage
31
31
 
32
32
  === SHA Encryption
33
33
 
34
- >> password = "shhhh"
34
+ >> password = 'shhhh'
35
35
  => "shhhh"
36
- >> crypted_password = password.encrypt
36
+ >> encrypted_password = password.encrypt
37
37
  => "66c85d26dadde7e1db27e15a0776c921e27143bd"
38
- >> crypted_password.class
38
+ >> encrypted_password.class
39
39
  => String
40
- >> crypted_password.encryptor
41
- => #<PluginAWeek::EncryptedStrings::ShaEncryptor:0x2b9238889460 @salt="salt">
42
- >> crypted_password == "shhhh"
40
+ >> encrypted_password.cipher
41
+ => #<PluginAWeek::EncryptedStrings::ShaCipher:0x2b9238889460 @salt="salt">
42
+ >> encrypted_password == 'shhhh'
43
43
  => true
44
- >> crypted_password.decrypt
45
- NotImplementedError: Decryption is not supported using a(n) PluginAWeek::EncryptedStrings::ShaEncryptor
46
- from ./script/../config/../config/../vendor/plugins/encrypted_strings/lib/encrypted_strings/encryptor.rb:13:in `decrypt'
44
+ >> encrypted_password.decrypt
45
+ NotImplementedError: Decryption is not supported using a(n) PluginAWeek::EncryptedStrings::ShaCipher
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
49
49
 
50
- When encrypt is called, it creates an +encryptor+ instance which is used for
51
- future encryption and decryption of the string. The default encryptor uses
52
- SHA-1 encryption. For encryption modes that do not support decryption, equality
53
- with other strings is tested by encrypting the other string and checking whether
54
- the resulting encrypted value is the same.
50
+ When encrypt is called, it creates a +cipher+ instance which is used for
51
+ future encryption and decryption of the string. The default cipher uses
52
+ SHA-1 encryption. For ciphers that do not support decryption, equality with
53
+ other strings is tested by encrypting the other string and checking whether the
54
+ resulting encrypted value is the same.
55
55
 
56
56
  === Symmetric Encryption
57
57
 
58
- >> password = "shhhh"
58
+ >> password = 'shhhh'
59
59
  => "shhhh"
60
- >> crypted_password = password.encrypt(:symmetric, :password => "secret_key")
61
- => "jDACXI5hMPI=\n"
60
+ >> crypted_password = password.encrypt(:symmetric, :password => 'secret_key')
61
+ => "qSg8vOo6QfU=\n"
62
62
  >> crypted_password.class
63
63
  => String
64
- >> crypted_password == "shhhh"
64
+ >> crypted_password == 'shhhh'
65
65
  => true
66
66
  >> password = crypted_password.decrypt
67
67
  => "shhhh"
68
68
 
69
69
  === Asymmetric encryption
70
70
 
71
- >> password = "shhhh"
71
+ >> password = 'shhhh'
72
72
  => "shhhh"
73
- >> crypted_password = password.encrypt(:asymmetric, :public_key_file => "./public.key", :private_key_file => "./private.key")
73
+ >> crypted_password = password.encrypt(:asymmetric, :public_key_file => './public.key', :private_key_file => './private.key')
74
74
  => "NEwVzcikYUKfS8HTc9L9eg/dMxBCLZ/nFr7J1aQYjkl3I2MPUD0lmjr/saC6\nTJEPwOl60Ki24H8TUwnGtZy14A==\n"
75
75
  >> crypted_password.class
76
76
  => String
77
- >> crypted_password == "shhhh"
77
+ >> crypted_password == 'shhhh'
78
78
  => true
79
79
  >> password = crypted_password.decrypt
80
80
  => "shhhh"
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.1.1'
8
+ s.version = '0.2.0'
9
9
  s.platform = Gem::Platform::RUBY
10
10
  s.summary = 'Dead-simple string encryption/decryption syntax.'
11
11
 
@@ -1,8 +1,13 @@
1
- require 'encrypted_strings/no_private_key_error'
2
- require 'encrypted_strings/no_public_key_error'
3
-
4
1
  module PluginAWeek #:nodoc:
5
2
  module EncryptedStrings
3
+ # Indicates no public key was found
4
+ class NoPublicKeyError < StandardError
5
+ end
6
+
7
+ # Indicates no private key was found
8
+ class NoPrivateKeyError < StandardError
9
+ end
10
+
6
11
  # Encryption in which the keys used to encrypt/decrypt come in pairs. Also
7
12
  # known as public key encryption. Anything that's encrypted using the
8
13
  # public key can only be decrypted with the same algorithm and a matching
@@ -13,58 +18,50 @@ module PluginAWeek #:nodoc:
13
18
  #
14
19
  # == Encrypting
15
20
  #
16
- # To encrypt a string using an asymmetric algorithm, the location of the
21
+ # To encrypt a string using an asymmetric cipher, the location of the
17
22
  # public key file must be specified. You can define the default for this
18
23
  # value like so:
19
24
  #
20
- # PluginAWeek::EncryptedStrings::AsymmetricEncryptor.default_public_key_file = "./public.key"
25
+ # PluginAWeek::EncryptedStrings::AsymmetricCipher.default_public_key_file = './public.key'
21
26
  #
22
27
  # If these configuration options are not passed in to #encrypt, then the
23
28
  # default values will be used. You can override the default values like so:
24
29
  #
25
- # password = "shhhh"
26
- # password.encrypt(:asymmetric, :public_key_file => "./encrypted_public.key") # => "INy95irZ8AlHmvc6ZAF/ARsTpbqPIB/4bEAKKOebjsayB7NYWtIzpswvzxqf\nNJ5yyuvxfMODrcg7RimEMFkFlg==\n"
30
+ # password = 'shhhh'
31
+ # password.encrypt(:asymmetric, :public_key_file => './encrypted_public.key') # => "INy95irZ8AlHmvc6ZAF/ARsTpbqPIB/4bEAKKOebjsayB7NYWtIzpswvzxqf\nNJ5yyuvxfMODrcg7RimEMFkFlg==\n"
27
32
  #
28
33
  # An exception will be raised if either the public key file could not be
29
34
  # found or the key could not decrypt the public key file.
30
35
  #
31
36
  # == Decrypting
32
37
  #
33
- # To decrypt a string using an asymmetric algorithm, the location of the
38
+ # To decrypt a string using an asymmetric cipher, the location of the
34
39
  # private key file must be specified. If this file is itself encrypted, you
35
- # must also specify the algorithm and key used to seed the symmetric
40
+ # must also specify the algorithm and password used to seed the symmetric
36
41
  # algorithm that will decrypt the plublic key file. You can define defaults
37
42
  # for these values like so:
38
43
  #
39
- # PluginAWeek::EncryptedStrings::AsymmetricEncryptor.default_private_key_file = "./private.key"
40
- # PluginAWeek::EncryptedStrings::SymmetricEncryptor.default_algorithm = "DES-EDE3-CBC"
41
- # PluginAWeek::EncryptedStrings::SymmetricEncryptor.default_password = "secret"
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'
42
47
  #
43
48
  # If these configuration options are not passed in to #decrypt, then the
44
49
  # default values will be used. You can override the default values like so:
45
50
  #
46
51
  # password = "INy95irZ8AlHmvc6ZAF/ARsTpbqPIB/4bEAKKOebjsayB7NYWtIzpswvzxqf\nNJ5yyuvxfMODrcg7RimEMFkFlg==\n"
47
- # password.decrypt(:asymmetric, :public_key_file => "./encrypted_public.key", :password => "secret") # => "shhhh"
52
+ # password.decrypt(:asymmetric, :public_key_file => './encrypted_public.key', :password => 'secret') # => "shhhh"
48
53
  #
49
54
  # An exception will be raised if either the private key file could not be
50
- # found or the key could not decrypt the private key file.
51
- class AsymmetricEncryptor < Encryptor
55
+ # found or the password could not decrypt the private key file.
56
+ class AsymmetricCipher < Cipher
52
57
  class << self
53
58
  # The default private key to use during encryption. Default is nil.
54
59
  attr_accessor :default_private_key_file
55
60
 
56
61
  # The default public key to use during encryption. Default is nil.
57
62
  attr_accessor :default_public_key_file
58
-
59
- # The default algorithm to use. Default is nil.
60
- attr_accessor :default_algorithm
61
63
  end
62
64
 
63
- # Set defaults
64
- @default_private_key_file = nil
65
- @default_public_key_file = nil
66
- @default_algorithm = nil
67
-
68
65
  # Private key used for decrypting data
69
66
  attr_reader :private_key_file
70
67
 
@@ -77,34 +74,31 @@ module PluginAWeek #:nodoc:
77
74
  # The password used during symmetric decryption of the key files
78
75
  attr_accessor :password
79
76
 
77
+ # Creates a new cipher that uses an asymmetric encryption strategy.
78
+ #
80
79
  # Configuration options:
81
80
  # * +private_key_file+ - Encrypted private key file
82
81
  # * +public_key_file+ - Public key file
83
- # * +password+ - The password to use in the symmetric encryptor
84
- # * +key+ - DEPRECATED. The password to use in the symmetric encryptor
82
+ # * +password+ - The password to use in the symmetric cipher
85
83
  # * +algorithm+ - Algorithm to use symmetrically encrypted strings
86
- # * +pkcs5_compliant+ - Whether the generated key/iv should comply to the PKCS #5 standard. Default is false.
87
84
  def initialize(options = {})
88
- invalid_options = options.keys - [:private_key_file, :public_key_file, :password, :key, :algorithm, :pkcs5_compliant]
85
+ invalid_options = options.keys - [:private_key_file, :public_key_file, :algorithm, :password]
89
86
  raise ArgumentError, "Unknown key(s): #{invalid_options.join(", ")}" unless invalid_options.empty?
90
87
 
91
88
  options = {
92
89
  :private_key_file => self.class.default_private_key_file,
93
- :public_key_file => self.class.default_public_key_file,
94
- :algorithm => self.class.default_algorithm
90
+ :public_key_file => self.class.default_public_key_file
95
91
  }.merge(options)
96
92
 
97
93
  @public_key = @private_key = nil
98
94
 
99
- self.algorithm = options[:algorithm]
100
95
  self.private_key_file = options[:private_key_file]
101
96
  self.public_key_file = options[:public_key_file]
102
- self.password = options[:password] || options[:key]
103
- warn(':key option is deprecated and will be removed from encrypted_attributes 0.2.0 (use :password)') if options[:key]
104
- @pkcs5_compliant = options[:pkcs5_compliant]
105
-
106
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
107
98
 
99
+ self.algorithm = options[:algorithm]
100
+ self.password = options[:password]
101
+
108
102
  super()
109
103
  end
110
104
 
@@ -136,7 +130,7 @@ module PluginAWeek #:nodoc:
136
130
  @public_key_file = file and load_public_key
137
131
  end
138
132
 
139
- # Does this encryptor have a public key available?
133
+ # Does this cipher have a public key available?
140
134
  def public?
141
135
  return true if @public_key
142
136
 
@@ -144,7 +138,7 @@ module PluginAWeek #:nodoc:
144
138
  !@public_key.nil?
145
139
  end
146
140
 
147
- # Does this encryptor have a private key available?
141
+ # Does this cipher have a private key available?
148
142
  def private?
149
143
  return true if @private_key
150
144
 
@@ -176,7 +170,6 @@ module PluginAWeek #:nodoc:
176
170
  if password
177
171
  options = {:password => password}
178
172
  options[:algorithm] = algorithm if algorithm
179
- options[:pkcs5_compliant] = @pkcs5_compliant if !@pkcs5_compliant.nil?
180
173
 
181
174
  private_key = @private_key.decrypt(:symmetric, options)
182
175
  OpenSSL::PKey::RSA.new(private_key)
@@ -1,9 +1,9 @@
1
1
  module PluginAWeek #:nodoc:
2
2
  module EncryptedStrings
3
- # Represents the base class for all encryptors. By default, all encryptors
4
- # are assumed to be able to decrypt strings. Note, however, that certain
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
5
  # encryption algorithms do not allow decryption.
6
- class Encryptor
6
+ class Cipher
7
7
  # Can this string be decrypted? Default is true.
8
8
  def can_decrypt?
9
9
  true
@@ -4,36 +4,36 @@ require 'base64'
4
4
  module PluginAWeek #:nodoc:
5
5
  module EncryptedStrings
6
6
  module Extensions #:nodoc:
7
- # Adds support for encryption/decryption of strings
7
+ # Adds support for in-place encryption/decryption of strings
8
8
  module String
9
9
  def self.included(base) #:nodoc:
10
10
  base.class_eval do
11
- attr_accessor :encryptor
11
+ attr_accessor :cipher
12
12
 
13
13
  alias_method :equals_without_encryption, :==
14
14
  alias_method :==, :equals_with_encryption
15
15
  end
16
16
  end
17
17
 
18
- # Encrypts the current string using the specified encryption mode.
19
- # The default encryption mode is sha.
18
+ # Encrypts the current string using the specified cipher. The default
19
+ # cipher is sha.
20
20
  #
21
- # Configuration options are encryption-specific. See the encryptor
22
- # class for that mode to find out the options available.
21
+ # Configuration options are cipher-specific. See each individual cipher
22
+ # class to find out the options available.
23
23
  #
24
24
  # == Example
25
25
  #
26
- # The following uses SHA mode to encrypt the string:
26
+ # The following uses an SHA cipher to encrypt the string:
27
27
  #
28
28
  # password = 'shhhh'
29
29
  # password.encrypt # => "66c85d26dadde7e1db27e15a0776c921e27143bd"
30
30
  #
31
31
  # == Custom encryption mode
32
32
  #
33
- # The following uses Symmetric mode (with a default key) to encrypt the
34
- # string:
33
+ # The following uses Symmetric cipher (with a default password) to
34
+ # encrypt the string:
35
35
  #
36
- # PluginAWeek::EncryptedStrings::SymmetricEncryptor.default_key = 'my_key'
36
+ # PluginAWeek::EncryptedStrings::SymmetricCipher.default_password = 'secret'
37
37
  # password = 'shhhh'
38
38
  # password.encrypt(:symmetric) # => "jDACXI5hMPI=\n"
39
39
  #
@@ -46,9 +46,9 @@ module PluginAWeek #:nodoc:
46
46
  # password = 'shhhh'
47
47
  # password.encrypt(:sha, :salt => 'secret') # => "3b22cbe4acde873c3efc82681096f3ae69aff828"
48
48
  def encrypt(*args)
49
- encryptor = encryptor_from_args(*args)
50
- encrypted_string = encryptor.encrypt(self)
51
- encrypted_string.encryptor = encryptor
49
+ cipher = cipher_from_args(*args)
50
+ encrypted_string = cipher.encrypt(self)
51
+ encrypted_string.cipher = cipher
52
52
 
53
53
  encrypted_string
54
54
  end
@@ -60,17 +60,17 @@ module PluginAWeek #:nodoc:
60
60
  # == Example
61
61
  #
62
62
  # password = 'shhhh'
63
- # password.encrypt!(:symmetric, :password => 'my_key') # => "jDACXI5hMPI=\n"
64
- # password # => "jDACXI5hMPI=\n"
63
+ # password.encrypt!(:symmetric, :password => 'secret') # => "qSg8vOo6QfU=\n"
64
+ # password # => "qSg8vOo6QfU=\n"
65
65
  def encrypt!(*args)
66
66
  encrypted_string = encrypt(*args)
67
- self.encryptor = encrypted_string.encryptor
67
+ self.cipher = encrypted_string.cipher
68
68
 
69
69
  replace(encrypted_string)
70
70
  end
71
71
 
72
72
  # Is this string encrypted? This will return true if the string is the
73
- # result of a call to #encrypt or #encrypt! was previously invoked.
73
+ # result of a call to #encrypt or #encrypt!.
74
74
  #
75
75
  # == Example
76
76
  #
@@ -79,23 +79,31 @@ module PluginAWeek #:nodoc:
79
79
  # password.encrypt! # => "66c85d26dadde7e1db27e15a0776c921e27143bd"
80
80
  # password.encrypted? # => true
81
81
  def encrypted?
82
- !@encryptor.nil?
82
+ !cipher.nil?
83
83
  end
84
84
 
85
- # Decrypts this string. If this is not a string that was previously encrypted,
86
- # the encryption algorithm must be specified in the same way the
87
- # algorithm is specified when encrypting a string.
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
88
  #
89
89
  # == Example
90
90
  #
91
- # password = "jDACXI5hMPI=\n"
92
- # password.decrypt(:symmetric, :password => 'my_key') # => "shhhh"
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"
93
101
  def decrypt(*args)
94
- raise ArgumentError, "An encryption algorithm must be specified since we can't figure it out" if args.empty? && !@encryptor
102
+ raise ArgumentError, 'Cipher cannot be inferred: must specify it as an argument' if args.empty? && !encrypted?
95
103
 
96
- encryptor = args.any? ? encryptor_from_args(*args) : (@encryptor || encryptor_from_args(*args))
97
- encrypted_string = encryptor.decrypt(self)
98
- encrypted_string.encryptor = nil
104
+ cipher = args.empty? && self.cipher || cipher_from_args(*args)
105
+ encrypted_string = cipher.decrypt(self)
106
+ encrypted_string.cipher = nil
99
107
 
100
108
  encrypted_string
101
109
  end
@@ -106,21 +114,21 @@ module PluginAWeek #:nodoc:
106
114
  #
107
115
  # For example,
108
116
  #
109
- # password = "jDACXI5hMPI=\n"
110
- # password.decrypt!(:symmetric, :password => 'my_key') # => "shhhh"
111
- # password # => "shhhh"
117
+ # password = "qSg8vOo6QfU=\n"
118
+ # password.decrypt!(:symmetric, :password => 'secret') # => "shhhh"
119
+ # password # => "shhhh"
112
120
  def decrypt!(*args)
113
121
  value = replace(decrypt(*args))
114
- self.encryptor = nil
122
+ self.cipher = nil
115
123
  value
116
124
  end
117
125
 
118
126
  # Can this string be decrypted? Strings can only be decrypted if they
119
- # have previously been decrypted +and+ the encryption algorithm supports
120
- # decryption. To determine whether or not the encryption algorithm
121
- # supports decryption, see the api for the algorithm's encryptor class.
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.
122
130
  def can_decrypt?
123
- encrypted? && @encryptor.can_decrypt?
131
+ encrypted? && cipher.can_decrypt?
124
132
  end
125
133
 
126
134
  # Tests whether the other object is equal to this one. Encrypted strings
@@ -180,16 +188,17 @@ module PluginAWeek #:nodoc:
180
188
  if encrypted_value.can_decrypt?
181
189
  encrypted_value.decrypt.equals_without_encryption(value)
182
190
  else
183
- # Otherwise encrypt this value based on the encryptor used on the encrypted value
191
+ # Otherwise encrypt this value based on the cipher used on the encrypted value
184
192
  # and test the equality of those strings
185
- encrypted_value.equals_without_encryption(encrypted_value.encryptor.encrypt(value))
193
+ encrypted_value.equals_without_encryption(encrypted_value.cipher.encrypt(value))
186
194
  end
187
195
  end
188
196
 
189
- def encryptor_from_args(*args) #:nodoc:
197
+ # Builds the cipher to use from the given arguments
198
+ def cipher_from_args(*args) #:nodoc:
190
199
  options = args.last.is_a?(Hash) ? args.pop : {}
191
- mode = (args.first || :sha).to_s.gsub(/(?:^|_)(.)/) {$1.upcase}
192
- PluginAWeek::EncryptedStrings.const_get("#{mode}Encryptor").new(options)
200
+ name = (args.first || :sha).to_s.gsub(/(?:^|_)(.)/) {$1.upcase}
201
+ PluginAWeek::EncryptedStrings.const_get("#{name}Cipher").new(options)
193
202
  end
194
203
  end
195
204
  end