local byte, char, concat = string.byte, string.char, table.concat local band, bxor, rshift, lshift = bit32.band, bit32.bxor, bit32.rshift, bit32.lshift local wait = function() end local B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" local B64R = {} for i = 1, #B64 do B64R[byte(B64, i)] = i - 1 end local function b64(s, expected_len) local out, cur, cn = {}, {}, 0 local len = #s local i = 1 local next_yield = 262144 -- Four input characters produce three output bytes. Decoding in one -- operation per character pair is what keeps this loop fast on mobile. while i + 3 <= len do local b1, b2, b3, b4 = byte(s, i, i + 3) if b2 == 61 or b3 == 61 or b4 == 61 then break end local v1, v2, v3, v4 = B64R[b1], B64R[b2], B64R[b3], B64R[b4] if v1 and v2 and v3 and v4 then local x = v1 * 262144 + v2 * 4096 + v3 * 64 + v4 cn = cn + 1 cur[cn] = char(band(rshift(x, 16), 255), band(rshift(x, 8), 255), band(x, 255)) if cn >= 4096 then out[#out + 1] = concat(cur) cur = {} cn = 0 end end i = i + 4 if wait and i >= next_yield then wait(0) next_yield = next_yield + 262144 end end local acc, bits = 0, 0 while i <= len do local c = byte(s, i) if c == 61 then break end local v = B64R[c] if v then acc = acc * 64 + v bits = bits + 6 if bits >= 8 then bits = bits - 8 cn = cn + 1 cur[cn] = char(band(rshift(acc, bits), 255)) acc = band(acc, lshift(1, bits) - 1) end end i = i + 1 end out[#out + 1] = concat(cur) local result = concat(out) if expected_len and #result ~= expected_len then return nil end return result end local buffer_lib = type(buffer) == "table" and type(buffer.fromstring) == "function" and type(buffer.create) == "function" and type(buffer.tostring) == "function" and type(buffer.readu32) == "function" and type(buffer.writeu32) == "function" and type(buffer.readu8) == "function" and type(buffer.writeu8) == "function" and buffer or nil local function make_ks64(key) local ks = {} for lb = 0, 511 do local idx = ((lb % 256) - 1) % 32 + 1 ks[lb + 1] = char(band(bxor(byte(key, idx), band(lb, 255)), 255)) end local kss = concat(ks) local ksb = buffer_lib.fromstring(kss) local out = buffer_lib.create(65536) local p = 1 while p <= 65536 do local lo = p % 256 local hi = band(rshift(p, 8), 255) local hi2 = band(rshift(p + 3, 8), 255) if hi == hi2 then local hi4 = hi + hi * 256 + hi * 65536 + hi * 16777216 buffer_lib.writeu32(out, p - 1, bxor(buffer_lib.readu32(ksb, lo), hi4)) else for q = p, p + 3 do buffer_lib.writeu8(out, q - 1, bxor(byte(kss, (q % 256) + 1), band(rshift(q, 8), 255))) end end p = p + 4 end return buffer_lib.tostring(out) end local function decrypt_buf(s, key, ks) local n = #s local src = buffer_lib.fromstring(s) local dst = buffer_lib.create(n) local ksb = buffer_lib.fromstring(ks) local off, ko = 0, 0 local next_yield = 262144 while off + 64 <= n do local a1 = buffer_lib.readu32(src, off) local a2 = buffer_lib.readu32(src, off + 4) local a3 = buffer_lib.readu32(src, off + 8) local a4 = buffer_lib.readu32(src, off + 12) local a5 = buffer_lib.readu32(src, off + 16) local a6 = buffer_lib.readu32(src, off + 20) local a7 = buffer_lib.readu32(src, off + 24) local a8 = buffer_lib.readu32(src, off + 28) local a9 = buffer_lib.readu32(src, off + 32) local a10 = buffer_lib.readu32(src, off + 36) local a11 = buffer_lib.readu32(src, off + 40) local a12 = buffer_lib.readu32(src, off + 44) local a13 = buffer_lib.readu32(src, off + 48) local a14 = buffer_lib.readu32(src, off + 52) local a15 = buffer_lib.readu32(src, off + 56) local a16 = buffer_lib.readu32(src, off + 60) local k1 = buffer_lib.readu32(ksb, ko) local k2 = buffer_lib.readu32(ksb, ko + 4) local k3 = buffer_lib.readu32(ksb, ko + 8) local k4 = buffer_lib.readu32(ksb, ko + 12) local k5 = buffer_lib.readu32(ksb, ko + 16) local k6 = buffer_lib.readu32(ksb, ko + 20) local k7 = buffer_lib.readu32(ksb, ko + 24) local k8 = buffer_lib.readu32(ksb, ko + 28) local k9 = buffer_lib.readu32(ksb, ko + 32) local k10 = buffer_lib.readu32(ksb, ko + 36) local k11 = buffer_lib.readu32(ksb, ko + 40) local k12 = buffer_lib.readu32(ksb, ko + 44) local k13 = buffer_lib.readu32(ksb, ko + 48) local k14 = buffer_lib.readu32(ksb, ko + 52) local k15 = buffer_lib.readu32(ksb, ko + 56) local k16 = buffer_lib.readu32(ksb, ko + 60) buffer_lib.writeu32(dst, off, bxor(a1, k1)) buffer_lib.writeu32(dst, off + 4, bxor(a2, k2)) buffer_lib.writeu32(dst, off + 8, bxor(a3, k3)) buffer_lib.writeu32(dst, off + 12, bxor(a4, k4)) buffer_lib.writeu32(dst, off + 16, bxor(a5, k5)) buffer_lib.writeu32(dst, off + 20, bxor(a6, k6)) buffer_lib.writeu32(dst, off + 24, bxor(a7, k7)) buffer_lib.writeu32(dst, off + 28, bxor(a8, k8)) buffer_lib.writeu32(dst, off + 32, bxor(a9, k9)) buffer_lib.writeu32(dst, off + 36, bxor(a10, k10)) buffer_lib.writeu32(dst, off + 40, bxor(a11, k11)) buffer_lib.writeu32(dst, off + 44, bxor(a12, k12)) buffer_lib.writeu32(dst, off + 48, bxor(a13, k13)) buffer_lib.writeu32(dst, off + 52, bxor(a14, k14)) buffer_lib.writeu32(dst, off + 56, bxor(a15, k15)) buffer_lib.writeu32(dst, off + 60, bxor(a16, k16)) off = off + 64 ko = ko + 64 if ko >= 65536 then ko = 0 end if wait and off >= next_yield then wait(0) next_yield = next_yield + 262144 end end while off + 16 <= n do local a1 = buffer_lib.readu32(src, off) local a2 = buffer_lib.readu32(src, off + 4) local a3 = buffer_lib.readu32(src, off + 8) local a4 = buffer_lib.readu32(src, off + 12) local k1 = buffer_lib.readu32(ksb, ko) local k2 = buffer_lib.readu32(ksb, ko + 4) local k3 = buffer_lib.readu32(ksb, ko + 8) local k4 = buffer_lib.readu32(ksb, ko + 12) buffer_lib.writeu32(dst, off, bxor(a1, k1)) buffer_lib.writeu32(dst, off + 4, bxor(a2, k2)) buffer_lib.writeu32(dst, off + 8, bxor(a3, k3)) buffer_lib.writeu32(dst, off + 12, bxor(a4, k4)) off = off + 16 ko = ko + 16 if ko >= 65536 then ko = 0 end end while off + 4 <= n do buffer_lib.writeu32(dst, off, bxor(buffer_lib.readu32(src, off), buffer_lib.readu32(ksb, ko))) off = off + 4 ko = ko + 4 if ko >= 65536 then ko = 0 end end while off < n do buffer_lib.writeu8(dst, off, bxor(buffer_lib.readu8(src, off), buffer_lib.readu8(ksb, ko))) off = off + 1 ko = ko + 1 if ko >= 65536 then ko = 0 end end return buffer_lib.tostring(dst) end local function decrypt_fast(s, key) local n = #s local out, cur, cn = {}, {}, 0 local ks = {} for lb = 0, 511 do local idx = ((lb % 256) - 1) % 32 + 1 ks[lb + 1] = char(band(bxor(byte(key, idx), band(lb, 255)), 255)) end local kss = concat(ks) local i = 1 local next_yield = 262144 while i + 15 <= n do local lo = i % 256 local hi = band(rshift(i, 8), 255) local hi2 = band(rshift(i + 15, 8), 255) if hi == hi2 then local b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16 = byte(s, i, i + 15) local k1, k2, k3, k4, k5, k6, k7, k8, k9, k10, k11, k12, k13, k14, k15, k16 = byte(kss, lo + 1, lo + 16) cn = cn + 1 cur[cn] = char( bxor(b1, k1, hi), bxor(b2, k2, hi), bxor(b3, k3, hi), bxor(b4, k4, hi), bxor(b5, k5, hi), bxor(b6, k6, hi), bxor(b7, k7, hi), bxor(b8, k8, hi), bxor(b9, k9, hi), bxor(b10, k10, hi), bxor(b11, k11, hi), bxor(b12, k12, hi), bxor(b13, k13, hi), bxor(b14, k14, hi), bxor(b15, k15, hi), bxor(b16, k16, hi)) else for p = i, i + 15 do cn = cn + 1 cur[cn] = char(bxor(byte(s, p), byte(kss, (p % 256) + 1), band(rshift(p, 8), 255))) end end i = i + 16 if cn >= 4096 then out[#out + 1] = concat(cur) cur = {} cn = 0 end if wait and i >= next_yield then wait(0) next_yield = next_yield + 262144 end end while i <= n do cn = cn + 1 cur[cn] = char(bxor(byte(s, i), byte(kss, (i % 256) + 1), band(rshift(i, 8), 255))) i = i + 1 end out[#out + 1] = concat(cur) return concat(out) end local function decrypt(s, key) if #key == 0 then return nil end if buffer_lib then local ok, out = pcall(function() return decrypt_buf(s, key, make_ks64(key)) end) if ok and type(out) == "string" and #out == #s then return out end end return decrypt_fast(s, key) end local B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" local function b64enc(s) local out, cur, cn = {}, {}, 0 local i, len = 1, #s while i + 2 <= len do local a, b, c = byte(s, i, i + 2) local x = a * 65536 + b * 256 + c cn = cn + 1 cur[cn] = string.sub(B64, band(rshift(x, 18), 63) + 1, band(rshift(x, 18), 63) + 1) .. string.sub(B64, band(rshift(x, 12), 63) + 1, band(rshift(x, 12), 63) + 1) .. string.sub(B64, band(rshift(x, 6), 63) + 1, band(rshift(x, 6), 63) + 1) .. string.sub(B64, band(x, 63) + 1, band(x, 63) + 1) if cn >= 4096 then out[#out + 1] = concat(cur); cur = {}; cn = 0 end i = i + 3 end local rem = len - i + 1 if rem > 0 then local a, b = byte(s, i, i + 1) local x = a * 65536 + (b or 0) * 256 cn = cn + 1 cur[cn] = string.sub(B64, band(rshift(x, 18), 63) + 1, band(rshift(x, 18), 63) + 1) .. string.sub(B64, band(rshift(x, 12), 63) + 1, band(rshift(x, 12), 63) + 1) .. (rem == 2 and string.sub(B64, band(rshift(x, 6), 63) + 1, band(rshift(x, 6), 63) + 1) or "=") .. "=" end out[#out + 1] = concat(cur) return concat(out) end local n = 619155 local t = {} for i = 1, n do t[i] = char((i * 37) % 256) end local data = concat(t) local key = "0123456789abcdef0123456789abcdef" local enc = b64enc(data) print("sizes", #data, #enc) local t0 = os.clock() local decoded = b64(enc, #data) print("b64", string.format("%.4f", os.clock() - t0), decoded and #decoded, decoded == data) local t1 = os.clock() local out = decrypt(data, key) print("dec", string.format("%.4f", os.clock() - t1), out and #out) assert(out and #out == #data, "decrypt length") for p = 1, #data, 997 do local ref = bxor(byte(data, p), byte(key, ((p - 1) % 32) + 1), p % 256, math.floor(p / 256) % 256) if byte(out, p) ~= ref then error("mismatch at " .. p) end end print("ok")