Skip to main content

Module blake3_core

Module blake3_core 

Source
Expand description

BLAKE3 compression and a fully pipelined Rust/RHDL implementation.

§Why this module exists

Haraka does the chain steps, but everything else a DSig key needs is BLAKE3, in every profile: deriving the one-time seed, expanding it into the 68 chain secrets and the nonces (BLAKE3’s extendable output, “XOF”), committing to the 68 chain ends, building the Merkle tree, and hashing the message. That is about 47 BLAKE3 compressions per signature (sustained).

BLAKE3 hashes any input by cutting it into 64-byte blocks and running a compression function on each one. A tree-hashing controller decides the order, the counters and the flags; the compression function does the actual mixing. This module is only the compression function. The callers that drive it:

CallerWhat it hashes
crate::slice_factoryseeds, XOF blocks, chain-end commitment, subtree, message digest, on a fixed 68-cycle timetable (crate::slice_schedule)
crate::cert_unitthe Merkle levels shared by all slices
crate::chain_lanethe chain step itself in the DSIG_CHAIN=blake3 profile
crate::slice_modelthe same work in plain Rust, using compress

Because one BLAKE3 lane per slice serves all of that work, the lane must accept a new block every clock. So this file builds a fully pipelined compression: 28 register stages, one block in and one block out per clock.

§What one compression does

The state is 16 words of 32 bits, viewed as a 4 × 4 matrix. It starts as the 8-word chaining value, 4 words of IV, the 64-bit counter, the block length and the flags. Seven rounds then mix in the 16 message words:

  state (16 × 32-bit words)          one round = 8 G functions
  ┌─────┬─────┬─────┬─────┐
  │ v0  │ v1  │ v2  │ v3  │          column step:   G(v0,v4,v8,v12)  G(v1,v5,v9,v13)
  ├─────┼─────┼─────┼─────┤                         G(v2,v6,v10,v14) G(v3,v7,v11,v15)
  │ v4  │ v5  │ v6  │ v7  │
  ├─────┼─────┼─────┼─────┤          diagonal step: G(v0,v5,v10,v15) G(v1,v6,v11,v12)
  │ v8  │ v9  │ v10 │ v11 │                         G(v2,v7,v8,v13)  G(v3,v4,v9,v14)
  ├─────┼─────┼─────┼─────┤
  │ v12 │ v13 │ v14 │ v15 │          then permute the 16 message words
  └─────┴─────┴─────┴─────┘          (MESSAGE_PERMUTATION) for the next round

  G(a, b, c, d) with message words x, y:
    first half:   a += b + x;  d = (d ⊕ a) ⋙ 16;  c += d;  b = (b ⊕ c) ⋙ 12
    second half:  a += b + y;  d = (d ⊕ a) ⋙ 8;   c += d;  b = (b ⊕ c) ⋙ 7

(⋙ is a rotate right; + wraps at 2^32.) After seven rounds a feed-forward XORs the two halves of the state together and the original chaining value into the upper half, giving 16 output words.

§From the picture to a pipeline

The four G functions of a step are independent, so they run side by side. Each G is cut in half, so one round is four register stages:

  CompressionInput ─▶ initialize ─▶ ┌ phase 0: column G, first half   ┐ ─reg─▶
                                    │ phase 1: column G, second half  │ ─reg─▶  × 7 rounds
                                    │ phase 2: diagonal G, first half │ ─reg─▶  = 28 stages
                                    └ phase 3: diagonal G, second half┘ ─reg─▶
                                      + message permutation
                                                         ─▶ feed_forward ─▶ CompressionOutput

Why half a G per stage: each half has two additions in series. A whole G per stage would put four in series between two registers and lower the clock the stage can reach. The out-of-context measurement of one BLAKE3 lane (~325 MHz alone, see sustained) is the evidence that the 205 MHz target has margin; nothing in this file measures it.

§Reading order

  1. Constants: ROUNDS, PIPELINE_STAGES, the flags, IV.
  2. The software model: compress, salted_digest_16.
  3. Ports: CompressionInput, CompressionOutput.
  4. Kernels: g_first_kernel, g_second_kernel, quarter_stage_kernel, feed_forward_kernel.
  5. The pipeline: RoundPipeline, round_pipeline_kernel, CompressionPipeline, InlineCompressionPipeline.

§Summary of the contract

CompressionPipeline implements all seven BLAKE3 rounds, with four register transfers per round. Each stage executes one half of four parallel G functions. Inputs are accepted every cycle; a corresponding output is visible after exactly PIPELINE_STAGES registered transfers. There is no backpressure. The caller must provision downstream capacity before sending a valid token.

Message and chaining words use BLAKE3’s little-endian encoding. This is a compression primitive, not a general-purpose tree-hashing controller. Its caller owns block padding, chunk counters, flag selection, and chaining. salted_digest_16 is the complete fixed-size online message operation: the first 64 XOF bytes of BLAKE3(pk_hash || nonce || message), with a 32-byte public-key hash, 16-byte nonce, and exactly 16 message bytes.

The round state, original chaining value, message schedule, and tag reside in resetless registers. Only the valid bits reset. Data is unspecified when valid is false, and reset discards every in-flight token. No resource, routed-frequency, or board-throughput claim follows from this architecture.

Structs§

CompressionInput
One compression request; all fields except valid are ignored in bubbles.
CompressionOutput
Compression result; words and tag are meaningful only when valid.
GState
Four words manipulated by one BLAKE3 G function.
InlineCompressionPipeline
Copyable, zero-sized adapter for embedding the complete pipeline in a parent.
RoundPipeline
Registered prefix of the BLAKE3 compression pipeline.
RoundState
Resetless token carried between the quarter-round stages.

Constants§

BLOCK_BYTES
Number of input bytes in a BLAKE3 compression block.
CHUNK_END
Marks the final block of a chunk.
CHUNK_START
Marks the first block of a chunk.
DERIVE_KEY_CONTEXT
Selects the context-hashing step of BLAKE3 key derivation.
DERIVE_KEY_MATERIAL
Selects the material-hashing step of BLAKE3 key derivation.
IV
BLAKE3 initialization vector, as specified by the BLAKE3 team.
KEYED_HASH
Selects BLAKE3’s keyed-hash mode.
MESSAGE_PERMUTATION
Word permutation applied after each complete round.
MESSAGE_SCHEDULE
Official seven-round message schedule, used by the independent Rust model.
OUTPUT_BYTES
Number of bytes returned by one BLAKE3 XOF output block.
PARENT
Marks a tree-parent block made from two child chaining values.
PIPELINE_STAGES
Number of registered G-half stages in a complete compression.
ROOT
Selects root/XOF output rather than an intermediate chaining value.
ROUNDS
Number of BLAKE3 compression rounds.
SINGLE_BLOCK_ROOT
Flags for one complete, unkeyed, single-block root hash.

Functions§

compress
Compresses a single BLAKE3 block, returning all sixteen feed-forward words.
compression_pipeline_unchecked_verilog
Renders the complete pipeline without invoking RHDL’s external syntax check.
compression_pipeline_verilog
Emits the complete checked 28-stage BLAKE3 compression hierarchy.
decode_block
Decodes a complete 64-byte block as sixteen little-endian words.
encode_words
Encodes sixteen compression-output words as 64 little-endian XOF bytes.
feed_forward_kernel
Applies BLAKE3’s final XORs, without any additional registered transfer.
g_first_kernel
Synthesizable first half of G: rotations by 16 and 12 bits.
g_second_kernel
Synthesizable second half of G: rotations by 8 and 7 bits.
initialize_kernel
Constructs the round-zero working state without modifying input words.
quarter_stage_kernel
Executes one quarter-round with compile-time phase selection in the pipeline.
round_pipeline_kernel
Connects fixed-phase stages and aligns resettable validity with data/tag.
round_pipeline_verilog
Emits a checked BLAKE3 pipeline or small test prefix as Verilog.
salted_block_16
Constructs the exact single block pk_hash || nonce || message.
salted_digest_16
Returns 64 BLAKE3 XOF bytes for the fixed 16-byte online-message profile.
salted_input_kernel
Builds the fixed 16-byte-message input directly from RHDL word arrays.

Type Aliases§

CompressionPipeline
All seven BLAKE3 rounds, 28-cycle latency, and one input per clock.