Table of Contents

Poly1305 Class

Definition

Namespace
Bodu.Security.Cryptography
Assembly
Bodu.Security.Cryptography.dll
Package
Bodu.Security.Cryptography 1.2.0
Source
Poly1305.cs

Computes the message authentication code (MAC) for the input data using the Poly1305 algorithm. This implementation enforces one-time key usage and produces a fixed 16-byte (128-bit) tag from a 256-bit key, as specified in RFC 8439.

public sealed class Poly1305 : KeyedBlockHashAlgorithm, ICryptoTransform, IDisposable
Inheritance
Poly1305
Implements
Inherited Members
Extension Methods

Examples

using System.Security.Cryptography;
using Bodu.Security.Cryptography;

// The 32-byte key MUST be unique per message - typically derived from a stream cipher's keystream.
byte[] oneTimeKey = DerivePoly1305KeyFromChaCha20(sessionKey, nonce);
using var poly = new Poly1305 { Key = oneTimeKey };
byte[] tag = poly.ComputeHash(message);

Remarks

Poly1305 is a cryptographic MAC function that operates by interpreting input data as a polynomial over a finite field, evaluated using a secret 128-bit key r, and combined with an additional 128-bit key s to produce the final output tag. The keys are derived from a single 256-bit (32-byte) input, where the first half is clamped to create r and the second half serves as the nonce-derived offset s.

The algorithm is designed for speed and simplicity, particularly on systems without specialized cryptographic hardware. It is most commonly used in conjunction with a stream cipher (e.g., ChaCha20) in AEAD constructions such as ChaCha20-Poly1305. This implementation adheres to the RFC 8439 specification and computes a 16-byte tag for each input message, ensuring authenticity and integrity.

Internally, the input is split into 16-byte blocks, each treated as a 130-bit number (with an additional high bit if full-sized), and accumulated using modular arithmetic modulo 2¹³⁰ - 5. After processing all blocks, the accumulator is finalized by adding the second half of the key s and serializing the result as the final MAC tag. The arithmetic runs on three 64-bit limbs with 128-bit products, straight from the caller's buffers, and takes the same time for every message of a given length. On x64 processors with AVX2, runs of 512 bytes or more are absorbed four or eight blocks at a time in vector registers, and on ARM64 runs of 256 bytes or more, 128 or more on Apple silicon, two at a time, over 26-bit limbs, to the same result.

Parameters at a glance.

  • Tag size: 128 bits (16 bytes), fixed.
  • Key size: 256 bits (32 bytes) - first half is clamped to r, second half is the nonce-derived s.
  • Block size: 16 bytes; arithmetic modulo 2¹³⁰ − 5.
  • Specification: RFC 8439 (ChaCha20-Poly1305).
  • Single-use: a key must authenticate exactly one message.

When to choose Poly1305. Reach for Poly1305 when implementing or extending a Poly1305-based AEAD (ChaCha20-Poly1305 in TLS 1.3, SSH, WireGuard, Noise) or when authenticating a single message with a fresh per-message key derived from a stream cipher. For multi-message keyed authentication use HMAC-SHA-256, Blake2b-MAC, or SipHash64 / SipHash128 - none of which share Poly1305's one-time-key restriction.

important

This algorithm is a one-time authenticator and must not be used with the same key for multiple messages. Reusing a key with different inputs compromises the cryptographic security and may lead to forgery attacks.

Constructors

Poly1305()

Initializes a new instance of the Poly1305 class with no key set.

public Poly1305()

Remarks

Unlike most keyed hash algorithms, Poly1305 deliberately does not auto-generate a random key in its constructor. Poly1305 is a one-time authenticator and reusing the same key against multiple messages breaks its security guarantees; an instance with an unsolicited random key tempts callers to drive ComputeHash(byte[]) twice and silently produce a forgeable tag the second time. Callers must therefore set Key explicitly before computing a hash; failing to do so causes Initialize() to raise a CryptographicException .

Fields

KeySize

Length of the Poly1305 key is 256 bits (32 bytes).

public const int KeySize = 256

Field Value

int

Properties

AlgorithmName

Gets the canonical, fully-qualified algorithm name for this instance, including any size or variant qualifiers (for example, "Tiger/192", "Skein-512-256", "BLAKE2b-512", "ASCON-HASH256", "SipHash-2-4-64").

public override string AlgorithmName { get; }

Property Value

string

A string identifying the algorithm and its current configuration.

Remarks

Derived classes implement this property to expose a stable, consumer-facing identifier suitable for logging, telemetry, registry keys, or interop with hash-name catalogues. Implementations should be pure and side-effect-free - the value may be queried before any input has been consumed and after disposal as part of error reporting.

CanReuseTransform

Gets a value indicating whether this transform instance can be reused after a hash operation is completed.

public override bool CanReuseTransform { get; }

Property Value

bool

false for Poly1305, which is a one-time authenticator that must not be reused with the same key.

Remarks

Poly1305 is a one-time message authentication code (MAC) algorithm. Reusing the same instance with the same key for multiple messages violates the security guarantees of the algorithm and may lead to forgery attacks.

The framework-invoked automatic re-initialization that occurs at the end of ComputeHash(byte[]) and related APIs is tolerated so that callers can read Hash without tripping the key-set guard. However, explicit reuse of a finalized instance - for example, a second ComputeHash(byte[]) call - is rejected by the inherited finalized-state guard on frameworks prior to .NET 6. A fresh instance should be created for each message.

CanTransformMultipleBlocks

When overridden in a derived class, gets a value indicating whether multiple blocks can be transformed.

public override bool CanTransformMultipleBlocks { get; }

Property Value

bool

true if multiple blocks can be transformed; otherwise, false.

Key

Gets or sets the secret key used by the keyed hash algorithm to compute the message authentication code (MAC).

public override byte[] Key { get; set; }

Property Value

byte[]

A byte array containing the key material. Both the getter and the setter operate on defensive copies.

Remarks

The key must be set prior to calling any hashing methods such as ComputeHash(byte[]). The setter stores a private copy of the supplied array, then invokes OnKeyChanged() so derived classes can rebuild any key-dependent internal state (for example, a polynomial key schedule or pre-computed state vectors).

The getter returns a defensive copy of the stored key so external callers cannot mutate the internal representation.

Exceptions

ObjectDisposedException

The algorithm instance has been disposed.

CryptographicUnexpectedOperationException

A hash computation has already started, and the key may not be reassigned while the algorithm is in use.

ArgumentNullException

The assigned value is null.

CryptographicException

The length of the assigned key does not match the required key size for this algorithm.

Methods

Dispose(bool)

Releases the unmanaged resources used by the algorithm and clears the key from memory.

protected override void Dispose(bool disposing)

Parameters

disposing bool

true to release both managed and unmanaged resources; false to release only unmanaged resources.

Remarks

Ensures all internal secrets are overwritten with zeros before releasing resources.

HashCore(ReadOnlySpan<byte>)

Processes the entirety of the input source and feeds it into the computation pipeline. This method updates the internal hash state accordingly by consuming the entire input span.

protected override void HashCore(ReadOnlySpan<byte> source)

Parameters

source ReadOnlySpan<byte>

The input byte span containing the data to hash.

Remarks

This method is part of the core hashing process and is automatically invoked by methods such as TransformBlock(byte[], int, int, byte[], int) and ComputeHash(byte[]). It handles processing of raw byte array input and ensures the hash algorithm receives data in properly sized blocks.

This method internally buffers incomplete blocks between calls to ensure proper alignment. Full blocks are immediately processed; any remaining bytes are stored until more data arrives or finalization occurs.

Exceptions

CryptographicUnexpectedOperationException

The hash algorithm has already been finalized and cannot accept more input data.

ObjectDisposedException

The algorithm instance has been disposed.

CryptographicException

The tag has already been produced under the current key; assign a fresh Key first.

HashFinal()

Finalizes the hash computation by padding and processing any residual data, and returns the resulting digest.

protected override byte[] HashFinal()

Returns

byte[]

A byte array containing the final computed hash value.

Remarks

The authenticator absorbs any partial last block itself, so this goes straight to ProcessFinalBlock().

Exceptions

ObjectDisposedException

The algorithm instance has been disposed.

CryptographicUnexpectedOperationException

On target frameworks prior to .NET 6, the hash computation has already been finalized.

ObjectDisposedException

The algorithm instance has been disposed.

CryptographicException

The tag has already been produced under the current key; assign a fresh Key first.

OnKeyChanged()

Rebuilds the polynomial key schedule and resets the accumulator whenever the key is assigned or the instance is re-initialized.

protected override void OnKeyChanged()

Remarks

Loads and clamps r, captures the second half of the key s used in the final tag calculation, and resets the accumulator so that the next hash computation starts from a clean state.

PadBlock(ReadOnlySpan<byte>, ulong, Span<byte>)

Pads the final partial block of input data into destination and appends the encoded total message length, without allocating a padded copy on the heap.

protected override int PadBlock(ReadOnlySpan<byte> block, ulong messageLength, Span<byte> destination)

Parameters

block ReadOnlySpan<byte>

The final block of unprocessed input, typically containing 0 to BlockSize-1 bytes.

messageLength ulong

The total number of message bytes consumed by the algorithm, including the bytes in block. This is the value most Merkle-Damgård length encodings append.

destination Span<byte>

The span receiving the padded block or blocks; at least two blocks (2 × BlockSize / 8 bytes) long. The caller clears the span after processing.

Returns

int

The number of bytes written - one or two whole blocks, ready for ProcessBlock(ReadOnlySpan<byte>).

Remarks

The default implementation delegates to the array-returning PadBlock(ReadOnlySpan<byte>, ulong) overload and clears the intermediate array; see that overload's remarks for the override contract.

ProcessBlock(ReadOnlySpan<byte>)

Transforms a complete block of input data and updates the internal hash state.

protected override void ProcessBlock(ReadOnlySpan<byte> block)

Parameters

block ReadOnlySpan<byte>

The input block to process. Its length must match the algorithm's configured block size.

Remarks

This method performs the core transformation logic of the hash algorithm. It is called repeatedly with aligned input blocks and is not responsible for padding or finalization steps.

Exceptions

ArgumentException

Thrown if the block is not the expected size.

CryptographicException

The tag has already been produced under the current key; assign a fresh Key first.

ProcessFinalBlock()

Finalizes the hash computation and produces the final hash output.

protected override byte[] ProcessFinalBlock()

Returns

byte[]

A byte array containing the final computed hash value.

Remarks

This method is invoked after all input has been processed and padded. It reads from the internal hash state and serializes the result to a byte array in the format expected by consumers of the algorithm (e.g., big-endian or little-endian).

Exceptions

CryptographicException

The tag has already been produced under the current key; assign a fresh Key first.

ShouldPadFinalBlock()

Determines whether the final block of input data should be padded before processing.

protected override bool ShouldPadFinalBlock()

Returns

bool

true if the final block should be padded; otherwise, false.

Remarks

This method is used to decide whether padding is required for the final block of input data. Derived classes can override this method to implement their own logic for padding behavior. By default, this method returns true, indicating that padding is required.

Applies to

ProductVersions
.NET8, 10