Ed25519

Header: #include <cryptopp/xed25519.h> | Namespace: CryptoPP Since: Crypto++ 8.0 Thread Safety: Not thread-safe per instance; use separate instances per thread

Ed25519 is a modern digital signature algorithm built on Curve25519. It provides fast signature generation and verification, deterministic signatures, and strong security guarantees. Used in SSH, GPG, cryptocurrencies, and secure messaging.

Quick Example

#include <cryptopp/xed25519.h>
#include <cryptopp/osrng.h>
#include <cryptopp/filters.h>
#include <cryptopp/cryptlib.h>  // NullRNG
#include <iostream>

int main() {
    using namespace CryptoPP;

    AutoSeededRandomPool rng;

    // Generate key pair
    ed25519::Signer signer(rng);
    ed25519::Verifier verifier(signer);

    // Sign message (Ed25519 is deterministic, no RNG needed after keygen)
    std::string message = "Hello, World!";
    std::string signature;

    StringSource(message, true,
        new SignerFilter(NullRNG(), signer,
            new StringSink(signature)
        )
    );

    // Verify signature
    bool valid = false;
    StringSource(signature + message, true,
        new SignatureVerificationFilter(verifier,
            new ArraySink((byte*)&valid, sizeof(valid))
        )
    );

    std::cout << "Signature valid: " << (valid ? "YES" : "NO") << std::endl;

    return 0;
}

Usage Guidelines

ℹ️

Do:

  • Use Ed25519 for digital signatures (recommended over RSA/ECDSA)
  • Use for software signing, document signing, authentication
  • Store private keys securely using SecByteBlock
  • Verify signatures before trusting signed data
  • Use deterministic signatures (no RNG needed after key generation)

Avoid:

  • Using RSA for new applications (Ed25519 is faster and simpler)
  • Sharing private keys across systems
  • Signing very large files directly via StringSource/SignerFilter—for multi-GB data, sign a hash (e.g., SHA-256) or use SignStream/VerifyStream on a seekable stream
  • Using for key exchange (use X25519 instead)

Class: ed25519::Signer

Sign messages with Ed25519 private key.

Constants

static const int SECRET_KEYLENGTH = 32;   // Private key (32 bytes)
static const int PUBLIC_KEYLENGTH = 32;   // Public key (32 bytes)
static const int SIGNATURE_LENGTH = 64;   // Signature (64 bytes)

Constructors

Constructor with RNG

ed25519::Signer(RandomNumberGenerator& rng);

Generate new signing key pair.

Example:

AutoSeededRandomPool rng;
ed25519::Signer signer(rng);

Constructor with Private Key

ed25519::Signer(const byte* secretKey);

Load existing private key (32 bytes).

Example:

byte privateKey[32];
// ... load key ...
ed25519::Signer signer(privateKey);

Methods

SignMessage()

size_t SignMessage(RandomNumberGenerator& rng,
                   const byte* message, size_t messageLen,
                   byte* signature) const;

Sign a message.

Parameters:

  • rng - Random number generator (not used, can be NullRNG)
  • message - Message to sign
  • messageLen - Message length
  • signature - Output buffer (64 bytes)

Returns: Signature length (always 64)

Example:

ed25519::Signer signer(rng);

std::string message = "Sign this";
byte signature[ed25519::Signer::SIGNATURE_LENGTH];

signer.SignMessage(NullRNG(),
    (const byte*)message.data(), message.size(),
    signature);

GetPrivateKey()

void GetPrivateKey(byte* secretKey) const;

Export private key.

Parameters:

  • secretKey - Output buffer (32 bytes)

Example:

SecByteBlock privateKey(32);
signer.GetPrivateKey(privateKey);

// Save securely...

GetPublicKey()

void GetPublicKey(byte* publicKey) const;

Export public key.

Parameters:

  • publicKey - Output buffer (32 bytes)

Example:

byte publicKey[32];
signer.GetPublicKey(publicKey);

// Share publicly...

Class: ed25519::Verifier

Verify Ed25519 signatures.

Constructors

Constructor with Public Key

ed25519::Verifier(const byte* publicKey);

Load public key for verification.

Example:

byte publicKey[32];
// ... receive from sender ...
ed25519::Verifier verifier(publicKey);

Constructor from Signer

ed25519::Verifier(const ed25519::Signer& signer);

Extract public key from signer.

Example:

ed25519::Signer signer(rng);
ed25519::Verifier verifier(signer);

Methods

VerifyMessage()

bool VerifyMessage(const byte* message, size_t messageLen,
                   const byte* signature, size_t signatureLen) const;

Verify signature on message.

Parameters:

  • message - Message that was signed
  • messageLen - Message length
  • signature - Signature to verify (64 bytes)
  • signatureLen - Signature length (must be 64)

Returns: true if valid, false if invalid

Example:

ed25519::Verifier verifier(publicKey);

bool valid = verifier.VerifyMessage(
    (const byte*)message.data(), message.size(),
    signature, 64
);

if (!valid) {
    std::cerr << "Invalid signature!" << std::endl;
}

Complete Example: Sign and Verify File

This example is suitable for small–medium files. For very large artefacts (multi-GB ISOs, backups, etc.), prefer SignStream/VerifyStream on a seekable std::ifstream, or sign a hash of the file as shown in the next example.

#include <cryptopp/xed25519.h>
#include <cryptopp/osrng.h>
#include <cryptopp/files.h>
#include <cryptopp/filters.h>
#include <iostream>

using namespace CryptoPP;

void signFile(const std::string& filename,
              const ed25519::Signer& signer) {
    // Read file
    std::string message;
    FileSource(filename.c_str(), true,
        new StringSink(message)
    );

    // Sign
    std::string signature;
    StringSource(message, true,
        new SignerFilter(NullRNG(), signer,
            new StringSink(signature)
        )
    );

    // Save signature
    FileSink(std::string(filename + ".sig").c_str())
        .Put((const byte*)signature.data(), signature.size());

    std::cout << "File signed: " << filename << std::endl;
}

bool verifyFile(const std::string& filename,
                const ed25519::Verifier& verifier) {
    // Read file
    std::string message;
    FileSource(filename.c_str(), true,
        new StringSink(message)
    );

    // Read signature
    std::string signature;
    FileSource(std::string(filename + ".sig").c_str(), true,
        new StringSink(signature)
    );

    // Verify
    bool valid = false;
    StringSource(signature + message, true,
        new SignatureVerificationFilter(verifier,
            new ArraySink((byte*)&valid, sizeof(valid))
        )
    );

    return valid;
}

int main() {
    AutoSeededRandomPool rng;

    // Generate keys
    ed25519::Signer signer(rng);
    ed25519::Verifier verifier(signer);

    // Sign file
    signFile("document.pdf", signer);

    // Verify file
    if (verifyFile("document.pdf", verifier)) {
        std::cout << "Signature verified!" << std::endl;
    } else {
        std::cout << "Invalid signature!" << std::endl;
    }

    return 0;
}

Complete Example: Software Release Signing

#include <cryptopp/xed25519.h>
#include <cryptopp/osrng.h>
#include <cryptopp/sha.h>
#include <cryptopp/hex.h>
#include <iostream>

using namespace CryptoPP;

// Sign hash of large file (not entire file)
std::string signFileHash(const std::string& filename,
                         const ed25519::Signer& signer) {
    // Hash file
    SHA256 hash;
    std::string digest;
    FileSource(filename.c_str(), true,
        new HashFilter(hash,
            new StringSink(digest)
        )
    );

    // Sign hash
    byte signature[64];
    signer.SignMessage(NullRNG(),
        (const byte*)digest.data(), digest.size(),
        signature);

    // Return as hex
    std::string hexSig;
    StringSource(signature, 64, true,
        new HexEncoder(new StringSink(hexSig))
    );

    return hexSig;
}

int main() {
    AutoSeededRandomPool rng;
    ed25519::Signer signer(rng);

    // Sign release
    std::string signature = signFileHash("myapp-v1.0.zip", signer);

    // Publish signature
    std::ofstream out("myapp-v1.0.zip.sig");
    out << signature;

    std::cout << "Release signed!" << std::endl;

    return 0;
}

Performance

Benchmarks (Approximate)

OperationTime (µs)Notes
Key generation40-80One-time
Sign50-100Very fast
Verify100-150Fast
RSA-2048 sign1000-200010-20x slower
RSA-2048 verify50-100Similar to Ed25519

Ed25519 is 10-20x faster than RSA-2048 for signing.

Signature Size

AlgorithmSignature SizePublic Key Size
Ed2551964 bytes32 bytes
RSA-2048256 bytes256 bytes
ECDSA P-25664 bytes64 bytes

Security

Security Properties

  • Security level: ~128-bit (comparable to AES-128)
  • Private key: 256 bits (32 bytes)
  • Public key: 256 bits (32 bytes)
  • Signature: 512 bits (64 bytes, r || s)
  • Deterministic: Signing the same message with the same key always yields the same signature
  • Standard: Ed25519 as specified in RFC 8032 (“pure” EdDSA over Curve25519)

Security Notes & Best Practices

  • Large data

    • For very large files, either:
      • sign a hash of the file (e.g., a SHA-256 digest), or
      • use ed25519::Signer::SignStream() / ed25519::Verifier::VerifyStream() on a seekable stream.
    • This avoids buffering multi-GB inputs in memory and matches the two-pass nature of Ed25519 in Crypto++.
  • Key validation

    • When loading keys that weren’t generated locally (files, config, user input), validate them before use using the underlying key objects’ Validate(rng, level) methods, especially at higher levels for private keys.
  • Key separation

    • Use distinct keys for distinct roles (e.g., one key for software releases, another for SSH, another for email).
    • This limits the blast radius of a compromise and avoids subtle cross-protocol issues from reusing a single identity key everywhere.
  • Key handling

    • Treat Ed25519 private keys as long-term, high-value secrets:
      • keep them in SecByteBlock or similar secure containers,
      • restrict file and backup access,
      • rotate keys if they’ve been exposed in untrusted environments.
  • No key exchange / encryption

    • Ed25519 is a signature scheme only. Don’t use it directly for key agreement or encryption.
    • Use X25519 for key exchange and a symmetric AEAD (AES-GCM or ChaCha20-Poly1305) for confidentiality.

Test Vectors (RFC 8032)

// Test Vector 1
byte secretKey[] = {
    0x9d, 0x61, 0xb1, 0x9d, 0xef, 0xfd, 0x5a, 0x60,
    0xba, 0x84, 0x4a, 0xf4, 0x92, 0xec, 0x2c, 0xc4,
    0x44, 0x49, 0xc5, 0x69, 0x7b, 0x32, 0x69, 0x19,
    0x70, 0x3b, 0xac, 0x03, 0x1c, 0xae, 0x7f, 0x60
};

byte message[] = { };  // Empty message

// Expected signature: e5564300c360ac729086e2cc806e828a...

Thread Safety

Not thread-safe. Use separate instances per thread.

When to Use Ed25519

✅ Use Ed25519 for:

  1. Digital Signatures - Sign documents, software, certificates
  2. Authentication - SSH keys, API authentication
  3. Code Signing - Software releases, firmware
  4. Email Signing - PGP/GPG replacement
  5. Blockchain - Cryptocurrency transactions
  6. Git Commits - Commit signing

❌ Don’t use Ed25519 for:

  1. Key Exchange - Use X25519 instead
  2. Encryption - Signatures don’t encrypt
  3. Legacy Systems - Use RSA if required for compatibility

Ed25519 vs RSA vs ECDSA

FeatureEd25519RSA-2048ECDSA P-256
Sign speed⚡⚡⚡⚡⚡⚡⚡⚡
Verify speed⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡
Key size32 bytes256 bytes64 bytes
Signature size64 bytes256 bytes64 bytes
Security128-bit112-bit128-bit
Recommended⭐ YesLegacy onlyAcceptable

Exceptions

  • InvalidDataFormat - Invalid key or signature format

See Also