use crypto_bigint::{
modular::{MontyForm, MontyParams},
rand_core::RngCore,
Concat, Int, InvMod, NonZero, RandomMod, Split, Uint, Zero,
};
use ziglet_primality::miller_rabin;
pub struct SecretKey<const LIMBS_SINGLE: usize> {
pub p: Uint<LIMBS_SINGLE>,
pub q: Uint<LIMBS_SINGLE>,
}
pub struct PublicKey<const LIMBS_DOUBLE: usize> {
pub n: Uint<LIMBS_DOUBLE>,
}
pub struct KeyPair<const LIMBS_SINGLE: usize, const LIMBS_DOUBLE: usize, const LIMBS_QUAD: usize> {
pub public_key: PublicKey<LIMBS_DOUBLE>,
pub secret_key: SecretKey<LIMBS_SINGLE>,
}
pub struct Paillier<
'a,
const LIMBS_SINGLE: usize,
const LIMBS_DOUBLE: usize,
const LIMBS_QUAD: usize,
const LIMBS_OCT: usize,
R,
> where
R: RngCore,
{
key_pair: &'a KeyPair<LIMBS_SINGLE, LIMBS_DOUBLE, LIMBS_QUAD>,
rng: &'a mut R,
n: Uint<LIMBS_QUAD>,
nsquared: Uint<LIMBS_QUAD>,
totient: Uint<LIMBS_QUAD>,
}
impl<
'a,
const LIMBS_SINGLE: usize,
const LIMBS_DOUBLE: usize,
const LIMBS_QUAD: usize,
const LIMBS_OCT: usize,
R,
> Paillier<'a, LIMBS_SINGLE, LIMBS_DOUBLE, LIMBS_QUAD, LIMBS_OCT, R>
where
Uint<LIMBS_SINGLE>: Concat<Output = Uint<LIMBS_DOUBLE>> + InvMod<Output = Uint<LIMBS_SINGLE>>,
Uint<LIMBS_DOUBLE>: Concat<Output = Uint<LIMBS_QUAD>> + InvMod<Output = Uint<LIMBS_DOUBLE>>,
Uint<LIMBS_QUAD>: Concat<Output = Uint<LIMBS_OCT>> + InvMod<Output = Uint<LIMBS_QUAD>>,
Uint<LIMBS_OCT>: Split<Output = Uint<LIMBS_QUAD>>,
Uint<LIMBS_QUAD>: Split<Output = Uint<LIMBS_DOUBLE>>,
Uint<LIMBS_DOUBLE>: Split<Output = Uint<LIMBS_SINGLE>>,
R: RngCore,
{
pub fn new(
key_pair: &'a KeyPair<LIMBS_SINGLE, LIMBS_DOUBLE, LIMBS_QUAD>,
rng: &'a mut R,
) -> Self {
let p = key_pair
.secret_key
.p
.concat(&Uint::<LIMBS_SINGLE>::ZERO)
.concat(&Uint::<LIMBS_DOUBLE>::ZERO);
let q = key_pair
.secret_key
.q
.concat(&Uint::<LIMBS_SINGLE>::ZERO)
.concat(&Uint::<LIMBS_DOUBLE>::ZERO);
let totient = (p - Uint::ONE) * (q - Uint::ONE);
let n = key_pair.public_key.n.concat(&Uint::<LIMBS_DOUBLE>::ZERO);
let n_squared = n.wrapping_square();
Self {
key_pair,
rng,
n,
nsquared: n_squared,
totient,
}
}
pub fn encrypt(&mut self, message: &Uint<LIMBS_DOUBLE>) -> Uint<LIMBS_QUAD> {
let r = Self::random_znsquared(
self.key_pair.secret_key.p,
self.key_pair.secret_key.q,
self.n,
self.nsquared,
&mut self.rng,
);
let r = MontyForm::new(&r, MontyParams::new(self.nsquared.to_odd().unwrap()));
#[allow(non_snake_case)]
let N = MontyForm::new(&self.n, MontyParams::new(self.nsquared.to_odd().unwrap()));
let one = MontyForm::new(
&Uint::<LIMBS_QUAD>::ONE,
MontyParams::new(self.nsquared.to_odd().unwrap()),
);
let ciphertext = (one + N).pow(message) * r.pow(&self.n);
let c = ciphertext.retrieve();
c
}
pub fn decrypt(&self, ciphertext: &Uint<LIMBS_QUAD>) -> Uint<LIMBS_DOUBLE> {
let c = MontyForm::new(
&ciphertext,
MontyParams::new(self.nsquared.to_odd().unwrap()),
);
let c_prime = c.pow(&self.totient);
let m_temp = (c_prime.retrieve() - Uint::ONE) / self.n;
let m_prime = MontyForm::new(&m_temp, MontyParams::new(self.n.to_odd().unwrap()));
let totient_inv = MontyForm::new(
&(self.totient.inv_mod(&self.n).unwrap()),
MontyParams::new(self.n.to_odd().unwrap()),
);
let m = m_prime * totient_inv;
m.retrieve().split().0
}
pub fn scalar_gcd<const LIMBS: usize>(
a: Int<LIMBS>,
b: Int<LIMBS>,
) -> (Int<LIMBS>, Int<LIMBS>, Int<LIMBS>) {
assert!(a.is_zero().unwrap_u8() == 0u8);
assert!(b.is_zero().unwrap_u8() == 0u8);
let mut q: Int<LIMBS>;
let mut r_minus_1 = if a > b { a } else { b };
let mut r = if a > b { b } else { a };
let mut s_minus_1 = Int::<LIMBS>::ONE;
let mut s = Int::<LIMBS>::ZERO;
let mut t_minus_1 = Int::<LIMBS>::ZERO;
let mut t = Int::<LIMBS>::ONE;
loop {
let (qx, _rx) = r_minus_1.checked_div_rem(&r.to_nz().unwrap());
q = qx.expect("division error");
(r_minus_1, r) = (r, r_minus_1 - q * r);
(s_minus_1, s) = (s, s_minus_1 - q * s);
(t_minus_1, t) = (t, t_minus_1 - q * t);
if r.is_zero().unwrap_u8() == 1u8 {
break;
}
}
(q, s, t)
}
pub fn random_znsquared(
p: Uint<LIMBS_SINGLE>,
q: Uint<LIMBS_SINGLE>,
n: Uint<LIMBS_QUAD>,
nsquared: Uint<LIMBS_QUAD>,
mut rng: &mut R,
) -> Uint<LIMBS_QUAD> {
let a = Uint::<LIMBS_QUAD>::random_mod(&mut rng, &n.to_nz().unwrap());
let b = Self::random_zn(p, q, &mut rng);
#[allow(non_snake_case)]
let B = MontyForm::new(&b, MontyParams::new(nsquared.to_odd().unwrap()));
let one = MontyForm::new(&Uint::ONE, MontyParams::new(nsquared.to_odd().unwrap()));
#[allow(non_snake_case)]
let N = MontyForm::new(&n, MontyParams::new(nsquared.to_odd().unwrap()));
let x = (N + one).pow(&a) * B.pow(&n);
x.retrieve()
}
pub fn scalar_lcm<const LIMBS: usize>(a: Int<LIMBS>, b: Int<LIMBS>) -> Uint<LIMBS> {
let (gcd, _, _) = Self::scalar_gcd(a, b);
let lcm = (a * b).checked_div(&gcd).expect("division error").abs();
lcm
}
pub fn lambda<const LIMBS: usize>(p: Uint<LIMBS>, q: Uint<LIMBS>) -> Uint<LIMBS> {
let p = p.as_int();
let q = q.as_int();
let p_minus_1 = p - Int::ONE;
let q_minus_1 = q - Int::ONE;
Self::scalar_lcm::<LIMBS>(p_minus_1, q_minus_1)
}
pub fn generate_probable_prime(t: u32, mut rng: &mut R) -> Uint<LIMBS_SINGLE> {
miller_rabin::generate_probable_prime(Uint::<LIMBS_SINGLE>::BITS, t, &mut rng)
}
pub fn random_zn(
p: Uint<LIMBS_SINGLE>,
q: Uint<LIMBS_SINGLE>,
mut rng: &mut R,
) -> Uint<LIMBS_QUAD> {
let mut random_mod_p: Uint<LIMBS_SINGLE>;
let mut random_mod_q: Uint<LIMBS_SINGLE>;
loop {
random_mod_p = Uint::<LIMBS_SINGLE>::random_mod(&mut rng, &NonZero::new(p).unwrap());
if random_mod_p != q {
break;
}
}
loop {
random_mod_q = Uint::<LIMBS_SINGLE>::random_mod(&mut rng, &NonZero::new(q).unwrap());
if random_mod_q != p {
break;
}
}
let x = random_mod_p.widening_mul::<LIMBS_SINGLE, LIMBS_DOUBLE>(&random_mod_q);
x.concat::<LIMBS_QUAD>(&Uint::<LIMBS_DOUBLE>::ZERO)
}
pub fn generate_key(
t: u32,
mut rng: &mut R,
) -> KeyPair<LIMBS_SINGLE, LIMBS_DOUBLE, LIMBS_QUAD> {
let mut p: Uint<LIMBS_SINGLE>;
let mut q: Uint<LIMBS_SINGLE>;
loop {
p = Self::generate_probable_prime(t, &mut rng);
q = Self::generate_probable_prime(t, &mut rng);
let (_q1, r1) = p.div_rem(&q.to_nz().unwrap());
let (_q2, r2) = q.div_rem(&p.to_nz().unwrap());
if r1.is_zero().unwrap_u8() == 0u8 && r2.is_zero().unwrap_u8() == 0u8 {
break;
}
}
let n = p.concat::<LIMBS_DOUBLE>(&Uint::ZERO) * q.concat::<LIMBS_DOUBLE>(&Uint::ZERO);
KeyPair {
secret_key: SecretKey { p, q },
public_key: PublicKey { n },
}
}
}
#[cfg(test)]
mod tests;