encrypted_strings 0.1.1 → 0.2.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,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