From d4c173c713d486a56b1ebce299352b5712d3fb93 Mon Sep 17 00:00:00 2001 From: Miguel Oliveira Date: Sun, 16 Oct 2022 19:28:56 -0300 Subject: [PATCH] Remove dependency on string.pack --- aead.lua | 15 ++-- blake3.lua | 20 +++--- chacha20.lua | 15 ++-- internal/fp.lua | 5 +- internal/fq.lua | 21 +++--- internal/packing.lua | 166 +++++++++++++++++++++++++++++++++++++++++++ internal/sha512.lua | 20 +++--- poly1305.lua | 16 +++-- random.lua | 5 +- sha256.lua | 22 +++--- 10 files changed, 251 insertions(+), 54 deletions(-) create mode 100644 internal/packing.lua diff --git a/aead.lua b/aead.lua index d5b3d63..77f1501 100644 --- a/aead.lua +++ b/aead.lua @@ -4,9 +4,12 @@ -- local expect = require "cc.expect".expect +local packing = require "ccryptolib.internal.packing" local chacha20 = require "ccryptolib.chacha20" local poly1305 = require "ccryptolib.poly1305" +local p8x1, fmt8x1 = packing.compilePack(" 0 then @@ -267,13 +271,13 @@ end function mod.newKeyed(key) expect(1, key, "string") assert(#key == 32, "key length must be 32") - return new({("= 1, "length must be a positive integer") - local h = new({("= 1, "length must be a positive integer") - local h = new({(" +-- +-- :::warning +-- For performance reasons, **the generated functions do not check types, +-- lengths, nor ranges**. You must ensure that the passed arguments are +-- well-formed and respect the format string yourself. +-- ::: +-- +--
+-- +-- @module[kind=internal] internal.packing +-- + +local fmt = string.format + +local function mkPack(words, BE) + local out = "local C=string.char return function(_," + local nb = 0 + for i = 1, #words do + out = out .. fmt("n%d,", i) + nb = nb + words[i] + end + out = out:sub(1, -2) .. ")local " + for i = 1, nb do + out = out .. fmt("b%d,", i) + end + out = out:sub(1, -2) .. " " + local bi = 1 + for i = 1, #words do + for _ = 1, words[i] - 1 do + out = out .. fmt("b%d=n%d%%2^8 n%d=(n%d-b%d)*2^-8 ", bi, i, i, i, bi) + bi = bi + 1 + end + bi = bi + 1 + end + out = out .. "return C(" + bi = 1 + if not BE then + for i = 1, #words do + for _ = 1, words[i] - 1 do + out = out .. fmt("b%d,", bi) + bi = bi + 1 + end + out = out .. fmt("n%d%%2^8,", i) + bi = bi + 1 + end + else + for i = 1, #words do + out = out .. fmt("n%d%%2^8,", i) + bi = bi + words[i] - 2 + for _ = 1, words[i] - 1 do + out = out .. fmt("b%d,", bi) + bi = bi - 1 + end + bi = bi + words[i] + 1 + end + end + out = out:sub(1, -2) .. ")end" + return load(out)() +end + +local function mkUnpack(words, BE) + local out = "local B=string.byte return function(_,s,i)local " + local bi = 1 + if not BE then + for i = 1, #words do + for _ = 1, words[i] do + out = out .. fmt("b%d,", bi) + bi = bi + 1 + end + end + else + for i = 1, #words do + bi = bi + words[i] - 1 + for _ = 1, words[i] do + out = out .. fmt("b%d,", bi) + bi = bi - 1 + end + bi = bi + words[i] + 1 + end + end + out = out:sub(1, -2) .. fmt("=B(s,i,i+%d)return ", bi - 2) + bi = 1 + for i = 1, #words do + out = out .. fmt("b%d", bi) + bi = bi + 1 + for j = 2, words[i] do + out = out .. fmt("+b%d*2^%d", bi, 8 * j - 8) + bi = bi + 1 + end + out = out .. "," + end + out = out .. fmt("i+%d end", bi - 1) + return load(out)() +end + +local mod = {} + +-- Check whether string.pack is implemented in a high-speed language. +if not string.pack or pcall(string.dump, string.pack) then + local function compile(fmt, fn) + local e = assert(fmt:match("^([><])I[I%d]+$"), "invalid format string") + local w = {} + for i in fmt:gmatch("I([%d]+)") do + local n = tonumber(i) or 4 + assert(n > 0 and n <= 4, "integral size out of limits") + w[#w + 1] = n + end + return fn(w, e == ">") + end + + local packCache = {} + local unpackCache = {} + + --- (`string.pack == nil`) Compiles a binary packing function. + -- @tparam string fmt A string matched by `^([><])I[I%d]+$`. + -- @treturn function A high-performance function that behaves like an unsafe + -- version of `string.pack` for the given format string. Note that the third + -- argument isn't optional. + -- @treturn string fmt + -- @throws If the string is invalid or has an invalid integral size. + -- @throws If the compiled function is too large. + function mod.compilePack(fmt) + if not packCache[fmt] then + packCache[fmt] = compile(fmt, mkPack) + end + return packCache[fmt], fmt + end + + --- (`string.pack == nil`) Compiles a binary unpacking function. + -- @tparam string fmt A string matched by `^([><])I[I%d]+$`. + -- @treturn function A high-performance function that behaves like an unsafe + -- version of `string.unpack` for the given format string. + -- @treturn string fmt + -- @throws If the string is invalid or has an invalid integral size. + -- @throws If the compiled function is too large. + function mod.compileUnpack(fmt) + if not unpackCache[fmt] then + unpackCache[fmt] = compile(fmt, mkUnpack) + end + return unpackCache[fmt], fmt + end + + return mod +else + --- (`string.pack ~= nil`) Compiles a binary packing function. + -- @tparam string fmt + -- @treturn function `string.pack` + -- @treturn string fmt + mod.compilePack = function(fmt) return string.pack, fmt end + + --- (`string.pack ~= nil`) Compiles a binary unpacking function. + -- @tparam string fmt + -- @treturn function `string.unpack` + -- @treturn string fmt + mod.compileUnpack = function(fmt) return string.unpack, fmt end +end + +return mod diff --git a/internal/sha512.lua b/internal/sha512.lua index 1c428da..87a9a82 100644 --- a/internal/sha512.lua +++ b/internal/sha512.lua @@ -10,13 +10,17 @@ -- @module[kind=internal] internal.sha512 -- -local expect = require "cc.expect".expect +local expect = require "cc.expect".expect +local packing = require "ccryptolib.internal.packing" local shl = bit32.lshift local shr = bit32.rshift local bxor = bit32.bxor local bnot = bit32.bnot local band = bit32.band +local p1x16, fmt1x16 = packing.compilePack(">I16") +local p16x4, fmt16x4 = packing.compilePack(">I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4") +local u32x4, fmt32x4 = packing.compileUnpack(">I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4") local function carry64(a1, a0) local r0 = a0 % 2 ^ 32 @@ -65,9 +69,7 @@ local function digest(data) -- Pad input. local bitlen = #data * 8 local padlen = -(#data + 17) % 128 - data = data .. "\x80" .. ("\0"):rep(padlen) .. (">I16"):pack(bitlen) - local dataWords = {(">" .. ("I4"):rep(#data / 4)):unpack(data)} - dataWords[#dataWords] = nil + data = data .. "\x80" .. ("\0"):rep(padlen) .. p1x16(fmt1x16, bitlen) -- Initialize state. local h01, h00 = 0x6a09e667, 0xf3bcc908 @@ -80,12 +82,8 @@ local function digest(data) local h71, h70 = 0x5be0cd19, 0x137e2179 -- Digest. - local w = {} - for i = 1, #dataWords, 32 do - local index = i - 1 - for j = 1, 32 do - w[j] = dataWords[index + j] - end + for i = 1, #data, 128 do + local w = {u32x4(fmt32x4, data, i)} -- Message schedule. for j = 33, 160, 2 do @@ -172,7 +170,7 @@ local function digest(data) h71, h70 = carry64(h71 + h1, h70 + h0) end - return (">I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4"):pack( + return p16x4(fmt16x4, h01, h00, h11, h10, h21, h20, h31, h30, h41, h40, h51, h50, h61, h60, h71, h70 ) diff --git a/poly1305.lua b/poly1305.lua index cd787d7..9ed77e0 100644 --- a/poly1305.lua +++ b/poly1305.lua @@ -3,8 +3,12 @@ -- @module poly1305 -- -local expect = require "cc.expect".expect -local random = require "ccryptolib.random" +local expect = require "cc.expect".expect +local random = require "ccryptolib.random" +local packing = require "ccryptolib.internal.packing" + +local u4x4, fmt4x4 = packing.compileUnpack("I8") +local p16x4, fmt16x4 = packing.compilePack(">I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4") +local u16x4 = packing.compileUnpack(fmt16x4) +local p8x4, fmt8x4 = packing.compilePack(">I4I4I4I4I4I4I4I4") +local u8x4 = packing.compileUnpack(fmt8x4) local function primes(n, exp) local out = {} @@ -82,15 +88,15 @@ local function digest(data) -- Pad input. local bitlen = #data * 8 local padlen = -(#data + 9) % 64 - data = data .. "\x80" .. ("\0"):rep(padlen) .. (">I8"):pack(bitlen) + data = data .. "\x80" .. ("\0"):rep(padlen) .. p1x8(fmt1x8, bitlen) -- Digest. local h = h0 for i = 1, #data, 64 do - h = compress(h, {(">I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4"):unpack(data, i)}) + h = compress(h, {u16x4(fmt16x4, data, i)}) end - return (">I4I4I4I4I4I4I4I4"):pack(unpack(h)) + return p8x4(fmt8x4, unpack(h)) end --- Hashes a password using PBKDF2-HMAC-SHA256. @@ -110,7 +116,7 @@ local function pbkdf2(password, salt, iter) -- Pad password. if #password > 64 then password = digest(password) end password = password .. ("\0"):rep(-#password % 64) - password = {(">I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4"):unpack(password)} + password = {u16x4(fmt16x4, password, 1)} -- Compute password blocks. local ikp = {} @@ -127,8 +133,8 @@ local function pbkdf2(password, salt, iter) local pad96 = {2 ^ 31, 0, 0, 0, 0, 0, 0, 0x300} -- First iteration. - local pre = (">I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4"):pack(unpack(ikp)) - local hs = {(">I4I4I4I4I4I4I4I4"):unpack(digest(pre .. salt .. "\0\0\0\1"))} + local pre = p16x4(fmt16x4, unpack(ikp)) + local hs = {u8x4(fmt8x4, digest(pre .. salt .. "\0\0\0\1"), 1)} for i = 1, 8 do hs[i + 8] = pad96[i] end hs = compress(hokp, hs) @@ -142,7 +148,7 @@ local function pbkdf2(password, salt, iter) for i = 1, 8 do out[i] = bxor(out[i], hs[i]) end end - return (">I4I4I4I4I4I4I4I4"):pack(unpack(out)) + return p8x4(fmt8x4, unpack(out)) end return {