Remove BLAKE3 state
I feel like a broken record already. This is simpler and can be changed later.
This commit is contained in:
parent
77dfbae843
commit
c85969605e
|
@ -3,7 +3,7 @@
|
||||||
-- @module blake3
|
-- @module blake3
|
||||||
--
|
--
|
||||||
|
|
||||||
local expect = require "cc.expect".expect
|
local expect = require "cc.expect".expect
|
||||||
local lassert = require "ccryptolib.internal.util".lassert
|
local lassert = require "ccryptolib.internal.util".lassert
|
||||||
local packing = require "ccryptolib.internal.packing"
|
local packing = require "ccryptolib.internal.packing"
|
||||||
|
|
||||||
|
@ -14,12 +14,12 @@ local p16x4, fmt16x4 = packing.compilePack("<I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4")
|
||||||
local u16x4 = packing.compileUnpack(fmt16x4)
|
local u16x4 = packing.compileUnpack(fmt16x4)
|
||||||
local u8x4, fmt8x4 = packing.compileUnpack("<I4I4I4I4I4I4I4I4")
|
local u8x4, fmt8x4 = packing.compileUnpack("<I4I4I4I4I4I4I4I4")
|
||||||
|
|
||||||
local CHUNK_START = 0x01
|
local CHUNK_START = 0x01
|
||||||
local CHUNK_END = 0x02
|
local CHUNK_END = 0x02
|
||||||
local PARENT = 0x04
|
local PARENT = 0x04
|
||||||
local ROOT = 0x08
|
local ROOT = 0x08
|
||||||
local KEYED_HASH = 0x10
|
local KEYED_HASH = 0x10
|
||||||
local DERIVE_KEY_CONTEXT = 0x20
|
local DERIVE_KEY_CONTEXT = 0x20
|
||||||
local DERIVE_KEY_MATERIAL = 0x40
|
local DERIVE_KEY_MATERIAL = 0x40
|
||||||
|
|
||||||
local IV = {
|
local IV = {
|
||||||
|
@ -123,170 +123,90 @@ local function merge(cvl, cvr)
|
||||||
return cvl
|
return cvl
|
||||||
end
|
end
|
||||||
|
|
||||||
local function expand(state, len, offset)
|
local function blake3(iv, flags, msg, len)
|
||||||
expect(1, state, "table")
|
-- Set up the state.
|
||||||
expect(1, len, "number")
|
local stateCvs = {}
|
||||||
lassert(len % 1 == 0, "desired output length must be an integer", 2)
|
local stateCv = iv
|
||||||
lassert(len >= 1, "desired output length must be positive", 2)
|
local stateT = 0
|
||||||
offset = expect(2, offset, "nil", "number") or 0
|
local stateN = 0
|
||||||
lassert(offset % 1 == 0, "offset must be an integer", 2)
|
local stateStart = CHUNK_START
|
||||||
lassert(offset >= 0, "offset must be nonnegative", 2)
|
local stateEnd = 0
|
||||||
lassert(offset + len <= 2 ^ 32, "offset is too large", 2)
|
|
||||||
|
-- Digest complete blocks.
|
||||||
|
for i = 1, #msg - 64, 64 do
|
||||||
|
-- Compress the block.
|
||||||
|
local block = {u16x4(fmt16x4, msg, i)}
|
||||||
|
local stateFlags = flags + stateStart + stateEnd
|
||||||
|
stateCv = compress(stateCv, block, stateT, 64, stateFlags)
|
||||||
|
stateStart = 0
|
||||||
|
stateN = stateN + 1
|
||||||
|
|
||||||
|
if stateN == 15 then
|
||||||
|
-- Last block in chunk.
|
||||||
|
stateEnd = CHUNK_END
|
||||||
|
elseif stateN == 16 then
|
||||||
|
-- Chunk complete, merge.
|
||||||
|
local mergeCv = stateCv
|
||||||
|
local mergeAmt = stateT + 1
|
||||||
|
while mergeAmt % 2 == 0 do
|
||||||
|
local block = merge(table.remove(stateCvs), mergeCv)
|
||||||
|
mergeCv = compress(iv, block, 0, 64, flags + PARENT)
|
||||||
|
mergeAmt = mergeAmt / 2
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Push back.
|
||||||
|
table.insert(stateCvs, mergeCv)
|
||||||
|
|
||||||
|
-- Update state back to next chunk.
|
||||||
|
stateCv = iv
|
||||||
|
stateT = stateT + 1
|
||||||
|
stateN = 0
|
||||||
|
stateStart = CHUNK_START
|
||||||
|
stateEnd = 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Pad the last message block.
|
||||||
|
local lastLen = #msg == 0 and 0 or (#msg - 1) % 64 + 1
|
||||||
|
local padded = msg:sub(-lastLen) .. ("\0"):rep(64)
|
||||||
|
local last = {u16x4(fmt16x4, padded, 1)}
|
||||||
|
|
||||||
|
-- Prepare output expansion state.
|
||||||
|
local outCv, outBlock, outLen, outFlags
|
||||||
|
if stateT > 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)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set output state.
|
||||||
|
outCv = iv
|
||||||
|
outBlock = merge(stateCvs[1], mergeCv)
|
||||||
|
outLen = 64
|
||||||
|
outFlags = flags + 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
|
||||||
|
end
|
||||||
|
|
||||||
-- Expand output.
|
-- Expand output.
|
||||||
local out = {}
|
local out = {}
|
||||||
for i = 0, len / 64 do
|
for i = 0, len / 64 do
|
||||||
local n = offset + i
|
local md = compress(outCv, outBlock, i, outLen, outFlags, true)
|
||||||
local md = compress(state.cv, state.m, n, state.n, state.f, true)
|
|
||||||
out[i + 1] = p16x4(fmt16x4, unpack(md))
|
out[i + 1] = p16x4(fmt16x4, unpack(md))
|
||||||
end
|
end
|
||||||
|
|
||||||
return table.concat(out):sub(1, len)
|
return table.concat(out):sub(1, len)
|
||||||
end
|
end
|
||||||
|
|
||||||
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, #blocks, 64 do
|
|
||||||
-- Compress the block.
|
|
||||||
local block = {u16x4(fmt16x4, blocks, i)}
|
|
||||||
local stateFlags = state.f + state.s + state.e
|
|
||||||
state.cv = compress(state.cv, block, state.t, 64, stateFlags)
|
|
||||||
state.s = 0
|
|
||||||
state.n = state.n + 1
|
|
||||||
|
|
||||||
if state.n == 15 then
|
|
||||||
-- Last block in chunk.
|
|
||||||
state.e = CHUNK_END
|
|
||||||
elseif state.n == 16 then
|
|
||||||
-- Chunk complete, merge.
|
|
||||||
local mergeCv = state.cv
|
|
||||||
local mergeAmt = state.t + 1
|
|
||||||
while mergeAmt % 2 == 0 do
|
|
||||||
local block = merge(table.remove(state.cvs), mergeCv)
|
|
||||||
mergeCv = compress(state.iv, block, 0, 64, state.f + PARENT)
|
|
||||||
mergeAmt = mergeAmt / 2
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Push back.
|
|
||||||
table.insert(state.cvs, mergeCv)
|
|
||||||
|
|
||||||
-- Update state back to next chunk.
|
|
||||||
state.cv = state.iv
|
|
||||||
state.t = state.t + 1
|
|
||||||
state.n = 0
|
|
||||||
state.s = CHUNK_START
|
|
||||||
state.e = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return state
|
|
||||||
end
|
|
||||||
|
|
||||||
local function finalize(state)
|
|
||||||
expect(1, state, "table")
|
|
||||||
|
|
||||||
-- Pad the last message block.
|
|
||||||
local lastLen = #state.m
|
|
||||||
local padded = state.m .. ("\0"):rep(64)
|
|
||||||
local last = {u16x4(fmt16x4, padded, 1)}
|
|
||||||
|
|
||||||
-- Prepare output expansion state.
|
|
||||||
if state.t > 0 then
|
|
||||||
-- Root is a parent, digest last block now and merge parents.
|
|
||||||
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.
|
|
||||||
return {
|
|
||||||
expand = expand,
|
|
||||||
cv = {unpack(state.iv)},
|
|
||||||
m = merge({unpack(state.cvs[1])}, mergeCv),
|
|
||||||
n = 64,
|
|
||||||
f = state.f + ROOT + PARENT,
|
|
||||||
}
|
|
||||||
else
|
|
||||||
-- 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 copy(state)
|
|
||||||
-- Copy CV stack.
|
|
||||||
local cvs = {}
|
|
||||||
for i = 1, #state.cvs do cvs[i] = {unpack(state.cvs[i])} end
|
|
||||||
|
|
||||||
return {
|
|
||||||
update = update,
|
|
||||||
finalize = finalize,
|
|
||||||
copy = copy,
|
|
||||||
iv = {unpack(state.iv)},
|
|
||||||
cv = {unpack(state.cv)},
|
|
||||||
cvs = cvs,
|
|
||||||
m = state.m,
|
|
||||||
t = state.t,
|
|
||||||
n = state.n,
|
|
||||||
s = state.s,
|
|
||||||
e = state.e,
|
|
||||||
f = state.f,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local function new(iv, f)
|
|
||||||
return {
|
|
||||||
update = update,
|
|
||||||
finalize = finalize,
|
|
||||||
copy = copy,
|
|
||||||
iv = iv,
|
|
||||||
cv = iv,
|
|
||||||
cvs = {},
|
|
||||||
m = "",
|
|
||||||
t = 0,
|
|
||||||
n = 0,
|
|
||||||
s = CHUNK_START,
|
|
||||||
e = 0,
|
|
||||||
f = f,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local mod = {}
|
local mod = {}
|
||||||
|
|
||||||
function mod.new()
|
|
||||||
return new({unpack(IV)}, 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
function mod.newKeyed(key)
|
|
||||||
expect(1, key, "string")
|
|
||||||
lassert(#key == 32, "key length must be 32", 2)
|
|
||||||
return new({u8x4(fmt8x4, key, 1)}, KEYED_HASH)
|
|
||||||
end
|
|
||||||
|
|
||||||
function mod.newDk(context)
|
|
||||||
expect(1, context, "string")
|
|
||||||
local iv = new(IV, DERIVE_KEY_CONTEXT):update(context):finalize():expand(32)
|
|
||||||
return new({u8x4(fmt8x4, iv, 1)}, DERIVE_KEY_MATERIAL)
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Hashes data using BLAKE3.
|
--- Hashes data using BLAKE3.
|
||||||
--
|
--
|
||||||
-- @tparam string message The input message.
|
-- @tparam string message The input message.
|
||||||
|
@ -298,7 +218,7 @@ function mod.digest(message, len)
|
||||||
len = expect(2, len, "number", "nil") or 32
|
len = expect(2, len, "number", "nil") or 32
|
||||||
lassert(len % 1 == 0, "desired output length must be an integer", 2)
|
lassert(len % 1 == 0, "desired output length must be an integer", 2)
|
||||||
lassert(len >= 1, "desired output length must be positive", 2)
|
lassert(len >= 1, "desired output length must be positive", 2)
|
||||||
return new(IV, 0):update(message):finalize():expand(len)
|
return blake3(IV, 0, message, len)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Performs a keyed hash.
|
--- Performs a keyed hash.
|
||||||
|
@ -315,8 +235,7 @@ function mod.digestKeyed(key, message, len)
|
||||||
len = expect(3, len, "number", "nil") or 32
|
len = expect(3, len, "number", "nil") or 32
|
||||||
lassert(len % 1 == 0, "desired output length must be an integer", 2)
|
lassert(len % 1 == 0, "desired output length must be an integer", 2)
|
||||||
lassert(len >= 1, "desired output length must be positive", 2)
|
lassert(len >= 1, "desired output length must be positive", 2)
|
||||||
local h = new({u8x4(fmt8x4, key, 1)}, KEYED_HASH)
|
return blake3({u8x4(fmt8x4, key, 1)}, KEYED_HASH, message, len)
|
||||||
return h:update(message):finalize():expand(len)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Makes a context-based key derivation function (KDF).
|
--- Makes a context-based key derivation function (KDF).
|
||||||
|
@ -326,15 +245,14 @@ end
|
||||||
--
|
--
|
||||||
function mod.deriveKey(context)
|
function mod.deriveKey(context)
|
||||||
expect(1, context, "string")
|
expect(1, context, "string")
|
||||||
local iv = new(IV, DERIVE_KEY_CONTEXT):update(context):finalize():expand(32)
|
local iv = {u8x4(fmt8x4, blake3(IV, DERIVE_KEY_CONTEXT, context, 32), 1)}
|
||||||
|
|
||||||
return function(material, len)
|
return function(material, len)
|
||||||
expect(1, material, "string")
|
expect(1, material, "string")
|
||||||
len = expect(2, len, "number", "nil") or 32
|
len = expect(2, len, "number", "nil") or 32
|
||||||
lassert(len % 1 == 0, "desired output length must be an integer", 2)
|
lassert(len % 1 == 0, "desired output length must be an integer", 2)
|
||||||
lassert(len >= 1, "desired output length must be positive", 2)
|
lassert(len >= 1, "desired output length must be positive", 2)
|
||||||
local h = new({u8x4(fmt8x4, iv, 1)}, DERIVE_KEY_MATERIAL)
|
return blake3(iv, DERIVE_KEY_MATERIAL, material, len)
|
||||||
return h:update(material):finalize():expand(len)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue