Expand description
§Direct DSig certificate bridge
CertificateSigner connects the actual DSig
batch_commitment_words
kernel to one actual compact Ed25519 CachedSignCore. The eight words become
SignRequest.message directly. SHA-512, key expansion, and Ed25519 signing
remain inside that core; this bridge performs no host prehash and repeats no
DSig domain or profile constants.
The wrapper has exactly one stateful child and adds no registers, queues,
credits, fault latch, or bank-admission controller. Its output is the original
CachedSignOut, including ready, one-cycle valid, signature, tag,
occupancy, and internal_error. This is a composition boundary, not a
continuous key-preparation/refill system or a throughput claim.
§Signed bytes and the routing tag
The producer supplies the full epoch, batch ID, and completed 128-key Merkle
root in CertificateSignerInput. certificate_request calls DSig’s packer
and returns the real Ed25519 request type. The signed message is DSig’s exact
64-byte versioned certificate envelope; the 16-bit tag is outside those bytes.
Use the tag only to look up a live outstanding record containing the full epoch, batch ID, root, and trusted Ed25519 signer identity. A tag is neither an anti-reuse namespace nor evidence that a bank is authenticated. This bridge does not generate a Merkle root, authenticate a returned certificate, or decide whether a bank may be admitted.
§Caller obligations
- Reserve a durable DSig namespace before producing keys or requesting its certificate. Reset, timeout, and uncertain completion never permit reuse.
- Load a 32-byte seed only when
load_key_readyis asserted. Wait forkey_loaded, and check the returned public key against the trusted signer identity before submitting any envelope. Seed words are little endian. - Reserve result-buffer capacity for every request before asserting
valid. Accept only onvalid && ready; hold a pending request stable otherwise. There is no output-ready signal: capture every completion pulse. - Latch
internal_errorexternally and stop admission/certification. The underlying core does not suppress readiness or completion on fault. A fault takes priority over a simultaneousvalidpulse; do not certify that result or interpret a later apparently healthy pulse as recovery. - Reject unknown, stale, or duplicate completion tags. Resolve the full saved envelope and signer, then strictly verify the certificate before any admission policy marks a key bank certified.
- Drain every accepted request and retained result before
clear_keyor a key change. Keepvalidandload_key_validlow during clear. Clear is not a cancellation handshake and does not erase all intermediate pipeline/memory storage. The caller must quarantine outstanding work across reset and keep its durable namespace burned.
The wrapper deliberately forwards all child outputs and controls unchanged; it cannot enforce these policies without adding the controller excluded from this boundary.
§Dependency and validation boundaries
The root-owned dependency graph selects the frozen Ed25519 snapshot with
default-features = false, under the same Git RHDL identities and compiler
patch as DSig. Before any native or RTL gate, the integration owner must audit
the resolved Cargo features. (The current core, rhdl_ed25519_scalable_core, has no
wide-sign feature.)
Disabling features on this bridge’s command line cannot disable a dependency
feature enabled elsewhere through Cargo feature unification. A bridge-local
cfg!(feature = "wide-sign") would not establish the required topology.
The small tests exercise the actual packing helper against DSig’s byte serializer, including namespace/root bit coverage and high-bit tags. A wiring test checks unmasked coincident completion/fault forwarding without issuing an illegal transaction to the core.
The ignored native_compact_certificate_handoff test instantiates the actual
CertificateSigner through the imported constant-memory CycleSim, with no
waveform tracing. It expands one public fixture seed, signs three distinct
DSig envelopes with high-bit tags, matches every signature exactly to Dalek
2.2, verifies each through the actual DSig BatchCertificate::verify_strict,
rejects changed signed bytes and a wrong trusted signer, tracks all tags, and
drains before clear. It uses a dedicated 256 MiB stack and at most 60,000
non-reset cycles. All test material is fixed and public, never production keys.
After central resource reservation and feature-graph audit:
cargo test -p dsig-ed25519-rhdl --profile dsig-sim --offline --locked --no-default-features -j1 --lib -- --test-threads=1
cargo test -p dsig-ed25519-rhdl --profile dsig-sim --offline --locked --no-default-features -j1 --test certificate_handoff actual_dsig_packer_preserves_namespace_root_and_high_tag_bits -- --exact --test-threads=1
cargo test -p dsig-ed25519-rhdl --profile dsig-sim --offline --locked --no-default-features -j1 --test certificate_handoff native_compact_certificate_handoff -- --ignored --exact --nocapture --test-threads=1These commands are proposed gates, not passing evidence. Native correctness
does not establish generated-RTL equivalence, LUT/register/DSP fit, routed
frequency, on-card operation, continuous refill, or sustained DSig throughput.
The existing frozen Ed25519 source has a missing publication notice; see
vendor/ed25519-compact/PROVENANCE.md before distributing that snapshot. Its
upstream unchecked-export bypass is not a valid joint RTL acceptance gate.
§Where this bridge sits, and why it is so thin
Every batch of 128 one-time keys needs one Ed25519 signature over its
64-byte envelope (see crate::certificate). The Ed25519 math is large
and was already built and tested by a separate project, so DSig reuses
that core unchanged (vendored as rhdl_ed25519_scalable_core) instead of
writing its own. This file is only the plug between the two.
crate::cert_unit (bookkeeping: tags, ≤ 32 in flight, in-order delivery)
│ CertificateSignerInput: key control + epoch, batch_id, root, tag
▼
┌──────────── CertificateSigner (this file, no state of its own) ───────┐
│ certificate_request(): batch_commitment_words() → SignRequest │
│ │ CachedSignIn │
│ ▼ │
│ Ed25519SignCoreSmall (SHA-512, key expansion, scalar multiply) │
└────────┬──────────────────────────────────────────────────────────────┘
│ CachedSignOut: ready, valid pulse, signature, tag, occupancy,
▼ internal_error (forwarded unchanged)
crate::cert_unitWhy no registers or queues here? Every extra stage would be a new place
where a request could be lost, duplicated or reordered, and a new piece of
state that a reset could leave half-finished. Keeping the bridge stateless
means all policy (credits, fault latching, tag records, draining) lives in
one place, crate::cert_unit, where it can be reviewed as a whole.
Figures: the core cost 25,383 CLB LUTs in context on the routed slices-2
link (the planner’s ED25519_LUT) and
25,911 LUTs and 248 DSPs inside the certificate unit of build v13 (routed,
measured). It finishes one signature per ~895 cycles with at least 32
requests in flight (Ed25519 project, simulation), far more than the one
certificate per 128 signatures, about 94,000 per second at 12M signatures
per second, that the engine needs.
Structs§
- Cached
Sign Out - Unmodified cached-signer output, including unbackpressured completion and fault.
Signer output port (the
CachedSignOutlayout). - Certificate
Signer - Direct RHDL composition of one DSig packer and exactly one cached signer.
- Certificate
Signer Input - Key-control signals and one complete DSig certificate request.
- Scalar
Words - Actual Ed25519 seed/public-key word type used at the bridge boundary. A scalar or 32-byte value as four little-endian 64-bit words.
Functions§
- certificate_
request - Pack the actual DSig fixed-64 message into the actual Ed25519 request type.
- certificate_
signer_ kernel - Stateless wiring for
CertificateSigner; clock/reset reaches its child through RHDL’s synchronous hierarchy, and every child output is forwarded.