Checksum Verifier — C source
Drag and drop a file to compute its MD5, SHA-1, SHA-256, and SHA-512 checksums. Paste an expected hash to verify integrity - detect tampered or corrupted downloads instantly. Runs entirely in your browser.
This is the C implementation — the same logic the interactive tool runs, in a shareable, citable form.
/*
* checksum-verifier — compute MD5 / SHA-1 / SHA-256 / SHA-512 of a buffer and
* verify a pasted hash against it.
*
* Language: C (C11, standard library only)
* Source: CosmoDev polyglot showcase port of the Checksum Verifier tool,
* ported from src/lib/checksum-verifier.ts (the canonical TypeScript
* implementation).
* License: display source — part of CosmoDev's polyglot tool pages.
*
* The TS reference hand-rolls MD5 (RFC 1321) because the Web Crypto API refuses
* to implement it, and delegates SHA-1/256/512 to `crypto.subtle.digest`. ISO C
* has no crypto library at all, so all four digests are implemented here from
* their specifications:
*
* MD5 RFC 1321 little-endian words, 64-byte blocks
* SHA-1 FIPS 180-4 big-endian words, 64-byte blocks
* SHA-256 FIPS 180-4 big-endian words, 64-byte blocks
* SHA-512 FIPS 180-4 big-endian 64-bit words, 128-byte blocks
*
* MD5 keeps the reference's incremental update/finalize shape (Md5 below), so
* arbitrarily large inputs can be streamed in chunks. The SHA functions are
* one-shot, matching `shaHex`.
*
* MD5 and SHA-1 are here for integrity checks against published sums — neither
* is collision-resistant, and neither should be used to authenticate anything.
*
* Build: cc -std=c11 checksum-verifier.c
*/
#include <ctype.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* ------------------------------------------------------------------ types --- */
/** The four algorithms the verifier knows. */
typedef enum {
HASH_MD5 = 0,
HASH_SHA1,
HASH_SHA256,
HASH_SHA512,
HASH_ALGO_COUNT,
HASH_UNKNOWN = -1 /* the TS `null` return */
} HashAlgorithm;
/** Hex digest lengths, indexed by HashAlgorithm. */
static const size_t HASH_HEX_LEN[HASH_ALGO_COUNT] = { 32, 40, 64, 128 };
static const char *const HASH_NAMES[HASH_ALGO_COUNT] = {
"md5", "sha1", "sha256", "sha512",
};
/** All four checksums of one buffer, lowercase hex, NUL-terminated. */
typedef struct {
char md5[33];
char sha1[41];
char sha256[65];
char sha512[129];
} ChecksumResult;
const char *hash_algorithm_name(HashAlgorithm a)
{
return (a >= 0 && a < HASH_ALGO_COUNT) ? HASH_NAMES[a] : "unknown";
}
/* ----------------------------------------------------------------- helpers --- */
static uint32_t rotl32(uint32_t x, unsigned n) { return (x << n) | (x >> (32 - n)); }
static uint32_t rotr32(uint32_t x, unsigned n) { return (x >> n) | (x << (32 - n)); }
static uint64_t rotr64(uint64_t x, unsigned n) { return (x >> n) | (x << (64 - n)); }
static void to_hex(const uint8_t *bytes, size_t n, char *out)
{
static const char HEX[] = "0123456789abcdef";
for (size_t i = 0; i < n; i++) {
out[i * 2] = HEX[bytes[i] >> 4];
out[i * 2 + 1] = HEX[bytes[i] & 0x0f];
}
out[n * 2] = '\0';
}
/* ------------------------------------------------------------ MD5, RFC 1321 --- */
/* Per-round shift amounts (RFC 1321 section 3.4). */
static const unsigned MD5_S[64] = {
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21,
};
/* T[i] = floor(2^32 * |sin(i + 1)|), i in radians — the RFC 1321 sine table.
* The TS version derives it with Math.sin at load time; the published constants
* are used here so the port needs no libm and no floating point. */
static const uint32_t MD5_K[64] = {
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
};
/** Incremental MD5 hasher — the C shape of the TS `MD5Hasher` interface. */
typedef struct {
uint32_t state[4];
uint64_t total; /* message length in bytes, before padding */
uint8_t buf[64];
size_t buffered;
} Md5;
void md5_init(Md5 *h)
{
h->state[0] = 0x67452301;
h->state[1] = 0xefcdab89;
h->state[2] = 0x98badcfe;
h->state[3] = 0x10325476;
h->total = 0;
h->buffered = 0;
}
static void md5_transform(Md5 *h, const uint8_t *block)
{
uint32_t m[16];
for (int i = 0; i < 16; i++) {
m[i] = (uint32_t)block[i * 4] | ((uint32_t)block[i * 4 + 1] << 8) |
((uint32_t)block[i * 4 + 2] << 16) | ((uint32_t)block[i * 4 + 3] << 24);
}
uint32_t a = h->state[0], b = h->state[1], c = h->state[2], d = h->state[3];
for (int i = 0; i < 64; i++) {
uint32_t f;
int g;
if (i < 16) {
f = (b & c) | (~b & d);
g = i;
} else if (i < 32) {
f = (d & b) | (~d & c);
g = (5 * i + 1) % 16;
} else if (i < 48) {
f = b ^ c ^ d;
g = (3 * i + 5) % 16;
} else {
f = c ^ (b | ~d);
g = (7 * i) % 16;
}
uint32_t sum = f + a + MD5_K[i] + m[g];
uint32_t tmp = d;
d = c;
c = b;
b = b + rotl32(sum, MD5_S[i]);
a = tmp;
}
h->state[0] += a;
h->state[1] += b;
h->state[2] += c;
h->state[3] += d;
}
/** Absorb bytes. Call repeatedly for chunked input (no length limit). */
void md5_update(Md5 *h, const uint8_t *data, size_t len)
{
h->total += len;
size_t pos = 0;
if (h->buffered > 0) {
size_t take = 64 - h->buffered;
if (take > len) take = len;
memcpy(h->buf + h->buffered, data, take);
h->buffered += take;
pos = take;
if (h->buffered == 64) {
md5_transform(h, h->buf);
h->buffered = 0;
}
}
while (pos + 64 <= len) {
md5_transform(h, data + pos);
pos += 64;
}
if (pos < len) {
memcpy(h->buf + h->buffered, data + pos, len - pos);
h->buffered += len - pos;
}
}
/** Pad, append the 64-bit little-endian bit length, emit lowercase hex. */
void md5_digest_hex(Md5 *h, char out[33])
{
uint64_t bits = h->total * 8; /* captured before padding grows `total` */
uint8_t pad[128] = { 0x80 };
size_t pad_len = (h->buffered < 56) ? 56 - h->buffered : 120 - h->buffered;
md5_update(h, pad, pad_len);
uint8_t len_bytes[8];
for (int i = 0; i < 8; i++) len_bytes[i] = (uint8_t)(bits >> (8 * i));
md5_update(h, len_bytes, 8); /* buffered is exactly 56, so this completes a block */
uint8_t digest[16];
for (int w = 0; w < 4; w++) {
for (int i = 0; i < 4; i++) digest[w * 4 + i] = (uint8_t)(h->state[w] >> (8 * i));
}
to_hex(digest, 16, out);
}
/** One-shot MD5 of a buffer, as lowercase hex. */
void md5_hex(const uint8_t *data, size_t len, char out[33])
{
Md5 h;
md5_init(&h);
md5_update(&h, data, len);
md5_digest_hex(&h, out);
}
/* ---------------------------------------------------- SHA-1, FIPS 180-4 --- */
void sha1_hex(const uint8_t *data, size_t len, char out[41])
{
uint32_t st[5] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 };
uint64_t bits = (uint64_t)len * 8;
/* One padded, length-suffixed stream, walked 64 bytes at a time. */
size_t total = len + 1;
while (total % 64 != 56) total++;
total += 8;
for (size_t off = 0; off < total; off += 64) {
uint8_t block[64];
for (size_t i = 0; i < 64; i++) {
size_t at = off + i;
if (at < len) block[i] = data[at];
else if (at == len) block[i] = 0x80;
else if (at < total - 8) block[i] = 0x00;
else block[i] = (uint8_t)(bits >> (8 * (total - 1 - at)));
}
uint32_t w[80];
for (int i = 0; i < 16; i++) {
w[i] = ((uint32_t)block[i * 4] << 24) | ((uint32_t)block[i * 4 + 1] << 16) |
((uint32_t)block[i * 4 + 2] << 8) | (uint32_t)block[i * 4 + 3];
}
for (int i = 16; i < 80; i++) {
w[i] = rotl32(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
}
uint32_t a = st[0], b = st[1], c = st[2], d = st[3], e = st[4];
for (int i = 0; i < 80; i++) {
uint32_t f, k;
if (i < 20) {
f = (b & c) | (~b & d);
k = 0x5a827999;
} else if (i < 40) {
f = b ^ c ^ d;
k = 0x6ed9eba1;
} else if (i < 60) {
f = (b & c) | (b & d) | (c & d);
k = 0x8f1bbcdc;
} else {
f = b ^ c ^ d;
k = 0xca62c1d6;
}
uint32_t t = rotl32(a, 5) + f + e + k + w[i];
e = d;
d = c;
c = rotl32(b, 30);
b = a;
a = t;
}
st[0] += a; st[1] += b; st[2] += c; st[3] += d; st[4] += e;
}
uint8_t digest[20];
for (int w = 0; w < 5; w++) {
for (int i = 0; i < 4; i++) digest[w * 4 + i] = (uint8_t)(st[w] >> (24 - 8 * i));
}
to_hex(digest, 20, out);
}
/* -------------------------------------------------- SHA-256, FIPS 180-4 --- */
static const uint32_t SHA256_K[64] = {
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,
};
void sha256_hex(const uint8_t *data, size_t len, char out[65])
{
uint32_t st[8] = {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
};
uint64_t bits = (uint64_t)len * 8;
size_t total = len + 1;
while (total % 64 != 56) total++;
total += 8;
for (size_t off = 0; off < total; off += 64) {
uint8_t block[64];
for (size_t i = 0; i < 64; i++) {
size_t at = off + i;
if (at < len) block[i] = data[at];
else if (at == len) block[i] = 0x80;
else if (at < total - 8) block[i] = 0x00;
else block[i] = (uint8_t)(bits >> (8 * (total - 1 - at)));
}
uint32_t w[64];
for (int i = 0; i < 16; i++) {
w[i] = ((uint32_t)block[i * 4] << 24) | ((uint32_t)block[i * 4 + 1] << 16) |
((uint32_t)block[i * 4 + 2] << 8) | (uint32_t)block[i * 4 + 3];
}
for (int i = 16; i < 64; i++) {
uint32_t s0 = rotr32(w[i - 15], 7) ^ rotr32(w[i - 15], 18) ^ (w[i - 15] >> 3);
uint32_t s1 = rotr32(w[i - 2], 17) ^ rotr32(w[i - 2], 19) ^ (w[i - 2] >> 10);
w[i] = w[i - 16] + s0 + w[i - 7] + s1;
}
uint32_t a = st[0], b = st[1], c = st[2], d = st[3];
uint32_t e = st[4], f = st[5], g = st[6], hh = st[7];
for (int i = 0; i < 64; i++) {
uint32_t S1 = rotr32(e, 6) ^ rotr32(e, 11) ^ rotr32(e, 25);
uint32_t ch = (e & f) ^ (~e & g);
uint32_t t1 = hh + S1 + ch + SHA256_K[i] + w[i];
uint32_t S0 = rotr32(a, 2) ^ rotr32(a, 13) ^ rotr32(a, 22);
uint32_t maj = (a & b) ^ (a & c) ^ (b & c);
uint32_t t2 = S0 + maj;
hh = g; g = f; f = e; e = d + t1;
d = c; c = b; b = a; a = t1 + t2;
}
st[0] += a; st[1] += b; st[2] += c; st[3] += d;
st[4] += e; st[5] += f; st[6] += g; st[7] += hh;
}
uint8_t digest[32];
for (int w = 0; w < 8; w++) {
for (int i = 0; i < 4; i++) digest[w * 4 + i] = (uint8_t)(st[w] >> (24 - 8 * i));
}
to_hex(digest, 32, out);
}
/* -------------------------------------------------- SHA-512, FIPS 180-4 --- */
static const uint64_t SHA512_K[80] = {
0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL,
0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL,
0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL,
0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL,
0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL,
0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL,
0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL,
0x92722c851482353bULL, 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL,
0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL,
0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL,
0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL,
0xc67178f2e372532bULL, 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL,
0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL,
0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL,
};
void sha512_hex(const uint8_t *data, size_t len, char out[129])
{
uint64_t st[8] = {
0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 0x3c6ef372fe94f82bULL,
0xa54ff53a5f1d36f1ULL, 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL,
0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL,
};
uint64_t bits = (uint64_t)len * 8;
/* SHA-512 blocks are 128 bytes and reserve 16 for a 128-bit length; only
* the low 64 bits are ever non-zero for buffers this code can address. */
size_t total = len + 1;
while (total % 128 != 112) total++;
total += 16;
for (size_t off = 0; off < total; off += 128) {
uint8_t block[128];
for (size_t i = 0; i < 128; i++) {
size_t at = off + i;
if (at < len) block[i] = data[at];
else if (at == len) block[i] = 0x80;
else if (at < total - 8) block[i] = 0x00;
else block[i] = (uint8_t)(bits >> (8 * (total - 1 - at)));
}
uint64_t w[80];
for (int i = 0; i < 16; i++) {
w[i] = 0;
for (int j = 0; j < 8; j++) w[i] = (w[i] << 8) | block[i * 8 + j];
}
for (int i = 16; i < 80; i++) {
uint64_t s0 = rotr64(w[i - 15], 1) ^ rotr64(w[i - 15], 8) ^ (w[i - 15] >> 7);
uint64_t s1 = rotr64(w[i - 2], 19) ^ rotr64(w[i - 2], 61) ^ (w[i - 2] >> 6);
w[i] = w[i - 16] + s0 + w[i - 7] + s1;
}
uint64_t a = st[0], b = st[1], c = st[2], d = st[3];
uint64_t e = st[4], f = st[5], g = st[6], hh = st[7];
for (int i = 0; i < 80; i++) {
uint64_t S1 = rotr64(e, 14) ^ rotr64(e, 18) ^ rotr64(e, 41);
uint64_t ch = (e & f) ^ (~e & g);
uint64_t t1 = hh + S1 + ch + SHA512_K[i] + w[i];
uint64_t S0 = rotr64(a, 28) ^ rotr64(a, 34) ^ rotr64(a, 39);
uint64_t maj = (a & b) ^ (a & c) ^ (b & c);
uint64_t t2 = S0 + maj;
hh = g; g = f; f = e; e = d + t1;
d = c; c = b; b = a; a = t1 + t2;
}
st[0] += a; st[1] += b; st[2] += c; st[3] += d;
st[4] += e; st[5] += f; st[6] += g; st[7] += hh;
}
uint8_t digest[64];
for (int w = 0; w < 8; w++) {
for (int i = 0; i < 8; i++) digest[w * 8 + i] = (uint8_t)(st[w] >> (56 - 8 * i));
}
to_hex(digest, 64, out);
}
/**
* Digest a buffer with one of the three SHA algorithms, lowercase hex — the C
* shape of `shaHex`. Returns false for an unsupported algorithm (MD5 has its
* own entry point, as in the reference).
*/
bool sha_hex(HashAlgorithm algorithm, const uint8_t *data, size_t len, char *out)
{
switch (algorithm) {
case HASH_SHA1: sha1_hex(data, len, out); return true;
case HASH_SHA256: sha256_hex(data, len, out); return true;
case HASH_SHA512: sha512_hex(data, len, out); return true;
default: return false;
}
}
/** Compute all four checksums of a buffer. */
void compute_checksums(const uint8_t *data, size_t len, ChecksumResult *out)
{
md5_hex(data, len, out->md5);
sha1_hex(data, len, out->sha1);
sha256_hex(data, len, out->sha256);
sha512_hex(data, len, out->sha512);
}
/* ----------------------------------------------------------- verification --- */
/** Normalize a pasted hash: drop whitespace and ':' grouping, lowercase. */
size_t normalize_hash(const char *hash, char *out, size_t cap)
{
size_t k = 0;
if (hash == NULL) {
if (cap > 0) out[0] = '\0';
return 0;
}
for (size_t i = 0; hash[i] != '\0' && k + 1 < cap; i++) {
unsigned char c = (unsigned char)hash[i];
if (isspace(c) || c == ':') continue;
out[k++] = (char)tolower(c);
}
out[k] = '\0';
return k;
}
/** Detect the algorithm from the hex length: 32/40/64/128 → md5/sha1/256/512. */
HashAlgorithm detect_hash_algorithm(const char *hash)
{
char norm[256];
size_t n = normalize_hash(hash, norm, sizeof norm);
if (n == 0) return HASH_UNKNOWN;
for (size_t i = 0; i < n; i++) {
char c = norm[i];
bool hex = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
if (!hex) return HASH_UNKNOWN;
}
for (int a = 0; a < HASH_ALGO_COUNT; a++) {
if (n == HASH_HEX_LEN[a]) return (HashAlgorithm)a;
}
return HASH_UNKNOWN;
}
/** Pick the matching digest out of a ChecksumResult. */
static const char *digest_for(const ChecksumResult *r, HashAlgorithm a)
{
switch (a) {
case HASH_MD5: return r->md5;
case HASH_SHA1: return r->sha1;
case HASH_SHA256: return r->sha256;
case HASH_SHA512: return r->sha512;
default: return NULL;
}
}
/**
* Compare an expected hash against a computed result. Returns false — the TS
* `null` — when `expected` is not a recognizable hex hash of a supported
* length; otherwise fills `*algorithm` and `*match`.
*/
bool verify_checksum(const char *expected, const ChecksumResult *result,
HashAlgorithm *algorithm, bool *match)
{
HashAlgorithm a = detect_hash_algorithm(expected);
if (a == HASH_UNKNOWN) return false;
char norm[256];
normalize_hash(expected, norm, sizeof norm);
*algorithm = a;
*match = strcmp(norm, digest_for(result, a)) == 0;
return true;
}
/* -------------------------------------------------------------------- demo --- */
int main(void)
{
const char *text = "The quick brown fox jumps over the lazy dog";
const uint8_t *data = (const uint8_t *)text;
size_t len = strlen(text);
ChecksumResult r;
compute_checksums(data, len, &r);
printf("input : \"%s\"\n\n", text);
printf("md5 : %s\n", r.md5);
printf("sha1 : %s\n", r.sha1);
printf("sha256: %s\n", r.sha256);
printf("sha512: %s\n\n", r.sha512);
/* Empty input — the padding-only path for every algorithm. */
ChecksumResult e;
compute_checksums((const uint8_t *)"", 0, &e);
printf("md5(\"\") : %s\n", e.md5);
printf("sha256(\"\"): %s\n\n", e.sha256);
/* Verification: pasted sums arrive with grouping and mixed case. */
const char *pasted[] = {
"9E107D9D372BB6826BD81D3542A419D6", /* md5, uppercase */
"2fd4e1c6:7a2d28fc:ed849ee1:bb76e739:1b93eb12 ", /* sha1 with colons */
"d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e593", /* sha256, last digit wrong */
"not-a-hash",
};
for (size_t i = 0; i < sizeof pasted / sizeof pasted[0]; i++) {
HashAlgorithm algo;
bool ok;
if (verify_checksum(pasted[i], &r, &algo, &ok)) {
printf("%-8s -> %s\n", hash_algorithm_name(algo), ok ? "MATCH" : "MISMATCH");
} else {
printf("%-8s -> unrecognized hash\n", "?");
}
}
/* Chunked input yields the same digest as one-shot. */
Md5 h;
md5_init(&h);
md5_update(&h, (const uint8_t *)"The quick brown fox ", 20);
md5_update(&h, (const uint8_t *)"jumps over the lazy dog", 23);
char chunked[33];
md5_digest_hex(&h, chunked);
printf("\nchunked md5 equals one-shot: %s\n",
strcmp(chunked, r.md5) == 0 ? "yes" : "no");
return EXIT_SUCCESS;
}
Also available in 8 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →