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}