Source: secp256k1.zig • secp256k1.wasm.tsTests: secp256k1.test.ts • secp256k1.wasm.test.ts
Try it Live
Run Secp256k1 examples in the interactive playground
Overview
Secp256k1 is an elliptic curve digital signature algorithm (ECDSA) over the secp256k1 curve, providing asymmetric cryptography for transaction authentication. Mainnet-critical algorithm - Primary signature scheme for Ethereum transactions, message signing, and public key recovery from signatures. Curve equation: y² = x³ + 7 (mod p) Parameters:- Prime field:
p = 2²⁵⁶ - 2³² - 977 - Curve order:
n = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - Generator point G with coordinates (Gx, Gy)
- sign(hash, privateKey) → signature - Create ECDSA signature with deterministic nonce (RFC 6979)
- verify(signature, hash, publicKey) → boolean - Validate signature authenticity
- recoverPublicKey(signature, hash) → publicKey - Recover signer’s public key (Ethereum’s ecRecover)
- derivePublicKey(privateKey) → publicKey - Elliptic curve point multiplication (privateKey * G)
Quick Start
Examples
Try all examples in the Live Playground The playground includes examples for:- Generate random private key and derive public key
- Sign Keccak256 hash with ECDSA
- Verify signature with public key
- Recover public key from signature (Ethereum’s ecRecover)
- 65-byte compact signature format (r+s+v)
- Serialize/deserialize signatures
- Elliptic Curve Diffie-Hellman key exchange
- Private/public key validation
- Elliptic curve point operations
- Sign Ethereum transactions
API Reference
Signing
sign(messageHash, privateKey)
Sign a 32-byte message hash with a private key using deterministic ECDSA (RFC 6979).
Parameters:
messageHash(HashType) - 32-byte Keccak256 hash to signprivateKey(Uint8Array) - 32-byte private key (must be > 0 and < curve order)
BrandedSignature with components:
r(Uint8Array) - 32-byte signature components(Uint8Array) - 32-byte signature component (low-s enforced)v(number) - Recovery ID (27 or 28 for Ethereum compatibility)
InvalidPrivateKeyError- Private key invalid (wrong length, zero, or >= curve order)Secp256k1Error- Signing operation failed
Verification
verify(signature, messageHash, publicKey)
Verify an ECDSA signature against a message hash and public key.
Parameters:
signature(BrandedSignature) - Signature with r, s, v componentsmessageHash(HashType) - 32-byte message hash that was signedpublicKey(Uint8Array) - 64-byte uncompressed public key (x || y coordinates)
boolean - true if signature is valid, false otherwise
Throws:
InvalidPublicKeyError- Public key wrong lengthInvalidSignatureError- Signature components wrong length
recoverPublicKey(signature, messageHash)
Recover the public key from a signature and message hash. This is the core of Ethereum’s ecRecover precompile.
Parameters:
signature(BrandedSignature) - Signature with r, s, v componentsmessageHash(HashType) - 32-byte message hash that was signed
Uint8Array - 64-byte uncompressed public key
Throws:
InvalidSignatureError- Invalid signature format or recovery failed
Key Management
randomPrivateKey()
Generate a cryptographically secure random private key.
Returns: Uint8Array - 32-byte random private key (guaranteed valid: > 0 and < curve order)
Throws:
Secp256k1Error- If CSPRNG fails or generates invalid key after retries
createKeyPair()
Generate a new random key pair (private key + public key).
Returns: { privateKey: Uint8Array, publicKey: Uint8Array } - 32-byte private key and 64-byte public key
derivePublicKey(privateKey)
Derive the public key from a private key using elliptic curve point multiplication (private_key * G).
Parameters:
privateKey(Uint8Array) - 32-byte private key
Uint8Array - 64-byte uncompressed public key
Throws:
InvalidPrivateKeyError- Invalid private key
isValidPrivateKey(privateKey)
Check if a byte array is a valid secp256k1 private key.
Parameters:
privateKey(Uint8Array) - Candidate private key
boolean - true if valid (32 bytes, > 0, < curve order)
isValidPublicKey(publicKey)
Check if a byte array is a valid secp256k1 public key.
Parameters:
publicKey(Uint8Array) - Candidate public key
boolean - true if valid (64 bytes, point on curve)
isValidSignature(signature)
Check if a signature has valid r, s, v components.
Parameters:
signature(BrandedSignature) - Candidate signature
boolean - true if valid
Constants
Error Handling
All secp256k1 functions throw typed errors that extendCryptoError:
Error Types
| Error | When Thrown |
|---|---|
Secp256k1Error | Base error for all secp256k1 operations |
InvalidPrivateKeyError | Invalid private key (wrong length, zero, or >= curve order) |
InvalidPublicKeyError | Invalid public key (wrong length or not on curve) |
InvalidSignatureError | Invalid signature (wrong component lengths, invalid v value, recovery failed) |
Error Properties
All errors include:name- Error class name (e.g.,"InvalidPrivateKeyError")message- Human-readable descriptioncode- Machine-readable error code (e.g.,"SECP256K1_INVALID_PRIVATE_KEY")docsPath- Link to relevant documentationcause- Original error if wrapping another errorcontext- Additional context (e.g.,{ privateKeyLength: 31 })
Security Considerations
Critical Warnings
⚠️ Signatures must be validated: Verify r and s are in valid range [1, n-1] where n is curve order. Invalid signature components can leak information or cause verification failures. ⚠️ Deterministic nonces prevent reuse attacks: RFC 6979 deterministic signatures eliminate nonce reuse vulnerability. Reusing a nonce with different messages leaks the private key - never implement custom nonce generation. ⚠️ Recovery ID (v parameter) for public key recovery: The v parameter (27 or 28 in Ethereum) indicates which of two possible public keys to recover from a signature. Critical for ecRecover precompile. ⚠️ Low-s enforcement: Signatures automatically use low-s values (s ≤ n/2) to prevent malleability. Both high-s and low-s signatures verify successfully, but Ethereum requires low-s. ⚠️ Use cryptographically secure random: Never useMath.random() for private key generation. Use crypto.getRandomValues() or similar CSPRNG.
Performance
Native Zig implementation provides 2-5x speedup over pure JavaScript on cryptographic operations, with negligible overhead for FFI calls.Test Vectors
RFC 6979 Deterministic Signatures
Signature Recovery
Edge Cases
Implementation Details
Voltaire provides three secp256k1 implementations for different use cases:Reference Implementation (Default)
Library:@noble/curves/secp256k1 by Paul Miller
- Audit status: Multiple security audits, widely used in production
- Features: Constant-time operations, RFC 6979 deterministic signing, point validation
- Size: ~20KB minified (tree-shakeable)
- Use case: Default for TypeScript/JavaScript applications, validation fallback
- 64-byte uncompressed public keys (x || y, no 0x04 prefix)
- Recovery ID v = 27 or 28 (Ethereum format)
- Low-s normalization enforced
Native Zig Implementation
Pure Zig elliptic curve arithmetic (src/crypto/secp256k1.zig - 92KB, 2682 lines)
- Status: ⚠️ UNAUDITED - Educational/testing only
- Performance: 2-5x faster than pure JavaScript
- Features: Affine point arithmetic, modular arithmetic, signature generation/verification
- Limitations: Not constant-time, unvalidated edge cases, no production guarantees
- Use case: Performance-critical operations, research, testing
WASM Implementation
Zig stdlib via wasm-loader (bundled inwasm/primitives.wasm)
- ReleaseSmall: 360KB (size-optimized for production bundles)
- ReleaseFast: 4.3MB (performance-optimized for benchmarking)
- Use case: Browser environments, sandboxed execution, consistent cross-platform behavior
- Import path:
import { Secp256k1Wasm } from '@tevm/voltaire/Secp256k1.wasm'
- Reference (@noble/curves): Default for all TypeScript applications
- Native Zig: Performance-critical paths in Node.js/Bun (when audited)
- WASM: Browser environments requiring consistent behavior
Ethereum Integration
Transaction Signing
Every Ethereum transaction is signed with secp256k1:Address Derivation
Ethereum addresses are derived from secp256k1 public keys:ecRecover Precompile
The EVM’secRecover precompile (address 0x01) uses secp256k1 signature recovery:
In-Depth Documentation
Comprehensive technical documentation:- Signing - ECDSA signing with RFC 6979 deterministic nonces
- Verification - Signature verification algorithm
- Key Derivation - Private → public key derivation
- Recovery - Public key recovery (ecRecover)
- Point Operations - Elliptic curve arithmetic
- Test Vectors - Official test vectors (RFC 6979, IETF, Ethereum)
- Security - Side-channel attacks, malleability, best practices
- Performance - Benchmarks and optimization techniques
- Usage Patterns - Transaction signing, EIP-191, EIP-712
Comparison with Other Curves
For comprehensive technical comparison with P-256 including performance, security, and use case analysis: Elliptic Curve Comparison: secp256k1 vs P-256Related
- Primitives: Signature - Generic signature type
- Keccak256 - Keccak256 hashing for message preparation
- Primitives: Address - Ethereum addresses from public keys
- Precompiles: ecRecover - EVM signature recovery
- Crypto: P256 - NIST P-256 curve (WebAuthn)
- Crypto: Ed25519 - Edwards curve signatures

