Skip to content

Instantly share code, notes, and snippets.

@Python1320
Last active June 19, 2022 21:11
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Python1320/ec447cadb229a86d2e6bdf6db684ef3b to your computer and use it in GitHub Desktop.
Save Python1320/ec447cadb229a86d2e6bdf6db684ef3b to your computer and use it in GitHub Desktop.
HMAC-SHA256 for GMod (no binary modules)
-- bootstrap slow version
function string.tohex(str)
return (str:gsub('.', function (c)
return string.format('%02X', string.byte(c))
end))
end
-- caching fast versions
local t = {}
for i = 0, 255 do
t[string.char(i):tohex()] = string.char(i)
t[string.char(i):tohex():lower()] = string.char(i)
end
function string.fromhex(str)
return (str:gsub('%w%w', t))
end
local t = {}
for i = 0, 255 do
t[string.char(i)] = string.char(i):tohex()
end
function string.tohex(str)
return (str:gsub('.', t))
end
if SERVER then
local ok,hex = pcall(require,'hex')
if not ok or not hex or hex==true then hex = _G.hex end
if hex and hex.decode then
string.tohex = hex.encode
string.fromhex = hex.decode
end
end
-- HMAC-SHA256 for GMod (no binary modules)
-- by Python1320
local TEST = true
local magic_ipad = 0x36
local magic_opad = 0x5c
local SHA256 = util.SHA256
local SHA1 = util.SHA1
local bit = bit
local SHA256_BLOCK_SIZE = 64
local k_ipad_t, k_opad_t = {}, {}
local function hmac_sha256(text, key)
if #key > SHA256_BLOCK_SIZE then
key = SHA256(key):fromhex()
end
for i = 1, SHA256_BLOCK_SIZE do
local key_byte = key:byte(i) or 0x00 -- implicit padding
k_ipad_t[i] = string.char(bit.bxor(magic_ipad, key_byte))
k_opad_t[i] = string.char(bit.bxor(magic_opad, key_byte))
end
local k_ipad = table.concat(k_ipad_t)
local k_opad = table.concat(k_opad_t)
local inner_hash = SHA256(k_ipad .. text):fromhex()
local outer_hash = SHA256(k_opad .. inner_hash)
return outer_hash
end
local function hmac_sha1(text, key)
if #key > SHA256_BLOCK_SIZE then
key = SHA1(key):fromhex()
end
for i = 1, SHA256_BLOCK_SIZE do
local key_byte = key:byte(i) or 0x00 -- implicit padding
k_ipad_t[i] = string.char(bit.bxor(magic_ipad, key_byte))
k_opad_t[i] = string.char(bit.bxor(magic_opad, key_byte))
end
local k_ipad = table.concat(k_ipad_t)
local k_opad = table.concat(k_opad_t)
local inner_hash = SHA1(k_ipad .. text):fromhex()
local outer_hash = SHA1(k_opad .. inner_hash)
return outer_hash
end
if TEST then
--TODO: https://datatracker.ietf.org/doc/html/rfc2202#section-2
local hmac = hmac_sha256
local test_vectors = { -- https://datatracker.ietf.org/doc/html/rfc4231#section-4.4
{"heck", "heeeck", "hecking heck", "f41d6ea9023fab5b9fb92b94761d9e2289c7ab3ddc8eb4e450b449bded3c5c11"},
{"Test with a key shorter than the length of the HMAC output", "\x4a\x65\x66\x65", "\x77\x68\x61\x74\x20\x64\x6f\x20\x79\x61\x20\x77\x61\x6e\x74\x20\x66" .. "\x6f\x72\x20\x6e\x6f\x74\x68\x69\x6e\x67\x3f", "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843"},
{[[Test with a combined length of key and data that is larger than 64
bytes (= block-size of SHA-224 and SHA-256). ]], "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" .. "\xaa\xaa\xaa", "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" .. "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd" .. "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd", "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe"},
{[[Test with a combined length of key and data that is larger than 64
bytes (= block-size of SHA-224 and SHA-256).]], "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11" .. "\x12\x13\x14\x15\x16\x17\x18\x19", "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" .. "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd" .. "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd", "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b"},
{[[Test with a key larger than 128 bytes (= block-size of SHA-384 and
SHA-512). ]], "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" .. "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" .. "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" .. "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" .. "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" .. "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" .. "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" .. "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", "\x54\x65\x73\x74\x20\x55\x73\x69\x6e\x67\x20\x4c\x61\x72\x67\x65\x72" .. "\x20\x54\x68\x61\x6e\x20\x42\x6c\x6f\x63\x6b\x2d\x53\x69\x7a\x65\x20" .. "\x4b\x65\x79\x20\x2d\x20\x48\x61\x73\x68\x20\x4b\x65\x79\x20\x46\x69" .. "\x72\x73\x74", "60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54"},
{[[Test with a key and data that is larger than 128 bytes (=
block-size of SHA-384 and SHA-512). ]], "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" .. "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" .. "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" .. "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" .. "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" .. "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" .. "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" .. "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", "\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20\x74\x65\x73\x74\x20\x75\x73" .. "\x69\x6e\x67\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20\x74\x68\x61\x6e" .. "\x20\x62\x6c\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x6b\x65\x79\x20\x61" .. "\x6e\x64\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20\x74\x68\x61\x6e\x20" .. "\x62\x6c\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x64\x61\x74\x61\x2e\x20" .. "\x54\x68\x65\x20\x6b\x65\x79\x20\x6e\x65\x65\x64\x73\x20\x74\x6f\x20" .. "\x62\x65\x20\x68\x61\x73\x68\x65\x64\x20\x62\x65\x66\x6f\x72\x65\x20" .. "\x62\x65\x69\x6e\x67\x20\x75\x73\x65\x64\x20\x62\x79\x20\x74\x68\x65" .. "\x20\x48\x4d\x41\x43\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x2e", "9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2"}
}
for testid, test in pairs(test_vectors) do
local test_comment, key, text, res = unpack(test)
assert(hmac(text, key) == res, "test failed: " .. testid .. ': ' .. test_comment)
end
end
util.HMAC_SHA256 = hmac_sha256
util.HMAC_SHA1 = hmac_sha1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment