use num_bigint::ToBigUint;
use vhe::{
Ciphertext, ElGamal, HomomorphicMode, HomomorphicOperations, KeyPair, VerifiableOperations,
};
#[test]
fn test_end_to_end_multiplicative_workflow() {
let keypair = KeyPair::generate_for_testing(512).expect("Failed to generate keys");
let elgamal = ElGamal::new(keypair.public_key.clone(), HomomorphicMode::Multiplicative);
let values = vec![2u32, 3u32, 5u32, 7u32];
let ciphertexts: Vec<Ciphertext> = values
.iter()
.map(|v| elgamal.encrypt(&v.to_biguint().unwrap()).unwrap())
.collect();
let product_ct = elgamal.homomorphic_batch_operation(&ciphertexts).unwrap();
let decrypted = elgamal.decrypt(&product_ct, &keypair.private_key).unwrap();
let expected = 2u32 * 3 * 5 * 7;
assert_eq!(decrypted, expected.to_biguint().unwrap());
}
#[test]
fn test_end_to_end_additive_workflow() {
let keypair = KeyPair::generate_for_testing(512).expect("Failed to generate keys");
let elgamal = ElGamal::new(keypair.public_key.clone(), HomomorphicMode::Additive);
let a = 100u32.to_biguint().unwrap();
let b = 200u32.to_biguint().unwrap();
let ct_a = elgamal.encrypt(&a).unwrap();
let ct_b = elgamal.encrypt(&b).unwrap();
let ct_sum = elgamal.homomorphic_operation(&ct_a, &ct_b).unwrap();
let sum = elgamal.decrypt(&ct_sum, &keypair.private_key).unwrap();
assert_eq!(sum, 300u32.to_biguint().unwrap());
let ct_diff = elgamal.homomorphic_subtract(&ct_b, &ct_a).unwrap();
let diff = elgamal.decrypt(&ct_diff, &keypair.private_key).unwrap();
assert_eq!(diff, 100u32.to_biguint().unwrap());
let scalar = 5u32.to_biguint().unwrap();
let ct_scaled = elgamal
.homomorphic_scalar_operation(&ct_a, &scalar)
.unwrap();
let scaled = elgamal.decrypt(&ct_scaled, &keypair.private_key).unwrap();
assert_eq!(scaled, 500u32.to_biguint().unwrap());
}
#[test]
fn test_linear_combination() {
let keypair = KeyPair::generate_for_testing(512).unwrap();
let elgamal = ElGamal::new(keypair.public_key.clone(), HomomorphicMode::Additive);
let values = vec![10u32, 20u32, 30u32];
let coefficients = vec![2u32, 3u32, 4u32];
let ciphertexts: Vec<_> = values
.iter()
.map(|v| elgamal.encrypt(&v.to_biguint().unwrap()).unwrap())
.collect();
let coeff_biguints: Vec<_> = coefficients
.iter()
.map(|c| c.to_biguint().unwrap())
.collect();
let result_ct = elgamal
.homomorphic_linear_combination(&ciphertexts, &coeff_biguints)
.unwrap();
let result = elgamal.decrypt(&result_ct, &keypair.private_key).unwrap();
assert_eq!(result, 200u32.to_biguint().unwrap());
}
#[test]
fn test_verifiable_operations_workflow() {
let keypair = KeyPair::generate_for_testing(512).unwrap();
let elgamal = ElGamal::new(keypair.public_key.clone(), HomomorphicMode::Additive);
let value = 42u32.to_biguint().unwrap();
let (ct, proof) = elgamal.encrypt_with_proof(&value, None).unwrap();
assert!(elgamal.verify_encryption_proof(&ct, &value, &proof));
let wrong_value = 43u32.to_biguint().unwrap();
assert!(!elgamal.verify_encryption_proof(&ct, &wrong_value, &proof));
let value2 = 58u32.to_biguint().unwrap();
let ct2 = elgamal.encrypt(&value2).unwrap();
let (result_ct, op_proof) = elgamal.homomorphic_operation_with_proof(&ct, &ct2).unwrap();
assert!(elgamal.verify_operation_proof(&ct, &ct2, &result_ct, &op_proof));
let result = elgamal.decrypt(&result_ct, &keypair.private_key).unwrap();
assert_eq!(result, 100u32.to_biguint().unwrap());
}
#[test]
fn test_rerandomization_preserves_plaintext() {
let keypair = KeyPair::generate_for_testing(512).unwrap();
for mode in [HomomorphicMode::Multiplicative, HomomorphicMode::Additive] {
let elgamal = ElGamal::new(keypair.public_key.clone(), mode.clone());
let plaintext = 999u32.to_biguint().unwrap();
let original = elgamal.encrypt(&plaintext).unwrap();
let mut current = original.clone();
for _ in 0..10 {
current = elgamal.rerandomize(¤t).unwrap();
let decrypted = elgamal.decrypt(¤t, &keypair.private_key).unwrap();
assert_eq!(decrypted, plaintext);
}
assert_ne!(original, current);
}
}
#[test]
fn test_proof_of_knowledge() {
let keypair = KeyPair::generate_for_testing(512).unwrap();
let elgamal = ElGamal::new(keypair.public_key.clone(), HomomorphicMode::Multiplicative);
let proof = elgamal.prove_knowledge_of_dlog(
keypair.private_key.secret_exponent(),
keypair.public_key.generator(),
keypair.public_key.public_component(),
);
assert!(elgamal.verify_knowledge_of_dlog(
&proof,
keypair.public_key.generator(),
keypair.public_key.public_component()
));
}
#[test]
fn test_division_in_multiplicative_mode() {
let keypair = KeyPair::generate_for_testing(512).unwrap();
let elgamal = ElGamal::new(keypair.public_key.clone(), HomomorphicMode::Multiplicative);
let a = 84u32.to_biguint().unwrap();
let b = 12u32.to_biguint().unwrap();
let ct_a = elgamal.encrypt(&a).unwrap();
let ct_b = elgamal.encrypt(&b).unwrap();
let ct_quotient = elgamal.homomorphic_divide(&ct_a, &ct_b).unwrap();
let decrypted = elgamal.decrypt(&ct_quotient, &keypair.private_key).unwrap();
let b_inv = vhe::utils::mod_inverse(&b, keypair.public_key.modulus()).unwrap();
let expected = (a * b_inv) % keypair.public_key.modulus();
assert_eq!(decrypted, expected);
}
#[test]
fn test_key_validation() {
use vhe::PublicKey;
let keypair = KeyPair::generate_for_testing(512).unwrap();
assert!(keypair.public_key.validate().is_ok());
let invalid_key = PublicKey::new(
2u32.to_biguint().unwrap(),
1u32.to_biguint().unwrap(),
1u32.to_biguint().unwrap(),
);
assert!(invalid_key.validate().is_err());
}
#[test]
fn test_mode_mismatch_prevention() {
let keypair = KeyPair::generate_for_testing(512).unwrap();
let elgamal_mult = ElGamal::new(keypair.public_key.clone(), HomomorphicMode::Multiplicative);
let elgamal_add = ElGamal::new(keypair.public_key.clone(), HomomorphicMode::Additive);
let value = 42u32.to_biguint().unwrap();
let ct_mult = elgamal_mult.encrypt(&value).unwrap();
assert!(elgamal_add.decrypt(&ct_mult, &keypair.private_key).is_err());
let ct_add = elgamal_add.encrypt(&value).unwrap();
assert!(elgamal_mult
.homomorphic_operation(&ct_mult, &ct_add)
.is_err());
}
#[test]
fn test_large_scale_operations() {
let keypair = KeyPair::generate_for_testing(512).unwrap();
let elgamal = ElGamal::new(keypair.public_key.clone(), HomomorphicMode::Additive);
let values: Vec<_> = (1..=100).collect();
let ciphertexts: Vec<_> = values
.iter()
.map(|v| elgamal.encrypt(&v.to_biguint().unwrap()).unwrap())
.collect();
let sum_ct = elgamal.homomorphic_batch_operation(&ciphertexts).unwrap();
let sum = elgamal.decrypt(&sum_ct, &keypair.private_key).unwrap();
assert_eq!(sum, 5050u32.to_biguint().unwrap());
}
#[test]
fn test_verifiable_rerandomization() {
let keypair = KeyPair::generate_for_testing(512).unwrap();
let elgamal = ElGamal::new(keypair.public_key.clone(), HomomorphicMode::Multiplicative);
let plaintext = 123u32.to_biguint().unwrap();
let original = elgamal.encrypt(&plaintext).unwrap();
let (rerandomized, proof) = elgamal.rerandomize_with_proof(&original).unwrap();
assert!(elgamal.verify_rerandomization_proof(&original, &rerandomized, &proof));
let dec1 = elgamal.decrypt(&original, &keypair.private_key).unwrap();
let dec2 = elgamal
.decrypt(&rerandomized, &keypair.private_key)
.unwrap();
assert_eq!(dec1, dec2);
assert_eq!(dec1, plaintext);
}
#[test]
fn test_error_handling() {
use vhe::ElGamalError;
let keypair = KeyPair::generate_for_testing(512).unwrap();
let elgamal = ElGamal::new(keypair.public_key.clone(), HomomorphicMode::Additive);
let large_value = 10_000_000u32.to_biguint().unwrap();
match elgamal.encrypt(&large_value) {
Err(ElGamalError::PlaintextTooLargeForAdditive { .. }) => (),
_ => panic!("Expected PlaintextTooLargeForAdditive error"),
}
let empty_cts: Vec<Ciphertext> = vec![];
match elgamal.homomorphic_batch_operation(&empty_cts) {
Err(ElGamalError::EmptyBatch) => (),
_ => panic!("Expected EmptyBatch error"),
}
}
#[test]
fn test_safe_prime_generation() {
let keypair = KeyPair::load_or_generate(512);
assert!(
keypair.is_ok(),
"Safe prime generation should work with lenient mode"
);
let keypair = keypair.unwrap();
assert!(keypair.public_key.validate().is_ok());
let bit_size = keypair.public_key.bit_size();
assert!(
bit_size >= 504 && bit_size <= 520,
"Bit size should be near 512, got {}",
bit_size
);
}