use crate::{mod_in, DecryptionKey, EncryptionKey};
use digest::{
generic_array::{typenum::Unsigned, GenericArray},
Digest,
};
use serde::{Deserialize, Serialize};
use unknown_order::BigNumber;
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct ProofSquareFree(Vec<BigNumber>);
const L: usize = 13;
#[cfg(feature = "wasm")]
wasm_slice_impl!(ProofSquareFree);
impl ProofSquareFree {
pub fn generate<D: Digest>(sk: &DecryptionKey, nonce: &[u8]) -> Option<Self> {
sk.pk.n.invert(&sk.totient).map(|m| {
let mut proof = generate_challenges::<D>(&sk.pk, nonce);
debug_assert_eq!(proof.len(), L);
for x in proof.as_mut_slice() {
*x = x.modpow(&m, &sk.pk.n);
}
ProofSquareFree(proof)
})
}
pub fn verify<D: Digest>(&self, pk: &EncryptionKey, nonce: &[u8]) -> bool {
let proof = generate_challenges::<D>(pk, nonce);
debug_assert_eq!(proof.len(), L);
debug_assert_eq!(proof.len(), self.0.len());
proof
.iter()
.zip(self.0.iter())
.all(|(a, b)| a == &b.modpow(&pk.n, &pk.n))
}
pub fn to_bytes(&self) -> Vec<u8> {
serde_bare::to_vec(&self.0).unwrap()
}
pub fn from_bytes<B: AsRef<[u8]>>(data: B) -> Result<Self, String> {
let data = data.as_ref();
let messages = serde_bare::from_slice::<Vec<BigNumber>>(data).map_err(|e| e.to_string())?;
Ok(Self(messages))
}
}
#[allow(clippy::many_single_char_names)]
fn generate_challenges<D: Digest>(pk: &EncryptionKey, nonce: &[u8]) -> Vec<BigNumber> {
let b = pk.n.to_bytes().len();
assert!(b >= 256);
let h = D::OutputSize::to_usize();
let s: usize = (b + h - 1) / h;
let mut j = 0;
let mut m = 0usize;
let mut x = Vec::with_capacity(L);
while j < L {
let mut e = Vec::with_capacity(s * h);
for k in 0..s {
e.extend_from_slice(
hash_pieces::<D>(&[nonce, &j.to_be_bytes(), &k.to_be_bytes(), &m.to_be_bytes()])
.as_slice(),
);
}
let xj = BigNumber::from_slice(&e[..b]);
if mod_in(&xj, &pk.n) {
x.push(xj);
j += 1;
m = 0
} else {
m += 1
}
}
x
}
fn hash_pieces<D: Digest>(data: &[&[u8]]) -> GenericArray<u8, D::OutputSize> {
let mut hasher = D::new();
data.iter().map(|datum| D::digest(&datum)).for_each(|d| {
hasher.update(d.as_slice());
});
hasher.finalize()
}