Fastest 5KB JS implementation of ed25519 EDDSA signatures compliant with RFC8032, FIPS 186-5 & ZIP215
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
RFC8032-compliant signing and verification operations with both async and sync variants. Supports ZIP215 consensus-friendly verification and provides strong unforgeability under chosen message attacks (SUF-CMA).
Creates ed25519 signatures for messages using a secret key, following RFC8032 section 5.1.6.
/**
* Sign a message asynchronously using ed25519
* @param message - Message to sign as Uint8Array
* @param secretKey - 32-byte secret key
* @returns Promise resolving to 64-byte signature
*/
function signAsync(message: Bytes, secretKey: Bytes): Promise<Bytes>;
/**
* Sign a message synchronously using ed25519 (requires hashes.sha512 to be set)
* @param message - Message to sign as Uint8Array
* @param secretKey - 32-byte secret key
* @returns 64-byte signature
*/
function sign(message: Bytes, secretKey: Bytes): Bytes;Usage Examples:
import * as ed from '@noble/ed25519';
// Generate keys
const { secretKey, publicKey } = await ed.keygenAsync();
// Sign a message (async)
const message = new TextEncoder().encode('Hello, World!');
const signature = await ed.signAsync(message, secretKey);
console.log(signature.length); // 64
// Sign with raw bytes
const rawMessage = new Uint8Array([1, 2, 3, 4, 5]);
const rawSignature = await ed.signAsync(rawMessage, secretKey);
// Sync version (requires setting hash function first)
import { sha512 } from '@noble/hashes/sha512';
ed.hashes.sha512 = sha512;
const syncSignature = ed.sign(message, secretKey);Verifies ed25519 signatures against messages and public keys, following RFC8032 section 5.1.7 with optional ZIP215 compliance.
/**
* Verify a signature asynchronously
* @param signature - 64-byte signature to verify
* @param message - Original message that was signed
* @param publicKey - 32-byte public key
* @param opts - Verification options
* @returns Promise resolving to true if signature is valid
*/
function verifyAsync(
signature: Bytes,
message: Bytes,
publicKey: Bytes,
opts?: EdDSAVerifyOpts
): Promise<boolean>;
/**
* Verify a signature synchronously (requires hashes.sha512 to be set)
* @param signature - 64-byte signature to verify
* @param message - Original message that was signed
* @param publicKey - 32-byte public key
* @param opts - Verification options
* @returns True if signature is valid
*/
function verify(
signature: Bytes,
message: Bytes,
publicKey: Bytes,
opts?: EdDSAVerifyOpts
): boolean;
/**
* Verification options for signature validation
*/
type EdDSAVerifyOpts = {
/** Enable ZIP215 compliance for consensus-friendly verification (default: true) */
zip215?: boolean
};Usage Examples:
import * as ed from '@noble/ed25519';
// Complete signing and verification flow
const { secretKey, publicKey } = await ed.keygenAsync();
const message = new TextEncoder().encode('Important message');
const signature = await ed.signAsync(message, secretKey);
// Verify signature (async)
const isValid = await ed.verifyAsync(signature, message, publicKey);
console.log(isValid); // true
// Verify with ZIP215 disabled (strict RFC8032 compliance)
const strictValid = await ed.verifyAsync(signature, message, publicKey, { zip215: false });
// Invalid signature verification
const tampered = new Uint8Array(signature);
tampered[0] = tampered[0] ^ 1; // Flip one bit
const invalidResult = await ed.verifyAsync(tampered, message, publicKey);
console.log(invalidResult); // false
// Sync version
import { sha512 } from '@noble/hashes/sha512';
ed.hashes.sha512 = sha512;
const syncValid = ed.verify(signature, message, publicKey);The library supports different verification compliance modes:
ZIP215 Mode (Default)
RFC8032 Strict Mode
{ zip215: false } in verification optionsUsage Example:
import * as ed from '@noble/ed25519';
const { secretKey, publicKey } = await ed.keygenAsync();
const message = new TextEncoder().encode('test message');
const signature = await ed.signAsync(message, secretKey);
// ZIP215 mode (default, consensus-friendly)
const zip215Valid = await ed.verifyAsync(signature, message, publicKey);
const zip215Explicit = await ed.verifyAsync(signature, message, publicKey, { zip215: true });
// RFC8032 strict mode
const strictValid = await ed.verifyAsync(signature, message, publicKey, { zip215: false });
console.log(zip215Valid, zip215Explicit, strictValid); // All should be true for valid signaturesThe signing and verification functions throw errors for invalid inputs:
import * as ed from '@noble/ed25519';
try {
// This will throw - invalid key length
const invalidKey = new Uint8Array(31); // Should be 32
await ed.signAsync(new Uint8Array(10), invalidKey);
} catch (error) {
console.log('Invalid key length error');
}type Bytes = Uint8Array;
type EdDSAVerifyOpts = { zip215?: boolean };