require_relative '../data_conversion' module Ccrypto module Java class HMACEngine include TR::CondUtils include DataConversion class HMACEngineError < StandardError; end class SupportedHMACList include InMemoryRecord def initialize #define_search_key(:algo) end end def self.supported_hmac if @supported.nil? @supported = SupportedHMACList.new Ccrypto::Java::DigestEngine.supported.each do |v| begin prov = v.provider_config[:jceProvider] digestAlgo = v.provider_config[:algo_name] if digestAlgo =~ /^SHA-/ digestAlgo = digestAlgo.gsub("-","") end algo = "HMAC#{digestAlgo}" if not_empty?(prov) logger.debug "Initializing HMAC algo '#{algo}' with provider '#{prov}'" javax.crypto.Mac.getInstance(algo, prov) else logger.debug "Initializing HMAC algo '#{algo}' with null provider" javax.crypto.Mac.getInstance(algo) end conf = Ccrypto::HMACConfig.new(v.dup) conf.provider_config = { hmac_algo: algo, jce_provider: prov } @supported.register(conf, { tag_under: :algo, tag_value: digestAlgo }) #@supported[algo] = conf rescue Exception => ex logger.debug "HMAC algo '#{algo}' failed. Error was : #{ex.message}" #logger.error ex.backtrace.join("\n") end end end @supported end class << self alias_method :supported_hmac_configs, :supported_hmac end def self.find_supported_hmac_by_digest(digest) case digest when Symbol, String supported_hmac.find( algo: digest ) when Ccrypto::DigestConfig supported_hmac.select { |hm| hm.digest_config.algo.to_s.downcase == digest.algo.to_s.downcase } else raise HMACEngineException, "Unsupported parameter for digest. Expected Ccrypto::DigestConfig, symbol or string. Got '#{digest.class}'" end end def self.default_hmac_digest_algo primary = find_supported_hmac_by_digest_algo("sha3-256").first if is_empty?(primary) secondary = find_supported_hmac_by_digest_algo("sha256").first if is_empty?(secondary) first = supported_hmac.values.first logger.debug "Both SHA3-256 and SHA256 are not supported. Default to '#{first.inspect}'" first else secondary end else primary end end private def self.logger Ccrypto::Java.logger(:hmac_eng_c) end public def initialize(*args, &block) @config = args.first logger.debug "HMAC Config : #{@config.inspect}" raise HMACEngineException, "HMAC config is expected" if not @config.is_a?(Ccrypto::HMACConfig) raise HMACEngineException, "Signing key is required" if is_empty?(@config.ccrypto_key) raise HMACEngineException, "Ccrypto:SecretKey is required. Given #{@config.ccrypto_key.class}" if not @config.ccrypto_key.is_a?(Ccrypto::SecretKey) begin macAlgo = @config.provider_config[:hmac_algo] prov = @config.provider_config[:jce_provider] if not_empty?(prov) logger.debug "Mac algo : #{macAlgo} with provider '#{prov}'" @hmac = javax.crypto.Mac.getInstance(macAlgo, prov) else logger.debug "Mac algo : #{macAlgo} with null provider" @hmac = javax.crypto.Mac.getInstance(macAlgo) end logger.debug "Initialize the Mac with ccrypto_key" @hmac.init(@config.ccrypto_key.native_key) rescue Exception => ex raise HMACEngineException, ex end end def hmac_update(val) @hmac.update(to_java_bytes(val)) if not_empty?(val) end def hmac_final @hmac.doFinal end def hmac_digest(val, output = :binary) hmac_update(val) res = hmac_final case output when :hex to_hex(res) when :b64 to_b64(res) else res end end private def logger Ccrypto::Java.logger(:hmac_eng) end end end end