use fast_paillier::{utils, DecryptionKey};
use rand::Rng;
use rug::{Complete, Integer};
#[test]
fn encrypt_decrypt() {
let mut rng = rand_dev::DevRng::new();
let dk = random_key_for_tests(&mut rng);
let ek = dk.encryption_key();
for _ in 0..50 {
let plaintext = ek
.n()
.clone()
.random_below(&mut utils::external_rand(&mut rng));
let plaintext = plaintext - (ek.n() / 2u8).complete();
println!("Plaintext: {plaintext}");
let (ciphertext, nonce) = ek.encrypt_with_random(&mut rng, &plaintext).unwrap();
println!("Ciphertext: {ciphertext}");
println!("Nonce: {nonce}");
let decrypted = dk.decrypt(&ciphertext).unwrap();
println!("Decrypted: {decrypted}");
assert_eq!(plaintext, decrypted);
println!();
}
let lower_bound = -(ek.n() / 2u8).complete();
let upper_bound = (ek.n() / 2u8).complete();
let corner_cases = [
lower_bound.clone(),
lower_bound.clone() + 1,
upper_bound.clone() - 1,
upper_bound.clone(),
];
for (i, plaintext) in corner_cases.into_iter().enumerate() {
println!("Corner case {i}");
let (ciphertext, _nonce) = ek.encrypt_with_random(&mut rng, &plaintext).unwrap();
let decrypted = dk.decrypt(&ciphertext).unwrap();
assert_eq!(plaintext, decrypted);
}
}
#[test]
fn doesnt_encrypt_plaintext_out_of_bounds() {
let mut rng = rand_dev::DevRng::new();
let dk = random_key_for_tests(&mut rng);
let ek = dk.encryption_key();
let lower_bound = -(ek.n() / 2u8).complete();
let upper_bound = (ek.n() / 2u8).complete();
let cases = [
lower_bound.clone() - 1,
lower_bound.clone() - 2,
upper_bound.clone() + 1,
upper_bound.clone() + 2,
];
for (i, plaintext) in cases.into_iter().enumerate() {
println!("Case {i}");
let _: fast_paillier::Error = ek.encrypt_with_random(&mut rng, &plaintext).unwrap_err();
}
}
#[test]
fn homorphic_ops() {
let mut rng = rand_dev::DevRng::new();
let dk = random_key_for_tests(&mut rng);
let ek = dk.encryption_key();
for _ in 0..100 {
let a = ek
.n()
.clone()
.random_below(&mut utils::external_rand(&mut rng));
let b = ek
.n()
.clone()
.random_below(&mut utils::external_rand(&mut rng));
let a = a - (ek.n() / 2u8).complete();
let b = b - (ek.n() / 2u8).complete();
println!("a: {a}");
println!("b: {b}");
let (enc_a, _nonce) = ek.encrypt_with_random(&mut rng, &a).unwrap();
let (enc_b, _nonce) = ek.encrypt_with_random(&mut rng, &b).unwrap();
{
let enc_a_plus_b = ek.oadd(&enc_a, &enc_b).unwrap();
let a_plus_b = dk.decrypt(&enc_a_plus_b).unwrap();
assert_eq!(a_plus_b, signed_modulo(&(&a + &b).complete(), ek.n()));
}
{
let enc_a_minus_b = ek.osub(&enc_a, &enc_b).unwrap();
let a_minus_b = dk.decrypt(&enc_a_minus_b).unwrap();
assert_eq!(a_minus_b, signed_modulo(&(&a - &b).complete(), ek.n()));
}
{
let enc_neg_a = ek.oneg(&enc_a).unwrap();
let neg_a = dk.decrypt(&enc_neg_a).unwrap();
assert_eq!(neg_a, signed_modulo(&(-&a).complete(), ek.n()));
}
{
let enc_a_at_b = ek.omul(&a, &enc_b).unwrap();
let a_at_b = dk.decrypt(&enc_a_at_b).unwrap();
assert_eq!(a_at_b, signed_modulo(&(&a * &b).complete(), ek.n()));
}
}
}
#[test]
fn encryption_with_known_factorization() {
let mut rng = rand_dev::DevRng::new();
let dk = random_key_for_tests(&mut rng);
let ek = dk.encryption_key();
for i in 0..100 {
println!("Iteration {i}");
let x = ek
.n()
.clone()
.random_below(&mut utils::external_rand(&mut rng));
let x = x - ek.half_n();
let nonce = utils::sample_in_mult_group(&mut rng, ek.n());
let enc_x1 = ek.encrypt_with(&x, &nonce).unwrap();
let enc_x2 = dk.encrypt_with(&x, &nonce).unwrap();
assert_eq!(enc_x1, enc_x2);
}
}
#[test]
fn factorized_exp_mod_n() {
let mut rng = rand_dev::DevRng::new();
let p = utils::generate_safe_prime(&mut rng, 512);
let q = utils::generate_safe_prime(&mut rng, 512);
let n = (&p * &q).complete();
println!("n: {n}");
let crt = utils::CrtExp::build_n(&p, &q).unwrap();
for _ in 0..100 {
let x: Integer = n
.random_below_ref(&mut utils::external_rand(&mut rng))
.into();
let mut e: Integer = Integer::random_bits(1024, &mut utils::external_rand(&mut rng)).into();
if rng.gen::<bool>() {
e = -e
}
let crt_e = crt.prepare_exponent(&e);
println!();
println!("x: {x}");
println!("e: {e}");
let expected: Integer = x.pow_mod_ref(&e, &n).unwrap().into();
let actual = crt.exp(&x, &crt_e).unwrap();
assert_eq!(expected, actual);
}
}
#[test]
fn factorized_exp_mod_nn() {
let mut rng = rand_dev::DevRng::new();
let p = utils::generate_safe_prime(&mut rng, 512);
let q = utils::generate_safe_prime(&mut rng, 512);
let nn = (&p * &q).complete().square();
println!("nn: {nn}");
let crt = utils::CrtExp::build_nn(&p, &q).unwrap();
for _ in 0..100 {
let x: Integer = nn
.random_below_ref(&mut utils::external_rand(&mut rng))
.into();
let mut e: Integer = Integer::random_bits(1024, &mut utils::external_rand(&mut rng)).into();
if rng.gen::<bool>() {
e = -e
}
let crt_e = crt.prepare_exponent(&e);
println!();
println!("x: {x}");
println!("e: {e}");
let expected: Integer = x.pow_mod_ref(&e, &nn).unwrap().into();
let actual = crt.exp(&x, &crt_e).unwrap();
assert_eq!(expected, actual);
}
}
fn signed_modulo(x: &Integer, n: &Integer) -> Integer {
let x = x.modulo_ref(n).complete();
unsigned_mod_to_signed(x, n)
}
fn unsigned_mod_to_signed(x: Integer, n: &Integer) -> Integer {
if (2u8 * &x).complete() >= *n {
x - n
} else {
x
}
}
fn random_key_for_tests(rng: &mut impl rand_core::RngCore) -> DecryptionKey {
let p = utils::generate_safe_prime(rng, 512);
let q = utils::generate_safe_prime(rng, 512);
DecryptionKey::from_primes(p, q).unwrap()
}