Checksum Verifier — Zig 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 Zig implementation — the same logic the interactive tool runs, in a shareable, citable form.
//! checksum-verifier — MD5 / SHA-1 / SHA-256 / SHA-512 checksums + verification.
//!
//! Language: Zig 0.14 (standard library only)
//! Ported from: src/lib/checksum-verifier.ts (the canonical TypeScript implementation).
//! display source — part of CosmoDev's polyglot tool pages.
//!
//! The TS reference implements MD5 from scratch (RFC 1321) because the Web
//! Crypto API does not support it. Zig's std.crypto ships MD5 natively
//! (`std.crypto.hash.Md5`), so this port uses the standard implementations
//! for all four algorithms — same inputs, same lowercase-hex outputs.
const std = @import("std");
pub const Md5 = std.crypto.hash.Md5;
pub const Sha1 = std.crypto.hash.Sha1;
pub const Sha256 = std.crypto.hash.sha2.Sha256;
pub const Sha512 = std.crypto.hash.sha2.Sha512;
pub const HashAlgorithm = enum {
md5,
sha1,
sha256,
sha512,
};
pub const ChecksumResult = struct {
md5: [32]u8,
sha1: [40]u8,
sha256: [64]u8,
sha512: [128]u8,
/// The lowercase-hex digest for a given algorithm.
pub fn get(self: *const ChecksumResult, algorithm: HashAlgorithm) []const u8 {
return switch (algorithm) {
.md5 => &self.md5,
.sha1 => &self.sha1,
.sha256 => &self.sha256,
.sha512 => &self.sha512,
};
}
};
/// Detect the algorithm from the hex length: 32=MD5, 40=SHA-1, 64=SHA-256,
/// 128=SHA-512. Returns null for non-hex input or an unrecognized length.
pub fn detectHashAlgorithm(hash: []const u8) ?HashAlgorithm {
const h = normalizeHashScratch(hash);
if (h.len == 0) return null;
for (h) |c| {
if (!std.ascii.isHex(c) and !(c >= 'a' and c <= 'f')) return null;
}
return switch (h.len) {
32 => .md5,
40 => .sha1,
64 => .sha256,
128 => .sha512,
else => null,
};
}
/// Compare an expected hash against a computed result. Returns null when the
/// expected string is not a recognizable hex hash of a supported length.
pub fn verifyChecksum(expected: []const u8, result: *const ChecksumResult) ?struct {
algorithm: HashAlgorithm,
match: bool,
} {
const algorithm = detectHashAlgorithm(expected) orelse return null;
var buf: [128]u8 = undefined;
const h = normalizeHash(&buf, expected);
return .{ .algorithm = algorithm, .match = std.mem.eql(u8, h, result.get(algorithm)) };
}
/// Normalize a pasted hash: drop whitespace and `:` grouping, lowercase.
/// Writes into `buf` (128 bytes covers the longest supported hash) and
/// returns the normalized slice.
pub fn normalizeHash(buf: []u8, hash: []const u8) []const u8 {
var n: usize = 0;
for (hash) |c| {
if (c == ' ' or c == '\t' or c == '\r' or c == '\n' or c == ':') continue;
buf[n] = std.ascii.toLower(c);
n += 1;
if (n == buf.len) break;
}
return buf[0..n];
}
/// Non-allocating variant for already-trimmed input (used internally).
fn normalizeHashScratch(hash: []const u8) []const u8 {
// Only valid as a length/hex check source; trailing whitespace is rare in
// detect paths, so trim both ends before measuring.
return std.mem.trim(u8, hash, " \t\r\n");
}
/// One-shot MD5 of a buffer as lowercase hex (`out` must be 32 bytes).
pub fn md5Hex(out: *[32]u8, data: []const u8) void {
var digest: [Md5.digest_length]u8 = undefined;
Md5.hash(data, &digest, .{});
@memcpy(out, &std.fmt.bytesToHex(digest, .lower));
}
/// One-shot SHA-1 as lowercase hex (`out` must be 40 bytes).
pub fn sha1Hex(out: *[40]u8, data: []const u8) void {
var digest: [Sha1.digest_length]u8 = undefined;
Sha1.hash(data, &digest, .{});
@memcpy(out, &std.fmt.bytesToHex(digest, .lower));
}
/// One-shot SHA-256 as lowercase hex (`out` must be 64 bytes).
pub fn sha256Hex(out: *[64]u8, data: []const u8) void {
var digest: [Sha256.digest_length]u8 = undefined;
Sha256.hash(data, &digest, .{});
@memcpy(out, &std.fmt.bytesToHex(digest, .lower));
}
/// One-shot SHA-512 as lowercase hex (`out` must be 128 bytes).
pub fn sha512Hex(out: *[128]u8, data: []const u8) void {
var digest: [Sha512.digest_length]u8 = undefined;
Sha512.hash(data, &digest, .{});
@memcpy(out, &std.fmt.bytesToHex(digest, .lower));
}
/// Compute all four checksums of a buffer, as lowercase hex.
pub fn computeChecksums(data: []const u8) ChecksumResult {
var r: ChecksumResult = undefined;
md5Hex(&r.md5, data);
sha1Hex(&r.sha1, data);
sha256Hex(&r.sha256, data);
sha512Hex(&r.sha512, data);
return r;
}
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 →