-------------------------------------------------------------------------------------------------------------------------- -- sha2.lua (Minimal version for HMAC-SHA256) -- AUTHOR: Egor Skriptunoff -- LICENSE: MIT (the same license as Lua itself) -- URL: https://github.com/Egor-Skriptunoff/pure_lua_SHA -- Only contains sha256 and hmac functions for use with hmac.lua -- Optimized for LuaJIT 5.1 -------------------------------------------------------------------------------------------------------------------------- local unpack, table_concat, byte, char, string_rep, sub, gsub, string_format, floor = table.unpack or unpack, table.concat, string.byte, string.char, string.rep, string.sub, string.gsub, string.format, math.floor -------------------------------------------------------------------------------- -- BRANCH DETECTION (LuaJIT) -------------------------------------------------------------------------------- local is_LuaJIT = jit and jit.version local branch = is_LuaJIT and "LJ" or "EMUL" -------------------------------------------------------------------------------- -- BITWISE OPERATORS (LJ branch) -------------------------------------------------------------------------------- local AND, OR, XOR, SHL, SHR, ROL, ROR, NOT, NORM, HEX, XOR_BYTE if branch == "LJ" then local b = bit or bit32 local library_name = b and (b.bit32 and "bit32" or "bit") AND = b.band OR = b.bor XOR = b.bxor SHL = b.lshift SHR = b.rshift ROL = b.rol or b.lrotate ROR = b.ror or b.rrotate NOT = b.bnot NORM = b.tobit HEX = b.tohex XOR_BYTE = XOR else error("This minimal sha2.lua only supports LuaJIT with bit library") end -------------------------------------------------------------------------------- -- DATA TABLES -------------------------------------------------------------------------------- local sha2_K_hi, sha2_H_hi = {}, {} local sha2_H_ext256 = {[224] = {}, [256] = sha2_H_hi} local common_W = {} local K_lo_modulo = 4294967296 -------------------------------------------------------------------------------- -- MAGIC NUMBERS CALCULATOR (for SHA-256 constants) -------------------------------------------------------------------------------- do local function mul(src1, src2, factor, result_length) local result, carry, value, weight = {}, 0.0, 0.0, 1.0 for j = 1, result_length do for k = math.max(1, j + 1 - #src2), math.min(j, #src1) do carry = carry + factor * src1[k] * src2[j + 1 - k] end local digit = carry % 2^24 result[j] = floor(digit) carry = (carry - digit) / 2^24 value = value + digit * weight weight = weight * 2^24 end return result, value end local idx, step, p, one, sqrt_hi = 0, {4, 1, 2, -2, 2}, 4, {1}, sha2_H_hi repeat p = p + step[p % 6] local d = 1 repeat d = d + step[d % 6] if d*d > p then local root = p^(1/3) local R = root * 2^40 R = mul({R - R % 1}, one, 1.0, 2) local _, delta = mul(R, mul(R, R, 1.0, 4), -1.0, 4) local hi = R[2] % 65536 * 65536 + floor(R[1] / 256) local lo = R[1] % 256 * 16777216 + floor(delta * (2^-56 / 3) * root / p) if idx < 8 then root = p^(1/2) R = root * 2^40 R = mul({R - R % 1}, one, 1.0, 2) _, delta = mul(R, R, -1.0, 2) local hi = R[2] % 65536 * 65536 + floor(R[1] / 256) local lo = R[1] % 256 * 16777216 + floor(delta * 2^-17 / root) local idx = idx % 8 + 1 sha2_H_ext256[224][idx] = lo sqrt_hi[idx] = hi end idx = idx + 1 sha2_K_hi[idx] = hi break end until p % d == 0 until idx > 63 end -------------------------------------------------------------------------------- -- SHA256 FEED FUNCTION (LJ branch) -------------------------------------------------------------------------------- function sha256_feed_64(H, str, offs, size) local W, K = common_W, sha2_K_hi for pos = offs, offs + size - 1, 64 do for j = 1, 16 do pos = pos + 4 local a, b, c, d = byte(str, pos - 3, pos) W[j] = OR(SHL(a, 24), SHL(b, 16), SHL(c, 8), d) end for j = 17, 64 do local a, b = W[j-15], W[j-2] W[j] = NORM( NORM( XOR(ROR(a, 7), ROL(a, 14), SHR(a, 3)) + XOR(ROL(b, 15), ROL(b, 13), SHR(b, 10)) ) + NORM( W[j-7] + W[j-16] ) ) end local a, b, c, d, e, f, g, h = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] for j = 1, 64, 8 do local z = NORM( XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + XOR(g, AND(e, XOR(f, g))) + (K[j] + W[j] + h) ) h, g, f, e = g, f, e, NORM(d + z) d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) z = NORM( XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + XOR(g, AND(e, XOR(f, g))) + (K[j+1] + W[j+1] + h) ) h, g, f, e = g, f, e, NORM(d + z) d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) z = NORM( XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + XOR(g, AND(e, XOR(f, g))) + (K[j+2] + W[j+2] + h) ) h, g, f, e = g, f, e, NORM(d + z) d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) z = NORM( XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + XOR(g, AND(e, XOR(f, g))) + (K[j+3] + W[j+3] + h) ) h, g, f, e = g, f, e, NORM(d + z) d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) z = NORM( XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + XOR(g, AND(e, XOR(f, g))) + (K[j+4] + W[j+4] + h) ) h, g, f, e = g, f, e, NORM(d + z) d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) z = NORM( XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + XOR(g, AND(e, XOR(f, g))) + (K[j+5] + W[j+5] + h) ) h, g, f, e = g, f, e, NORM(d + z) d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) z = NORM( XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + XOR(g, AND(e, XOR(f, g))) + (K[j+6] + W[j+6] + h) ) h, g, f, e = g, f, e, NORM(d + z) d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) z = NORM( XOR(ROR(e, 6), ROR(e, 11), ROL(e, 7)) + XOR(g, AND(e, XOR(f, g))) + (K[j+7] + W[j+7] + h) ) h, g, f, e = g, f, e, NORM(d + z) d, c, b, a = c, b, a, NORM( XOR(AND(a, XOR(b, c)), AND(b, c)) + XOR(ROR(a, 2), ROR(a, 13), ROL(a, 10)) + z ) end H[1], H[2], H[3], H[4] = NORM(a + H[1]), NORM(b + H[2]), NORM(c + H[3]), NORM(d + H[4]) H[5], H[6], H[7], H[8] = NORM(e + H[5]), NORM(f + H[6]), NORM(g + H[7]), NORM(h + H[8]) end end -------------------------------------------------------------------------------- -- MAIN FUNCTIONS -------------------------------------------------------------------------------- local function sha256ext(width, message) local H, length, tail = {unpack(sha2_H_ext256[width])}, 0.0, "" local function partial(message_part) if message_part then if tail then length = length + #message_part local offs = 0 if tail ~= "" and #tail + #message_part >= 64 then offs = 64 - #tail sha256_feed_64(H, tail..sub(message_part, 1, offs), 0, 64) tail = "" end local size = #message_part - offs local size_tail = size % 64 sha256_feed_64(H, message_part, offs, size - size_tail) tail = tail..sub(message_part, #message_part + 1 - size_tail) return partial else error("Adding more chunks is not allowed after receiving the result", 2) end else if tail then local final_blocks = {tail, "\128", string_rep("\0", (-9 - length) % 64 + 1)} tail = nil length = length * (8 / 256^7) for j = 4, 10 do length = length % 1 * 256 final_blocks[j] = char(floor(length)) end final_blocks = table_concat(final_blocks) sha256_feed_64(H, final_blocks, 0, #final_blocks) local max_reg = width / 32 for j = 1, max_reg do H[j] = HEX(H[j]) end H = table_concat(H, "", 1, max_reg) end return H end end if message then return partial(message)() else return partial end end -------------------------------------------------------------------------------- -- HELPER FUNCTIONS -------------------------------------------------------------------------------- local hex_to_bin do function hex_to_bin(hex_string) return (gsub(hex_string, "%x%x", function (hh) return char(tonumber(hh, 16)) end )) end end local function pad_and_xor(str, result_length, byte_for_xor) return gsub(str, ".", function(c) return char(XOR_BYTE(byte(c), byte_for_xor)) end )..string_rep(char(byte_for_xor), result_length - #str) end local block_size_for_HMAC ---@return string local function hmac(hash_func, key, message) local block_size = block_size_for_HMAC[hash_func] if not block_size then error("Unknown hash function", 2) end if #key > block_size then key = hex_to_bin(hash_func(key)) end local append = hash_func()(pad_and_xor(key, block_size, 0x36)) local result local function partial(message_part) if not message_part then result = result or hash_func(pad_and_xor(key, block_size, 0x5C)..hex_to_bin(append())) return result elseif result then error("Adding more chunks is not allowed after receiving the result", 2) else append(message_part) return partial end end if message then return partial(message)() else return partial end end -------------------------------------------------------------------------------- -- MODULE EXPORT -------------------------------------------------------------------------------- local sha = { sha256 = function (message) return sha256ext(256, message) end, hmac = hmac, hex_to_bin = hex_to_bin, } block_size_for_HMAC = { [sha.sha256] = 64, } return sha