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.

๐Ÿ’ก In Simple Terms

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.

๐Ÿ“Š Name Breakdown
  • SHA โ€” Secure Hash Algorithm
  • 256 โ€” Output size in bits (32 bytes / 64 hex characters)
๐Ÿ’ก Analogy: Like a person's unique fingerprint identifies them, SHA-256 creates a unique hash for every unique input. Just as you can't reconstruct a person from their fingerprint, you cannot reverse a hash to find the original input.

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.

Block Hash = SHA-256( SHA-256( Block Header ) )
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.

๐Ÿ’ฐ Why Satoshi Chose SHA-256
  • 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.

Example โ€” Same input, same output
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.

๐Ÿ”’ Why This Matters for Bitcoin

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.

๐Ÿ”’ Why This Matters for Bitcoin

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.

โšก Why This Matters for Bitcoin Mining

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

Input Message (any length)
โ–ผ
Step 1: Padding (add 1, zeros, length)
โ–ผ
Step 2: Split into 512-bit chunks
โ–ผ
Step 3: 16 words (W[0] to W[15])
โ–ผ
Step 4: Message Schedule (expand to 64 words)
โ–ผ
Step 5: Compression Function (64 rounds)
โ–ผ
Step 6: Final Hash (256 bits / 64 hex chars)

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:

Bitcoin Hash = SHA-256( SHA-256( data ) )
โš ๏ธ Why Double SHA-256? (Length Extension Attack Prevention)

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.

๐Ÿ”’ Where Bitcoin Uses Double SHA-256
  • 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)
๐Ÿ’ก Analogy: Single SHA-256 is like a transparent envelope โ€” anyone who sees it can see how it was sealed. Double SHA-256 puts that envelope inside a second, opaque envelope. The attacker only sees the outer envelope and cannot determine how the inner one was sealed.

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 #840,000 (April 2024 Halving Block):
Block Hash: 0000000000000000000320283a032748cef8227873ff4872689bf23f1cda83a5

The number of leading zeros indicates the difficulty of mining. This hash has 19 leading zeros (76 bits).

Probability of finding such a hash = 1 / 16ยนโน = 1 / 2โทโถ โ‰ˆ 1 in 75 sextillion (7.5 ร— 10ยฒยฒ)
๐Ÿ“Š What This Means

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.

๐Ÿ’ก The probability is so small that: You are far more likely to win the Powerball lottery multiple times in a row than to find a single valid Bitcoin block hash with one guess. This is by design!

1.7 Security Level

Attack TypeComplexityPracticality
Pre-image Attack2ยฒโตโถ operationsImpossible (sun would burn out)
Second Pre-image2ยฒโตโถ operationsImpossible
Collision (Birthday)2ยนยฒโธ operationsImpossible with current tech
Quantum (Grover's)2ยนยฒโธ operationsTheoretically possible in distant future
โœ… SHA-256 Remains Secure

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 FunctionOutput SizeBlock SizeRoundsStatusUsed In
SHA-256256 bits512 bits64โœ… SecureBitcoin, SSL/TLS, Blockchain
SHA-512512 bits1024 bits80โœ… SecurePassword hashing, SHA-2 family
SHA-1160 bits512 bits80โŒ Broken (2017)Git (legacy), SSL certificates (deprecated)
MD5128 bits512 bits64โŒ Broken (2004)Checksums only
RIPEMD-160160 bits512 bits80โœ… SecureBitcoin addresses (HASH160)
๐Ÿ” Why SHA-1 and MD5 Are Broken
  • 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

โŒ Myth #1: "SHA-256 can be reversed with enough computing power"

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.

โŒ Myth #2: "Quantum computers will break SHA-256 tomorrow"

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.

โŒ Myth #3: "SHA-256 is only used in Bitcoin"

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

โŒ Myth #4: "Longer inputs produce longer hashes"

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.

For an original message of length L 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
๐Ÿ’ก Example: "abc" (24 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.

Chunk[0:511] โ†’ W[0], W[1], W[2], ..., W[15]
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:

For t = 16 to 63:
ฯƒ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
๐Ÿ”ง Operation Definitions
  • โ‹™ 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.

VariablePrimeInitial Value (Hex)
H0 / a20x6a09e667
H1 / b30xbb67ae85
H2 / c50x3c6ef372
H3 / d70xa54ff53a
H4 / e110x510e527f
H5 / f130x9b05688c
H6 / g170x1f83d9ab
H7 / h190x5be0cd19
๐Ÿ”ข "Nothing Up My Sleeve" Numbers

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.

K[t] = floor( fractional_part( โˆ›prime ) ร— 2ยณยฒ )
K00 = 0x428a2f98
K01 = 0x71374491
K02 = 0xb5c0fbcf
K03 = 0xe9b5dba5
K04 = 0x3956c25b
K05 = 0x59f111f1
K06 = 0x923f82a4
K07 = 0xab1c5ed5
K08 = 0xd807aa98
K09 = 0x12835b01
K10 = 0x243185be
K11 = 0x550c7dc3
K12 = 0x72be5d74
K13 = 0x80deb1fe
K14 = 0x9bdc06a7
K15 = 0xc19bf174
K16 = 0xe49b69c1
K17 = 0xefbe4786
K18 = 0x0fc19dc6
K19 = 0x240ca1cc
K20 = 0x2de92c6f
K21 = 0x4a7484aa
K22 = 0x5cb0a9dc
K23 = 0x76f988da
K24 = 0x983e5152
K25 = 0xa831c66d
K26 = 0xb00327c8
K27 = 0xbf597fc7
K28 = 0xc6e00bf3
K29 = 0xd5a79147
K30 = 0x06ca6351
K31 = 0x14292967
K32 = 0x27b70a85
K33 = 0x2e1b2138
K34 = 0x4d2c6dfc
K35 = 0x53380d13
K36 = 0x650a7354
K37 = 0x766a0abb
K38 = 0x81c2c92e
K39 = 0x92722c85
K40 = 0xa2bfe8a1
K41 = 0xa81a664b
K42 = 0xc24b8b70
K43 = 0xc76c51a3
K44 = 0xd192e819
K45 = 0xd6990624
K46 = 0xf40e3585
K47 = 0x106aa070
K48 = 0x19a4c116
K49 = 0x1e376c08
K50 = 0x2748774c
K51 = 0x34b0bcb5
K52 = 0x391c0cb3
K53 = 0x4ed8aa4a
K54 = 0x5b9cca4f
K55 = 0x682e6ff3
K56 = 0x748f82ee
K57 = 0x78a5636f
K58 = 0x84c87814
K59 = 0x8cc70208
K60 = 0x90befffa
K61 = 0xa4506ceb
K62 = 0xbef9a3f7
K63 = 0xc67178f2

2.6 Step 6: Compression Function (64 Rounds)

This is the heart of SHA-256. Each round updates the 8 working variables.

Per-Round Operations:

ฮฃ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
a, b, c
โ†’
ฮฃ0 + Maj
=
temp2
e, f, g, h
โ†’
ฮฃ1 + Ch
+ K[t] + W[t] =
temp1
โ–ผ
NEW a = temp1 + temp2
NEW e = d + temp1
๐Ÿงฎ Understanding Each Function
  • ฮฃ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 a = initial_a + a
Final b = initial_b + b
...
Final h = initial_h + h

Hash = a || b || c || d || e || f || g || h = 256 bits
โœ… Why Add the Initial Values?

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

Changing one bit in the input changes approximately 50% of output bits
โšก Why This Matters

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!

โœ… Built-in Libraries Used
  • hashlib โ€” for SHA-256 (Python built-in)
  • struct โ€” for byte packing (Python built-in)

3.2 Simple Hash Example

Python - Basic SHA-256
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

sha256_complete.py โ€” Full implementation from scratch
#!/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

InputSHA-256 Hash
(empty string) e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
"a" ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb
"abc" ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
"My name is jun_a!d" da7b31346cab757f0af4ca1bbce7fbbd7875cfa15e04d778b546fdc6c576d5d9
โœ… How to Test Your Implementation

Run the code above and compare your output with these test vectors. If they match, your implementation is correct!

3.5 Running the Code

๐Ÿ’ป How to Run
  • Copy the code into a file named sha256.py
  • Run with: python sha256.py
  • No installation required โ€” uses only Python built-ins!