use crypto_bigint::{
NonZero,
U256,
U512,
};
use zeroize::{
Zeroize,
ZeroizeOnDrop,
};
use crate::error::PrfError;
use crate::field::{
fp_add,
fp_pow,
to_monty,
};
use crate::keys::{
validate_key_u256,
validate_key_u512,
};
use crate::params::{
GoldPrfParams256,
GoldPrfParams512,
};
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
pub struct GoldKey256 {
k: U256,
}
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
pub struct GoldKey512 {
k: U512,
}
impl GoldKey256 {
pub fn from_uint(k: U256, params: &GoldPrfParams256) -> Result<Self, PrfError> {
validate_key_u256(&k, ¶ms.p)?;
Ok(Self { k })
}
pub fn derive_from_seed(seed: &[u8], params: &GoldPrfParams256) -> Result<Self, PrfError> {
let k = crate::shake::shake256_to_field_u256(seed, b"lib-q-prf/gold-k256/v1", ¶ms.p)?;
Self::from_uint(k, params)
}
#[inline]
#[must_use]
pub fn as_uint(&self) -> &U256 {
&self.k
}
}
impl GoldKey512 {
pub fn from_uint(k: U512, params: &GoldPrfParams512) -> Result<Self, PrfError> {
validate_key_u512(&k, ¶ms.p)?;
Ok(Self { k })
}
pub fn derive_from_seed(seed: &[u8], params: &GoldPrfParams512) -> Result<Self, PrfError> {
let k = crate::shake::shake256_to_field_u512(seed, b"lib-q-prf/gold-k512/v1", ¶ms.p)?;
Self::from_uint(k, params)
}
#[inline]
#[must_use]
pub fn as_uint(&self) -> &U512 {
&self.k
}
}
pub fn gold_prf_u256(
key: &GoldKey256,
x: &U256,
params: &GoldPrfParams256,
) -> Result<[u8; 32], PrfError> {
let nz = NonZero::new(params.p)
.into_option()
.ok_or(PrfError::InvalidParam)?;
let xr = x.rem_vartime(&nz);
let xm = to_monty(&xr, ¶ms.monty);
let km = to_monty(&key.k, ¶ms.monty);
let sum = fp_add(xm, &km);
let out_m = fp_pow(&sum, ¶ms.g);
Ok(out_m.retrieve().to_le_bytes().into())
}
pub fn gold_prf_u512(
key: &GoldKey512,
x: &U512,
params: &GoldPrfParams512,
) -> Result<[u8; 64], PrfError> {
let nz = NonZero::new(params.p)
.into_option()
.ok_or(PrfError::InvalidParam)?;
let xr = x.rem_vartime(&nz);
let xm = to_monty(&xr, ¶ms.monty);
let km = to_monty(&key.k, ¶ms.monty);
let sum = fp_add(xm, &km);
let out_m = fp_pow(&sum, ¶ms.g);
Ok(out_m.retrieve().to_le_bytes().into())
}