module EncryptedStrings # Indicates no password was specified for the symmetric cipher class NoPasswordError < StandardError end # Symmetric encryption uses a specific algorithm and password to encrypt # the string. As long as the algorithm and password are known, the string # can be decrypted. # # Source: http://support.microsoft.com/kb/246071 # # == Encrypting # # To encrypt a string using a symmetric cipher, the algorithm and password # must be specified. You can define the defaults for these values like so: # # EncryptedStrings::SymmetricCipher.default_algorithm = 'des-ecb' # EncryptedStrings::SymmetricCipher.default_password = 'secret' # # If these configuration options are not passed in to #encrypt, then the # default values will be used. You can override the default values like so: # # password = 'shhhh' # password.encrypt(:symmetric, :algorithm => 'des-ecb', :password => 'secret') # => "S/sEkViX3v4=\n" # # An exception will be raised if no password is specified. # # == Decrypting # # To decrypt a string using an symmetric cipher, the algorithm and password # must be specified. Defaults for these values can be defined as show above. # # If these configuration options are not passed in to #decrypt, then the # default values will be used. You can override the default values like so: # # password = "S/sEkViX3v4=\n" # password.decrypt(:symmetric, :algorithm => 'des-ecb', :password => 'secret') # => "shhhh" # # An exception will be raised if no password is specified. class SymmetricCipher < Cipher class << self # The default algorithm to use for encryption. Default is DES-EDE3-CBC. attr_accessor :default_algorithm # The default password to use for generating the key and initialization # vector. Default is nil. attr_accessor :default_password end # Set default values @default_algorithm = 'DES-EDE3-CBC' # The algorithm to use for encryption/decryption attr_accessor :algorithm # The password that generates the key/initialization vector for the # algorithm attr_accessor :password # Creates a new cipher that uses a symmetric encryption strategy. # # Configuration options: # * :algorithm - The algorithm to use for generating the encrypted string # * :password - The secret value to use for generating the # key/initialization vector for the algorithm def initialize(options = {}) invalid_options = options.keys - [:algorithm, :password] raise ArgumentError, "Unknown key(s): #{invalid_options.join(", ")}" unless invalid_options.empty? options = { :algorithm => SymmetricCipher.default_algorithm, :password => SymmetricCipher.default_password }.merge(options) self.algorithm = options[:algorithm] self.password = options[:password] raise NoPasswordError if password.nil? super() end # Decrypts the current string using the current key and algorithm specified def decrypt(data) cipher = build_cipher(:decrypt) cipher.update(data.unpack('m')[0]) + cipher.final end # Encrypts the current string using the current key and algorithm specified def encrypt(data) cipher = build_cipher(:encrypt) [cipher.update(data) + cipher.final].pack('m') end private def build_cipher(type) #:nodoc: cipher = OpenSSL::Cipher::Cipher.new(algorithm).send(type) cipher.pkcs5_keyivgen(password) cipher end end end