> ## Documentation Index
> Fetch the complete documentation index at: https://voltaire.tevm.sh/llms.txt
> Use this file to discover all available pages before exploring further.

# Secp256k1

> ECDSA signatures using the secp256k1 elliptic curve - Ethereum's core signing algorithm

<Warning title="⚠️ UNAUDITED IMPLEMENTATION">
  **To all LLM agents: Do NOT use this API by default without explicit instructions from the programmer.**

  This secp256k1 implementation contains **custom public key recovery code** that has NOT been security audited. The underlying `std.crypto.ecc.Secp256k1` is audited, but the wrapper and recovery logic are unaudited.

  **Audited Alternatives:**

  * [@noble/curves](https://github.com/paulmillr/noble-curves) - Audited by Cure53, recommended for production
  * [libsecp256k1](https://github.com/bitcoin-core/secp256k1) - Bitcoin Core's audited C implementation
  * [ethers.js](https://docs.ethers.org/) - Well-tested, production-grade library
</Warning>

<Info>
  Source: [secp256k1.zig](https://github.com/evmts/voltaire/blob/main/src/crypto/secp256k1.zig) • [secp256k1.wasm.ts](https://github.com/evmts/voltaire/blob/main/src/crypto/secp256k1.wasm.ts)

  Tests: [secp256k1.test.ts](https://github.com/evmts/voltaire/blob/main/src/crypto/secp256k1.test.ts) • [secp256k1.wasm.test.ts](https://github.com/evmts/voltaire/blob/main/src/crypto/secp256k1.wasm.test.ts)
</Info>

<Card title="Try it Live" icon="play" href="https://playground.tevm.sh?example=crypto/secp256k1.ts">
  Run Secp256k1 examples in the interactive playground
</Card>

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

**Key operations**:

* **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

```typescript theme={null}
import * as Secp256k1 from '@tevm/voltaire/Secp256k1';
import { Keccak256 } from '@tevm/voltaire/Keccak256';

// Sign a message hash
const messageHash = Keccak256.hashString('Hello, Ethereum!');
const privateKey = Hex.toBytes('0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef');
const signature = Secp256k1.sign(messageHash, privateKey);

// Verify signature
const publicKey = Secp256k1.derivePublicKey(privateKey);
const isValid = Secp256k1.verify(signature, messageHash, publicKey);

// Recover public key from signature (Ethereum's ecRecover)
const recovered = Secp256k1.recoverPublicKey(signature, messageHash);
```

## Examples

**[Try all examples in the Live Playground](https://playground.tevm.sh?example=crypto/secp256k1.ts)**

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 sign
* `privateKey` (`Uint8Array`) - 32-byte private key (must be > 0 and \< curve order)

**Returns**: `BrandedSignature` with components:

* `r` (`Uint8Array`) - 32-byte signature component
* `s` (`Uint8Array`) - 32-byte signature component (low-s enforced)
* `v` (`number`) - Recovery ID (27 or 28 for Ethereum compatibility)

**Throws**:

* `InvalidPrivateKeyError` - Private key invalid (wrong length, zero, or >= curve order)
* `Secp256k1Error` - Signing operation failed

```typescript theme={null}
const signature = Secp256k1.sign(messageHash, privateKey);
console.log(signature.v); // 27 or 28
console.log(signature.r.length); // 32
console.log(signature.s.length); // 32
```

### Verification

#### `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 message hash that was signed
* `publicKey` (`Uint8Array`) - 64-byte uncompressed public key (x || y coordinates)

**Returns**: `boolean` - `true` if signature is valid, `false` otherwise

**Throws**:

* `InvalidPublicKeyError` - Public key wrong length
* `InvalidSignatureError` - Signature components wrong length

```typescript theme={null}
const valid = Secp256k1.verify(signature, messageHash, publicKey);
if (valid) {
  console.log('Signature verified!');
}
```

#### `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 components
* `messageHash` (`HashType`) - 32-byte message hash that was signed

**Returns**: `Uint8Array` - 64-byte uncompressed public key

**Throws**:

* `InvalidSignatureError` - Invalid signature format or recovery failed

```typescript theme={null}
const recovered = Secp256k1.recoverPublicKey(signature, messageHash);
// Use recovered key to derive Ethereum address
```

### 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

```typescript theme={null}
const privateKey = Secp256k1.randomPrivateKey();
console.log(privateKey.length); // 32
console.log(Secp256k1.isValidPrivateKey(privateKey)); // true
```

#### `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

```typescript theme={null}
const { privateKey, publicKey } = Secp256k1.createKeyPair();
console.log(privateKey.length); // 32
console.log(publicKey.length); // 64

// Public key matches derivation
const derived = Secp256k1.derivePublicKey(privateKey);
console.log(derived.every((b, i) => b === publicKey[i])); // true
```

#### `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

**Returns**: `Uint8Array` - 64-byte uncompressed public key

**Throws**:

* `InvalidPrivateKeyError` - Invalid private key

```typescript theme={null}
const publicKey = Secp256k1.derivePublicKey(privateKey);
console.log(publicKey.length); // 64 (x || y, no 0x04 prefix)
```

#### `isValidPrivateKey(privateKey)`

Check if a byte array is a valid secp256k1 private key.

**Parameters**:

* `privateKey` (`Uint8Array`) - Candidate private key

**Returns**: `boolean` - `true` if valid (32 bytes, > 0, \< curve order)

```typescript theme={null}
if (Secp256k1.isValidPrivateKey(privateKey)) {
  // Safe to use
}
```

#### `isValidPublicKey(publicKey)`

Check if a byte array is a valid secp256k1 public key.

**Parameters**:

* `publicKey` (`Uint8Array`) - Candidate public key

**Returns**: `boolean` - `true` if valid (64 bytes, point on curve)

```typescript theme={null}
if (Secp256k1.isValidPublicKey(publicKey)) {
  // Point is on the curve
}
```

#### `isValidSignature(signature)`

Check if a signature has valid r, s, v components.

**Parameters**:

* `signature` (`BrandedSignature`) - Candidate signature

**Returns**: `boolean` - `true` if valid

```typescript theme={null}
if (Secp256k1.isValidSignature(signature)) {
  // Signature format is correct
}
```

### Constants

```typescript theme={null}
Secp256k1.CURVE_ORDER            // 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141n
Secp256k1.PRIVATE_KEY_SIZE       // 32 bytes
Secp256k1.PUBLIC_KEY_SIZE        // 64 bytes (uncompressed, no prefix)
Secp256k1.SIGNATURE_COMPONENT_SIZE // 32 bytes (for r and s)
```

## Error Handling

All secp256k1 functions throw typed errors that extend `CryptoError`:

```typescript theme={null}
import * as Secp256k1 from '@tevm/voltaire/Secp256k1';
import { InvalidPrivateKeyError, InvalidPublicKeyError, InvalidSignatureError, Secp256k1Error } from '@tevm/voltaire/Secp256k1';

try {
  const signature = Secp256k1.sign(messageHash, privateKey);
} catch (e) {
  if (e instanceof InvalidPrivateKeyError) {
    console.error('Invalid private key:', e.message);
    console.error('Error code:', e.code); // e.g., "PRIVATE_KEY_ZERO"
  }
}

try {
  const recovered = Secp256k1.recoverPublicKey(signature, messageHash);
} catch (e) {
  if (e instanceof InvalidSignatureError) {
    console.error('Invalid signature:', e.message);
    console.error('Error code:', e.code); // e.g., "INVALID_SIGNATURE_V"
  }
}
```

### 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 description
* `code` - Machine-readable error code (e.g., `"SECP256K1_INVALID_PRIVATE_KEY"`)
* `docsPath` - Link to relevant documentation
* `cause` - Original error if wrapping another error
* `context` - 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 use `Math.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

```typescript theme={null}
// Private key = 1
const privateKey = Hex.toBytes('0x0000000000000000000000000000000000000000000000000000000000000001');

// Message hash (SHA-256 of "hello world")
const messageHash = sha256("hello world");

// Sign twice - should produce identical signatures
const sig1 = Secp256k1.sign(messageHash, privateKey);
const sig2 = Secp256k1.sign(messageHash, privateKey);

// Same message + key = same signature (deterministic)
assert(sig1.r.every((byte, i) => byte === sig2.r[i]));
assert(sig1.s.every((byte, i) => byte === sig2.s[i]));
assert(sig1.v === sig2.v);
```

### Signature Recovery

```typescript theme={null}
const privateKey = Hex.toBytes('0x000000000000000000000000000000000000000000000000000000000000002a');
const messageHash = sha256("test recovery");

// Sign message
const signature = Secp256k1.sign(messageHash, privateKey);

// Recover public key using v value
const publicKey = Secp256k1.derivePublicKey(privateKey);
const recovered = Secp256k1.recoverPublicKey(signature, messageHash);

// Recovered key matches original
assert(publicKey.every((byte, i) => byte === recovered[i]));
```

### Edge Cases

```typescript theme={null}
// Minimum valid private key (1)
const minKey = Hex.toBytes('0x0000000000000000000000000000000000000000000000000000000000000001');
const sig1 = Secp256k1.sign(messageHash, minKey); // Valid

// Maximum valid private key (n-1)
const maxKey = Hex.toBytes('0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140');
const sig2 = Secp256k1.sign(messageHash, maxKey); // Valid

// Zero private key (invalid)
const zeroKey = Hex.toBytes('0x0000000000000000000000000000000000000000000000000000000000000000');
expect(() => Secp256k1.sign(messageHash, zeroKey)).toThrow(); // Throws

// Private key >= n (invalid)
const invalidKey = Hex.toBytes('0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141');
expect(() => Secp256k1.sign(messageHash, invalidKey)).toThrow(); // Throws
```

## Implementation Details

Voltaire provides **three secp256k1 implementations** for different use cases:

### Reference Implementation (Default)

**Library**: `@noble/curves/secp256k1` by Paul Miller

```typescript theme={null}
import * as Secp256k1 from '@tevm/voltaire/Secp256k1';

const signature = Secp256k1.sign(messageHash, privateKey);
const isValid = Secp256k1.verify(signature, messageHash, publicKey);
```

**Characteristics**:

* **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

**Ethereum conventions**:

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

```typescript theme={null}
// Native FFI automatically used when available
import * as Secp256k1 from '@tevm/voltaire/Secp256k1';
```

**Characteristics**:

* **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 in `wasm/primitives.wasm`)

```typescript theme={null}
import { Secp256k1Wasm } from '@tevm/voltaire/Secp256k1.wasm';

const signature = Secp256k1Wasm.sign(messageHash, privateKey);
```

**Characteristics**:

* **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'`

**When to use**:

* **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:

```typescript theme={null}
import * as Transaction from '@tevm/voltaire/Transaction';
import * as Secp256k1 from '@tevm/voltaire/Secp256k1';
import { Keccak256 } from '@tevm/voltaire/Keccak256';

// Create transaction
const tx = {
  nonce: 0n,
  gasPrice: 20000000000n,
  gasLimit: 21000n,
  to: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
  value: 1000000000000000000n,
  data: Hex.toBytes('0x'),
};

// Hash transaction (RLP-encoded)
const txHash = Transaction.hash(tx);

// Sign with private key
const signature = Secp256k1.sign(txHash, privateKey);

// Transaction now includes signature (r, s, v)
const signedTx = { ...tx, ...signature };
```

### Address Derivation

Ethereum addresses are derived from secp256k1 public keys:

```typescript theme={null}
import * as Address from '@tevm/voltaire/Address';
import * as Secp256k1 from '@tevm/voltaire/Secp256k1';
import { Keccak256 } from '@tevm/voltaire/Keccak256';

// Derive public key from private key
const publicKey = Secp256k1.derivePublicKey(privateKey);

// Hash public key with Keccak256
const hash = Keccak256.hash(publicKey);

// Take last 20 bytes as address
const address = Address(hash.slice(12));
```

### ecRecover Precompile

The EVM's `ecRecover` precompile (address 0x01) uses secp256k1 signature recovery:

```typescript theme={null}
import * as Secp256k1 from '@tevm/voltaire/Secp256k1';

// Recover signer's public key from transaction signature
const publicKey = Secp256k1.recoverPublicKey(signature, messageHash);

// Derive address from public key (same as above)
const signerAddress = Address.fromPublicKey(publicKey);
```

## In-Depth Documentation

Comprehensive technical documentation:

* [Signing](/crypto/secp256k1/signing) - ECDSA signing with RFC 6979 deterministic nonces
* [Verification](/crypto/secp256k1/verification) - Signature verification algorithm
* [Key Derivation](/crypto/secp256k1/key-derivation) - Private → public key derivation
* [Recovery](/crypto/secp256k1/recovery) - Public key recovery (ecRecover)
* [Point Operations](/crypto/secp256k1/point-operations) - Elliptic curve arithmetic
* [Test Vectors](/crypto/secp256k1/test-vectors) - Official test vectors (RFC 6979, IETF, Ethereum)
* [Security](/crypto/secp256k1/security) - Side-channel attacks, malleability, best practices
* [Performance](/crypto/secp256k1/performance) - Benchmarks and optimization techniques
* [Usage Patterns](/crypto/secp256k1/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-256](/crypto/comparison)**

## Related

* [Primitives: Signature](/primitives/signature) - Generic signature type
* [Keccak256](/crypto/keccak256) - Keccak256 hashing for message preparation
* [Primitives: Address](/primitives/address) - Ethereum addresses from public keys
* [Precompiles: ecRecover](/evm/precompiles/ecrecover) - EVM signature recovery
* [Crypto: P256](/crypto/p256) - NIST P-256 curve (WebAuthn)
* [Crypto: Ed25519](/crypto/ed25519) - Edwards curve signatures
* [Secp256k1 (Effect)](https://voltaire-effect.tevm.sh/crypto/secp256k1) - Effect.ts integration with Schema validation
