# encoding: utf-8 module DynamicImage # = DynamicImage Digest Verifier # # ==== Usage # # verifier = DynamicImage::DigestVerifier.new("super secret!") # # digest = verifier.generate("foo") # # digest.verify("foo", digest) # # => true # digest.verify("bar", digest) # # => raises DynamicImage::Errors::InvalidSignature # # Credit where credit is due: adapted and simplified from # +ActiveSupport::MessageVerifier+, since we don't need to handle # arbitrary data structures and ship the serialized data to the client. class DigestVerifier def initialize(secret, options = {}) @secret = secret @digest = options[:digest] || 'SHA1' end # Generates a digest for a string. def generate(data) generate_digest(data) end # Verifies that digest is valid for data. # Raises a +DynamicImage::Errors::InvalidSignature+ error if not. def verify(data, digest) if valid_digest?(data, digest) true else raise DynamicImage::Errors::InvalidSignature end end private def secure_compare(a, b) return false unless a.bytesize == b.bytesize l = a.unpack "C#{a.bytesize}" res = 0 b.each_byte { |byte| res |= byte ^ l.shift } res == 0 end def generate_digest(data) require 'openssl' unless defined?(OpenSSL) OpenSSL::HMAC.hexdigest(OpenSSL::Digest.const_get(@digest).new, @secret, data) end def valid_digest?(data, digest) data.present? && digest.present? && secure_compare(digest, generate_digest(data)) end end end