Skip to main content

Try it Live

Run Ed25519 examples in the interactive playground
This page is a placeholder. All examples on this page are currently AI-generated and are not correct. This documentation will be completed in the future with accurate, tested examples.

Overview

Ed25519 is a modern elliptic curve signature scheme using the Edwards curve form of Curve25519. It provides high security (128-bit security level) with excellent performance and simple implementation. Curve: Edwards curve y² + x² = 1 + dx²y² over prime field 2²⁵⁵ - 19 Key features:
  • Deterministic: No random nonce needed (unlike ECDSA)
  • Fast: Faster than secp256k1 for both signing and verification
  • Simple: No malleability, no special cases, straightforward implementation
  • Secure: Designed to resist timing attacks and side-channel analysis
Modern usage: SSH (RFC 8709), TLS 1.3, Signal Protocol, WireGuard, Tor, Zcash, Monero, Stellar, and many cryptocurrency wallets.

Quick Start

import * as Ed25519 from '@tevm/voltaire/Ed25519';
import * as Hex from '@tevm/voltaire/Hex';

// Generate keypair from seed
const seed = Hex('0x9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60');
const keypair = Ed25519.keypairFromSeed(seed);

// Sign a message (any length)
const message = new TextEncoder().encode('Hello, Ed25519!');
const signature = Ed25519.sign(message, keypair.secretKey);

// Verify signature
const isValid = Ed25519.verify(signature, message, keypair.publicKey);

API Reference

Key Generation

keypairFromSeed(seed)

Generate deterministic Ed25519 keypair from a 32-byte seed. Parameters:
  • seed (Uint8Array) - 32-byte seed for deterministic generation
Returns: { secretKey: Uint8Array, publicKey: Uint8Array }
  • secretKey - 32-byte secret key (same as seed in Ed25519)
  • publicKey - 32-byte public key
Throws:
  • InvalidSeedError - Seed wrong length
import * as Hex from '@tevm/voltaire/Hex';

const seed = Hex('0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef');
const { secretKey, publicKey } = Ed25519.keypairFromSeed(seed);

derivePublicKey(secretKey)

Derive public key from secret key. Parameters:
  • secretKey (Uint8Array) - 32-byte secret key
Returns: Uint8Array - 32-byte public key Throws:
  • InvalidSecretKeyError - Secret key wrong length
const publicKey = Ed25519.derivePublicKey(secretKey);

Signing

sign(message, secretKey)

Sign a message with Ed25519 secret key. Message can be any length. Parameters:
  • message (Uint8Array) - Message to sign (any length)
  • secretKey (Uint8Array) - 32-byte secret key
Returns: Uint8Array - 64-byte signature Throws:
  • InvalidSecretKeyError - Secret key invalid
  • Ed25519Error - Signing failed
const message = new TextEncoder().encode('Sign this message');
const signature = Ed25519.sign(message, secretKey);
console.log(signature.length); // 64

Verification

verify(signature, message, publicKey)

Verify an Ed25519 signature. Parameters:
  • signature (Uint8Array) - 64-byte signature to verify
  • message (Uint8Array) - Original message that was signed
  • publicKey (Uint8Array) - 32-byte public key
Returns: boolean - true if signature is valid, false otherwise Throws:
  • InvalidPublicKeyError - Public key format invalid
  • InvalidSignatureError - Signature format invalid
const valid = Ed25519.verify(signature, message, publicKey);
if (valid) {
  console.log('Signature verified!');
}

Validation

validateSecretKey(secretKey)

Check if a byte array is a valid Ed25519 secret key. Parameters:
  • secretKey (Uint8Array) - Candidate secret key
Returns: boolean - true if valid (32 bytes)
if (Ed25519.validateSecretKey(secretKey)) {
  // Safe to use
}

validatePublicKey(publicKey)

Check if a byte array is a valid Ed25519 public key. Parameters:
  • publicKey (Uint8Array) - Candidate public key
Returns: boolean - true if valid (32 bytes, point on curve)
if (Ed25519.validatePublicKey(publicKey)) {
  // Valid point on curve
}

validateSeed(seed)

Check if a byte array is a valid seed. Parameters:
  • seed (Uint8Array) - Candidate seed
Returns: boolean - true if valid (32 bytes)
if (Ed25519.validateSeed(seed)) {
  // Can generate keypair
}

Constants

Ed25519.SECRET_KEY_SIZE  // 32 bytes
Ed25519.PUBLIC_KEY_SIZE  // 32 bytes
Ed25519.SIGNATURE_SIZE   // 64 bytes
Ed25519.SEED_SIZE        // 32 bytes

Security Considerations

Advantages over ECDSA (secp256k1)

No nonce generation: Ed25519 is deterministic. The same message and key always produce the same signature, eliminating the catastrophic nonce reuse vulnerability in ECDSA. No malleability: Signatures cannot be modified to create alternative valid signatures (unlike ECDSA which requires low-s normalization). Simpler implementation: Fewer edge cases and special conditions reduce attack surface. Better performance: Typically 2-3x faster than secp256k1 for signing and verification. Built-in security: Designed from the ground up to resist timing attacks and side-channel analysis.

Critical Warnings

⚠️ Protect secret keys: Ed25519 secret keys are 32-byte seeds. If compromised, all signatures can be forged. ⚠️ Validate public keys: Always validate public keys before use to ensure they are valid curve points. ⚠️ Use cryptographically secure random: Never use Math.random() for seed generation. Use crypto.getRandomValues(). ⚠️ Message length: Unlike ECDSA which signs 32-byte hashes, Ed25519 signs the actual message. For very large messages, consider hashing first (but this is not required).

TypeScript Implementation

The TypeScript implementation uses @noble/curves/ed25519 by Paul Miller:
  • Constant-time operations
  • Compliant with RFC 8032
  • Multiple security audits
  • Widely used in production (SSH, Signal, cryptocurrency)
  • ~15KB minified

Test Vectors

RFC 8032 Test Vectors

// Test vector 1 from RFC 8032
const seed1 = new Uint8Array([
  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,
]);

const { secretKey, publicKey } = Ed25519.keypairFromSeed(seed1);

// Expected public key
const expectedPublicKey = new Uint8Array([
  0xd7, 0x5a, 0x98, 0x01, 0x82, 0xb1, 0x0a, 0xb7,
  0xd5, 0x4b, 0xfe, 0xd3, 0xc9, 0x64, 0x07, 0x3a,
  0x0e, 0xe1, 0x72, 0xf3, 0xda, 0xa6, 0x23, 0x25,
  0xaf, 0x02, 0x1a, 0x68, 0xf7, 0x07, 0x51, 0x1a,
]);

assert(publicKey.every((byte, i) => byte === expectedPublicKey[i]));

// Sign empty message
const message = new Uint8Array(0);
const signature = Ed25519.sign(message, secretKey);

// Expected signature
const expectedSignature = new Uint8Array([
  0xe5, 0x56, 0x43, 0x00, 0xc3, 0x60, 0xac, 0x72,
  0x90, 0x86, 0xe2, 0xcc, 0x80, 0x6e, 0x82, 0x8a,
  0x84, 0x87, 0x7f, 0x1e, 0xb8, 0xe5, 0xd9, 0x74,
  0xd8, 0x73, 0xe0, 0x65, 0x22, 0x49, 0x01, 0x55,
  0x5f, 0xb8, 0x82, 0x15, 0x90, 0xa3, 0x3b, 0xac,
  0xc6, 0x1e, 0x39, 0x70, 0x1c, 0xf9, 0xb4, 0x6b,
  0xd2, 0x5b, 0xf5, 0xf0, 0x59, 0x5b, 0xbe, 0x24,
  0x65, 0x51, 0x41, 0x43, 0x8e, 0x7a, 0x10, 0x0b,
]);

assert(signature.every((byte, i) => byte === expectedSignature[i]));

// Verify signature
assert(Ed25519.verify(signature, message, publicKey));

Deterministic Signatures

import * as Hex from '@tevm/voltaire/Hex';

// Ed25519 is deterministic - same message + key = same signature
const seed = Hex('0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890');
const { secretKey, publicKey } = Ed25519.keypairFromSeed(seed);

const message = new TextEncoder().encode('test');

const sig1 = Ed25519.sign(message, secretKey);
const sig2 = Ed25519.sign(message, secretKey);

// Identical signatures
assert(sig1.every((byte, i) => byte === sig2[i]));

Message Length Flexibility

import * as Hex from '@tevm/voltaire/Hex';

const seed = Hex('0xfedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321');
const { secretKey, publicKey } = Ed25519.keypairFromSeed(seed);

// Empty message
const emptyMsg = Hex('0x');
const sig0 = Ed25519.sign(emptyMsg, secretKey);
assert(Ed25519.verify(sig0, emptyMsg, publicKey));

// Short message
const msg1 = new TextEncoder().encode('Hi');
const sig1 = Ed25519.sign(msg1, secretKey);
assert(Ed25519.verify(sig1, msg1, publicKey));

// Long message (1 MB) - for illustration, represented as hex
const largeData = new TextEncoder().encode('a'.repeat(1024 * 1024));
const sig2 = Ed25519.sign(largeData, secretKey);
assert(Ed25519.verify(sig2, largeData, publicKey));

Implementation Details

TypeScript

Library: @noble/curves/ed25519 by Paul Miller
  • Audit status: Security audited, production-ready
  • Standard: RFC 8032 compliant
  • Features: Constant-time, batch verification, cofactor handling
  • Size: ~15KB minified (tree-shakeable)
  • Performance: 2-3x faster than secp256k1
Key design choices:
  • Uses twisted Edwards curve internally
  • Point compression for compact public keys (32 bytes)
  • Deterministic signature generation (no randomness needed)
  • Built-in validation and security checks

Zig

Implementation: Will use std.crypto.sign.Ed25519 from Zig standard library
  • Status: Future FFI support planned
  • Features: Constant-time, RFC 8032 compliant
  • Audit: Part of Zig standard library
Currently only available through TypeScript/WASM interface.

WASM

Ed25519 operations available in WASM builds:
  • ReleaseSmall: Size-optimized (~15KB)
  • ReleaseFast: Performance-optimized
import { Ed25519 } from '@tevm/voltaire/Ed25519';
// Automatically uses WASM in supported environments

Ethereum Context

Ed25519 is not used in Ethereum’s core protocol (which uses secp256k1), but it appears in:

Layer 2 and Rollups

  • StarkNet: Uses Ed25519 for account signatures
  • zkSync: Optional Ed25519 support for certain operations
  • Optimistic Rollups: Some use Ed25519 for off-chain aggregation

Modern Web3 Applications

  • Solana integration: Solana uses Ed25519, so cross-chain apps benefit
  • Decentralized identity: DIDs often use Ed25519 for key management
  • Encrypted communication: Signal Protocol with Ethereum accounts

Future EVM Integration

  • EIP-665: Proposed Ed25519 signature verification precompile (draft)
  • Account abstraction: ED25519 keys for smart contract wallets
  • Hardware wallets: Secure Enclave and TEE support

Ed25519 vs Secp256k1

FeatureEd25519Secp256k1
Security Level128-bit128-bit
Key Size32 bytes32 bytes (private)
Public Key32 bytes (compressed)64 bytes (uncompressed)
Signature Size64 bytes64 bytes (r,s) + 1 byte (v)
DeterministicYes (built-in)Yes (RFC 6979)
MalleabilityNoYes (requires low-s)
PerformanceFaster (2-3x)Slower
Nonce IssuesNoneCritical (ECDSA)
Ethereum SupportNo (L2 only)Yes (core)
Modern AdoptionHighMedium
When to use Ed25519:
  • New protocols and applications
  • High-performance requirements
  • Simplified security model
  • Cross-chain with Solana, Stellar, etc.
  • SSH, TLS, or other modern protocols
When to use Secp256k1:
  • Ethereum transaction signing (required)
  • Bitcoin compatibility
  • EVM precompile support (ecRecover)
  • Address derivation from signatures