Lyo.Hashing
1.0.6
dotnet add package Lyo.Hashing --version 1.0.6
NuGet\Install-Package Lyo.Hashing -Version 1.0.6
<PackageReference Include="Lyo.Hashing" Version="1.0.6" />
<PackageVersion Include="Lyo.Hashing" Version="1.0.6" />
<PackageReference Include="Lyo.Hashing" />
paket add Lyo.Hashing --version 1.0.6
#r "nuget: Lyo.Hashing, 1.0.6"
#:package Lyo.Hashing@1.0.6
#addin nuget:?package=Lyo.Hashing&version=1.0.6
#tool nuget:?package=Lyo.Hashing&version=1.0.6
Lyo.Hashing
Digests (SHA-256/384/512), optional MD5 for 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 injectable IHashingService / HashingService. A process-wide default is 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 shows 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.
- Checksums.
Checksummer/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 namespaceLyo.Hashing(ByteArrayHexExtensions). Lowercase hex for historical consistency.HashingStream. Wrap anyStream. Hash updates on read/write.GetHash()/GetHashHex.- Sparse fingerprints.
SparseFileFingerprintersamples large files. MD5 of size + samples, and mtime for very large files. - DI.
AddLyoHashingregistersHashingService.Sharedor a configuredHashingService.
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 a 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() stays 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.
HashFileAsyncthrows if the file is missing.FingerprintSampledFileAsyncreturnsnullwhen the path does not exist.- On netstandard2.0, file hashing uses synchronous
HashAlgorithmpaths 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.Abstractions10.0.5(direct, microsoft)System.IO.Hashing10.0.5(direct, microsoft, net10.0)System.Memory4.6.3(direct, microsoft, netstandard2.0)Microsoft.Extensions.Logging.Abstractions10.0.5(transitive, microsoft)System.Text.Json10.0.5(transitive, microsoft, netstandard2.0)
| Product | Versions 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. |
-
.NETStandard 2.0
- Lyo.Common (>= 1.0.6)
- Lyo.Exceptions (>= 1.0.6)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.5)
- System.Memory (>= 4.6.3)
-
net10.0
- Lyo.Common (>= 1.0.6)
- Lyo.Exceptions (>= 1.0.6)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.5)
- System.IO.Hashing (>= 10.0.5)
NuGet packages (10)
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.Diagnostic
Diagnostic utilities for classifying exceptions, decoding and sanitising stack traces, and enriching structured logs for observability. |
|
|
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.Api
Core API library for building RESTful APIs with Entity Framework Core, caching, and mapping support. |
|
|
Lyo.FileMetadataStore
File store service interface and base implementation for metadata and file tracking. |
GitHub repositories
This package is not used by any popular GitHub repositories.