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-deriveds. - 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
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
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
disposingbooltrue 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
sourceReadOnlySpan<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
blockReadOnlySpan<byte>The final block of unprocessed input, typically containing 0 to BlockSize-1 bytes.
messageLengthulongThe 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.destinationSpan<byte>The span receiving the padded block or blocks; at least two blocks (
2 × BlockSize / 8bytes) 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
blockReadOnlySpan<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
blockis 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
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
| Product | Versions |
|---|---|
| .NET | 8, 10 |