--[[
This file is part of luax.

luax is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

luax is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with luax.  If not, see <https://www.gnu.org/licenses/>.

For further information about luax you can visit
https://codeberg.org/cdsoft/luax
--]]

--@LIB

-- Pure Lua implementation of crypt.c

local crypt = {}

local floor = math.floor

local byte = string.byte
local char = string.char
local format = string.format
local gsub = string.gsub
local pack = string.pack

local concat = table.concat
local tunpack = table.unpack

local tonumber = tonumber

-- FNV-1a
local fnv1a_32_init = 0x811c9dc5
local fnv1a_32_prime = 1<<24 | 1<<8 | 0x93
local function fnv1a_32(hash, bs)
    for i=1,#bs do
        hash = (hash ~ byte(bs, i)) * fnv1a_32_prime
    end
    return hash & 0xFFFFFFFF
end

local fnv1a_64_init = 0xcbf29ce484222325
local fnv1a_64_prime = 1<<40 | 1<<8 | 0xb3
local function fnv1a_64(hash, bs)
    for i=1,#bs do
        hash = (hash ~ byte(bs, i)) * fnv1a_64_prime
    end
    return hash & 0xFFFFFFFFFFFFFFFF
end

local fnv1a_128_init = {0x6c62272e, 0x07bb0142, 0x62b82175, 0x6295c58d}
local fnv1a_128_prime_b, fnv1a_128_prime_d = 1<<(88-2*32), 1<<8 | 0x3b
local function fnv1a_128(hash, bs)
    local a, b, c, d = tunpack(hash)
    for i=1,#bs do
        d = d ~ byte(bs, i)
        local c0, d0 = c, d
        local carry
        d =         d0*fnv1a_128_prime_d                            d, carry = d & 0xFFFFFFFF, d >> 32
        c = carry + c0*fnv1a_128_prime_d                            c, carry = c & 0xFFFFFFFF, c >> 32
        b = carry + b *fnv1a_128_prime_d + d0*fnv1a_128_prime_b     b, carry = b & 0xFFFFFFFF, b >> 32
        a = carry + a *fnv1a_128_prime_d + c0*fnv1a_128_prime_b
    end
    return a&0xFFFFFFFF, b, c, d
end

-- Random number generator

local prng_mt = {__index={}}

local entropy do
    local hash = fnv1a_64_init
    entropy = function(ptr)
        hash = fnv1a_64(hash, pack("<I8I8I8I8",
            os.time(),
            floor(os.clock()*1000000),
            tonumber(format("%p", ptr)),
            tonumber(format("%p", {}))
        ))
        return hash
    end
end

local RAND_MAX <const> = 0xFFFFFFFF

crypt.RAND_MAX = RAND_MAX

local default_prng_state <const> = 0x4d595df4d0f33173
local prng_multiplier <const> = 6364136223846793005
local default_prng_increment <const> = 1442695040888963407

function crypt.prng(seed, incr)
    local self = setmetatable({}, prng_mt)
    return self:seed(seed, incr)
end

function prng_mt.__index:seed(seed, incr)
    if seed == -1 then seed = default_prng_state end
    if incr == -1 then incr = default_prng_increment end
    self.state = seed or entropy(self)
    self.increment = incr or entropy(self)
    self.state = prng_multiplier*self.state + self.increment
    self.state = prng_multiplier*self.state + self.increment
    return self
end

function prng_mt.__index:clone()
    local clone = {
        state = self.state,
        increment = self.increment,
    }
    return setmetatable(clone, prng_mt)
end

local function xsh_rr(state)
    local xorshifted = (((state >> 18) ~ state) >> 27) & 0xFFFFFFFF
    local rot = state >> 59
    return ((xorshifted >> rot) | (xorshifted << ((-rot) & 31))) & 0xFFFFFFFF
end

local function prng_int(self, a, b)
    local oldstate = self.state
    self.state = prng_multiplier*oldstate + self.increment
    local r = xsh_rr(oldstate)

    if not a then return r end
    if not b then return r % a + 1 end
    return r % (b-a+1) + a
end
prng_mt.__index.int = prng_int

local function prng_float(self, a, b)
    local r = prng_int(self) / RAND_MAX
    if not a then return r end
    if not b then return r * a end
    return r*(b-a) + a
end
prng_mt.__index.float = prng_float

local function prng_str(self, n)
    local bs = {}
    for i = 1, n, 4 do
        local r = prng_int(self)
        bs[i  ] = char((r>>(0*8))&0xff)
        bs[i+1] = char((r>>(1*8))&0xff)
        bs[i+2] = char((r>>(2*8))&0xff)
        bs[i+3] = char((r>>(3*8))     )
    end
    return concat(bs, nil, 1, n)
end
prng_mt.__index.str = prng_str

-- global random number generator
local _rng = crypt.prng()

function crypt.seed(seed, incr) return _rng:seed(seed, incr) end

function crypt.int(a, b) return _rng:int(a, b) end

function crypt.float(a, b) return _rng:float(a, b) end

function crypt.str(n) return _rng:str(n) end

-- Hexadecimal encoding

function crypt.hex(s)
    return (gsub(s, '.', function(c) return format("%02x", byte(c)) end))
end

function crypt.unhex(s)
    return (gsub(s, '..', function(h) return char(tonumber(h, 16)) end))
end

-- Base64 encoding

-- see <https://en.wikipedia.org/wiki/Base64>

local base64_map = { [0] =
   'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
   'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
   '0','1','2','3','4','5','6','7','8','9',
   '+','/',
}

local base64_rev = {}
for i, c in pairs(base64_map) do base64_rev[byte(c)] = i end
base64_rev[byte'='] = 0

function crypt.base64(s)
    local tokens = {}
    local remainder = #s % 3
    for i = 1, #s-remainder, 3 do
        local a, b, c = byte(s, i, i+2)
        tokens[#tokens+1] = base64_map[a>>2]
        tokens[#tokens+1] = base64_map[(a&3)<<4|b>>4]
        tokens[#tokens+1] = base64_map[(b&15)<<2|c>>6]
        tokens[#tokens+1] = base64_map[c&63]
    end
    if remainder == 1 then
        local a = byte(s, -1)
        tokens[#tokens+1] = base64_map[a>>2]
        tokens[#tokens+1] = base64_map[(a&3)<<4]
        tokens[#tokens+1] = "=="
    elseif remainder == 2 then
        local a, b = byte(s, -2, -1)
        tokens[#tokens+1] = base64_map[a>>2]
        tokens[#tokens+1] = base64_map[(a&3)<<4|b>>4]
        tokens[#tokens+1] = base64_map[(b&15)<<2]
        tokens[#tokens+1] = "="
    end
    return concat(tokens)
end

function crypt.base64url(s)
    return (crypt.base64(s):gsub("+", "-"):gsub("/", "_"))
end

function crypt.unbase64(s)
    local tokens = {}
    for i = 1, #s, 4 do
        local a, b, c, d = byte(s, i, i+3)
        a = base64_rev[a]
        b = base64_rev[b]
        c = base64_rev[c]
        d = base64_rev[d]
        tokens[#tokens+1] = char((a<<2 | b>>4)&0xFF)
        tokens[#tokens+1] = char((b<<4 | c>>2)&0xFF)
        tokens[#tokens+1] = char((c<<6 | d)&0xFF)
    end
    local y, z = byte(s, -2, -1)
    return concat(tokens, nil, 1, #tokens - (y==61 and 2 or z==61 and 1 or 0))
end

function crypt.unbase64url(s)
    return crypt.unbase64(s:gsub("-", "+"):gsub("_", "/"))
end

-- CRC32 hash

local crc32_table = { [0]=
    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
    0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
    0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
    0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
    0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
    0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
    0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
    0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
    0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
    0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
    0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
    0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
    0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
    0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
    0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
    0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
    0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
    0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
    0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
    0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
    0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
    0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
}

function crypt.crc32(s)
    local crc = 0xFFFFFFFF
    for i = 1, #s do
        crc = (crc>>8) ~ crc32_table[(crc~byte(s, i))&0xFF]
    end
    return crc ~ 0xFFFFFFFF
end

-- CRC64 hash

local crc64_table = { [0]=
    0x0000000000000000, 0xb32e4cbe03a75f6f, 0xf4843657a840a05b, 0x47aa7ae9abe7ff34,
    0x7bd0c384ff8f5e33, 0xc8fe8f3afc28015c, 0x8f54f5d357cffe68, 0x3c7ab96d5468a107,
    0xf7a18709ff1ebc66, 0x448fcbb7fcb9e309, 0x0325b15e575e1c3d, 0xb00bfde054f94352,
    0x8c71448d0091e255, 0x3f5f08330336bd3a, 0x78f572daa8d1420e, 0xcbdb3e64ab761d61,
    0x7d9ba13851336649, 0xceb5ed8652943926, 0x891f976ff973c612, 0x3a31dbd1fad4997d,
    0x064b62bcaebc387a, 0xb5652e02ad1b6715, 0xf2cf54eb06fc9821, 0x41e11855055bc74e,
    0x8a3a2631ae2dda2f, 0x39146a8fad8a8540, 0x7ebe1066066d7a74, 0xcd905cd805ca251b,
    0xf1eae5b551a2841c, 0x42c4a90b5205db73, 0x056ed3e2f9e22447, 0xb6409f5cfa457b28,
    0xfb374270a266cc92, 0x48190ecea1c193fd, 0x0fb374270a266cc9, 0xbc9d3899098133a6,
    0x80e781f45de992a1, 0x33c9cd4a5e4ecdce, 0x7463b7a3f5a932fa, 0xc74dfb1df60e6d95,
    0x0c96c5795d7870f4, 0xbfb889c75edf2f9b, 0xf812f32ef538d0af, 0x4b3cbf90f69f8fc0,
    0x774606fda2f72ec7, 0xc4684a43a15071a8, 0x83c230aa0ab78e9c, 0x30ec7c140910d1f3,
    0x86ace348f355aadb, 0x3582aff6f0f2f5b4, 0x7228d51f5b150a80, 0xc10699a158b255ef,
    0xfd7c20cc0cdaf4e8, 0x4e526c720f7dab87, 0x09f8169ba49a54b3, 0xbad65a25a73d0bdc,
    0x710d64410c4b16bd, 0xc22328ff0fec49d2, 0x85895216a40bb6e6, 0x36a71ea8a7ace989,
    0x0adda7c5f3c4488e, 0xb9f3eb7bf06317e1, 0xfe5991925b84e8d5, 0x4d77dd2c5823b7ba,
    0x64b62bcaebc387a1, 0xd7986774e864d8ce, 0x90321d9d438327fa, 0x231c512340247895,
    0x1f66e84e144cd992, 0xac48a4f017eb86fd, 0xebe2de19bc0c79c9, 0x58cc92a7bfab26a6,
    0x9317acc314dd3bc7, 0x2039e07d177a64a8, 0x67939a94bc9d9b9c, 0xd4bdd62abf3ac4f3,
    0xe8c76f47eb5265f4, 0x5be923f9e8f53a9b, 0x1c4359104312c5af, 0xaf6d15ae40b59ac0,
    0x192d8af2baf0e1e8, 0xaa03c64cb957be87, 0xeda9bca512b041b3, 0x5e87f01b11171edc,
    0x62fd4976457fbfdb, 0xd1d305c846d8e0b4, 0x96797f21ed3f1f80, 0x2557339fee9840ef,
    0xee8c0dfb45ee5d8e, 0x5da24145464902e1, 0x1a083bacedaefdd5, 0xa9267712ee09a2ba,
    0x955cce7fba6103bd, 0x267282c1b9c65cd2, 0x61d8f8281221a3e6, 0xd2f6b4961186fc89,
    0x9f8169ba49a54b33, 0x2caf25044a02145c, 0x6b055fede1e5eb68, 0xd82b1353e242b407,
    0xe451aa3eb62a1500, 0x577fe680b58d4a6f, 0x10d59c691e6ab55b, 0xa3fbd0d71dcdea34,
    0x6820eeb3b6bbf755, 0xdb0ea20db51ca83a, 0x9ca4d8e41efb570e, 0x2f8a945a1d5c0861,
    0x13f02d374934a966, 0xa0de61894a93f609, 0xe7741b60e174093d, 0x545a57dee2d35652,
    0xe21ac88218962d7a, 0x5134843c1b317215, 0x169efed5b0d68d21, 0xa5b0b26bb371d24e,
    0x99ca0b06e7197349, 0x2ae447b8e4be2c26, 0x6d4e3d514f59d312, 0xde6071ef4cfe8c7d,
    0x15bb4f8be788911c, 0xa6950335e42fce73, 0xe13f79dc4fc83147, 0x521135624c6f6e28,
    0x6e6b8c0f1807cf2f, 0xdd45c0b11ba09040, 0x9aefba58b0476f74, 0x29c1f6e6b3e0301b,
    0xc96c5795d7870f42, 0x7a421b2bd420502d, 0x3de861c27fc7af19, 0x8ec62d7c7c60f076,
    0xb2bc941128085171, 0x0192d8af2baf0e1e, 0x4638a2468048f12a, 0xf516eef883efae45,
    0x3ecdd09c2899b324, 0x8de39c222b3eec4b, 0xca49e6cb80d9137f, 0x7967aa75837e4c10,
    0x451d1318d716ed17, 0xf6335fa6d4b1b278, 0xb199254f7f564d4c, 0x02b769f17cf11223,
    0xb4f7f6ad86b4690b, 0x07d9ba1385133664, 0x4073c0fa2ef4c950, 0xf35d8c442d53963f,
    0xcf273529793b3738, 0x7c0979977a9c6857, 0x3ba3037ed17b9763, 0x888d4fc0d2dcc80c,
    0x435671a479aad56d, 0xf0783d1a7a0d8a02, 0xb7d247f3d1ea7536, 0x04fc0b4dd24d2a59,
    0x3886b22086258b5e, 0x8ba8fe9e8582d431, 0xcc0284772e652b05, 0x7f2cc8c92dc2746a,
    0x325b15e575e1c3d0, 0x8175595b76469cbf, 0xc6df23b2dda1638b, 0x75f16f0cde063ce4,
    0x498bd6618a6e9de3, 0xfaa59adf89c9c28c, 0xbd0fe036222e3db8, 0x0e21ac88218962d7,
    0xc5fa92ec8aff7fb6, 0x76d4de52895820d9, 0x317ea4bb22bfdfed, 0x8250e80521188082,
    0xbe2a516875702185, 0x0d041dd676d77eea, 0x4aae673fdd3081de, 0xf9802b81de97deb1,
    0x4fc0b4dd24d2a599, 0xfceef8632775faf6, 0xbb44828a8c9205c2, 0x086ace348f355aad,
    0x34107759db5dfbaa, 0x873e3be7d8faa4c5, 0xc094410e731d5bf1, 0x73ba0db070ba049e,
    0xb86133d4dbcc19ff, 0x0b4f7f6ad86b4690, 0x4ce50583738cb9a4, 0xffcb493d702be6cb,
    0xc3b1f050244347cc, 0x709fbcee27e418a3, 0x3735c6078c03e797, 0x841b8ab98fa4b8f8,
    0xadda7c5f3c4488e3, 0x1ef430e13fe3d78c, 0x595e4a08940428b8, 0xea7006b697a377d7,
    0xd60abfdbc3cbd6d0, 0x6524f365c06c89bf, 0x228e898c6b8b768b, 0x91a0c532682c29e4,
    0x5a7bfb56c35a3485, 0xe955b7e8c0fd6bea, 0xaeffcd016b1a94de, 0x1dd181bf68bdcbb1,
    0x21ab38d23cd56ab6, 0x9285746c3f7235d9, 0xd52f0e859495caed, 0x6601423b97329582,
    0xd041dd676d77eeaa, 0x636f91d96ed0b1c5, 0x24c5eb30c5374ef1, 0x97eba78ec690119e,
    0xab911ee392f8b099, 0x18bf525d915feff6, 0x5f1528b43ab810c2, 0xec3b640a391f4fad,
    0x27e05a6e926952cc, 0x94ce16d091ce0da3, 0xd3646c393a29f297, 0x604a2087398eadf8,
    0x5c3099ea6de60cff, 0xef1ed5546e415390, 0xa8b4afbdc5a6aca4, 0x1b9ae303c601f3cb,
    0x56ed3e2f9e224471, 0xe5c372919d851b1e, 0xa26908783662e42a, 0x114744c635c5bb45,
    0x2d3dfdab61ad1a42, 0x9e13b115620a452d, 0xd9b9cbfcc9edba19, 0x6a978742ca4ae576,
    0xa14cb926613cf817, 0x1262f598629ba778, 0x55c88f71c97c584c, 0xe6e6c3cfcadb0723,
    0xda9c7aa29eb3a624, 0x69b2361c9d14f94b, 0x2e184cf536f3067f, 0x9d36004b35545910,
    0x2b769f17cf112238, 0x9858d3a9ccb67d57, 0xdff2a94067518263, 0x6cdce5fe64f6dd0c,
    0x50a65c93309e7c0b, 0xe388102d33392364, 0xa4226ac498dedc50, 0x170c267a9b79833f,
    0xdcd7181e300f9e5e, 0x6ff954a033a8c131, 0x28532e49984f3e05, 0x9b7d62f79be8616a,
    0xa707db9acf80c06d, 0x14299724cc279f02, 0x5383edcd67c06036, 0xe0ada17364673f59
}

function crypt.crc64(s)
    local crc = 0xFFFFFFFFFFFFFFFF
    for i = 1, #s do
        crc = (crc>>8) ~ crc64_table[(crc~byte(s, i))&0xFF]
    end
    return crc ~ 0xFFFFFFFFFFFFFFFF
end

-- ARC4 encryption

function crypt.arc4(input, key, drop)
    assert(type(key) == "string", "arc4 key shall be a string")
    drop = drop or 768
    local S = {}
    for i = 0, 255 do S[i] = i end
    local j = 0
    if #key > 0 then
        for i = 0, 255 do
            j = (j + S[i] + byte(key, i%#key+1)) % 256
            S[i], S[j] = S[j], S[i]
        end
    end
    local i = 0
    j = 0
    for _ = 1, drop do
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
    end
    local output = {}
    for k = 1, #input do
        i = (i + 1) % 256
        j = (j + S[i]) % 256
        S[i], S[j] = S[j], S[i]
        output[k] = char(byte(input, k) ~ S[(S[i] + S[j]) % 256])
    end
    return concat(output)
end

crypt.unarc4 = crypt.arc4

function crypt.hash32(s) return ("<I4"):pack(fnv1a_32(fnv1a_32_init, s)):hex() end

function crypt.hash64(s) return ("<I8"):pack(fnv1a_64(fnv1a_64_init, s)):hex() end

function crypt.hash128(s)
    local a, b, c, d = fnv1a_128(fnv1a_128_init, s)
    return ("<I4I4I4I4"):pack(d, c, b, a):hex()
end

crypt.hash = crypt.hash64

return crypt
