use super::secp::{self, ProjectivePoint, Scalar};
use crate::frost::hashing::sha512_256i_tagged;
use purecrypto::rng::RngCore;
pub struct ZkProof {
pub alpha: ProjectivePoint,
pub t: Scalar,
}
impl ZkProof {
pub fn prove(
session: &[u8],
x: &Scalar,
x_pub: &ProjectivePoint,
rng: &mut impl RngCore,
) -> Self {
let a = secp::random_scalar(rng);
let alpha = secp::mul_base(&a);
let c = challenge(session, x_pub, &alpha);
let t = c.mul(x).add(&a);
ZkProof { alpha, t }
}
pub fn verify(&self, session: &[u8], x_pub: &ProjectivePoint) -> bool {
let c = challenge(session, x_pub, &self.alpha);
let tg = secp::mul_base(&self.t);
let axc = self.alpha.add(&x_pub.mul(&c));
secp::point_eq(&tg, &axc)
}
pub fn to_wire(&self) -> Option<([u8; 33], Vec<u8>)> {
Some((
secp::to_sec1_compressed(&self.alpha)?,
secp::scalar_to_be_min(&self.t),
))
}
pub fn from_wire(alpha_sec1: &[u8], t_be: &[u8]) -> Option<Self> {
let alpha = secp::from_sec1(alpha_sec1)?;
Some(ZkProof {
t: secp::scalar_from_be_reduce(t_be),
alpha,
})
}
}
fn challenge(session: &[u8], x_pub: &ProjectivePoint, alpha: &ProjectivePoint) -> Scalar {
let (xx, xy) = secp::affine_be(x_pub);
let (gx, gy) = secp::affine_be(&secp::generator());
let (ax, ay) = secp::affine_be(alpha);
let digest = sha512_256i_tagged(
session,
&[
xx.as_slice(),
xy.as_slice(),
gx.as_slice(),
gy.as_slice(),
ax.as_slice(),
ay.as_slice(),
],
);
Scalar::from_bytes_be_reduce(&digest)
}
#[cfg(test)]
mod tests {
use super::*;
use purecrypto::rng::OsRng;
#[test]
fn prove_then_verify() {
let x = secp::random_scalar(&mut OsRng);
let xp = secp::mul_base(&x);
let pf = ZkProof::prove(b"session", &x, &xp, &mut OsRng);
assert!(pf.verify(b"session", &xp));
assert!(!pf.verify(b"other", &xp));
let other = secp::mul_base(&secp::random_scalar(&mut OsRng));
assert!(!pf.verify(b"session", &other));
}
#[test]
fn wire_roundtrip() {
let x = secp::random_scalar(&mut OsRng);
let xp = secp::mul_base(&x);
let pf = ZkProof::prove(b"s", &x, &xp, &mut OsRng);
let (a, t) = pf.to_wire().unwrap();
assert!(ZkProof::from_wire(&a, &t).unwrap().verify(b"s", &xp));
}
}