Lyo.Hashing 1.0.0

dotnet add package Lyo.Hashing --version 1.0.0
                    
NuGet\Install-Package Lyo.Hashing -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Lyo.Hashing" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Lyo.Hashing" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Lyo.Hashing" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Lyo.Hashing --version 1.0.0
                    
#r "nuget: Lyo.Hashing, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Lyo.Hashing@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Lyo.Hashing&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Lyo.Hashing&version=1.0.0
                    
Install as a Cake Tool

Lyo.Hashing

Digests (SHA-256/384/512), optional MD5 (non-security fingerprints only), non-cryptographic checksums (CRC-32/CRC-32C/CRC-64/Adler-32), hexadecimal encoding (HexEncoding), incremental hashing (HashingStream), sparse file fingerprints (SparseFileFingerprinter), and an injectable façade (IHashingService / HashingService). A process-wide default is exposed as HashingService.Shared (analogous to Random.Shared).

The public contracts are IHashingService, Hasher, HexEncoding, HashingStream, and SparseFileFingerprinter; HashingService is the default IHashingService implementation. With XML doc generation enabled in the repo, IntelliSense surfaces the same summaries as this README. Implementation types use <inheritdoc /> where they mirror the interfaces.

Hex letter casing for service helpers uses TextLetterCase (Upper / Lower) from Lyo.Common.

Features

  • SHA-2 – One-shot buffer hashing on modern .NET; stream hashing via HashAlgorithm
  • MD5 – Legacy compatibility and fingerprints only (not for security)
  • ChecksumsChecksummer / ChecksumStream: CRC-32, CRC-32C, CRC-64/ECMA-182, Adler-32 for corruption detection (not for security)
  • IHashingService – Buffers, streams, files, hex encode/parse, timing-safe equality, HMAC-SHA-256/512, fingerprinting, CreateHashingStream, checksums (Checksum / ChecksumValue / ChecksumFileAsync / CreateChecksumStream)
  • Hasher – Static digest helpers without allocating a service
  • HexEncoding – Encode/decode hex with explicit casing
  • byte[].ToHexString() – Extension in namespace Lyo.Hashing (ByteArrayHexExtensions) — lowercase hex for historical consistency
  • HashingStream – Wrap any Stream; hash updates on read/write; GetHash() / GetHashHex
  • Sparse fingerprintsSparseFileFingerprinter samples large files; MD5 of size + samples (and mtime for very large files)
  • DIAddLyoHashing registers HashingService.Shared or a configured HashingService

Examples

Register with DI

using Lyo.Common.Enums;
using Lyo.Hashing;
using Lyo.Hashing.Registration;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();

// Default process-wide singleton (HashingService.Shared)
services.AddLyoHashing();

// Or custom defaults:
services.AddLyoHashing(o =>
{
    o.DefaultHexLetterCase = TextLetterCase.Lower;
    o.FingerprintDefaults.SampleSize = 256;
});

// Or explicit options instance:
// services.AddLyoHashing(myOptions);

using var sp = services.BuildServiceProvider();
var hashing = sp.GetRequiredService<IHashingService>();

Buffers and streams (service)

var digest = hashing.Hash(ContentDigestAlgorithm.Sha256, payload);
var hex = hashing.ToHex(digest); // uses DefaultHexLetterCase from options

using var ms = new MemoryStream(payload);
var digest2 = hashing.Hash(ContentDigestAlgorithm.Sha256, ms);

var fileDigest = await hashing.HashFileAsync(ContentDigestAlgorithm.Sha512, "/path/to/file.bin", ct);

Static Hasher (no service)

var sha256 = Hasher.ComputeSha256(data);
var sha384 = Hasher.ComputeSha384(span);
var fromStream = Hasher.ComputeSha512(stream);

// Generic SHA-2 selector: digestBits 256, 384, or 512
var any = Hasher.ComputeSha2(256, data);

Hex encode / parse / compare

var upper = HexEncoding.ToHexString(digest, TextLetterCase.Upper);
var lower = HexEncoding.ToHexString(digest, TextLetterCase.Lower);
var roundTrip = HexEncoding.FromHex(upper);

// Timing-safe compare (length mismatch → false)
var ok = hashing.FixedTimeEquals(left, right);

// Parse expected hex then compare (invalid hex → false)
var matches = hashing.EqualsHex(digest, expectedHexChars);

byte[] extension (lowercase hex)

using Lyo.Hashing;

byte[] buf = [0xDE, 0xAD];
var s = buf.ToHexString(); // "dead" — always lowercase

HashingStream

using System.Security.Cryptography;
using var inner = File.OpenRead(path);
using var hashingStream = new HashingStream(inner, SHA256.Create());
var buffer = new byte[8192];
int n;
while ((n = await hashingStream.ReadAsync(buffer, ct)) > 0) { /* process buffer */ }
var digest = hashingStream.GetHash();

Checksums (non-cryptographic)

// Numeric value (32-bit checksums in the low bits)
uint crc = Checksummer.ComputeCrc32(payload); // e.g. 0xCBF43926 for "123456789"
ulong crc64 = Checksummer.ComputeValue(ChecksumAlgorithm.Crc64, payload);

// Big-endian bytes (4 for 32-bit, 8 for CRC-64) — flows through HexEncoding/ToHex
byte[] bytes = Checksummer.Compute(ChecksumAlgorithm.Crc32C, payload);

// Service surface (honors HashingOptions.DefaultHexLetterCase via ToHex)
var svc = HashingService.Shared;
byte[] viaSvc = svc.Checksum(ChecksumAlgorithm.Crc32, payload);
byte[] fileCrc = await svc.ChecksumFileAsync(ChecksumAlgorithm.Crc64, "/path/to/file.bin", ct);

// Incremental over a stream
using var cs = svc.CreateChecksumStream(File.OpenRead(path), ChecksumAlgorithm.Crc32);
var buffer = new byte[8192];
while (cs.Read(buffer, 0, buffer.Length) > 0) { /* ... */ }
ulong value = cs.GetChecksumValue();

Sparse file fingerprint

byte[]? fp = await hashing.FingerprintSampledFileAsync(path, new FileInfo(path).Length, ct: ct);
// null if path does not exist

// Metadata-only (size + last write UTC); no content read
var metaHex = SparseFileFingerprinter.MetadataOnlyHex(fileSize, lastWriteTimeUtc);

HMAC

var mac = hashing.HmacSha256(key, payload);
var mac512 = hashing.HmacSha512(key, payload);

Benchmarks

  • Portfolio suite: hashing

Choosing an API

Situation Prefer
One-off digest in a hot path, no DI Hasher.ComputeSha256 / HexEncoding.ToHexString
Tests, scripts, or Random.Shared-style access HashingService.Shared
ASP.NET / hosted apps Inject IHashingService via AddLyoHashing
Hash while copying or processing a stream HashingStream or IHashingService.CreateHashingStream
Detect accidental corruption (transport, storage) Checksummer / ChecksumStream (CRC / Adler-32)
“Did this huge file change?” without full read FingerprintSampledFileAsync / SparseFileFingerprinter

Dependency injection

Use using Lyo.Hashing.Registration so extension methods AddLyoHashing resolve on IServiceCollection.

HashingStream

Wrap an inner stream; every byte read or written updates the hash. Call GetHash() when finished (or GetHashHex for a string). GetHashString() remains **uppercase ** for backward compatibility; prefer GetHashHex(TextLetterCase) for explicit casing. When created via IHashingService.CreateHashingStream, the correct HashAlgorithm instance is chosen for ContentDigestAlgorithm.

Checksums (non-cryptographic)

For accidental-corruption detection (transport, storage, archive formats) — not for security, signatures, or tamper detection. On modern .NET the CRC-32 and CRC-64 buffer paths delegate to System.IO.Hashing; CRC-32C and Adler-32 use internal implementations that produce identical results across targets.

Sparse file fingerprint

For directory snapshots or “probably unchanged” checks without hashing entire files: Thresholds and sample sizes come from FileFingerprintOptions (service defaults in HashingOptions.FingerprintDefaults).

HMAC

Key lifecycle and storage are caller responsibilities.

HashingOptions

Property Default Description
DefaultHexLetterCase Upper Casing for IHashingService.ToHex when letterCase is omitted
FingerprintDefaults FileFingerprintOptions.Default Defaults passed to FingerprintSampledFileAsync when options argument is null

FileFingerprintOptions

Property Default Description
LargeFileThreshold 100 MiB Above this, extra middle/end samples are read
VeryLargeThreshold 1 GiB Above this, uses mtime + smaller content sample
SampleSize 128 bytes Sample length for start/middle/end reads
VeryLargeSampleSize 64 bytes Content sample size in the very-large path

ContentDigestAlgorithm

Value Meaning
Sha256 SHA-256
Sha384 SHA-384
Sha512 SHA-512
Md5 MD5 — not for security

ChecksumAlgorithm

Value Definition Check value of "123456789"
Crc32 CRC-32 IEEE/ISO-HDLC (zip, gzip, PNG) 0xCBF43926
Crc32C CRC-32C Castagnoli (iSCSI, ext4, SSE4.2) 0xE3069283
Crc64 CRC-64/ECMA-182 (matches System.IO.Hashing) 0x6C40DF5F0B497347
Adler32 Adler-32 (zlib / RFC 1950) 0x091E01DE

Checksummer.Compute and IHashingService.Checksum return big-endian bytes (4 for 32-bit checksums, 8 for CRC-64); ComputeValue / ChecksumValue return the raw numeric value.

Notes

  • MD5 and sparse fingerprints are for compatibility, change detection, or tooling — do not use them for passwords, signatures, or integrity where an attacker can influence inputs.
  • Checksums (CRC / Adler-32) detect accidental corruption only; they are trivially forgeable and must not be used as a security or tamper-detection boundary.
  • HashFileAsync throws if the file is missing; FingerprintSampledFileAsync returns null when the path does not exist.
  • On netstandard2.0, file hashing uses synchronous HashAlgorithm paths under the hood where async OS APIs are unavailable.

Dependencies

Generated from ProjectReference / PackageReference (same model as docs/Lyo.ProjectGraph.html).

  • Lyo.Common — (direct, lyo)
  • Lyo.Exceptions — (direct, lyo)
  • Microsoft.Extensions.DependencyInjection.Abstractions 10.0.5 — (direct, microsoft)
  • System.IO.Hashing 10.0.5 — (direct, microsoft, net10.0)
  • System.Memory 4.6.3 — (direct, microsoft, netstandard2.0)
  • Microsoft.Extensions.Logging.Abstractions 10.0.5 — (transitive, microsoft)
  • System.Text.Json 10.0.5 — (transitive, microsoft, netstandard2.0)
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (11)

Showing the top 5 NuGet packages that depend on Lyo.Hashing:

Package Downloads
Lyo.PackageMetadata

Multi-ecosystem package catalog types and IPackageMetadataStore for stack traces and persistence adapters.

Lyo.Encryption

A production-ready .NET encryption library providing secure, authenticated encryption with support for multiple algorithms (AES-GCM, ChaCha20Poly1305, RSA), key management, and envelope encryption patterns.

Lyo.Diagnostic

Diagnostic utilities for classifying exceptions, decoding and sanitising stack traces, and enriching structured logs for observability.

Lyo.FileMetadataStore

File store service interface and base implementation for metadata and file tracking.

Lyo.FileStorage

File storage service interface and base implementation for file operations.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 247 8/16/2026