--HMAC implementation
--http://tools.ietf.org/html/rfc2104
--http://en.wikipedia.org/wiki/HMAC
local ffi = require'ffi'
local bit = require'bit'
local function string_xor(s1, s2)
assert(#s1 == #s2, 'strings must be of equal length')
local buf = ffi.new('uint8_t[?]', #s1)
for i=1,#s1 do
buf[i-1] = bit.bxor(s1:byte(i,i), s2:byte(i,i))
end
return ffi.string(buf, #s1)
end
--any hash function works, md5, sha256, etc.
--blocksize is that of the underlying hash function (64 for MD5 and SHA-256, 128 for SHA-384 and SHA-512)
local function compute(key, message, hash, blocksize, opad, ipad)
if #key > blocksize then
key = hash(key) --keys longer than blocksize are shortened
end
key = key .. string.rep('\0', blocksize - #key) --keys shorter than blocksize are zero-padded
opad = opad or string_xor(key, string.rep(string.char(0x5c), blocksize))
ipad = ipad or string_xor(key, string.rep(string.char(0x36), blocksize))
return hash(opad .. hash(ipad .. message)), opad, ipad --opad and ipad can be cached for the same key
end
local function new(hash, blocksize)
return function(message, key)
return (compute(key, message, hash, blocksize))
end
end
local glue = require'glue'
return glue.autoload({
new = new,
compute = compute,
}, {
md5 = 'hmac_md5',
sha256 = 'hmac_sha2',
sha384 = 'hmac_sha2',
sha512 = 'hmac_sha2',
})