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};
19use crate::keys::{
20    validate_key_u256,
21    validate_key_u512,
22};
23use crate::params::{
24    GoldPrfParams256,
25    GoldPrfParams512,
26};
27
28/// Secret key `k` for the Gold PRF (pilot: [`U256`] field).
29///
30/// Invariant: `k` is a reduced field element in `[1, p)` for the modulus `p` in
31/// [`GoldPrfParams256`]. Keys are only constructible via [`GoldKey256::from_uint`] or
32/// [`GoldKey256::derive_from_seed`]; use [`GoldKey256::as_uint`] for read-only access.
33/// Evaluation assumes this invariant and does not re-validate on each call.
34#[derive(Clone, Zeroize, ZeroizeOnDrop)]
35pub struct GoldKey256 {
36    k: U256,
37}
38
39/// Same invariant as [`GoldKey256`], with [`GoldPrfParams512`].
40/// Construct only via [`GoldKey512::from_uint`] / [`GoldKey512::derive_from_seed`];
41/// read the scalar with [`GoldKey512::as_uint`].
42#[derive(Clone, Zeroize, ZeroizeOnDrop)]
43pub struct GoldKey512 {
44    k: U512,
45}
46
47impl GoldKey256 {
48    pub fn from_uint(k: U256, params: &GoldPrfParams256) -> Result<Self, PrfError> {
49        validate_key_u256(&k, &params.p)?;
50        Ok(Self { k })
51    }
52
53    pub fn derive_from_seed(seed: &[u8], params: &GoldPrfParams256) -> Result<Self, PrfError> {
54        let k = crate::shake::shake256_to_field_u256(seed, b"lib-q-prf/gold-k256/v1", &params.p)?;
55        Self::from_uint(k, params)
56    }
57
58    /// Borrow the key as a reduced field element in `[1, p)` (see type invariant).
59    #[inline]
60    #[must_use]
61    pub fn as_uint(&self) -> &U256 {
62        &self.k
63    }
64}
65
66impl GoldKey512 {
67    pub fn from_uint(k: U512, params: &GoldPrfParams512) -> Result<Self, PrfError> {
68        validate_key_u512(&k, &params.p)?;
69        Ok(Self { k })
70    }
71
72    pub fn derive_from_seed(seed: &[u8], params: &GoldPrfParams512) -> Result<Self, PrfError> {
73        let k = crate::shake::shake256_to_field_u512(seed, b"lib-q-prf/gold-k512/v1", &params.p)?;
74        Self::from_uint(k, params)
75    }
76
77    /// Borrow the key as a reduced field element in `[1, p)` (see type invariant).
78    #[inline]
79    #[must_use]
80    pub fn as_uint(&self) -> &U512 {
81        &self.k
82    }
83}
84
85/// Evaluate Gold PRF; returns canonical residue in little-endian bytes.
86///
87/// Assumes `key` satisfies the [`GoldKey256`] invariant (validated in
88/// [`GoldKey256::from_uint`] / [`GoldKey256::derive_from_seed`]).
89pub fn gold_prf_u256(
90    key: &GoldKey256,
91    x: &U256,
92    params: &GoldPrfParams256,
93) -> Result<[u8; 32], PrfError> {
94    let nz = NonZero::new(params.p)
95        .into_option()
96        .ok_or(PrfError::InvalidParam)?;
97    let xr = x.rem_vartime(&nz);
98    let xm = to_monty(&xr, &params.monty);
99    let km = to_monty(&key.k, &params.monty);
100    let sum = fp_add(xm, &km);
101    let out_m = fp_pow(&sum, &params.g);
102    Ok(out_m.retrieve().to_le_bytes().into())
103}
104
105/// 512-bit field variant; returns canonical residue in little-endian bytes.
106///
107/// Assumes `key` satisfies the [`GoldKey512`] invariant (see [`GoldKey512::from_uint`]).
108pub fn gold_prf_u512(
109    key: &GoldKey512,
110    x: &U512,
111    params: &GoldPrfParams512,
112) -> Result<[u8; 64], PrfError> {
113    let nz = NonZero::new(params.p)
114        .into_option()
115        .ok_or(PrfError::InvalidParam)?;
116    let xr = x.rem_vartime(&nz);
117    let xm = to_monty(&xr, &params.monty);
118    let km = to_monty(&key.k, &params.monty);
119    let sum = fp_add(xm, &km);
120    let out_m = fp_pow(&sum, &params.g);
121    Ok(out_m.retrieve().to_le_bytes().into())
122}