Skip to main content

Checksums: Trust but Verify

Introduction

This entire section has quietly rested on an assumption you probably never questioned: that the bytes which arrive are the bytes that were sent. They almost always are. But almost is doing a lot of work in that sentence — because every so often, somewhere between the sender and you, a wire, a chip, or a stray burst of electrical noise flips a single bit. A 0 silently becomes a 1. And just like that, $100 becomes $228, or a pixel goes wrong, or a config value turns to garbage.

Here's what makes this scarier than losing a packet: a lost packet is obvious — it never shows up, so TCP just resends it. A corrupted packet arrives looking perfectly normal, carrying wrong data that your application will happily trust. Silent corruption is worse than loss. So something has to catch it — cheaply, on every packet, billions of times a second — before bad bytes reach your code.

That something is a checksum, and the principle behind it is one of the most reusable ideas in all of computing:

Trust, but verify. Assume the data is probably fine — but attach a tiny fingerprint of it and check that fingerprint anyway, every single time. Verifying is cheap; being silently wrong is expensive.

This is the last networking primitive in the section, and it's a fitting finale because it reaches far beyond the network: the exact same idea guards your file downloads, your hard drives, your databases, and every commit in Git. By the end you'll know how a few extra bytes catch corruption, why not all checksums are equally trustworthy, the one thing a checksum absolutely cannot do (stop a hacker), and why the only check that truly counts is the one at the very ends.

Illustration of checksums as trust but verify. A sender computes a small fingerprint value, the checksum, from its data and sends both together as a packet of data plus checksum. As the packet crosses the wire a single bit flips, corrupting the data. The receiver recomputes the checksum from the data it actually received and compares it to the one that was sent; because they no longer match, the corruption is detected, and the bad packet is discarded and retransmitted, whereas a clean packet whose checksums match is delivered. A key point is that a checksum only detects corruption, it does not fix it; fixing means retransmitting or using error-correcting codes, because detection is cheap and correction is expensive. Packets are checked at multiple layers for defense in depth: a thirty-two-bit cyclic redundancy check in the Ethernet frame at layer two, a header checksum in the IP packet at layer three, and a checksum over the header and data in TCP or UDP at layer four, much of it computed in network-card hardware at line speed. Not all checksums are equal: a simple sum is fast but weak and can be fooled because adding is order-independent, a cyclic redundancy check spreads every bit across the result so a single flipped bit always changes it and it catches bursts of errors, and a cryptographic hash or message authentication code is stronger still and uniquely resists deliberate tampering. The end-to-end principle notes that per-hop link checks are recomputed at every hop, so corruption inside a router can slip past them, which means the application at the very ends is the only check that truly counts. A checksum detects accidental corruption but is not security, because an attacker can simply recompute it, so tamper protection needs a cryptographic message authentication code. The same trust-but-verify pattern appears everywhere: in file downloads verified with a SHA-256 hash, in storage systems, in Git, and in databases. The everyday analogy is the check digit on a credit-card or ISBN number, a small extra digit computed from the rest that catches a mistyped digit.

Bits Flip in Transit

It's easy to imagine the network as a clean pipe that either delivers your bytes perfectly or drops them entirely. Reality is messier. Data crosses copper, fiber, radio, and a gauntlet of routers and switches — and any of them can corrupt a bit along the way:

  • Electrical interference on a cable nudges a signal across the line between a 0 and a 1.
  • A marginal or failing component — a flaky network card, a bad cable, an overheating router, a dodgy optical transceiver — mangles bits as they pass through.
  • Even cosmic rays — high-energy particles from space — occasionally flip a bit sitting in a memory buffer. (Yes, really. It's a documented cause of data corruption.)

Per individual bit, this is astronomically rare — you could send data for a lifetime and never personally see it. But zoom out to the whole internet, moving exabytes of data every day, and "astronomically rare" becomes "constantly happening, somewhere." At that scale, corruption isn't a maybe; it's a steady drizzle.

And remember the crucial asymmetry: a delivered-but-corrupt packet is more dangerous than a lost one. Loss is loud — the data is missing, so the reliability machinery notices and resends. Corruption is quiet — the packet arrives, looks fine, and hands your application data that is subtly, invisibly wrong. Nothing upstream will catch it unless we build something to. So the network's job isn't just "deliver the bytes" — it's "deliver the bytes, and be able to tell when they've been mangled." That second half needs a way to verify the data, without re-sending all of it just to compare. Which is exactly what a checksum is for.

The Checksum: A Fingerprint You Recompute

The mechanism is beautifully simple, and you've almost certainly relied on it without knowing. A checksum is a small, fixed-size value computed from the data — a fingerprint of it. Here's the whole dance:

  1. The sender runs the data through a checksum function, gets a small value, and sends it alongside the data: [ data | checksum ].
  2. The receiver runs the data it actually received through the same function, producing its own checksum.
  3. It compares the two. Match → the data almost certainly arrived intact. Mismatch → the data was corrupted in transit, and we caught it.

That's the entire idea: instead of shipping the data twice to compare it (wasteful), you ship a tiny summary and recompute it at the far end. A few extra bytes buy you the power to verify the whole payload.

If this feels abstract, you use a checksum literally every time you type a card number. The last digit of a credit-card number isn't part of the account — it's a check digit, computed from all the other digits (by the Luhn formula). When you mistype one digit, the website instantly says "invalid card number" — it recomputed the check digit, saw it didn't match, and caught your typo, without asking the bank a thing. ISBNs on books and the digits of a bank routing number work the same way. Every one of those is a checksum: a small redundant value that lets you verify a big value cheaply, and catch accidental changes. A network checksum is that exact trick, applied to every packet — trust the wire, but verify the fingerprint. But notice what the check digit does when your number is wrong: it tells you "that's invalid" — it doesn't tell you the right number. And that limitation is the next crucial idea.

Detect, Don't Fix

Here's a distinction that trips people up constantly: a checksum detects corruption — it does not fix it. When the two checksums disagree, all you've learned is "something in here is wrong." You don't know what bit flipped, you don't know where, and you certainly can't reconstruct the original. The checksum is a smoke alarm, not a fire truck. It tells you there's a problem; putting it out is a separate job.

So what do you do with a detected corruption? You throw the bad data away and get a fresh copy. In the network, that means the corrupt packet is discarded, and — if it was TCP — the missing data triggers a retransmission (the reliability machinery from the transport lesson kicks in, treating corrupt-and-discarded exactly like lost). If it was UDP, the packet is simply gone and the application must cope. Either way, the fix is "send it again," not "repair it in place."

You might wonder: couldn't we make a code that fixes the error instead of just flagging it? You can — they're called error-correcting codes (ECC), and they're used where you can't easily re-send (deep-space probes, storage, RAM). But they're bigger and more expensive: correcting an error requires enough redundancy to figure out which bit is wrong and flip it back, which costs far more space and computation than just noticing that something's wrong. This is the fundamental trade the network makes: detection is cheap, correction is expensive. So the network chooses the cheap option — detect with a tiny checksum, then let a higher layer decide to retransmit. It's the count-the-eggs strategy: the box says "12 eggs," you count and find 11, you know something's wrong — and your fix is to ask for a new box, not to un-break the missing egg. Let's watch the whole detect-and-discard loop happen to a real packet.

See It: Corrupt a Byte

Nothing makes a checksum click like breaking one — so below is a packet carrying its data and the checksum computed from it, traveling from sender to receiver.

First, send it clean and watch the receiver recompute the checksum, see it match, and deliver the data. Then do the fun part: corrupt it on purpose — click a byte to flip a bit — and watch the recomputed checksum stop matching. Corruption caught: the packet is discarded and retransmitted. That's the whole "trust but verify" loop, live. Then meet the two subtleties that separate someone who knows about checksums from someone who truly gets them. Switch to a weak checksum and corrupt two bytes so their errors cancel out — watch the weak sum get fooled while a strong CRC still catches it (not all checksums are equal). And flip on attacker mode, where the data is changed and the checksum is recomputed to match — and watch it sail straight through, undetected. That last one is the whole reason a checksum is not security. Go break some packets.

Trust but Verify. A packet carries its data and a checksum computed from it. Send it clean and the receiver recomputes the checksum, sees it match, and delivers it. Now corrupt it on purpose — click a byte to flip a bit — and watch the recomputed checksum stop matching: corruption caught, packet discarded, retransmit. Then meet the subtleties that separate a good engineer from a great one: switch to a weak checksum and corrupt two bytes so their errors cancel — the weak sum is fooled while a strong CRC still catches it — and flip on attacker mode, where the data is changed and the checksum recomputed to match, sailing right through. That last one is why a checksum is not security.

Checked at Every Layer

A single packet doesn't get verified once — it gets verified several times on its journey, at different layers, each one trusting-but-verifying independently. It's defense in depth, and it maps neatly onto the layers you learned back in the addressing lesson:

  • Layer 2 — the Ethernet frame carries a 32-bit CRC at its tail (called the Frame Check Sequence). Every piece of network hardware checks it on every single frame; if it doesn't match, the frame is silently discarded on the spot and the sender is left to retransmit. This is the front-line guard on every hop.
  • Layer 3 — the IP packet has a checksum over its header, so a corrupted destination address or TTL gets caught before the packet is misrouted.
  • Layer 4 — TCP and UDP each carry a checksum over the header and the data, guarding your actual payload end to end.

So your bytes are fingerprinted and re-verified at the link, at the network layer, and at the transport layer — a corrupt packet has to slip past multiple independent checks to reach your app. And the best part: it's essentially free. Modern network cards compute and verify these checksums in dedicated hardware, at full line rate ("checksum offload"), so your CPU barely lifts a finger. When a check fails, the packet is dropped so quietly you never know it happened — you simply receive correct data, because the corrupt version was caught and thrown away, and (for TCP) resent. Billions of times a second, invisibly, the internet is verifying its own bytes. But if it's checked at so many layers, how does corrupt data ever reach an application? The answer is a subtle and important one — and it hinges on the fact that not all of these checks are equally strong.

Checksums are applied at every layer for defense in depth, drawn as nested envelopes. The L2 Ethernet frame carries a 32-bit CRC (FCS) over the whole frame, recomputed on every hop in hardware, catching cable/radio bursts. Inside, the L3 IP packet checksum covers the header only, guarding the destination address and TTL. Inside that, the L4 TCP/UDP checksum covers header plus your data and is the first check that spans sender to receiver end-to-end. Because link CRCs are recomputed per hop, only the end-to-end check proves the data you got is the data sent.

Not All Checksums Are Equal

"Checksum" is really a family of techniques, and they differ enormously in how much they catch. Getting this hierarchy straight is what separates a working mental model from a dangerous one:

  • A simple checksum — literally add up all the bytes — is fast but weak. Its fatal flaw is that addition is order-independent: since A + B + C equals C + B + A, a simple sum can't detect if bytes were reordered, and two separate errors can cancel each other out and leave the sum unchanged. The internet's classic 16-bit TCP/IP checksum is in this family — and as a result, roughly 1 in every 65,000 corrupt packets slips past it undetected. Cheap, but leaky.
  • A CRC (cyclic redundancy check) is dramatically stronger. Instead of adding, it does polynomial division, which spreads the influence of every single bit across the whole result. The payoff: a single flipped bit always changes the CRC — no cancellation, no reordering blindness — and it catches every burst of consecutive errors up to its length. Ethernet's 32-bit CRC is far more trustworthy than TCP's little 16-bit sum, which is why the strong check lives at the link layer.
  • A cryptographic hash or MAC (SHA-256, HMAC) is stronger still — and has a unique power the others lack: it resists deliberate tampering (more on that next).

The practical lesson is to match the strength to the stakes. A weak, fast sum is perfectly fine for a video frame that'll be obsolete in 30 milliseconds anyway. A file download deserves a proper CRC or hash. Anything involving money, credentials, or code deserves a cryptographic one. And the deeper warning: a matching checksum means very probably intact — not certainly. No checksum is a 100% guarantee; weak ones just have bigger holes. Which raises an unsettling question — if the strong CRC lives at the link layer and gets checked at every hop, how does bad data ever get through? The answer is one of the most important principles in all of system design.

A strength ladder of checksums as three rising bars. WEAK: a simple sum or parity is fast and tiny and catches random single-bit flips but is order-blind, so swapping two bytes leaves the sum identical; it guards accidents only. STRONG: a CRC-32 catches every single-bit flip and burst error and is the network workhorse (Ethernet, ZIP, PNG) but misses a determined attacker who recomputes it. SECURE: a crypto hash or MAC (SHA-256, HMAC) catches any change including deliberate tampering, and a keyed MAC needs the secret. Each rung costs more and guards more — corruption is not tampering, so pick the rung for the threat.

The End-to-End Principle (and Why a Checksum Isn't Security)

Here's the subtle trap that makes application-level verification necessary. Those strong per-hop link checksums (the Ethernet CRC) are recomputed at every hop. A switch verifies the incoming frame's CRC, strips it, does its work, and then generates a brand-new CRC for the outgoing frame. Now imagine the packet gets corrupted inside that switch — after the check passed but before the new CRC is calculated (a bad memory chip, say). The corruption gets wrapped in a fresh, perfectly valid CRC and sent onward, and the next hop's CRC check passes with flying colors. The strong link checksum never notices, because the error happened in the gap it doesn't cover.

This is the famous end-to-end principle (Saltzer, Reed, and Clark, 1984), and it's a load-bearing idea in system design: the only integrity check that truly counts is the one performed at the very ends — the source and destination applications — because any check done by an intermediary can be defeated by corruption that happens between the intermediaries. It's exactly why TCP carries its own checksum on top of the link CRCs, and why critical applications add their own checksum on top of that. A classic 2000 study by Stone and Partridge found real corruption reaching applications despite both TCP and Ethernet checksums — at internet scale, "the network checked it" is not a guarantee. If your data absolutely must be right, verify it yourself, at the end.

And now the single most important caveat in this whole lesson: a checksum is not security. Every checksum we've discussed detects accidental corruption — clumsy wires, flaky chips, cosmic rays. But a checksum offers zero protection against a deliberate attacker. Why? Because if a malicious middleman changes your data, they can simply recompute the checksum to match the altered data — and the receiver sees a perfect match and trusts it completely. Checksums assume the wire is clumsy, not evil. To detect tampering, you need a cryptographic MAC or signature (HMAC, digital signatures — the integrity guarantee from the TLS lesson), which an attacker cannot forge without a secret key. Confusing "integrity against accidents" with "integrity against attackers" is one of the most dangerous mistakes in the field — so burn this in: checksum catches mistakes; crypto catches malice.

The Trade-off

The humble checksum quietly embodies two clean trade-offs that show up all over systems.

First: detection vs correction. Second: weak-and-cheap vs strong-and-costly. A checksum is wonderful precisely because it refuses to over-promise.

It's tiny and nearly free because it only tells you that something is wrong, not what — you pay for recovery separately, with a retransmit. If it tried to fix errors too (error-correcting codes), it would need far more redundancy and computation; for a network that can just ask again, that's a bad bargain, so it detects and defers. Cheap detection, deferred correction.

And within detection, there's a spectrum of strength you pay for:

CheckCostCatchesMisses
simple sumalmost freemost random errorsreordering, canceling pairs (~1 in 65k)
CRCa little moreevery single-bit flip, all short burstsvanishingly little
crypto hash / MACmoreaccidental and deliberate tampering(needs the key)

You match the strength to the stakes — a fast sum for a disposable video frame, a CRC for a file, a cryptographic MAC for money or credentials. The meta-principle underneath both trades, and the thing to carry out of this lesson: verifying is so cheap you should almost always do it — but know exactly what your check actually guarantees. A checksum guarantees probably not accidentally corrupted — not certainly intact, and definitely not safe from attackers. Reach for the strength the stakes demand, and never mistake a smoke alarm for a bodyguard.

Mental-Model Corrections

"A checksum fixes corruption." No — it only detects it. Fixing means retransmitting (TCP) or error-correcting codes. Detection is cheap; correction is expensive.

"A checksum protects against hackers / tampering." No — a plain checksum catches accidental corruption; an attacker just recomputes it to match. Tamper-protection needs a cryptographic MAC / signature (TLS). Checksum catches mistakes; crypto catches malice.

"If the checksum matches, the data is definitely correct." Not certain — weak checksums (the 16-bit TCP/IP one) miss some errors (~1 in 65,000 corrupt packets). Stronger ones (CRC, crypto hashes) miss far fewer, but no checksum is 100%. Match = very probably fine.

"There's one checksum per packet." No — a packet is checked at multiple layers (Ethernet CRC, IP header, TCP/UDP) — defense in depth.

"Checksums are expensive." No — simple arithmetic, usually offloaded to network-card hardware at line rate. Cryptographic hashes cost more, but are still fast.

"The TCP checksum makes my data bulletproof." It's weak, and the end-to-end principle warns that corruption inside a router can slip past the per-hop link CRCs — so critical apps verify their own data. (Real corruption has reached applications despite TCP + Ethernet checksums.)

"Checksums are just a networking thing." They're everywhere: downloads (SHA-256), storage (ZFS / RAID), Git (content hashes), databases, backups. Trust but verify is universal.

Try It Yourself: Break a Fingerprint

Ten minutes and you'll see a one-byte change flip an entire fingerprint upside down. Predict first: if you change a single byte in a big file and recompute its hash, how much of the hash do you think will change — one character? a few? most of it?

  1. Be the receiver. Download any file that publishes a checksum (many downloads list a SHA-256). Run shasum -a 256 <file> on macOS/Linux (or Get-FileHash <file> on Windows) and compare your result to the published value. If they match, you just verified integrity exactly the way the network does — trust the download, but verify the fingerprint.
  2. Corrupt it on purpose. Make a copy, open it in a hex editor (or just append one character), and re-hash it. Watch the hash come out completely different — not one character off, but wholesale different, even for a one-byte change. That dramatic change from a tiny edit is the "avalanche" property of a good checksum, and it's exactly why the tiniest corruption can't hide.
  3. Catch Git doing it. Run git cat-file -p HEAD and git fsck in any repo — Git names every object by the hash of its contents and verifies them, so it can detect if a stored object ever gets corrupted. cksum <file> shows a quick CRC too.

Once you've watched a single flipped byte turn a whole hash into something unrecognizable, the magic evaporates and the mechanism is obvious: any corruption, however small, changes the fingerprint — so recomputing and comparing catches it. That's the whole idea, and now you've done it with your own hands.

Recap & What’s Next

Trust, but verify:

  • Networks don't only lose packets — they occasionally corrupt bits, and a delivered-but-corrupt packet is more dangerous than a lost one (it's silent).
  • A checksum is a small fingerprint the sender attaches; the receiver recomputes and compares — mismatch means corruption, caught. It detects, it doesn't fix (the fix is a retransmit).
  • Packets are checked at multiple layers (Ethernet CRC-32, IP header, TCP/UDP), mostly in hardware, for near-free defense in depth.
  • Not all checksums are equal: a simple sum is weak (and can be fooled), a CRC is strong (every bit flip changes it), a cryptographic hash/MAC is stronger still and uniquely stops tampering.
  • Two rules to carry forever: the end-to-end principle — the app at the ends is the only check that truly counts — and a checksum is not security (it catches mistakes, not malice; use crypto for that). The pattern is everywhere: downloads, disks, Git, databases.

And with that, the Networking section is complete. Step back and look at what you can now trace: a human-friendly name becomes an address (DNS); a packet finds that address across the layers (IP); a connection carries your bytes reliably or fast (TCP/UDP); you speak through it (HTTP), securely (TLS), through proxies, reaching the nearest edge via anycast — and every packet along the way is verified (checksums). You can follow a request from a name typed in a browser to the right program on a server across the planet, and explain every hop. That's a genuinely deep understanding of how the internet works. Time to prove it: the Networking Checkpoint will drop you into a real puzzle — "why is this page slow?" — and have you diagnose it using everything this section taught.