ElGamal
Header: #include <cryptopp/elgamal.h>
Namespace: CryptoPP
Since: Crypto++ 1.0
Thread Safety: Not thread-safe per instance; use separate instances per thread
ElGamal is a public-key encryption scheme based on the Diffie-Hellman key exchange. It provides semantic security under the Decisional Diffie-Hellman (DDH) assumption. While secure for encryption, modern applications may prefer ECIES for efficiency.
Quick Example
#include <cryptopp/elgamal.h>
#include <cryptopp/osrng.h>
#include <cryptopp/filters.h>
#include <iostream>
int main() {
using namespace CryptoPP;
AutoSeededRandomPool rng;
// Generate ElGamal key pair (2048-bit)
ElGamal::Decryptor decryptor;
decryptor.AccessKey().GenerateRandomWithKeySize(rng, 2048);
ElGamal::Encryptor encryptor(decryptor);
// Encrypt
std::string message = "Secret message";
std::string ciphertext;
StringSource(message, true,
new PK_EncryptorFilter(rng, encryptor,
new StringSink(ciphertext)
)
);
// Decrypt
std::string recovered;
StringSource(ciphertext, true,
new PK_DecryptorFilter(rng, decryptor,
new StringSink(recovered)
)
);
std::cout << "Decrypted: " << recovered << std::endl;
return 0;
}Usage Guidelines
ElGamal is an older discrete-log encryption scheme. It’s secure but has larger ciphertexts than RSA or ECIES. For modern applications, prefer ECIES (elliptic curve variant) for better performance.
Use ElGamal for:
- Legacy system compatibility
- When integer DH groups are required
- Educational purposes
Modern alternatives:
- Encryption: Use ECIES (smaller keys/ciphertexts, faster)
- Key exchange: Use X25519 or ECDH
- Signatures: Use Ed25519 or ECDSA
ElGamal Notes:
- Ciphertext is 2x the size of plaintext plus overhead
- Key generation can be slow for large key sizes
- Message length is limited by group size
Security Advisory: CVE-2024-28285
Fixed in cryptopp-modern 2026.2.0
A fault-injection vulnerability in hybrid ElGamal decryption was fixed. The fix provides:
- No-write-on-failure guarantee: Plaintext buffer remains untouched on decryption failure
- Blinded verification: Detects faulted key-agreement computations
- RNG now used:
Decrypt()RNG parameter now used for blinding (was previously ignored)
See Security.md for details.
Class: ElGamal::Decryptor
ElGamal decryptor (private key holder).
Key Generation
AutoSeededRandomPool rng;
ElGamal::Decryptor decryptor;
// Generate with specific key size (bits)
decryptor.AccessKey().GenerateRandomWithKeySize(rng, 2048);
// Or generate with specific parameters
Integer p("..."); // Prime modulus
Integer g("..."); // Generator
Integer x("..."); // Private exponent
decryptor.AccessKey().Initialize(p, g, x);Decryption
// IMPORTANT: Must use real RNG since 2026.2.0
AutoSeededRandomPool rng;
std::string plaintext;
StringSource(ciphertext, true,
new PK_DecryptorFilter(rng, decryptor,
new StringSink(plaintext)
)
);Key Access
// Get private key components
const Integer& p = decryptor.GetGroupParameters().GetModulus();
const Integer& g = decryptor.GetGroupParameters().GetSubgroupGenerator();
const Integer& x = decryptor.AccessKey().GetPrivateExponent();
// Get public key
ElGamal::Encryptor encryptor(decryptor);Class: ElGamal::Encryptor
ElGamal encryptor (public key holder).
Construction
// From decryptor (extracts public key)
ElGamal::Encryptor encryptor(decryptor);
// From explicit parameters
Integer p("..."); // Prime modulus
Integer g("..."); // Generator
Integer y("..."); // Public key (g^x mod p)
ElGamal::Encryptor encryptor;
encryptor.AccessKey().Initialize(p, g, y);Encryption
AutoSeededRandomPool rng;
std::string ciphertext;
StringSource(plaintext, true,
new PK_EncryptorFilter(rng, encryptor,
new StringSink(ciphertext)
)
);Maximum Message Length
size_t maxLen = encryptor.FixedMaxPlaintextLength();Key Serialization
Save Keys
// Save private key
FileSink privFile("private.key");
decryptor.AccessKey().Save(privFile);
// Save public key
FileSink pubFile("public.key");
encryptor.AccessKey().Save(pubFile);Load Keys
// Load private key
FileSource privFile("private.key", true);
ElGamal::Decryptor decryptor;
decryptor.AccessKey().Load(privFile);
// Load public key
FileSource pubFile("public.key", true);
ElGamal::Encryptor encryptor;
encryptor.AccessKey().Load(pubFile);Parameter Selection
Recommended Key Sizes
| Security Level | Key Size | Notes |
|---|---|---|
| 112-bit | 2048-bit | Minimum for new applications |
| 128-bit | 3072-bit | Recommended |
| 192-bit | 7680-bit | High security (slow) |
| 256-bit | 15360-bit | Maximum security (very slow) |
Standard Groups
For interoperability, consider using standardized DH groups (RFC 3526):
#include <cryptopp/dh.h>
// Use RFC 3526 2048-bit MODP group
Integer p("0xFFFFFFFF..."); // From RFC 3526
Integer g(2);
ElGamal::Decryptor decryptor;
decryptor.AccessKey().GenerateRandom(rng,
MakeParameters("Modulus", p)("SubgroupGenerator", g));Error Handling
try {
AutoSeededRandomPool rng;
std::string plaintext;
StringSource(ciphertext, true,
new PK_DecryptorFilter(rng, decryptor,
new StringSink(plaintext)
)
);
} catch (const InvalidCiphertext& e) {
// Invalid ciphertext format or decryption failed
std::cerr << "Decryption failed: " << e.what() << std::endl;
} catch (const Exception& e) {
// Other Crypto++ error
std::cerr << "Error: " << e.what() << std::endl;
}Performance Considerations
- Key Generation: Slow for large key sizes (finding safe primes)
- Encryption: One modular exponentiation
- Decryption: One modular exponentiation plus verification (since 2026.2.0)
- Ciphertext Size: 2x modulus size plus message overhead
For better performance, consider:
- ECIES: Elliptic curve variant with smaller keys
- Hybrid encryption: ElGamal for key encapsulation, AES-GCM for data
Thread Safety
ElGamal objects are not thread-safe. Each thread should use its own encryptor/decryptor instance. The underlying key objects can be shared read-only if properly synchronized.
// Thread-safe usage: separate instances per thread
void encryptInThread(const ElGamal::PublicKey& pubKey,
const std::string& data) {
AutoSeededRandomPool rng;
ElGamal::Encryptor encryptor(pubKey); // Thread-local instance
// ... encrypt ...
}