SHA-256: The Cryptographic Hash Function That Secures Bitcoin
Complete guide with Theory, Mathematics, and Code โ from beginner to advanced
1.1 What is SHA-256?
SHA-256 (Secure Hash Algorithm 256-bit) is a cryptographic hash function designed by the United States National Security Agency (NSA) and published by the National Institute of Standards and Technology (NIST) in 2001. It is part of the SHA-2 family, which includes SHA-224, SHA-256, SHA-384, and SHA-512.
Think of SHA-256 as a digital fingerprint generator. No matter how large or small your input is (a single letter, a book, or an entire hard drive), SHA-256 always produces a unique 64-character "fingerprint" called a hash or digest. Change even one bit in the input, and the fingerprint becomes completely different.
- SHA โ Secure Hash Algorithm
- 256 โ Output size in bits (32 bytes / 64 hex characters)
1.2 Why Does Bitcoin Use SHA-256?
Bitcoin uses SHA-256 in four critical places. Without it, Bitcoin would not exist as we know it.
1.2.1 Mining (Proof of Work)
Miners repeatedly hash the block header with different nonce values until the resulting hash is below a target value. This is what secures the Bitcoin network and requires massive computational energy.
Valid if Block Hash < Target
The difficulty of finding a valid hash is adjusted every 2,016 blocks to maintain a 10-minute block time. This is why Bitcoin mining is often called a "lottery" โ you're guessing billions of numbers per second hoping to find one that produces a hash with many leading zeros.
1.2.2 Transaction IDs (TXIDs)
Every transaction in Bitcoin is identified by a unique TXID, which is the double SHA-256 hash of the serialized transaction data. This allows anyone to look up a specific transaction on the blockchain.
1.2.3 Block Chaining
Each block header contains the hash of the previous block, creating an immutable chain. Changing any block would change its hash and break the link to all subsequent blocks โ making tampering immediately detectable.
1.2.4 Merkle Trees
Transactions in a block are organized into a Merkle tree, where each node is a double SHA-256 hash. The Merkle root is stored in the block header, allowing efficient SPV (Simplified Payment Verification) โ mobile wallets can verify a transaction exists without downloading the entire block.
- It was the most trusted hash function available in 2008
- It offers 256-bit security (128-bit quantum security)
- It has no known backdoors (unlike some NIST curves)
- Double hashing prevents length extension attacks
- It's fast and well-studied by cryptographers worldwide
1.3 Core Properties of Cryptographic Hash Functions
For a hash function to be considered cryptographically secure, it must satisfy four essential properties. SHA-256 satisfies all of them.
1.3.1 Deterministic
The same input always produces the same output. This is essential for verification โ if you hash a file today and tomorrow, you should get the same result. This property allows anyone to verify data integrity independently.
import hashlib
print(hashlib.sha256(b"Bitcoin").hexdigest())
print(hashlib.sha256(b"Bitcoin").hexdigest())
# Both outputs are identical!
1.3.2 Pre-image Resistance (One-way)
Given a hash value h, it is computationally infeasible to find any message m such that hash(m) = h. You cannot reverse a hash to find the original input.
If you could reverse hashes, Bitcoin mining would be trivial โ miners could compute the nonce directly instead of guessing billions of times. Passwords stored as hashes would be instantly cracked. Digital signatures would be worthless.
1.3.3 Collision Resistance
It should be computationally infeasible to find two different messages m1 and m2 such that hash(m1) = hash(m2). No two different inputs should produce the same hash.
If collisions were easy to find, an attacker could create a fake transaction that hashes to the same value as a legitimate one, causing chaos in the blockchain. Two different blocks could have the same hash, breaking the chain.
1.3.4 Avalanche Effect
A small change in the input (even flipping a single bit) should change approximately 50% of the output bits. This makes the output unpredictable and prevents attackers from making "small adjustments" to achieve a desired hash.
Because of the avalanche effect, miners cannot predict what nonce will produce a valid hash. The only way to find a valid nonce is brute force โ trying billions of possibilities per second. There's no mathematical shortcut. This is what makes Proof of Work truly "work" โ it requires real energy expenditure.
1.4 Visual Overview of SHA-256
The process above is repeated for each 512-bit chunk. If there are multiple chunks, the output of one chunk becomes the input (initial hash values) for the next chunk.
1.5 Double SHA-256 (Bitcoin Standard)
Bitcoin applies SHA-256 twice in sequence for most operations:
Single SHA-256 is vulnerable to length extension attacks โ an attacker who knows H(message) can compute H(message || padding || extra) without knowing the original message.
Double SHA-256 prevents this because the second hash "resets" the internal state. The attacker never sees the intermediate hash value, making the attack impossible.
- Block Hashing (Mining) โ Every block hash is double SHA-256
- Transaction IDs (TXIDs) โ Every transaction's unique identifier
- Merkle Tree Nodes โ All nodes in the Merkle tree use double SHA-256
- Address Generation โ As part of HASH160 (SHA-256 then RIPEMD-160)
1.6 Real Bitcoin Example
Here's an actual block hash from the Bitcoin blockchain. Notice the leading zeros โ proof of the massive work required to mine it.
Block Hash: 0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5
The number of leading zeros indicates the difficulty of mining. This hash has 19 leading zeros (76 bits).
This is why Bitcoin mining requires massive computational power โ finding a valid hash is like winning a trillion-trillion lottery every 10 minutes. The energy cost is what makes the blockchain immutable.
1.7 Security Level
| Attack Type | Complexity | Practicality |
|---|---|---|
| Pre-image Attack | 2ยฒโตโถ operations | Impossible (sun would burn out) |
| Second Pre-image | 2ยฒโตโถ operations | Impossible |
| Collision (Birthday) | 2ยนยฒโธ operations | Impossible with current tech |
| Quantum (Grover's) | 2ยนยฒโธ operations | Theoretically possible in distant future |
As of 2026, SHA-256 has no known practical vulnerabilities. It continues to be used by Bitcoin, SSL/TLS certificates, and governments worldwide. The double hashing used in Bitcoin provides additional protection against length extension attacks.
1.8 Comparison with Other Hash Functions
| Hash Function | Output Size | Block Size | Rounds | Status | Used In |
|---|---|---|---|---|---|
| SHA-256 | 256 bits | 512 bits | 64 | โ Secure | Bitcoin, SSL/TLS, Blockchain |
| SHA-512 | 512 bits | 1024 bits | 80 | โ Secure | Password hashing, SHA-2 family |
| SHA-1 | 160 bits | 512 bits | 80 | โ Broken (2017) | Git (legacy), SSL certificates (deprecated) |
| MD5 | 128 bits | 512 bits | 64 | โ Broken (2004) | Checksums only |
| RIPEMD-160 | 160 bits | 512 bits | 80 | โ Secure | Bitcoin addresses (HASH160) |
- MD5 (2004): Researchers found collisions โ two different inputs producing the same hash. Used only for checksums now.
- SHA-1 (2017): Google and CWI Amsterdam created two different PDF files with the same SHA-1 hash (SHAttered attack).
- SHA-256: No practical collisions have ever been found.
1.9 Common Misconceptions
Truth: Even with all the computers in the world working for billions of years, you could not reverse a single SHA-256 hash. The output space (2ยฒโตโถ) is larger than the number of atoms in the universe. Brute force is impossible.
Truth: Grover's algorithm would reduce the security from 2ยฒโตโถ to 2ยนยฒโธ operations โ still practically impossible. The first quantum computer capable of this is likely decades away, and Bitcoin can hard-fork to quantum-resistant algorithms if needed.
Truth: SHA-256 is everywhere! Your browser uses it for SSL/TLS certificates (the padlock icon), your operating system uses it for file integrity, password hashing, digital signatures, and blockchain technology beyond Bitcoin (Ethereum, etc.).
Truth: SHA-256 always produces exactly 256 bits (64 hex characters) regardless of input size. A single letter and a 10GB file both produce a 64-character hash.
2.1 Step 1: Input Padding
The input message must be padded to a multiple of 512 bits.
1. Append a '1' bit
2. Append '0' bits until length โก 448 (mod 512)
3. Append L as a 64-bit integer (big-endian)
Result: Total length = multiple of 512 bits
After '1': 25 bits โ After zeros: 448 bits โ After length (24): 512 bits
2.2 Step 2: Message Decomposition
The padded message is split into 512-bit chunks. Each chunk is divided into 16 words of 32 bits each.
Each W[t] is a 32-bit unsigned integer (0 to 4,294,967,295)
2.3 Step 3: Message Schedule (Expanding to 64 Words)
SHA-256 requires 64 words for 64 rounds. The remaining 48 words are generated using:
ฯ0 = (W[t-15] โ 7) โ (W[t-15] โ 18) โ (W[t-15] >> 3)
ฯ1 = (W[t-2] โ 17) โ (W[t-2] โ 19) โ (W[t-2] >> 10)
W[t] = W[t-16] + ฯ0 + W[t-7] + ฯ1
- โ r โ Rotate right by r bits (bits wrap around)
- >> r โ Shift right by r bits (zeros enter from left)
- โ โ XOR (exclusive OR)
- + โ Addition modulo 2ยณยฒ
2.4 Step 4: Initialize Hash Values (H[0-7])
Eight 32-bit working variables are set to initial values from the fractional parts of the square roots of the first 8 primes.
| Variable | Prime | Initial Value (Hex) |
|---|---|---|
| H0 / a | 2 | 0x6a09e667 |
| H1 / b | 3 | 0xbb67ae85 |
| H2 / c | 5 | 0x3c6ef372 |
| H3 / d | 7 | 0xa54ff53a |
| H4 / e | 11 | 0x510e527f |
| H5 / f | 13 | 0x9b05688c |
| H6 / g | 17 | 0x1f83d9ab |
| H7 / h | 19 | 0x5be0cd19 |
These constants come from irrational numbers (square roots of primes), proving no backdoor was hidden in the algorithm.
2.5 Step 5: Round Constants (K[0-63])
64 constants, one for each round, derived from the fractional parts of the cube roots of the first 64 primes.
2.6 Step 6: Compression Function (64 Rounds)
This is the heart of SHA-256. Each round updates the 8 working variables.
ฮฃ1 = (e โ 6) โ (e โ 11) โ (e โ 25)
Ch = (e โง f) โ (ยฌe โง g)
temp1 = h + ฮฃ1 + Ch + K[t] + W[t]
ฮฃ0 = (a โ 2) โ (a โ 13) โ (a โ 22)
Maj = (a โง b) โ (a โง c) โ (b โง c)
temp2 = ฮฃ0 + Maj
Then rotate variables (the shuffle):
h = g
g = f
f = e
e = d + temp1
d = c
c = b
b = a
a = temp1 + temp2
- ฮฃ0 and ฮฃ1 (Sigma): Scramble bits through rotation and XOR โ creates diffusion
- Ch (Choose): Chooses bits from f or g based on e โ "if e bit is 1, take from f; if 0, take from g"
- Maj (Majority): Outputs 1 if at least two of a,b,c are 1 โ provides non-linearity
2.7 Step 7: Final Hash
After all 64 rounds, the working variables are added to the initial hash values (feed-forward).
Final b = initial_b + b
...
Final h = initial_h + h
Hash = a || b || c || d || e || f || g || h = 256 bits
The feed-forward operation ensures that even if an attacker could reverse the 64 rounds, they would still need the initial values. Without this, SHA-256 would be vulnerable to certain attacks.
2.8 The Avalanche Effect
Because of the avalanche effect, miners cannot predict what nonce will produce a valid hash. The only way to find a valid nonce is brute force โ trying billions of possibilities per second. There's no mathematical shortcut.
3.1 Installation (No external dependencies needed)
The code below uses only Python's built-in libraries. No installation required!
hashlibโ for SHA-256 (Python built-in)structโ for byte packing (Python built-in)
3.2 Simple Hash Example
import hashlib
# Single SHA-256
message = b"Hello, Bitcoin!"
single_hash = hashlib.sha256(message).hexdigest()
print(f"Single SHA-256: {single_hash}")
# Double SHA-256 (Bitcoin standard)
double_hash = hashlib.sha256(hashlib.sha256(message).digest()).hexdigest()
print(f"Double SHA-256: {double_hash}")
# Demonstrating the avalanche effect
msg1 = b"Bitcoin"
msg2 = b"bitcoin" # Only 'B' vs 'b' changed
hash1 = hashlib.sha256(msg1).hexdigest()
hash2 = hashlib.sha256(msg2).hexdigest()
print(f"\n'Bitcoin' โ {hash1[:32]}...")
print(f"'bitcoin' โ {hash2[:32]}...")
3.3 Complete Pure Python Implementation
#!/usr/bin/env python3
"""
Complete Pure Python SHA-256 Implementation
No external dependencies โ uses only built-in libraries
"""
import struct
# Initial hash values (first 32 bits of fractional parts of โprimes)
H = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
]
# Round constants (first 32 bits of fractional parts of โprimes)
K = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
]
def rotr(x, n):
"""Rotate right (circular right shift)"""
return ((x >> n) | (x << (32 - n))) & 0xFFFFFFFF
def shr(x, n):
"""Shift right (zeros enter from left)"""
return (x >> n) & 0xFFFFFFFF
def pad_message(message):
"""Pad message to 512-bit chunks"""
ml = len(message) * 8
message += b'\x80'
while (len(message) * 8) % 512 != 448:
message += b'\x00'
message += struct.pack('>Q', ml)
return message
def sha256(message):
"""Pure Python SHA-256 implementation"""
if isinstance(message, str):
message = message.encode('utf-8')
message = pad_message(message)
h = H.copy()
for i in range(0, len(message), 64):
chunk = message[i:i+64]
w = list(struct.unpack('>16L', chunk))
# Message schedule: expand to 64 words
for t in range(16, 64):
s0 = rotr(w[t-15], 7) ^ rotr(w[t-15], 18) ^ shr(w[t-15], 3)
s1 = rotr(w[t-2], 17) ^ rotr(w[t-2], 19) ^ shr(w[t-2], 10)
w.append((w[t-16] + s0 + w[t-7] + s1) & 0xFFFFFFFF)
# Initialize working variables
a, b, c, d, e, f, g, hh = h
# Compression function (64 rounds)
for t in range(64):
s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25)
ch = (e & f) ^ ((~e) & g)
temp1 = (hh + s1 + ch + K[t] + w[t]) & 0xFFFFFFFF
s0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22)
maj = (a & b) ^ (a & c) ^ (b & c)
temp2 = (s0 + maj) & 0xFFFFFFFF
# The great shuffle
hh, g, f = g, f, e
e = (d + temp1) & 0xFFFFFFFF
d, c, b = c, b, a
a = (temp1 + temp2) & 0xFFFFFFFF
# Feed-forward: add to the hash
h = [
(h[0] + a) & 0xFFFFFFFF,
(h[1] + b) & 0xFFFFFFFF,
(h[2] + c) & 0xFFFFFFFF,
(h[3] + d) & 0xFFFFFFFF,
(h[4] + e) & 0xFFFFFFFF,
(h[5] + f) & 0xFFFFFFFF,
(h[6] + g) & 0xFFFFFFFF,
(h[7] + hh) & 0xFFFFFFFF
]
return ''.join(f'{x:08x}' for x in h)
if __name__ == '__main__':
# Test the implementation
import hashlib
test = b"Hello, Bitcoin!"
print(f"Input: {test.decode()}")
print(f"SHA-256: {sha256(test)}")
# Compare with hashlib
expected = hashlib.sha256(test).hexdigest()
print(f"Expected: {expected}")
print(f"Match: {sha256(test) == expected}")
3.4 Test Vectors
| Input | SHA-256 Hash |
|---|---|
| (empty string) | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 |
| "a" | ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb |
| "abc" | ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad |
| "My name is jun_a!d" | da7b31346cab757f0af4ca1bbce7fbbd7875cfa15e04d778b546fdc6c576d5d9 |
Run the code above and compare your output with these test vectors. If they match, your implementation is correct!
3.5 Running the Code
- Copy the code into a file named
sha256.py - Run with:
python sha256.py - No installation required โ uses only Python built-ins!