Skip to content

Instantly share code, notes, and snippets.

@howmanysmall
Created January 7, 2020 22:12
Show Gist options
  • Save howmanysmall/f41a2832d98209329971675f1a48c117 to your computer and use it in GitHub Desktop.
Save howmanysmall/f41a2832d98209329971675f1a48c117 to your computer and use it in GitHub Desktop.
local module = {}
local AND = bit32.band
local NOT = bit32.bnot
local OR = bit32.bor
local XOR = bit32.bxor
local ROL = bit32.lrotate
local SHL = bit32.lshift
local ROR = bit32.rrotate
local SHR = bit32.rshift
-- Every value up to 64 bits worth
local binValues = {1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483648, 4294967296, 8589934592, 17179869184, 34359738368, 68719476736, 137438953472, 274877906944, 549755813888, 1099511627776, 2199023255552, 4398046511104, 8796093022208, 17592186044416, 35184372088832, 70368744177664, 1.4073748835533e+14, 2.8147497671066e+14, 5.6294995342131e+14, 1.1258999068426e+15, 2.2517998136852e+15, 4.5035996273705e+15, 9.007199254741e+15, 1.8014398509482e+16, 3.6028797018964e+16, 7.2057594037928e+16, 1.4411518807586e+17, 2.8823037615171e+17, 5.7646075230342e+17, 1.1529215046068e+18, 2.3058430092137e+18, 4.6116860184274e+18, 9.2233720368548e+18}
local binValuesLength = 64
-- Binary values from 0..255
local commonBinary = {"00000000", "00000001", "00000010", "00000011", "00000100", "00000101", "00000110", "00000111", "00001000", "00001001", "00001010", "00001011", "00001100", "00001101", "00001110", "00001111", "00010000", "00010001", "00010010", "00010011", "00010100", "00010101", "00010110", "00010111", "00011000", "00011001", "00011010", "00011011", "00011100", "00011101", "00011110", "00011111", "00100000", "00100001", "00100010", "00100011", "00100100", "00100101", "00100110", "00100111", "00101000", "00101001", "00101010", "00101011", "00101100", "00101101", "00101110", "00101111", "00110000", "00110001", "00110010", "00110011", "00110100", "00110101", "00110110", "00110111", "00111000", "00111001", "00111010", "00111011", "00111100", "00111101", "00111110", "00111111", "01000000", "01000001", "01000010", "01000011", "01000100", "01000101", "01000110", "01000111", "01001000", "01001001", "01001010", "01001011", "01001100", "01001101", "01001110", "01001111", "01010000", "01010001", "01010010", "01010011", "01010100", "01010101", "01010110", "01010111", "01011000", "01011001", "01011010", "01011011", "01011100", "01011101", "01011110", "01011111", "01100000", "01100001", "01100010", "01100011", "01100100", "01100101", "01100110", "01100111", "01101000", "01101001", "01101010", "01101011", "01101100", "01101101", "01101110", "01101111", "01110000", "01110001", "01110010", "01110011", "01110100", "01110101", "01110110", "01110111", "01111000", "01111001", "01111010", "01111011", "01111100", "01111101", "01111110", "01111111", "10000000", "10000001", "10000010", "10000011", "10000100", "10000101", "10000110", "10000111", "10001000", "10001001", "10001010", "10001011", "10001100", "10001101", "10001110", "10001111", "10010000", "10010001", "10010010", "10010011", "10010100", "10010101", "10010110", "10010111", "10011000", "10011001", "10011010", "10011011", "10011100", "10011101", "10011110", "10011111", "10100000", "10100001", "10100010", "10100011", "10100100", "10100101", "10100110", "10100111", "10101000", "10101001", "10101010", "10101011", "10101100", "10101101", "10101110", "10101111", "10110000", "10110001", "10110010", "10110011", "10110100", "10110101", "10110110", "10110111", "10111000", "10111001", "10111010", "10111011", "10111100", "10111101", "10111110", "10111111", "11000000", "11000001", "11000010", "11000011", "11000100", "11000101", "11000110", "11000111", "11001000", "11001001", "11001010", "11001011", "11001100", "11001101", "11001110", "11001111", "11010000", "11010001", "11010010", "11010011", "11010100", "11010101", "11010110", "11010111", "11011000", "11011001", "11011010", "11011011", "11011100", "11011101", "11011110", "11011111", "11100000", "11100001", "11100010", "11100011", "11100100", "11100101", "11100110", "11100111", "11101000", "11101001", "11101010", "11101011", "11101100", "11101101", "11101110", "11101111", "11110000", "11110001", "11110010", "11110011", "11110100", "11110101", "11110110", "11110111", "11111000", "11111001", "11111010", "11111011", "11111100", "11111101", "11111110", "11111111"}
local TwoPower = setmetatable({}, {
__index = function(self, Index)
local Value = 2^Index
self[Index] = Value
return Value
end;
})
function module.numToBinary(int, width)
local width = width or 8
if width > 64 then
for i = 64, width - 1 do
binValuesLength = binValuesLength + 1
binValues[binValuesLength] = TwoPower[i]
end
end
--assert(int < 2^width, string.format("int value out of range for %s bit width", width))
local value = ""
for i = width, 1, -1 do
local v = binValues[i]
if int >= v then
value = value.."1"
int = int - v
else
value = value.."0"
end
end
return value
end
function module.binaryToNum(str)
local len = #str
-- Generate binary values for the width given
if len > 64 then
for i = 64, len - 1 do
binValuesLength = binValuesLength + 1
binValues[binValuesLength] = TwoPower[i]
end
end
local int = 0
for i = 1, len do
if string.sub(str, i, i) == "1" then
int = int + binValues[len - (i-1)]
end
end
return int
end
local module_numToBinary = module.numToBinary
local module_binaryToNum = module.binaryToNum
local base16Values = {"0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"}
function module.binaryToHex(str, prepend0x)
local prepend0x = prepend0x == nil and true or prepend0x
local hex = ""
for i = 1, #str, 4 do
hex = hex .. base16Values[module_binaryToNum(string.sub(str,i,i+3))+1]
end
if prepend0x then
return "0x"..hex
else
return hex
end
end
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
function module.base64Encode(str)
local Output = {}
local Length = 0
for Index = 1, #str, 3 do
local C1, C2, C3 = string.byte(str, Index, Index + 2)
local A = SHR(C1, 2)
local B = SHL(AND(C1, 3), 4) + SHR(C2 or 0, 4)
local C = SHL(AND(C2 or 0, 15), 2) + SHR(C3 or 0, 6)
local D = AND(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
local Base64 = table.concat(NewOutput)
local Base64Length = #Base64
for Index = 76, Base64Length, 76 do
Base64 = string.sub(Base64, Index, Index) .. "\n" .. string.sub(Base64, Index + 1, Base64Length)
end
return Base64
end
--module.base64Decode = FasterBase64.Decode
function module.base64Decode(base64)
assert(type(base64) == "string", "Base64 must be quotable for decoding")
base64 = string.gsub(base64, "\n", "")
local Output = {}
local Length = 0
for Index = 1, #base64, 4 do
local C1, C2, C3, C4 = string.byte(base64, 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 = SHL(I1, 2) + SHR(I2, 4)
local B = SHL(AND(I2, 15), 4) + SHR(I3, 2)
local C = SHL(AND(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
-- Define internal sha2 functions
local function ch(x, y, z)
return XOR(AND(x,y),AND(NOT(x),z))
end
local function maj(x, y, z)
return XOR(XOR(AND(x,y),AND(x,z)),AND(y,z))
end
local function bsig0(x)
return XOR(XOR(ROR(x, 2),ROR(x, 13)),ROR(x, 22))
end
local function bsig1(x)
return XOR(XOR(ROR(x, 6),ROR(x, 11)),ROR(x, 25))
end
local function ssig0(x)
return XOR(XOR(ROR(x, 7),ROR(x, 18)),SHR(x, 3))
end
local function ssig1(x)
return XOR(XOR(ROR(x, 17),ROR(x, 19)),SHR(x, 10))
end
-- Define constants K[1..64]
local K = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
}
function module.sha256(str)
-- Generate bit length and string
local len = #str
local L = len * 8
local bitArray = {}
local bitArrayLength = len
for i = 1, len do
bitArray[i] = commonBinary[string.byte(str,i)+1]
end
-- If str == "" then hash for 0 bit length (this satisfies a test vector)
if len == 0 then
bitArray[1] = "10000000"
for i = 2,64 do
bitArray[i] = "00000000"
end
L = 512
end
-- Pad bitString so the length is a multiple of 512
if L % 512 ~= 0 then
local pad = (448 - ((L + 1) % 512)) % 512
bitArrayLength = bitArrayLength + 1
bitArray[bitArrayLength] = "10000000"
for i = 1, (pad-7)/8 do
bitArrayLength = bitArrayLength + 1
bitArray[bitArrayLength] = "00000000"
end
-- add message length to end
len = module_numToBinary(L, 64)
for i = 1, 64, 8 do
bitArrayLength = bitArrayLength + 1
bitArray[bitArrayLength] = string.sub(len,i,i+7)
end
end
-- Split bitString into blocks of 512 bits
local blocks = {}
local blocksLength = 0
for i = 1, bitArrayLength, 64 do
local words = table.create(16)
local wordsLength = 0
for w = 1, 64, 4 do
wordsLength = wordsLength + 1
words[wordsLength] = module_binaryToNum(table.concat(bitArray, "", w, w+3))
end
blocksLength = blocksLength + 1
blocks[blocksLength] = words
end
-- Initialise hash values H[1..8]
local H = table.create(8)
H[1] = 0x6a09e667
H[2] = 0xbb67ae85
H[3] = 0x3c6ef372
H[4] = 0xa54ff53a
H[5] = 0x510e527f
H[6] = 0x9b05688c
H[7] = 0x1f83d9ab
H[8] = 0x5be0cd19
-- Main hash loop
for i, W in ipairs(blocks) do
-- Initialise message schedule W[1..64] for block
for t = 17, 64 do
W[t] = (ssig1(W[t-2]) + W[t-7] + ssig0(W[t-15]) + W[t-16]) % 4294967296
end
-- Define working variables
local a = H[1]
local b = H[2]
local d = H[4]
local c = H[3]
local e = H[5]
local f = H[6]
local g = H[7]
local h = H[8]
for t = 1, 64 do
local T1 = (h + bsig1(e) + ch(e, f, g) + K[t] + W[t]) % 4294967296
local T2 = (bsig0(a) + maj(a, b, c)) % 4294967296
h = g
g = f
f = e
e = (d + T1) % 4294967296
d = c
c = b
b = a
a = (T1 + T2) % 4294967296
end
-- Add working variables to the hash value table
H[1] = (a + H[1]) % 4294967296
H[2] = (b + H[2]) % 4294967296
H[3] = (c + H[3]) % 4294967296
H[4] = (d + H[4]) % 4294967296
H[5] = (e + H[5]) % 4294967296
H[6] = (f + H[6]) % 4294967296
H[7] = (g + H[7]) % 4294967296
H[8] = (h + H[8]) % 4294967296
end
-- Produce result as hex string
local result = ""
for v = 1, 8 do
result = result .. string.format("%x", H[v])
end
return result
end
function module.sha1(str)
-- Initial variables
local h0 = 0x67452301
local h1 = 0xEFCDAB89
local h2 = 0x98BADCFE
local h3 = 0x10325476
local h4 = 0xC3D2E1F0
local len = #str
-- Message length in bits
local ml = len * 8
local bitArray = {}
local bitArrayLength = len
for i = 1, len do
bitArray[i] = commonBinary[string.byte(str,i)+1]
end
-- If str == "" then hash for 0 bit length (this satisfies a test vector)
if len == 0 then
bitArray[1] = "10000000"
for i = 2,64 do
bitArray[i] = "00000000"
end
ml = 512
end
-- Pad bitString so the length is a multiple of 512
if ml % 512 ~= 0 then
local pad = (448 - ((ml + 1) % 512)) % 512
bitArrayLength = bitArrayLength + 1
bitArray[bitArrayLength] = "10000000"
for i = 1, (pad-7)/8 do
bitArrayLength = bitArrayLength + 1
bitArray[bitArrayLength] = "00000000"
end
-- add message length to end
len = module_numToBinary(ml, 64)
for i = 1, 64, 8 do
bitArrayLength = bitArrayLength + 1
bitArray[bitArrayLength] = string.sub(len,i,i+7)
end
end
-- Split bitString into blocks of 512 bits containing 16 32 bit words
local blocks = {}
local blocksLength = 0
for i = 1, bitArrayLength, 64 do
local words = table.create(16)
local wordsLength = 0
for w = 1, 64, 4 do
wordsLength = wordsLength + 1
words[wordsLength] = module_binaryToNum(table.concat(bitArray, "", w, w+3))
end
blocksLength = blocksLength + 1
blocks[blocksLength] = words
end
for i, W in ipairs(blocks) do
-- Break blocks into 16 32bit words
-- Extend message schedule
for t = 17, 80 do
W[t] = ROL(XOR(XOR(XOR(W[t-3], W[t-8]), W[t-14]), W[t-16]), 1)
end
local a = h0
local b = h1
local c = h2
local d = h3
local e = h4
for i = 1, 80 do
local f
local k
if i <= 20 then
f = OR(AND(b, c), AND(NOT(b), d))
k = 0x5A827999
elseif i <= 40 then
f = XOR(XOR(b, c), d)
k = 0x6ED9EBA1
elseif i <= 60 then
f = OR(OR(AND(b, c), AND(b, d)), AND(c, d))
k = 0x8F1BBCDC
elseif i <= 80 then
f = XOR(XOR(b, c), d)
k = 0xCA62C1D6
end
local T1 = (ROL(a, 5) + f + e + k + W[i]) % 4294967296
e = d
d = c
c = ROL(b, 30)
b = a
a = T1
end
h0 = (h0 + a) % 4294967296
h1 = (h1 + b) % 4294967296
h2 = (h2 + c) % 4294967296
h3 = (h3 + d) % 4294967296
h4 = (h4 + e) % 4294967296
end
return
string.format("%x", h0)..
string.format("%x", h1)..
string.format("%x", h2)..
string.format("%x", h3)..
string.format("%x", h4)
end
return module
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment