lib/symmetric_encryption/symmetric_encryption.rb in symmetric-encryption-3.2 vs lib/symmetric_encryption/symmetric_encryption.rb in symmetric-encryption-3.3

- old
+ new

@@ -12,10 +12,24 @@ # Defaults @@cipher = nil @@secondary_ciphers = [] @@select_cipher = nil + # List of types supported when encrypting or decrypting data + # + # Each type maps to the built-in Ruby types as follows: + # :string => String + # :integer => Integer + # :float => Float + # :decimal => BigDecimal + # :datetime => DateTime + # :time => Time + # :date => Date + # :json => Uses JSON serialization, useful for hashes and arrays + # :yaml => Uses YAML serialization, useful for hashes and arrays + COERCION_TYPES = [:string, :integer, :float, :decimal, :datetime, :time, :date, :boolean, :json, :yaml] + # Set the Primary Symmetric Cipher to be used # # Example: For testing purposes the following test cipher can be used: # # SymmetricEncryption.cipher = SymmetricEncryption::Cipher.new( @@ -56,20 +70,27 @@ def self.secondary_ciphers @@secondary_ciphers end # AES Symmetric Decryption of supplied string - # Returns decrypted string - # Returns nil if the supplied str is nil + # Returns decrypted value + # Returns nil if the supplied value is nil # Returns "" if it is a string and it is empty # # Parameters # str # Encrypted string to decrypt # version # Specify which cipher version to use if no header is present on the # encrypted string + # type [:string|:integer|:float|:decimal|:datetime|:time|:date|:boolean] + # If value is set to something other than :string, then the coercible gem + # will be use to coerce the unencrypted string value into the specified + # type. This assumes that the value was stored using the same type. + # Note: If type is set to something other than :string, it's expected + # that the coercible gem is available in the path. + # Default: :string # # If the supplied string has an encryption header then the cipher matching # the version number in the header will be used to decrypt the string # # When no header is present in the encrypted data, a custom Block/Proc can @@ -82,11 +103,11 @@ # NOTE: #decrypt will _not_ attempt to use a secondary cipher if it fails # to decrypt the current string. This is because in a very small # yet significant number of cases it is possible to decrypt data using # the incorrect key. Clearly the data returned is garbage, but it still # successfully returns a string of data - def self.decrypt(encrypted_and_encoded_string, version=nil) + def self.decrypt(encrypted_and_encoded_string, version=nil, type=:string) raise "Call SymmetricEncryption.load! or SymmetricEncryption.cipher= prior to encrypting or decrypting data" unless @@cipher return encrypted_and_encoded_string if encrypted_and_encoded_string.nil? || (encrypted_and_encoded_string == '') str = encrypted_and_encoded_string.to_s @@ -107,20 +128,20 @@ # Try to force result to UTF-8 encoding, but if it is not valid, force it back to Binary unless decrypted.force_encoding(SymmetricEncryption::UTF8_ENCODING).valid_encoding? decrypted.force_encoding(SymmetricEncryption::BINARY_ENCODING) end end - decrypted + coerce_from_string(decrypted, type) end # AES Symmetric Encryption of supplied string # Returns result as a Base64 encoded string # Returns nil if the supplied str is nil # Returns "" if it is a string and it is empty # # Parameters - # str [String] + # value [Object] # String to be encrypted. If str is not a string, #to_s will be called on it # to convert it to a string # # random_iv [true|false] # Whether the encypted value should use a random IV every time the @@ -143,15 +164,24 @@ # Should only be used for large strings since compression overhead and # the overhead of adding the 'magic' header may exceed any benefits of # compression # Note: Adds a 6 byte header prior to encoding, only if :random_iv is false # Default: false - def self.encrypt(str, random_iv=false, compress=false) + # + # type [:string|:integer|:float|:decimal|:datetime|:time|:date|:boolean] + # Expected data type of the value to encrypt + # Uses the coercible gem to coerce non-string values into string values. + # When type is set to :string (the default), uses #to_s to convert + # non-string values to string values. + # Note: If type is set to something other than :string, it's expected that + # the coercible gem is available in the path. + # Default: :string + def self.encrypt(str, random_iv=false, compress=false, type=:string) raise "Call SymmetricEncryption.load! or SymmetricEncryption.cipher= prior to encrypting or decrypting data" unless @@cipher # Encrypt and then encode the supplied string - @@cipher.encrypt(str, random_iv, compress) + @@cipher.encrypt(coerce_to_string(str, type), random_iv, compress) end # Invokes decrypt # Returns decrypted String # Return nil if it fails to decrypt a String @@ -422,9 +452,66 @@ end # Decrypt Symmetric Keys Cipher.new(config) end + + # Uses coercible gem to coerce values from strings into the target type + # Note: if the type is :string, then the value is returned as is, and the + # coercible gem is not used at all. + def self.coerce_from_string(value, type) + return if value.nil? + case type + when :string + value + when :json + JSON.load(value) + when :yaml + YAML.load(value) + else + coercer = Coercible::Coercer.new + coercion_method = "to_#{type}".to_sym + coercer[String].send(coercion_method, value) + end + end + + # Uses coercible gem to coerce values to strings from the specified type + # Note: if the type is :string, and value is not nil, then #to_s is called + # on the value and the coercible gem is not used at all. + def self.coerce_to_string(value, type) + return if value.nil? + + case type + when :string + value.to_s + when :json + value.to_json + when :yaml + value.to_yaml + else + coercer = Coercible::Coercer.new + coercer[coercion_type(type, value)].to_string(value) + end + end + + # Returns the correct coercion type to use for the specified symbol and value + def self.coercion_type(symbol, value) + if symbol == :boolean + value.class + else + COERCION_TYPE_MAP[symbol] + end + end + + COERCION_TYPE_MAP = { + :string => String, + :integer => Integer, + :float => Float, + :decimal => BigDecimal, + :datetime => DateTime, + :time => Time, + :date => Date + } # With Ruby 1.9 strings have encodings if defined?(Encoding) BINARY_ENCODING = Encoding.find("binary") UTF8_ENCODING = Encoding.find("UTF-8") \ No newline at end of file