keccak_rhdl/lib.rs
1//! Legacy Keccak-256 primitives for the `HashSigsRS` compatibility profile.
2//!
3//! This crate deliberately implements **Keccak-256**, whose final partial block
4//! uses the legacy `0x01` domain byte. It does not implement FIPS 202
5//! SHA3-256, whose corresponding byte is `0x06`. Confusing those encodings
6//! produces unrelated digests and therefore incompatible `HashSigs` keys and
7//! signatures.
8//!
9//! The hardware-facing foundation is split into three layers:
10//!
11//! - [`keccak_round`] is one fully combinational `Keccak-f[1600]` round. Its
12//! rotation offsets and lane destinations are compile-time constants.
13//! - [`keccak_f1600`] composes all 24 rounds and is useful as a correctness
14//! oracle and an RTL generation target. It is not the intended area-efficient
15//! implementation.
16//! - [`IterativeKeccak`] stores one 1,600-bit state and applies one round per
17//! cycle. A request accepted while [`IterativeOutput::ready`] is true emits a
18//! one-cycle [`IterativeOutput::done`] pulse after exactly 24 round cycles.
19//!
20//! [`LegacyKeccak256`] is a software/block-packing model used by tests and by
21//! downstream schedulers. It exposes 136-byte rate blocks in the same
22//! little-endian lane order consumed by the RHDL absorption kernel. This keeps
23//! variable-length byte buffering out of the permutation core while covering
24//! every `HashSigs` message size (32, 35, 64, and 2,144 bytes).
25//!
26//! # Evidence boundary
27//!
28//! The tests exercise the Rust model, RHDL VM, lowered RTL VM, and emitted
29//! Verilog in Icarus. No synthesis, utilization, timing, route, or physical
30//! hardware claim follows from those tests.
31
32#![forbid(unsafe_code)]
33
34mod engine;
35mod permutation;
36mod round;
37mod sponge;
38
39pub use engine::{IterativeInput, IterativeKeccak, IterativeOutput};
40pub use permutation::{keccak_f1600, software_keccak_f1600};
41pub use round::{
42 ROTATION_OFFSETS, ROUND_CONSTANTS, keccak_round, keccak_round_00, keccak_round_01,
43 keccak_round_02, keccak_round_03, keccak_round_04, keccak_round_05, keccak_round_06,
44 keccak_round_07, keccak_round_08, keccak_round_09, keccak_round_10, keccak_round_11,
45 keccak_round_12, keccak_round_13, keccak_round_14, keccak_round_15, keccak_round_16,
46 keccak_round_17, keccak_round_18, keccak_round_19, keccak_round_20, keccak_round_21,
47 keccak_round_22, keccak_round_23, software_keccak_round,
48};
49pub use sponge::{
50 Digest, LEGACY_DOMAIN_BYTE, LegacyKeccak256, RATE_BYTES, RATE_LANES, RateBlock,
51 absorb_rate_block, digest_from_state, xor_rate_block,
52};
53
54use rhdl::prelude::RHDLError;
55use rhdl::prelude::{ClockReset, Func, Pretty, Synchronous, kernel};
56
57/// Number of rounds in `Keccak-f[1600]`.
58pub const ROUND_COUNT: usize = 24;
59
60/// A `Keccak-f[1600]` state in the canonical `x + 5*y` lane order.
61///
62/// Lane zero occupies the least-significant 64 bits when RHDL flattens this
63/// array. Bytes within each lane are little-endian, as required by Keccak.
64pub type KeccakState = [rhdl::prelude::b64; 25];
65
66/// Generate a standalone combinational one-round Verilog module.
67///
68/// The generated `keccak_round_ooc` top accepts and produces a 1,600-bit state.
69/// Round zero's Iota constant is bound at compile time, so the isolated top has
70/// no round-constant mux. RHDL's pure synchronous-function wrapper also emits
71/// an unused two-bit clock/reset port; the datapath itself is combinational.
72/// It contains no handwritten cryptographic Verilog. This API is intended for
73/// the downstream RTL exporter and isolated synthesis experiments.
74///
75/// # Errors
76///
77/// Returns an RHDL compilation or Verilog-lowering error.
78pub fn generate_round_verilog() -> Result<String, RHDLError> {
79 let top = Func::<KeccakState, KeccakState>::try_new::<round_export_kernel>()?;
80 Ok(top
81 .descriptor("keccak_round_ooc".into())?
82 .hdl()?
83 .modules
84 .pretty())
85}
86
87/// Generate a standalone 24-round combinational permutation module.
88///
89/// This very deep combinational module is a verification/export artifact. Use
90/// [`IterativeKeccak`] when one physical round reused over 24 cycles is desired.
91///
92/// # Errors
93///
94/// Returns an RHDL compilation or Verilog-lowering error.
95pub fn generate_permutation_verilog() -> Result<String, RHDLError> {
96 let top = Func::<KeccakState, KeccakState>::try_new::<permutation_export_kernel>()?;
97 Ok(top
98 .descriptor("keccak_f1600_ooc".into())?
99 .hdl()?
100 .modules
101 .pretty())
102}
103
104/// Generate the 24-cycle iterative engine and its RHDL-owned storage modules.
105///
106/// # Errors
107///
108/// Returns an RHDL compilation or Verilog-lowering error.
109pub fn generate_iterative_verilog() -> Result<String, RHDLError> {
110 Ok(IterativeKeccak::default()
111 .descriptor("keccak_iterative".into())?
112 .hdl()?
113 .modules
114 .pretty())
115}
116
117#[kernel]
118#[doc(hidden)]
119#[allow(clippy::used_underscore_binding)] // The RHDL wrapper consumes this port.
120pub fn round_export_kernel(_cr: ClockReset, state: KeccakState) -> KeccakState {
121 keccak_round_00(state)
122}
123
124#[kernel]
125#[doc(hidden)]
126#[allow(clippy::used_underscore_binding)] // The RHDL wrapper consumes this port.
127pub fn permutation_export_kernel(_cr: ClockReset, state: KeccakState) -> KeccakState {
128 keccak_f1600(state)
129}