Skip to content

Instantly share code, notes, and snippets.

@Retinalogic
Created February 26, 2023 21:43
Show Gist options
  • Save Retinalogic/36b1d62af63a122da264ac78f3128a63 to your computer and use it in GitHub Desktop.
Save Retinalogic/36b1d62af63a122da264ac78f3128a63 to your computer and use it in GitHub Desktop.
--[=[------------------------------------------------------------------------------------------------------------------------
-- HashLib by Egor Skriptunoff, boatbomber, and howmanysmall
Documentation here: https://devforum.roblox.com/t/open-source-hashlib/416732/1
--------------------------------------------------------------------------------------------------------------------------
Module was originally written by Egor Skriptunoff and distributed under an MIT license.
It can be found here: https://github.com/Egor-Skriptunoff/pure_lua_SHA/blob/master/sha2.lua
That version was around 3000 lines long, and supported Lua versions 5.1, 5.2, 5.3, and 5.4, and LuaJIT.
Although that is super cool, Roblox only uses Lua 5.1, so that was extreme overkill.
I, boatbomber, worked to port it to Roblox in a way that doesn't overcomplicate it with support of unreachable
cases. Then, howmanysmall did some final optimizations that really squeeze out all the performance possible.
It's gotten stupid fast, thanks to her!
After quite a bit of work and benchmarking, this is what we were left with.
Enjoy!
--------------------------------------------------------------------------------------------------------------------------
DESCRIPTION:
This module contains functions to calculate SHA digest:
MD5, SHA-1,
SHA-224, SHA-256, SHA-512/224, SHA-512/256, SHA-384, SHA-512,
SHA3-224, SHA3-256, SHA3-384, SHA3-512, SHAKE128, SHAKE256,
HMAC
Additionally, it has a few extra utility functions:
hex_to_bin
base64_to_bin
bin_to_base64
Written in pure Lua.
USAGE:
Input data should be a string
Result (SHA digest) is returned in hexadecimal representation as a string of lowercase hex digits.
Simplest usage example:
local HashLib = require(script.HashLib)
local your_hash = HashLib.sha256("your string")
API:
HashLib.md5
HashLib.sha1
SHA2 hash functions:
HashLib.sha224
HashLib.sha256
HashLib.sha512_224
HashLib.sha512_256
HashLib.sha384
HashLib.sha512
SHA3 hash functions:
HashLib.sha3_224
HashLib.sha3_256
HashLib.sha3_384
HashLib.sha3_512
HashLib.shake128
HashLib.shake256
Misc utilities:
HashLib.hmac (Applicable to any hash function from this module except SHAKE*)
HashLib.hex_to_bin
HashLib.base64_to_bin
HashLib.bin_to_base64
--]=]---------------------------------------------------------------------------
local Base64 = {}
-- @original: https://gist.github.com/Reselim/40d62b17d138cc74335a1b0709e19ce2
local Alphabet = {}
local Indexes = {}
-- A-Z
for Index = 65, 90 do
table.insert(Alphabet, Index)
end
-- a-z
for Index = 97, 122 do
table.insert(Alphabet, Index)
end
-- 0-9
for Index = 48, 57 do
table.insert(Alphabet, Index)
end
table.insert(Alphabet, 43) -- +
table.insert(Alphabet, 47) -- /
for Index, Character in ipairs(Alphabet) do
Indexes[Character] = Index
end
local Base64 = {}
local bit32_rshift = bit32.rshift
local bit32_lshift = bit32.lshift
local bit32_band = bit32.band
--[[**
Encodes a string in Base64.
@param [t:string] Input The input string to encode.
@returns [t:string] The string encoded in Base64.
**--]]
function Base64.Encode(Input)
local Output = {}
local Length = 0
for Index = 1, #Input, 3 do
local C1, C2, C3 = string.byte(Input, Index, Index + 2)
local A = bit32_rshift(C1, 2)
local B = bit32_lshift(bit32_band(C1, 3), 4) + bit32_rshift(C2 or 0, 4)
local C = bit32_lshift(bit32_band(C2 or 0, 15), 2) + bit32_rshift(C3 or 0, 6)
local D = bit32_band(C3 or 0, 63)
Length = Length + 1
Output[Length] = Alphabet[A + 1]
Length = Length + 1
Output[Length] = Alphabet[B + 1]
Length = Length + 1
Output[Length] = C2 and Alphabet[C + 1] or 61
Length = Length + 1
Output[Length] = C3 and Alphabet[D + 1] or 61
end
local NewOutput = {}
local NewLength = 0
local IndexAdd4096Sub1
for Index = 1, Length, 4096 do
NewLength = NewLength + 1
IndexAdd4096Sub1 = Index + 4096 - 1
NewOutput[NewLength] = string.char(table.unpack(
Output,
Index,
IndexAdd4096Sub1 > Length and Length or IndexAdd4096Sub1
))
end
return table.concat(NewOutput)
end
--[[**
Decodes a string from Base64.
@param [t:string] Input The input string to decode.
@returns [t:string] The newly decoded string.
**--]]
function Base64.Decode(Input)
local Output = {}
local Length = 0
for Index = 1, #Input, 4 do
local C1, C2, C3, C4 = string.byte(Input, Index, Index + 3)
local I1 = Indexes[C1] - 1
local I2 = Indexes[C2] - 1
local I3 = (Indexes[C3] or 1) - 1
local I4 = (Indexes[C4] or 1) - 1
local A = bit32_lshift(I1, 2) + bit32_rshift(I2, 4)
local B = bit32_lshift(bit32_band(I2, 15), 4) + bit32_rshift(I3, 2)
local C = bit32_lshift(bit32_band(I3, 3), 6) + I4
Length = Length + 1
Output[Length] = A
if C3 ~= 61 then
Length = Length + 1
Output[Length] = B
end
if C4 ~= 61 then
Length = Length + 1
Output[Length] = C
end
end
local NewOutput = {}
local NewLength = 0
local IndexAdd4096Sub1
for Index = 1, Length, 4096 do
NewLength = NewLength + 1
IndexAdd4096Sub1 = Index + 4096 - 1
NewOutput[NewLength] = string.char(table.unpack(
Output,
Index,
IndexAdd4096Sub1 > Length and Length or IndexAdd4096Sub1
))
end
return table.concat(NewOutput)
end
--------------------------------------------------------------------------------
-- LOCALIZATION FOR VM OPTIMIZATIONS
--------------------------------------------------------------------------------
local ipairs = ipairs
--------------------------------------------------------------------------------
-- 32-BIT BITWISE FUNCTIONS
--------------------------------------------------------------------------------
-- Only low 32 bits of function arguments matter, high bits are ignored
-- The result of all functions (except HEX) is an integer inside "correct range":
-- for "bit" library: (-TWO_POW_31)..(TWO_POW_31-1)
-- for "bit32" library: 0..(TWO_POW_32-1)
local bit32_band = bit32.band -- 2 arguments
local bit32_bor = bit32.bor -- 2 arguments
local bit32_bxor = bit32.bxor -- 2..5 arguments
local bit32_lshift = bit32.lshift -- second argument is integer 0..31
local bit32_rshift = bit32.rshift -- second argument is integer 0..31
local bit32_lrotate = bit32.lrotate -- second argument is integer 0..31
local bit32_rrotate = bit32.rrotate -- second argument is integer 0..31
--------------------------------------------------------------------------------
-- CREATING OPTIMIZED INNER LOOP
--------------------------------------------------------------------------------
-- Arrays of SHA2 "magic numbers" (in "INT64" and "FFI" branches "*_lo" arrays contain 64-bit values)
local sha2_K_lo, sha2_K_hi, sha2_H_lo, sha2_H_hi, sha3_RC_lo, sha3_RC_hi = {}, {}, {}, {}, {}, {}
local sha2_H_ext256 = {
[224] = {};
[256] = sha2_H_hi;
}
local sha2_H_ext512_lo, sha2_H_ext512_hi = {
[384] = {};
[512] = sha2_H_lo;
}, {
[384] = {};
[512] = sha2_H_hi;
}
local md5_K, md5_sha1_H = {}, {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0}
local md5_next_shift = {0, 0, 0, 0, 0, 0, 0, 0, 28, 25, 26, 27, 0, 0, 10, 9, 11, 12, 0, 15, 16, 17, 18, 0, 20, 22, 23, 21}
local HEX64, XOR64A5, lanes_index_base -- defined only for branches that internally use 64-bit integers: "INT64" and "FFI"
local common_W = {} -- temporary table shared between all calculations (to avoid creating new temporary table every time)
local K_lo_modulo, hi_factor, hi_factor_keccak = 4294967296, 0, 0
local TWO_POW_NEG_56 = 2 ^ -56
local TWO_POW_NEG_17 = 2 ^ -17
local TWO_POW_2 = 2 ^ 2
local TWO_POW_3 = 2 ^ 3
local TWO_POW_4 = 2 ^ 4
local TWO_POW_5 = 2 ^ 5
local TWO_POW_6 = 2 ^ 6
local TWO_POW_7 = 2 ^ 7
local TWO_POW_8 = 2 ^ 8
local TWO_POW_9 = 2 ^ 9
local TWO_POW_10 = 2 ^ 10
local TWO_POW_11 = 2 ^ 11
local TWO_POW_12 = 2 ^ 12
local TWO_POW_13 = 2 ^ 13
local TWO_POW_14 = 2 ^ 14
local TWO_POW_15 = 2 ^ 15
local TWO_POW_16 = 2 ^ 16
local TWO_POW_17 = 2 ^ 17
local TWO_POW_18 = 2 ^ 18
local TWO_POW_19 = 2 ^ 19
local TWO_POW_20 = 2 ^ 20
local TWO_POW_21 = 2 ^ 21
local TWO_POW_22 = 2 ^ 22
local TWO_POW_23 = 2 ^ 23
local TWO_POW_24 = 2 ^ 24
local TWO_POW_25 = 2 ^ 25
local TWO_POW_26 = 2 ^ 26
local TWO_POW_27 = 2 ^ 27
local TWO_POW_28 = 2 ^ 28
local TWO_POW_29 = 2 ^ 29
local TWO_POW_30 = 2 ^ 30
local TWO_POW_31 = 2 ^ 31
local TWO_POW_32 = 2 ^ 32
local TWO_POW_40 = 2 ^ 40
local TWO56_POW_7 = 256 ^ 7
-- Implementation for Lua 5.1/5.2 (with or without bitwise library available)
local function sha256_feed_64(H, str, offs, size)
-- offs >= 0, size >= 0, size is multiple of 64
local W, K = common_W, sha2_K_hi
local h1, h2, h3, h4, h5, h6, h7, h8 = H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8]
for pos = offs, offs + size - 1, 64 do
for j = 1, 16 do
pos = pos + 4
local a, b, c, d = string.byte(str, pos - 3, pos)
W[j] = ((a * 256 + b) * 256 + c) * 256 + d
end
for j = 17, 64 do
local a, b = W[j - 15], W[j - 2]
W[j] = bit32_bxor(bit32_rrotate(a, 7), bit32_lrotate(a, 14), bit32_rshift(a, 3)) + bit32_bxor(bit32_lrotate(b, 15), bit32_lrotate(b, 13), bit32_rshift(b, 10)) + W[j - 7] + W[j - 16]
end
local a, b, c, d, e, f, g, h = h1, h2, h3, h4, h5, h6, h7, h8
for j = 1, 64 do
local z = bit32_bxor(bit32_rrotate(e, 6), bit32_rrotate(e, 11), bit32_lrotate(e, 7)) + bit32_band(e, f) + bit32_band(-1 - e, g) + h + K[j] + W[j]
h = g
g = f
f = e
e = z + d
d = c
c = b
b = a
a = z + bit32_band(d, c) + bit32_band(a, bit32_bxor(d, c)) + bit32_bxor(bit32_rrotate(a, 2), bit32_rrotate(a, 13), bit32_lrotate(a, 10))
end
h1, h2, h3, h4 = (a + h1) % 4294967296, (b + h2) % 4294967296, (c + h3) % 4294967296, (d + h4) % 4294967296
h5, h6, h7, h8 = (e + h5) % 4294967296, (f + h6) % 4294967296, (g + h7) % 4294967296, (h + h8) % 4294967296
end
H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] = h1, h2, h3, h4, h5, h6, h7, h8
end
local function sha512_feed_128(H_lo, H_hi, str, offs, size)
-- offs >= 0, size >= 0, size is multiple of 128
-- W1_hi, W1_lo, W2_hi, W2_lo, ... Wk_hi = W[2*k-1], Wk_lo = W[2*k]
local W, K_lo, K_hi = common_W, sha2_K_lo, sha2_K_hi
local h1_lo, h2_lo, h3_lo, h4_lo, h5_lo, h6_lo, h7_lo, h8_lo = H_lo[1], H_lo[2], H_lo[3], H_lo[4], H_lo[5], H_lo[6], H_lo[7], H_lo[8]
local h1_hi, h2_hi, h3_hi, h4_hi, h5_hi, h6_hi, h7_hi, h8_hi = H_hi[1], H_hi[2], H_hi[3], H_hi[4], H_hi[5], H_hi[6], H_hi[7], H_hi[8]
for pos = offs, offs + size - 1, 128 do
for j = 1, 16 * 2 do
pos = pos + 4
local a, b, c, d = string.byte(str, pos - 3, pos)
W[j] = ((a * 256 + b) * 256 + c) * 256 + d
end
for jj = 34, 160, 2 do
local a_lo, a_hi, b_lo, b_hi = W[jj - 30], W[jj - 31], W[jj - 4], W[jj - 5]
local tmp1 = bit32_bxor(bit32_rshift(a_lo, 1) + bit32_lshift(a_hi, 31), bit32_rshift(a_lo, 8) + bit32_lshift(a_hi, 24), bit32_rshift(a_lo, 7) + bit32_lshift(a_hi, 25)) % 4294967296 +
bit32_bxor(bit32_rshift(b_lo, 19) + bit32_lshift(b_hi, 13), bit32_lshift(b_lo, 3) + bit32_rshift(b_hi, 29), bit32_rshift(b_lo, 6) + bit32_lshift(b_hi, 26)) % 4294967296 +
W[jj - 14] + W[jj - 32]
local tmp2 = tmp1 % 4294967296
W[jj - 1] = bit32_bxor(bit32_rshift(a_hi, 1) + bit32_lshift(a_lo, 31), bit32_rshift(a_hi, 8) + bit32_lshift(a_lo, 24), bit32_rshift(a_hi, 7)) +
bit32_bxor(bit32_rshift(b_hi, 19) + bit32_lshift(b_lo, 13), bit32_lshift(b_hi, 3) + bit32_rshift(b_lo, 29), bit32_rshift(b_hi, 6)) +
W[jj - 15] + W[jj - 33] + (tmp1 - tmp2) / 4294967296
W[jj] = tmp2
end
local a_lo, b_lo, c_lo, d_lo, e_lo, f_lo, g_lo, h_lo = h1_lo, h2_lo, h3_lo, h4_lo, h5_lo, h6_lo, h7_lo, h8_lo
local a_hi, b_hi, c_hi, d_hi, e_hi, f_hi, g_hi, h_hi = h1_hi, h2_hi, h3_hi, h4_hi, h5_hi, h6_hi, h7_hi, h8_hi
for j = 1, 80 do
local jj = 2 * j
local tmp1 = bit32_bxor(bit32_rshift(e_lo, 14) + bit32_lshift(e_hi, 18), bit32_rshift(e_lo, 18) + bit32_lshift(e_hi, 14), bit32_lshift(e_lo, 23) + bit32_rshift(e_hi, 9)) % 4294967296 +
(bit32_band(e_lo, f_lo) + bit32_band(-1 - e_lo, g_lo)) % 4294967296 +
h_lo + K_lo[j] + W[jj]
local z_lo = tmp1 % 4294967296
local z_hi = bit32_bxor(bit32_rshift(e_hi, 14) + bit32_lshift(e_lo, 18), bit32_rshift(e_hi, 18) + bit32_lshift(e_lo, 14), bit32_lshift(e_hi, 23) + bit32_rshift(e_lo, 9)) +
bit32_band(e_hi, f_hi) + bit32_band(-1 - e_hi, g_hi) +
h_hi + K_hi[j] + W[jj - 1] +
(tmp1 - z_lo) / 4294967296
h_lo = g_lo
h_hi = g_hi
g_lo = f_lo
g_hi = f_hi
f_lo = e_lo
f_hi = e_hi
tmp1 = z_lo + d_lo
e_lo = tmp1 % 4294967296
e_hi = z_hi + d_hi + (tmp1 - e_lo) / 4294967296
d_lo = c_lo
d_hi = c_hi
c_lo = b_lo
c_hi = b_hi
b_lo = a_lo
b_hi = a_hi
tmp1 = z_lo + (bit32_band(d_lo, c_lo) + bit32_band(b_lo, bit32_bxor(d_lo, c_lo))) % 4294967296 + bit32_bxor(bit32_rshift(b_lo, 28) + bit32_lshift(b_hi, 4), bit32_lshift(b_lo, 30) + bit32_rshift(b_hi, 2), bit32_lshift(b_lo, 25) + bit32_rshift(b_hi, 7)) % 4294967296
a_lo = tmp1 % 4294967296
a_hi = z_hi + (bit32_band(d_hi, c_hi) + bit32_band(b_hi, bit32_bxor(d_hi, c_hi))) + bit32_bxor(bit32_rshift(b_hi, 28) + bit32_lshift(b_lo, 4), bit32_lshift(b_hi, 30) + bit32_rshift(b_lo, 2), bit32_lshift(b_hi, 25) + bit32_rshift(b_lo, 7)) + (tmp1 - a_lo) / 4294967296
end
a_lo = h1_lo + a_lo
h1_lo = a_lo % 4294967296
h1_hi = (h1_hi + a_hi + (a_lo - h1_lo) / 4294967296) % 4294967296
a_lo = h2_lo + b_lo
h2_lo = a_lo % 4294967296
h2_hi = (h2_hi + b_hi + (a_lo - h2_lo) / 4294967296) % 4294967296
a_lo = h3_lo + c_lo
h3_lo = a_lo % 4294967296
h3_hi = (h3_hi + c_hi + (a_lo - h3_lo) / 4294967296) % 4294967296
a_lo = h4_lo + d_lo
h4_lo = a_lo % 4294967296
h4_hi = (h4_hi + d_hi + (a_lo - h4_lo) / 4294967296) % 4294967296
a_lo = h5_lo + e_lo
h5_lo = a_lo % 4294967296
h5_hi = (h5_hi + e_hi + (a_lo - h5_lo) / 4294967296) % 4294967296
a_lo = h6_lo + f_lo
h6_lo = a_lo % 4294967296
h6_hi = (h6_hi + f_hi + (a_lo - h6_lo) / 4294967296) % 4294967296
a_lo = h7_lo + g_lo
h7_lo = a_lo % 4294967296
h7_hi = (h7_hi + g_hi + (a_lo - h7_lo) / 4294967296) % 4294967296
a_lo = h8_lo + h_lo
h8_lo = a_lo % 4294967296
h8_hi = (h8_hi + h_hi + (a_lo - h8_lo) / 4294967296) % 4294967296
end
H_lo[1], H_lo[2], H_lo[3], H_lo[4], H_lo[5], H_lo[6], H_lo[7], H_lo[8] = h1_lo, h2_lo, h3_lo, h4_lo, h5_lo, h6_lo, h7_lo, h8_lo
H_hi[1], H_hi[2], H_hi[3], H_hi[4], H_hi[5], H_hi[6], H_hi[7], H_hi[8] = h1_hi, h2_hi, h3_hi, h4_hi, h5_hi, h6_hi, h7_hi, h8_hi
end
local function md5_feed_64(H, str, offs, size)
-- offs >= 0, size >= 0, size is multiple of 64
local W, K, md5_next_shift = common_W, md5_K, md5_next_shift
local h1, h2, h3, h4 = H[1], H[2], H[3], H[4]
for pos = offs, offs + size - 1, 64 do
for j = 1, 16 do
pos = pos + 4
local a, b, c, d = string.byte(str, pos - 3, pos)
W[j] = ((d * 256 + c) * 256 + b) * 256 + a
end
local a, b, c, d = h1, h2, h3, h4
local s = 25
for j = 1, 16 do
local F = bit32_rrotate(bit32_band(b, c) + bit32_band(-1 - b, d) + a + K[j] + W[j], s) + b
s = md5_next_shift[s]
a = d
d = c
c = b
b = F
end
s = 27
for j = 17, 32 do
local F = bit32_rrotate(bit32_band(d, b) + bit32_band(-1 - d, c) + a + K[j] + W[(5 * j - 4) % 16 + 1], s) + b
s = md5_next_shift[s]
a = d
d = c
c = b
b = F
end
s = 28
for j = 33, 48 do
local F = bit32_rrotate(bit32_bxor(bit32_bxor(b, c), d) + a + K[j] + W[(3 * j + 2) % 16 + 1], s) + b
s = md5_next_shift[s]
a = d
d = c
c = b
b = F
end
s = 26
for j = 49, 64 do
local F = bit32_rrotate(bit32_bxor(c, bit32_bor(b, -1 - d)) + a + K[j] + W[(j * 7 - 7) % 16 + 1], s) + b
s = md5_next_shift[s]
a = d
d = c
c = b
b = F
end
h1 = (a + h1) % 4294967296
h2 = (b + h2) % 4294967296
h3 = (c + h3) % 4294967296
h4 = (d + h4) % 4294967296
end
H[1], H[2], H[3], H[4] = h1, h2, h3, h4
end
local function sha1_feed_64(H, str, offs, size)
-- offs >= 0, size >= 0, size is multiple of 64
local W = common_W
local h1, h2, h3, h4, h5 = H[1], H[2], H[3], H[4], H[5]
for pos = offs, offs + size - 1, 64 do
for j = 1, 16 do
pos = pos + 4
local a, b, c, d = string.byte(str, pos - 3, pos)
W[j] = ((a * 256 + b) * 256 + c) * 256 + d
end
for j = 17, 80 do
W[j] = bit32_lrotate(bit32_bxor(W[j - 3], W[j - 8], W[j - 14], W[j - 16]), 1)
end
local a, b, c, d, e = h1, h2, h3, h4, h5
for j = 1, 20 do
local z = bit32_lrotate(a, 5) + bit32_band(b, c) + bit32_band(-1 - b, d) + 0x5A827999 + W[j] + e -- constant = math.floor(TWO_POW_30 * sqrt(2))
e = d
d = c
c = bit32_rrotate(b, 2)
b = a
a = z
end
for j = 21, 40 do
local z = bit32_lrotate(a, 5) + bit32_bxor(b, c, d) + 0x6ED9EBA1 + W[j] + e -- TWO_POW_30 * sqrt(3)
e = d
d = c
c = bit32_rrotate(b, 2)
b = a
a = z
end
for j = 41, 60 do
local z = bit32_lrotate(a, 5) + bit32_band(d, c) + bit32_band(b, bit32_bxor(d, c)) + 0x8F1BBCDC + W[j] + e -- TWO_POW_30 * sqrt(5)
e = d
d = c
c = bit32_rrotate(b, 2)
b = a
a = z
end
for j = 61, 80 do
local z = bit32_lrotate(a, 5) + bit32_bxor(b, c, d) + 0xCA62C1D6 + W[j] + e -- TWO_POW_30 * sqrt(10)
e = d
d = c
c = bit32_rrotate(b, 2)
b = a
a = z
end
h1 = (a + h1) % 4294967296
h2 = (b + h2) % 4294967296
h3 = (c + h3) % 4294967296
h4 = (d + h4) % 4294967296
h5 = (e + h5) % 4294967296
end
H[1], H[2], H[3], H[4], H[5] = h1, h2, h3, h4, h5
end
local function keccak_feed(lanes_lo, lanes_hi, str, offs, size, block_size_in_bytes)
-- This is an example of a Lua function having 79 local variables :-)
-- offs >= 0, size >= 0, size is multiple of block_size_in_bytes, block_size_in_bytes is positive multiple of 8
local RC_lo, RC_hi = sha3_RC_lo, sha3_RC_hi
local qwords_qty = block_size_in_bytes / 8
for pos = offs, offs + size - 1, block_size_in_bytes do
for j = 1, qwords_qty do
local a, b, c, d = string.byte(str, pos + 1, pos + 4)
lanes_lo[j] = bit32_bxor(lanes_lo[j], ((d * 256 + c) * 256 + b) * 256 + a)
pos = pos + 8
a, b, c, d = string.byte(str, pos - 3, pos)
lanes_hi[j] = bit32_bxor(lanes_hi[j], ((d * 256 + c) * 256 + b) * 256 + a)
end
local L01_lo, L01_hi, L02_lo, L02_hi, L03_lo, L03_hi, L04_lo, L04_hi, L05_lo, L05_hi, L06_lo, L06_hi, L07_lo, L07_hi, L08_lo, L08_hi, L09_lo, L09_hi, L10_lo, L10_hi, L11_lo, L11_hi, L12_lo, L12_hi, L13_lo, L13_hi, L14_lo, L14_hi, L15_lo, L15_hi, L16_lo, L16_hi, L17_lo, L17_hi, L18_lo, L18_hi, L19_lo, L19_hi, L20_lo, L20_hi, L21_lo, L21_hi, L22_lo, L22_hi, L23_lo, L23_hi, L24_lo, L24_hi, L25_lo, L25_hi = lanes_lo[1], lanes_hi[1], lanes_lo[2], lanes_hi[2], lanes_lo[3], lanes_hi[3], lanes_lo[4], lanes_hi[4], lanes_lo[5], lanes_hi[5], lanes_lo[6], lanes_hi[6], lanes_lo[7], lanes_hi[7], lanes_lo[8], lanes_hi[8], lanes_lo[9], lanes_hi[9], lanes_lo[10], lanes_hi[10], lanes_lo[11], lanes_hi[11], lanes_lo[12], lanes_hi[12], lanes_lo[13], lanes_hi[13], lanes_lo[14], lanes_hi[14], lanes_lo[15], lanes_hi[15], lanes_lo[16], lanes_hi[16], lanes_lo[17], lanes_hi[17], lanes_lo[18], lanes_hi[18], lanes_lo[19], lanes_hi[19], lanes_lo[20], lanes_hi[20], lanes_lo[21], lanes_hi[21], lanes_lo[22], lanes_hi[22], lanes_lo[23], lanes_hi[23], lanes_lo[24], lanes_hi[24], lanes_lo[25], lanes_hi[25]
for round_idx = 1, 24 do
local C1_lo = bit32_bxor(L01_lo, L06_lo, L11_lo, L16_lo, L21_lo)
local C1_hi = bit32_bxor(L01_hi, L06_hi, L11_hi, L16_hi, L21_hi)
local C2_lo = bit32_bxor(L02_lo, L07_lo, L12_lo, L17_lo, L22_lo)
local C2_hi = bit32_bxor(L02_hi, L07_hi, L12_hi, L17_hi, L22_hi)
local C3_lo = bit32_bxor(L03_lo, L08_lo, L13_lo, L18_lo, L23_lo)
local C3_hi = bit32_bxor(L03_hi, L08_hi, L13_hi, L18_hi, L23_hi)
local C4_lo = bit32_bxor(L04_lo, L09_lo, L14_lo, L19_lo, L24_lo)
local C4_hi = bit32_bxor(L04_hi, L09_hi, L14_hi, L19_hi, L24_hi)
local C5_lo = bit32_bxor(L05_lo, L10_lo, L15_lo, L20_lo, L25_lo)
local C5_hi = bit32_bxor(L05_hi, L10_hi, L15_hi, L20_hi, L25_hi)
local D_lo = bit32_bxor(C1_lo, C3_lo * 2 + (C3_hi % TWO_POW_32 - C3_hi % TWO_POW_31) / TWO_POW_31)
local D_hi = bit32_bxor(C1_hi, C3_hi * 2 + (C3_lo % TWO_POW_32 - C3_lo % TWO_POW_31) / TWO_POW_31)
local T0_lo = bit32_bxor(D_lo, L02_lo)
local T0_hi = bit32_bxor(D_hi, L02_hi)
local T1_lo = bit32_bxor(D_lo, L07_lo)
local T1_hi = bit32_bxor(D_hi, L07_hi)
local T2_lo = bit32_bxor(D_lo, L12_lo)
local T2_hi = bit32_bxor(D_hi, L12_hi)
local T3_lo = bit32_bxor(D_lo, L17_lo)
local T3_hi = bit32_bxor(D_hi, L17_hi)
local T4_lo = bit32_bxor(D_lo, L22_lo)
local T4_hi = bit32_bxor(D_hi, L22_hi)
L02_lo = (T1_lo % TWO_POW_32 - T1_lo % TWO_POW_20) / TWO_POW_20 + T1_hi * TWO_POW_12
L02_hi = (T1_hi % TWO_POW_32 - T1_hi % TWO_POW_20) / TWO_POW_20 + T1_lo * TWO_POW_12
L07_lo = (T3_lo % TWO_POW_32 - T3_lo % TWO_POW_19) / TWO_POW_19 + T3_hi * TWO_POW_13
L07_hi = (T3_hi % TWO_POW_32 - T3_hi % TWO_POW_19) / TWO_POW_19 + T3_lo * TWO_POW_13
L12_lo = T0_lo * 2 + (T0_hi % TWO_POW_32 - T0_hi % TWO_POW_31) / TWO_POW_31
L12_hi = T0_hi * 2 + (T0_lo % TWO_POW_32 - T0_lo % TWO_POW_31) / TWO_POW_31
L17_lo = T2_lo * TWO_POW_10 + (T2_hi % TWO_POW_32 - T2_hi % TWO_POW_22) / TWO_POW_22
L17_hi = T2_hi * TWO_POW_10 + (T2_lo % TWO_POW_32 - T2_lo % TWO_POW_22) / TWO_POW_22
L22_lo = T4_lo * TWO_POW_2 + (T4_hi % TWO_POW_32 - T4_hi % TWO_POW_30) / TWO_POW_30
L22_hi = T4_hi * TWO_POW_2 + (T4_lo % TWO_POW_32 - T4_lo % TWO_POW_30) / TWO_POW_30
D_lo = bit32_bxor(C2_lo, C4_lo * 2 + (C4_hi % TWO_POW_32 - C4_hi % TWO_POW_31) / TWO_POW_31)
D_hi = bit32_bxor(C2_hi, C4_hi * 2 + (C4_lo % TWO_POW_32 - C4_lo % TWO_POW_31) / TWO_POW_31)
T0_lo = bit32_bxor(D_lo, L03_lo)
T0_hi = bit32_bxor(D_hi, L03_hi)
T1_lo = bit32_bxor(D_lo, L08_lo)
T1_hi = bit32_bxor(D_hi, L08_hi)
T2_lo = bit32_bxor(D_lo, L13_lo)
T2_hi = bit32_bxor(D_hi, L13_hi)
T3_lo = bit32_bxor(D_lo, L18_lo)
T3_hi = bit32_bxor(D_hi, L18_hi)
T4_lo = bit32_bxor(D_lo, L23_lo)
T4_hi = bit32_bxor(D_hi, L23_hi)
L03_lo = (T2_lo % TWO_POW_32 - T2_lo % TWO_POW_21) / TWO_POW_21 + T2_hi * TWO_POW_11
L03_hi = (T2_hi % TWO_POW_32 - T2_hi % TWO_POW_21) / TWO_POW_21 + T2_lo * TWO_POW_11
L08_lo = (T4_lo % TWO_POW_32 - T4_lo % TWO_POW_3) / TWO_POW_3 + T4_hi * TWO_POW_29 % TWO_POW_32
L08_hi = (T4_hi % TWO_POW_32 - T4_hi % TWO_POW_3) / TWO_POW_3 + T4_lo * TWO_POW_29 % TWO_POW_32
L13_lo = T1_lo * TWO_POW_6 + (T1_hi % TWO_POW_32 - T1_hi % TWO_POW_26) / TWO_POW_26
L13_hi = T1_hi * TWO_POW_6 + (T1_lo % TWO_POW_32 - T1_lo % TWO_POW_26) / TWO_POW_26
L18_lo = T3_lo * TWO_POW_15 + (T3_hi % TWO_POW_32 - T3_hi % TWO_POW_17) / TWO_POW_17
L18_hi = T3_hi * TWO_POW_15 + (T3_lo % TWO_POW_32 - T3_lo % TWO_POW_17) / TWO_POW_17
L23_lo = (T0_lo % TWO_POW_32 - T0_lo % TWO_POW_2) / TWO_POW_2 + T0_hi * TWO_POW_30 % TWO_POW_32
L23_hi = (T0_hi % TWO_POW_32 - T0_hi % TWO_POW_2) / TWO_POW_2 + T0_lo * TWO_POW_30 % TWO_POW_32
D_lo = bit32_bxor(C3_lo, C5_lo * 2 + (C5_hi % TWO_POW_32 - C5_hi % TWO_POW_31) / TWO_POW_31)
D_hi = bit32_bxor(C3_hi, C5_hi * 2 + (C5_lo % TWO_POW_32 - C5_lo % TWO_POW_31) / TWO_POW_31)
T0_lo = bit32_bxor(D_lo, L04_lo)
T0_hi = bit32_bxor(D_hi, L04_hi)
T1_lo = bit32_bxor(D_lo, L09_lo)
T1_hi = bit32_bxor(D_hi, L09_hi)
T2_lo = bit32_bxor(D_lo, L14_lo)
T2_hi = bit32_bxor(D_hi, L14_hi)
T3_lo = bit32_bxor(D_lo, L19_lo)
T3_hi = bit32_bxor(D_hi, L19_hi)
T4_lo = bit32_bxor(D_lo, L24_lo)
T4_hi = bit32_bxor(D_hi, L24_hi)
L04_lo = T3_lo * TWO_POW_21 % TWO_POW_32 + (T3_hi % TWO_POW_32 - T3_hi % TWO_POW_11) / TWO_POW_11
L04_hi = T3_hi * TWO_POW_21 % TWO_POW_32 + (T3_lo % TWO_POW_32 - T3_lo % TWO_POW_11) / TWO_POW_11
L09_lo = T0_lo * TWO_POW_28 % TWO_POW_32 + (T0_hi % TWO_POW_32 - T0_hi % TWO_POW_4) / TWO_POW_4
L09_hi = T0_hi * TWO_POW_28 % TWO_POW_32 + (T0_lo % TWO_POW_32 - T0_lo % TWO_POW_4) / TWO_POW_4
L14_lo = T2_lo * TWO_POW_25 % TWO_POW_32 + (T2_hi % TWO_POW_32 - T2_hi % TWO_POW_7) / TWO_POW_7
L14_hi = T2_hi * TWO_POW_25 % TWO_POW_32 + (T2_lo % TWO_POW_32 - T2_lo % TWO_POW_7) / TWO_POW_7
L19_lo = (T4_lo % TWO_POW_32 - T4_lo % TWO_POW_8) / TWO_POW_8 + T4_hi * TWO_POW_24 % TWO_POW_32
L19_hi = (T4_hi % TWO_POW_32 - T4_hi % TWO_POW_8) / TWO_POW_8 + T4_lo * TWO_POW_24 % TWO_POW_32
L24_lo = (T1_lo % TWO_POW_32 - T1_lo % TWO_POW_9) / TWO_POW_9 + T1_hi * TWO_POW_23 % TWO_POW_32
L24_hi = (T1_hi % TWO_POW_32 - T1_hi % TWO_POW_9) / TWO_POW_9 + T1_lo * TWO_POW_23 % TWO_POW_32
D_lo = bit32_bxor(C4_lo, C1_lo * 2 + (C1_hi % TWO_POW_32 - C1_hi % TWO_POW_31) / TWO_POW_31)
D_hi = bit32_bxor(C4_hi, C1_hi * 2 + (C1_lo % TWO_POW_32 - C1_lo % TWO_POW_31) / TWO_POW_31)
T0_lo = bit32_bxor(D_lo, L05_lo)
T0_hi = bit32_bxor(D_hi, L05_hi)
T1_lo = bit32_bxor(D_lo, L10_lo)
T1_hi = bit32_bxor(D_hi, L10_hi)
T2_lo = bit32_bxor(D_lo, L15_lo)
T2_hi = bit32_bxor(D_hi, L15_hi)
T3_lo = bit32_bxor(D_lo, L20_lo)
T3_hi = bit32_bxor(D_hi, L20_hi)
T4_lo = bit32_bxor(D_lo, L25_lo)
T4_hi = bit32_bxor(D_hi, L25_hi)
L05_lo = T4_lo * TWO_POW_14 + (T4_hi % TWO_POW_32 - T4_hi % TWO_POW_18) / TWO_POW_18
L05_hi = T4_hi * TWO_POW_14 + (T4_lo % TWO_POW_32 - T4_lo % TWO_POW_18) / TWO_POW_18
L10_lo = T1_lo * TWO_POW_20 % TWO_POW_32 + (T1_hi % TWO_POW_32 - T1_hi % TWO_POW_12) / TWO_POW_12
L10_hi = T1_hi * TWO_POW_20 % TWO_POW_32 + (T1_lo % TWO_POW_32 - T1_lo % TWO_POW_12) / TWO_POW_12
L15_lo = T3_lo * TWO_POW_8 + (T3_hi % TWO_POW_32 - T3_hi % TWO_POW_24) / TWO_POW_24
L15_hi = T3_hi * TWO_POW_8 + (T3_lo % TWO_POW_32 - T3_lo % TWO_POW_24) / TWO_POW_24
L20_lo = T0_lo * TWO_POW_27 % TWO_POW_32 + (T0_hi % TWO_POW_32 - T0_hi % TWO_POW_5) / TWO_POW_5
L20_hi = T0_hi * TWO_POW_27 % TWO_POW_32 + (T0_lo % TWO_POW_32 - T0_lo % TWO_POW_5) / TWO_POW_5
L25_lo = (T2_lo % TWO_POW_32 - T2_lo % TWO_POW_25) / TWO_POW_25 + T2_hi * TWO_POW_7
L25_hi = (T2_hi % TWO_POW_32 - T2_hi % TWO_POW_25) / TWO_POW_25 + T2_lo * TWO_POW_7
D_lo = bit32_bxor(C5_lo, C2_lo * 2 + (C2_hi % TWO_POW_32 - C2_hi % TWO_POW_31) / TWO_POW_31)
D_hi = bit32_bxor(C5_hi, C2_hi * 2 + (C2_lo % TWO_POW_32 - C2_lo % TWO_POW_31) / TWO_POW_31)
T1_lo = bit32_bxor(D_lo, L06_lo)
T1_hi = bit32_bxor(D_hi, L06_hi)
T2_lo = bit32_bxor(D_lo, L11_lo)
T2_hi = bit32_bxor(D_hi, L11_hi)
T3_lo = bit32_bxor(D_lo, L16_lo)
T3_hi = bit32_bxor(D_hi, L16_hi)
T4_lo = bit32_bxor(D_lo, L21_lo)
T4_hi = bit32_bxor(D_hi, L21_hi)
L06_lo = T2_lo * TWO_POW_3 + (T2_hi % TWO_POW_32 - T2_hi % TWO_POW_29) / TWO_POW_29
L06_hi = T2_hi * TWO_POW_3 + (T2_lo % TWO_POW_32 - T2_lo % TWO_POW_29) / TWO_POW_29
L11_lo = T4_lo * TWO_POW_18 + (T4_hi % TWO_POW_32 - T4_hi % TWO_POW_14) / TWO_POW_14
L11_hi = T4_hi * TWO_POW_18 + (T4_lo % TWO_POW_32 - T4_lo % TWO_POW_14) / TWO_POW_14
L16_lo = (T1_lo % TWO_POW_32 - T1_lo % TWO_POW_28) / TWO_POW_28 + T1_hi * TWO_POW_4
L16_hi = (T1_hi % TWO_POW_32 - T1_hi % TWO_POW_28) / TWO_POW_28 + T1_lo * TWO_POW_4
L21_lo = (T3_lo % TWO_POW_32 - T3_lo % TWO_POW_23) / TWO_POW_23 + T3_hi * TWO_POW_9
L21_hi = (T3_hi % TWO_POW_32 - T3_hi % TWO_POW_23) / TWO_POW_23 + T3_lo * TWO_POW_9
L01_lo = bit32_bxor(D_lo, L01_lo)
L01_hi = bit32_bxor(D_hi, L01_hi)
L01_lo, L02_lo, L03_lo, L04_lo, L05_lo = bit32_bxor(L01_lo, bit32_band(-1 - L02_lo, L03_lo)), bit32_bxor(L02_lo, bit32_band(-1 - L03_lo, L04_lo)), bit32_bxor(L03_lo, bit32_band(-1 - L04_lo, L05_lo)), bit32_bxor(L04_lo, bit32_band(-1 - L05_lo, L01_lo)), bit32_bxor(L05_lo, bit32_band(-1 - L01_lo, L02_lo))
L01_hi, L02_hi, L03_hi, L04_hi, L05_hi = bit32_bxor(L01_hi, bit32_band(-1 - L02_hi, L03_hi)), bit32_bxor(L02_hi, bit32_band(-1 - L03_hi, L04_hi)), bit32_bxor(L03_hi, bit32_band(-1 - L04_hi, L05_hi)), bit32_bxor(L04_hi, bit32_band(-1 - L05_hi, L01_hi)), bit32_bxor(L05_hi, bit32_band(-1 - L01_hi, L02_hi))
L06_lo, L07_lo, L08_lo, L09_lo, L10_lo = bit32_bxor(L09_lo, bit32_band(-1 - L10_lo, L06_lo)), bit32_bxor(L10_lo, bit32_band(-1 - L06_lo, L07_lo)), bit32_bxor(L06_lo, bit32_band(-1 - L07_lo, L08_lo)), bit32_bxor(L07_lo, bit32_band(-1 - L08_lo, L09_lo)), bit32_bxor(L08_lo, bit32_band(-1 - L09_lo, L10_lo))
L06_hi, L07_hi, L08_hi, L09_hi, L10_hi = bit32_bxor(L09_hi, bit32_band(-1 - L10_hi, L06_hi)), bit32_bxor(L10_hi, bit32_band(-1 - L06_hi, L07_hi)), bit32_bxor(L06_hi, bit32_band(-1 - L07_hi, L08_hi)), bit32_bxor(L07_hi, bit32_band(-1 - L08_hi, L09_hi)), bit32_bxor(L08_hi, bit32_band(-1 - L09_hi, L10_hi))
L11_lo, L12_lo, L13_lo, L14_lo, L15_lo = bit32_bxor(L12_lo, bit32_band(-1 - L13_lo, L14_lo)), bit32_bxor(L13_lo, bit32_band(-1 - L14_lo, L15_lo)), bit32_bxor(L14_lo, bit32_band(-1 - L15_lo, L11_lo)), bit32_bxor(L15_lo, bit32_band(-1 - L11_lo, L12_lo)), bit32_bxor(L11_lo, bit32_band(-1 - L12_lo, L13_lo))
L11_hi, L12_hi, L13_hi, L14_hi, L15_hi = bit32_bxor(L12_hi, bit32_band(-1 - L13_hi, L14_hi)), bit32_bxor(L13_hi, bit32_band(-1 - L14_hi, L15_hi)), bit32_bxor(L14_hi, bit32_band(-1 - L15_hi, L11_hi)), bit32_bxor(L15_hi, bit32_band(-1 - L11_hi, L12_hi)), bit32_bxor(L11_hi, bit32_band(-1 - L12_hi, L13_hi))
L16_lo, L17_lo, L18_lo, L19_lo, L20_lo = bit32_bxor(L20_lo, bit32_band(-1 - L16_lo, L17_lo)), bit32_bxor(L16_lo, bit32_band(-1 - L17_lo, L18_lo)), bit32_bxor(L17_lo, bit32_band(-1 - L18_lo, L19_lo)), bit32_bxor(L18_lo, bit32_band(-1 - L19_lo, L20_lo)), bit32_bxor(L19_lo, bit32_band(-1 - L20_lo, L16_lo))
L16_hi, L17_hi, L18_hi, L19_hi, L20_hi = bit32_bxor(L20_hi, bit32_band(-1 - L16_hi, L17_hi)), bit32_bxor(L16_hi, bit32_band(-1 - L17_hi, L18_hi)), bit32_bxor(L17_hi, bit32_band(-1 - L18_hi, L19_hi)), bit32_bxor(L18_hi, bit32_band(-1 - L19_hi, L20_hi)), bit32_bxor(L19_hi, bit32_band(-1 - L20_hi, L16_hi))
L21_lo, L22_lo, L23_lo, L24_lo, L25_lo = bit32_bxor(L23_lo, bit32_band(-1 - L24_lo, L25_lo)), bit32_bxor(L24_lo, bit32_band(-1 - L25_lo, L21_lo)), bit32_bxor(L25_lo, bit32_band(-1 - L21_lo, L22_lo)), bit32_bxor(L21_lo, bit32_band(-1 - L22_lo, L23_lo)), bit32_bxor(L22_lo, bit32_band(-1 - L23_lo, L24_lo))
L21_hi, L22_hi, L23_hi, L24_hi, L25_hi = bit32_bxor(L23_hi, bit32_band(-1 - L24_hi, L25_hi)), bit32_bxor(L24_hi, bit32_band(-1 - L25_hi, L21_hi)), bit32_bxor(L25_hi, bit32_band(-1 - L21_hi, L22_hi)), bit32_bxor(L21_hi, bit32_band(-1 - L22_hi, L23_hi)), bit32_bxor(L22_hi, bit32_band(-1 - L23_hi, L24_hi))
L01_lo = bit32_bxor(L01_lo, RC_lo[round_idx])
L01_hi = L01_hi + RC_hi[round_idx] -- RC_hi[] is either 0 or 0x80000000, so we could use fast addition instead of slow XOR
end
lanes_lo[1] = L01_lo
lanes_hi[1] = L01_hi
lanes_lo[2] = L02_lo
lanes_hi[2] = L02_hi
lanes_lo[3] = L03_lo
lanes_hi[3] = L03_hi
lanes_lo[4] = L04_lo
lanes_hi[4] = L04_hi
lanes_lo[5] = L05_lo
lanes_hi[5] = L05_hi
lanes_lo[6] = L06_lo
lanes_hi[6] = L06_hi
lanes_lo[7] = L07_lo
lanes_hi[7] = L07_hi
lanes_lo[8] = L08_lo
lanes_hi[8] = L08_hi
lanes_lo[9] = L09_lo
lanes_hi[9] = L09_hi
lanes_lo[10] = L10_lo
lanes_hi[10] = L10_hi
lanes_lo[11] = L11_lo
lanes_hi[11] = L11_hi
lanes_lo[12] = L12_lo
lanes_hi[12] = L12_hi
lanes_lo[13] = L13_lo
lanes_hi[13] = L13_hi
lanes_lo[14] = L14_lo
lanes_hi[14] = L14_hi
lanes_lo[15] = L15_lo
lanes_hi[15] = L15_hi
lanes_lo[16] = L16_lo
lanes_hi[16] = L16_hi
lanes_lo[17] = L17_lo
lanes_hi[17] = L17_hi
lanes_lo[18] = L18_lo
lanes_hi[18] = L18_hi
lanes_lo[19] = L19_lo
lanes_hi[19] = L19_hi
lanes_lo[20] = L20_lo
lanes_hi[20] = L20_hi
lanes_lo[21] = L21_lo
lanes_hi[21] = L21_hi
lanes_lo[22] = L22_lo
lanes_hi[22] = L22_hi
lanes_lo[23] = L23_lo
lanes_hi[23] = L23_hi
lanes_lo[24] = L24_lo
lanes_hi[24] = L24_hi
lanes_lo[25] = L25_lo
lanes_hi[25] = L25_hi
end
end
--------------------------------------------------------------------------------
-- MAGIC NUMBERS CALCULATOR
--------------------------------------------------------------------------------
-- Q:
-- Is 53-bit "double" math enough to calculate square roots and cube roots of primes with 64 correct bits after decimal point?
-- A:
-- Yes, 53-bit "double" arithmetic is enough.
-- We could obtain first 40 bits by direct calculation of p^(1/3) and next 40 bits by one step of Newton's method.
do
local function mul(src1, src2, factor, result_length)
-- src1, src2 - long integers (arrays of digits in base TWO_POW_24)
-- factor - small integer
-- returns long integer result (src1 * src2 * factor) and its floating point approximation
local result, carry, value, weight = table.create(result_length), 0, 0, 1
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] -- "int32" is not enough for multiplication result, that's why "factor" must be of type "double"
end
local digit = carry % TWO_POW_24
result[j] = math.floor(digit)
carry = (carry - digit) / TWO_POW_24
value = value + digit * weight
weight = weight * TWO_POW_24
end
return result, value
end
local idx, step, p, one, sqrt_hi, sqrt_lo = 0, {4, 1, 2, -2, 2}, 4, {1}, sha2_H_hi, sha2_H_lo
repeat
p = p + step[p % 6]
local d = 1
repeat
d = d + step[d % 6]
if d * d > p then
-- next prime number is found
local root = p ^ (1 / 3)
local R = root * TWO_POW_40
R = mul(table.create(1, math.floor(R)), one, 1, 2)
local _, delta = mul(R, mul(R, R, 1, 4), -1, 4)
local hi = R[2] % 65536 * 65536 + math.floor(R[1] / 256)
local lo = R[1] % 256 * 16777216 + math.floor(delta * (TWO_POW_NEG_56 / 3) * root / p)
if idx < 16 then
root = math.sqrt(p)
R = root * TWO_POW_40
R = mul(table.create(1, math.floor(R)), one, 1, 2)
_, delta = mul(R, R, -1, 2)
local hi = R[2] % 65536 * 65536 + math.floor(R[1] / 256)
local lo = R[1] % 256 * 16777216 + math.floor(delta * TWO_POW_NEG_17 / root)
local idx = idx % 8 + 1
sha2_H_ext256[224][idx] = lo
sqrt_hi[idx], sqrt_lo[idx] = hi, lo + hi * hi_factor
if idx > 7 then
sqrt_hi, sqrt_lo = sha2_H_ext512_hi[384], sha2_H_ext512_lo[384]
end
end
idx = idx + 1
sha2_K_hi[idx], sha2_K_lo[idx] = hi, lo % K_lo_modulo + hi * hi_factor
break
end
until p % d == 0
until idx > 79
end
-- Calculating IVs for SHA512/224 and SHA512/256
for width = 224, 256, 32 do
local H_lo, H_hi = {}, nil
if XOR64A5 then
for j = 1, 8 do
H_lo[j] = XOR64A5(sha2_H_lo[j])
end
else
H_hi = {}
for j = 1, 8 do
H_lo[j] = bit32_bxor(sha2_H_lo[j], 0xA5A5A5A5) % 4294967296
H_hi[j] = bit32_bxor(sha2_H_hi[j], 0xA5A5A5A5) % 4294967296
end
end
sha512_feed_128(H_lo, H_hi, "SHA-512/" .. tostring(width) .. "\128" .. string.rep("\0", 115) .. "\88", 0, 128)
sha2_H_ext512_lo[width] = H_lo
sha2_H_ext512_hi[width] = H_hi
end
-- Constants for MD5
do
for idx = 1, 64 do
-- we can't use formula math.floor(abs(sin(idx))*TWO_POW_32) because its result may be beyond integer range on Lua built with 32-bit integers
local hi, lo = math.modf(math.abs(math.sin(idx)) * TWO_POW_16)
md5_K[idx] = hi * 65536 + math.floor(lo * TWO_POW_16)
end
end
-- Constants for SHA3
do
local sh_reg = 29
local function next_bit()
local r = sh_reg % 2
sh_reg = bit32_bxor((sh_reg - r) / 2, 142 * r)
return r
end
for idx = 1, 24 do
local lo, m = 0, nil
for _ = 1, 6 do
m = m and m * m * 2 or 1
lo = lo + next_bit() * m
end
local hi = next_bit() * m
sha3_RC_hi[idx], sha3_RC_lo[idx] = hi, lo + hi * hi_factor_keccak
end
end
--------------------------------------------------------------------------------
-- MAIN FUNCTIONS
--------------------------------------------------------------------------------
local function sha256ext(width, message)
-- Create an instance (private objects for current calculation)
local Array256 = sha2_H_ext256[width] -- # == 8
local length, tail = 0, ""
local H = table.create(8)
H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] = Array256[1], Array256[2], Array256[3], Array256[4], Array256[5], Array256[6], Array256[7], Array256[8]
local function partial(message_part)
if message_part then
local partLength = #message_part
if tail then
length = length + partLength
local offs = 0
local tailLength = #tail
if tail ~= "" and tailLength + partLength >= 64 then
offs = 64 - tailLength
sha256_feed_64(H, tail .. string.sub(message_part, 1, offs), 0, 64)
tail = ""
end
local size = partLength - offs
local size_tail = size % 64
sha256_feed_64(H, message_part, offs, size - size_tail)
tail = tail .. string.sub(message_part, partLength + 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 = table.create(10) --{tail, "\128", string.rep("\0", (-9 - length) % 64 + 1)}
final_blocks[1] = tail
final_blocks[2] = "\128"
final_blocks[3] = string.rep("\0", (-9 - length) % 64 + 1)
tail = nil
-- Assuming user data length is shorter than (TWO_POW_53)-9 bytes
-- Anyway, it looks very unrealistic that someone would spend more than a year of calculations to process TWO_POW_53 bytes of data by using this Lua script :-)
-- TWO_POW_53 bytes = TWO_POW_56 bits, so "bit-counter" fits in 7 bytes
length = length * (8 / TWO56_POW_7) -- convert "byte-counter" to "bit-counter" and move decimal point to the left
for j = 4, 10 do
length = length % 1 * 256
final_blocks[j] = string.char(math.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] = string.format("%08x", H[j] % 4294967296)
end
H = table.concat(H, "", 1, max_reg)
end
return H
end
end
if message then
-- Actually perform calculations and return the SHA256 digest of a message
return partial(message)()
else
-- Return function for chunk-by-chunk loading
-- User should feed every chunk of input data as single argument to this function and finally get SHA256 digest by invoking this function without an argument
return partial
end
end
local function sha512ext(width, message)
-- Create an instance (private objects for current calculation)
local length, tail, H_lo, H_hi = 0, "", table.pack(table.unpack(sha2_H_ext512_lo[width])), not HEX64 and table.pack(table.unpack(sha2_H_ext512_hi[width]))
local function partial(message_part)
if message_part then
local partLength = #message_part
if tail then
length = length + partLength
local offs = 0
if tail ~= "" and #tail + partLength >= 128 then
offs = 128 - #tail
sha512_feed_128(H_lo, H_hi, tail .. string.sub(message_part, 1, offs), 0, 128)
tail = ""
end
local size = partLength - offs
local size_tail = size % 128
sha512_feed_128(H_lo, H_hi, message_part, offs, size - size_tail)
tail = tail .. string.sub(message_part, partLength + 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 = table.create(3) --{tail, "\128", string.rep("\0", (-17-length) % 128 + 9)}
final_blocks[1] = tail
final_blocks[2] = "\128"
final_blocks[3] = string.rep("\0", (-17 - length) % 128 + 9)
tail = nil
-- Assuming user data length is shorter than (TWO_POW_53)-17 bytes
-- TWO_POW_53 bytes = TWO_POW_56 bits, so "bit-counter" fits in 7 bytes
length = length * (8 / TWO56_POW_7) -- convert "byte-counter" to "bit-counter" and move floating point to the left
for j = 4, 10 do
length = length % 1 * 256
final_blocks[j] = string.char(math.floor(length))
end
final_blocks = table.concat(final_blocks)
sha512_feed_128(H_lo, H_hi, final_blocks, 0, #final_blocks)
local max_reg = math.ceil(width / 64)
if HEX64 then
for j = 1, max_reg do
H_lo[j] = HEX64(H_lo[j])
end
else
for j = 1, max_reg do
H_lo[j] = string.format("%08x", H_hi[j] % 4294967296) .. string.format("%08x", H_lo[j] % 4294967296)
end
H_hi = nil
end
H_lo = string.sub(table.concat(H_lo, "", 1, max_reg), 1, width / 4)
end
return H_lo
end
end
if message then
-- Actually perform calculations and return the SHA512 digest of a message
return partial(message)()
else
-- Return function for chunk-by-chunk loading
-- User should feed every chunk of input data as single argument to this function and finally get SHA512 digest by invoking this function without an argument
return partial
end
end
local function md5(message)
-- Create an instance (private objects for current calculation)
local H, length, tail = table.create(4), 0, ""
H[1], H[2], H[3], H[4] = md5_sha1_H[1], md5_sha1_H[2], md5_sha1_H[3], md5_sha1_H[4]
local function partial(message_part)
if message_part then
local partLength = #message_part
if tail then
length = length + partLength
local offs = 0
if tail ~= "" and #tail + partLength >= 64 then
offs = 64 - #tail
md5_feed_64(H, tail .. string.sub(message_part, 1, offs), 0, 64)
tail = ""
end
local size = partLength - offs
local size_tail = size % 64
md5_feed_64(H, message_part, offs, size - size_tail)
tail = tail .. string.sub(message_part, partLength + 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 = table.create(3) --{tail, "\128", string.rep("\0", (-9 - length) % 64)}
final_blocks[1] = tail
final_blocks[2] = "\128"
final_blocks[3] = string.rep("\0", (-9 - length) % 64)
tail = nil
length = length * 8 -- convert "byte-counter" to "bit-counter"
for j = 4, 11 do
local low_byte = length % 256
final_blocks[j] = string.char(low_byte)
length = (length - low_byte) / 256
end
final_blocks = table.concat(final_blocks)
md5_feed_64(H, final_blocks, 0, #final_blocks)
for j = 1, 4 do
H[j] = string.format("%08x", H[j] % 4294967296)
end
H = string.gsub(table.concat(H), "(..)(..)(..)(..)", "%4%3%2%1")
end
return H
end
end
if message then
-- Actually perform calculations and return the MD5 digest of a message
return partial(message)()
else
-- Return function for chunk-by-chunk loading
-- User should feed every chunk of input data as single argument to this function and finally get MD5 digest by invoking this function without an argument
return partial
end
end
local function sha1(message)
-- Create an instance (private objects for current calculation)
local H, length, tail = table.pack(table.unpack(md5_sha1_H)), 0, ""
local function partial(message_part)
if message_part then
local partLength = #message_part
if tail then
length = length + partLength
local offs = 0
if tail ~= "" and #tail + partLength >= 64 then
offs = 64 - #tail
sha1_feed_64(H, tail .. string.sub(message_part, 1, offs), 0, 64)
tail = ""
end
local size = partLength - offs
local size_tail = size % 64
sha1_feed_64(H, message_part, offs, size - size_tail)
tail = tail .. string.sub(message_part, partLength + 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 = table.create(10) --{tail, "\128", string.rep("\0", (-9 - length) % 64 + 1)}
final_blocks[1] = tail
final_blocks[2] = "\128"
final_blocks[3] = string.rep("\0", (-9 - length) % 64 + 1)
tail = nil
-- Assuming user data length is shorter than (TWO_POW_53)-9 bytes
-- TWO_POW_53 bytes = TWO_POW_56 bits, so "bit-counter" fits in 7 bytes
length = length * (8 / TWO56_POW_7) -- convert "byte-counter" to "bit-counter" and move decimal point to the left
for j = 4, 10 do
length = length % 1 * 256
final_blocks[j] = string.char(math.floor(length))
end
final_blocks = table.concat(final_blocks)
sha1_feed_64(H, final_blocks, 0, #final_blocks)
for j = 1, 5 do
H[j] = string.format("%08x", H[j] % 4294967296)
end
H = table.concat(H)
end
return H
end
end
if message then
-- Actually perform calculations and return the SHA-1 digest of a message
return partial(message)()
else
-- Return function for chunk-by-chunk loading
-- User should feed every chunk of input data as single argument to this function and finally get SHA-1 digest by invoking this function without an argument
return partial
end
end
local function keccak(block_size_in_bytes, digest_size_in_bytes, is_SHAKE, message)
-- "block_size_in_bytes" is multiple of 8
if type(digest_size_in_bytes) ~= "number" then
-- arguments in SHAKE are swapped:
-- NIST FIPS 202 defines SHAKE(message,num_bits)
-- this module defines SHAKE(num_bytes,message)
-- it's easy to forget about this swap, hence the check
error("Argument 'digest_size_in_bytes' must be a number", 2)
end
-- Create an instance (private objects for current calculation)
local tail, lanes_lo, lanes_hi = "", table.create(25, 0), hi_factor_keccak == 0 and table.create(25, 0)
local result
--~ pad the input N using the pad function, yielding a padded bit string P with a length divisible by r (such that n = len(P)/r is integer),
--~ break P into n consecutive r-bit pieces P0, ..., Pn-1 (last is zero-padded)
--~ initialize the state S to a string of b 0 bits.
--~ absorb the input into the state: For each block Pi,
--~ extend Pi at the end by a string of c 0 bits, yielding one of length b,
--~ XOR that with S and
--~ apply the block permutation f to the result, yielding a new state S
--~ initialize Z to be the empty string
--~ while the length of Z is less than d:
--~ append the first r bits of S to Z
--~ if Z is still less than d bits long, apply f to S, yielding a new state S.
--~ truncate Z to d bits
local function partial(message_part)
if message_part then
local partLength = #message_part
if tail then
local offs = 0
if tail ~= "" and #tail + partLength >= block_size_in_bytes then
offs = block_size_in_bytes - #tail
keccak_feed(lanes_lo, lanes_hi, tail .. string.sub(message_part, 1, offs), 0, block_size_in_bytes, block_size_in_bytes)
tail = ""
end
local size = partLength - offs
local size_tail = size % block_size_in_bytes
keccak_feed(lanes_lo, lanes_hi, message_part, offs, size - size_tail, block_size_in_bytes)
tail = tail .. string.sub(message_part, partLength + 1 - size_tail)
return partial
else
error("Adding more chunks is not allowed after receiving the result", 2)
end
else
if tail then
-- append the following bits to the message: for usual SHA3: 011(0*)1, for SHAKE: 11111(0*)1
local gap_start = is_SHAKE and 31 or 6
tail = tail .. (#tail + 1 == block_size_in_bytes and string.char(gap_start + 128) or string.char(gap_start) .. string.rep("\0", (-2 - #tail) % block_size_in_bytes) .. "\128")
keccak_feed(lanes_lo, lanes_hi, tail, 0, #tail, block_size_in_bytes)
tail = nil
local lanes_used = 0
local total_lanes = math.floor(block_size_in_bytes / 8)
local qwords = {}
local function get_next_qwords_of_digest(qwords_qty)
-- returns not more than 'qwords_qty' qwords ('qwords_qty' might be non-integer)
-- doesn't go across keccak-buffer boundary
-- block_size_in_bytes is a multiple of 8, so, keccak-buffer contains integer number of qwords
if lanes_used >= total_lanes then
keccak_feed(lanes_lo, lanes_hi, "\0\0\0\0\0\0\0\0", 0, 8, 8)
lanes_used = 0
end
qwords_qty = math.floor(math.min(qwords_qty, total_lanes - lanes_used))
if hi_factor_keccak ~= 0 then
for j = 1, qwords_qty do
qwords[j] = HEX64(lanes_lo[lanes_used + j - 1 + lanes_index_base])
end
else
for j = 1, qwords_qty do
qwords[j] = string.format("%08x", lanes_hi[lanes_used + j] % 4294967296) .. string.format("%08x", lanes_lo[lanes_used + j] % 4294967296)
end
end
lanes_used = lanes_used + qwords_qty
return string.gsub(table.concat(qwords, "", 1, qwords_qty), "(..)(..)(..)(..)(..)(..)(..)(..)", "%8%7%6%5%4%3%2%1"), qwords_qty * 8
end
local parts = {} -- digest parts
local last_part, last_part_size = "", 0
local function get_next_part_of_digest(bytes_needed)
-- returns 'bytes_needed' bytes, for arbitrary integer 'bytes_needed'
bytes_needed = bytes_needed or 1
if bytes_needed <= last_part_size then
last_part_size = last_part_size - bytes_needed
local part_size_in_nibbles = bytes_needed * 2
local result = string.sub(last_part, 1, part_size_in_nibbles)
last_part = string.sub(last_part, part_size_in_nibbles + 1)
return result
end
local parts_qty = 0
if last_part_size > 0 then
parts_qty = 1
parts[parts_qty] = last_part
bytes_needed = bytes_needed - last_part_size
end
-- repeats until the length is enough
while bytes_needed >= 8 do
local next_part, next_part_size = get_next_qwords_of_digest(bytes_needed / 8)
parts_qty = parts_qty + 1
parts[parts_qty] = next_part
bytes_needed = bytes_needed - next_part_size
end
if bytes_needed > 0 then
last_part, last_part_size = get_next_qwords_of_digest(1)
parts_qty = parts_qty + 1
parts[parts_qty] = get_next_part_of_digest(bytes_needed)
else
last_part, last_part_size = "", 0
end
return table.concat(parts, "", 1, parts_qty)
end
if digest_size_in_bytes < 0 then
result = get_next_part_of_digest
else
result = get_next_part_of_digest(digest_size_in_bytes)
end
end
return result
end
end
if message then
-- Actually perform calculations and return the SHA3 digest of a message
return partial(message)()
else
-- Return function for chunk-by-chunk loading
-- User should feed every chunk of input data as single argument to this function and finally get SHA3 digest by invoking this function without an argument
return partial
end
end
local function HexToBinFunction(hh)
return string.char(tonumber(hh, 16))
end
local function hex2bin(hex_string)
return (string.gsub(hex_string, "%x%x", HexToBinFunction))
end
local base64_symbols = {
["+"] = 62, ["-"] = 62, [62] = "+";
["/"] = 63, ["_"] = 63, [63] = "/";
["="] = -1, ["."] = -1, [-1] = "=";
}
local symbol_index = 0
for j, pair in ipairs{"AZ", "az", "09"} do
for ascii = string.byte(pair), string.byte(pair, 2) do
local ch = string.char(ascii)
base64_symbols[ch] = symbol_index
base64_symbols[symbol_index] = ch
symbol_index = symbol_index + 1
end
end
local function bin2base64(binary_string)
local stringLength = #binary_string
local result = table.create(math.ceil(stringLength / 3))
local length = 0
for pos = 1, #binary_string, 3 do
local c1, c2, c3, c4 = string.byte(string.sub(binary_string, pos, pos + 2) .. '\0', 1, -1)
length = length + 1
result[length] =
base64_symbols[math.floor(c1 / 4)] ..
base64_symbols[c1 % 4 * 16 + math.floor(c2 / 16)] ..
base64_symbols[c3 and c2 % 16 * 4 + math.floor(c3 / 64) or -1] ..
base64_symbols[c4 and c3 % 64 or -1]
end
return table.concat(result)
end
local function base642bin(base64_string)
local result, chars_qty = {}, 3
for pos, ch in string.gmatch(string.gsub(base64_string, "%s+", ""), "()(.)") do
local code = base64_symbols[ch]
if code < 0 then
chars_qty = chars_qty - 1
code = 0
end
local idx = pos % 4
if idx > 0 then
result[-idx] = code
else
local c1 = result[-1] * 4 + math.floor(result[-2] / 16)
local c2 = (result[-2] % 16) * 16 + math.floor(result[-3] / 4)
local c3 = (result[-3] % 4) * 64 + code
result[#result + 1] = string.sub(string.char(c1, c2, c3), 1, chars_qty)
end
end
return table.concat(result)
end
local block_size_for_HMAC -- this table will be initialized at the end of the module
--local function pad_and_xor(str, result_length, byte_for_xor)
-- return string.gsub(str, ".", function(c)
-- return string.char(bit32_bxor(string.byte(c), byte_for_xor))
-- end) .. string.rep(string.char(byte_for_xor), result_length - #str)
--end
-- For the sake of speed of converting hexes to strings, there's a map of the conversions here
local BinaryStringMap = {}
for Index = 0, 255 do
BinaryStringMap[string.format("%02x", Index)] = string.char(Index)
end
-- Update 02.14.20 - added AsBinary for easy GameAnalytics replacement.
local function hmac(hash_func, key, message, AsBinary)
-- Create an instance (private objects for current calculation)
local block_size = block_size_for_HMAC[hash_func]
if not block_size then
error("Unknown hash function", 2)
end
local KeyLength = #key
if KeyLength > block_size then
key = string.gsub(hash_func(key), "%x%x", HexToBinFunction)
KeyLength = #key
end
local append = hash_func()(string.gsub(key, ".", function(c)
return string.char(bit32_bxor(string.byte(c), 0x36))
end) .. string.rep("6", block_size - KeyLength)) -- 6 = string.char(0x36)
local result
local function partial(message_part)
if not message_part then
result = result or hash_func(
string.gsub(key, ".", function(c)
return string.char(bit32_bxor(string.byte(c), 0x5c))
end) .. string.rep("\\", block_size - KeyLength) -- \ = string.char(0x5c)
.. (string.gsub(append(), "%x%x", HexToBinFunction))
)
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
-- Actually perform calculations and return the HMAC of a message
local FinalMessage = partial(message)()
return AsBinary and (string.gsub(FinalMessage, "%x%x", BinaryStringMap)) or FinalMessage
else
-- Return function for chunk-by-chunk loading of a message
-- User should feed every chunk of the message as single argument to this function and finally get HMAC by invoking this function without an argument
return partial
end
end
local sha = {
md5 = md5,
sha1 = sha1,
-- SHA2 hash functions:
sha224 = function(message)
return sha256ext(224, message)
end;
sha256 = function(message)
return sha256ext(256, message)
end;
sha512_224 = function(message)
return sha512ext(224, message)
end;
sha512_256 = function(message)
return sha512ext(256, message)
end;
sha384 = function(message)
return sha512ext(384, message)
end;
sha512 = function(message)
return sha512ext(512, message)
end;
-- SHA3 hash functions:
sha3_224 = function(message)
return keccak((1600 - 2 * 224) / 8, 224 / 8, false, message)
end;
sha3_256 = function(message)
return keccak((1600 - 2 * 256) / 8, 256 / 8, false, message)
end;
sha3_384 = function(message)
return keccak((1600 - 2 * 384) / 8, 384 / 8, false, message)
end;
sha3_512 = function(message)
return keccak((1600 - 2 * 512) / 8, 512 / 8, false, message)
end;
shake128 = function(message, digest_size_in_bytes)
return keccak((1600 - 2 * 128) / 8, digest_size_in_bytes, true, message)
end;
shake256 = function(message, digest_size_in_bytes)
return keccak((1600 - 2 * 256) / 8, digest_size_in_bytes, true, message)
end;
-- misc utilities:
hmac = hmac; -- HMAC(hash_func, key, message) is applicable to any hash function from this module except SHAKE*
hex_to_bin = hex2bin; -- converts hexadecimal representation to binary string
base64_to_bin = base642bin; -- converts base64 representation to binary string
bin_to_base64 = bin2base64; -- converts binary string to base64 representation
base64_encode = Base64.Encode;
base64_decode = Base64.Decode;
}
block_size_for_HMAC = {
[sha.md5] = 64;
[sha.sha1] = 64;
[sha.sha224] = 64;
[sha.sha256] = 64;
[sha.sha512_224] = 128;
[sha.sha512_256] = 128;
[sha.sha384] = 128;
[sha.sha512] = 128;
[sha.sha3_224] = (1600 - 2 * 224) / 8;
[sha.sha3_256] = (1600 - 2 * 256) / 8;
[sha.sha3_384] = (1600 - 2 * 384) / 8;
[sha.sha3_512] = (1600 - 2 * 512) / 8;
}
return sha
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment