#![allow(dead_code)]
use super::bn::{self, Modulus};
use crate::frost::hashing::sha512_256i_tagged;
use purecrypto::bignum::BoxedUint;
use purecrypto::rng::RngCore;
pub(crate) struct ProofFac {
pub p: BoxedUint,
pub q: BoxedUint,
pub a: BoxedUint,
pub b: BoxedUint,
pub t: BoxedUint,
pub sigma: BoxedUint,
pub z1: BoxedUint,
pub z2: BoxedUint,
pub w1: BoxedUint,
pub w2: BoxedUint,
pub v: BoxedUint,
}
impl ProofFac {
pub(crate) fn to_parts(&self) -> Vec<Vec<u8>> {
[
&self.p,
&self.q,
&self.a,
&self.b,
&self.t,
&self.sigma,
&self.z1,
&self.z2,
&self.w1,
&self.w2,
&self.v,
]
.iter()
.map(|x| bn::to_be(x))
.collect()
}
pub(crate) fn from_parts(parts: &[Vec<u8>]) -> Option<ProofFac> {
if parts.len() != 11 {
return None;
}
let g = |i: usize| bn::from_be(&parts[i]);
Some(ProofFac {
p: g(0),
q: g(1),
a: g(2),
b: g(3),
t: g(4),
sigma: g(5),
z1: g(6),
z2: g(7),
w1: g(8),
w2: g(9),
v: g(10),
})
}
}
fn challenge(
session: &[u8],
q: &BoxedUint,
n0: &BoxedUint,
ncap: &BoxedUint,
s: &BoxedUint,
t: &BoxedUint,
pf: &ProofFac,
) -> BoxedUint {
let ops: Vec<Vec<u8>> = [n0, ncap, s, t, &pf.p, &pf.q, &pf.a, &pf.b, &pf.t, &pf.sigma]
.iter()
.map(|x| bn::to_be(x))
.collect();
let refs: Vec<&[u8]> = ops.iter().map(|v| v.as_slice()).collect();
let h = sha512_256i_tagged(session, &refs);
bn::rem(&bn::from_be(&h), q)
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn prove<R: RngCore>(
session: &[u8],
n0: &BoxedUint,
ncap: &BoxedUint,
s: &BoxedUint,
t: &BoxedUint,
n0p: &BoxedUint,
n0q: &BoxedUint,
rng: &mut R,
) -> ProofFac {
let q = bn::secp256k1_order();
let q3 = bn::mul(&bn::mul(&q, &q), &q);
let qncap = bn::mul(&q, ncap);
let qn0ncap = bn::mul(&qncap, n0);
let q3ncap = bn::mul(&q3, ncap);
let q3n0ncap = bn::mul(&q3ncap, n0);
let q3sqrtn0 = bn::mul(&q3, &bn::sqrt(n0));
let alpha = bn::rand_below(&q3sqrtn0, rng);
let beta = bn::rand_below(&q3sqrtn0, rng);
let mu = bn::rand_below(&qncap, rng);
let nu = bn::rand_below(&qncap, rng);
let sigma = bn::rand_below(&qn0ncap, rng);
let r = bn::rand_unit(&q3n0ncap, rng);
let x = bn::rand_below(&q3ncap, rng);
let y = bn::rand_below(&q3ncap, rng);
let m = Modulus::new(ncap);
let pp = m.mul(&m.pow(s, n0p), &m.pow(t, &mu));
let qq = m.mul(&m.pow(s, n0q), &m.pow(t, &nu));
let a = m.mul(&m.pow(s, &alpha), &m.pow(t, &x));
let b = m.mul(&m.pow(s, &beta), &m.pow(t, &y));
let tt = m.mul(&m.pow(&qq, &alpha), &m.pow(t, &r));
let mut pf = ProofFac {
p: pp,
q: qq,
a,
b,
t: tt,
sigma: sigma.clone(),
z1: bn::u64(0),
z2: bn::u64(0),
w1: bn::u64(0),
w2: bn::u64(0),
v: bn::u64(0),
};
let e = challenge(session, &q, n0, ncap, s, t, &pf);
pf.z1 = bn::add(&bn::mul(&e, n0p), &alpha);
pf.z2 = bn::add(&bn::mul(&e, n0q), &beta);
pf.w1 = bn::add(&bn::mul(&e, &mu), &x);
pf.w2 = bn::add(&bn::mul(&e, &nu), &y);
let a_val = bn::add(&r, &bn::mul(&e, &sigma));
let b_val = bn::mul(&bn::mul(&e, &nu), n0p);
pf.v = if bn::ge(&a_val, &b_val) {
bn::sub(&a_val, &b_val)
} else {
bn::sub(&b_val, &a_val)
};
pf
}
pub(crate) fn verify(
session: &[u8],
n0: &BoxedUint,
ncap: &BoxedUint,
s: &BoxedUint,
t: &BoxedUint,
pf: &ProofFac,
) -> bool {
if n0.is_zero() {
return false;
}
let q = bn::secp256k1_order();
let q3 = bn::mul(&bn::mul(&q, &q), &q);
let q3sqrtn0 = bn::mul(&q3, &bn::sqrt(n0));
if !pf.z1.lt(&q3sqrtn0) || !pf.z2.lt(&q3sqrtn0) {
return false;
}
let e = challenge(session, &q, n0, ncap, s, t, pf);
let m = Modulus::new(ncap);
let lhs1 = m.mul(&m.pow_pub(s, &pf.z1), &m.pow_pub(t, &pf.w1));
let rhs1 = m.mul(&pf.a, &m.pow_pub(&pf.p, &e));
if lhs1 != rhs1 {
return false;
}
let lhs2 = m.mul(&m.pow_pub(s, &pf.z2), &m.pow_pub(t, &pf.w2));
let rhs2 = m.mul(&pf.b, &m.pow_pub(&pf.q, &e));
if lhs2 != rhs2 {
return false;
}
let r = m.mul(&m.pow_pub(s, n0), &m.pow_pub(t, &pf.sigma));
let lhs3 = m.mul(&m.pow_pub(&pf.q, &pf.z1), &m.pow_pub(t, &pf.v));
let rhs3 = m.mul(&pf.t, &m.pow_pub(&r, &e));
lhs3 == rhs3
}
#[cfg(test)]
mod tests {
use super::super::testvec::{dec, fixtures};
use super::*;
#[test]
fn go_facproof_verifies() {
let f = fixtures();
let fp = &f["facproof"];
let session = fp["session"].as_str().unwrap().as_bytes();
let (n0, ncap, s, t) = (
dec(&fp["n0"]),
dec(&fp["ncap"]),
dec(&fp["s"]),
dec(&fp["t"]),
);
let pf = ProofFac {
p: dec(&fp["P"]),
q: dec(&fp["Q"]),
a: dec(&fp["A"]),
b: dec(&fp["B"]),
t: dec(&fp["T"]),
sigma: dec(&fp["Sigma"]),
z1: dec(&fp["Z1"]),
z2: dec(&fp["Z2"]),
w1: dec(&fp["W1"]),
w2: dec(&fp["W2"]),
v: dec(&fp["V"]),
};
assert!(
verify(session, &n0, &ncap, &s, &t, &pf),
"Go facproof must verify"
);
let mut bad = pf;
bad.z1 = bn::add(&bad.z1, &bn::one());
assert!(!verify(session, &n0, &ncap, &s, &t, &bad));
}
#[test]
fn rust_facproof_roundtrip() {
let f = fixtures();
let fp = &f["facproof"];
let pp = &f["paillier_proof"];
let (n0, n0p, n0q) = (dec(&pp["n"]), dec(&pp["p"]), dec(&pp["q"]));
let (ncap, s, t) = (dec(&fp["ncap"]), dec(&fp["s"]), dec(&fp["t"]));
let session = b"rust-fac-session";
let mut rng = purecrypto::rng::OsRng;
let proof = prove(session, &n0, &ncap, &s, &t, &n0p, &n0q, &mut rng);
assert!(verify(session, &n0, &ncap, &s, &t, &proof));
assert!(!verify(b"other", &n0, &ncap, &s, &t, &proof));
}
}