Try it Live
Run Secp256k1 examples in the interactive playground
Examples
- Sign Message - Sign Keccak256 hash with ECDSA
- Sign Transaction - Sign Ethereum transaction
- Compact Signature - 65-byte compact format
- Signature Bytes - Serialize signatures
Secp256k1 Signing
Sign message hashes with secp256k1 using deterministic ECDSA (RFC 6979). Ethereum uses secp256k1 signatures for all transaction authorization and authentication.Overview
ECDSA (Elliptic Curve Digital Signature Algorithm) signing computes a signature (r, s, v) from:- Message hash (32 bytes) - Keccak256 of transaction or message
- Private key (32 bytes) - Secret scalar (0 < key < curve order)
API
sign(messageHash, privateKey)
Sign a 32-byte message hash with deterministic ECDSA.
Parameters:
messageHash(HashType) - 32-byte hash to signprivateKey(Uint8Array) - 32-byte private key (0 < key < n)
BrandedSignature
InvalidPrivateKeyError- Key wrong length, zero, or >= curve orderSecp256k1Error- Signing operation failed
Algorithm Details
ECDSA Signature Generation
- Hash message:
e = hash(message)(typically Keccak256) - Generate nonce (RFC 6979):
k = HMAC_DRBG(private_key, message_hash)(deterministic) - Calculate point:
R = k * G(scalar multiplication of generator) - Compute r:
r = R.x mod n(x-coordinate of R) - Compute s:
s = k^-1 * (e + r * private_key) mod n - Normalize s: If
s > n/2, sets = n - s(low-s malleability fix) - Calculate v: Recovery ID (0 or 1) + 27 for Ethereum compatibility
RFC 6979 Deterministic Nonces
Why deterministic? Random nonce generation is dangerous:- Nonce reuse with different messages leaks the private key
- Weak randomness (bad RNG) enables key recovery attacks
- Implementation bugs in random generation are common
- No RNG required - Eliminates entropy source vulnerabilities
- Reproducible - Same message + key = same signature (testable)
- Secure - Nonce is cryptographically derived from secrets
- Standard - RFC 6979 widely adopted (Bitcoin, Ethereum, etc.)
Signature Components
r (32 bytes)
The x-coordinate of the ephemeral public keyR = k * G:
- Must be in range
[1, n-1]where n is curve order - Derived from deterministic nonce k
- Acts as a commitment to the nonce
s (32 bytes)
The proof that binds the signature to the private key:- Computed as
s = k^-1 * (e + r * private_key) mod n - Must be in range
[1, n-1] - Low-s enforced: If
s > n/2, signature usesn - sinstead - Low-s prevents signature malleability (BIP 62, EIP-2)
v (recovery ID)
Ethereum-specific value for public key recovery:- Standard: 0 or 1 (which of two possible y-coordinates)
- Ethereum: 27 or 28 (v = recovery_id + 27)
- EIP-155 (replay protection): v = chain_id * 2 + 35 + recovery_id
- Enables
ecRecoverprecompile to extract public key from signature
Security Considerations
Critical Warnings
⚠️ NEVER reuse nonces: Reusing k with different messages leaks the private key. RFC 6979 prevents this - do not override. ⚠️ Validate private keys: Keys must be 32 bytes and satisfy0 < key < n. Zero keys and keys >= n are invalid.
⚠️ Use cryptographically secure random: For private key generation, use crypto.getRandomValues() or similar CSPRNG. Never use Math.random().
⚠️ Protect private keys: Store keys in secure hardware (HSM, Secure Enclave) when possible. Never log or transmit unencrypted keys.
⚠️ Hash before signing: Sign hashes, not raw messages. Ethereum signs Keccak256 hashes of RLP-encoded transactions.
Low-s Malleability
ECDSA signatures have an inherent malleability: if (r, s) is valid, so is (r, n - s). Both verify correctly but produce different signature bytes. Problem: Malleability enables:- Transaction replay with modified signature
- Smart contract exploit via signature verification bypass
- Blockchain state inconsistency
Side-Channel Resistance
Timing attacks can leak private keys through:- Non-constant-time modular arithmetic - Branch timing leaks bit values
- Cache timing - Memory access patterns reveal secrets
- Power analysis - CPU power consumption correlates with operations
- TypeScript: Uses
@noble/curves(constant-time operations) - Zig: ⚠️ NOT constant-time - Custom implementation unaudited
- Production: Use audited libraries or hardware wallets
Test Vectors
RFC 6979 Deterministic Signatures
Different Messages
Edge Cases
Implementation Notes
TypeScript
Uses@noble/curves/secp256k1:
- Audit status: Multiple security audits, production-ready
- RFC 6979: Built-in deterministic nonces
- Low-s: Automatic normalization
- Constant-time: Side-channel resistant
- Size: ~20KB minified (tree-shakeable)
Zig
Custom implementation insrc/crypto/secp256k1.zig:
- ⚠️ UNAUDITED - Not security reviewed
- ⚠️ NOT constant-time - Vulnerable to timing attacks
- ⚠️ Educational only - Do not use in production
- Implements basic ECDSA with RFC 6979
Related
- Verification - Verify secp256k1 signatures
- Key Derivation - Private → public key
- Recovery - Public key recovery (ecRecover)
- Security - Side-channel attacks, malleability
- Usage Patterns - Transaction signing examples

