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

# Security

> Security considerations, side-channel attacks, and best practices for secp256k1

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

<Warning>
  **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.
</Warning>

# Secp256k1 Security

Security considerations, attack vectors, and best practices for elliptic curve cryptography with secp256k1.

## Critical Warnings

⚠️ **Zig Implementation NOT Audited**

The Zig implementation (`src/crypto/secp256k1.zig`) is:

* **UNAUDITED** - No security review
* **NOT constant-time** - Vulnerable to timing attacks
* **Educational only** - Do not use in production

For production, use:

1. TypeScript implementation (@noble/curves) - audited
2. Hardware wallets (Ledger, Trezor)
3. EVM precompiles (ecRecover)

## Private Key Security

### Generation

**Use cryptographically secure random:**

✅ **Correct:**

```typescript theme={null}
// Browser
const privateKey = Bytes32();
crypto.getRandomValues(privateKey);

// Node.js
import crypto from 'crypto';
const privateKey = crypto.randomBytes(32);

// Hardware wallet
// Keys generated in secure hardware, never exported
```

❌ **Never:**

```typescript theme={null}
// Math.random() is NOT cryptographic
for (let i = 0; i < 32; i++) {
  privateKey[i] = Math.floor(Math.random() * 256); // ❌ INSECURE
}

// Timestamps have low entropy
const timestamp = Date.now();
const hash = keccak256(numberToBytes(timestamp)); // ❌ PREDICTABLE

// User input alone has low entropy
const password = "mypassword123";
const key = keccak256(stringToBytes(password)); // ❌ WEAK
```

**Entropy requirements:**

* Minimum 256 bits (32 bytes) of cryptographic randomness
* Use OS-provided CSPRNG (crypto.getRandomValues)
* Hardware RNG preferred (TPM, Secure Enclave)
* For offline: dice rolls + hashing (256 rolls \* 2.58 bits = 660 bits)

### Storage

⚠️ **Protect keys at rest:**

**Best practices:**

* **Hardware wallets**: Store in Ledger/Trezor, never export
* **Encrypted keystores**: AES-256-GCM with strong KDF (scrypt/argon2)
* **Secure Enclave**: iOS/macOS hardware-backed storage
* **HSM**: Enterprise hardware security modules
* **Environment isolation**: Air-gapped for high-value keys

**Avoid:**

* Plain text files
* Environment variables (leak in logs)
* Git repositories (permanent history)
* Clipboard (malware can read)
* Screenshots (OCR readable)
* Cloud storage unencrypted
* Browser localStorage unencrypted

### Key Derivation

**Use BIP32/BIP39 for backups:**

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

// Generate mnemonic (12-24 words)
const mnemonic = Bip39.generateMnemonic(256); // 24 words

// Derive master key
const seed = Bip39.mnemonicToSeed(mnemonic);
const masterKey = HDWallet.fromSeed(seed);

// Derive account keys (BIP44)
const accountKey = HDWallet.derivePath(masterKey, "m/44'/60'/0'/0/0");

// Backup: Write down 24 words, never private keys directly
```

## Nonce Security

### RFC 6979 Deterministic Nonces

**Why deterministic?**

Random nonce generation has catastrophic failure modes:

❌ **Nonce reuse leaks private key:**

```
Sign message1 with nonce k: s1 = k^-1 * (e1 + r * privkey)
Sign message2 with nonce k: s2 = k^-1 * (e2 + r * privkey)

Solve for privkey:
k = (e1 - e2) / (s1 - s2)
privkey = (s1*k - e1) / r
```

Real attacks:

* PlayStation 3 hack (2010) - Sony reused k=4
* Bitcoin theft - Bad RNG in Android wallet (2013)
* Blockchain.info bug (2014) - Weak Java SecureRandom

✅ **RFC 6979 prevents this:**

```
k = HMAC_DRBG(key: privkey, data: message_hash)
```

Benefits:

* Same (message, key) always produces same nonce
* No RNG required
* Deterministic = testable
* HMAC\_DRBG provides cryptographic strength

### Implementation Requirements

All implementations MUST:

* Use RFC 6979 deterministic nonces
* NEVER allow custom nonce input
* NEVER reuse nonces across different messages
* Validate nonce is in range \[1, n-1]

## Signature Malleability

### Problem

ECDSA signatures have inherent malleability:

```typescript theme={null}
// Both signatures verify for same (message, publicKey)
const sig1 = { r, s: s, v: 27 };
const sig2 = { r, s: n - s, v: 28 }; // Malleated
```

**Attacks:**

* Transaction replay with modified txHash
* Smart contract vulnerabilities (signature-based authentication)
* Blockchain state inconsistency

### Solution: Low-s Enforcement

Ethereum enforces `s ≤ n/2` (BIP 62, EIP-2):

```typescript theme={null}
const HALF_N = SECP256K1_N >> 1n;

if (s > HALF_N) {
  s = SECP256K1_N - s;  // Normalize to low-s
  v ^= 1;  // Flip recovery ID
}
```

**Our implementation:**

* `sign()` always produces low-s
* `verify()` rejects high-s signatures
* `recoverPublicKey()` rejects high-s

## Side-Channel Attacks

### Timing Attacks

Non-constant-time implementations leak secrets via execution time:

❌ **Vulnerable:**

```zig theme={null}
// Branch timing leaks bit values
if (bit == 1) {
  result = result + addend;  // Takes longer
}
```

✅ **Constant-time:**

```zig theme={null}
// Same timing regardless of bit value
mask = -(bit & 1);  // 0x00000000 or 0xFFFFFFFF
result = result + (addend & mask);  // Always executes
```

**Attack scenario:**

```
Measure signing time for different messages
→ Deduce private key bits from timing variations
→ After ~1000 signatures, recover full key
```

**Mitigations:**

* Use constant-time libraries (@noble/curves ✅)
* Avoid conditional branches on secrets
* Use hardware wallets (constant-time guaranteed)
* Avoid Zig implementation (⚠️ NOT constant-time)

### Power Analysis

**Differential Power Analysis (DPA):**

* Measure CPU power consumption during crypto ops
* Correlate power spikes with bit operations
* Recover secret keys after many measurements

**Simple Power Analysis (SPA):**

* Single power trace reveals operation sequence
* Identify point additions vs doublings
* Reconstruct scalar multiplication pattern

**Mitigations:**

* Hardware security (HSM, Secure Enclave)
* Power randomization
* Constant-time algorithms
* Blinding techniques

### Cache Timing Attacks

Memory access patterns leak information via cache hits/misses:

❌ **Vulnerable:**

```c theme={null}
// Table lookup leaks index via cache timing
value = precomputed_table[secret_index];
```

✅ **Secure:**

```c theme={null}
// Load entire table (constant-time access)
for (int i = 0; i < table_size; i++) {
  mask = -(i == secret_index);
  value |= precomputed_table[i] & mask;
}
```

**Real attacks:**

* Flush+Reload on AES T-tables
* Prime+Probe on RSA/ECC

## Message Hashing

### Always Hash Before Signing

⚠️ **Sign hashes, not raw messages:**

❌ **Vulnerable:**

```typescript theme={null}
// Signing raw message allows chosen-plaintext attacks
const signature = Secp256k1.sign(rawMessage, privateKey);
```

✅ **Secure:**

```typescript theme={null}
// Hash message first (collision-resistant)
const messageHash = Keccak256.hash(rawMessage);
const signature = Secp256k1.sign(messageHash, privateKey);
```

**Why?**

* ECDSA security requires random-looking messages
* Raw messages may have structure attackers exploit
* Hashing provides collision resistance
* Fixed-length input simplifies validation

### Hash Function Requirements

Use collision-resistant hash functions:

✅ **Approved:**

* Keccak256 (Ethereum standard)
* SHA-256 (Bitcoin, general use)
* SHA-3 (NIST standard)
* Blake2b (high performance)

❌ **Deprecated:**

* MD5 (broken - collisions trivial)
* SHA-1 (broken - collisions practical)

## Public Key Validation

### Always Validate Public Keys

⚠️ **Verify points are on curve:**

```typescript theme={null}
function isValidPublicKey(pubkey: Uint8Array): boolean {
  if (pubkey.length !== 64) return false;

  const x = bytesToBigInt(pubkey.slice(0, 32));
  const y = bytesToBigInt(pubkey.slice(32, 64));

  // Check y² = x³ + 7 (mod p)
  const p = SECP256K1_P;
  const y2 = (y * y) % p;
  const x3_plus_7 = (x * x * x + 7n) % p;

  return y2 === x3_plus_7;
}
```

**Attacks if unvalidated:**

* Invalid curve attacks (point not on secp256k1)
* Small subgroup attacks
* Twist attacks (point on quadratic twist)

### Check for Point at Infinity

```typescript theme={null}
// Reject point at infinity (identity element)
if (x === 0n && y === 0n) return false;
```

## Ethereum-Specific Considerations

### EIP-155 Replay Protection

**Problem:** Signatures valid on one chain can be replayed on forks.

**Solution:** Include chainId in v value:

```typescript theme={null}
// Pre-EIP-155 (vulnerable to replay)
v = recoveryId + 27;

// Post-EIP-155 (replay protected)
v = chainId * 2 + 35 + recoveryId;

// Examples:
// Ethereum mainnet (chainId=1): v = 37 or 38
// Goerli testnet (chainId=5): v = 45 or 46
```

### ecRecover Gotchas

**Precompile behavior:**

* Returns zero address on invalid signature (NOT error)
* Always check return value != 0x0

```solidity theme={null}
function verifySigner(bytes32 hash, uint8 v, bytes32 r, bytes32 s, address expected)
  public pure returns (bool)
{
  address signer = ecrecover(hash, v, r, s);

  // ⚠️ Check for zero address (invalid signature)
  if (signer == address(0)) return false;

  return signer == expected;
}
```

### EIP-191 Personal Sign

**Prefix prevents signing raw transactions:**

```typescript theme={null}
// Without prefix: attacker could trick user into signing transaction
const hash = keccak256(transaction);  // ❌ Dangerous

// With prefix: clearly marked as non-transaction message
const prefix = `\x19Ethereum Signed Message:\n${message.length}`;
const hash = keccak256(prefix + message);  // ✅ Safe
```

## Best Practices Summary

### DO

✅ Use hardware wallets for high-value keys
✅ Use @noble/curves (audited) for TypeScript
✅ Generate keys with crypto.getRandomValues()
✅ Store encrypted keystores (AES-256-GCM + scrypt)
✅ Validate all inputs (keys, signatures, hashes)
✅ Use RFC 6979 deterministic nonces
✅ Enforce low-s malleability protection
✅ Hash messages before signing
✅ Include EIP-155 chainId in signatures
✅ Use BIP39/BIP32 for backups
✅ Test with official test vectors

### DON'T

❌ Use Math.random() for key generation
❌ Reuse nonces across messages
❌ Store private keys unencrypted
❌ Sign raw messages without hashing
❌ Skip public key validation
❌ Use Zig implementation in production (unaudited)
❌ Implement custom crypto (use audited libraries)
❌ Trust user-provided public keys without validation
❌ Ignore signature malleability
❌ Forget EIP-155 replay protection

## Security Checklist

* [ ] Keys generated with CSPRNG
* [ ] Keys stored encrypted or in hardware
* [ ] Using audited library (@noble/curves)
* [ ] RFC 6979 deterministic nonces
* [ ] Low-s enforcement enabled
* [ ] Public keys validated (point on curve)
* [ ] Messages hashed before signing
* [ ] EIP-155 chainId in signatures
* [ ] Test vectors passing
* [ ] No custom crypto implementation
* [ ] Side-channel mitigations in place
* [ ] ecRecover zero-address checks

## Related

* [Signing](/crypto/secp256k1/signing) - ECDSA signing implementation
* [Verification](/crypto/secp256k1/verification) - Signature verification
* [Test Vectors](/crypto/secp256k1/test-vectors) - Validation test cases
* [HD Wallet](/crypto/hdwallet) - BIP32 key derivation
* [Bip39](/crypto/bip39) - Mnemonic seed phrases
* [RFC 6979](https://tools.ietf.org/html/rfc6979) - Deterministic ECDSA
