cyber_hemera/params.rs
1//! Hemera — Poseidon2 parameter set over the Goldilocks field.
2//!
3//! Single source of truth for every constant in the protocol.
4//! The WGSL shader (`gpu/poseidon2.wgsl`) duplicates a subset of
5//! these values because WGSL cannot import Rust; keep them in sync.
6//!
7//! ```text
8//! ┌──────────────────────────────────────────────────────────┐
9//! │ HEMERA — Complete Specification │
10//! │ │
11//! │ Field: p = 2⁶⁴ − 2³² + 1 (Goldilocks) │
12//! │ S-box: d = 7 (x → x⁷, minimum for field) │
13//! │ State width: t = 16 = 2⁴ │
14//! │ Full rounds: R_F = 8 (4 + 4) = 2³ │
15//! │ Partial rounds: R_P = 64 = 2⁶ │
16//! │ Rate: r = 8 elements (56 bytes) = 2³ │
17//! │ Capacity: c = 8 elements (64 bytes) = 2³ │
18//! │ Output: 8 elements (64 bytes) = 2³ │
19//! │ │
20//! │ Full round constants: 8 × 16 = 128 = 2⁷ │
21//! │ Partial round constants: 64 = 2⁶ │
22//! │ Total constants: 192 = 3 × 2⁶ │
23//! │ Total rounds: 72 = 9 × 2³ │
24//! │ │
25//! │ Classical collision resistance: 256 bits = 2⁸ │
26//! │ Quantum collision resistance: 170 bits │
27//! │ Algebraic degree: 2¹⁸⁰ │
28//! │ │
29//! │ Every parameter that appears in code is a power of 2. │
30//! └──────────────────────────────────────────────────────────┘
31//! ```
32
33use crate::field::Goldilocks;
34
35// ── Permutation parameters ──────────────────────────────────────────
36
37/// Width of the Poseidon2 state (number of Goldilocks field elements).
38pub const WIDTH: usize = 16;
39
40/// Number of full (external) rounds — 4 initial + 4 final.
41pub const ROUNDS_F: usize = 8;
42
43/// Number of partial (internal) rounds.
44pub const ROUNDS_P: usize = 64;
45
46/// S-box degree (x → x^d).
47pub const SBOX_DEGREE: usize = 7;
48
49// ── Sponge parameters ───────────────────────────────────────────────
50
51/// Number of rate elements in the sponge.
52pub const RATE: usize = 8;
53
54/// Number of capacity elements in the sponge.
55pub const CAPACITY: usize = WIDTH - RATE; // 8
56
57// ── Encoding parameters ─────────────────────────────────────────────
58
59/// Bytes per field element when encoding arbitrary input data.
60///
61/// We use 7 bytes per element because 2^56 − 1 < p (Goldilocks prime),
62/// so any 7-byte value fits without reduction.
63pub const INPUT_BYTES_PER_ELEMENT: usize = 7;
64
65/// Bytes per field element when encoding hash output.
66///
67/// For output we use the full canonical u64 representation (8 bytes),
68/// since output elements are already valid field elements.
69pub const OUTPUT_BYTES_PER_ELEMENT: usize = 8;
70
71// ── Derived constants ───────────────────────────────────────────────
72
73/// Number of input bytes that fill one rate block (8 elements × 7 bytes).
74pub const RATE_BYTES: usize = RATE * INPUT_BYTES_PER_ELEMENT; // 56
75
76/// Number of output elements extracted per squeeze (= rate).
77pub const OUTPUT_ELEMENTS: usize = RATE; // 8
78
79/// Number of output bytes per squeeze (8 elements × 8 bytes).
80pub const OUTPUT_BYTES: usize = OUTPUT_ELEMENTS * OUTPUT_BYTES_PER_ELEMENT; // 64
81
82// ── Tree parameters ─────────────────────────────────────────────────
83
84/// Canonical chunk size for content tree construction (4 KB).
85///
86/// Content is split into fixed 4 KB chunks. Each chunk is hashed via
87/// `chunk_cv`. The last chunk may be shorter. See spec §4.6.1.
88pub const CHUNK_SIZE: usize = 4096;
89
90// ── Security properties (informational) ─────────────────────────────
91
92/// Classical collision resistance in bits.
93pub const COLLISION_BITS: usize = 256;
94
95// ── Permutation entry point ─────────────────────────────────────────
96
97/// Apply the Poseidon2 permutation in-place.
98#[inline]
99pub(crate) fn permute(state: &mut [Goldilocks; WIDTH]) {
100 crate::permutation::permute(state);
101}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106
107 #[test]
108 fn permutation_is_deterministic() {
109 let mut s1 = [Goldilocks::ZERO; WIDTH];
110 let mut s2 = [Goldilocks::ZERO; WIDTH];
111 permute(&mut s1);
112 permute(&mut s2);
113 assert_eq!(s1, s2);
114 }
115
116 #[test]
117 fn permutation_changes_state() {
118 let mut state = [Goldilocks::ZERO; WIDTH];
119 let original = state;
120 permute(&mut state);
121 assert_ne!(state, original);
122 }
123
124 #[test]
125 fn different_inputs_different_outputs() {
126 let mut s1 = [Goldilocks::ZERO; WIDTH];
127 let mut s2 = [Goldilocks::ZERO; WIDTH];
128 s2[0] = Goldilocks::new(1);
129 permute(&mut s1);
130 permute(&mut s2);
131 assert_ne!(s1, s2);
132 }
133
134 #[test]
135 fn sponge_geometry() {
136 assert_eq!(WIDTH, RATE + CAPACITY);
137 assert_eq!(RATE_BYTES, 56);
138 assert_eq!(OUTPUT_BYTES, 64);
139 assert_eq!(OUTPUT_ELEMENTS, 8);
140 }
141}