Skip to content

Checksum Verifier — Kotlin 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 Kotlin implementation — the same logic the interactive tool runs, in a shareable, citable form.

// Checksum Verifier - MD5 / SHA-1 / SHA-256 / SHA-512 digests + verification.
//
// Language: Kotlin 1.9+ (JVM), standard library only.
// Ported from src/lib/checksum-verifier.ts — display source, part of
// CosmoDev's polyglot tool pages. Functionally equivalent to the TS
// reference: same inputs -> same digests, detections and verdicts.
//
// The TS reference implements MD5 from scratch (RFC 1321) because the Web
// Crypto API dropped MD5; the JVM's java.security.MessageDigest still ships
// it — and is itself an incremental update/finalize hasher — so all four
// algorithms use the platform digest here. The verification helpers
// (length-based algorithm detection, normalization, comparison) are ported
// as-is.

import java.security.MessageDigest

/** The four digests the tool computes, as lowercase hex. */
data class ChecksumResult(
    val md5: String,
    val sha1: String,
    val sha256: String,
    val sha512: String,
)

/** Supported hash algorithms, keyed by their hex-digest length. */
enum class HashAlgorithm(val digestName: String, val hexLength: Int) {
    MD5("MD5", 32),
    SHA1("SHA-1", 40),
    SHA256("SHA-256", 64),
    SHA512("SHA-512", 128);

    companion object {
        private val BY_LENGTH = entries.associateBy { it.hexLength }

        /** Detect the algorithm from a hex string length: 32/40/64/128. */
        fun fromHexLength(length: Int): HashAlgorithm? = BY_LENGTH[length]
    }
}

/** An incremental hasher: absorb bytes, then emit the lowercase hex digest. */
interface MD5Hasher {
    /** Absorb bytes. Call repeatedly for chunked input (no length limit). */
    fun update(data: ByteArray)

    /** Pad, append the 64-bit length, and return the lowercase hex digest. */
    fun digestHex(): String
}

/**
 * Create an incremental MD5 hasher (RFC 1321). MessageDigest already keeps
 * the update/finalize split the TS reference hand-rolled; MD5 is only for
 * integrity checks - not security.
 */
fun createMD5(): MD5Hasher {
    val digest = MessageDigest.getInstance("MD5")
    return object : MD5Hasher {
        override fun update(data: ByteArray) = digest.update(data)
        override fun digestHex(): String = digest.digest().toHex()
    }
}

/** Synchronous one-shot MD5 of a byte array, as lowercase hex. */
fun md5(data: ByteArray): String = createMD5().let { hasher ->
    hasher.update(data)
    hasher.digestHex()
}

/** Lowercase hex of a byte array. */
private fun ByteArray.toHex(): String = joinToString("") { "%02x".format(it) }

/** Digest a buffer with the platform hasher and return lowercase hex. */
fun shaHex(algorithm: HashAlgorithm, data: ByteArray): String =
    MessageDigest.getInstance(algorithm.digestName).digest(data).toHex()

/** Compute all four checksums of a buffer (MD5 + 3 platform digests). */
fun computeChecksums(data: ByteArray): ChecksumResult = ChecksumResult(
    md5 = md5(data),
    sha1 = shaHex(HashAlgorithm.SHA1, data),
    sha256 = shaHex(HashAlgorithm.SHA256, data),
    sha512 = shaHex(HashAlgorithm.SHA512, data),
)

/** The verdict of verifyChecksum: which algorithm matched (or did not). */
data class Verification(val algorithm: HashAlgorithm, val match: Boolean)

/** Normalize a pasted hash: drop whitespace and `:` grouping, lowercase. */
private fun normalizeHash(hash: String): String =
    hash.trim().replace(Regex("[\\s:]"), "").lowercase()

/** Detect the algorithm from the hex length: 32=MD5, 40=SHA-1, 64=SHA-256, 128=SHA-512. */
fun detectHashAlgorithm(hash: String): HashAlgorithm? {
    val h = normalizeHash(hash)
    if (!Regex("^[0-9a-f]+$").matches(h)) return null
    return HashAlgorithm.fromHexLength(h.length)
}

/**
 * Compare an expected hash against a computed result. Returns null when the
 * expected string is not a recognizable hex hash of a supported length.
 */
fun verifyChecksum(expected: String, result: ChecksumResult): Verification? {
    val algorithm = detectHashAlgorithm(expected) ?: return null
    val match = normalizeHash(expected) == when (algorithm) {
        HashAlgorithm.MD5 -> result.md5
        HashAlgorithm.SHA1 -> result.sha1
        HashAlgorithm.SHA256 -> result.sha256
        HashAlgorithm.SHA512 -> result.sha512
    }
    return Verification(algorithm, match)
}

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 →