Skip to main content

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}