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