Skip to main content

Module slice_model

Module slice_model 

Source
Expand description

Bit-exact software model of every compression a slice issues.

§Why this file exists

The hardware never calls “BLAKE3 of this message”. It feeds one 64-byte compression at a time into its BLAKE3 lane, and it has to choose every chaining value, counter, length and flag itself. One wrong flag bit gives a different hash, and a key that no verifier accepts. The high-level software reference cannot help locate such a bug, because it calls a BLAKE3 library that hides those details.

So this file sits in between. It rebuilds a whole key out of the same single compressions the hardware issues, written as plain Rust, one function per kind of request. A test proves the result equals the reference. After that, this file is the readable specification of what the RTL must send, and RTL tests can compare against it request by request.

  reference (BLAKE3 library)  ══ test: equal ══  slice_model (single compressions)
                                                        ║ RTL tests: equal, request by request
                                                 slice_factory / slice_signer (hardware)

§What it models

Each function below returns the exact BLAKE3 request (chaining value, block, counter, length, flags) that one Op sends into a slice’s BLAKE3 lane. model_key builds a complete one-time key using only these requests, compress, dsig_chain and the packer stream layout. A test proves the result equals the high-level reference, so:

  • the flags, counters and lengths in this file are the specification the RTL must follow, one function per op, and
  • RTL tests can compare against model_key request by request.
  host: prefix_cv = BLAKE3-derive-key(context) ∘ (master ‖ signer)   [once per run]
  Seed      cv=prefix_cv  block=epoch‖batch‖index (20 B)  CHUNK_END|ROOT|DERIVE_KEY_MATERIAL
  PkNonce   cv=IV         block=LE32(0x5eed)‖seed (36 B)  single-block root
  Nonce     cv=IV         block=LE32(0xc0ca)‖seed (36 B)  single-block root
  Xof(t)    cv=IV         block=seed (32 B), counter t     single-block root
  Chunk0(j) cv=IV or CV   block=stream[64j..]   counter 0  START on j=0, END on j=15
  Chunk1(m) cv=IV or CV   block=stream[1024+64m..] ctr 1   START on m=0, END on m=3 (24 B)
  Parent    cv=IV         block=CV0‖CV1                    PARENT|ROOT  → pk_hash
  Merkle    cv=IV         block=left‖right                 single-block root
  Message   cv=IV         block=pk_hash‖nonce‖message      single-block root → digits

Per key that is 1 seed + 2 nonces + 20 XOF + 20 commitment blocks + 1 parent = 44 requests (the test checks this count). Merkle and Message are not part of one key: they serve the subtree and the signer.

§The story of one key, in data

  seed ──▶ pk_nonce, nonce
    └───▶ XOF 20 × 64 B = 1,280 B ──▶ 68 chain secrets of 18 B (1,224 B used)
            position 0 ──Haraka──▶ position 1 ──▶ position 2 ──▶ position 3 (chain end)
            (all four positions of all 68 chains go to the key cache)
  pk_nonce ‖ 68 chain ends = 1,240 B ──▶ chunk 0 (16 blocks) + chunk 1 (4 blocks)
                                    ──▶ Parent(CV0, CV1) = pk_hash (the Merkle leaf)

1,240 bytes is more than one 1,024-byte BLAKE3 chunk, so BLAKE3 hashes it as two chunks and one parent node. That is standard BLAKE3; the model just spells it out.

The master seed never enters the FPGA: the trusted host derives prefix_cv, the chaining value after the first 64 material bytes, and loads that instead. Both are equally secret.

Structs§

ModelKey
Everything a slice produces for one key, built only from the requests above.
Namespace
The batch namespace a slice works in.
Request
One BLAKE3 compression request, exactly as the lane receives it.

Constants§

NONCE_PREFIX
Prefix of the signer-private message nonce derivation.
PK_NONCE_PREFIX
Prefix of the public pk_nonce derivation.

Functions§

commitment_blocks
The 1,240-byte commitment stream split into the packer’s 20 blocks.
commitment_request
Chunk0(j) (chunk = 0) or Chunk1(m) (chunk = 1) of the commitment.
context_key
BLAKE3 derive-key context key for SEED_DERIVATION_CONTEXT.
merkle_request
Merkle: one batch-tree parent, BLAKE3(left ‖ right).
message_digest
The message digest (64 XOF words) as the slice computes it: one block in profile 1, two chained blocks in profile 2.
message_request
Message: salted digit hash for a 16-byte message.
message_tail_request
Profile 2’s second digest block: message bytes 16..64 (48 bytes), chained from the first block’s chaining value cv (its output words 0..8), CHUNK_END | ROOT. Its output is the 64-byte XOF.
model_key
Build key index of the namespace purely from slice requests.
model_root
Batch root from 128 leaves using only merkle_request.
nonce_request
PkNonce / Nonce: hash LE32(prefix) ‖ seed, keep 16 bytes.
parent_request
Parent: combine the two chunk chaining values into pk_hash.
prefix_cv
Host-side: chaining value after hashing master ‖ signer as key material.
seed_request
Seed: the second (final) material block, epoch ‖ batch ‖ index.
xof_request
Xof(t): output block t of BLAKE3-XOF(seed).