Modified ElGamal
Modified ElGamal [JCJ02] is a public key encryption scheme over a cyclic group of prime order that introduces a two-dimensional secret key and a three-component ciphertext. The change preserves the core guarantees of classical ElGamal (IND-CPA under DDH, rerandomizability, and homomorphism) while making the ciphertext structure more convenient for zero-knowledge proofs, shuffles, and threshold-style workflows.
Mathematical setting
Let be a cyclic group of prime order with independent generators .
- Secret key: sampled uniformly at random.
- Public key: .
Encryption of :
- Sample uniformly at random.
- Output ciphertext where
Decryption with secret key :
Correctness follows since:
Homomorphism
For any two ciphertexts and , Thus Modified ElGamal is multiplicatively homomorphic.
When messages are represented in the exponent, i.e., for a third fixed generator , then which induces additive homomorphism on exponents. Recovering from requires solving a discrete logarithm and is feasible only for small message domains.
Rerandomization
Given , any party can produce a distribution-identical ciphertext on the same plaintext by sampling in and outputting Rerandomization preserves correctness and hides .
Applications
- Verifiable shuffles and mix-nets: ciphertexts can be permuted and rerandomized while preserving plaintexts.
- Threshold workflows: the split structure of the secret key and the explicit randomness terms simplify share verifications.
- Privacy-preserving systems: voting, sealed-bid auctions, credential systems, and any setting that benefits from efficient NIZKs over ElGamal relations.
Example
The crate is generic over a group backend. For documentation builds, select exactly one backend feature for the library (for example, p256 or ristretto). The alias Curve refers to the selected backend in this build.
// In Cargo.toml of this doc build:
// dlog-sigma-primitives = { version = "x.y.z", features = ["p256"] }
use rand_core::OsRng;
use dlog_sigma_primitives::prelude::*;
// The library exposes a concrete backend alias selected by features:
// type Curve = dlog_group::<backend>::Group;
// All APIs remain generic; Curve is provided for convenience.
fn main() {
let mut rng = OsRng;
// (sk, pk) sampled in the selected Curve
let params = ElGamalParams::<Curve>::new(&mut rng);
let (sk, pk) = KeyPair::<Curve>::new_from_params(¶ms, &mut rng).into_tuple();
// Sample a random message
let m = Curve::generator() * Curve::scalar_random(&mut rng);
// Encrypt and decrypt
let (ct, _r) = pk.encrypt(m, ¶ms, &mut rng).into_tuple();
let m_dec = sk.decrypt(&ct);
assert_eq!(m, m_dec);
}