~rootmos/lua-hack

0ab6274a2fdbe35d9c31d040dbbda1409e3a591f — Gustav Behm 1 year, 1 month ago 2d372dd + d2d2cbf
Merge branch 'merkle'
M README.md => README.md +5 -3
@@ 9,12 9,14 @@ A set of Lua modules guaranteed to be public domain.
- [char](char.lua): ASCII character utilities (e.g. `is_printable`)
- [deep](deep/init.lua): deep equality and copy operations
- [execute](execute.lua): extend os.execute and io.popen to safely quote arguments, set environment variables and change directory
- [hex](hex.lua): [hexadecimal binary encoding](https://en.wikipedia.org/wiki/Hexadecimal#Base16_(transfer_encoding))
- [lamport](lamport.lua): implementation of the [Lamport signature scheme](https://en.wikipedia.org/wiki/Lamport_signature)
- [hex](hex.lua): [hexadecimal](https://en.wikipedia.org/wiki/Hexadecimal#Base16_(transfer_encoding)) binary encoding
- [lamport-merkle](lamport-merkle/init.lua): implementation of the combined [Lamport-Merkle](https://en.wikipedia.org/wiki/Merkle_signature_scheme) signature scheme
- [lamport](lamport/init.lua): implementation of the [Lamport](https://en.wikipedia.org/wiki/Lamport_signature) signature scheme
- [merkle](merkle/init.lua): implementation of the [Merkle tree]() data structure
- [random.prng](random/prng.lua): Pseudorandom number generators (e.g. [xorshift32](https://en.wikipedia.org/wiki/Xorshift))
- [random.rng](random/rng.lua): entropy sources (e.g. `/dev/random`)
- [ser](ser.lua): serialize Lua values as Lua expressions and a pretty-printer for free
- [sha](sha/init.lua): reference implementations of [SHA](https://en.wikipedia.org/wiki/Secure_Hash_Algorithms) (currently: SHA1 and SHA-256)
- [tar](tar/tar.lua): implementation of the tar archive format
- [tar](tar/tar.lua): implementation of the [tar](https://en.wikipedia.org/wiki/Tar_(computing)) archive format (currently: [UStar](https://en.wikipedia.org/wiki/Tar_(computing)#UStar_format))
- [tree](tree.lua): a tree-graph implementation
- [uint32](uint32/init.lua): `uint32_t` but in Lua

M base64.lua => base64.lua +5 -1
@@ 91,4 91,8 @@ function M.encoded_length(bs)
    return (l+p)*4/3
end

return M
return setmetatable(M, {
    __call = function(N, bs)
        return N.encode(bs)
    end,
})

A buffer.lua => buffer.lua +68 -0
@@ 0,0 1,68 @@
local M = {}

local mt = {
    __len = function(st) return st.remaining end,
}

function M.init(bs)
    local st = setmetatable({
        i = 1,
        j = 0,
        k = 0,
        total = 0,
        consumed = 0,
        remaining = 0,
        append = M.append,
        consume = M.consume,
    }, mt)

    if bs then
        st:append(bs)
    end

    return st
end

function M.append(st, bs)
    st.j = st.j + 1
    st[st.j] = bs
    st.total = st.total + #bs
    st.remaining = st.remaining + #bs
    return st
end

function M.consume(st, n)
    if st.remaining < n then
        return nil
    end

    local t = {}
    while n > 0 do
        local b = st[st.i]
        local r = #b - st.k
        if n <= r then
            table.insert(t, b:sub(st.k+1, st.k+n))
            st.k = st.k + n
            st.remaining = st.remaining - n

            if r == n then
                st[st.i] = nil
                st.i = st.i + 1
                st.k = 0
            end

            break
        else
            table.insert(t, b:sub(st.k+1))
            st[st.i] = nil
            st.i = st.i + 1
            st.k = 0
            st.remaining = st.remaining - r
            n = n - r
        end
    end

    return table.concat(t)
end

return M

A buffer.test.lua => buffer.test.lua +40 -0
@@ 0,0 1,40 @@
local lu = require("luaunit")
local B = require("buffer")

function test_empty()
    local b = B.init()
    lu.assertEquals(b:consume(0), "")
end

function test_foobar_1()
    local b = B.init("foobar")
    lu.assertEquals(b:consume(2), "fo")
    lu.assertEquals(b:consume(2), "ob")
    lu.assertEquals(b:consume(2), "ar")
    lu.assertNil(b:consume(2))
end

function test_foobar_2()
    local b = B.init("foo"):append("bar")
    lu.assertEquals(b:consume(2), "fo")
    lu.assertEquals(b:consume(2), "ob")
    lu.assertEquals(b:consume(2), "ar")
    lu.assertNil(b:consume(2))
end

function test_foobar_3()
    local b = B.init("foo"):append("bar")
    lu.assertEquals(b:consume(3), "foo")
    lu.assertEquals(b:consume(3), "bar")
    lu.assertNil(b:consume(1))
end

function test_foobar_4()
    local b = B.init("foo")
    lu.assertEquals(b:consume(3), "foo")
    b:append("bar")
    lu.assertEquals(b:consume(3), "bar")
    lu.assertNil(b:consume(1))
end

os.exit(lu.LuaUnit.run())

M hex.lua => hex.lua +5 -1
@@ 48,4 48,8 @@ function M.decode(s)
    return table.concat(t)
end

return M
return setmetatable(M, {
    __call = function(N, bs)
        return N.encode(bs)
    end,
})

A lamport-merkle/init.lua => lamport-merkle/init.lua +126 -0
@@ 0,0 1,126 @@
local M = {}

local function setup()
    if M.hash == nil then
        M.hash = require("sha").sha256
    end

    if M.cprng == nil then
        M.cprng = require("random.prng").sha256
    end

    if M.lamport == nil then
        M.lamport = require("lamport")
        M.lamport.hash = M.hash
    end

    if M.merkle == nil then
        M.merkle = require("merkle")
        M.merkle.hash = M.hash
    end

    if M.array == nil then
        M.array = require("seeded-array")
        M.array.prng = M.cprng
    end
end

local mt = {
    __len = function(st) return st.n end,
    __index = function(st, i) return st.tree[i] end,
}

function M.generate_key(n)
    setup()

    local N = #M.hash("")
    local seed = (M.rng or require"random.rng")(N)

    local keys = M.array.make{
        n = n,
        N = N,
        seed = seed,
        f = function(s) return M.lamport.generate_key(M.hash, M.cprng(s)) end,
    }

    local tree = M.merkle(keys, nil, function(k) return k.id end)

    return setmetatable({
        i = 0,
        n = n,
        tree = tree,
        pub = tree.root,
        sign = M.sign,
    }, mt)
end

function M.sign_hash(key, h)
    setup()
    key.i = key.i + 1
    if key.i > key.n then
        error("no key available")
    end
    return {
        lsig = M.lamport.sign_hash(key.tree[key.i], h, true),
        auth = key.tree:proof(key.i),
        verify = M.verify,
        verify_hash = M.verify_hash,
        dump = M.dump_sig,
    }
end

function M.load_sig(f)
    setup()

    local n = 0
    local auth, lsig
    if io.type(f) == "file" then
        auth, i = M.merkle.load_proof(f)
        n = n + i
        lsig, i = M.lamport.load_sig(f)
        n = n + i
    else
        auth, i = M.merkle.load_proof(f)
        n = n + i
        lsig, i = M.lamport.load_sig(f, nil, i)
        n = n + i
    end

    return {
        lsig = lsig,
        auth = auth,
        verify = M.verify,
        verify_hash = M.verify_hash,
        dump = M.dump_sig,
    }, n
end

function M.dump_sig(sig, f)
    setup()
    if io.type(f) == "file" then
        sig.auth:dump(f)
        sig.lsig:dump(f)
    else
        return sig.auth:dump() .. sig.lsig:dump()
    end
end

function M.sign(key, msg)
    setup()
    return M.sign_hash(key, M.hash(msg))
end

function M.verify_hash(sig, pub, h)
    setup()
    local lpub = sig.auth[0]
    sig.lsig:verify_hash(lpub, h)
    sig.auth:verify_hash(pub, lpub)
    return true
end

function M.verify(sig, pub, msg)
    setup()
    return M.verify_hash(sig, pub, M.hash(msg))
end

return M

A lamport-merkle/test.lua => lamport-merkle/test.lua +59 -0
@@ 0,0 1,59 @@
local lu = require("luaunit")
local F = require("fresh")
local L = require("init")
local D = require("deep")

local function N()
    return 1<<math.random(1,3)
end

function test_generate_key()
    local n = N()
    local key = L.generate_key(n)
    lu.assertEquals(#key, n)
    lu.assertEquals(key.n, n)
    lu.assertEquals(key.i, 0)
end

function test_sign_verify()
    local key = L.generate_key(N())
    local msg = F.bytestring()
    local sig = key:sign(msg)
    lu.assertTrue(L.verify(sig, key.pub, msg))
    lu.assertTrue(sig:verify(key.pub, msg))
end

function test_sign_verify_modified()
    local key = L.generate_key(N())
    local msg0 = F.bytestring()
    local sig = L.sign(key, msg0)
    local msg1 = F.bytestring()
    lu.assertError(L.verify, sig, key.pub, msg1)
    lu.assertError(sig.verify, sig, key.pub, msg1)
end

function test_sign_verify_many()
    local key = L.generate_key(N())
    for i = 1,#key do
        local msg = F.bytestring()
        local sig = key:sign(msg)
        lu.assertTrue(sig:verify(key.pub, msg))
        lu.assertEquals(i, key.i)
    end

    lu.assertError(L.sign, key, F.bytestring())
end

function test_dump_and_load_sig()
    local key = L.generate_key(N())
    for i = 1,#key do
        local msg = F.bytestring()
        local s0 = key:sign(msg)
        local s = s0:dump()
        local s1, n = L.load_sig(s)
        lu.assertEquals(n, #s)
        lu.assertTrue(D.eq(s0, s1))
    end
end

os.exit(lu.LuaUnit.run())

M lamport/init.lua => lamport/init.lua +43 -24
@@ 1,9 1,9 @@
-- https://en.wikipedia.org/wiki/Lamport_signature
local M = {}
local default_hash = function() return require("sha").sha256 end
local function default_hash() return M.hash or require("sha").sha256 end

function M.N(hash)
    local hash = hash or M.hash or default_hash()
    local hash = hash or default_hash()
    return #hash("")
end



@@ 14,11 14,12 @@ function M.dump_key(key, f)
            f:write(k[2])
        end
    else
        local s = ""
        local t = {}
        for _, k in ipairs(key) do
            s = s .. k[1] .. k[2]
            table.insert(t, k[1])
            table.insert(t, k[2])
        end
        return s
        return table.concat(t)
    end
end



@@ 63,25 64,34 @@ local function mk_key(iter, hash)
end

local function load_key(mk)
    return function(f, hash)
        local hash = hash or M.hash or default_hash()
    return function(f, hash, offset)
        local hash = hash or default_hash()
        local n = M.N(hash)
        local i = 0

        if io.type(f) == "file" then
            if offset then
                f:seek("cur", offset)
            end

            local function g()
                i = i + n + n
                return { f:read(n), f:read(n) }
            end
            return mk(g, hash)

            return mk(g, hash), i
        else
            local o = 0
            local o = offset or 0
            local function g()
                local k0 = f:sub(o + 1, o + n)
                o = o + n
                i = i + n
                local k1 = f:sub(o + 1, o + n)
                o = o + n
                i = i + n
                return { k0, k1 }
            end
            return mk(g, hash)
            return mk(g, hash), i
        end
    end
end


@@ 92,7 102,7 @@ M.dump_pub = M.dump_key

function M.generate_key(hash, rng)
    local rng = rng or M.rng or require("random.rng")
    local hash = hash or M.hash or default_hash()
    local hash = hash or default_hash()
    local n = M.N(hash)

    local function f()


@@ 114,18 124,19 @@ function M.dump_sig(sig, f)
            end
        end
    else
        local t = {}
        local s = ""
        for _, k in ipairs(sig) do
            s = s .. k
            table.insert(t, k)
        end

        if sig.unused then
            for _, k in ipairs(sig.unused) do
                s = s .. k
                table.insert(t, k)
            end
        end

        return s
        return table.concat(t)
    end
end



@@ 151,21 162,27 @@ local function mk_sig(iter, hash)

    sig.dump = M.dump_sig
    sig.verify = M.verify
    sig.verify_hash = M.verify_hash

    return sig
end

function M.load_sig(f, hash)
    local hash = hash or M.hash or default_hash()
function M.load_sig(f, hash, offset)
    local hash = hash or default_hash()
    local n = M.N(hash)
    local i = 0

    if io.type(f) == "file" then
        if offset then
            f:seek("cur", offset)
        end
        local function g()
            i = i + n
            return f:read(n)
        end
        return mk_sig(g, hash)
        return mk_sig(g, hash), i
    else
        local o = 0
        local o = offset or 0
        local N = #f
        local function g()
            if o == N then


@@ 173,9 190,10 @@ function M.load_sig(f, hash)
            end
            local k = f:sub(o + 1, o + n)
            o = o + n
            i = i + n
            return k
        end
        return mk_sig(g, hash)
        return mk_sig(g, hash), i
    end
end



@@ 224,12 242,13 @@ function M.sign_hash(key, h, include_unused)

    sig.dump = M.dump_sig
    sig.verify = M.verify
    sig.verify_hash = M.verify_hash

    return sig
end

function M.sign(key, msg, include_unused)
    local hash = M.hash or default_hash()
function M.sign(key, msg, include_unused, hash)
    local hash = hash or default_hash()
    return M.sign_hash(key, hash(msg), include_unused)
end



@@ 263,7 282,7 @@ local function verify_using_keyid(sig, keyid, h, hash)
end

function M.verify_hash(sig, pub, h, hash)
    local hash = hash or M.hash or default_hash()
    local hash = hash or default_hash()

    if type(pub) == "string" then
        return verify_using_keyid(sig, pub, h, hash)


@@ 272,8 291,8 @@ function M.verify_hash(sig, pub, h, hash)
    end
end

function M.verify(sig, pub, msg)
    local hash = M.hash or default_hash()
function M.verify(sig, pub, msg, hash)
    local hash = hash or default_hash()
    return M.verify_hash(sig, pub, hash(msg), hash)
end


M lamport/test.lua => lamport/test.lua +13 -7
@@ 34,7 34,9 @@ end

function test_load_key()
    local k0 = L.generate_key()
    local k1 = L.load_key(k0:dump())
    local s = k0:dump()
    local k1, n = L.load_key(s)
    lu.assertEquals(n, #s)
    lu.assertEquals(k0.id, k1.id)
    lu.assertTrue(D.eq(k0, k1))
end


@@ 60,7 62,9 @@ end

function test_load_pub()
    local p0 = L.generate_key().pub
    local p1 = L.load_pub(p0:dump())
    local s = p0:dump()
    local p1, n = L.load_pub(s)
    lu.assertEquals(n, #s)
    lu.assertEquals(p0.id, p1.id)
    lu.assertTrue(D.eq(p0, p1))
end


@@ 91,8 95,8 @@ function test_sign_verify_modified()
    local msg0 = F.bytestring()
    local sig = L.sign(key, msg0)
    local msg1 = F.bytestring()
    lu.assertError(L.verify(sig, key.pub, msg0))
    lu.assertError(sig:verify(key.pub, msg0))
    lu.assertError(L.verify, sig, key.pub, msg1)
    lu.assertError(sig.verify, sig, key.pub, msg1)
end

function test_sign_verify_using_keyid()


@@ 108,8 112,8 @@ function test_sign_verify_using_keyid_modified()
    local msg0 = F.bytestring()
    local sig = L.sign(key, msg0, true)
    local msg1 = F.bytestring()
    lu.assertError(L.verify(sig, key.id, msg0))
    lu.assertError(sig:verify(key.id, msg0))
    lu.assertError(L.verify, sig, key.id, msg1)
    lu.assertError(sig.verify, sig, key.id, msg1)
end

function test_dump_sig()


@@ 132,7 136,9 @@ function test_load_sig()
    local key = L.generate_key()
    local msg = F.bytestring()
    local s0 = key:sign(msg)
    local s1 = L.load_sig(s0:dump())
    local s = s0:dump()
    local s1, n = L.load_sig(s)
    lu.assertEquals(n, #s)
    lu.assertTrue(D.eq(s0, s1))
end


A merkle/init.lua => merkle/init.lua +190 -0
@@ 0,0 1,190 @@
local M = {}
local function default_hash() return M.hash or require("sha").sha256 end

function M.N(hash)
    local hash = hash or default_hash()
    return #hash("")
end

function M.verify_hash(p, root, h, hash)
    local hash = hash or default_hash()

    local i = p.i
    for _, q in ipairs(p) do
        local k = hash.init()
        if ((i+1)%2) == 0 then
            k:update(h)
            k:update(q)
            i = (i>>1) + 1
        else
            k:update(q)
            k:update(h)
            i = (i-1>>1) + 1
        end
        h = k:finalize()
    end

    if h ~= root then
        error("Merkle-tree verification failed")
    end

    return true
end

function M.verify(p, root, leaf, leaf_hash, hash)
    local hash = hash or default_hash()
    local leaf_hash = leaf_hash or hash
    return M.verify_hash(p, root, leaf_hash(leaf), hash)
end

function M.proof(mt, i)
    local p = {i=i, n=mt.n, [0]=mt.tree[1][i]}
    for k = 1,mt.n do
        local j
        if ((i+1)%2) == 0 then
            j = i + 1
            i = (i>>1) + 1
        else
            j = i - 1
            i = (j>>1) + 1
        end
        p[k] = mt.tree[k][j]
    end

    p.verify = M.verify
    p.verify_hash = M.verify_hash
    p.dump = M.dump_proof

    return p
end

function M.dump_proof(p, f)
    local u32 = require("uint32")
    if io.type(f) == "file" then
        f:write(u32.to_bytes(u32(p.n)))
        f:write(u32.to_bytes(u32(p.i)))
        f:write(p[0])
        for _, q in ipairs(p) do
            f:write(q)
        end
    else
        local t = {u32.to_bytes(u32(p.n)), u32.to_bytes(u32(p.i)), p[0]}
        for _, q in ipairs(p) do
            table.insert(t, q)
        end
        return table.concat(t)
    end
end

function M.load_proof(f, hash, offset)
    local N = M.N(hash)
    local u32 = require("uint32")
    local p
    local n = 0
    if io.type(f) == "file" then
        if offset then
            f:seek("cur", offset)
        end

        p = {
            n = u32.tointeger(u32.from_bytes(f:read(4))),
            i = u32.tointeger(u32.from_bytes(f:read(4))),
            [0] = f:read(N),
        }
        n = n + 4 + 4 + N

        for i = 1,p.n do
            p[i] = f:read(N)
            n = n + N
        end
    else
        p = {
            n = u32.tointeger(u32.from_bytes(f:sub(1,4))),
            i = u32.tointeger(u32.from_bytes(f:sub(5,8))),
            [0] = f:sub(9, 8+N),
        }
        n = n + 4 + 4 + N
        local o = (offset or 0) + 4 + 4 + N
        for i = 1,p.n do
            p[i] = f:sub(o+1,o+N)
            o = o + N
            n = n + N
        end
    end

    p.verify = M.verify
    p.verify_hash = M.verify_hash
    p.dump = M.dump_proof

    return p, n
end

function M.make(leafs, hash, leaf_hash)
    local hash = hash or default_hash()
    local leaf_hash = leaf_hash or hash

    local N = #leafs
    if N == 0 then
        error("refusing to create empty tree")
        return nil
    end

    local n = 0
    while (1<<n) < N do
        n = n + 1
    end
    if (1<<n) ~= N then
        error(string.format("number of leafs is not a multiple of 2: %d", N))
    end

    local merkle = {}
    local t = {}
    for i, l in ipairs(leafs) do
        merkle[i] = l
        t[i] = leaf_hash(l)
    end
    local tree = {t}

    for k = 1,n do
        local t = {}
        for i = 1,1<<(n-k) do
            local h = hash.init()
            local j = ((i-1)<<1) + 1
            h:update(tree[k][j])
            h:update(tree[k][j+1])
            t[i] = h:finalize()
        end
        tree[k+1] = t
    end

    merkle.tree = tree
    merkle.n = n
    merkle.root = tree[n+1][1]

    merkle.proof = M.proof
    merkle.render = M.render

    return merkle
end

function M.render(mt, enc)
    local s = ""
    local enc = enc or require("hex").encode
    for i = 1,mt.n+1 do
        local t = mt.tree[i]
        for j = 1,#t do
            if j == #t then
                s = s .. enc(t[j]) .. "\n"
            else
                s = s .. enc(t[j]) .. " "
            end
        end
    end
    return s
end

return setmetatable(M, {
    __call = function(N, leafs, hash, leaf_hash)
        return N.make(leafs, hash, leaf_hash)
    end,
})

A merkle/test.lua => merkle/test.lua +65 -0
@@ 0,0 1,65 @@
local lu = require("luaunit")
local F = require("fresh")
local L = require("init")
local D = require("deep")

local function fresh_tree(N)
    local n = math.random(1, N or 7)
    local ls = {}
    for i = 1,1<<n do
        ls[i] = F.bytestring()
    end

    return L(ls)
end

function test_verify()
    local mt = fresh_tree()
    for i, l in ipairs(mt) do
        local p = mt:proof(i)
        lu.assertTrue(p:verify(mt.root, l))
    end
end

function test_verify_modified()
    local mt = fresh_tree()
    for i, l in ipairs(mt) do
        local p = mt:proof(i)
        lu.assertError(p.verify, p, mt.root, F.bytestring())
    end
end

function test_dump_proof()
    local mt = fresh_tree()
    local p = mt:proof(1)
    lu.assertEquals(#p:dump(), (4*2) + L.N()*(mt.n + 1))
    lu.assertEquals(p:dump(), L.dump_proof(p))
end

function test_load_proof()
    local mt = fresh_tree()
    for i, _ in ipairs(mt) do
        local p0 = mt:proof(i)
        local s = p0:dump()
        local p1, n = L.load_proof(s)
        lu.assertEquals(n, #s)
        lu.assertTrue(D.eq(p0, p1))
    end
end

function test_load_proof_file()
    local mt = fresh_tree()
    for i, _ in ipairs(mt) do
        local p0 = mt:proof(i)

        local f = io.tmpfile()
        p0:dump(f)
        f:flush()
        f:seek("set")

        local p1 = L.load_proof(f)
        lu.assertTrue(D.eq(p0, p1))
    end
end

os.exit(lu.LuaUnit.run())

M random/prng.lua => random/prng.lua +16 -6
@@ 52,18 52,24 @@ function M.xorshift32(seed)
    return mk(chunk)
end

function M.sha256(seed)
function M.hash(seed, hash)
    local n = #hash("")

    if seed == nil then
        seed = M.generate_seed(32)
        seed = M.generate_seed(n)
    elseif #seed ~= n then
        seed = hash(seed)
    end

    local h = require("sha").sha256

    local st = seed
    local c = 0
    local function chunk()
        local x = h(st)
        local h = hash.init(st)
        c = c + 1
        h:update(tostring(i))
        local x = h:finalize()
        st = ""
        for i = 1,32 do
        for i = 1,n do
            st = st .. string.char(seed:byte(i) ~ x:byte(i))
        end
        return st


@@ 72,4 78,8 @@ function M.sha256(seed)
    return mk(chunk)
end

function M.sha256(seed)
    return M.hash(seed, require("sha").sha256)
end

return M

A seeded-array.lua => seeded-array.lua +44 -0
@@ 0,0 1,44 @@
local M = {}

local function default_prng() return M.prng or require"random.prng".sha256 end
local function default_rng() return M.rng or require"random.rng" end

local mt = {
    __len = function(st)
        return st.n
    end,
    __index = function(st, j)
        if st.n and j > st.n then
            return nil
        end

        if j == st.i + 1 then
            st.i = j
            return st.f(st.prng(st.N))
        else
            local prng = st.mk_prng(st.seed)
            local x
            for i = 1,j do
                x = st.f(prng(st.N))
            end
            return x
        end
    end
}

function M.make(args)
    local args = args or {}
    st = {
        i = 0,
        n = args.n,
        N = args.N or 32,
        f = args.f or function(x) return x end,
        mk_prng = args.prng or default_prng(),
    }
    st.seed = args.seed or (args.rng or default_rng())(st.N)
    st.prng = st.mk_prng(st.seed)

    return setmetatable(st, mt)
end

return M

M sha/init.lua => sha/init.lua +24 -36
@@ 1,5 1,7 @@
local M = {}

local B = require("buffer")

local u32 = require("uint32")
local rotr = u32.rotr
local rotl = u32.rotl


@@ 58,13 60,14 @@ M.sha256 = setmetatable({
})

local function sha256_process_blocks(ctx)
   if #ctx.buf < 64 then
   local buf = ctx.buf:consume(64)
   if buf == nil then
      return
   end

   local W = {}
   for t = 1,16 do
      W[t] = u32.from_bytes(ctx.buf:sub(t*4-3,t*4))
      W[t] = u32.from_bytes(buf:sub(t*4-3,t*4))
   end
   assert(#W == 16)



@@ 110,44 113,38 @@ local function sha256_process_blocks(ctx)
   ctx.H[7] = add(g, ctx.H[7])
   ctx.H[8] = add(h, ctx.H[8])

   ctx.buf = ctx.buf:sub(65)

   return sha256_process_blocks(ctx)
end

function M.sha256.update(ctx, bs)
   ctx.buf = ctx.buf .. bs
   ctx.L = ctx.L + #bs
   ctx.buf:append(bs)
   sha256_process_blocks(ctx)
   return ctx
end

function M.sha256.pad(buf, l)
local function pad(buf)
   local l = buf.total
   local k = 0
   while (l*8 + 1 + k) % 512 ~= 448 do
      k = k + 1
   end
   assert(k % 8 == 7)
   k = k - 7
   local n = k // 8
   buf = buf .. string.char(0x80) .. string.rep("\0", n)
   buf = buf .. "\0\0\0\0" .. u32.to_bytes(u32(l*8)) -- TODO: support large messages
   assert((#buf % 64) == 0)
   return buf
   buf:append(string.char(0x80) .. string.rep("\0", n))
   buf:append("\0\0\0\0" .. u32.to_bytes(u32(l*8))) -- TODO: support large messages
end

function M.sha256.finalize(ctx)
   sha256_process_blocks(ctx)
   ctx.buf = M.sha256.pad(ctx.buf, ctx.L)
   pad(ctx.buf)
   sha256_process_blocks(ctx)
   assert(#ctx.buf == 0)

   local digest = ""
   local digest = {}
   for i = 1,8 do
      digest = digest .. u32.to_bytes(ctx.H[i])
      digest[i] = u32.to_bytes(ctx.H[i])
   end

   return digest
   return table.concat(digest)
end

function M.sha256.init(buf)


@@ 156,11 153,9 @@ function M.sha256.init(buf)
      H[k] = v
   end

   local buf = buf or ""
   local ctx = {
      H = H,
      buf = buf,
      L = #buf,
      buf = B.init(buf),
      update = M.sha256.update,
      finalize = M.sha256.finalize,
   }


@@ 205,13 200,14 @@ for t = 1,80 do
end

local function sha1_process_blocks(ctx)
   if #ctx.buf < 64 then
   local buf = ctx.buf:consume(64)
   if buf == nil then
      return
   end

   local W = {}
   for t = 1,16 do
      W[t] = u32.from_bytes(ctx.buf:sub(t*4-3,t*4))
      W[t] = u32.from_bytes(buf:sub(t*4-3,t*4))
   end
   assert(#W == 16)



@@ 244,32 240,26 @@ local function sha1_process_blocks(ctx)
   ctx.H[4] = add(ctx.H[4], D)
   ctx.H[5] = add(ctx.H[5], E)

   ctx.buf = ctx.buf:sub(65)

   return sha1_process_blocks(ctx)
end

function M.sha1.update(ctx, bs)
   ctx.buf = ctx.buf .. bs
   ctx.L = ctx.L + #bs
   ctx.buf:append(bs)
   sha1_process_blocks(ctx)
   return ctx
end

M.sha1.pad = M.sha256.pad

function M.sha1.finalize(ctx)
   sha1_process_blocks(ctx)
   ctx.buf = M.sha1.pad(ctx.buf, ctx.L)
   pad(ctx.buf)
   sha1_process_blocks(ctx)
   assert(#ctx.buf == 0)

   local digest = ""
   local digest = {}
   for i = 1,5 do
      digest = digest .. u32.to_bytes(ctx.H[i])
      digest[i] = u32.to_bytes(ctx.H[i])
   end

   return digest
   return table.concat(digest)
end

function M.sha1.init(buf)


@@ 278,11 268,9 @@ function M.sha1.init(buf)
      H[k] = v
   end

   local buf = buf or ""
   local ctx = {
      H = H,
      buf = buf,
      L = #buf,
      buf = B.init(buf),
      update = M.sha1.update,
      finalize = M.sha1.finalize,
   }

M sha/test.lua => sha/test.lua +0 -6
@@ 26,12 26,6 @@ function test_sha256_foobar_2()
    lu.assertEquals(H.encode(digest), "c3ab8ff13720e8ad9047dd39466b3c8974e592c2fa383d4a3960714caef0c4f2")
end

function test_sha256_pad()
    msg = H.decode("6162636465")
    padded = L.sha256.pad(msg, #msg)
    lu.assertEquals(padded, H.decode("61626364658000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000028"))
end

function test_sha256_fresh()
    for _ = 1,N do
        local bs0 = F.bytestring()

M test => test +1 -1
@@ 72,7 72,7 @@ for t in "${TESTS[@]}"; do
    set +o errexit
    env LUA_PATH="$ROOT/?.lua;$SCRIPT_DIR/?.lua;$SCRIPT_DIR/?/init.lua;$DEPS/?.lua" \
        LUA_CPATH="$ROOT/?.so" \
        $LUA "$t" -v -p "${2-}" \
        $LUA "$t" -v \
        | sed 's/^/  /'
    ec=$?
    if [ "$ec" != 0 ]; then

M uint32/init.lua => uint32/init.lua +11 -20
@@ 2,26 2,6 @@ local M = {}

local bits = require("bits")

--function M.check(x)
   --if type(x) ~= "number" then
      --error("not a number")
   --end

   --if math.type(x) ~= "integer" then
      --error("not an integer")
   --end

   --if x < 0 then
      --error("negative number")
   --end

   --if x > M.max then
      --error("integer overflow")
   --end

   --return x
--end

if bits == 64 then
   function M.bor(x, y)
      return x | y


@@ 93,6 73,10 @@ if bits == 64 then
         return (x<<16) + y
      end
   end

   function M.tointeger(x)
      return x
   end
else
   function M.bor(x, y)
      return { x[1] | y[1], x[2] | y[2] }


@@ 148,6 132,13 @@ else
      end
   end

   function M.tointeger(x)
      if x[1] > 0x7fff then
         error ("integer overflow")
      end
      return (x[1]<<16) | x[2]
   end

   function M.rotr(x, n)
      return M.bor(M.shr(x, n), M.shl(x, 32 - n))
   end