Skip to main content

lib_q_prf/
params.rs

1//! Hard-coded safe primes and Gold exponents for pilot parameter sets.
2//!
3//! # Provenance (256-bit set)
4//!
5//! - **Generation:** `openssl prime -generate -safe -bits 255` (OpenSSL 3.x), decimal output
6//!   `50427571419144900628919267453863926315743253667527897108380512375229288045819`.
7//! - **Independent check:** SymPy `isprime(p)` and `isprime((p-1)//2)` both `True` (Miller-Rabin
8//!   internally; deterministic for this size class).
9//! - **Bit length:** 255 bits (`p < 2^256`, fits [`crypto_bigint::U256`]).
10//! - **Encoding below:** big-endian hex (same integer as OpenSSL decimal).
11//!
12//! # Provenance (512-bit set)
13//!
14//! - **Generation:** `openssl prime -generate -safe -bits 511`, decimal
15//!   `5846462199204458696044836418989331885058164550456003028279732171283212220247560926277230464259799968087668834545163644537944481399188821346081377725974863`.
16//! - **Independent check:** SymPy `isprime(p)` and `isprime((p-1)//2)` both `True`.
17//! - **Bit length:** 511 bits (fits [`crypto_bigint::U512`]).
18//!
19//! # Gold exponent
20//!
21//! For each safe prime `p = 2q + 1` with `q` prime, `p - 1 = 2^1 ยท q`. The pilot Gold exponent is
22//! `g = q = (p-1)/2`, an odd divisor of `p-1` suitable for the power-residue PRF construction.
23
24use crypto_bigint::modular::MontyParams;
25use crypto_bigint::{
26    NonZero,
27    Odd,
28    U256,
29    U512,
30};
31
32/// Parameters for the Legendre PRF over \(\mathbb{F}_p\) with `p` a safe prime.
33#[derive(Clone, Copy, Debug, Eq, PartialEq)]
34pub struct LegendrePrfParams256 {
35    /// Safe prime modulus (`p > 0`).
36    pub p: NonZero<U256>,
37    /// Montgomery parameters for `p`.
38    pub monty: MontyParams<U256>,
39}
40
41/// Parameters for the Legendre PRF at the 512-bit pilot modulus.
42#[derive(Clone, Copy, Debug, Eq, PartialEq)]
43pub struct LegendrePrfParams512 {
44    /// Safe prime modulus.
45    pub p: U512,
46    /// Montgomery parameters for `p`.
47    pub monty: MontyParams<U512>,
48}
49
50/// Parameters for the Gold (power-residue) PRF: odd divisor `g` of `p-1` and modulus `p`.
51#[derive(Clone, Copy, Debug, Eq, PartialEq)]
52pub struct GoldPrfParams256 {
53    /// Prime modulus.
54    pub p: U256,
55    /// Montgomery parameters for `p`.
56    pub monty: MontyParams<U256>,
57    /// Gold exponent `g | (p-1)`.
58    pub g: U256,
59}
60
61/// Gold PRF parameters at 512-bit pilot modulus.
62#[derive(Clone, Copy, Debug, Eq, PartialEq)]
63pub struct GoldPrfParams512 {
64    /// Prime modulus.
65    pub p: U512,
66    /// Montgomery parameters for `p`.
67    pub monty: MontyParams<U512>,
68    /// Gold exponent `g | (p-1)`.
69    pub g: U512,
70}
71
72/// 256-bit pilot: `p` (255-bit safe prime), big-endian hex.
73pub const P256_BE_HEX: &str = "6f7cfe74b8a1892ed54ec11ae8141a65dad3440973464111361ce7de4a5c5cfb";
74
75/// 512-bit pilot: `p` (511-bit safe prime), big-endian hex.
76pub const P512_BE_HEX: &str = "6fa0e975b4660858abfccfb1a2f3b5f8cda4239a89afa1840e62d758ae53a94059ab27f1f7833146306bf0d1c2647d9ca136b85e4c24dbdf0a4c8ef916f0094f";
77
78#[inline]
79fn monty_params_u256(p: U256) -> MontyParams<U256> {
80    let odd = Odd::new(p).into_option().expect("pilot modulus is odd");
81    MontyParams::new_vartime(odd)
82}
83
84#[inline]
85fn monty_params_u512(p: U512) -> MontyParams<U512> {
86    let odd = Odd::new(p).into_option().expect("pilot modulus is odd");
87    MontyParams::new_vartime(odd)
88}
89
90impl LegendrePrfParams256 {
91    /// Pilot modulus: 255-bit safe prime (`p = 2q+1`, `q` prime).
92    #[must_use]
93    pub fn pilot() -> Self {
94        let p_uint = U256::from_be_hex(P256_BE_HEX);
95        let p = NonZero::new(p_uint).expect("pilot modulus is non-zero");
96        let monty = monty_params_u256(p.get());
97        Self { p, monty }
98    }
99
100    /// Sophie Germain cofactor `q = (p-1)/2`.
101    #[must_use]
102    pub fn sophie_germain_cofactor(&self) -> U256 {
103        self.p.get().wrapping_sub(&U256::ONE).shr(1)
104    }
105}
106
107impl LegendrePrfParams512 {
108    /// Pilot modulus: 511-bit safe prime.
109    #[must_use]
110    pub fn pilot() -> Self {
111        let p = U512::from_be_hex(P512_BE_HEX);
112        let monty = monty_params_u512(p);
113        Self { p, monty }
114    }
115
116    /// `q = (p-1)/2`.
117    #[must_use]
118    pub fn sophie_germain_cofactor(&self) -> U512 {
119        self.p.wrapping_sub(&U512::ONE).shr(1)
120    }
121}
122
123impl GoldPrfParams256 {
124    /// Pilot Gold PRF: `g = (p-1)/2` for the 256-bit safe prime field.
125    #[must_use]
126    pub fn pilot() -> Self {
127        let leg = LegendrePrfParams256::pilot();
128        let g = leg.sophie_germain_cofactor();
129        GoldPrfParams256 {
130            p: leg.p.get(),
131            monty: leg.monty,
132            g,
133        }
134    }
135}
136
137impl GoldPrfParams512 {
138    /// Pilot Gold PRF at the 512-bit modulus.
139    #[must_use]
140    pub fn pilot() -> Self {
141        let leg = LegendrePrfParams512::pilot();
142        let g = leg.sophie_germain_cofactor();
143        GoldPrfParams512 {
144            p: leg.p,
145            monty: leg.monty,
146            g,
147        }
148    }
149}
150
151/// Encode a field element as fixed little-endian bytes (for wire formats / digests).
152#[must_use]
153pub fn u256_to_le_bytes(x: &U256) -> [u8; 32] {
154    x.to_le_bytes().into()
155}
156
157/// Encode a `U512` as little-endian bytes.
158#[must_use]
159pub fn u512_to_le_bytes(x: &U512) -> [u8; 64] {
160    x.to_le_bytes().into()
161}
162
163/// Parse a little-endian field element; must be `< p` for valid keys.
164#[must_use]
165pub fn u256_from_le_bytes(bytes: &[u8; 32]) -> U256 {
166    U256::from_le_slice(bytes.as_slice())
167}
168
169/// Parse `U512` from little-endian bytes.
170#[must_use]
171pub fn u512_from_le_bytes(bytes: &[u8; 64]) -> U512 {
172    U512::from_le_slice(bytes.as_slice())
173}