use crate::{
hash::{HashInto, sha256::HashMatPolynomialRingZq},
signature::SignatureScheme,
};
use qfall_math::{
integer::{MatPolyOverZ, Z},
integer_mod_q::{MatPolynomialRingZq, Modulus},
rational::Q,
};
use qfall_tools::{
primitive::psf::{PSF, PSFGPVRing},
sample::g_trapdoor::gadget_parameters::GadgetParametersRing,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Serialize, Deserialize)]
pub struct FDHGPVRing {
pub psf: PSFGPVRing,
pub storage: HashMap<String, MatPolyOverZ>,
pub hash: HashMatPolynomialRingZq,
}
impl FDHGPVRing {
pub fn setup(n: impl Into<Z>, q: impl Into<Modulus>, s: impl Into<Q>) -> Self {
let (n, q, s) = (n.into(), q.into(), s.into());
let psf = PSFGPVRing {
gp: GadgetParametersRing::init_default(&n, &q),
s,
s_td: Q::from(1.005_f64),
};
let modulus = psf.gp.modulus.clone();
Self {
psf,
storage: HashMap::new(),
hash: HashMatPolynomialRingZq {
modulus,
rows: 1,
cols: 1,
},
}
}
}
impl SignatureScheme for FDHGPVRing {
type SecretKey = (MatPolyOverZ, MatPolyOverZ);
type PublicKey = MatPolynomialRingZq;
type Signature = MatPolyOverZ;
fn key_gen(&mut self) -> (Self::PublicKey, Self::SecretKey) {
self.psf.trap_gen()
}
fn sign(&mut self, m: String, sk: &Self::SecretKey, pk: &Self::PublicKey) -> Self::Signature {
if let Some(sigma) = self.storage.get(&m) {
return sigma.clone();
}
let u = (self.hash).hash(&m);
let signature = self.psf.samp_p(pk, sk, &u);
self.storage.insert(m, signature.clone());
signature
}
fn vfy(&self, m: String, sigma: &Self::Signature, pk: &Self::PublicKey) -> bool {
if !self.psf.check_domain(sigma) {
return false;
}
let u = (self.hash).hash(&m);
self.psf.f_a(pk, sigma) == u
}
}
#[cfg(test)]
mod test_fdh {
use crate::signature::{SignatureScheme, fdh::gpv_ring::FDHGPVRing};
use qfall_math::rational::Q;
const MODULUS: i64 = 512;
const N: i64 = 8;
fn compute_s() -> Q {
((2 * 2 * Q::from(1.005_f64) * Q::from(N).sqrt() + 1) * 2) * 4
}
#[test]
fn ensure_valid_signature_is_generated() {
let mut fdh = FDHGPVRing::setup(N, MODULUS, compute_s());
let (pk, sk) = fdh.key_gen();
for i in 0..10 {
let m = &format!("Hello World! {i}");
let sigma = fdh.sign(m.to_owned(), &sk, &pk);
assert!(
fdh.vfy(m.to_owned(), &sigma, &pk),
"This is a probabilistic test and may fail with negligible probability. \
As n is rather small here, try to rerun the test and check whether the \
test fails again."
)
}
}
#[test]
fn storage_filled() {
let mut fdh = FDHGPVRing::setup(N, MODULUS, compute_s());
let m = "Hello World!";
let (pk, sk) = fdh.key_gen();
let sign_1 = fdh.sign(m.to_owned(), &sk, &pk);
let sign_2 = fdh.sign(m.to_owned(), &sk, &pk);
assert!(fdh.storage.contains_key(m));
assert_eq!(sign_1, sign_2);
}
#[test]
fn reload_hashmap() {
let mut fdh = FDHGPVRing::setup(N, MODULUS, compute_s());
let m = "Hello World!";
let (pk, sk) = fdh.key_gen();
let _ = fdh.sign(m.to_owned(), &sk, &pk);
let fdh_string = serde_json::to_string(&fdh).expect("Unable to create a json object");
let fdh_2: FDHGPVRing = serde_json::from_str(&fdh_string).unwrap();
assert_eq!(fdh.storage, fdh_2.storage);
}
}