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
dreveals the node at depthd; a verifier hashes it the remaining3 − dsteps and must land on the public endpoint. - Checksum digits: the last 4 digits encode the sum of
3 − digitover 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_hashvalues; its root stands for all 128 at once, so one Ed25519 signature can vouch for all of them.
§Wire layout of a signature
| Bytes | Field |
|---|---|
| 0..64 | BatchCommitment (see crate::certificate) |
| 64..128 | Ed25519 signature R ‖ S |
| 128..130 | key index, little endian |
| 130..162 | pk_hash |
| 162..178 | pk_nonce |
| 178..194 | message nonce |
| 194..418 | 7 Merkle siblings × 32 bytes, bottom up |
| 418..1642 | 68 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::signtakesselfby value, so Rust’s ownership rules stop a second call.PreparedBatchstores keys asOptionandtakes them, so a repeated index returnsNone. - 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::leasedoes that.
§Neighbours
crate::certificatedefines the signed 64-byte batch envelope.crate::harakaprovidesdsig_chain, one chain step in the compiled-in chain hash (Haraka-512 unless achain-*feature says otherwise).crate::slice_modelreusesmessage_digitsandPreparedBatchto predict the hardware cycle by cycle.crate::leasewrapsPreparedBatchwith never-reused batch ranges.
Structs§
- Batch
Tree - Fixed 128-leaf BLAKE3 tree authenticating public-key commitments.
- Dsig
Signature - Transferable signature: hash-based component, inclusion proof, certificate.
- KeyPublic
- Public metadata for one cached one-time key.
- Prepared
Batch - Prepare and certify a reference batch; repeated namespace allocation is unsafe.
- Prepared
Key - Single-use secret cache. Signing consumes this object before returning data.
- Verified
Batch - Authenticated background cache entry; no unchecked public constructor.
- Wots
Signature - 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§
- Chain
Cache - Four stored positions for every chain; the last row contains public nodes.