Cryptography
SHA-256 (Merkle-Damgard)
One-way, one fixed size, no matter how big the input
SHA-256 hashes any message to a fixed 256-bit digest by padding it, splitting it into 512-bit blocks, and chaining them through a one-way compression function in the Merkle-Damgard construction.
- Digest size256 bits (64 hex chars)
- Block size512 bits
- Rounds per block64
- Collision resistance≈ 2¹²⁸ work
- Preimage resistance≈ 2²⁵⁶ work
Interactive visualization
Press play, or step through manually. The visualization is yours to drive — try it before reading on.
Watch the 60-second explainer
A condensed visual walkthrough — narrated, captioned, under a minute.
How SHA-256 turns any input into 256 bits
A hash function has an awkward job: accept an input of any length — a single byte or a 10 GB disk image — and return a fixed 256-bit fingerprint that is one-way (you can't run it backwards) and collision-resistant (you can't find two inputs that map to the same output). The trick that makes this possible is the Merkle-Damgard construction, and SHA-256 is its most-deployed example: it backs TLS certificates, Git commit IDs, Bitcoin's proof-of-work, file checksums, and HMAC tokens.
SHA-256 keeps a 256-bit state: eight 32-bit words named a through h, initialized to fixed constants (the first 32 bits of the fractional parts of the square roots of the first eight primes). It processes the message in three phases:
- Pad. Append a single
1bit, then zeros, then the 64-bit message length, so the total is an exact multiple of 512 bits. - Chain. Split the padded message into 512-bit blocks and fold each one into the state:
state = compress(state, block). Each block depends on the result of the previous one — this serial chaining is the heart of Merkle-Damgard. - Finalize. After the last block, the 256-bit state is the digest. Concatenate the eight words and you have your 64-hex-character hash.
The compression function itself does the cryptographic heavy lifting. For each block it builds a 64-entry message schedule W (the 16 input words expanded to 64 via rotation and XOR mixing), then runs 64 rounds. Each round stirs in one schedule word W[t] and one round constant K[t] through the nonlinear functions Ch, Maj, Σ0, and Σ1, diffusing every input bit across the whole state. After 64 rounds the round output is added word-wise (mod 2³²) back to the block's starting state — this feed-forward addition is what makes a single round block hard to invert.
Why the chaining matters
The reason cryptographers reach for Merkle-Damgard is a security reduction: Merkle and Damgard proved independently in 1989 that if the underlying compression function f is collision-resistant, then the full chained hash is collision-resistant too. You only have to design and analyze a small fixed-input primitive; the construction promises the any-length wrapper inherits its strength.
The proof leans entirely on the padding. The final block always encodes the original message length (this is the Merkle-Damgard strengthening or "MD-strengthening"). Without that length field, an attacker could find two messages of different lengths that collide by exploiting the chain's serial structure. With it, the padding is injective: distinct messages always produce distinct padded bit strings, so a collision in the hash forces a collision somewhere inside f.
The same serial chaining has one famous downside, covered below: the digest exposes the full internal state, which enables length-extension attacks. Newer designs (the sponge construction in SHA-3, or the truncated SHA-512/256) drop Merkle-Damgard precisely to close that gap.
When to use SHA-256 — and when not to
- Integrity checks and content addressing. Git names every object by its hash; package managers verify downloads; deduplicating backups use the digest as a key. SHA-256 is ideal here — fast, deterministic, collision-resistant.
- Digital signatures and certificates. You sign the 256-bit hash of a document, not the document. TLS certificate fingerprints are SHA-256.
- Proof-of-work. Bitcoin mining is a brute-force search for an input whose double-SHA-256 starts with enough zero bits.
- Message authentication — but wrapped in HMAC. Never authenticate with raw
SHA256(key || message); the length-extension flaw breaks it. Use HMAC-SHA-256.
Do not use SHA-256 directly for password storage. It is too fast — a GPU computes billions per second, so a leaked database of unsalted SHA-256 password hashes is crackable in hours. Passwords need a deliberately slow, salted, memory-hard KDF: bcrypt, scrypt, or Argon2. And don't use it for a random unique ID where you only need uniqueness, not security — a UUID or Snowflake ID is cheaper.
SHA-256 vs other hash functions
| SHA-256 | SHA-1 | MD5 | SHA-512 | SHA-3-256 | BLAKE3 | |
|---|---|---|---|---|---|---|
| Digest size | 256 bits | 160 bits | 128 bits | 512 bits | 256 bits | 256 bits (extendable) |
| Construction | Merkle-Damgard | Merkle-Damgard | Merkle-Damgard | Merkle-Damgard | Sponge (Keccak) | Merkle tree |
| Block / word size | 512 b / 32 b | 512 b / 32 b | 512 b / 32 b | 1024 b / 64 b | 1088 b rate | 64-byte chunks |
| Collision status | none known | broken (2017) | trivially broken | none known | none known | none known |
| Length-extension | vulnerable | vulnerable | vulnerable | vulnerable | immune | immune |
| Parallelizable | no (serial chain) | no | no | no | no | yes (tree) |
| Throughput (1 core) | ~0.5–2 GB/s* | faster than SHA-256 | fastest, insecure | often faster on 64-bit CPUs | slower than SHA-256 | multi-GB/s |
| Use it for | certificates, Git, PoW | legacy only | non-security checksums only | 64-bit servers, KDF base | new protocols | fast file hashing |
*With the Intel/ARM SHA hardware extensions enabled; pure software SHA-256 is several times slower. The headline lesson: MD5 and SHA-1 share SHA-256's Merkle-Damgard skeleton but have weaker compression functions and shorter digests, so both have practical collisions. SHA-512 is the same design with 64-bit words and a 512-bit state — counterintuitively often faster than SHA-256 on 64-bit CPUs because it does fewer rounds per byte. SHA-3 abandons Merkle-Damgard entirely for a sponge, which is why it's immune to length extension.
What the numbers actually say
- Collision resistance ≈ 2¹²⁸ (the birthday bound). That's about 3.4 × 10³⁸ hashes. The Bitcoin network in mid-2024 ran near 6 × 10²⁰ SHA-256 hashes per second — the largest sustained hash effort in history. At that rate a birthday collision search would still take roughly the current age of the universe.
- Preimage resistance ≈ 2²⁵⁶. Reversing a digest to find any input is squared-harder than finding a collision — out of reach for any conceivable classical computer. Grover's algorithm on a hypothetical large quantum computer would cut this to ~2¹²⁸, still astronomically safe, which is why SHA-256 is considered quantum-resistant for preimages.
- One block ≈ 64 rounds. Each 512-bit block costs a fixed 64 rounds of work, so SHA-256 is exactly linear in input length: hashing a 2× larger file takes 2× longer, with no surprises.
- Throughput. Software SHA-256 runs around 0.2–0.5 GB/s per core; with the SHA-NI / ARMv8 crypto extensions it jumps to 1–2 GB/s. That gap — roughly 4–8× — is why "is the SHA extension available?" matters for any high-volume hashing service.
- Empty-string digest.
SHA256("")=e3b0c442…b855, a fixed 64-hex value worth memorizing as a sanity check that your implementation pads correctly.
JavaScript implementation
Modern browsers and Node ship SHA-256 in the Web Crypto API — use it in production, never hand-rolled crypto. The async one-liner:
async function sha256(message) {
const data = new TextEncoder().encode(message); // UTF-8 bytes
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
return [...new Uint8Array(hashBuffer)]
.map(b => b.toString(16).padStart(2, '0'))
.join(''); // 64 hex chars
}
await sha256(''); // e3b0c442...b855
await sha256('abc'); // ba7816bf...ad15
To see the Merkle-Damgard chain, here is the padding and block loop spelled out (the inner compress is the standard 64-round core, omitted for length):
function padMessage(bytes) {
const bitLen = bytes.length * 8;
const out = [...bytes, 0x80]; // append the mandatory 1 bit
while ((out.length % 64) !== 56) out.push(0); // zero-fill to 56 mod 64
// append 64-bit big-endian length
for (let i = 7; i >= 0; i--) out.push((bitLen / 2 ** (8 * i)) & 0xff);
return out;
}
function sha256Bytes(bytes) {
let state = [...H0]; // eight 32-bit init constants
const padded = padMessage(bytes);
for (let i = 0; i < padded.length; i += 64) { // one 512-bit block at a time
state = compress(state, padded.slice(i, i + 64)); // chain: depends on previous
}
return state.map(w => (w >>> 0).toString(16).padStart(8, '0')).join('');
}
Python implementation
The standard library wraps OpenSSL's optimized SHA-256:
import hashlib
def sha256(message: str) -> str:
return hashlib.sha256(message.encode('utf-8')).hexdigest()
sha256('') # 'e3b0c442...b855'
sha256('abc') # 'ba7816bf...ad15'
# Streaming a large file without loading it into memory —
# this IS the Merkle-Damgard chain, one block-batch at a time:
def sha256_file(path: str) -> str:
h = hashlib.sha256()
with open(path, 'rb') as f:
for chunk in iter(lambda: f.read(65536), b''):
h.update(chunk) # folds each chunk into the running state
return h.hexdigest()
The update() / hexdigest() split is the construction laid bare: update advances the chain, hexdigest reads out the final state. For authentication, reach for the keyed wrapper instead of concatenating a secret:
import hmac, hashlib
def tag(key: bytes, message: bytes) -> str:
# HMAC defeats length extension: SHA256(key || msg) does NOT.
return hmac.new(key, message, hashlib.sha256).hexdigest()
Variants worth knowing
SHA-224. Same engine as SHA-256 with different initial constants, truncated to 224 bits. The truncation alone blunts length extension because the attacker can't see the full 256-bit state.
SHA-512 and SHA-384. The SHA-2 family member built on 64-bit words, a 1024-bit block, and 80 rounds. On 64-bit CPUs it often beats SHA-256 on throughput. SHA-384 is SHA-512 truncated to 384 bits.
SHA-512/256. SHA-512 truncated to 256 bits with distinct initial constants. Same 256-bit output as SHA-256, but the truncation makes it immune to length extension — a drop-in upgrade when that attack is a concern and you're on a 64-bit machine.
HMAC-SHA-256. Not a hash but a keyed MAC: H((key ⊕ opad) || H((key ⊕ ipad) || msg)). The nested structure is specifically engineered to neutralize Merkle-Damgard's length-extension weakness; it's the right tool for tokens and API signatures.
SHA-3 (Keccak). A different beast entirely — a sponge construction, not Merkle-Damgard. Standardized in 2015 as a structural backup in case a flaw were ever found in SHA-2. It's immune to length extension by design but generally slower in software than SHA-256.
Common bugs and edge cases
- Using
SHA256(secret || msg)for authentication. The single most common SHA-256 security bug. The digest leaks the internal state, so an attacker can append data and forge a valid hash. Always use HMAC-SHA-256. - Hashing passwords with raw SHA-256. It's far too fast. A leaked unsalted table falls to GPU brute force and rainbow tables in hours. Use bcrypt, scrypt, or Argon2.
- Encoding ambiguity. SHA-256 hashes bytes, not strings.
SHA256("café")differs between UTF-8 and Latin-1. Pin the encoding explicitly or two systems will disagree on the "same" input. - 32-bit overflow in hand-rolled code. JavaScript's bitwise ops are signed 32-bit; forgetting the final
>>> 0to coerce back to unsigned yields negative words and a wrong digest. This is why the empty-string test vector exists. - Forgetting the length field on the last block. Drop the 64-bit length and you've broken MD-strengthening — collisions become findable and your hash is no longer SHA-256.
- Confusing hex, base64, and raw-byte digests. A SHA-256 digest is 32 bytes, 64 hex characters, or 44 base64 characters. Comparing a hex digest to a base64 one always fails; normalize the representation before comparing.
Frequently asked questions
Why is SHA-256 called a Merkle-Damgard construction?
Merkle-Damgard is the design pattern that turns a fixed-input compression function into a hash that accepts any-length input. You pad the message to a whole number of blocks, then fold each block into a running state: state = compress(state, block). SHA-256, SHA-1, and MD5 all follow it; Ralph Merkle and Ivan Damgard proved independently in 1989 that if the compression function is collision-resistant, the whole chained hash is too.
What is the length-extension attack and does SHA-256 have it?
Yes, SHA-256 is vulnerable. Because the final digest is just the internal state after the last block, an attacker who knows hash(secret || message) and the length of the secret can resume the chain and compute hash(secret || message || padding || extra) without ever seeing the secret. The fix is to never use raw SHA-256 for authentication — use HMAC-SHA-256, which wraps the message in two keyed hashes, or SHA-512/256 / SHA-3, which are not extendable.
How many SHA-256 hashes would it take to find a collision?
By the birthday bound, about 2^128 hashes — roughly 3.4 × 10^38. Even at the entire Bitcoin network's mid-2024 rate of ~6 × 10^20 hashes per second, a collision search would take on the order of the age of the universe. No SHA-256 collision has ever been found; SHA-1, with a 160-bit digest and 2^80 birthday bound, was broken in practice in 2017.
What is the padding rule in SHA-256?
Append a single 1 bit, then enough 0 bits so the length is 64 bits short of a multiple of 512, then append the original message length as a 64-bit big-endian integer. The length field and the mandatory 1 bit are what make the padding injective — two different messages can never pad to the same bit string, which is required for collision resistance.
Why does SHA-256 produce exactly 256 bits no matter the input size?
The internal state is eight 32-bit words = 256 bits, and each compression step maps a 256-bit state plus a 512-bit block back to a 256-bit state. After the last block the state is the digest. A one-gigabyte file and an empty string both leave the chain in a 256-bit state, so both digests are 64 hex characters long.
Is SHA-256 reversible, and can it ever fail to be one-way?
It is designed to be one-way: given a digest there is no known method faster than brute force (2^256 work) to recover an input. But short or low-entropy inputs like passwords are recoverable by precomputed rainbow tables or GPU brute force — modern GPUs do billions of SHA-256 per second. That is why password storage uses a slow, salted KDF such as bcrypt, scrypt, or Argon2 instead of raw SHA-256.