Networking in C · intermediate · ~20 min
Compute and verify CRC / Internet checksum on byte streams.
Checksums detect accidental corruption — single bit flips, dropped bytes — by mixing all input bytes into a fixed-size output. They are not security primitives; for adversarial integrity you need a MAC like HMAC.
Every wire protocol and binary format carries one. Reverse engineering a binary blob often starts with 'find the checksum'.
Internet checksum. One's complement sum of 16-bit words. Used in IP, TCP, UDP, ICMP. Cheap; detects many transmission errors.
CRC. Polynomial division in GF(2). CRC-8 (1-Wire), CRC-16 (XMODEM), CRC-32 (Ethernet, ZIP, PNG). Stronger than the Internet checksum but still not cryptographic.
Cryptographic hash. SHA-256, BLAKE3 — adversarial integrity. Use these (or HMAC) when an attacker may try to forge the input.
Pentester mindset. A checksum at the protocol layer doesn't stop tampering; an attacker who can write the data can recompute the checksum. Don't confuse the two.
Defensive coding habit. Use the right primitive: checksum for accidental corruption, MAC for adversarial integrity. Never invent your own.
uint16_t inet_checksum(const uint8_t *buf, size_t len); /* one's complement sum */
uint32_t crc32(const uint8_t *buf, size_t len); /* poly 0xEDB88320 */
A checksum compresses a buffer into a small integer such that any single byte change produces a different output. The Internet checksum (one's complement sum) is in every IP/TCP/UDP header; CRC-8/16/32 are in 1-Wire, Ethernet, ZIP, PNG, USB.
unsigned char crc8(const unsigned char *d, size_t n){
unsigned char c = 0;
for (size_t i = 0; i < n; i++) {
c ^= d[i];
for (int b = 0; b < 8; b++)
c = (c & 0x80) ? (c << 1) ^ 0x07 : c << 1;
}
return c;
}
/* Internet checksum kernel */
uint32_t sum = 0;
for (size_t i = 0; i + 1 < len; i += 2)
sum += (buf[i] << 8) | buf[i+1];
while (sum >> 16) sum = (sum & 0xFFFF) + (sum >> 16);
return ~(uint16_t)sum;
The canonical CRC-8 test vector is '123456789' → 0xF4. Match against that.
Always cap the buffer length; never read past len. Sums use uint32_t to hold the carry.
TCP/IP, Ethernet frames, ZIP/PNG/ELF files, USB packets, 1-Wire sensors.
Checksum for accidents, MAC for adversaries. Use the right primitive; never invent your own.