hmac.rb
112 lines
| 2.7 KiB
| text/x-ruby
|
RubyLexer
|
r2376 | # Copyright (C) 2001 Daiki Ueno <ueno@unixuser.org> | ||
# This library is distributed under the terms of the Ruby license. | ||||
# This module provides common interface to HMAC engines. | ||||
# HMAC standard is documented in RFC 2104: | ||||
# | ||||
# H. Krawczyk et al., "HMAC: Keyed-Hashing for Message Authentication", | ||||
# RFC 2104, February 1997 | ||||
# | ||||
# These APIs are inspired by JCE 1.2's javax.crypto.Mac interface. | ||||
# | ||||
# <URL:http://java.sun.com/security/JCE1.2/spec/apidoc/javax/crypto/Mac.html> | ||||
module HMAC | ||||
class Base | ||||
def initialize(algorithm, block_size, output_length, key) | ||||
@algorithm = algorithm | ||||
@block_size = block_size | ||||
@output_length = output_length | ||||
@status = STATUS_UNDEFINED | ||||
@key_xor_ipad = '' | ||||
@key_xor_opad = '' | ||||
set_key(key) unless key.nil? | ||||
end | ||||
private | ||||
def check_status | ||||
unless @status == STATUS_INITIALIZED | ||||
raise RuntimeError, | ||||
"The underlying hash algorithm has not yet been initialized." | ||||
end | ||||
end | ||||
public | ||||
def set_key(key) | ||||
# If key is longer than the block size, apply hash function | ||||
# to key and use the result as a real key. | ||||
key = @algorithm.digest(key) if key.size > @block_size | ||||
key_xor_ipad = "\x36" * @block_size | ||||
key_xor_opad = "\x5C" * @block_size | ||||
for i in 0 .. key.size - 1 | ||||
key_xor_ipad[i] ^= key[i] | ||||
key_xor_opad[i] ^= key[i] | ||||
end | ||||
@key_xor_ipad = key_xor_ipad | ||||
@key_xor_opad = key_xor_opad | ||||
@md = @algorithm.new | ||||
@status = STATUS_INITIALIZED | ||||
end | ||||
def reset_key | ||||
@key_xor_ipad.gsub!(/./, '?') | ||||
@key_xor_opad.gsub!(/./, '?') | ||||
@key_xor_ipad[0..-1] = '' | ||||
@key_xor_opad[0..-1] = '' | ||||
@status = STATUS_UNDEFINED | ||||
end | ||||
def update(text) | ||||
check_status | ||||
# perform inner H | ||||
md = @algorithm.new | ||||
md.update(@key_xor_ipad) | ||||
md.update(text) | ||||
str = md.digest | ||||
# perform outer H | ||||
md = @algorithm.new | ||||
md.update(@key_xor_opad) | ||||
md.update(str) | ||||
@md = md | ||||
end | ||||
alias << update | ||||
def digest | ||||
check_status | ||||
@md.digest | ||||
end | ||||
def hexdigest | ||||
check_status | ||||
@md.hexdigest | ||||
end | ||||
alias to_s hexdigest | ||||
# These two class methods below are safer than using above | ||||
# instance methods combinatorially because an instance will have | ||||
# held a key even if it's no longer in use. | ||||
def Base.digest(key, text) | ||||
begin | ||||
hmac = self.new(key) | ||||
hmac.update(text) | ||||
hmac.digest | ||||
ensure | ||||
hmac.reset_key | ||||
end | ||||
end | ||||
def Base.hexdigest(key, text) | ||||
begin | ||||
hmac = self.new(key) | ||||
hmac.update(text) | ||||
hmac.hexdigest | ||||
ensure | ||||
hmac.reset_key | ||||
end | ||||
end | ||||
private_class_method :new, :digest, :hexdigest | ||||
end | ||||
STATUS_UNDEFINED, STATUS_INITIALIZED = 0, 1 | ||||
end | ||||