Skip to main content

Try it Live

Run Secp256k1 examples in the interactive playground
This page is a placeholder. All examples on this page are currently AI-generated and are not correct. This documentation will be completed in the future with accurate, tested examples.

Examples

Secp256k1 Verification

Verify ECDSA signatures against public keys to authenticate messages. Signature verification is the cornerstone of Ethereum’s security model - every transaction must pass verification before execution.

Overview

ECDSA verification confirms that a signature was created by the private key corresponding to a given public key. The verifier needs:
  • Signature (r, s, v) - 65 bytes total
  • Message hash - 32 bytes (what was signed)
  • Public key - 64 bytes uncompressed (x || y coordinates)
Verification succeeds if the signature was created by the matching private key, fails otherwise. No secret information is revealed during verification - it’s safe to perform publicly.

API

verify(signature, messageHash, publicKey)

Verify an ECDSA signature against a message hash and public key. Parameters:
  • signature (BrandedSignature) - Signature with r, s, v components
  • messageHash (HashType) - 32-byte hash that was signed
  • publicKey (Uint8Array) - 64-byte uncompressed public key (x || y)
Returns: boolean
  • true - Signature is cryptographically valid
  • false - Signature is invalid or forged
Throws:
  • InvalidPublicKeyError - Public key wrong length or not on curve
  • InvalidSignatureError - Signature components wrong length
Example:

Algorithm Details

ECDSA Verification

  1. Validate inputs:
    • Check 1 ≤ r < n and 1 ≤ s < n (signature component bounds)
    • Verify public key is a valid curve point (satisfies y² = x³ + 7)
  2. Compute message hash scalar: e = hash(message) mod n
  3. Calculate inverse: s_inv = s^-1 mod n
  4. Compute point: R = (e * s_inv) * G + (r * s_inv) * public_key
    • G is the generator point
    • public_key is the signer’s public key point
  5. Verify: Check if R.x mod n == r
    • If equal, signature is valid
    • If not equal, signature is invalid or forged

Why Verification Works

The signature was created as: s = k^-1 * (e + r * private_key) mod n Rearranging: k = s^-1 * (e + r * private_key) mod n Since R = k * G and public_key = private_key * G:
This matches step 4 above. If the signature is valid, R.x == r.

Validation Checks

Signature Component Validation

Invalid components always fail verification.

Public Key Validation

Invalid public keys (not on curve) always fail verification.

Security Considerations

Malleability and Low-s

ECDSA signatures have inherent malleability: both (r, s) and (r, n - s) are valid for the same message and key. This can cause issues: Problem:
Solution: Ethereum enforces low-s (s ≤ n/2):
All signatures created by sign() use low-s. Verification accepts only low-s.

Recovery ID (v) Not Required

The v component is only needed for public key recovery (ecRecover). Standard verification ignores it because the public key is already provided.
For recovery-based verification (like Ethereum’s ecRecover), v is critical.

Public Key Format

Secp256k1 public keys can be represented in multiple formats: Uncompressed (65 bytes): 0x04 || x || y
  • Standard format with 0x04 prefix
  • Contains both x and y coordinates
Uncompressed without prefix (64 bytes): x || y
  • Voltaire’s internal format (no prefix)
  • Used by our verification API
Compressed (33 bytes): 0x02 || x or 0x03 || x
  • Only x-coordinate + parity bit for y
  • Not directly supported (must decompress first)
Our API expects 64-byte keys (no prefix). If you have prefixed keys:

Test Vectors

Basic Verification

Wrong Public Key

Wrong Message

Malleated Signature

Invalid Signature Components

Performance

Verification Cost

ECDSA verification is computationally expensive:
  1. Modular inversion - s^-1 mod n (expensive)
  2. Two scalar multiplications - u1 * G + u2 * public_key
  3. Point operations - Elliptic curve point addition
Typical verification time:
  • TypeScript (@noble/curves): ~1-2ms per signature
  • Zig (native): ~0.5-1ms per signature
  • WASM (portable): ~2-4ms per signature
For batch verification of multiple signatures, use optimized batch algorithms (not currently exposed in API).

EVM Precompile

Ethereum provides ecRecover precompile (address 0x01) for on-chain verification:
  • Gas cost: 3000 gas
  • Input: 128 bytes (hash, v, r, s)
  • Output: 32 bytes (recovered address, zero-padded)
For smart contracts, use ecrecover() built-in instead of implementing verification in Solidity.

Implementation Notes

TypeScript

Uses @noble/curves/secp256k1:
  • Constant-time operations (side-channel resistant)
  • Validates all inputs (signature components, public keys)
  • Enforces low-s malleability protection
  • ~20KB minified, tree-shakeable

Zig

Custom implementation:
  • ⚠️ UNAUDITED - Not security reviewed
  • ⚠️ NOT constant-time - Timing attack vulnerable
  • Basic validation only
  • Educational purposes only