use core::fmt;
use crate::public_key::bigint::{BigUint, MontgomeryCtx};
use crate::public_key::io::{
decode_biguints, encode_biguints, pem_unwrap, pem_wrap, xml_unwrap, xml_wrap,
};
use crate::public_key::primes::{
gcd, is_probable_prime, lcm, mod_inverse, mod_pow, random_coprime_below, random_probable_prime,
};
use crate::Csprng;
const PAILLIER_PUBLIC_LABEL: &str = "CRYPTOGRAPHY PAILLIER PUBLIC KEY";
const PAILLIER_PRIVATE_LABEL: &str = "CRYPTOGRAPHY PAILLIER PRIVATE KEY";
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PaillierPublicKey {
n: BigUint,
n_squared: BigUint,
zeta: BigUint,
zeta_mont: Option<BigUint>,
n_squared_ctx: Option<MontgomeryCtx>,
}
#[derive(Clone, Eq, PartialEq)]
pub struct PaillierPrivateKey {
n: BigUint,
n_squared: BigUint,
lambda: BigUint,
u: BigUint,
n_squared_ctx: Option<MontgomeryCtx>,
n_ctx: Option<MontgomeryCtx>,
}
pub struct Paillier;
impl PaillierPublicKey {
#[must_use]
pub fn modulus(&self) -> &BigUint {
&self.n
}
#[must_use]
pub fn generator(&self) -> &BigUint {
&self.zeta
}
#[must_use]
pub fn max_plaintext_exclusive(&self) -> &BigUint {
&self.n
}
#[must_use]
pub fn encrypt_with_nonce(&self, message: &BigUint, nonce: &BigUint) -> Option<BigUint> {
if message >= &self.n {
return None;
}
if nonce.is_zero() || nonce >= &self.n || gcd(nonce, &self.n) != BigUint::one() {
return None;
}
let left = if let (Some(ctx), Some(zeta_mont)) = (&self.n_squared_ctx, &self.zeta_mont) {
ctx.pow_encoded(zeta_mont, message)
} else {
mod_pow(&self.zeta, message, &self.n_squared)
};
let right = if let Some(ctx) = &self.n_squared_ctx {
ctx.pow(nonce, &self.n)
} else {
mod_pow(nonce, &self.n, &self.n_squared)
};
let product = if let Some(ctx) = &self.n_squared_ctx {
ctx.mul(&left, &right)
} else {
BigUint::mod_mul(&left, &right, &self.n_squared)
};
Some(product)
}
#[must_use]
pub fn encrypt<R: Csprng>(&self, message: &[u8], rng: &mut R) -> Option<BigUint> {
let message_int = BigUint::from_be_bytes(message);
let nonce = random_coprime_below(rng, &self.n, &self.n)?;
self.encrypt_with_nonce(&message_int, &nonce)
}
#[must_use]
pub fn encrypt_bytes<R: Csprng>(&self, message: &[u8], rng: &mut R) -> Option<Vec<u8>> {
let ciphertext = self.encrypt(message, rng)?;
Some(encode_biguints(&[&ciphertext]))
}
#[must_use]
pub fn rerandomize<R: Csprng>(&self, ciphertext: &BigUint, rng: &mut R) -> Option<BigUint> {
let nonce = random_coprime_below(rng, &self.n, &self.n)?;
if ciphertext >= &self.n_squared {
return None;
}
let factor = if let Some(ctx) = &self.n_squared_ctx {
ctx.pow(&nonce, &self.n)
} else {
mod_pow(&nonce, &self.n, &self.n_squared)
};
let product = if let Some(ctx) = &self.n_squared_ctx {
ctx.mul(ciphertext, &factor)
} else {
BigUint::mod_mul(ciphertext, &factor, &self.n_squared)
};
Some(product)
}
#[must_use]
pub fn add_ciphertexts(&self, lhs: &BigUint, rhs: &BigUint) -> Option<BigUint> {
if lhs >= &self.n_squared || rhs >= &self.n_squared {
return None;
}
if let Some(ctx) = &self.n_squared_ctx {
Some(ctx.mul(lhs, rhs))
} else {
Some(BigUint::mod_mul(lhs, rhs, &self.n_squared))
}
}
#[must_use]
pub fn to_key_blob(&self) -> Vec<u8> {
encode_biguints(&[&self.n, &self.zeta])
}
#[must_use]
pub fn from_key_blob(blob: &[u8]) -> Option<Self> {
let mut fields = decode_biguints(blob)?.into_iter();
let n = fields.next()?;
let zeta = fields.next()?;
if fields.next().is_some() || n <= BigUint::one() || !n.is_odd() || zeta <= BigUint::one() {
return None;
}
let n_squared = n.mul_ref(&n);
let n_squared_ctx = MontgomeryCtx::new(&n_squared);
let zeta_mont = n_squared_ctx.as_ref().map(|ctx| ctx.encode(&zeta));
Some(Self {
n,
n_squared,
zeta,
zeta_mont,
n_squared_ctx,
})
}
#[must_use]
pub fn to_pem(&self) -> String {
pem_wrap(PAILLIER_PUBLIC_LABEL, &self.to_key_blob())
}
#[must_use]
pub fn to_xml(&self) -> String {
xml_wrap("PaillierPublicKey", &[("n", &self.n), ("zeta", &self.zeta)])
}
#[must_use]
pub fn from_pem(pem: &str) -> Option<Self> {
let blob = pem_unwrap(PAILLIER_PUBLIC_LABEL, pem)?;
Self::from_key_blob(&blob)
}
#[must_use]
pub fn from_xml(xml: &str) -> Option<Self> {
let mut fields = xml_unwrap("PaillierPublicKey", &["n", "zeta"], xml)?.into_iter();
let n = fields.next()?;
let zeta = fields.next()?;
if fields.next().is_some() || n <= BigUint::one() || !n.is_odd() || zeta <= BigUint::one() {
return None;
}
let n_squared = n.mul_ref(&n);
let n_squared_ctx = MontgomeryCtx::new(&n_squared);
let zeta_mont = n_squared_ctx.as_ref().map(|ctx| ctx.encode(&zeta));
Some(Self {
n,
n_squared,
zeta,
zeta_mont,
n_squared_ctx,
})
}
}
impl PaillierPrivateKey {
#[must_use]
pub fn modulus(&self) -> &BigUint {
&self.n
}
#[must_use]
pub fn lambda(&self) -> &BigUint {
&self.lambda
}
#[must_use]
pub fn decryption_factor(&self) -> &BigUint {
&self.u
}
#[must_use]
pub fn decrypt_raw(&self, ciphertext: &BigUint) -> BigUint {
let value = if let Some(ctx) = &self.n_squared_ctx {
ctx.pow(ciphertext, &self.lambda)
} else {
mod_pow(ciphertext, &self.lambda, &self.n_squared)
};
let lifted = paillier_l(&value, &self.n);
if let Some(ctx) = &self.n_ctx {
ctx.mul(&lifted, &self.u)
} else {
BigUint::mod_mul(&lifted, &self.u, &self.n)
}
}
#[must_use]
pub fn decrypt(&self, ciphertext: &BigUint) -> Vec<u8> {
self.decrypt_raw(ciphertext).to_be_bytes()
}
#[must_use]
pub fn decrypt_bytes(&self, ciphertext: &[u8]) -> Option<Vec<u8>> {
let mut fields = decode_biguints(ciphertext)?.into_iter();
let value = fields.next()?;
if fields.next().is_some() {
return None;
}
Some(self.decrypt(&value))
}
#[must_use]
pub fn to_key_blob(&self) -> Vec<u8> {
encode_biguints(&[&self.n, &self.lambda, &self.u])
}
#[must_use]
pub fn from_key_blob(blob: &[u8]) -> Option<Self> {
let mut fields = decode_biguints(blob)?.into_iter();
let n = fields.next()?;
let lambda = fields.next()?;
let u = fields.next()?;
if fields.next().is_some()
|| n <= BigUint::one()
|| !n.is_odd()
|| lambda.is_zero()
|| u.is_zero()
{
return None;
}
let n_squared = n.mul_ref(&n);
let n_squared_ctx = MontgomeryCtx::new(&n_squared);
let n_ctx = MontgomeryCtx::new(&n);
Some(Self {
n,
n_squared,
lambda,
u,
n_squared_ctx,
n_ctx,
})
}
#[must_use]
pub fn to_pem(&self) -> String {
pem_wrap(PAILLIER_PRIVATE_LABEL, &self.to_key_blob())
}
#[must_use]
pub fn to_xml(&self) -> String {
xml_wrap(
"PaillierPrivateKey",
&[("n", &self.n), ("lambda", &self.lambda), ("u", &self.u)],
)
}
#[must_use]
pub fn from_pem(pem: &str) -> Option<Self> {
let blob = pem_unwrap(PAILLIER_PRIVATE_LABEL, pem)?;
Self::from_key_blob(&blob)
}
#[must_use]
pub fn from_xml(xml: &str) -> Option<Self> {
let mut fields = xml_unwrap("PaillierPrivateKey", &["n", "lambda", "u"], xml)?.into_iter();
let n = fields.next()?;
let lambda = fields.next()?;
let u = fields.next()?;
if fields.next().is_some()
|| n <= BigUint::one()
|| !n.is_odd()
|| lambda.is_zero()
|| u.is_zero()
{
return None;
}
let n_squared = n.mul_ref(&n);
let n_squared_ctx = MontgomeryCtx::new(&n_squared);
let n_ctx = MontgomeryCtx::new(&n);
Some(Self {
n,
n_squared,
lambda,
u,
n_squared_ctx,
n_ctx,
})
}
}
impl fmt::Debug for PaillierPrivateKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("PaillierPrivateKey(<redacted>)")
}
}
impl Paillier {
#[must_use]
pub fn from_primes_with_base(
p: &BigUint,
q: &BigUint,
base: &BigUint,
) -> Option<(PaillierPublicKey, PaillierPrivateKey)> {
if p == q || !is_probable_prime(p) || !is_probable_prime(q) {
return None;
}
let n = p.mul_ref(q);
let p_minus_one = p.sub_ref(&BigUint::one());
let q_minus_one = q.sub_ref(&BigUint::one());
let totient = p_minus_one.mul_ref(&q_minus_one);
if gcd(&n, &totient) != BigUint::one() {
return None;
}
let lambda = lcm(&p_minus_one, &q_minus_one);
let n_squared = n.mul_ref(&n);
let zeta = base.modulo(&n_squared);
if zeta <= BigUint::one() {
return None;
}
let lifted = paillier_l(&mod_pow(&zeta, &lambda, &n_squared), &n);
let u = mod_inverse(&lifted, &n)?;
let n_squared_ctx = MontgomeryCtx::new(&n_squared);
let n_ctx = MontgomeryCtx::new(&n);
let zeta_mont = n_squared_ctx.as_ref().map(|ctx| ctx.encode(&zeta));
Some((
PaillierPublicKey {
n: n.clone(),
n_squared: n_squared.clone(),
zeta,
zeta_mont,
n_squared_ctx: n_squared_ctx.clone(),
},
PaillierPrivateKey {
n,
n_squared,
lambda,
u,
n_squared_ctx,
n_ctx,
},
))
}
#[must_use]
pub fn from_primes(
p: &BigUint,
q: &BigUint,
) -> Option<(PaillierPublicKey, PaillierPrivateKey)> {
let n = p.mul_ref(q);
let base = n.add_ref(&BigUint::one());
Self::from_primes_with_base(p, q, &base)
}
#[must_use]
pub fn generate<R: Csprng>(
rng: &mut R,
bits: usize,
) -> Option<(PaillierPublicKey, PaillierPrivateKey)> {
if bits < 8 {
return None;
}
let p_bits = bits / 2;
let q_bits = bits - p_bits;
loop {
let p = random_probable_prime(rng, p_bits)?;
let q = random_probable_prime(rng, q_bits)?;
if let Some(keypair) = Self::from_primes(&p, &q) {
return Some(keypair);
}
}
}
}
fn paillier_l(value: &BigUint, modulus: &BigUint) -> BigUint {
let shifted = value.sub_ref(&BigUint::one());
let (quotient, remainder) = shifted.div_rem(modulus);
debug_assert!(
remainder.is_zero(),
"Paillier L input is congruent to 1 mod n"
);
quotient
}
#[cfg(test)]
mod tests {
use super::{Paillier, PaillierPrivateKey, PaillierPublicKey};
use crate::public_key::bigint::BigUint;
use crate::CtrDrbgAes256;
#[test]
fn derive_small_reference_key() {
let p = BigUint::from_u64(3);
let q = BigUint::from_u64(5);
let (public, private) = Paillier::from_primes(&p, &q).expect("valid Paillier key");
assert_eq!(public.modulus(), &BigUint::from_u64(15));
assert_eq!(public.generator(), &BigUint::from_u64(16));
assert_eq!(private.modulus(), &BigUint::from_u64(15));
assert_eq!(private.lambda(), &BigUint::from_u64(4));
assert_eq!(private.decryption_factor(), &BigUint::from_u64(4));
}
#[test]
fn roundtrip_small_messages() {
let p = BigUint::from_u64(3);
let q = BigUint::from_u64(5);
let nonce = BigUint::from_u64(2);
let (public, private) = Paillier::from_primes(&p, &q).expect("valid Paillier key");
for msg in [0u64, 1, 7, 14] {
let message = BigUint::from_u64(msg);
let ciphertext = public
.encrypt_with_nonce(&message, &nonce)
.expect("valid Paillier nonce");
let plaintext = private.decrypt_raw(&ciphertext);
assert_eq!(plaintext, message);
}
}
#[test]
fn exact_small_ciphertext_matches_reference() {
let p = BigUint::from_u64(3);
let q = BigUint::from_u64(5);
let nonce = BigUint::from_u64(2);
let (public, private) = Paillier::from_primes(&p, &q).expect("valid Paillier key");
let message = BigUint::from_u64(7);
let ciphertext = public
.encrypt_with_nonce(&message, &nonce)
.expect("valid Paillier nonce");
assert_eq!(ciphertext, BigUint::from_u64(83));
assert_eq!(private.decrypt_raw(&ciphertext), message);
}
#[test]
fn raw_paillier_is_additively_homomorphic() {
let p = BigUint::from_u64(3);
let q = BigUint::from_u64(5);
let left_nonce = BigUint::from_u64(2);
let right_nonce = BigUint::from_u64(4);
let (public, private) = Paillier::from_primes(&p, &q).expect("valid Paillier key");
let left = BigUint::from_u64(7);
let right = BigUint::from_u64(6);
let left_cipher = public
.encrypt_with_nonce(&left, &left_nonce)
.expect("valid Paillier nonce");
let right_cipher = public
.encrypt_with_nonce(&right, &right_nonce)
.expect("valid Paillier nonce");
let modulus_squared = public.modulus().mul_ref(public.modulus());
let combined_cipher = BigUint::mod_mul(&left_cipher, &right_cipher, &modulus_squared);
let decrypted = private.decrypt_raw(&combined_cipher);
let expected = left.add_ref(&right).modulo(public.modulus());
assert_eq!(decrypted, expected);
}
#[test]
fn rejects_invalid_parameters() {
let p = BigUint::from_u64(3);
let q = BigUint::from_u64(7);
assert!(Paillier::from_primes(&p, &q).is_none());
let p = BigUint::from_u64(3);
let q = BigUint::from_u64(5);
assert!(Paillier::from_primes_with_base(&p, &q, &BigUint::one()).is_none());
}
#[test]
fn rejects_invalid_nonce() {
let p = BigUint::from_u64(3);
let q = BigUint::from_u64(5);
let (public, _) = Paillier::from_primes(&p, &q).expect("valid Paillier key");
let message = BigUint::from_u64(7);
assert!(public
.encrypt_with_nonce(&message, &BigUint::zero())
.is_none());
assert!(public
.encrypt_with_nonce(&message, &BigUint::from_u64(3))
.is_none());
assert!(public
.encrypt_with_nonce(&message, &BigUint::from_u64(15))
.is_none());
}
#[test]
fn byte_wrapper_roundtrip_and_rerandomize() {
let p = BigUint::from_u64(257);
let q = BigUint::from_u64(263);
let (public, private) = Paillier::from_primes(&p, &q).expect("valid Paillier key");
let mut drbg = CtrDrbgAes256::new(&[0x52; 48]);
let ciphertext = public
.encrypt(&[0x12, 0x34], &mut drbg)
.expect("message fits");
let rerandomized = public
.rerandomize(&ciphertext, &mut drbg)
.expect("rerandomization");
assert_eq!(private.decrypt(&ciphertext), vec![0x12, 0x34]);
assert_eq!(private.decrypt(&rerandomized), vec![0x12, 0x34]);
assert_ne!(ciphertext, rerandomized);
}
#[test]
fn add_ciphertexts_wrapper_matches_plaintext_addition() {
let p = BigUint::from_u64(257);
let q = BigUint::from_u64(263);
let (public, private) = Paillier::from_primes(&p, &q).expect("valid Paillier key");
let left = public
.encrypt_with_nonce(&BigUint::from_u64(0x12), &BigUint::from_u64(2))
.expect("valid nonce");
let right = public
.encrypt_with_nonce(&BigUint::from_u64(0x34), &BigUint::from_u64(3))
.expect("valid nonce");
let combined = public
.add_ciphertexts(&left, &right)
.expect("ciphertexts are in range");
assert_eq!(private.decrypt(&combined), vec![0x46]);
}
#[test]
fn generate_keypair_roundtrip() {
let mut drbg = CtrDrbgAes256::new(&[0x53; 48]);
let (public, private) = Paillier::generate(&mut drbg, 32).expect("Paillier key generation");
let ciphertext = public.encrypt(&[0x2a], &mut drbg).expect("message fits");
assert_eq!(private.decrypt(&ciphertext), vec![0x2a]);
}
#[test]
fn wrappers_reject_out_of_range_ciphertexts() {
let p = BigUint::from_u64(257);
let q = BigUint::from_u64(263);
let (public, _) = Paillier::from_primes(&p, &q).expect("valid Paillier key");
let invalid = public.modulus().mul_ref(public.modulus());
let mut drbg = CtrDrbgAes256::new(&[0x95; 48]);
assert!(public.rerandomize(&invalid, &mut drbg).is_none());
let valid = public
.encrypt_with_nonce(&BigUint::from_u64(7), &BigUint::from_u64(2))
.expect("valid nonce");
assert!(public.add_ciphertexts(&valid, &invalid).is_none());
}
#[test]
fn key_serialization_roundtrip() {
let p = BigUint::from_u64(3);
let q = BigUint::from_u64(5);
let (public, private) = Paillier::from_primes(&p, &q).expect("valid key");
let public_blob = public.to_key_blob();
let private_blob = private.to_key_blob();
assert_eq!(
PaillierPublicKey::from_key_blob(&public_blob),
Some(public.clone())
);
assert_eq!(
PaillierPrivateKey::from_key_blob(&private_blob),
Some(private.clone())
);
let public_pem = public.to_pem();
let private_pem = private.to_pem();
let public_xml = public.to_xml();
let private_xml = private.to_xml();
assert_eq!(
PaillierPublicKey::from_pem(&public_pem),
Some(public.clone())
);
assert_eq!(
PaillierPrivateKey::from_pem(&private_pem),
Some(private.clone())
);
assert_eq!(PaillierPublicKey::from_xml(&public_xml), Some(public));
assert_eq!(PaillierPrivateKey::from_xml(&private_xml), Some(private));
}
#[test]
fn generated_key_serialization_roundtrip() {
let mut key_rng = CtrDrbgAes256::new(&[0xb5; 48]);
let mut enc_rng = CtrDrbgAes256::new(&[0xb6; 48]);
let (public, private) =
Paillier::generate(&mut key_rng, 32).expect("Paillier key generation");
let message = [0x03];
let public =
PaillierPublicKey::from_key_blob(&public.to_key_blob()).expect("public binary");
let private = PaillierPrivateKey::from_xml(&private.to_xml()).expect("private XML");
let ciphertext = public
.encrypt(&message, &mut enc_rng)
.expect("message fits");
assert_eq!(private.decrypt(&ciphertext), message.to_vec());
}
#[test]
fn byte_ciphertext_roundtrip() {
let mut drbg = CtrDrbgAes256::new(&[0x57; 48]);
let p = BigUint::from_u64(257);
let q = BigUint::from_u64(263);
let (public, private) = Paillier::from_primes(&p, &q).expect("valid key");
let ciphertext = public
.encrypt_bytes(&[0x2a], &mut drbg)
.expect("message fits");
assert_eq!(private.decrypt_bytes(&ciphertext), Some(vec![0x2a]));
}
}