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 LevelKey SizeNotes
112-bit2048-bitMinimum for new applications
128-bit3072-bitRecommended
192-bit7680-bitHigh security (slow)
256-bit15360-bitMaximum 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 ...
}

See Also

  • ECIES - Elliptic curve integrated encryption (recommended)
  • RSA - RSA encryption
  • ECDH - Elliptic curve Diffie-Hellman