CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-noble--ed25519

Fastest 5KB JS implementation of ed25519 EDDSA signatures compliant with RFC8032, FIPS 186-5 & ZIP215

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

signatures.mddocs/

Digital Signatures

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).

Capabilities

Message Signing

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);

Signature Verification

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);

Compliance Modes

The library supports different verification compliance modes:

ZIP215 Mode (Default)

  • Consensus-friendly verification
  • Accepts non-canonical point encodings
  • Suitable for blockchain and consensus applications
  • Matches behavior expected by many crypto protocols

RFC8032 Strict Mode

  • Strict compliance with RFC8032 specification
  • Rejects non-canonical point encodings
  • More restrictive validation
  • Use { zip215: false } in verification options

Usage 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 signatures

Error Handling

The signing and verification functions throw errors for invalid inputs:

  • Invalid secret key length (must be 32 bytes)
  • Invalid public key length (must be 32 bytes)
  • Invalid signature length (must be 64 bytes)
  • Invalid public key point (not on curve)
  • Missing hash function for sync operations
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');
}

Types

type Bytes = Uint8Array;
type EdDSAVerifyOpts = { zip215?: boolean };

docs

index.md

key-management.md

point-operations.md

signatures.md

utilities.md

tile.json