A wax seal that nobody can copy
Centuries ago, a king would close a letter with a blob of melted wax and press his signet ring into it. Anyone could read the seal at a glance and know two things: this came from the king, and nobody opened it on the way. Break the wax to peek inside, and there was no way to re-seal it that looked identical. A cryptographic hash is the digital version of that seal — except instead of a ring pressed into wax, it is a piece of arithmetic pressed onto your data.
A hash function takes any input — a single word, a 4-gigabyte movie, the entire text of every book ever written — and returns a short string of fixed length called the digest (or just the hash). Feed it the same input and you always get the same digest, every time, on any machine on Earth. Change the input by one comma and the digest changes completely. That short string is your data's fingerprint: tiny enough to write down, yet so tightly bound to the original that no two different inputs anyone has ever found share one.
The five promises a good hash makes
Not every function that shrinks data is safe to build money on. The checksum that catches a typo in a credit-card number is a hash of sorts, but you could forge a matching number in seconds. A cryptographic hash is held to a far higher bar — five promises in particular:
- Deterministic. The same input always yields the same digest. There is no randomness, no clock, no salt hidden inside — given the bytes, the answer is fixed.
- Fixed, compact size. One letter or a whole library, the digest is the same length (256 bits for SHA-256). The fingerprint never grows with the data.
- Fast to compute forward. Hashing even a large file takes milliseconds. The difficulty is never in making a digest — only in working backward from one.
- One-way (preimage resistance). Given a digest, there is no shortcut to recover an input that produces it. Your only option is to guess inputs and hash them until one matches — and there are astronomically many to try.
- Collision resistant. Nobody can find two different inputs that hash to the same digest. Collisions must exist in theory (infinite inputs, finite digests), but finding even one must be hopelessly expensive.
Hidden inside that last property is a sixth, almost magical behaviour called the avalanche effect: flip a single bit of the input and, on average, half the bits of the output flip too — unpredictably. There is no smooth dial where a small change to the input nudges the digest a little. Every change, however tiny, throws the whole fingerprint into a fresh, seemingly random pattern. That is exactly what we want to see next, with our own eyes.
Watch the avalanche with your own eyes
Let's hash a few short strings with SHA-256 and read the real digests. You can run these commands yourself on any Mac or Linux terminal — the numbers below are not made up, they are exactly what the function returns.
$ printf 'hello' | shasum -a 256 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824 $ printf 'Hello' | shasum -a 256 # only the 'h' became 'H' 185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969 $ printf 'hello.' | shasum -a 256 # added a single period 1589999b0ca6ef8814283026a9f166d51c70a910671c3d44049755f07f2eb910 $ printf '' | shasum -a 256 # the empty string still has a hash e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Look closely at `hello` versus `Hello`. We changed exactly one bit — lowercase `h` is byte `0x68`, uppercase `H` is `0x48`, a difference in a single binary digit. Yet the two digests share no visible relationship at all. The first begins `2cf24dba…`, the second `185f8db3…`. You cannot peek at one and guess anything about the other. That is the avalanche effect: there is no 'close', no 'warmer / colder'. A digest is either an exact match or it is noise.
Inside SHA-256: arithmetic, not magic
The avalanche looks like sorcery, but SHA-256 is a fixed, public recipe that anyone can implement — there are no secret ingredients. It belongs to a family of designs called Merkle–Damgård constructions, which chew through the message one 512-bit block at a time, stirring each block into a running internal state. Here is the whole thing at a high level:
- Pad the message. Append a single `1` bit, then enough `0` bits, then a 64-bit number recording the original length, so the total is an exact multiple of 512 bits. (Padding the length in is why even the empty string has a rich digest.)
- Start from eight fixed words. Initialise eight 32-bit registers (call them a…h) to constants taken from the fractional parts of the square roots of the first eight primes.
- Expand each block. Stretch the 16 words of the block into 64 words by mixing earlier words with bit-rotations and XORs.
- Run 64 rounds. Each round shuffles the eight registers using additions, rotations, XORs, and the non-linear `Ch` and `Maj` functions, plus a round constant (from the cube roots of the first 64 primes). This is where one changed bit fans out across everything.
- Chain and finish. Add the round results back into the running state, then move to the next block. After the last block, concatenate the eight registers — that 256-bit string is your digest.
H = INITIAL_EIGHT_WORDS # from sqrt of primes
for block in pad(message).blocks_of_512_bits():
w = expand(block) # 16 words -> 64 words
a,b,c,d,e,f,g,h = H
for t in range(64): # 64 rounds of stirring
T1 = h + Sigma1(e) + Ch(e,f,g) + K[t] + w[t]
T2 = Sigma0(a) + Maj(a,b,c)
h,g,f,e = g, f, e, (d + T1)
d,c,b,a = c, b, a, (T1 + T2)
H = [ (x + y) mod 2**32 for x,y in zip(H, [a,b,c,d,e,f,g,h]) ]
digest = concat(H) # 8 x 32 bits = 256-bit fingerprintThe 2^256 wall — and the day SHA-1 fell
Why is reversing a hash hopeless? Because SHA-256 has 2^256 possible outputs, and that number is almost unimaginably large: about 1.16 × 10^77 — comparable to the count of atoms in the observable universe. To break preimage resistance you would have to keep guessing inputs and hashing them until one matched a target. Even a machine trying a trillion trillion (10^24) guesses every second would, on average, need far longer than the age of the universe to land a single hit.
Collisions are a little easier to hunt than preimages, thanks to the birthday paradox: in a room of just 23 people, two probably share a birthday, because you only need some pair to match. The same maths means a digest of n bits can be collided in roughly 2^(n/2) tries, not 2^n. So SHA-256's collision resistance is worth about 2^128 work — still around 3.4 × 10^38, comfortably out of reach for anyone.
But 'hard today' is not 'hard forever', and weaker hashes have genuinely fallen. MD5 was broken so thoroughly that in 2012 the Flame espionage malware forged a Microsoft code-signing certificate using an MD5 collision. SHA-1 held longer, but in February 2017 Google and CWI Amsterdam announced SHAttered: two different PDF files with the same SHA-1 digest, found with about 2^63 computations — expensive, but no longer science fiction. This is exactly why serious systems moved to SHA-256 and its siblings, and why honest engineers say a hash is 'secure as far as we know', never 'secure forever'.
From one fingerprint to an unchangeable history
Now the payoff. A blockchain is, at heart, a stack of blocks where every block header stores the hash of the block before it. Each block is sealed with a fingerprint that depends on its own contents and on that previous hash, so the blocks are chained: block 100's fingerprint is baked into block 101, whose fingerprint is baked into block 102, and so on, all the way back to the very first block.
Suppose a thief wants to quietly edit a payment buried in block 100 — say, raise an amount they received. Changing even one digit changes block 100's contents, which (avalanche!) changes its fingerprint into something unrelated. But block 101 still records the old fingerprint, so the chain visibly snaps. To hide the edit, the thief must re-seal block 100 and recompute every block after it so the fingerprints line up again. Each block also commits to all its transactions through a Merkle tree, so they cannot even sneak a change inside a block without the seal noticing.
That is the whole foundation, and you now hold it. A hash is a fingerprint you cannot fake, cannot reverse, and cannot collide — small enough to pass around, yet so sensitive that one flipped bit rewrites it entirely. Stack those fingerprints into a chain and you get a history that announces its own tampering. Next we'll use exactly this tool to build a Merkle tree, the structure that lets a phone verify one payment is in a block without downloading the whole block.