From b9457e9dd52f4002ca90b2ed4ffe3700f9c6632d Mon Sep 17 00:00:00 2001 From: Miguel Oliveira Date: Sun, 10 Apr 2022 17:38:16 -0300 Subject: [PATCH] Make BLAKE3 stateful --- blake3.lua | 181 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 126 insertions(+), 55 deletions(-) diff --git a/blake3.lua b/blake3.lua index dd6185c..0f4c6d4 100644 --- a/blake3.lua +++ b/blake3.lua @@ -118,90 +118,164 @@ local function merge(cvl, cvr) return cvl end -local function blake3(iv, flags, msg, len) - -- Set up the state. - local stateCvs = {} - local stateCv = iv - local stateT = 0 - local stateN = 0 - local stateStart = CHUNK_START - local stateEnd = 0 +local function update(state, message) + expect(1, state, "table") + expect(1, message, "string") + + -- Append to buffer. + state.m = state.m .. message + + -- Split off complete blocks. + local blockslen = #state.m - (#state.m - 1) % 64 - 1 + local blocks = state.m:sub(1, blockslen) + state.m = state.m:sub(1 + blockslen) -- Digest complete blocks. - for i = 1, #msg - 64, 64 do + for i = 1, #blocks, 64 do -- Compress the block. - local block = {(" 0 then + if state.t > 0 then -- Root is a parent, digest last block now and merge parents. - local stateFlags = flags + stateStart + CHUNK_END - local mergeCv = compress(stateCv, last, stateT, lastLen, stateFlags) - for i = #stateCvs, 2, -1 do - local block = merge(stateCvs[i], mergeCv) - mergeCv = compress(iv, block, 0, 64, flags + PARENT) + local stateFlags = state.f + state.s + CHUNK_END + local mergeCv = compress(state.cv, last, state.t, lastLen, stateFlags) + for i = #state.cvs, 2, -1 do + local block = merge({unpack(state.cvs[i])}, mergeCv) + mergeCv = compress(state.iv, block, 0, 64, state.f + PARENT) end -- Set output state. - outCv = iv - outBlock = merge(stateCvs[1], mergeCv) - outLen = 64 - outFlags = flags + ROOT + PARENT + return { + expand = expand, + cv = {unpack(state.iv)}, + m = merge({unpack(state.cvs[1])}, mergeCv), + n = 64, + f = state.f + ROOT + PARENT, + } else - -- Root block is in the first chunk, set output state. - outCv = stateCv - outBlock = last - outLen = lastLen - outFlags = flags + stateStart + CHUNK_END + ROOT + -- Root is in the first chunk, set output state. + return { + expand = expand, + cv = {unpack(state.cv)}, + m = last, + n = lastLen, + f = state.f + state.s + CHUNK_END + ROOT, + } end +end + +local function expand(out, len, offset) + expect(1, out, "table") + expect(1, len, "number") + expect(2, offset, "nil", "number") + offset = offset or 0 -- Expand output. local out = {} for i = 0, len / 64 do - local md = compress(outCv, outBlock, i, outLen, outFlags, true) + local n = offset + i + local md = compress(out.cv, out.m, n, out.n, out.f, true) out[i + 1] = ("= 1, "length must be a positive integer") - - return blake3(IV, 0, message, len) + return new(IV, 0):update(message):finalize():expand(len) end --- Performs a keyed hash. @@ -231,8 +304,8 @@ function mod.digestKeyed(key, message, len) expect(3, len, "number", "nil") len = len or 32 assert(len % 1 == 0 and len >= 1, "length must be a positive integer") - - return blake3({("= 1, "length must be a positive integer") - - return blake3(iv, DERIVE_KEY_MATERIAL, material, len) + local h = new({("