#![allow(dead_code)]
use super::Error;
use super::bn::{self, Modulus};
use crate::frost::hashing::sha512_256_parts;
use purecrypto::bignum::BoxedUint;
use purecrypto::rng::RngCore;
pub(crate) const PROOF_ITERS: usize = 13;
#[derive(Clone)]
pub struct PublicKey {
pub n: BoxedUint,
}
#[derive(Clone)]
pub struct PrivateKey {
pub pk: PublicKey,
pub lambda: BoxedUint, pub phi: BoxedUint, pub p: BoxedUint,
pub q: BoxedUint,
}
impl PublicKey {
pub(crate) fn gamma(&self) -> BoxedUint {
bn::add(&self.n, &bn::one())
}
pub(crate) fn nsquare(&self) -> BoxedUint {
bn::mul(&self.n, &self.n)
}
pub(crate) fn encrypt_with(&self, m: &BoxedUint, x: &BoxedUint) -> Result<BoxedUint, Error> {
if bn::ge(m, &self.n) {
return Err(Error::Validation("paillier: message >= N".into()));
}
let n2 = Modulus::new(&self.nsquare());
let gm = n2.pow(&self.gamma(), m);
let xn = n2.pow_pub(x, &self.n);
Ok(n2.mul(&gm, &xn))
}
pub(crate) fn encrypt<R: RngCore>(
&self,
m: &BoxedUint,
rng: &mut R,
) -> Result<(BoxedUint, BoxedUint), Error> {
let x = bn::rand_unit(&self.n, rng);
let c = self.encrypt_with(m, &x)?;
Ok((c, x))
}
pub(crate) fn homo_add(&self, c1: &BoxedUint, c2: &BoxedUint) -> BoxedUint {
Modulus::new(&self.nsquare()).mul(c1, c2)
}
pub(crate) fn homo_mult(&self, m: &BoxedUint, c1: &BoxedUint) -> BoxedUint {
Modulus::new(&self.nsquare()).pow(c1, m)
}
}
impl PrivateKey {
pub(crate) fn from_primes(p: BoxedUint, q: BoxedUint) -> PrivateKey {
let n = bn::mul(&p, &q);
let pm1 = bn::sub(&p, &bn::one());
let qm1 = bn::sub(&q, &bn::one());
let phi = bn::mul(&pm1, &qm1);
let g = bn::gcd(&pm1, &qm1);
let (lambda, _) = bn::divrem(&phi, &g);
PrivateKey {
pk: PublicKey { n },
lambda,
phi,
p,
q,
}
}
pub(crate) fn decrypt(&self, c: &BoxedUint) -> Result<BoxedUint, Error> {
let n2v = self.pk.nsquare();
if bn::ge(c, &n2v) {
return Err(Error::Validation("paillier: ciphertext >= N²".into()));
}
if !bn::is_one(&bn::gcd(c, &n2v)) {
return Err(Error::Validation("paillier: malformed ciphertext".into()));
}
let n2 = Modulus::new(&n2v);
let n = &self.pk.n;
let l = |u: &BoxedUint| -> BoxedUint {
let t = bn::sub(u, &bn::one());
bn::divrem(&t, n).0
};
let lc = l(&n2.pow_secret(c, &self.lambda));
let lg = l(&n2.pow_secret(&self.pk.gamma(), &self.lambda));
let modn = Modulus::new(n);
let inv = modn
.inv(&lg)
.ok_or_else(|| Error::Validation("paillier: Lg not invertible".into()))?;
Ok(modn.mul(&lc, &inv))
}
pub(crate) fn proof(
&self,
k: &BoxedUint,
ecdsa_x: &BoxedUint,
ecdsa_y: &BoxedUint,
) -> Result<[BoxedUint; PROOF_ITERS], Error> {
let xs = generate_xs(PROOF_ITERS, k, &self.pk.n, ecdsa_x, ecdsa_y);
let m = bn::mod_inv(&self.pk.n, &self.phi)
.ok_or_else(|| Error::Validation("paillier: N not invertible mod φ(N)".into()))?;
let modn = Modulus::new(&self.pk.n);
let pi: Vec<BoxedUint> = xs.iter().map(|x| modn.pow_secret(x, &m)).collect();
Ok(pi.try_into().map_err(|_| ()).expect("PROOF_ITERS items"))
}
}
pub(crate) fn verify_proof(
n: &BoxedUint,
k: &BoxedUint,
ecdsa_x: &BoxedUint,
ecdsa_y: &BoxedUint,
pi: &[BoxedUint],
) -> bool {
if pi.len() != PROOF_ITERS {
return false;
}
for &p in PRIMES_BELOW_1000 {
if bn::mod_small(n, p) == 0 {
return false;
}
}
let xs = generate_xs(PROOF_ITERS, k, n, ecdsa_x, ecdsa_y);
let modn = Modulus::new(n);
for (xi, yi) in xs.iter().zip(pi.iter()) {
if bn::rem(xi, n) != modn.pow_pub(yi, n) {
return false;
}
}
true
}
fn generate_xs(
m: usize,
k: &BoxedUint,
n: &BoxedUint,
sx: &BoxedUint,
sy: &BoxedUint,
) -> Vec<BoxedUint> {
let blocks = n.bit_len().div_ceil(256);
let (kb, sxb, syb, nb) = (bn::to_be(k), bn::to_be(sx), bn::to_be(sy), bn::to_be(n));
let mut ret = Vec::with_capacity(m);
let mut i = 0usize;
let mut nn = 0usize;
while i < m {
let ib = i.to_string().into_bytes();
let nbz = nn.to_string().into_bytes();
let mut xi_bytes = Vec::with_capacity(blocks * 32);
for j in 0..blocks {
let jb = j.to_string().into_bytes();
let h = sha512_256_parts(&[&ib, &jb, &nbz, &kb, &sxb, &syb, &nb]);
xi_bytes.extend_from_slice(&h);
}
let xi = bn::from_be(&xi_bytes);
if in_mult_group(n, &xi) {
ret.push(xi);
i += 1;
} else {
nn += 1;
}
}
ret
}
fn in_mult_group(n: &BoxedUint, v: &BoxedUint) -> bool {
!v.is_zero() && v.lt(n) && bn::is_one(&bn::gcd(v, n))
}
const PRIMES_BELOW_1000: &[u64] = &[
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97,
101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193,
197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307,
311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421,
431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547,
557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659,
661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797,
809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929,
937, 941, 947, 953, 967, 971, 977, 983, 991, 997,
];
#[cfg(test)]
mod tests {
use super::super::testvec::{dec, fixtures};
use super::*;
#[test]
fn small_key_encrypt_decrypt_homo() {
let f = fixtures();
let ps = &f["paillier_small"];
let p = dec(&ps["p"]);
let q = dec(&ps["q"]);
let sk = PrivateKey::from_primes(p, q);
assert_eq!(bn::to_be(&sk.pk.n), bn::to_be(&dec(&ps["n"])));
assert_eq!(bn::to_be(&sk.lambda), bn::to_be(&dec(&ps["lambda"])));
for e in ps["enc"].as_array().unwrap() {
let m = dec(&e["M"]);
let x = dec(&e["X"]);
let c = dec(&e["C"]);
assert_eq!(
bn::to_be(&sk.pk.encrypt_with(&m, &x).unwrap()),
bn::to_be(&c)
);
assert_eq!(bn::to_be(&sk.decrypt(&c).unwrap()), bn::to_be(&m));
}
let add_c = dec(&ps["homo_add"]["c"]);
assert_eq!(
bn::to_be(&sk.decrypt(&add_c).unwrap()),
bn::to_be(&dec(&ps["homo_add"]["m"]))
);
let mult_c = dec(&ps["homo_mult"]["c"]);
assert_eq!(
bn::to_be(&sk.decrypt(&mult_c).unwrap()),
bn::to_be(&dec(&ps["homo_mult"]["m"]))
);
}
#[test]
fn proof_fixture_verifies() {
let f = fixtures();
let pp = &f["paillier_proof"];
let n = dec(&pp["n"]);
let k = dec(&pp["k"]);
let sx = dec(&pp["ecdsa_x"]);
let sy = dec(&pp["ecdsa_y"]);
let pi: Vec<BoxedUint> = pp["pi"].as_array().unwrap().iter().map(dec).collect();
assert!(verify_proof(&n, &k, &sx, &sy, &pi), "Go proof must verify");
let sk = PrivateKey::from_primes(dec(&pp["p"]), dec(&pp["q"]));
let rust_pi = sk.proof(&k, &sx, &sy).unwrap();
assert!(verify_proof(&n, &k, &sx, &sy, &rust_pi));
let mut bad = pi.clone();
bad[0] = bn::add(&bad[0], &bn::one());
assert!(!verify_proof(&n, &k, &sx, &sy, &bad));
}
#[test]
fn roundtrip_fresh_key() {
let mut rng = purecrypto::rng::OsRng;
let p = bn::generate_safe_prime(128, &mut rng);
let q = bn::generate_safe_prime(128, &mut rng);
let sk = PrivateKey::from_primes(p, q);
let m = bn::u64(123456);
let (c, _x) = sk.pk.encrypt(&m, &mut rng).unwrap();
assert_eq!(bn::to_be(&sk.decrypt(&c).unwrap()), bn::to_be(&m));
}
}