require 'dionysus' require 'digest' ## # Convenience methods for the Digest module. # # require 'dionysus/digest' # # The Digest::DEFAULT_DIGESTS are automatically registered, if they # exist. You can register additional digests with Digest.register_digest. # The given class must give the digest with the digest(string). # # TODO add digest detection -- by length and by proc on the digests hash module Digest DEFAULT_DIGESTS = [:md5, :sha1, :sha2, :sha256, :sha384, :sha512] @digests = {} ## # Register a digest. Raises an error if the interpreted class doesn't exist. # It will interpret the klass as Digest::SYM if it's nil, # and it will run the digest method on the klass to determine the # digests bit length if bits is nil. # # This will register :my_digest and automatically determine the bit # length by executing the class's digest method on the string # '1': # # Digest.register_digest!( :my_digest, :klass => MyDigestClass ) # # Options: # [klass] The digest class (also can be an arbitrary object). Default: # Digest::#{sym.to_s.upcase} # [bit_length] The bit length of the digest. Default: calculated by # running the digest on the string '1'. # [method] The calculation method for the digest. Default: # :digest def self.register_digest!( sym, options = {} ) options = options.with_indifferent_access options[:method] ||= :digest options[:klass] ||= "Digest::#{sym.to_s.upcase}".constantize options[:bit_length] ||= options[:klass].send(options[:method], '1').length * 8 @digests[sym.to_sym] = options end ## # Register a digest. Returns nil if an error occurs. def self.register_digest( sym, options = {} ) self.register_digest!(sym, options) rescue LoadError nil end ## # The hash of registered digests. def self.digests @digests end ## # The available digests. def self.available_digests self.digests.keys end ## # The lengths of the registered digests in the given encoding. def self.digest_lengths( encoding = :binary ) if encoding.is_a?(Symbol) bits_per_char = case encoding when :binary then 8 when :base64 then 6 when :hex, :hexidecimal then 4 when :bit then 1 else raise ArgumentError, "Invalid encoding" end elsif encoding.is_a?(Integer) and encoding > 0 bits_per_char = encoding else raise ArgumentError, "Invalid encoding" end Hash[ self.digests.collect { |dig, info| [dig, info[:bit_length] / bits_per_char] } ] end ## # Calculate the given digest of the given string. # # Examples: # # Digest.digest(:sha512, 'foobar') #=> binary digest # Digest.digest(Digest::SHA512, 'foobar') #=> binary digest def self.digest( sym_or_klass, str ) if sym_or_klass.is_a?(Class) sym_or_klass else Digest.const_get(sym_or_klass.to_s.upcase) end.digest(str) end end # Register some default digests Digest::DEFAULT_DIGESTS.each do |dig| Digest.register_digest(dig) end