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
Interactive examples in the Voltaire Playground:- Generate Keypair - Generate random private key and derive public key
- Sign Message - Sign Keccak256 hash with ECDSA
- Verify Signature - Verify signature with public key
- Recover Public Key - Recover public key from signature (ecRecover)
- Compact Signature - 65-byte compact format (r+s+v)
- Signature Bytes - Serialize/deserialize signatures
- ECDH - Elliptic Curve Diffie-Hellman key exchange
- Validate Private Key - Private key validation
- Validate Public Key - Public key validation
- Validate Signature - Signature format validation
- Point Addition - Elliptic curve point addition
- Scalar Multiply - Scalar multiplication on curve
- Sign Transaction - Sign Ethereum transaction
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
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
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
Tevm 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/crypto/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

