Hashing and checksums

The SharpProspero.Security namespace computes message digests and checksums with no system module, so a build can verify a downloaded file against a published checksum, compare two files, or authenticate a message with a shared secret. The digests share one small base type, HashAlgorithm, and offer the same one-shot, file, and streaming forms. Crc32 names its one-shot helpers Compute and ComputeFileValue instead, and Hmac stands outside the base type with static helpers and a streaming form but no file helper. Each returns the identical result on the device and in tests.

On this page

Choose an algorithm

Pick by what the check needs to catch. Sha256 (or Sha512 for a wider digest) guards against deliberate tampering. Md5 and Sha1 exist to match sums that other tools still publish, not to resist an attacker. Crc32 is a fast integer check for accidental corruption. Sha3 is the Keccak-based alternative to the SHA-2 family for a build that requires it.

Type Digest Use it for
Crc32 4 bytes fast check against accidental corruption
Md5 16 bytes matching MD5 sums other tools publish
Sha1 20 bytes matching SHA-1 sums other tools publish
Sha256 32 bytes verifying a file against a published checksum
Sha512 64 bytes the same, with a wider digest
Sha3 32, 48 or 64 bytes the Keccak alternative, width set by Sha3Variant
Hmac matches the chosen hash proving a message was not changed without the key

Crc32, Md5 and Sha1 do not resist a deliberate change. When the check has to survive an attacker, reach for Sha256, Sha512, Sha3, or a keyed Hmac.

One-shot digests

The common case needs no object. Each hash exposes static helpers that take a block of bytes or a file path and return either raw bytes or a lowercase hexadecimal string.

using SharpProspero.Security;

string checksum = Sha256.HashFileHex("/app0/data.bin");   // lowercase hex
bool matches = checksum == published;

byte[] digest = Sha256.Hash(payload);                     // 32 raw bytes
string hex = Sha256.HashHex(payload);

uint crc = Crc32.ComputeFileValue("/app0/archive.bin");

Sha1, Sha512 and Md5 carry the same four helpers — Hash, HashHex, HashFile, HashFileHex. The HashFile* forms read the file in fixed-size blocks, so a file of any size is hashed without holding all of it in memory.

Stream a digest in chunks

When the data arrives in pieces, construct the hash, feed it with Update as many times as needed in any chunk sizes, then read the result. Finish() returns a new array, FinishHex() returns hexadecimal, and Finish(Span<byte>) writes into a buffer you own.

var sha = new Sha256();
sha.Update(header);
sha.Update(body);
string tag = sha.FinishHex();
Span<byte> digest = stackalloc byte[32];      // Sha256.HashSize
var sha = new Sha256();
sha.Update(payload);
sha.Finish(digest);

A hasher accumulates state and finalizes with a length pad, so a single instance computes one digest. Use a fresh object for each value rather than reusing one after Finish.

The hashing base type

Every digest derives from HashAlgorithm, so code can accept any of them through the base. It defines HashSize, Update, the two Finish overloads and FinishHex, and ComputeFile(string), which streams a file through the running digest and returns the result.

static string Checksum(HashAlgorithm hash, ReadOnlySpan<byte> data)
{
    hash.Update(data);
    return hash.FinishHex();
}

string a = Checksum(new Sha256(), payload);
string b = Checksum(new Sha3(Sha3Variant.Bits512), payload);

Md5, Sha1 and Sha256 also share BlockHashAlgorithm, the machinery for digests that process data in 64-byte blocks and finish with a length pad. Sha512 works in 128-byte blocks and Sha3 absorbs a variable-size rate, so both derive from HashAlgorithm directly. You rarely touch these two intermediate types; they exist so the concrete hashes only supply their block transform.

CRC-32

Crc32 is the reflected variant that zip archives and PNG files use. Read the running value at any time from Value, or take a one-shot checksum with Compute or ComputeFileValue.

uint value = Crc32.Compute(payload);

var running = new Crc32();
running.Update(chunk1);
running.Update(chunk2);
uint total = running.Value;

SHA-3

Sha3 computes the Keccak sponge (FIPS 202). Pick the width with Sha3VariantBits256, Bits384 or Bits512, which name the digest size in bits. The default is SHA3-256.

string sha3 = Sha3.HashHex(payload, Sha3Variant.Bits512);       // SHA3-512, 64 bytes
byte[] digest = Sha3.HashFile("/app0/data.bin", Sha3Variant.Bits256);

var streaming = new Sha3(Sha3Variant.Bits384);
streaming.Update(part1);
streaming.Update(part2);
byte[] result = streaming.Finish();

Keyed digests (HMAC)

Hmac is a hash that also depends on a secret key, so a matching tag proves a message was not changed by anyone without that key. The static helpers cover the common hashes:

string tag = Hmac.Sha256Hex(key, message);   // also Sha512Hex, Sha1Hex, Md5Hex
bool authentic = tag == expected;

Hmac.Sha256, Sha512, Sha1 and Md5 take the same arguments and return the tag as raw bytes, for a caller comparing against bytes from a protocol rather than against text.

For a keyed stream, construct one over any hash factory and its internal block size, then feed it with Update:

var mac = new Hmac(key, static () => new Sha256(), blockSize: 64);
mac.Update(part1);
mac.Update(part2);
byte[] tag = mac.Finish();

HashSize reports the tag width in bytes, the same as the underlying hash, and sizes a buffer for Finish(Span<byte>).

The blockSize argument is the underlying hash’s internal block size in bytes, not its digest size. It is 64 for SHA-256, SHA-1 and MD5, and 128 for SHA-512. The static helpers pass the right value for you.

Block sizes

The internal block size is the width of one transform step and the value Hmac needs. It differs from the digest size and, for Sha3, changes with the chosen width (the sponge rate).

Algorithm Digest Internal block
MD5 16 bytes 64 bytes
SHA-1 20 bytes 64 bytes
SHA-256 32 bytes 64 bytes
SHA-512 64 bytes 128 bytes
SHA3-256 32 bytes 136 bytes
SHA3-384 48 bytes 104 bytes
SHA3-512 64 bytes 72 bytes

For byte buffers and encodings that feed these hashes — span readers, ring buffers and Base-N text — see Buffers and encodings.


Copyright © SvenGDK 2026. Distributed under the GPL-3.0-or-later license.

This site uses Just the Docs, a documentation theme for Jekyll.