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 useSignStream/VerifyStreamon 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 signmessageLen- Message lengthsignature- 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 signedmessageLen- Message lengthsignature- 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)
| Operation | Time (µs) | Notes |
|---|---|---|
| Key generation | 40-80 | One-time |
| Sign | 50-100 | Very fast |
| Verify | 100-150 | Fast |
| RSA-2048 sign | 1000-2000 | 10-20x slower |
| RSA-2048 verify | 50-100 | Similar to Ed25519 |
Ed25519 is 10-20x faster than RSA-2048 for signing.
Signature Size
| Algorithm | Signature Size | Public Key Size |
|---|---|---|
| Ed25519 | 64 bytes | 32 bytes |
| RSA-2048 | 256 bytes | 256 bytes |
| ECDSA P-256 | 64 bytes | 64 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++.
- For very large files, either:
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.
- When loading keys that weren’t generated locally (files, config, user input), validate them before use using the underlying key objects’
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
SecByteBlockor similar secure containers, - restrict file and backup access,
- rotate keys if they’ve been exposed in untrusted environments.
- keep them in
- Treat Ed25519 private keys as long-term, high-value secrets:
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:
- Digital Signatures - Sign documents, software, certificates
- Authentication - SSH keys, API authentication
- Code Signing - Software releases, firmware
- Email Signing - PGP/GPG replacement
- Blockchain - Cryptocurrency transactions
- Git Commits - Commit signing
❌ Don’t use Ed25519 for:
- Key Exchange - Use X25519 instead
- Encryption - Signatures don’t encrypt
- Legacy Systems - Use RSA if required for compatibility
Ed25519 vs RSA vs ECDSA
| Feature | Ed25519 | RSA-2048 | ECDSA P-256 |
|---|---|---|---|
| Sign speed | ⚡⚡⚡⚡⚡ | ⚡ | ⚡⚡⚡ |
| Verify speed | ⚡⚡⚡⚡ | ⚡⚡⚡⚡ | ⚡⚡⚡ |
| Key size | 32 bytes | 256 bytes | 64 bytes |
| Signature size | 64 bytes | 256 bytes | 64 bytes |
| Security | 128-bit | 112-bit | 128-bit |
| Recommended | ⭐ Yes | Legacy only | Acceptable |
Exceptions
InvalidDataFormat- Invalid key or signature format
See Also
- X25519 - Key exchange
- RSA - Legacy signatures
- Public-Key Cryptography - Overview
- Security Concepts - Understanding signatures