Skip to main content

Module reference

Module reference 

Source
Expand description

Complete byte-oriented reference for the explicitly versioned DSig profile.

This is a software oracle and a trusted preload path, not synthesizable hardware or sustained FPGA key-generation evidence. A PreparedKey is consumed by signing, is deliberately not Clone, and wipes its cached secret nodes on drop. The caller must still persist epoch/batch allocation before deriving seeds: reconstructing the same seed after reset defeats one-time security. Do not expose precomputed chain caches outside a trusted boundary.

§Why this module exists

Hardware is hard to debug. When the engine writes a 1,642-byte signature to HBM, how do you know that every byte is right? You need an answer key. This module is that answer key: a plain, slow, easy-to-read Rust implementation of the whole DSig profile, from seed to transferable signature. Every hardware block is checked against it bit for bit (see crate::slice_model and the tests in tests/), and the host tool dsig_engine_host uses it to verify what the card produced.

It is written for clarity, not speed. It uses byte arrays instead of RHDL Bits, and the blake3 and ed25519-dalek crates instead of hardware lanes. Nothing here is synthesized.

§The story of one signature, in software

 master seed ─┐
 signer key  ─┼─ one_time_seed() ──▶ 32-byte seed per key (×128 per batch)
 epoch,batch ─┤        (BLAKE3 derive_key, SEED_DERIVATION_CONTEXT)
 key index   ─┘
                             │
                             ▼
                 PreparedKey::from_seed()
                   pk_nonce, nonce   (BLAKE3 XOF, prefixes 0x5eed / 0xc0ca)
                   nodes[0][0..68]   (BLAKE3 XOF of the seed: chain secrets)
                   nodes[1..=3]      (3 chain steps per chain: dsig_chain)
                   pk_hash           (endpoint_commitment of nodes[3])
                             │
           128 pk_hash ──▶ BatchTree::new() ──▶ root
                             │
                             ▼
           BatchCommitment { epoch, batch_id, root } ── Ed25519 ──▶ BatchCertificate
                             │
 message ──▶ message_digits() ──▶ 68 base-4 digits (64 message + 4 checksum)
                             │
                             ▼
           PreparedKey::sign(): reveal nodes[digit][chain] for each chain
                             │
                             ▼
           DsigSignature = WOTS part + key index + 7 siblings + certificate
                             │
                             ▼
           to_bytes(): 1,642 bytes on the wire

§Words you will meet

  • W-OTS+ (Winternitz one-time signature): each of the 68 chains starts at a secret node and is hashed forward. Signing a base-4 digit d reveals the node at depth d; a verifier hashes it the remaining 3 − d steps and must land on the public endpoint.
  • Checksum digits: the last 4 digits encode the sum of 3 − digit over the 64 message digits. An attacker can only hash forward, which raises digits; that lowers the checksum, which they cannot do. So no forgery comes for free from a seen signature.
  • Nonce (salt): a 16-byte random value mixed into hashes so that two keys never share chain inputs.
  • Merkle tree: a binary tree of hashes. Its 128 leaves are the keys’ pk_hash values; its root stands for all 128 at once, so one Ed25519 signature can vouch for all of them.

§Wire layout of a signature

BytesField
0..64BatchCommitment (see crate::certificate)
64..128Ed25519 signature R ‖ S
128..130key index, little endian
130..162pk_hash
162..178pk_nonce
178..194message nonce
194..4187 Merkle siblings × 32 bytes, bottom up
418..164268 chain nodes × 18 bytes, in chain order

§Security rules this module enforces (and the ones it cannot)

  • A one-time key signs at most once. PreparedKey::sign takes self by value, so Rust’s ownership rules stop a second call. PreparedBatch stores keys as Option and takes them, so a repeated index returns None.
  • Secrets are wiped. Secret nodes, nonces, seeds and the master seed are zeroized as soon as they are no longer needed; the master seed is never stored in PreparedBatch.
  • What it cannot enforce: that (epoch, batch_id) was never used before. Rebuilding a batch from the same master seed and namespace recreates the same keys, which would let one key sign two messages. The host must durably allocate namespaces first; crate::lease does that.

§Neighbours

Structs§

BatchTree
Fixed 128-leaf BLAKE3 tree authenticating public-key commitments.
DsigSignature
Transferable signature: hash-based component, inclusion proof, certificate.
KeyPublic
Public metadata for one cached one-time key.
PreparedBatch
Prepare and certify a reference batch; repeated namespace allocation is unsafe.
PreparedKey
Single-use secret cache. Signing consumes this object before returning data.
VerifiedBatch
Authenticated background cache entry; no unchecked public constructor.
WotsSignature
WOTS component only; its public-key hash is not itself a trusted identity.

Constants§

SIGNATURE_WIRE_BYTES
Self-standing signature wire length, with no transport padding.

Functions§

digits_from_xof
Extract low two bits from each XOF byte and append the complement checksum.
endpoint_commitment
Public salt plus endpoint commitment, exactly ordered as upstream DSig.
message_digits
Salted BLAKE3-XOF message mapping; 64 low-bit digits encode 128 digest bits.
root_from_proof
Recover a root from a public-key commitment and checked leaf index.

Type Aliases§

ChainCache
Four stored positions for every chain; the last row contains public nodes.