Extendable-Output Functions (XOF)
Overview
An Extendable-Output Function (XOF) is a hash function that can produce an arbitrarily long output. Unlike traditional hash algorithms with a fixed digest size, XOFs allow callers to request as many output bytes as needed.
The CryptoHives Cryptography package exposes XOF functionality through the IExtendableOutput interface, providing a unified, allocation-free API across all supported algorithms.
The IExtendableOutput Interface
using CryptoHives.Foundation.Security.Cryptography.Hash;
public interface IExtendableOutput
{
void Absorb(ReadOnlySpan<byte> input);
void Squeeze(Span<byte> output);
void Reset();
}
| Method | Description |
|---|---|
Absorb |
Feeds input data into the XOF state. May be called multiple times before squeezing. |
Squeeze |
Extracts output bytes. The first call finalizes the hash; subsequent calls continue the output stream. |
Reset |
Resets the XOF state so the instance can be reused for a new computation. |
Supported Algorithms
Hash Algorithms
| Algorithm | Class | XOF Mechanism |
|---|---|---|
| SHAKE128 | Shake128 |
Keccak sponge (rate 168) |
| SHAKE256 | Shake256 |
Keccak sponge (rate 136) |
| TurboSHAKE128 | TurboShake128 |
Keccak sponge (rate 168, reduced rounds) |
| TurboSHAKE256 | TurboShake256 |
Keccak sponge (rate 136, reduced rounds) |
| cSHAKE128 | CShake128 |
Keccak sponge with customization |
| cSHAKE256 | CShake256 |
Keccak sponge with customization |
| KT128 | KT128 |
KangarooTwelve (TurboSHAKE128-based) |
| KT256 | KT256 |
KangarooTwelve (TurboSHAKE256-based) |
| BLAKE3 | Blake3 |
Counter-mode output expansion |
| Ascon-XOF128 | AsconXof128 |
Ascon permutation sponge |
MAC Algorithms
| Algorithm | Class | XOF Mechanism |
|---|---|---|
| KMAC128 | KMac128 |
Keccak sponge with key (NIST SP 800-185) |
| KMAC256 | KMac256 |
Keccak sponge with key (NIST SP 800-185) |
Usage
Basic Absorb / Squeeze
using var shake = Shake256.Create(64);
// Absorb input data
shake.Absorb(new byte[] { 0x01, 0x02, 0x03 });
// Squeeze output into a stack-allocated buffer
Span<byte> output = stackalloc byte[64];
shake.Squeeze(output);
Multi-Absorb (Incremental Input)
Data can be absorbed in multiple calls before squeezing. The result is identical to absorbing all data at once:
using var xof = TurboShake128.Create(32);
xof.Absorb(header);
xof.Absorb(payload);
xof.Absorb(footer);
Span<byte> digest = stackalloc byte[32];
xof.Squeeze(digest);
Streaming Squeeze (Multi-Squeeze)
After the first Squeeze call finalizes the hash, subsequent calls continue the output stream. This is useful for producing large or incremental output without allocating a single large buffer:
using var blake3 = Blake3.Create();
blake3.Absorb(data);
// Squeeze 256 bytes in 64-byte chunks
Span<byte> chunk = stackalloc byte[64];
for (int i = 0; i < 4; i++)
{
blake3.Squeeze(chunk);
ProcessChunk(chunk);
}
The output from multiple Squeeze calls is identical to a single call requesting the same total length:
// These produce the same output:
// Option A: single call
xof.Squeeze(new byte[128]);
// Option B: two calls
xof.Squeeze(new byte[64]);
xof.Squeeze(new byte[64]);
Reuse with Reset
The Reset method allows reusing an instance for a new computation without allocating a new object:
using var xof = CShake256.Create(
outputBytes: 32,
functionName: "",
customization: "session-key");
// First computation
xof.Absorb(input1);
Span<byte> hash1 = stackalloc byte[32];
xof.Squeeze(hash1);
// Reset and compute again
xof.Reset();
xof.Absorb(input2);
Span<byte> hash2 = stackalloc byte[32];
xof.Squeeze(hash2);
Runtime Detection
Use pattern matching to detect XOF support on any hash algorithm:
using System.Security.Cryptography;
HashAlgorithm algo = GetAlgorithm();
if (algo is IExtendableOutput xof)
{
xof.Absorb(data);
Span<byte> output = stackalloc byte[128];
xof.Squeeze(output);
}
else
{
byte[] hash = algo.ComputeHash(data);
}
KMAC in XOF Mode
KMAC supports XOF output through the same interface, combining keyed authentication with variable-length output:
using CryptoHives.Foundation.Security.Cryptography.Mac;
byte[] key = GetSecretKey();
using var kmac = KMac256.Create(key, outputBytes: 64, customization: "MyApp");
kmac.Absorb(message);
Span<byte> tag = stackalloc byte[64];
kmac.Squeeze(tag);
Fixed-Output vs XOF Mode
All XOF algorithms also support the standard HashAlgorithm API with a fixed output size specified at construction:
// Fixed-output mode (standard HashAlgorithm API)
using var shake = Shake256.Create(outputBytes: 32);
byte[] hash = shake.ComputeHash(data); // Always 32 bytes
// XOF mode (IExtendableOutput API)
using var xof = Shake256.Create(outputBytes: 32);
xof.Absorb(data);
Span<byte> output = stackalloc byte[128]; // Any length
xof.Squeeze(output);
The outputBytes constructor parameter controls the size returned by ComputeHash and TryHashFinal. The Squeeze method ignores this parameter and fills the entire output span.
Important Notes
- No absorb after squeeze: Once
Squeezeis called, the hash is finalized. FurtherAbsorbcalls are not supported without callingResetfirst. - Deterministic streaming: Multiple
Squeezecalls produce the same byte stream as a single large call. The output is fully deterministic. - Thread safety: Like all hash algorithm instances, XOF instances are not thread-safe. Create separate instances for concurrent use.
See Also
© 2026 The Keepers of the CryptoHives