Skip to main content

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}