rhdl_ed25519_core/lib.rs
1//! # Dalek-compatible Ed25519 hardware in RHDL
2//!
3//! This crate is the hardware facade for the workspace. Its modules re-export
4//! the real SHA-512, field, scalar, point, message-feeder, and wire-type crates
5//! used by the synthesizable design. It contains no Dalek implementation code:
6//! the `rhdl_ed25519_model` crate uses the pinned Dalek snapshot only as a
7//! differential-test oracle.
8//!
9//! The workspace has two hardware tracks:
10//!
11//! - The **compatibility core** implements public-key derivation, cold and
12//! cached-key signing, strict verification, multipart byte streams, and key
13//! clearing. `rhdl_ed25519_top::Ed25519Core` assembles the modules exported
14//! here into the end-to-end synchronous design.
15//! - The **fixed-64 fast path** is an optimization track for saturated cold
16//! signing of 64-byte messages on an AMD Alveo U280. Its `fast_field`,
17//! `fast_fixed_base`, `fast_sha512`, `fast_point_codec`, `fast_scalar`, and
18//! `fast_sign` crates contain the specialized SystemVerilog datapath, RHDL
19//! wrapper types, cycle models, and RTL tests. It is not yet a replacement for
20//! the compatibility core.
21//!
22//! ## End-to-end signing
23//!
24//! For an RFC 8032 seed `sk` and message `M`, the hardware executes:
25//!
26//! ```text
27//! SHA512(sk) -----------------> clamp -> a -------> [a]B -> encode -> A
28//! |
29//! +-----------------------> prefix
30//!
31//! SHA512(prefix || M) --------> reduce mod l -> r -> [r]B -> encode -> R
32//! SHA512(R || A || M) --------> reduce mod l -> k
33//! S = r + k*a mod l
34//! signature = R || S
35//! ```
36//!
37//! The host never supplies a prehash, expanded key, reduced nonce, challenge,
38//! or intermediate point. SHA-512 padding, clamping, reduction, scalar
39//! multiplication, point compression, and signature assembly all execute in
40//! hardware.
41//!
42//! ## Strict verification
43//!
44//! Verification rejects malformed inputs before evaluating the group equation:
45//!
46//! ```text
47//! canonical(S)?
48//! decode(A) -> reject malformed or small-order A
49//! decode(R) -> reject malformed or small-order R
50//! SHA512(R || A || M) -> reduce mod l -> k
51//! compare R with encode([S]B - [k]A)
52//! ```
53//!
54//! Dedicated result codes distinguish framing errors, a missing cached key,
55//! malformed points, non-canonical `S`, small-order points, and equation
56//! failure. See [`types`] for the wire-level contract.
57//!
58//! ## Arithmetic hierarchy
59//!
60//! The compatibility backend follows Dalek's serial 32-bit organization:
61//!
62//! - [`field`] represents values modulo `p = 2^255 - 19` with ten alternating
63//! 26/25-bit limbs stored in 32-bit lanes.
64//! - [`scalar`] represents values modulo the Ed25519 group order `l` and
65//! implements wide reduction, 256-bit reduction, canonicality, and
66//! `r + k*a mod l`.
67//! - [`point`] uses extended Edwards coordinates `(X:Y:Z:T)`, Projective Niels
68//! points, balanced radix-16 recoding, and fixed-pattern candidate scans.
69//! - [`sha512`] implements padding, a 16-word circular schedule, and all 80
70//! compression rounds in synchronous logic.
71//! - [`hash_feeder`] inserts hash prefixes and retains the first 4 KiB of a
72//! message for the second signing pass.
73//!
74//! ## Memory and traffic
75//!
76//! Arithmetic engines use registers and local BRAM/ROM only. They do not use
77//! HBM or DDR as scratch storage. For a signing message of `N` bytes, the 4 KiB
78//! cache gives the logical external message traffic
79//!
80//! ```text
81//! N + max(N - 4096, 0)
82//! ```
83//!
84//! before AXI-line padding. Verification reads `N` message bytes once. The
85//! top-level result separately reports logical stream bytes and external read
86//! bytes; host/AXI/HBM traffic must also count command records, result records,
87//! line padding, and data-mover behavior.
88//!
89//! ## Secret-dependent work
90//!
91//! Scalar loops have fixed trip counts. Point-table lookup reads every candidate
92//! bank at the same public address and selects only after the reads. A secret
93//! digit must not select a BRAM address, enabled bank, iteration count, or stall
94//! pattern. Zero digits execute the same point-operation schedule as nonzero
95//! digits. These properties require emitted-RTL address and enable tests in
96//! addition to functional Rust tests.
97//!
98//! ## Source map
99//!
100//! The modules below are the stable way to navigate arithmetic and protocol
101//! APIs. Their items are re-exported from the owning implementation crates.
102//! Higher-level control is intentionally split into separate crates:
103//!
104//! | Crate | Responsibility |
105//! |---|---|
106//! | `rhdl_ed25519_top` | child wiring, arbitration, and top-level synchronous design |
107//! | `rhdl_ed25519_controller_types` | top-level I/O, states, and status |
108//! | `rhdl_ed25519_transition` | pure high-level next-state kernel |
109//! | `rhdl_ed25519_commands` | child-engine command generation |
110//! | `rhdl_ed25519_registers` | retained cache/work/point register updates |
111//! | `rhdl_ed25519_result` | result and error classification |
112//! | `rhdl_ed25519_api` | Dalek signing traits over a driver/simulator transport |
113//! | `rhdl_ed25519_model` | pinned-Dalek reference behavior and vectors |
114//! | `rhdl_ed25519_sim` | cycle simulation, traces, RTL export, and checksums |
115//! | [`architecture`] | current backend map, pipeline, cycle breakdown, and contributor guide |
116//!
117//! ## Evidence and performance status
118//!
119//! The compatibility core has end-to-end RHDL simulation evidence, including
120//! exact RFC 8032 signing and strict verification. The recorded 64-byte signing
121//! run takes 90,928 core cycles. A U280 out-of-context synthesis constrained to
122//! 300 MHz records 237,294 LUTs, 94,129 registers, 800 DSPs, and 6 block-RAM
123//! tiles, but has negative WNS and therefore must not be described as a closed
124//! 300 MHz implementation.
125//!
126//! The latest 64-signature Verilator test observed 33,051 cycles across 31
127//! post-warmup completion intervals, or 1,066.161 cycles/completion. Every
128//! signature matched pinned Dalek. At an assumed 250 MHz that finite window
129//! projects to 234,486 signatures/s, but no integrated 250 MHz timing result is
130//! current. The latest archived integrated U280 OOC report targets 300 MHz,
131//! uses 360,288 LUTs, 154,783 registers, 90 RAMB36 tiles, and 720 DSPs, and has
132//! WNS of -1.078 ns. It therefore neither closes 300 MHz nor meets the earlier
133//! 260,000-LUT budget. Final acceptance still requires post-route timing and a
134//! long sustained U280 run with every signature checked by pinned Dalek.
135//!
136//! ## Contributor workflow
137//!
138//! 1. Start in the owning module below, not in generated Verilog.
139//! 2. Add an independent arithmetic or Dalek differential test first.
140//! 3. Compile the affected RHDL kernel and simulate emitted RTL when lowering or
141//! a SystemVerilog black box is involved.
142//! 4. Inspect secret-dependent address, enable, and valid traces.
143//! 5. Run the end-to-end simulator before changing a compatibility claim.
144//! 6. Regenerate Vivado evidence before changing an area or timing claim.
145//!
146//! On memory-constrained hosts, use one Cargo job and iterate on narrow crates:
147//!
148//! ```text
149//! cargo test -p rhdl_ed25519_model --release -j 1
150//! cargo test -p rhdl_ed25519_core --release -j 1
151//! RHDL_SKIP_IVERILOG_CHECK=1 cargo test --workspace --release -j 1
152//! cargo doc --workspace --no-deps -j 1
153//! ```
154
155#![allow(clippy::needless_range_loop)]
156
157#[doc = include_str!("../../../docs/ARCHITECTURE.md")]
158pub mod architecture {}
159
160pub mod field {
161 //! Field arithmetic modulo `2^255 - 19`.
162 //!
163 //! The compatibility backend uses [`FieldElement2625`] and exposes serial
164 //! and four-lane engines. Encoding, inversion, square-root chains, and
165 //! parallel multiplication are implemented by the owning
166 //! `rhdl_ed25519_field` crate.
167
168 pub use rhdl_ed25519_field::*;
169}
170
171pub mod hash_feeder {
172 //! Streaming SHA-512 input assembly and the 4 KiB message replay cache.
173 //!
174 //! [`HashFeederEngine`] combines an internal [`crate::sha512::Sha512Engine`]
175 //! with prefix selection, 64-bit message-word framing, logical/external byte
176 //! counters, and signing-pass replay requests.
177
178 pub use rhdl_ed25519_hash_feeder::*;
179}
180
181pub mod point {
182 //! Edwards-point arithmetic, scalar multiplication, and point encoding.
183 //!
184 //! This module re-exports the point controller, balanced-radix-16 scalar
185 //! multiplier, strict compression/decompression controller, and the
186 //! constant-time 32-byte equality kernel used by verification.
187
188 pub use rhdl_ed25519_bytes_equal::*;
189 pub use rhdl_ed25519_point::*;
190 pub use rhdl_ed25519_point_codec::*;
191 pub use rhdl_ed25519_scalar_mul::*;
192}
193
194pub mod scalar {
195 //! Scalar arithmetic modulo the Ed25519 group order.
196 //!
197 //! [`ScalarEngine`] accepts wide and 256-bit reductions, modular multiply-add,
198 //! and canonicality commands through one fixed-latency state machine.
199
200 pub use rhdl_ed25519_scalar::*;
201}
202
203pub mod sha512 {
204 //! End-to-end hardware SHA-512.
205 //!
206 //! [`Sha512Engine`] accepts an optional internal prefix and a byte stream,
207 //! constructs FIPS 180-4 padding in hardware, and emits the eight-word
208 //! digest. No host prehash is part of the public path.
209
210 pub use rhdl_ed25519_sha512::*;
211}
212
213pub mod types {
214 //! Public command, message-stream, pass-request, result, flag, and error
215 //! encodings shared by the core, simulator, and FPGA shell.
216
217 pub use rhdl_ed25519_types::*;
218}
219
220pub use types::*;