DeferredFinalBlockHashAlgorithm Class
Definition
- Namespace
- Bodu.Security.Cryptography
- Assembly
- Bodu.Security.Cryptography.dll
- Package
- Bodu.Security.Cryptography 1.2.0
Base class for hash algorithms that defer compression of the final full block until HashFinal() so that a finalization flag may be raised on the last compression call (the Blake-family shape). Owns the defer-on-full-block buffering loop and the zero-pad-then-finalize orchestration; the residual buffer, running byte counter, and disposal latch are inherited from BufferedBlockHashAlgorithm.
public abstract class DeferredFinalBlockHashAlgorithm : BufferedBlockHashAlgorithm, ICryptoTransform, IDisposable
- Inheritance
-
DeferredFinalBlockHashAlgorithm
- Implements
- Derived
- Inherited Members
- Extension Methods
Examples
// Consume through a concrete BLAKE-family derivative - the base class defers the final
// block until HashFinal so the compression call can carry isFinal: true.
using HashAlgorithm hash = new Blake3();
byte[] digest = hash.ComputeHash("hello"u8.ToArray());
// Streaming use - the deferral remains transparent across TransformBlock calls.
using HashAlgorithm streaming = new Blake2s();
streaming.TransformBlock(buffer1, 0, buffer1.Length, null, 0);
streaming.TransformBlock(buffer2, 0, buffer2.Length, null, 0);
streaming.TransformFinalBlock(Array.Empty<byte>(), 0, 0);
byte[] result = streaming.Hash!;
Remarks
Unlike Merkle–Damgård hashes, Blake-family compression functions take a per-block byte counter and an
isFinal flag. The final compression call must carry both the actual total byte count of the message and
isFinal: true. This base class implements the consequent invariant: a block that fills the residual buffer
exactly is not compressed at the point it is filled, because the algorithm cannot yet tell whether the
caller will supply more data. Compression of that block is deferred until either (a) the next
HashCore(ReadOnlySpan<byte>) call provides at least one more byte (in which case the
pending block is compressed with isFinal: false) or (b) HashFinal() is called (in
which case the pending block is compressed with isFinal: true).
The inherited Bodu.Security.Cryptography.BufferedBlockHashAlgorithm._totalBytes field stores the total number of bytes already compressed (i.e. consumed from the residual buffer by previous ProcessBlock(ReadOnlySpan<byte>, ulong, bool) calls). Bytes still held in the residual buffer are not included in this total — they contribute to the counter only at the moment they are compressed.
Derived classes must implement the following:
-
Initialize() resets the algorithm-specific chaining variables to their initial state.
Override and call
base.Initialize()first. - ProcessBlock(ReadOnlySpan<byte>, ulong, bool) compresses a single block, receiving the per-block counter and the finalization flag.
- ProcessFinalBlock() extracts the digest from the chaining variables after the final compression has run.
When to derive from this class. Pick DeferredFinalBlockHashAlgorithm for the BLAKE family and any other algorithm whose compression function takes an explicit "is this the final block?" flag rather than padding the trailing partial block with a length encoding - Blake3 is the canonical user. For BLAKE2-style hashes that also accept an optional secret key (Blake2b, Blake2s) derive from KeyedDeferredFinalBlockHashAlgorithm, which adds RFC 7693 key-block handling on top of this base. For Merkle-Damgård hashes (SHA-2, Tiger, Whirlpool) use BlockHashAlgorithm.
Constructors
DeferredFinalBlockHashAlgorithm(int)
Initializes a new instance of the DeferredFinalBlockHashAlgorithm class with the specified input block size.
protected DeferredFinalBlockHashAlgorithm(int blockSize)
Parameters
blockSizeintThe fixed size, in bits, of each block consumed by the algorithm. Must be a positive multiple of 8.
Exceptions
- ArgumentOutOfRangeException
Thrown when
blockSizeis less than or equal to zero.
Methods
HashCore(ReadOnlySpan<byte>)
Consumes the supplied input span using the deferred-final-block buffering rule. At the start of every iteration
of the inner loop, if the residual buffer is already full and the caller has supplied at least one more byte,
the held block is compressed with isFinal: false and the counter is advanced before the new byte is
copied in. Compression of a block that fills the buffer exactly is otherwise deferred to
HashFinal() so that isFinal: true may be raised on it.
protected override void HashCore(ReadOnlySpan<byte> source)
Parameters
sourceReadOnlySpan<byte>The input bytes to consume. May be empty, partial, exact-block, or multi-block in length.
Exceptions
- ObjectDisposedException
The algorithm instance has been disposed.
- CryptographicUnexpectedOperationException
On target frameworks prior to .NET 6, the hash algorithm has already been finalized and cannot accept more input.
HashFinal()
Finalizes the hash computation. Zero-pads the residual buffer (if not already full) up to the block size,
performs the final compression with isFinal: true and a counter equal to the total bytes consumed (
Bodu.Security.Cryptography.BufferedBlockHashAlgorithm._totalBytes plus the residual byte count), and returns the digest
produced by ProcessFinalBlock().
protected override byte[] HashFinal()
Returns
- byte[]
The computed hash bytes as produced by ProcessFinalBlock().
Exceptions
- ObjectDisposedException
The algorithm instance has been disposed.
- CryptographicUnexpectedOperationException
On target frameworks prior to .NET 6, the hash computation has already been finalized.
ProcessBlock(ReadOnlySpan<byte>, ulong, bool)
Compresses a single full block of input using the algorithm's internal compression function.
protected abstract void ProcessBlock(ReadOnlySpan<byte> block, ulong totalBytesIncludingThisBlock, bool isFinal)
Parameters
blockReadOnlySpan<byte>The input block to compress. Always exactly BlockSize bytes long, possibly zero-padded by HashFinal() when
isFinalis true.totalBytesIncludingThisBlockulongThe cumulative byte count including the bytes in
blockbeing compressed. For mid-stream blocks this equals the previous total plus BlockSize; for the final compression it equals the previous total plus the residual byte count (which may be in the range[0, BlockSize / 8]).isFinalbooltrue for the last compression call (raised by HashFinal()); otherwise false. Algorithms that maintain a finalization flag should consult this parameter rather than tracking message length internally.
Remarks
Implementations must not retain a reference to the supplied span beyond the call — the underlying buffer is the inherited residual buffer and may be overwritten by subsequent input.
ProcessFinalBlock()
Extracts the digest from the algorithm's chaining variables after ProcessBlock(ReadOnlySpan<byte>, ulong, bool) has been called
with isFinal: true for the last time.
protected abstract byte[] ProcessFinalBlock()
Returns
- byte[]
A byte array containing the final computed hash value.
Remarks
This method is invoked once per HashFinal() call, immediately after the final compression. It reads from the internal hash state and serializes the result to a byte array in the format expected by consumers of the algorithm (typically little-endian for Blake-family hashes).
Applies to
| Product | Versions |
|---|---|
| .NET | 8, 10 |