Expand description
DSig as a component of the shared chip planner and TUI
([rhdl_fpga_planner]), next to Ed25519 Sign/Verify and the shared SHA-512.
§The problem
DSig is not built alone. The comparison paper puts a DSig signer and
verifier, an Ed25519 signer and verifier and a shared SHA-512 line on one
chip, and asks “with this much area, how many operations per second does
each get?” The shared chip planner (rhdl_fpga_planner, a sibling crate
with a terminal UI, or TUI) answers that by giving each design a share
of the chip (LUTs, DSPs, block RAM, UltraRAM) and asking it for a plan.
So DSig must speak the planner’s language: the [Component] trait.
§The reasoning
crate::config already knows what DSig costs and how fast it is. This
module does not repeat those measurements; it imports them and adds the
knobs the comparison wants to explore. Some of those choices have never
been synthesized, so their costs are estimates, and every [Plan]
says whether it rests on measurement (measured).
Two lessons shaped the plan search:
- A share is not the device. Moving S-boxes into spare RAM must use
the RAM in DSig’s share, not the whole chip’s, or every partial share
fails the RAM check (
config::Build::fill_within). - Small shares still deserve an answer. The smallest built engine has two slices (~207k LUT with S-boxes in RAM). A one-slice engine is offered as a model so a small share still plans something.
§The decision
Two components:
Dsig, the signer we build. Its plan is the largest slice count that fits the share.DsigVerify, a model of a verifier that is not built yet, so the paper can compare sign and verify on the same footing.
What the verifier model counts, step by step (nothing here is built):
rhdl_fpga_planner (TUI)
│ Share {lut, dsp, bram36, uram}, Context {mhz, ...}
▼
Dsig::plan ── for slices in 1..=16 ─────▶ Dsig::cost(slices, share) ──▶ fits?
│ │ yes: keep, try next size
│ │ no : stop, record the limit
▼ ▼
Plan {cost, rate = slices·f/period (own core: min with K·f/895), demands, ...}
│
▼
Dsig::build_flags ──▶ DSIG_SLICES, DSIG_CHAIN, DSIG_SBOX_PLACEMENTThe cost model of one slice is taken apart into lanes so a knob can swap a lane:
slice = slice_rest + factory BLAKE3 lane + 3 × chain lane
slice_rest = SLICE_LUT − 3 × HARAKA_LANE_LUT − BLAKE3_LANE_LUT
kernel = slices × slice + glue (+ Ed25519 core when DSig has its own)§Knobs that the signer and verifier share
A verifier can only check what the signer made, so the scheme knobs
(HBSS, chain hash, SHA lanes, w, K) are shared: the
TUI lists them once, under DSig sign, and sets both modules.
§Where the certificates come from
Every batch of K keys is certified by one Ed25519 signature. Two ways:
- the chip’s Ed25519 sign module (default): DSig sends its batch roots
to the paper’s Ed25519 signer, which the planner sizes for
1/Kof DSig’s rate. DSig’s own cost then has no Ed25519 core. - inside DSig (as built): the hardware built today carries its own
small Ed25519 signer (
ED25519_LUT), capping DSig atK × f / 895. The plan covers Ed25519 signing, so the chip’s Ed25519 sign module is not needed; counting both would count the signer twice.
Knobs of Dsig (and, where they apply, DsigVerify):
| Knob | Choices | What it changes | Status |
|---|---|---|---|
| HBSS | W-OTS+, HORS | the hash-based one-time scheme | W-OTS+ built; HORS not implemented (no plan) |
| chain hash | Haraka-512, BLAKE3, SHA-512 | the lane in every slice (chain-* feature) | built, simulated |
| SHA lanes | own, shared line | SHA-512 chains on DSig’s own pipelines, or as clients of the shared line | own: built; shared: model |
| Winternitz w | 4, 16 | the HBSS: chain length and chain count | 4 built; 16 model |
| batch K | 128, 64, 256 | keys per Ed25519 certificate | 128 built; others model |
| S-box RAM | fill, LUT only | Haraka S-boxes in the share’s spare UltraRAM / block RAM | built |
| BLAKE3 adders | LUT, DSP | 32-bit adders of every BLAKE3 lane in DSP48E2s | planned, estimated |
| Ed25519 certificates | chip’s Ed25519 sign module, inside DSig | who signs batch roots (above) | inside: built |
| message | 16 B (profile 1), 64 B (profile 2) | signed message length, DSIG_MESSAGE_BYTES: profile 2 signs uBFT’s whole 48-byte signed context; one more digest compression per signature (an idle timetable residue, so the same rate) | 16: built, measured; 64: simulated, cost estimated |
Slice counts: any of 1 to 16. 2, 4 and 8 are built (DSIG_SLICES=N), and 2 is measured on the card (build v3: 786,432 signatures
at 6,019,465/s, 34.06 cycles per signature); the others are a model
with the same per-slice cost and slices × f / key period rate.
Costs: measured (routed slices-2 link and Vivado OOC) for the Haraka lane,
the BLAKE3 lane, the slice and the Ed25519 core; estimates for the
SHA-512 lane, the DSP-adder BLAKE3 lane, the one-slice build and S-box
savings (see config).
Structs§
- Dsig
- The DSig component.
- Dsig
Ubft Sign - DSig signing as a uBFT service: the batch signer’s slices and certificate
unit behind a request stream (
crate::ubft::UbftSigner), with its own Ed25519 certificate core and the T-table rounds of the measured builds. - Dsig
Ubft Verify - DSig verification as a uBFT service: one verify core behind a request
stream (
crate::ubft::UbftVerifier) and the Ed25519 known-key verify core with one key slot per peer replica. - Dsig
Verify - DSig verification (built:
crate::verify). - Hbss
- The hash-based one-time signature scheme (HBSS) and its batching.
- Ubft
Message - The uBFT services’ message settings, shared by signer and verifier. Profile 2 is the default: it signs uBFT’s whole context.
Enums§
- Scheme
- Which hash-based one-time signature scheme DSig uses.
Constants§
- AUTO_
TABLE_ ROUNDS - The “table rounds” value meaning auto: [
Component::plan] tries 0..=6 inside its share and keeps the fastest. - BLAK
E3_ DSP_ LANE_ LUT - BLAKE3 lane with its ~224 32-bit adders in DSPs: estimate (each adder ~32 LUTs → one DSP48E2).
- BLAK
E3_ LANE_ DSP - DSPs of a BLAKE3 lane with DSP adders: estimate.
- BLAK
E3_ LANE_ LUT - BLAKE3 compression lane, Vivado OOC routed.
- CHAIN_
LATENCY - Latency of one chain step, in cycles, for Haraka-512, BLAKE3 and SHA-512 (indexed by the chain-hash knob; measured in simulation).
- HARAKA_
LANE_ LUT - Haraka chain-step lane, Vivado OOC routed.
- HARAKA_
TABLE_ LANE_ BRAM36 - BRAM36 tiles of one T-table lane: 176 dual-port BRAM18 ROMs (32 per table round in rounds 3–6, 24 in rounds 7–8 where 16 bytes are never observed). Build v5 synthesized 2,135 RAMB18 for its 12 lanes and caches.
- HARAKA_
TABLE_ LANE_ LUT - One Haraka chain-step lane with T-tables in rounds 3–8 (from synthesis): what is left of a v5 slice after the parts a v3 slice also has.
- LOOKUPS_
PER_ URAM - Table lookups one UltraRAM serves: one per port of a dual-port 4,096 × 72 URAM (a 256 × 32 T-table fills 1/16 of it). Block RAM serves four per BRAM36 (two per BRAM18), so URAM is the poorer home per tile, but a slices-4 kernel leaves ~670 of the device’s 960 unused while block RAM is the wall.
- MESSAGES
- Display names of the message-length choices (index 1 = profile 2).
- SHA512_
CLIENT_ LUT - A chain lane that sends its blocks to the shared SHA-512 line instead of hashing them itself: block assembly, a tag FIFO and the truncation (estimate).
- SHA512_
LANE_ LUT - SHA-512 lane (
rhdl_sha512_pipelined, 80 unrolled rounds): estimate until synthesized (80 × ~420 + 64 × ~150 LUTs). - SIGN_
LATENCY_ CYCLES - Idle signing latency: native simulation (cycle-accurate RHDL, not the
card) of the slice signer behind the uBFT service’s request stream
(
tests/ubft_signer.rs, 2 slices, 8 isolated requests after the first certificate: 171–188 cycles, mean 173.1, every frame the reference signature). A key is cached and its certificate made, so this is the message digest, the 68-cycle schedule slot it waits for, the node reads from the key cache, the 26-beat frame and the relay registers. It replaces an unmeasured 64-cycle estimate (28 BLAKE3 + 26 beats + ~10 registers) that left out the schedule wait and the cache reads. The batch kernel’s HBM reader and writer add their own cycles (not measured). - TABLE_
LOADER_ LUT - LUTs of the loader one lane needs when any of its T-tables are in URAM:
estimate. UltraRAM cannot be preloaded by the bitstream, so after
every reset an 8-bit counter walks the 256 entries and writes each table
(as
crate::aes_table::SboxLoaderdoes for URAM S-boxes); the lane reports not-ready until it finishes. Not built for T-tables. - UBFT_
SIGN_ LATENCY_ CYCLES - Idle signing latency of the whole uBFT service kernel
(
crate::ubft::UbftEngine): native simulation,tests/ubft_engine.rs, request beat to the frame’s last beat, 6 isolated requests after the first certificate. Profile 1: 207–217 cycles, mean 208.7 (1.01–1.06 µs at 205 MHz); profile 2 (64-byte messages): 207–257, mean 215.3. It isSIGN_LATENCY_CYCLES(the signer alone) plus the engine’s stream registers and arbitration, about 36 cycles. The uBFT signing component uses this one, since a replica sees the whole kernel. - UBFT_
SLICES - Slice counts the uBFT signer can be built with (
DSIG_SLICES). - UBFT_
VERIFY_ LATENCY_ CYCLES - Checks that the planner component agrees with measurement and behaves
sensibly as the budget grows.
Isolated verify latency of the uBFT service with its certificate already
checked: native simulation (
tests/ubft_blocks.rs, instant Ed25519 model, header handshake to verdict: 732–736 cycles; the verify core alone isVERIFY_LATENCY_MEASURED). - V5_
KERNEL_ LUT_ SYNTH - Kernel CLB LUTs of build v5 after synthesis (not routed):
slices-4, T-tables in AES rounds 3–8 (DSIG_TABLE_MASK=0x1f8), Ed25519 inside. - VERIFY_
CARD_ RATE - The verifier kernel’s card rate: measured (build v3, three units,
1,572,864 valid signatures in 19,012,289 cycles at 205.00 MHz, finite
burst
vrun-clean-1): 12.09 cycles per signature for the kernel, 36.3 per unit. - VERIFY_
CARD_ UNITS - Units in the card-measured verifier kernel.
- VERIFY_
CHAIN_ STEPS - Expected chain steps to finish one DSig verification: 68 W-OTS+ chains
(w = 4) each need
3 − digitsteps; the 64 message digits are uniform (mean 1.5 steps) and the 4 checksum digits skew low. - VERIFY_
CORE_ BRAM36 - Block-RAM tiles of that core in the kernel (88 RAMB36 + 388 RAMB18): measured, build v1.
- VERIFY_
CORE_ LUT - CLB LUTs of one routed verify core (Haraka lanes with
VERIFY_CORE_TABLE_ROUNDStable rounds) inside the kernel: measured, mean of the three cores of verifier build v1 (Vivado 2023.2 routed, 89,495–89,716). Routed alone (OOC) it was 80,211. - VERIFY_
CORE_ TABLE_ ROUNDS - Table rounds of the measured core’s lanes (
DSIG_TABLE_MASK=0x078). - VERIFY_
CYCLES_ MEASURED - Cycles per verification of one core for the built scheme: native simulation (two whole batches, steady state).
- VERIFY_
ED25519_ BRAM36 - Its block-RAM tiles (41 RAMB36 + 4 RAMB18; measured, 007zzi).
- VERIFY_
ED25519_ DSP - Its DSP slices (measured, 007zzi).
- VERIFY_
ED25519_ ISOLATED - Its latency for an isolated check: native simulation (Ed25519 project, one request at a time, 4 samples: 52,642–52,672, median 52,670).
- VERIFY_
ED25519_ LATENCY - Its latency per check, saturated median: native simulation (Ed25519 project: min 54,144, median 63,026, p90 81,738 cycles; isolated ~52k). The verifier’s verdict queue hides it after a run’s first batch.
- VERIFY_
ED25519_ LUT - The Ed25519 known-key verify core a verifier embeds (fix14, constant
banks as
ConstRom): CLB LUTs inside the verifier kernel, measured (build v3, routed,engine/c1: 79,532). Alone it routes at 84,435 with WNS +0.072 ns (Ed25519 project, 007zzr; fix12 in build v1: 79,786 in context, 82,184 alone). - VERIFY_
ED25519_ RATE - Its certificate checks per second at 205 MHz: native simulation (Ed25519 project, fix12 dcb2e6e: 600 verifications, 735.25 cycles each).
- VERIFY_
ED25519_ URAM - Its UltraRAMs (measured, 007zzi).
- VERIFY_
LATENCY_ MEASURED - Idle latency of the hash path for the built scheme (first beat to verdict, certificate already checked): native simulation.
- VERIFY_
UNIT_ IO_ LUT - LUTs of a unit beyond its core: HBM reader and writer and its share of the cluster (registered links, registers, arbiter). Measured, build v1: (366,916 engine − 79,786 Ed25519) / 3 units − 89,627 per core.
Functions§
- table_
lane - LUTs and BRAM36 of one Haraka lane with its first
roundstable rounds (0..=6, from round 3). - table_
lookups - Table lookups (S-boxes replaced by T-table reads) in one lane’s first
roundstable rounds, counting up from round 3.