Skip to main content

lib_q_prf/
gold.rs

1//! Gold (power-residue) PRF: \(\mathrm{Gold}_k(x) = (k+x)^g \bmod p\).
2
3use crypto_bigint::{
4    NonZero,
5    U256,
6    U512,
7};
8use zeroize::{
9    Zeroize,
10    ZeroizeOnDrop,
11};
12
13use crate::error::PrfError;
14use crate::field::{
15    fp_add,
16    fp_pow,
17    to_monty,
18    uint_ct_eq_zero,
19};
20use crate::keys::{
21    validate_key_u256,
22    validate_key_u512,
23};
24use crate::params::{
25    GoldPrfParams256,
26    GoldPrfParams512,
27};
28
29/// Secret key `k` for the Gold PRF (pilot: [`U256`] field).
30///
31/// Invariant: `k` is a reduced field element in `[1, p)` for the modulus `p` in
32/// [`GoldPrfParams256`]. Keys are only constructible via [`GoldKey256::from_uint`] or
33/// [`GoldKey256::derive_from_seed`]; use [`GoldKey256::as_uint`] for read-only access.
34/// Evaluation assumes this invariant and does not re-validate on each call.
35#[derive(Clone, Zeroize, ZeroizeOnDrop)]
36pub struct GoldKey256 {
37    k: U256,
38}
39
40/// Same invariant as [`GoldKey256`], with [`GoldPrfParams512`].
41/// Construct only via [`GoldKey512::from_uint`] / [`GoldKey512::derive_from_seed`];
42/// read the scalar with [`GoldKey512::as_uint`].
43#[derive(Clone, Zeroize, ZeroizeOnDrop)]
44pub struct GoldKey512 {
45    k: U512,
46}
47
48impl GoldKey256 {
49    pub fn from_uint(k: U256, params: &GoldPrfParams256) -> Result<Self, PrfError> {
50        validate_key_u256(&k, &params.p)?;
51        Ok(Self { k })
52    }
53
54    pub fn derive_from_seed(seed: &[u8], params: &GoldPrfParams256) -> Result<Self, PrfError> {
55        let k = crate::shake::shake256_to_field_u256(seed, b"lib-q-prf/gold-k256/v1", &params.p)?;
56        Self::from_uint(k, params)
57    }
58
59    /// Borrow the key as a reduced field element in `[1, p)` (see type invariant).
60    #[inline]
61    #[must_use]
62    pub fn as_uint(&self) -> &U256 {
63        &self.k
64    }
65}
66
67impl GoldKey512 {
68    pub fn from_uint(k: U512, params: &GoldPrfParams512) -> Result<Self, PrfError> {
69        validate_key_u512(&k, &params.p)?;
70        Ok(Self { k })
71    }
72
73    pub fn derive_from_seed(seed: &[u8], params: &GoldPrfParams512) -> Result<Self, PrfError> {
74        let k = crate::shake::shake256_to_field_u512(seed, b"lib-q-prf/gold-k512/v1", &params.p)?;
75        Self::from_uint(k, params)
76    }
77
78    /// Borrow the key as a reduced field element in `[1, p)` (see type invariant).
79    #[inline]
80    #[must_use]
81    pub fn as_uint(&self) -> &U512 {
82        &self.k
83    }
84}
85
86/// Evaluate Gold PRF; returns canonical residue in little-endian bytes.
87///
88/// Assumes `key` satisfies the [`GoldKey256`] invariant (validated in
89/// [`GoldKey256::from_uint`] / [`GoldKey256::derive_from_seed`]).
90pub fn gold_prf_u256(
91    key: &GoldKey256,
92    x: &U256,
93    params: &GoldPrfParams256,
94) -> Result<[u8; 32], PrfError> {
95    let nz = NonZero::new(params.p)
96        .into_option()
97        .ok_or(PrfError::InvalidParam)?;
98    let xr = x.rem_vartime(&nz);
99    let xm = to_monty(&xr, &params.monty);
100    let km = to_monty(&key.k, &params.monty);
101    let sum = fp_add(xm, &km);
102    // Reject (k + x) ≡ 0 (mod p): raising zero to any power is 0, creating a
103    // degenerate key-relation oracle.  Mirror the identical check in legendre_prf_u256.
104    if bool::from(uint_ct_eq_zero(&sum.retrieve())) {
105        return Err(PrfError::ZeroInput);
106    }
107    let out_m = fp_pow(&sum, &params.g);
108    Ok(out_m.retrieve().to_le_bytes().into())
109}
110
111/// 512-bit field variant; returns canonical residue in little-endian bytes.
112///
113/// Assumes `key` satisfies the [`GoldKey512`] invariant (see [`GoldKey512::from_uint`]).
114pub fn gold_prf_u512(
115    key: &GoldKey512,
116    x: &U512,
117    params: &GoldPrfParams512,
118) -> Result<[u8; 64], PrfError> {
119    let nz = NonZero::new(params.p)
120        .into_option()
121        .ok_or(PrfError::InvalidParam)?;
122    let xr = x.rem_vartime(&nz);
123    let xm = to_monty(&xr, &params.monty);
124    let km = to_monty(&key.k, &params.monty);
125    let sum = fp_add(xm, &km);
126    // Reject (k + x) ≡ 0 (mod p): mirror legendre_prf_u512.
127    if bool::from(uint_ct_eq_zero(&sum.retrieve())) {
128        return Err(PrfError::ZeroInput);
129    }
130    let out_m = fp_pow(&sum, &params.g);
131    Ok(out_m.retrieve().to_le_bytes().into())
132}