https://github.com/10d9e/vhe
Verifiable Homomorphic Encryption
https://github.com/10d9e/vhe
cryptography
Last synced: 5 months ago
JSON representation
Verifiable Homomorphic Encryption
- Host: GitHub
- URL: https://github.com/10d9e/vhe
- Owner: 10d9e
- License: mit
- Created: 2025-09-26T13:24:10.000Z (9 months ago)
- Default Branch: main
- Last Pushed: 2025-09-26T22:12:54.000Z (9 months ago)
- Last Synced: 2026-01-13T09:58:39.381Z (5 months ago)
- Topics: cryptography
- Language: Rust
- Homepage:
- Size: 139 KB
- Stars: 3
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
Awesome Lists containing this project
README
# Verifiable Homomorphic Encryption
[](https://crates.io/crates/vhe)
Rust implementation of ElGamal homomorphic encryption with non-interactive zero-knowledge proofs (NIZK) for verifiable operations.
## Features
- **Dual-mode operation**: Switch between multiplicative and additive homomorphism
- **Operator overrides**: Use familiar `+`, `-`, `*`, `/` operators directly on ciphertexts
- **Verifiable computation**: All operations can be verified with NIZK proofs
- **Batch operations**: Efficient processing of multiple ciphertexts
- **Re-randomization**: Generate different ciphertexts for the same plaintext
- **Zero-knowledge proofs**: Prove correctness without revealing secrets
- **No trusted setup**: Unlike zk-SNARKs, no ceremony needed
## Installation
Add to your `Cargo.toml`:
```toml
[dependencies]
vhe = "0.1.1"
```
Or clone and build:
```bash
git clone https://github.com/yourusername/vhe
cd vhe
cargo build --release
```
## Quick Start
### Basic Encryption with Operator Overrides
```rust
use vhe::{KeyPair, ElGamal, ElGamalOperators, HomomorphicMode};
use num_bigint::ToBigUint;
// Generate keys
let keypair = KeyPair::load_or_generate(512)?;
// Create ElGamal instance for additive operations
let elgamal = ElGamal::new(
keypair.public_key.clone(),
HomomorphicMode::Additive
);
// Encrypt values
let ct1 = elgamal.encrypt(&10u32.to_biguint().unwrap())?;
let ct2 = elgamal.encrypt(&20u32.to_biguint().unwrap())?;
// Wrap ciphertexts with context for operator overrides
let ctx1 = elgamal.wrap_ciphertext(ct1);
let ctx2 = elgamal.wrap_ciphertext(ct2);
// Use standard operators!
let sum = (&ctx1 + &ctx2)?; // Addition: 10 + 20
let diff = (&ctx1 - &ctx2)?; // Subtraction: 10 - 20
let neg = (-&ctx1)?; // Negation: -10
let scalar_add = (&ctx1 + &5u32.to_biguint().unwrap())?; // Scalar addition: 10 + 5
// Decrypt results
let sum_result = elgamal.decrypt(&sum, &keypair.private_key)?;
let diff_result = elgamal.decrypt(&diff, &keypair.private_key)?;
let neg_result = elgamal.decrypt(&neg, &keypair.private_key)?;
let scalar_result = elgamal.decrypt(&scalar_add, &keypair.private_key)?;
assert_eq!(sum_result, 30u32.to_biguint().unwrap());
assert_eq!(scalar_result, 15u32.to_biguint().unwrap());
```
### Multiplicative Mode with Operators
```rust
// Create ElGamal instance for multiplicative operations
let elgamal_mult = ElGamal::new(
keypair.public_key.clone(),
HomomorphicMode::Multiplicative
);
// Encrypt values
let ct1 = elgamal_mult.encrypt(&7u32.to_biguint().unwrap())?;
let ct2 = elgamal_mult.encrypt(&3u32.to_biguint().unwrap())?;
// Wrap with context
let ctx1 = elgamal_mult.wrap_ciphertext(ct1);
let ctx2 = elgamal_mult.wrap_ciphertext(ct2);
// Use operators for multiplicative operations
let product = (&ctx1 * &ctx2)?; // Multiplication: 7 * 3
let quotient = (&ctx1 / &ctx2)?; // Division: 7 / 3
let power = (&ctx1 * &2u32.to_biguint().unwrap())?; // Exponentiation: 7^2
// Decrypt results
let product_result = elgamal_mult.decrypt(&product, &keypair.private_key)?;
let quotient_result = elgamal_mult.decrypt("ient, &keypair.private_key)?;
let power_result = elgamal_mult.decrypt(&power, &keypair.private_key)?;
assert_eq!(product_result, 21u32.to_biguint().unwrap());
assert_eq!(power_result, 49u32.to_biguint().unwrap());
```
### Verifiable Operations with Operator Overrides
```rust
use vhe::{VerifiableOperations, HomomorphicOperations, ElGamalOperators};
use num_bigint::ToBigUint;
let keypair = KeyPair::load_or_generate(512)?;
let elgamal = ElGamal::new(keypair.public_key.clone(), HomomorphicMode::Additive);
// Encrypt values with proofs
let value1 = 5u32.to_biguint().unwrap();
let value2 = 3u32.to_biguint().unwrap();
let (ct1, enc_proof1) = elgamal.encrypt_with_proof(&value1, None)?;
let (ct2, enc_proof2) = elgamal.encrypt_with_proof(&value2, None)?;
// Verify encryption proofs
assert!(elgamal.verify_encryption_proof(&ct1, &value1, &enc_proof1));
assert!(elgamal.verify_encryption_proof(&ct2, &value2, &enc_proof2));
// Wrap ciphertexts with context for operator overrides
let ctx1 = elgamal.wrap_ciphertext(ct1);
let ctx2 = elgamal.wrap_ciphertext(ct2);
// Use operators for intuitive computation
let sum = (&ctx1 + &ctx2)?; // Addition: 5 + 3
let scalar_add = (&ctx1 + &2u32.to_biguint().unwrap())?; // Scalar addition: 5 + 2
// Generate proof for the ciphertext-to-ciphertext operation
let (_, sum_proof) = elgamal.homomorphic_operation_with_proof(ctx1.ciphertext(), ctx2.ciphertext())?;
// Verify the operation proof
assert!(elgamal.verify_operation_proof(ctx1.ciphertext(), ctx2.ciphertext(), &sum, &sum_proof));
// Decrypt and verify results
let sum_result = elgamal.decrypt(&sum, &keypair.private_key)?;
let scalar_result = elgamal.decrypt(&scalar_add, &keypair.private_key)?;
assert_eq!(sum_result, 8u32.to_biguint().unwrap());
assert_eq!(scalar_result, 7u32.to_biguint().unwrap());
```
### Advanced Verifiable Computation
```rust
// Multi-step verifiable computation with operators
let values = vec![5u32, 10u32, 15u32, 20u32];
let mut ciphertexts = Vec::new();
let mut enc_proofs = Vec::new();
// Encrypt all values with proofs
for value in &values {
let (ct, proof) = elgamal.encrypt_with_proof(&value.to_biguint().unwrap(), None)?;
ciphertexts.push(ct);
enc_proofs.push(proof);
}
// Verify all encryption proofs
for (i, (ct, proof)) in ciphertexts.iter().zip(enc_proofs.iter()).enumerate() {
assert!(elgamal.verify_encryption_proof(ct, &values[i].to_biguint().unwrap(), proof));
}
// Perform verifiable aggregation using operators
let mut ctx_sum = elgamal.wrap_ciphertext(ciphertexts[0].clone());
let mut operation_proofs = Vec::new();
for i in 1..ciphertexts.len() {
let ctx_next = elgamal.wrap_ciphertext(ciphertexts[i].clone());
// Use operator for addition
let (new_sum, op_proof) = elgamal.homomorphic_operation_with_proof(
&ctx_sum.ciphertext,
&ctx_next.ciphertext
)?;
// Verify the operation
assert!(elgamal.verify_operation_proof(
&ctx_sum.ciphertext,
&ctx_next.ciphertext,
&new_sum,
&op_proof
));
ctx_sum = elgamal.wrap_ciphertext(new_sum);
operation_proofs.push(op_proof);
}
// Final result should be sum of all values
let final_result = elgamal.decrypt(&ctx_sum.ciphertext, &keypair.private_key)?;
let expected_sum: u32 = values.iter().sum();
assert_eq!(final_result, expected_sum.to_biguint().unwrap());
```
## Operator Overrides
The library provides intuitive operator overrides that make homomorphic operations feel natural:
### CiphertextWithContext
The recommended approach uses `CiphertextWithContext` to wrap ciphertexts with their ElGamal context:
```rust
use vhe::{ElGamal, ElGamalOperators, HomomorphicMode, KeyPair};
use num_bigint::ToBigUint;
let keypair = KeyPair::load_or_generate(512)?;
let elgamal = ElGamal::new(keypair.public_key.clone(), HomomorphicMode::Additive);
// Encrypt and wrap with context
let ct1 = elgamal.encrypt(&5u32.to_biguint().unwrap())?;
let ct2 = elgamal.encrypt(&3u32.to_biguint().unwrap())?;
let ctx1 = elgamal.wrap_ciphertext(ct1);
let ctx2 = elgamal.wrap_ciphertext(ct2);
// Use standard operators!
let sum = (&ctx1 + &ctx2)?; // Addition
let diff = (&ctx1 - &ctx2)?; // Subtraction
let neg = (-&ctx1)?; // Negation
let scalar_add = (&ctx1 + &2u32.to_biguint().unwrap())?; // Scalar addition
let scalar_mul = (&ctx1 * &2u32.to_biguint().unwrap())?; // Scalar multiplication
```
### Operators with Verifiable Proofs
You can combine the intuitive operator syntax with verifiable operations:
```rust
use vhe::{VerifiableOperations, ElGamalOperators};
// Encrypt with proofs
let (ct1, enc_proof1) = elgamal.encrypt_with_proof(&5u32.to_biguint().unwrap(), None)?;
let (ct2, enc_proof2) = elgamal.encrypt_with_proof(&3u32.to_biguint().unwrap(), None)?;
// Verify encryption proofs
assert!(elgamal.verify_encryption_proof(&ct1, &5u32.to_biguint().unwrap(), &enc_proof1));
assert!(elgamal.verify_encryption_proof(&ct2, &3u32.to_biguint().unwrap(), &enc_proof2));
// Wrap with context for operators
let ctx1 = elgamal.wrap_ciphertext(ct1);
let ctx2 = elgamal.wrap_ciphertext(ct2);
// Use operators for the computation
let sum = (&ctx1 + &ctx2)?; // Intuitive syntax
let diff = (&ctx1 - &ctx2)?; // Clean and readable
// Generate proof for the operation (ciphertext-to-ciphertext only)
let (_, sum_proof) = elgamal.homomorphic_operation_with_proof(ctx1.ciphertext(), ctx2.ciphertext())?;
// Verify the operation proof
assert!(elgamal.verify_operation_proof(ctx1.ciphertext(), ctx2.ciphertext(), &sum, &sum_proof));
// Decrypt and verify results
let sum_result = elgamal.decrypt(&sum, &keypair.private_key)?;
let diff_result = elgamal.decrypt(&diff, &keypair.private_key)?;
assert_eq!(sum_result, 8u32.to_biguint().unwrap());
assert_eq!(diff_result, 2u32.to_biguint().unwrap());
```
This approach gives you the best of both worlds:
- **Intuitive operators** for clean, readable code
- **Verifiable proofs** for ciphertext-to-ciphertext operations
- **Full auditability** of all operations
- **Note**: Scalar operations use operators for convenience but don't generate verifiable proofs
## Homomorphic Modes
### Multiplicative Mode
- **Operation**: `Enc(a) × Enc(b) = Enc(a × b)`
- **Use cases**: Product aggregation, RSA-like operations
- **Message space**: Full range [0, p-1]
### Additive Mode
- **Operation**: `Enc(a) ⊗ Enc(b) = Enc(a + b)`
- **Use cases**: Voting, statistics, counters
- **Message space**: Limited (configurable, default 1M)
## Available Operations
| Operation | Multiplicative Mode | Additive Mode |
|-----------|-------------------|---------------|
| Basic Operation | Multiplication | Addition |
| Scalar Operation | Exponentiation | Scalar Multiplication |
| Division | ✓ | ✗ |
| Subtraction | ✗ | ✓ |
| Negation | ✗ | ✓ |
| Batch Operations | ✓ | ✓ |
| Linear Combination | ✗ | ✓ |
| Re-randomization | ✓ | ✓ |
## Zero-Knowledge Proofs
The library includes several NIZK proof types:
1. **Proof of Knowledge**: Prove you know a secret without revealing it
2. **Proof of Correct Encryption**: Prove a ciphertext encrypts a specific plaintext
3. **Proof of Equality**: Prove two ciphertexts encrypt the same value
4. **Proof of Correct Operation**: Prove operations were performed correctly
5. **Proof of Re-randomization**: Prove ciphertext was correctly re-randomized
## Running Examples
```bash
# Basic encryption demo
cargo run --example basic_encryption
# Operator overrides demonstration
cargo run --example operator_overrides
# Verifiable operators with proofs
cargo run --example verifiable_operators
# Privacy-preserving voting system
cargo run --example voting_system
# Verifiable computation with proofs
cargo run --example verifiable_computation
# Private statistics computation
cargo run --example private_statistics
```
## Testing
```bash
# Run all tests
cargo test
# Run with verbose output
cargo test -- --nocapture
# Run benchmarks
cargo bench
```
## Security Considerations
- **Discrete Log Assumption**: Security based on hardness of discrete logarithm
- **Random Oracle Model**: Proofs rely on hash function as random oracle
- **Safe Primes**: Uses safe primes (p = 2q + 1) for strong security
- **No Trusted Setup**: Unlike zk-SNARKs, no ceremony or trusted parameters needed
## Performance
- **Key Generation**: ~100-500ms for 1024-bit keys
- **Encryption**: ~5-10ms per value
- **Homomorphic Operations**: ~1-2ms per operation
- **Proof Generation**: ~10-20ms per proof
- **Proof Verification**: ~5-10ms per verification
## Use Cases
- **E-Voting**: Privacy-preserving electronic voting
- **Private Analytics**: Compute statistics without accessing raw data
- **Secure Auctions**: Sealed-bid auctions with verifiable fairness
- **GDPR Compliance**: Process encrypted personal data
- **Multi-party Computation**: Collaborative computation without sharing inputs
- **Blockchain**: Private smart contracts and confidential transactions
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
This project is dual-licensed under MIT OR Apache-2.0.
## References
- [ElGamal Encryption](https://en.wikipedia.org/wiki/ElGamal_encryption)
- [Exponential ElGamal](https://crypto.stanford.edu/pbc/notes/crypto/additive.html)
- [Schnorr Identification Protocol](https://en.wikipedia.org/wiki/Schnorr_signature)
- [Chaum-Pedersen Protocol](https://link.springer.com/chapter/10.1007/3-540-48285-7_24)
- [Fiat-Shamir Heuristic](https://en.wikipedia.org/wiki/Fiat%E2%80%93Shamir_heuristic)
## Contact
For questions or suggestions, please open an issue on GitHub.