Crypto at a Glance
Bitcoin's security rests on a handful of cryptographic primitives. Here's why each one matters:
Private Key
A random 256-bit number. The secret that lets you spend bitcoin. Never leaves your machine.
Public Key
Derived from the private key via elliptic curve multiplication. Shared publicly. Cannot be reversed.
Digital Signature
Proves you own the private key without revealing it. Created per-transaction. Verified by every node.
Hash Functions
One-way functions that create fixed-size fingerprints. Used for addresses, block headers, Merkle trees.
The secp256k1 Curve
Bitcoin uses the elliptic curve secp256k1, defined by the equation:
y² = x³ + 7 (over a 256-bit prime field)
Key properties:
- Order n ≈ 2²⁵⁶: the number of possible private keys is astronomically large.
- Generator point G: a fixed point on the curve known to everyone.
- Public key = private_key × G: elliptic curve point multiplication (easy to compute forward, infeasible to reverse).
- Chosen for performance, secp256k1 has a special structure that allows faster computation than random curves.
Unlike the NIST curves, secp256k1's parameters were chosen in a "nothing up my sleeve" manner; they're verifiably non-random, reducing suspicion of backdoors. It's also significantly faster to verify thanks to its efficiently computable endomorphism.
Private Keys: CKey
In Bitcoin Core, private keys are managed by the CKey class:
Key Operations
MakeNewKey(compressed): generates a cryptographically random 32-byte keyGetPubKey(): derives the corresponding public key via EC multiplicationSign(hash, sig): creates an ECDSA signature over a 32-byte hashSignCompact(hash, sig): creates a compact recoverable signatureSignSchnorr(hash, sig): creates a Schnorr signature (BIP 340, for Taproot)
Memory Security
Private keys use secure_allocator, a custom allocator that:
- Locks memory pages to prevent swapping to disk (
mlock()) - Zeros memory on deallocation (prevents key remnants in freed memory)
- Uses
memory_cleanse()which compilers can't optimize away
Public Keys: CPubKey
The CPubKey class represents the public half of a key pair:
Compressed vs. Uncompressed
A public key is a point (x, y) on the secp256k1 curve:
- Uncompressed: 65 bytes:
0x04 || x (32 bytes) || y (32 bytes) - Compressed: 33 bytes:
0x02 or 0x03 || x (32 bytes)
Since the curve equation allows recovering y from x (there are exactly two y values for each x), the compressed format only needs to store x plus a single parity bit (0x02 for even y, 0x03 for odd y). Modern Bitcoin always uses compressed keys; they save space in scripts and transactions.
Key Operations
Verify(hash, sig): verify an ECDSA signatureGetID()/Hash160(pubkey):RIPEMD160(SHA256(pubkey)), used for P2PKH addressesIsCompressed(): check format by looking at first byteDecompress(): expand compressed to uncompressed form
ECDSA Signing
ECDSA (Elliptic Curve Digital Signature Algorithm) is the original signature scheme used by Bitcoin:
How Signing Works
- Hash the message: compute
SHA256d(transaction_data)→ 32-byte hashh - Pick random nonce k: generate a random value (using RFC 6979 deterministic method)
- Compute R:
R = k × G, take the x-coordinate asr - Compute s:
s = k⁻¹ × (h + r × private_key) mod n - Signature is (r, s): encoded in DER format for Bitcoin
How Verification Works
- Parse signature into (r, s)
- Compute
u₁ = h × s⁻¹ mod n - Compute
u₂ = r × s⁻¹ mod n - Compute point
R' = u₁ × G + u₂ × PublicKey - Signature is valid if
R'.x == r
Bitcoin Core enforces the "low-S" rule: the s value must be in the lower half of the curve order. If s > n/2, replace it with n - s. This prevents signature malleability (BIP 62 / BIP 146).
Schnorr Signatures (BIP 340)
Taproot (activated November 2021) introduced Schnorr signatures, which have several advantages:
Linearity
Signatures can be aggregated: multiple signers can produce a single 64-byte signature (MuSig2).
Smaller & Faster
64 bytes (vs ~72 for DER-encoded ECDSA). Batch verification is significantly faster.
Provable Security
Security proof under standard assumptions (random oracle model), unlike ECDSA.
Schnorr uses the same secp256k1 curve but a simpler signing equation:
// Schnorr signature (BIP 340)
// 1. Choose random nonce k, compute R = k × G
// 2. e = SHA256(R.x || P.x || message)
// 3. s = k + e × private_key
// Signature = (R.x, s), exactly 64 bytes
Hashing Algorithms
SHA-256
The workhorse of Bitcoin. Used everywhere:
- Double SHA-256 (
SHA256d): transaction hashes, block hashes, Merkle trees - Single SHA-256: SegWit witness commitment, Taproot tagged hashes
- Implementation:
CSHA256class with hardware acceleration (SHA-NI, SSE4, AVX2)
RIPEMD-160
Used in Hash160 = RIPEMD160(SHA256(data)) for:
- P2PKH address generation (hash of public key)
- P2SH address generation (hash of redeem script)
The two-step hash (SHA-256 first, then RIPEMD-160) provides 160-bit security while protecting against potential weaknesses in either algorithm alone.
Other Hash Functions
- SipHash: fast keyed hash used for hash table randomization (DoS protection)
- MuHash: rolling hash for UTXO set integrity checking
- SHA-512: used in HMAC-SHA512 for HD key derivation (BIP 32)
- HMAC-SHA256: message authentication for BIP 324 encrypted P2P
- ChaCha20-Poly1305: authenticated encryption for BIP 324
Security Measures
Bitcoin Core takes cryptographic security very seriously:
- Deterministic nonces (RFC 6979): ECDSA nonces are derived deterministically from the private key and message hash. This eliminates the catastrophic failure mode of nonce reuse (which would leak the private key).
- Constant-time operations: signature creation and key operations use libsecp256k1, which is written to avoid timing side-channels.
- Memory locking: secret keys are stored in
mlock()ed memory to prevent swapping to disk. - Memory wiping:
memory_cleanse()ensures key material is zeroed when no longer needed. - Signature cache: validated signatures are cached to avoid re-verification during block connection.