# Introduction

Geralt is a modern cryptographic library for [.NET 8+](https://dotnet.microsoft.com/en-us/download/dotnet) based on [libsodium](https://doc.libsodium.org/) and inspired by [Monocypher](https://monocypher.org/).

* **Simple**: an easy-to-learn API with descriptive naming. Only one algorithm for each task is provided when possible.
* **Modern**: the latest and greatest cryptographic algorithms, such as X-Wing, ML-KEM-768, AEGIS-128L/AEGIS-256, (X)ChaCha20-Poly1305, BLAKE2b, Argon2id, X25519, and Ed25519.
* **Secure**: libsodium was [audited](https://www.privateinternetaccess.com/blog/libsodium-audit-results/) in 2017 and is the library of choice for [lots](https://doc.libsodium.org/libsodium_users) of projects and [even](https://doc.libsodium.org/libsodium_users#companies-using-libsodium) large companies. Geralt has also been [repeatedly audited](#code-audits), albeit by AI.
* **Fast**: libsodium is [faster](https://monocypher.org/speed) than many other cryptographic libraries. Furthermore, Geralt uses [Span\<T>](https://docs.microsoft.com/en-us/archive/msdn-magazine/2017/connect/csharp-all-about-span-exploring-a-new-net-mainstay) buffers to avoid memory allocations.

## Installation

Geralt is available as a [NuGet](https://www.nuget.org/packages/Geralt) package. It's supported on the following [platforms](https://docs.microsoft.com/en-us/dotnet/core/rid-catalog):

| Windows     | Linux            | macOS       | Other         |
| ----------- | ---------------- | ----------- | ------------- |
| `win-x64`   | `linux-x64`      | `osx-x64`   | `ios`         |
| `win-x86`   | `linux-musl-x64` | `osx-arm64` | `tvos`        |
| `win-arm64` | `linux-arm64`    |             | `maccatalyst` |
|             | `linux-arm`      |             | `android`     |

[Stable releases](https://doc.libsodium.org/doc/quickstart#what-is-the-difference-between-point-releases-and-stable-releases) of the [libsodium NuGet package](https://www.nuget.org/packages/libsodium/) are supported without Geralt being updated, whereas point releases require Geralt to be updated.

{% hint style="info" %}
Note that libsodium requires the [latest Microsoft Visual C++ Redistributable](https://learn.microsoft.com/en-US/cpp/windows/latest-supported-vc-redist?view=msvc-170) on <mark style="color:yellow;">**Windows**</mark>. Instructions on how to deal with this can be found [here](/getting-libsodium-to-work-on-windows).

The platforms in the `Other` column have not been tested and require [.NET MAUI](https://dotnet.microsoft.com/en-us/apps/maui).
{% endhint %}

## Source code

You can find the source code on [GitHub](https://github.com/samuel-lucas6/Geralt).

As of v3.3.0, this documentation is also [mirrored](https://github.com/samuel-lucas6/geralt-docs) to GitHub.

## License

Geralt is licensed under the [MIT](https://github.com/samuel-lucas6/Geralt/blob/main/LICENSE) license.

## Contact

To report a bug, provide feedback, or ask for a new feature, please raise a [GitHub issue](https://github.com/samuel-lucas6/Geralt/issues/new).

For questions and technical support, please create a [GitHub discussion](https://github.com/samuel-lucas6/Geralt/discussions/new/choose).

Finally, please see the [SECURITY.md](https://github.com/samuel-lucas6/Geralt/blob/main/SECURITY.md) file on GitHub for vulnerability reporting.

## Code audits

{% file src="/files/E477tNewrXUi5USTaoAW" %}

{% file src="/files/ucuAVlxG2UqIcnUqujTr" %}

{% file src="/files/bXsYcFkVMePOIaYsDySj" %}

## Goals

* [Span\<T>](https://docs.microsoft.com/en-us/archive/msdn-magazine/2017/connect/csharp-all-about-span-exploring-a-new-net-mainstay) all the things: enables the secure erasure of bytes/chars and boosts performance.
* Descriptive naming: BLAKE2b, not GenericHash.
* Same vocabulary for everything: key, nonce, salt, input keying material, output keying material, etc.
* Minimal parameters: no key parameter for an unkeyed hash.
* Consistent parameter ordering: buffers come first.
* Public constants: easy to create buffers.
* One algorithm for each task: (X)ChaCha20-Poly1305, BLAKE2b, Argon2id, X25519, and Ed25519.
* Some low-level functions: useful for [custom](https://github.com/jedisct1/libsodium-xchacha20-siv) [constructions](https://github.com/jedisct1/spake2-ee).
* Internal secure zeroing: avoids leaving sensitive data in temporary buffers.

## Out of scope

* Full misuse resistance (e.g., no nonces or optional nonces). This can limit the user, doesn't work well with spans, and overcomplicates code.
* Solving the key reuse problem (e.g., a [mandatory context](https://youtu.be/1NVgRPb0tHU) for everything or [wrappers](https://nsec.rocks/docs/api/nsec.cryptography.key) instead of raw bytes). I'm [not](https://github.com/ektrah/nsec/issues/31) convinced either tactic works, and it again adds complexity.
* Old [NaCl](https://nacl.cr.yp.to/) APIs, such as `crypto_box`. These [shouldn't](https://github.com/jedisct1/libsodium/issues/586) be used.
* Other primitives unless they solve a problem. AES-GCM causes problems (e.g., it [requires](https://doc.libsodium.org/secret-key_cryptography/aead/aes-256-gcm#limitations) hardware support). [AEGIS](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-aegis-aead) solves problems (e.g., it's [key committing](https://eprint.iacr.org/2022/268) and supports random nonces whilst being [faster](https://eprint.iacr.org/2023/523)/[stronger](https://competitions.cr.yp.to/round3/aegisv11.pdf) than AES-GCM).
* Experimental ideas/custom constructions (e.g., anything without an RFC or Internet-Draft), which can go in a [separate project](https://github.com/samuel-lucas6/Daence.NET).
* Duplicate methods that return byte arrays.
* Unnecessary 'convenience' functions, like `GenerateKey()` in almost every class.
* Internal [guarded heap allocations](https://doc.libsodium.org/memory_management#guarded-heap-allocations), which [reduce](https://github.com/ektrah/nsec/issues/52) performance and are unnecessary for very short-lived secrets.
* Support for old/no longer supported versions of [.NET](https://dotnet.microsoft.com/en-us/platform/support/policy/dotnet-core) and [.NET MAUI](https://dotnet.microsoft.com/en-us/platform/support/policy/maui).

## Acknowledgements

I'd like to say a big thanks to:

* [GitBook](https://www.gitbook.com/) for their free open source plan.
* [Tuta](https://tuta.com/) for donating their private email service.
* [Frank Denis](https://github.com/jedisct1) for writing the [libsodium](https://doc.libsodium.org/) library.
* [Loup Vaillant](https://github.com/LoupVaillant) for writing the [Monocypher](https://github.com/LoupVaillant/Monocypher) library.
* [Klaus Hartke](https://github.com/ektrah) for creating [NSec](https://nsec.rocks/) and doing .NET [PRs](https://github.com/jedisct1/libsodium/commits?author=ektrah) for libsodium.
* [Trond Arne Bråthen](https://github.com/tabrath) for creating the [libsodium-core](https://github.com/ektrah/libsodium-core) library.​
* [Adam Caudill](https://github.com/adamcaudill) and everyone who contributed to the [libsodium-net](https://web.archive.org/web/20221205225204/https://github.com/adamcaudill/libsodium-net) library.​
* Everyone who has [contributed](https://github.com/samuel-lucas6/Geralt/graphs/contributors) to, used, or provided feedback about Geralt. In particular:
  * [Alexey Avramov](https://github.com/hakavlad) for providing AI code audits of Geralt v4.0.1-v4.2.0, which identified several Informational-Medium issues that were addressed in the next release.


# Random data

## Purpose

This class produces **unpredictable, cryptographically secure** random numbers. Using a *predictable* random number generator, such as [System.Random](https://docs.microsoft.com/en-us/dotnet/api/system.random), is **insecure**.

These functions should be used to randomly generate encryption keys, nonces, salts, seeds, integers, strings, and passphrases.

## Usage

### Fill

Fills a span with random bytes.

```csharp
SecureRandom.Fill(Span<byte> buffer);
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`buffer.Length` is equal to 0.

### FillDeterministic

Fills a span with **deterministic** bytes indistinguishable from random without knowing the seed.

```csharp
SecureRandom.FillDeterministic(Span<byte> buffer, ReadOnlySpan<byte> seed);
```

{% hint style="warning" %}
This should be reserved for tests and custom constructions (e.g., an [XOF](https://github.com/jedisct1/libsodium/issues/623)).
{% endhint %}

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`buffer.Length` is equal to 0.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`seed.Length` is not equal to `SeedSize`.

### GetInt32

Returns a random integer between 0 (inclusive) and the upper bound (exclusive).

```csharp
int number = SecureRandom.GetInt32(int upperBound);
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`upperBound` is less than `MinUpperBound`.

### GenerateString

Fills a span with a random string based on the provided character set.

```csharp
SecureRandom.GenerateString(Span<char> buffer, ReadOnlySpan<char> characterSet);
```

{% hint style="info" %}
See the [constants](#constants) for some included character sets.
{% endhint %}

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`buffer.Length` is less than `MinStringSize` or greater than `MaxStringSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`characterSet.Length` is less than `MinCharacterSetSize`.

### GetPassphraseBufferSize

Returns the required buffer size for `GeneratePassphrase()`. The length of the longest word in the wordlist being used must be known (see `LongestWordSize` for examples).

```csharp
int bufferSize = SecureRandom.GetPassphraseBufferSize(int longestWord, int wordCount);
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`longestWord` is less than `MinLongestWordSize` or greater than `MaxLongestWordSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`wordCount` is less than `MinWordCount` or greater than `MaxWordCount`.

### GeneratePassphrase

Fills a span with a random passphrase using the [EFF's long wordlist](https://www.eff.org/files/2016/07/18/eff_large_wordlist.txt) (minus hyphenated words).

```csharp
SecureRandom.GeneratePassphrase(Span<char> buffer, out int passphraseSize, int wordCount, char separatorChar = '-', bool capitalize = false, bool includeNumber = false);
```

{% hint style="danger" %}
`buffer` must be sliced using `passphraseSize` to remove padding.
{% endhint %}

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`buffer.Length` is not equal to `GetPassphraseBufferSize()`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`wordCount` is less than `MinWordCount` or greater than `MaxWordCount`.

[ArgumentException](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception)

`separatorChar` is not a printable character.

### GeneratePassphrase

Fills a span with a random passphrase using a [custom wordlist](https://gist.github.com/atoponce/95c4f36f2bc12ec13242a3ccc55023af).

```csharp
SecureRandom.GeneratePassphrase(Span<char> buffer, out int passphraseSize, ReadOnlySpan<string> wordlist, int wordCount, char separatorChar = '-', bool capitalize = false, bool includeNumber = false);
```

{% hint style="danger" %}
`buffer` must be sliced using `passphraseSize` to remove padding.
{% endhint %}

{% hint style="success" %}
View the source code for `GetWordlist()` to see how to format a custom wordlist. It is recommended to stick to English wordlists because this is what has been tested.
{% endhint %}

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`buffer.Length` is not equal to `GetPassphraseBufferSize()`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`wordlist.Length` is less than `MinWordlistSize`.

[FormatException](https://learn.microsoft.com/en-us/dotnet/api/system.formatexception)

`wordlist` contains empty words, spaces, or non-printable characters.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

The longest word in the wordlist is less than `MinLongestWordSize` or greater than `MaxLongestWordSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`wordCount` is less than `MinWordCount` or greater than `MaxWordCount`.

[ArgumentException](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception)

`separatorChar` is not a printable character.

### GetWordlist

Returns the built-in [EFF long wordlist](https://www.eff.org/files/2016/07/18/eff_large_wordlist.txt) (minus hyphenated words) formatted correctly for `GeneratePassphrase()`.

```csharp
ReadOnlySpan<string> wordlist = SecureRandom.GetWordlist();
```

#### Exceptions

N/A

## Constants

These are used for validation and/or save you from defining your own constants.

```csharp
public const int SeedSize = 32;
public const string AlphabeticChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
public const string NumericChars = "0123456789";
public const string SymbolChars = "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
public const string AlphanumericChars = AlphabeticChars + NumericChars;
public const string AlphanumericSymbolChars = AlphanumericChars + SymbolChars;
public const int MinUpperBound = 2;
public const int MinStringSize = 8;
public const int MaxStringSize = 128;
public const int MinCharacterSetSize = MinUpperBound;
public const int MinLongestWordSize = 1;
public const int MaxLongestWordSize = 45;
public const int MinWordlistSize = MinUpperBound;
public const int MinWordCount = 4;
public const int MaxWordCount = 20;

public enum LongestWordSize
{
    EffLong = 9, // https://www.eff.org/files/2016/07/18/eff_large_wordlist.txt
    EffShort1 = 5, // https://www.eff.org/files/2016/09/08/eff_short_wordlist_1.txt
    EffShort2 = 10, // https://www.eff.org/files/2016/09/08/eff_short_wordlist_2_0.txt
    Bip39 = 8, // https://github.com/bitcoin/bips/blob/master/bip-0039/english.txt
    Slip39 = 8, // https://github.com/satoshilabs/slips/blob/master/slip-0039/wordlist.txt
    Monero = 12, // https://github.com/monero-project/monero/blob/master/src/mnemonics/english.h
    Diceware = 6 // https://theworld.com/~reinhold/diceware.html (both 7776 and 8192 words)
}
```

## Notes

{% hint style="danger" %}
If the non-deterministic functions are called inside a virtual machine (VM) that has had a snapshot restored, the same output may be produced.​
{% endhint %}

{% hint style="success" %}
For random strings (e.g., passwords), the number of combinations is equal to the length of the character set raised to the power of the number of characters. For example, `52^8` for an 8-character password with uppercase/lowercase English letters.

For random passphrases, the number of combinations is equal to the length of the wordlist raised to the power of the number of words. For example, `7776^6` for a 6-word passphrase generated using a wordlist containing 7776 words.

In both cases, the entropy (in bits) is approximately `log2()` of the above. For instance, `log2(7776^6)` = 77.6 bits.

An acceptable minimum for passwords/passphrases is \~80 bits of entropy.
{% endhint %}

{% hint style="info" %}
The libsodium library uses `RtlGenRandom()` on Windows and `getrandom` or `/dev/urandom` on Linux and macOS to generate cryptographically secure, non-deterministic random numbers. Deterministic generation is done using the IETF version of [ChaCha20](https://datatracker.ietf.org/doc/html/rfc8439#section-2.4) with a hardcoded nonce.
{% endhint %}


# Constant time

## Purpose

Whenever you interact with secrets and cryptographic parameters, you should use constant time functions to avoid leaking information via timing to an attacker. Such leaks can completely compromise security.

For example, you should increment counters/nonces, compare tags, compare retyped passwords, and so on using this class.

## Usage

### Equals

Returns whether two spans are equal in length and contain equal data.

```csharp
bool equal = ConstantTime.Equals(ReadOnlySpan<byte> a, ReadOnlySpan<byte> b);
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`a.Length` is equal to 0.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`b.Length` is equal to 0.

### Increment

Increments a span counter in little-endian format.

```csharp
ConstantTime.Increment(Span<byte> buffer);
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`buffer.Length` is equal to 0.

### Add

Fills a span with the sum of two spans in little-endian format.

```csharp
ConstantTime.Add(Span<byte> buffer, ReadOnlySpan<byte> a, ReadOnlySpan<byte> b);
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`buffer.Length` is equal to 0.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`a.Length` is equal to 0 or not equal to `buffer.Length`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`b.Length` is equal to 0 or not equal to `a.Length`.

### Subtract

Fills a span with the result of subtracting the second span from the first span in little-endian format.

```csharp
ConstantTime.Subtract(Span<byte> buffer, ReadOnlySpan<byte> a, ReadOnlySpan<byte> b);
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`buffer.Length` is equal to 0.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`a.Length` is equal to 0 or not equal to `buffer.Length`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`b.Length` is equal to 0 or not equal to `a.Length`.

### IsLessThan

Returns whether the contents of the first span is less than the second span in little-endian format.

```csharp
bool lessThan = ConstantTime.IsLessThan(ReadOnlySpan<byte> a, ReadOnlySpan<byte> b);
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`a.Length` is equal to 0 or not equal to `b.Length`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`b.Length` is equal to 0.

### IsGreaterThan

Returns whether the contents of the first span is greater than the second span in little-endian format.

```csharp
bool greaterThan = ConstantTime.IsGreaterThan(ReadOnlySpan<byte> a, ReadOnlySpan<byte> b);
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`a.Length` is equal to 0 or not equal to `b.Length`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`b.Length` is equal to 0.

### IsAllZeros

Returns whether a span only contains zeros.

```csharp
bool allZeros = ConstantTime.IsAllZeros(ReadOnlySpan<byte> buffer);
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`buffer.Length` is equal to 0.

## Notes

{% hint style="danger" %}
[Tags](/message-authentication) **MUST** be compared in constant time using `ConstantTime.Equals()` to prevent [forgery](https://crypto.stackexchange.com/a/101635). The `VerifyTag()` and `FinalizeAndVerify()` functions do this for you.
{% endhint %}

{% hint style="success" %}
These constant time functions can also be used for non-secret values. If in doubt, use constant time.
{% endhint %}


# Secure memory

## Purpose

If an attacker can retrieve sensitive data, like keys, passwords, and plaintexts, they can effectively bypass the associated cryptography. Therefore, it is recommended to limit the exposure of and protect such sensitive data in memory, reducing the likelihood of it being accessible to attackers.

Here are several steps you should take as a developer:

1. Use byte or char arrays over strings. Strings are immutable, meaning they can't reliably be erased and copies may be made. [SecureString](https://learn.microsoft.com/en-gb/dotnet/api/system.security.securestring) is also [not worth using](https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md).
2. Use [stackalloc](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/stackalloc) or [GC.AllocateArray\<T>()](https://learn.microsoft.com/en-us/dotnet/api/system.gc.allocatearray) with `pinned: true` for allocations. This prevents .NET from copying the memory around, allowing proper erasure.
3. Use [ZeroMemory()](/secure-memory#zeromemory) to wipe arrays in a manner that won't be optimised away by the compiler. This should be done as soon as the array is no longer needed and when an exception occurs. For example, you can call this method in a [try-finally](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/exception-handling-statements) statement.
4. Use [guarded heap allocations](#guardedheapallocation), which perform memory locking and limit access to the memory at an operating system level. This protects against data being swapped to disk, buffer overflows/underflows, and other processes accessing the data.
5. Use a library like [memguard](https://github.com/awnumar/memguard) (a Go library, but a C# equivalent may be made) that [encrypts memory](https://spacetime.dev/encrypting-secrets-in-memory) whilst using guarded heap allocations. This does all of the above whilst protecting against things like [cold boot attacks](https://en.wikipedia.org/wiki/Cold_boot_attack) and [speculative execution vulnerabilities](https://en.wikipedia.org/wiki/Speculative_execution#Security_vulnerabilities).
6. Support using hardware that stores secrets and performs cryptographic operations on your behalf, like [YubiKeys](https://docs.yubico.com/yesdk/users-manual/getting-started/what-is-a-yubikey.html).

{% hint style="success" %}
The first three bullets are the most important.
{% endhint %}

## Usage

### ZeroMemory

Overwrites a byte array with zeros.

```csharp
SecureMemory.ZeroMemory(Span<byte> buffer);
```

{% hint style="warning" %}
`buffer` **MUST** be [pinned](#purpose) for this method to work properly.
{% endhint %}

#### Exceptions

N/A

### ZeroMemory

Overwrites a char array or string with zeros.

```csharp
SecureMemory.ZeroMemory(ReadOnlySpan<char> buffer);
```

{% hint style="warning" %}
`buffer` **MUST** be [pinned](#purpose) for this method to work properly.

Also, calling this function on a string [converted to a span](https://learn.microsoft.com/en-us/dotnet/api/system.memoryextensions.asspan#system-memoryextensions-asspan\(system-string\)) may crash the .NET runtime in the future. However, it doesn't currently based on my testing.
{% endhint %}

#### Exceptions

N/A

### GuardedHeapAllocation

Provides access to guarded heap allocations, which can be used instead of regular allocations for additional security. However, there is a performance penalty, and this functionality **SHOULD NOT** be used for large amounts of variables/data due to system limits.

```csharp
// Instead of Span<byte> key = stackalloc byte[32];
using var key = new GuardedHeapAllocation(int size);

// Access the memory
SecureRandom.Fill(key.AsSpan());

// Make the memory inaccessible
key.NoAccess();

// Make the memory read-only
key.ReadOnly();

// Make the memory readable and writable
key.ReadWrite();

// Destroy the secret (done automatically with a using statement)
key.Dispose();
```

{% hint style="danger" %}
Do **NOT** assign the `AsSpan()` return value to a span variable. <mark style="color:yellow;">This is a good way to crash your application</mark>. Instead, call `AsSpan()` every time you want to access the memory.

Similarly, do **NOT** access the memory after calling `NoAccess()` or try to write to the memory after calling `ReadOnly()`. <mark style="color:yellow;">This will immediately crash your application</mark>.
{% endhint %}

{% hint style="warning" %}
Do **NOT** use this class with multiple threads. If you absolutely have to, you need to use [lock statements](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/lock) to avoid exceptions being thrown.
{% endhint %}

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`size` is less than 1 or greater than `MaxSize`.

[InsufficientMemoryException](https://learn.microsoft.com/en-us/dotnet/api/system.insufficientmemoryexception)

Insufficient memory for guarded heap allocation.

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Error marking memory as inaccessible.

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Error marking memory as read-only.

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Error marking memory as readable and writable.

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Methods cannot be called from multiple threads simultaneously.

[ObjectDisposedException](https://learn.microsoft.com/en-us/dotnet/api/system.objectdisposedexception)

The object has been disposed.

## Constants

These are used for validation and/or save you from defining your own constants.

```csharp
// GuardedHeapAllocation class
public static readonly int MaxSize = Environment.SystemPageSize - 16;
```

## Notes

Guarded heap allocations have the following memory layout, with a few caveats:

* The stored size in the first guard page refers to the canary plus data size rounded up to a multiple of the page size.
* The length of the stored size in the first guard page depends on the platform, as does the page size.
* The canary plus data does not necessarily take up a whole page; there may be padding at the beginning (unused memory) and multiple pages (if the data is large enough).
* The canary is initialized with random bytes, and data is initialized with `0xdb` bytes to help catch bugs due to uninitialized data.
* If access protection is not supported on the platform, the last page contains a canary at the beginning, meaning there can be two canaries with the same value surrounding the data. In this scenario, access to guard pages is not restricted.

<figure><img src="/files/SOXlIGC04k5jwiCUutOJ" alt=""><figcaption><p>Guarded heap allocation layout</p></figcaption></figure>

{% hint style="warning" %}
Guarded heap allocations are not as fast as ordinary allocations and use up a lot more memory. Additionally, there are system limits on how much memory can be locked by a process. Therefore, use them sparingly for small amounts of data and test your application thoroughly.
{% endhint %}

{% hint style="warning" %}
Memory locking is not guaranteed with guarded heap allocations. For example, it may fail or not be supported on the system, in which case the function continues with no error.
{% endhint %}

{% hint style="success" %}
It is **RECOMMENDED** to use a separate guarded heap allocation for each secret. This is less error-prone whilst offering greater protection and access control.
{% endhint %}

{% hint style="success" %}
As well as taking these steps as a developer, you can recommend that users of your application do the following:

* Limit the number of installed/running applications on their machine.
* Harden their machine against malware (e.g., allowlisting, a local firewall, etc).
* Encrypt their OS drive.
* Disable hibernation.
* Disable memory dumps.
* Disable swap files/the swap partition.
  {% endhint %}

{% hint style="info" %}
Memory locking uses [VirtualLock()](https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtuallock) on Windows and [mlock()](https://linux.die.net/man/2/mlock) with [madvise()](https://linux.die.net/man/2/madvise) and `MADV_DONTDUMP` on Unix.

Guarded heap allocations require 3 or 4 extra pages of memory over a typical allocation. Allocations are done with [VirtualAlloc()](https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc) on Windows and [mmap()](https://linux.die.net/man/2/mmap) with `MAP_NOCORE` or [posix\_memalign()](https://linux.die.net/man/3/posix_memalign) on Unix. [VirtualProtect()](https://learn.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualprotect) on Windows and [mprotect()](https://linux.die.net/man/2/mprotect) on Unix are used for the memory access restrictions.

The protection on Unix is [likely superior](https://github.com/awnumar/memguard/issues/123) to on Windows.
{% endhint %}


# Encoding

## Purpose

It can be useful to convert bytes to strings. For example, to represent [hashes](/hashing) or to create shareable [X25519](/key-exchange) and [Ed25519](/digital-signatures) public keys. Hex and Base64 encoding can be used to do this.

## Usage

### GetToHexBufferSize

Returns the output buffer size required for `ToHex()`.

```csharp
Encodings.GetToHexBufferSize(ReadOnlySpan<byte> data)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`data` has a length of 0.

[OverflowException](https://learn.microsoft.com/en-us/dotnet/api/system.overflowexception)

`data.Length * 2` has resulted in an overflow.

### ToHex

Fills a span with the hexadecimal string that represents the provided data.

```csharp
Encodings.ToHex(Span<char> hex, ReadOnlySpan<byte> data)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`hex` has a length not equal to `GetToHexBufferSize()`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`data` has a length of 0.

[OverflowException](https://learn.microsoft.com/en-us/dotnet/api/system.overflowexception)

`data.Length * 2` has resulted in an overflow.

[CryptographicException](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error converting bytes to hex.

### GetFromHexBufferSize

Returns the output buffer size required for `FromHex()`.

```csharp
Encodings.GetFromHexBufferSize(ReadOnlySpan<char> hex, ReadOnlySpan<char> ignoreChars = default)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`hex` has a length of 0 or is not a multiple of 2 (after discounting ignored chars).

[ArgumentException](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception)

`hex` cannot contain non-ASCII characters.

[ArgumentException](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception)

`ignoreChars` cannot contain hex characters or represent the only characters in `hex`.

### FromHex

Fills a span with the data from decoding a hexadecimal string. Separator characters to ignore when parsing can optionally be provided.

```csharp
Encodings.FromHex(Span<byte> data, ReadOnlySpan<char> hex, ReadOnlySpan<char> ignoreChars = default)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`data` has a length not equal to `GetFromHexBufferSize()`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`hex` has a length of 0 or is not a multiple of 2 (after discounting ignored chars).

[ArgumentException](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception)

`hex` cannot contain non-ASCII characters.

[ArgumentException](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception)

`ignoreChars` cannot contain hex characters or represent the only characters in `hex`.

[FormatException](https://docs.microsoft.com/en-us/dotnet/api/system.formatexception)

Invalid hex string.

### GetToBase64BufferSize

Returns the output buffer size required for `ToBase64()`.

```csharp
Encodings.GetToBase64BufferSize(ReadOnlySpan<byte> data, Base64Variant variant = Base64Variant.Original)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`data` has a length of 0.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`variant` must be a value within the enum.

[OverflowException](https://learn.microsoft.com/en-us/dotnet/api/system.overflowexception)

The encoded length is too large for an array.

### ToBase64

Fills a span with the Base64 string that represents the provided data. Choose one variant (e.g., Base64URL for file names/URLs) and only ever use that variant.

```csharp
Encodings.ToBase64(Span<char> base64, ReadOnlySpan<byte> data, Base64Variant variant = Base64Variant.Original)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`base64` has a length not equal to `GetToBase64BufferSize()`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`data` has a length of 0.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`variant` must be a value within the enum.

[CryptographicException](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error converting bytes to Base64.

### GetFromBase64BufferSize

Returns the output buffer size required for `FromBase64()`.

```csharp
Encodings.GetFromBase64BufferSize(ReadOnlySpan<char> base64, Base64Variant variant = Base64Variant.Original, ReadOnlySpan<char> ignoreChars = default)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`base64` has a length of 0 or is not a valid length based on `variant`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`variant` must be a value within the enum.

[ArgumentException](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception)

`base64` cannot contain non-ASCII characters.

[ArgumentException](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception)

`ignoreChars` cannot contain Base64 characters or represent the only characters in `base64`.

[OverflowException](https://learn.microsoft.com/en-us/dotnet/api/system.overflowexception)

The buffer size computation has resulted in an overflow.

### FromBase64

Fills a span with the data from decoding a Base64 string. The variant must match the one used for encoding. Separator characters to ignore when parsing can optionally be provided.

```csharp
Encodings.FromBase64(Span<byte> data, ReadOnlySpan<char> base64, Base64Variant variant = Base64Variant.Original, ReadOnlySpan<char> ignoreChars = default)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`data` has a length not equal to `GetFromBase64BufferSize()`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`base64` has a length of 0 or is not a valid length based on `variant`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`variant` must be a value within the enum.

[ArgumentException](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception)

`base64` cannot contain non-ASCII characters.

[ArgumentException](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception)

`ignoreChars` cannot contain Base64 characters or represent the only characters in `base64`.

[OverflowException](https://learn.microsoft.com/en-us/dotnet/api/system.overflowexception)

The buffer size computation has resulted in an overflow.

[FormatException](https://docs.microsoft.com/en-us/dotnet/api/system.formatexception)

Invalid Base64 string.

## Constants

```csharp
public const string HexCharacterSet = "0123456789ABCDEFabcdef";
public const string Base64CharacterSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
public const string Base64UrlCharacterSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=";
public const string Base64FullCharacterSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/-_=";
public const string HexIgnoreChars = ":- _./,%;";
public const string Base64IgnoreChars = " \r\n";

public enum Base64Variant
{
    Original = 1,
    OriginalNoPadding = 3,
    Url = 5,
    UrlNoPadding = 7
}
```

## Notes

{% hint style="danger" %}
Do **NOT** support multiple variants of Base64 in your application (e.g., Base64 with and without padding). This can lead to [vulnerabilities](https://cendyne.dev/posts/2022-02-18-user-provided-primary-keys.html).
{% endhint %}

{% hint style="success" %}
Unlike in the .NET library, these functions run in constant time to avoid potential [side-channel attacks](https://en.wikipedia.org/wiki/Side-channel_attack). Furthermore, the Base64 implementation should be more resistant to [malleability attacks](https://dl.acm.org/doi/10.1145/3488932.3527284).
{% endhint %}

{% hint style="info" %}
Base64 has a better compression rate than hex. However, they tend to be used for different purposes. For instance, hex is often used for [test vectors](https://datatracker.ietf.org/doc/html/rfc8439#appendix-A) and [encoding hashes](https://crypto.stackexchange.com/questions/34995/why-do-we-use-hex-output-for-hash-functions), whereas Base64 is more commonly used for [encoding keys](https://www.wireguard.com/quickstart/#key-generation).
{% endhint %}


# Padding

## Purpose

The length of a ciphertext from a [stream cipher](/authenticated-encryption/chacha20-poly1305) is equal to the length of the plaintext. In most cases, this is not considered an issue. However, hiding the length of a message can be desirable, and [ISO/IEC 7816-4](https://en.wikipedia.org/wiki/Padding_\(cryptography\)#ISO/IEC_7816-4) padding can be used to do this.

The amount of padding, determined by the block size, can either be deterministic or [randomised](/random-data#getint32). Both have their [strengths and weaknesses](https://en.wikipedia.org/wiki/Padding_\(cryptography\)#Traffic_analysis_and_protection_via_padding).

{% hint style="warning" %}
Padding to a block size much smaller than the message length leaves the approximate unpadded length largely unprotected. [PADMÉ](https://github.com/samuel-lucas6/PADME.NET) can be used to limit leakage.
{% endhint %}

{% hint style="success" %}
Padding should be applied to the plaintext before encryption and removed from the plaintext after decryption. The amount of padding does not need to be stored.
{% endhint %}

## Usage

### Fill

Fills a span with padding. This can then be [manually concatenated](/advanced/concat) with some data.

```csharp
Iso78164Padding.Fill(Span<byte> buffer)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`buffer` has a length of 0.

### GetPaddedBufferSize

Returns the required buffer size for `Pad()` based on the unpadded data length and a block size (e.g., 16 bytes).

```csharp
Iso78164Padding.GetPaddedBufferSize(ReadOnlySpan<byte> data, int blockSize)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`blockSize` is less than or equal to 0.

[OverflowException](https://learn.microsoft.com/en-us/dotnet/api/system.overflowexception)

`data.Length + paddingSize` has resulted in an overflow.

### Pad

Fills a span with the data padded up to the specified block size (e.g., a multiple of 16 bytes).

```csharp
Iso78164Padding.Pad(Span<byte> buffer, ReadOnlySpan<byte> data, int blockSize)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`buffer` has a length not equal to `GetPaddedBufferSize(data, blockSize)`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`blockSize` is less than or equal to 0.

[CryptographicException](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error padding data.

### GetUnpaddedBufferSize

Returns the number of bytes to slice from the end of the padded data.

```csharp
Iso78164Padding.GetUnpaddedBufferSize(ReadOnlySpan<byte> paddedData, int blockSize)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`paddedData` has a length of 0.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`blockSize` is less than or equal to 0.

[FormatException](https://docs.microsoft.com/en-us/dotnet/api/system.formatexception)

Invalid padding.

## Notes

{% hint style="warning" %}
It is very difficult to hide that cryptography is being used. For example, even if padding is done appropriately and there are no plaintext headers, [X25519](/key-exchange) public keys are [distinguishable from random](https://elligator.org/).
{% endhint %}

{% hint style="warning" %}
Using padding to hide the length of a password is **NOT** recommended. Instead, the password can be prehashed using [BLAKE2b](/hashing) or [Argon2id](/password-hashing#notes) on the client before being sent to the server for password hashing.
{% endhint %}


# Hashing

## Purpose

[BLAKE2b](https://datatracker.ietf.org/doc/html/rfc7693) is a cryptographic hash function. It takes a message of any size and produces a 128-bit to 512-bit hash.

This hash acts as a fingerprint for the data. Hashes can be used to uniquely identify messages, detect corruption, detect duplicate data, and index data in a hash table.

It is also possible to personalize the output for your application/a specific use case via a constant and to randomize hashing by using a salt. The former provides domain separation, helping to avoid collisions between applications/use cases. The latter can also be used for this purpose but is primarily designed for digital signature schemes.

However, **unkeyed** hashes do not provide [authentication](/message-authentication) (e.g., for [Encrypt-then-MAC](https://samuellucas.com/draft-lucas-generalised-committing-aead/draft-lucas-generalised-committing-aead.html#section-3)). Furthermore, they should be avoided for [key derivation](/key-derivation). Use the linked APIs instead.

{% hint style="danger" %}
BLAKE2b is **NOT** suitable for hashing passwords. Use [Argon2id](/password-hashing) instead.
{% endhint %}

{% hint style="danger" %}
A hash size of at least 256 bits is **strongly recommended** to obtain collision resistance.
{% endhint %}

## Usage

### ComputeHash

Fills a span with a hash computed from a message.

```csharp
BLAKE2b.ComputeHash(Span<byte> hash, ReadOnlySpan<byte> message)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`hash` has a length less than `MinHashSize` or greater than `MaxHashSize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error computing hash.

### ComputeHash

Fills a span with a personalized and/or salted hash computed from a message.

```csharp
BLAKE2b.ComputeHash(Span<byte> hash, ReadOnlySpan<byte> message, ReadOnlySpan<byte> personalization, ReadOnlySpan<byte> salt = default)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`hash` has a length less than `MinHashSize` or greater than `MaxHashSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`personalization` and `salt` both have a length of 0.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`personalization` has a length greater than 0 but not equal to `PersonalizationSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`salt` has a length greater than 0 but not equal to `SaltSize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error computing personalized/salted hash.

### IncrementalBLAKE2b

Provides support for computing a hash from several messages.

```csharp
using var blake2b = new IncrementalBLAKE2b(int hashSize);
blake2b.Update(ReadOnlySpan<byte> message1);
blake2b.Update(ReadOnlySpan<byte> message2);
blake2b.Finalize(Span<byte> hash1);

// Avoid another using statement
blake2b.Reinitialize(int hashSize);
blake2b.Update(ReadOnlySpan<byte> message3);
// Cache the state
blake2b.CacheState();
blake2b.Finalize(Span<byte> hash2);

// Restore the cached state
blake2b.RestoreCachedState();
// hash3 == hash2
blake2b.Finalize(Span<byte> hash3);

// Compute a personalized and/or salted hash
using var blake2b = new IncrementalBLAKE2b(int hashSize, key: ReadOnlySpan<byte>.Empty, ReadOnlySpan<byte> personalization, ReadOnlySpan<byte> salt = default);
blake2b.Update(ReadOnlySpan<byte> message4);
blake2b.Finalize(Span<byte> hash4);
```

{% hint style="warning" %}
`CacheState()` can only cache the state once. Each subsequent call will overwrite the previously cached state. See the [Notes](#notes) for when this method should be used.
{% endhint %}

{% hint style="warning" %}
Do **NOT** use this class with multiple threads. If you absolutely have to, you need to use [lock statements](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/lock) to avoid exceptions being thrown.
{% endhint %}

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`hashSize` is less than `MinHashSize` or greater than `MaxHashSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`personalization` has a length greater than 0 but not equal to `PersonalizationSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`salt` has a length greater than 0 but not equal to `SaltSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`hash` has a length not equal to `hashSize`.

[OutOfMemoryException](https://learn.microsoft.com/en-us/dotnet/api/system.outofmemoryexception)

Allocating memory for the state failed.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error initializing/updating/finalizing hash function state.

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Cannot update after finalizing or finalize twice (without reinitializing or restoring a cached state).

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Cannot cache the state after finalizing (without reinitializing).

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Cannot restore the state when it has not been cached or when the current hash size differs from what was cached.

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Methods cannot be called from multiple threads simultaneously.

[ObjectDisposedException](https://learn.microsoft.com/en-us/dotnet/api/system.objectdisposedexception)

The object has been disposed.

## Constants

These are used for validation and/or save you defining your own constants.

```csharp
public const int HashSize = 32;
public const int MinHashSize = 16;
public const int MaxHashSize = 64;
public const int BlockSize = 128;
public const int SaltSize = 16;
public const int PersonalizationSize = 16;
```

## Notes

{% hint style="danger" %}
Do **NOT** use `ComputeHash()` for key derivation. Read the [Key derivation](/key-derivation) page instead.
{% endhint %}

{% hint style="warning" %}
Do **NOT** manually truncate a hash. Instead, specify the hash size you want directly. The hash size affects the output, which provides domain separation.
{% endhint %}

{% hint style="success" %}
Unlike older hash functions (e.g., MD5, SHA-1, SHA-256, and SHA-512), BLAKE2b is immune to [length extension attacks](https://en.wikipedia.org/wiki/Length_extension_attack).
{% endhint %}

{% hint style="success" %}
If you are making multiple calls to `IncrementalBLAKE2b` with unchanging/static data at the beginning, you can cache the state to improve performance. This allows you to only process this data once. It is more relevant in [message authentication](/message-authentication) scenarios, as explained on that page.
{% endhint %}

{% hint style="info" %}
The security level of BLAKE2b is 1/2 the output length (e.g., 128-bit security for a 256-bit hash).​
{% endhint %}


# Message authentication

## Purpose

[BLAKE2b](https://datatracker.ietf.org/doc/html/rfc7693) is a cryptographic hash function and message authentication code (MAC). As a MAC, it takes a message of any size and a 256-bit to 512-bit key and produces a 128-bit to 512-bit tag.

This tag allows you to verify that a message has not been tampered with. A change to the message or the use of a different key will result in a different tag, at which point you should throw an error.

{% hint style="warning" %}
A tag size of at least 256 bits is **strongly recommended** to obtain [committing security](https://samuellucas.com/draft-lucas-generalised-committing-aead/draft-lucas-generalised-committing-aead.html), which requires collision resistance.
{% endhint %}

{% hint style="success" %}
A 256-bit key is recommended regardless of the tag size. Larger keys are unnecessary.
{% endhint %}

## Usage

### ComputeTag

Fills a span with a tag computed from a message and a key.

```csharp
BLAKE2b.ComputeTag(Span<byte> tag, ReadOnlySpan<byte> message, ReadOnlySpan<byte> key)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`tag` has a length less than `MinTagSize` or greater than `MaxTagSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`key` has a length less than `MinKeySize` or greater than `MaxKeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

The tag could not be computed.

### VerifyTag

Verifies that a tag is correct in **constant time** for a given message and key. It returns `true` if the tag is valid and `false` otherwise.

```csharp
BLAKE2b.VerifyTag(ReadOnlySpan<byte> tag, ReadOnlySpan<byte> message, ReadOnlySpan<byte> key)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`tag` has a length less than `MinTagSize` or greater than `MaxTagSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`key` has a length less than `MinKeySize` or greater than `MaxKeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

The tag could not be recomputed.

### IncrementalBLAKE2b

Provides support for computing a tag from several messages and a key.

```csharp
using var blake2b = new IncrementalBLAKE2b(int hashSize, ReadOnlySpan<byte> key);
blake2b.Update(ReadOnlySpan<byte> message1);
blake2b.Update(ReadOnlySpan<byte> message2);
// Compute
blake2b.Finalize(Span<byte> tag1);
// Or verify
bool valid = blake2b.FinalizeAndVerify(ReadOnlySpan<byte> tag1);

// Avoid another using statement
blake2b.Reinitialize(int hashSize, ReadOnlySpan<byte> key);
// Cache the state
blake2b.CacheState();
blake2b.Update(ReadOnlySpan<byte> message3);
blake2b.Finalize(Span<byte> tag2);

// Restore the cached state
blake2b.RestoreCachedState();
blake2b.Update(ReadOnlySpan<byte> message3);
// tag3 == tag2
blake2b.Finalize(Span<byte> tag3);
```

{% hint style="warning" %}
`CacheState()` can only cache the state once. Each subsequent call will overwrite the previously cached state. See the [Notes](#notes) for when this method should be used.
{% endhint %}

{% hint style="warning" %}
Do **NOT** use this class with multiple threads. If you absolutely have to, you need to use [lock statements](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/lock) to avoid exceptions being thrown.
{% endhint %}

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`hashSize` is less than `MinHashSize` or greater than `MaxHashSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`key` has a length less than `MinKeySize` or greater than `MaxKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`hash` has a length not equal to `hashSize`.

[OutOfMemoryException](https://learn.microsoft.com/en-us/dotnet/api/system.outofmemoryexception)

Allocating memory for the state failed.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

The tag could not be computed.

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Cannot update after finalizing or finalize twice (without reinitializing or restoring a cached state).

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Cannot cache the state after finalizing (without reinitializing).

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Cannot restore the state when it has not been cached or when the current hash size differs from what was cached.

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Methods cannot be called from multiple threads simultaneously.

[ObjectDisposedException](https://learn.microsoft.com/en-us/dotnet/api/system.objectdisposedexception)

The object has been disposed.

## Constants

These are used for validation and/or save you defining your own constants.

```csharp
public const int TagSize = 32;
public const int KeySize = 32;
public const int MinTagSize = 16;
public const int MaxTagSize = 64;
public const int MinKeySize = 16;
public const int MaxKeySize = 64;
public const int BlockSize = 128;
```

## Notes

{% hint style="danger" %}
Tags **MUST** be compared in constant time to avoid leaking information, so use the `VerifyTag()` or `FinalizeAndVerify()` function.​
{% endhint %}

{% hint style="danger" %}
With [Encrypt-then-MAC](https://samuellucas.com/draft-lucas-generalised-committing-aead/draft-lucas-generalised-committing-aead.html#section-3), you **MUST** include the nonce in the message when computing the tag.
{% endhint %}

{% hint style="danger" %}
If you intend to feed **multiple variable-length** inputs into the message, beware of [canonicalization attacks](https://soatok.blog/2021/07/30/canonicalization-attacks-against-macs-and-signatures/). Please read the [Concat](/advanced/concat) page for more information.
{% endhint %}

{% hint style="danger" %}
The key **MUST** be uniformly random. It should be the output of a [KDF](/key-derivation), **NOT** randomly generated.
{% endhint %}

{% hint style="danger" %}
Do **NOT** use the same key for multiple purposes (e.g., encryption and authentication). You should [derive](/key-derivation) separate keys using the same input keying material and personalisation but different salts and/or info.
{% endhint %}

{% hint style="success" %}
The same key can be reused for multiple messages, but it is good practice to [derive](/key-derivation) a unique key each time.
{% endhint %}

{% hint style="success" %}
If you are making multiple calls to `IncrementalBLAKE2b` with unchanging/static data at the beginning (e.g., the same key), you can cache the state to improve performance. This allows you to only process this data once and can help you quickly zero the key from memory.
{% endhint %}

{% hint style="info" %}
The security level of BLAKE2b against a generic attack on hash-based MACs is 1/2 the output length (e.g., 128-bit security for a 256-bit tag).​ However, the security level is equal to the output length for typical attacks against MACs (e.g., 256-bit security for a 256-bit tag). Both types of attacks are completely impractical.
{% endhint %}


# Password hashing

## Purpose

[Argon2id](https://www.rfc-editor.org/rfc/rfc9106.html) is a memory-hard password hashing and password-based key derivation function (KDF). It takes the following parameters:

* A password.
* A 128-bit [random](/random-data#fill) salt.
* An iteration count.
* A memory size in bytes.

{% hint style="success" %}

1. Set the iteration count to 3.
2. Set the memory size as high as possible (minimum of 64 MiB) for a reasonable delay (e.g., 100 ms to 1 sec) on the type of device your application will run on.
3. If the delay is lower than you would like, increase the iterations.

See the [Notes](#notes) for some example parameters.
{% endhint %}

{% hint style="warning" %}
The same Unicode character can sometimes get encoded in multiple different ways depending on things like the OS/keyboard/device, which can prevent a user deriving the correct key/password hash every time. There are two solutions to this problem:

1. Only accept [ASCII](https://en.wikipedia.org/wiki/ASCII#Character_set) characters in passwords.
2. If non-ASCII characters are accepted in passwords, apply [Unicode Normalization Form C (NFC)](https://learn.microsoft.com/en-us/dotnet/api/system.string.normalize) to the password before password hashing. This is recommended by [NIST SP 800-63B-4](https://csrc.nist.gov/pubs/sp/800/63/b/4/final) and [RFC 8265](https://www.rfc-editor.org/rfc/rfc8265.html).

However, note that 2 does not actually fix this problem. There are still [rare cases](https://1passwordstatic.com/files/security/1password-white-paper.pdf) where Unicode characters can be problematic. Normalization also requires having a string copy of the password in memory, which is bad for secure erasure.
{% endhint %}

## Usage

### DeriveKey

Fills a span with output keying material computed from a password, a [random](/random-data#fill) salt, an iteration count, and a memory size in bytes.

```csharp
Argon2id.DeriveKey(Span<byte> outputKeyingMaterial, ReadOnlySpan<byte> password, ReadOnlySpan<byte> salt, int iterations, int memorySize)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`outputKeyingMaterial` has a length less than `MinKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`salt` has a length not equal to `SaltSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`iterations` is less than `MinIterations`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`memorySize` is less than `MinMemorySize`.

[InsufficientMemoryException](https://docs.microsoft.com/en-us/dotnet/api/system.insufficientmemoryexception)

Insufficient memory to perform key derivation.

### ComputeHash

Fills a span with an encoded password hash computed from a password, a randomly generated salt, an iteration count, and a memory size in bytes.

```csharp
Argon2id.ComputeHash(Span<char> hash, ReadOnlySpan<byte> password, int iterations, int memorySize)
```

{% hint style="success" %}
`hash` must be a fixed length due to libsodium's API, which pads the potentially variable-length output with null characters (`'\0'`).

You can convert the hash into a string for storage in a database using [Span\<T>.ToString()](https://learn.microsoft.com/en-us/dotnet/api/system.span-1.tostring). Any null characters at the end can either be left alone or removed with [String.TrimEnd()](https://learn.microsoft.com/en-us/dotnet/api/system.string.trimend). Only this hash needs to be stored as the cost parameters and salt are encoded.
{% endhint %}

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`hash` has a length not equal to `HashSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`iterations` is less than `MinIterations`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`memorySize` is less than `MinMemorySize`.

[InsufficientMemoryException](https://docs.microsoft.com/en-us/dotnet/api/system.insufficientmemoryexception)

Insufficient memory to perform password hashing.

### VerifyHash

Verifies that an encoded password hash is correct for a given password. It returns `true` if the hash is valid and `false` otherwise.

```csharp
Argon2id.VerifyHash(ReadOnlySpan<char> hash, ReadOnlySpan<byte> password)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`hash` has a length less than `MinHashSize` or greater than `MaxHashSize` (internal constants).

[FormatException](https://docs.microsoft.com/en-us/dotnet/api/system.formatexception)

Invalid password hash string encoding.

[FormatException](https://docs.microsoft.com/en-us/dotnet/api/system.formatexception)

Invalid password hash string termination.

[FormatException](https://docs.microsoft.com/en-us/dotnet/api/system.formatexception)

Invalid password hash string prefix.

### NeedsRehash

Determines if an encoded password hash matches the expected iteration count and memory size. It returns `true` if the hash does not match and `false` if the hash matches.

```csharp
Argon2id.NeedsRehash(ReadOnlySpan<char> hash, int iterations, int memorySize)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`hash` has a length less than `MinHashSize` or greater than `MaxHashSize` (internal constants).

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`iterations` is less than `MinIterations`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`memorySize` is less than `MinMemorySize`.

[FormatException](https://docs.microsoft.com/en-us/dotnet/api/system.formatexception)

Invalid password hash string encoding.

[FormatException](https://docs.microsoft.com/en-us/dotnet/api/system.formatexception)

Invalid password hash string termination.

[FormatException](https://docs.microsoft.com/en-us/dotnet/api/system.formatexception)

Invalid password hash string prefix.

## Constants

These are used for validation and/or save you defining your own constants.

```csharp
public const int KeySize = 32;
public const int SaltSize = 16;
public const int HashSize = 128;
public const int MinKeySize = 16;
public const int MinIterations = 1;
public const int MinMemorySize = 8192;
```

## Notes

{% hint style="info" %}
The best defence against password cracking will always be to use strong passwords. For example, [diceware](https://www.eff.org/dice) with 6+ words.
{% endhint %}

{% hint style="success" %}
Here are some recommended delays based on scenario:

* Interactive (e.g., online login): 50-250 ms
* Semi-interactive (e.g., file encryption): 250-1000 ms
* Non-interactive (e.g., disk encryption): 1000-5000 ms
  {% endhint %}

Here are some example parameters for different scenarios:

|             Source            | Iterations | Memory (bytes) | \*Delay (ms) |
| :---------------------------: | :--------: | :------------: | :----------: |
|    libsodium's interactive    |      2     |    67108864​   |      51      |
| RFC second recommended option |      3     |    67108864​   |      72      |
|      libsodium's moderate     |      3     |    268435456   |      314     |
|     libsodium's sensitive     |      4     |   1073741824   |     1745     |

**\*These delays are for my desktop**. You should perform benchmarks on a typical device for your application using [BenchmarkArgon2.NET](https://github.com/samuel-lucas6/benchmark-argon2-dotnet).

{% hint style="success" %}
More memory is better than more iterations. However, you will need to increase the iterations in most cases because there should be a limit on how much memory your application uses.
{% endhint %}

{% hint style="warning" %}
Too high of an iteration count/memory size on a server could lead to denial-of-service (DoS) attacks. You can do client-side password hashing as well as server-side password hashing to help, sometimes called [server relief](https://doc.libsodium.org/password_hashing#server-relief).

If an attacker can manipulate password hash strings, that also allows DoS because the iteration count/memory size are embedded in the string. Preventing access/tampering or validating against an upper parameter limit can mitigate this.
{% endhint %}

{% hint style="info" %}
The parallelism is always 1 for deriving keys/hashes in libsodium. However, hashes with a parallelism greater than 1 can be verified.
{% endhint %}

{% hint style="info" %}
Libsodium also supports Argon2i, which is more side-channel resistant but less GPU resistant. However, Geralt only supports Argon2id because it is the [mandatory](https://www.rfc-editor.org/rfc/rfc9106.html#name-introduction) and [recommended](https://www.rfc-editor.org/rfc/rfc9106.html#name-recommendations) variant in the RFC, plus there are [attacks](https://en.wikipedia.org/wiki/Argon2#Cryptanalysis) against Argon2i.
{% endhint %}


# Key derivation

## Purpose

[BLAKE2b](https://datatracker.ietf.org/doc/html/rfc7693) can be used as a key derivation function (KDF) for **high-entropy** keys. It takes the following parameters to produce 256 to 512 bits of output keying material:

* 256 to 512 bits of input keying material (e.g., a shared secret).
* A 128-bit personalization constant (e.g., an application/protocol name).
* A 128-bit salt (e.g., a [counter](/constant-time#increment) or [random](/random-data#fill) data).
* Optional contextual info of any length (e.g., an explanation of what the key will be used for).

This allows you to derive new, distinct keys from a **high-entropy** master key. For example, separate keys for encryption and authentication with [Encrypt-then-MAC](https://samuellucas.com/draft-lucas-generalised-committing-aead/draft-lucas-generalised-committing-aead.html#section-3) by changing the personalization constant, salt, and/or info.

{% hint style="danger" %}
BLAKE2b is **NOT** suitable for deriving keys from passwords. Use [Argon2id](/password-hashing) instead.
{% endhint %}

{% hint style="success" %}
256-bit keys are recommended. Larger keys are unnecessary unless splitting the output in two (e.g., keys for different directions) or doing a symmetric ratchet (the state size should be [double](https://eprint.iacr.org/2024/220) the security level).
{% endhint %}

## Usage

### DeriveKey

Fills a span with output keying material computed from input keying material, a personalization constant, a salt, and optional additional contextual info.

```csharp
BLAKE2b.DeriveKey(Span<byte> outputKeyingMaterial, ReadOnlySpan<byte> inputKeyingMaterial, ReadOnlySpan<byte> personalization, ReadOnlySpan<byte> salt = default, ReadOnlySpan<byte> info = default)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`outputKeyingMaterial` has a length less than `MinKeySize` or greater than `MaxKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`inputKeyingMaterial` has a length less than `MinKeySize` or greater than `MaxKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`personalization` has a length not equal to `PersonalizationSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`salt` has a length greater than 0 but not equal to `SaltSize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

The key could not be derived.

### IncrementalBLAKE2b

Provides support for computing output keying material from several messages.

```csharp
using var blake2b = new IncrementalBLAKE2b(int hashSize, ReadOnlySpan<byte> inputKeyingMaterial, ReadOnlySpan<byte> personalization, ReadOnlySpan<byte> salt = default);
blake2b.Update(ReadOnlySpan<byte> info1);
blake2b.Update(ReadOnlySpan<byte> info2);
blake2b.Finalize(Span<byte> outputKeyingMaterial1);

// Avoid another using statement
blake2b.Reinitialize(int hashSize);
blake2b.Update(ReadOnlySpan<byte> info3);
// Cache the state
blake2b.CacheState();
blake2b.Finalize(Span<byte> outputKeyingMaterial2);

// Restore the cached state
blake2b.RestoreCachedState();
// outputKeyingMaterial3 == outputKeyingMaterial2
blake2b.Finalize(Span<byte> outputKeyingMaterial3);
```

{% hint style="warning" %}
`CacheState()` can only cache the state once. Each subsequent call will overwrite the previously cached state. See the [Notes](#notes) for when this method should be used.
{% endhint %}

{% hint style="warning" %}
Do **NOT** use this class with multiple threads. If you absolutely have to, you need to use [lock statements](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/lock) to avoid exceptions being thrown.
{% endhint %}

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`hashSize` is less than `MinHashSize` or greater than `MaxHashSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`key` has a length less than `MinKeySize` or greater than `MaxKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`personalization` has a length greater than 0 but not equal to `PersonalizationSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`salt` has a length greater than 0 but not equal to `SaltSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`hash` has a length not equal to `hashSize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error initializing/updating/finalizing hash function state.

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Cannot update after finalizing or finalize twice (without reinitializing or restoring a cached state).

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Cannot cache the state after finalizing (without reinitializing).

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Cannot restore the state when it has not been cached or when the current hash size differs from what was cached.

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Methods cannot be called from multiple threads simultaneously.

[ObjectDisposedException](https://learn.microsoft.com/en-us/dotnet/api/system.objectdisposedexception)

The object has been disposed.

## Constants

These are used for validation and/or save you defining your own constants.

```csharp
public const int KeySize = 32;
public const int SaltSize = 16;
public const int PersonalizationSize = 16;
public const int MinKeySize = 32;
public const int MaxKeySize = 64;
public const int BlockSize = 128;
```

## Notes

{% hint style="danger" %}
The input keying material **MUST** be high in entropy (e.g., a shared secret).
{% endhint %}

{% hint style="danger" %}
Do **NOT** use the same output keying material for multiple purposes (e.g., encryption and authentication). You should derive separate keys using the same input keying material and personalization but different salts and/or info.
{% endhint %}

{% hint style="danger" %}
If you intend to feed **multiple variable-length** inputs into the info, beware of [canonicalization attacks](https://soatok.blog/2021/07/30/canonicalization-attacks-against-macs-and-signatures/). Please read the [Concat](/advanced/concat) page for more information.
{% endhint %}

{% hint style="success" %}
If you are making multiple calls to `IncrementalBLAKE2b` with unchanging/static data at the beginning (e.g., the same key), you can cache the state to improve performance. This allows you to only process this data once and can help you quickly zero the key from memory.
{% endhint %}


# Authenticated encryption


# Stream and file encryption

## Purpose

Encrypting streams and files should be done in chunks. This keeps memory usage low and detects corrupted chunks early. However, this is more complicated than one would expect because you need to prevent chunks from being removed, truncated, duplicated, and reordered.

This [high-level API](https://doc.libsodium.org/secret-key_cryptography/secretstream) uses [XChaCha20-Poly1305](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha#section-2) internally whilst handling these issues for you. Plaintext chunks can have different sizes, there is no practical stream length limit, associated data is supported, and rekeying is possible midway through encrypting the stream.

The optional associated data is useful for authenticating file headers, version numbers, timestamps, counters, and so on. It can be used to prevent [confused deputy attacks](https://cloud.google.com/kms/docs/additional-authenticated-data#confused_deputy_attack_example) and [replay attacks](https://en.wikipedia.org/wiki/Replay_attack). It is not encrypted nor part of the ciphertext. It must be reproduceable or stored somewhere for decryption to be possible.

{% hint style="success" %}
For file encryption, optimal chunk sizes include 16 KiB, 32 KiB, and 64 KiB. Pick one and use it for everything but the last chunk, which will likely be smaller.
{% endhint %}

## Usage

### IncrementalXChaCha20Poly1305

Initializes stream encryption or decryption using a key and header. For encryption, the header is filled with a random nonce. It **MUST** be sent/stored before the sequence of ciphertext messages because it is required to decrypt the stream.

```csharp
using var secretstream = new IncrementalXChaCha20Poly1305(Span<byte> header, ReadOnlySpan<byte> key, bool encryption)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`header` has a length not equal to `HeaderSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`key` has a length not equal to `KeySize`.

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Methods cannot be called from multiple threads simultaneously.

[OutOfMemoryException](https://learn.microsoft.com/en-us/dotnet/api/system.outofmemoryexception)

Allocating memory for the state failed.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error initializing stream encryption/decryption.

### ChunkFlag

Before encryption, a flag is attached to each chunk to provide information about that chunk:

* `ChunkFlag.Message`: an ordinary plaintext chunk.
* `ChunkFlag.Boundary`: the end of a set of messages but not the end of the stream.
* `ChunkFlag.Rekey`: derive a new key after this chunk.
* `ChunkFlag.Final`: the last plaintext chunk, marking the end of the stream.

For file encryption, `ChunkFlag.Message` and `ChunkFlag.Final` are the only flags required. For online communication, `ChunkFlag.Boundary` and `ChunkFlag.Rekey` may also be useful.

{% hint style="danger" %}
During decryption, you **MUST** check that the last chunk had the `ChunkFlag.Final` flag. Otherwise, you **MUST** throw a [CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception) to detect stream truncation.
{% endhint %}

### EncryptChunk

Fills a span with the ciphertext chunk computed from a plaintext chunk and optional associated data. `ChunkFlag.Final` **MUST** be specified for the last plaintext chunk.

```csharp
secretstream.EncryptChunk(Span<byte> ciphertextChunk, ReadOnlySpan<byte> plaintextChunk, ReadOnlySpan<byte> associatedData, ChunkFlag chunkFlag = ChunkFlag.Message)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`ciphertextChunk` has a length not equal to `plaintextChunk.Length + TagSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`chunkFlag` must be a value within the enum.

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Cannot encrypt on a decryption stream or after the final chunk without reinitializing.

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Methods cannot be called from multiple threads simultaneously.

[ObjectDisposedException](https://learn.microsoft.com/en-us/dotnet/api/system.objectdisposedexception)

The object has been disposed.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error encrypting plaintext chunk.

### DecryptChunk

Verifies that the tag appended to the ciphertext chunk is correct for the given inputs. If verification fails, an exception is thrown. Otherwise, it fills a span with the decrypted ciphertext and returns the decrypted flag for that chunk.

To detect stream truncation, if you reach the end of the stream and `ChunkFlag.Final` was not returned, you **MUST** throw a [CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception).

```csharp
var chunkFlag = secretstream.DecryptChunk(Span<byte> plaintextChunk, ReadOnlySpan<byte> ciphertextChunk, ReadOnlySpan<byte> associatedData = default)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`ciphertextChunk` has a length less than `TagSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`plaintextChunk` has a length not equal to `ciphertextChunk.Length - TagSize`.

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Cannot decrypt on an encryption stream or after the final chunk without reinitializing.

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Methods cannot be called from multiple threads simultaneously.

[ObjectDisposedException](https://learn.microsoft.com/en-us/dotnet/api/system.objectdisposedexception)

The object has been disposed.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Invalid chunk authentication tag for the given inputs.

### Rekey

Explicitly rekeys without storing the `ChunkFlag.Rekey` flag. For decryption, this function **MUST** be called at the same stream location to avoid an exception when decrypting the following chunk.

```csharp
secretstream.Rekey()
```

#### Exceptions

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Cannot rekey after the final chunk without reinitializing.

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Methods cannot be called from multiple threads simultaneously.

[ObjectDisposedException](https://learn.microsoft.com/en-us/dotnet/api/system.objectdisposedexception)

The object has been disposed.

### Reinitialize

Reinitializes stream encryption or decryption using a key and header. This avoids creating another using statement. For encryption, the header is filled with a random nonce. It **MUST** be sent/stored before the sequence of ciphertext messages because it is required to decrypt the stream.

```csharp
secretstream.Reinitialize(Span<byte> header, ReadOnlySpan<byte> key, bool encryption)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`header` has a length not equal to `HeaderSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`key` has a length not equal to `KeySize`.

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Methods cannot be called from multiple threads simultaneously.

[ObjectDisposedException](https://learn.microsoft.com/en-us/dotnet/api/system.objectdisposedexception)

The object has been disposed.

[OutOfMemoryException](https://learn.microsoft.com/en-us/dotnet/api/system.outofmemoryexception)

Allocating memory for the state failed.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error initializing stream encryption/decryption.

## Constants

These are used for validation and/or save you defining your own constants.

```csharp
public const int KeySize = 32;
public const int HeaderSize = 24;
public const int TagSize = 17;
```

## Notes

{% hint style="danger" %}
If you intend to feed **multiple variable-length** inputs into the associated data, beware of [canonicalization attacks](https://soatok.blog/2021/07/30/canonicalization-attacks-against-macs-and-signatures/). Please read the [Concat](/advanced/concat) page for more information.
{% endhint %}

{% hint style="danger" %}
The key **MUST** be uniformly random. It can either be [randomly generated](/random-data#fill) or the output of a [KDF](/key-derivation). Furthermore, it **SHOULD** be rotated periodically (e.g., a different key per file).
{% endhint %}

{% hint style="warning" %}
Do **NOT** use this class with multiple threads. If you absolutely have to, you need to use [lock statements](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/lock) to avoid exceptions being thrown.
{% endhint %}

{% hint style="warning" %}
As a *general* rule, avoid compression before encryption. It can [leak information](https://iacr.org/archive/fse2002/23650264/23650264.pdf) and has been the cause of [several attacks](https://crypto.stackexchange.com/a/38055).
{% endhint %}


# AEGIS-128L

## Purpose

[AEGIS-128L](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-aegis-aead) is an AES-based authenticated encryption with associated data (AEAD) scheme that was a [CAESAR competition](https://competitions.cr.yp.to/caesar-submissions.html) finalist. It encrypts a plaintext message using a 128-bit key and nonce (**number used only once**) whilst calculating a 256-bit tag over the plaintext and associated data.

The associated data is useful for authenticating file headers, version numbers, timestamps, counters, and so on. It can be used to prevent [confused deputy attacks](https://cloud.google.com/kms/docs/additional-authenticated-data#confused_deputy_attack_example) and [replay attacks](https://en.wikipedia.org/wiki/Replay_attack). It is not encrypted nor part of the ciphertext. It must be reproduceable or stored somewhere for decryption to be possible.

Decryption involves verifying the tag for the given inputs, which detects tampering and incorrect parameters. If verification fails, an error is returned. Otherwise, the plaintext is returned.

{% hint style="danger" %}
For encryption, the nonce **MUST NOT** be repeated or reused with the same key. You **MUST** [increment](/constant-time#increment) or [randomly generate](/random-data#fill) the nonce for each plaintext message encrypted using the same key.

Random nonces can be used for up to 2^48 messages with the same key. If in doubt, use [AEGIS-256](/authenticated-encryption/aegis-256), which has no practical limit.
{% endhint %}

{% hint style="success" %}
Consider using [AEGIS-256](/authenticated-encryption/aegis-256) for a 256-bit security level, which provides [additional protection](https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/Brochure/quantum-safe-cryptography.html?nn=916626) against cryptographically relevant quantum computers. **However**, 128-bit security [should](https://crypto.stackexchange.com/questions/102671/is-aes-128-quantum-safe) be post-quantum secure, and AEGIS-128L is [faster](https://eprint.iacr.org/2023/523).
{% endhint %}

## Usage

### Encrypt

Fills a span with ciphertext and an appended tag computed from a plaintext message, nonce, key, and optional associated data.

```csharp
AEGIS128L.Encrypt(Span<byte> ciphertext, ReadOnlySpan<byte> plaintext, ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> key, ReadOnlySpan<byte> associatedData = default)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`ciphertext` has a length not equal to `plaintext.Length + TagSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`nonce` has a length not equal to `NonceSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`key` has a length not equal to `KeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Encryption failed.

### Decrypt

Verifies that the tag appended to the ciphertext is correct for the given inputs. If verification fails, an exception is thrown. Otherwise, it fills a span with the decrypted ciphertext.

```csharp
AEGIS128L.Decrypt(Span<byte> plaintext, ReadOnlySpan<byte> ciphertext, ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> key, ReadOnlySpan<byte> associatedData = default)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`plaintext` has a length not equal to `ciphertext.Length - TagSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`ciphertext` has a length less than `TagSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`nonce` has a length not equal to `NonceSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`key` has a length not equal to `KeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Invalid authentication tag for the given inputs.

## Constants

These are used for validation and/or save you defining your own constants.

```csharp
public const int KeySize = 16;
public const int NonceSize = 16;
public const int TagSize = 32;
```

## Notes

{% hint style="danger" %}
If you intend to feed **multiple, variable-length** inputs into the associated data, beware of [canonicalization attacks](https://soatok.blog/2021/07/30/canonicalization-attacks-against-macs-and-signatures/). Please read the [Concat](/advanced/concat) page for more information.
{% endhint %}

{% hint style="danger" %}
The key **MUST** be uniformly random. It can either be [randomly generated](/random-data#fill) or the output of a [KDF](/key-derivation). Furthermore, it **SHOULD** be rotated periodically (e.g., a different key per file).
{% endhint %}

{% hint style="warning" %}
Encrypting data in 16-64 KiB chunks instead of as a single plaintext message is **RECOMMENDED** to keep memory usage low and detect corrupted chunks early. Unfortunately, it is difficult to get right. You **MUST** ensure that chunks cannot be:

1. Truncated
2. Removed
3. Reordered
4. Duplicated

1 and 2 can be accomplished by including the length of all the ciphertext chunks added together in the associated data of the first chunk. Alternatively, you can use the [STREAM](https://eprint.iacr.org/2015/189) construction.

3 and 4 can be resolved by using a [counter](/constant-time#increment) nonce or by including the previous tag in the associated data of the next chunk.

If decryption fails midway through a stream due to tampering or corruption, erase the previous plaintext outputs from memory and/or disk and throw an error.
{% endhint %}

{% hint style="warning" %}
As a *general* rule, avoid compression before encryption. It can [leak information](https://iacr.org/archive/fse2002/23650264/23650264.pdf) and has been the cause of [several attacks](https://crypto.stackexchange.com/a/38055).
{% endhint %}

{% hint style="warning" %}
To make AEGIS-128L [fully committing](https://eprint.iacr.org/2022/1260), you can [hash](/hashing) the associated data using a cryptographic hash function with a minimum of 128-bit preimage resistance and use the output as the AEAD associated data parameter.
{% endhint %}

{% hint style="success" %}
Current popular AEAD schemes like AES-GCM and ChaCha20-Poly1305 are [non-committing](https://eprint.iacr.org/2020/1491). By contrast, AEGIS-128L is believed to be [key committing](https://eprint.iacr.org/2022/268) (but not [fully committing](https://eprint.iacr.org/2022/1260)). This prevents [attacks](https://eprint.iacr.org/2023/526) when the adversary can choose the key and/or nonce (but [not](https://eprint.iacr.org/2023/1495) when they can choose the associated data). For example, it is computationally infeasible to output an AEGIS-128L ciphertext that can be decrypted without an authentication error using a different key.
{% endhint %}

{% hint style="success" %}
AEGIS-128L is [significantly faster](https://eprint.iacr.org/2023/523) than AEGIS-256, (X)ChaCha20-Poly1305, and AES-GCM when there is AES hardware support, which is available on most modern x64/ARM64 CPUs.

Without hardware support, (X)ChaCha20-Poly1305 is faster, and the AEGIS-128L implementation may be vulnerable to side-channel attacks.
{% endhint %}

{% hint style="success" %}
AEGIS-128L can be used as a [MAC](/message-authentication) by encrypting with the message as the associated data and an empty plaintext, resulting in just a tag. However, it **MUST NOT** be used as a hash function (e.g., without a secret key or for key derivation).
{% endhint %}

{% hint style="info" %}
AEGIS-128L was [originally](https://competitions.cr.yp.to/round3/aegisv11.pdf) specified to use a 128-bit tag. This is currently not supported in libsodium. Similarly, AEGIS-128 from the [CAESAR competition](https://competitions.cr.yp.to/caesar-submissions.html) is not supported nor part of the [Internet-Draft](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-aegis-aead).
{% endhint %}


# AEGIS-256

## Purpose

[AEGIS-256](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-aegis-aead) is an AES-based authenticated encryption with associated data (AEAD) scheme that was a [CAESAR competition](https://competitions.cr.yp.to/caesar-submissions.html) finalist. It encrypts a plaintext message using a 256-bit key and nonce (**number used only once**) whilst calculating a 256-bit tag over the plaintext and associated data.

The associated data is useful for authenticating file headers, version numbers, timestamps, counters, and so on. It can be used to prevent [confused deputy attacks](https://cloud.google.com/kms/docs/additional-authenticated-data#confused_deputy_attack_example) and [replay attacks](https://en.wikipedia.org/wiki/Replay_attack). It is not encrypted nor part of the ciphertext. It must be reproduceable or stored somewhere for decryption to be possible.

Decryption involves verifying the tag for the given inputs, which detects tampering and incorrect parameters. If verification fails, an error is returned. Otherwise, the plaintext is returned.

{% hint style="danger" %}
For encryption, the nonce **MUST NOT** be repeated or reused with the same key. You **MUST** [increment](/constant-time#increment) or [randomly generate](/random-data#fill) the nonce for each plaintext message encrypted using the same key.
{% endhint %}

{% hint style="success" %}
Unlike with [ChaCha20-Poly1305](/authenticated-encryption/chacha20-poly1305), it is safe to [randomly generate](/random-data#fill) nonces with the same key. Nonces can be public and are typically manually prepended to the ciphertext.

It is not necessary to use the full 256-bit nonce. For example, a 160- or 192-bit random nonce **MAY** be used, with zero padding up to 256 bits. However, a 256-bit nonce ensures there is no practical limit on the number of messages that can be encrypted using the same key.
{% endhint %}

## Usage

### Encrypt

Fills a span with ciphertext and an appended tag computed from a plaintext message, nonce, key, and optional associated data.

```csharp
AEGIS256.Encrypt(Span<byte> ciphertext, ReadOnlySpan<byte> plaintext, ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> key, ReadOnlySpan<byte> associatedData = default)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`ciphertext` has a length not equal to `plaintext.Length + TagSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`nonce` has a length not equal to `NonceSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`key` has a length not equal to `KeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Encryption failed.

### Decrypt

Verifies that the tag appended to the ciphertext is correct for the given inputs. If verification fails, an exception is thrown. Otherwise, it fills a span with the decrypted ciphertext.

```csharp
AEGIS256.Decrypt(Span<byte> plaintext, ReadOnlySpan<byte> ciphertext, ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> key, ReadOnlySpan<byte> associatedData = default)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`plaintext` has a length not equal to `ciphertext.Length - TagSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`ciphertext` has a length less than `TagSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`nonce` has a length not equal to `NonceSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`key` has a length not equal to `KeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Invalid authentication tag for the given inputs.

## Constants

These are used for validation and/or save you defining your own constants.

```csharp
public const int KeySize = 32;
public const int NonceSize = 32;
public const int TagSize = 32;
```

## Notes

{% hint style="danger" %}
If you intend to feed **multiple, variable-length** inputs into the associated data, beware of [canonicalization attacks](https://soatok.blog/2021/07/30/canonicalization-attacks-against-macs-and-signatures/). Please read the [Concat](/advanced/concat) page for more information.
{% endhint %}

{% hint style="danger" %}
The key **MUST** be uniformly random. It can either be [randomly generated](/random-data#fill) or the output of a [KDF](/key-derivation). Furthermore, it **SHOULD** be rotated periodically (e.g., a different key per file).
{% endhint %}

{% hint style="warning" %}
Encrypting data in 16-64 KiB chunks instead of as a single plaintext message is **RECOMMENDED** to keep memory usage low and detect corrupted chunks early. Unfortunately, it is difficult to get right. You **MUST** ensure that chunks cannot be:

1. Truncated
2. Removed
3. Reordered
4. Duplicated

1 and 2 can be accomplished by including the length of all the ciphertext chunks added together in the associated data of the first chunk. Alternatively, you can use the [STREAM](https://eprint.iacr.org/2015/189) construction.

3 and 4 can be resolved by using a [counter](/constant-time#increment) nonce or by including the previous tag in the associated data of the next chunk.

If decryption fails midway through a stream due to tampering or corruption, erase the previous plaintext outputs from memory and/or disk and throw an error.
{% endhint %}

{% hint style="warning" %}
As a *general* rule, avoid compression before encryption. It can [leak information](https://iacr.org/archive/fse2002/23650264/23650264.pdf) and has been the cause of [several attacks](https://crypto.stackexchange.com/a/38055).
{% endhint %}

{% hint style="warning" %}
To make AEGIS-256 [fully committing](https://eprint.iacr.org/2022/1260), you can [hash](/hashing) the associated data using a cryptographic hash function with a minimum of 128-bit preimage resistance and use the output as the AEAD associated data parameter.
{% endhint %}

{% hint style="success" %}
Current popular AEAD schemes like AES-GCM and ChaCha20-Poly1305 are [non-committing](https://eprint.iacr.org/2020/1491). By contrast, AEGIS-256 is believed to be [key committing](https://eprint.iacr.org/2022/268) (but not [fully committing](https://eprint.iacr.org/2022/1260)). This prevents [attacks](https://eprint.iacr.org/2023/526) when the adversary can choose the key and/or nonce (but [not](https://eprint.iacr.org/2023/1495) when they can choose the associated data). For example, it is computationally infeasible to output an AEGIS-256 ciphertext that can be decrypted without an authentication error using a different key.
{% endhint %}

{% hint style="success" %}
AEGIS-256 is [significantly faster](https://eprint.iacr.org/2023/523) than (X)ChaCha20-Poly1305 and AES-GCM when there is AES hardware support, which is available on most modern x64/ARM64 CPUs.

Without hardware support, (X)ChaCha20-Poly1305 is faster, and the AEGIS-256 implementation may be vulnerable to side-channel attacks.
{% endhint %}

{% hint style="success" %}
AEGIS-256 can be used as a [MAC](/message-authentication) by encrypting with the message as the associated data and an empty plaintext, resulting in just a tag. However, it **MUST NOT** be used as a hash function (e.g., without a secret key or for key derivation).
{% endhint %}

{% hint style="info" %}
AEGIS-256 was [originally](https://competitions.cr.yp.to/round3/aegisv11.pdf) specified to use a 128-bit tag. This is currently not supported in libsodium. Similarly, AEGIS-128 from the [CAESAR competition](https://competitions.cr.yp.to/caesar-submissions.html) is not supported nor part of the [Internet-Draft](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-aegis-aead).
{% endhint %}


# ChaCha20-Poly1305

## Purpose

[ChaCha20-Poly1305](https://datatracker.ietf.org/doc/html/rfc8439#section-2.8) is an authenticated encryption with associated data (AEAD) scheme. It encrypts a plaintext message using a 256-bit key and 96-bit nonce (**number used only once**) before computing a tag over the ciphertext and associated data.

The associated data is useful for authenticating file headers, version numbers, timestamps, counters, and so on. It can be used to prevent [confused deputy attacks](https://cloud.google.com/kms/docs/additional-authenticated-data#confused_deputy_attack_example) and [replay attacks](https://en.wikipedia.org/wiki/Replay_attack). It is not encrypted nor part of the ciphertext. It must be reproduceable or stored somewhere for decryption to be possible.

For decryption, the tag is first verified for the given inputs, which detects tampering and incorrect parameters. If verification fails, an error is returned. Otherwise, the ciphertext is decrypted and plaintext is returned.

{% hint style="warning" %}
Use [XChaCha20-Poly1305](/authenticated-encryption/xchacha20-poly1305) if you want random nonces with the same key.
{% endhint %}

{% hint style="danger" %}
For encryption, the nonce **MUST NOT** be repeated or reused with the same key. You **MUST** [increment](/constant-time#increment) the nonce for each plaintext message encrypted using the same key.
{% endhint %}

## Usage

### Encrypt

Fills a span with ciphertext and an appended tag computed from a plaintext message, nonce, key, and optional associated data.

```csharp
ChaCha20Poly1305.Encrypt(Span<byte> ciphertext, ReadOnlySpan<byte> plaintext, ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> key, ReadOnlySpan<byte> associatedData = default)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`ciphertext` has a length not equal to `plaintext.Length + TagSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`nonce` has a length not equal to `NonceSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`key` has a length not equal to `KeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Encryption failed.

### Decrypt

Verifies that the tag appended to the ciphertext is correct for the given inputs. If verification fails, an exception is thrown. Otherwise, it fills a span with the decrypted ciphertext.

```csharp
ChaCha20Poly1305.Decrypt(Span<byte> plaintext, ReadOnlySpan<byte> ciphertext, ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> key, ReadOnlySpan<byte> associatedData = default)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`plaintext` has a length not equal to `ciphertext.Length - TagSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`ciphertext` has a length less than `TagSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`nonce` has a length not equal to `NonceSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`key` has a length not equal to `KeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Invalid authentication tag for the given inputs.

## Constants

These are used for validation and/or save you defining your own constants.

```csharp
public const int KeySize = 32;
public const int NonceSize = 12;
public const int TagSize = 16;
```

## Notes

{% hint style="danger" %}
If you intend to feed **multiple variable-length** inputs into the associated data, beware of [canonicalization attacks](https://soatok.blog/2021/07/30/canonicalization-attacks-against-macs-and-signatures/). Please read the [Concat](/advanced/concat) page for more information.
{% endhint %}

{% hint style="danger" %}
The key **MUST** be uniformly random. It can either be [randomly generated](/random-data#fill) or the output of a [KDF](/key-derivation). Furthermore, it **SHOULD** be rotated periodically (e.g., a different key per file).
{% endhint %}

{% hint style="warning" %}
Encrypting data in 16-64 KiB chunks instead of as a single plaintext message is **recommended** to keep memory usage low and detect corrupted chunks early. Unfortunately, it is difficult to get right. You **MUST** ensure that chunks cannot be:

1. Truncated
2. Removed
3. Reordered
4. Duplicated

1 and 2 can be accomplished by including the length of all the ciphertext chunks added together in the associated data of the first chunk. Alternatively, you can use the [STREAM](https://eprint.iacr.org/2015/189) construction.

3 and 4 can be resolved by using a [counter](/constant-time#increment) nonce or by including the previous tag in the associated data of the next chunk.

If decryption fails midway through a stream due to tampering or corruption, erase the previous plaintext outputs from memory and/or disk and throw an error.
{% endhint %}

{% hint style="warning" %}
As a *general* rule, avoid compression before encryption. It can [leak information](https://iacr.org/archive/fse2002/23650264/23650264.pdf) and has been the cause of [several attacks](https://crypto.stackexchange.com/a/38055).
{% endhint %}

{% hint style="warning" %}
ChaCha20-Poly1305 is **NOT** [key- or message-committing](https://samuellucas.com/draft-lucas-generalised-committing-aead/draft-lucas-generalised-committing-aead.html#name-introduction).

1. A ciphertext message can be decrypted under multiple keys without an error.
2. An attacker who knows the key can find different messages that lead to the same tag.

This can enable [attacks](https://youtu.be/dZqEtrLh9aM) in scenarios where keys can be adversarial. For example, when an attacker can submit a ciphertext encrypted using a password to a server that knows the encryption key (an oracle).

The best fix is to switch to [Encrypt-then-MAC](https://samuellucas.com/draft-lucas-generalised-committing-aead/draft-lucas-generalised-committing-aead.html#section-3) as outlined in that link.
{% endhint %}


# XChaCha20-Poly1305

## Purpose

[XChaCha20-Poly1305](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha#section-2) is an authenticated encryption with associated data (AEAD) scheme. It encrypts a plaintext message using a 256-bit key and 192-bit nonce (**number used only once**) before computing a tag over the ciphertext and associated data.

The associated data is useful for authenticating file headers, version numbers, timestamps, counters, and so on. It can be used to prevent [confused deputy attacks](https://cloud.google.com/kms/docs/additional-authenticated-data#confused_deputy_attack_example) and [replay attacks](https://en.wikipedia.org/wiki/Replay_attack). It is not encrypted nor part of the ciphertext. It must be reproduceable or stored somewhere for decryption to be possible.

For decryption, the tag is first verified for the given inputs, which detects tampering and incorrect parameters. If verification fails, an error is returned. Otherwise, the ciphertext is decrypted and plaintext is returned.

{% hint style="danger" %}
For encryption, the nonce **MUST NOT** be repeated or reused with the same key. You **MUST** [increment](/constant-time#increment) or [randomly generate](/random-data#fill) the nonce for each plaintext message encrypted using the same key.
{% endhint %}

{% hint style="success" %}
Unlike with [ChaCha20-Poly1305](/authenticated-encryption/chacha20-poly1305), it is safe to [randomly generate](/random-data#fill) nonces with the same key. Nonces can be public and are typically manually prepended to the ciphertext.
{% endhint %}

## Usage

### Encrypt

Fills a span with ciphertext and an appended tag computed from a plaintext message, nonce, key, and optional associated data.

```csharp
XChaCha20Poly1305.Encrypt(Span<byte> ciphertext, ReadOnlySpan<byte> plaintext, ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> key, ReadOnlySpan<byte> associatedData = default)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`ciphertext` has a length not equal to `plaintext.Length + TagSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`nonce` has a length not equal to `NonceSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`key` has a length not equal to `KeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Encryption failed.

### Decrypt

Verifies that the tag appended to the ciphertext is correct for the given inputs. If verification fails, an exception is thrown. Otherwise, it fills a span with the decrypted ciphertext.

```csharp
XChaCha20Poly1305.Decrypt(Span<byte> plaintext, ReadOnlySpan<byte> ciphertext, ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> key, ReadOnlySpan<byte> associatedData = default)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`plaintext` has a length not equal to `ciphertext.Length - TagSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`ciphertext` has a length less than `TagSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`nonce` has a length not equal to `NonceSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`key` has a length not equal to `KeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Invalid authentication tag for the given inputs.

## Constants

These are used for validation and/or save you defining your own constants.

```csharp
public const int KeySize = 32;
public const int NonceSize = 24;
public const int TagSize = 16;
```

## Notes

{% hint style="danger" %}
If you intend to feed **multiple variable-length** inputs into the associated data, beware of [canonicalization attacks](https://soatok.blog/2021/07/30/canonicalization-attacks-against-macs-and-signatures/). Please read the [Concat](/advanced/concat) page for more information.
{% endhint %}

{% hint style="danger" %}
The key **MUST** be uniformly random. It can either be [randomly generated](/random-data#fill) or the output of a [KDF](/key-derivation). Furthermore, it **SHOULD** be rotated periodically (e.g., a different key per file).
{% endhint %}

{% hint style="warning" %}
Encrypting data in 16-64 KiB chunks instead of as a single plaintext message is **recommended** to keep memory usage low and detect corrupted chunks early. Unfortunately, it is difficult to get right. You **MUST** ensure that chunks cannot be:

1. Truncated
2. Removed
3. Reordered
4. Duplicated

1 and 2 can be accomplished by including the length of all the ciphertext chunks added together in the associated data of the first chunk. Alternatively, you can use the [STREAM](https://eprint.iacr.org/2015/189) construction.

3 and 4 can be resolved by using a [counter](/constant-time#increment) nonce (the initial nonce can be [random](/random-data#fill)) or by including the previous tag in the associated data of the next chunk.

If decryption fails midway through a stream due to tampering or corruption, erase the previous plaintext outputs from memory and/or disk and throw an error.
{% endhint %}

{% hint style="warning" %}
As a *general* rule, avoid compression before encryption. It can [leak information](https://iacr.org/archive/fse2002/23650264/23650264.pdf) and has been the cause of [several attacks](https://crypto.stackexchange.com/a/38055).
{% endhint %}

{% hint style="warning" %}
XChaCha20-Poly1305 is **NOT** [key- or message-committing](https://samuellucas.com/draft-lucas-generalised-committing-aead/draft-lucas-generalised-committing-aead.html#name-introduction).

1. A ciphertext message can be decrypted under multiple keys without an error.
2. An attacker who knows the key can find different messages that lead to the same tag.

This can enable [attacks](https://youtu.be/dZqEtrLh9aM) in scenarios where keys can be adversarial. For example, when an attacker can submit a ciphertext encrypted using a password to a server that knows the encryption key (an oracle).

The best fix is to switch to [Encrypt-then-MAC](https://samuellucas.com/draft-lucas-generalised-committing-aead/draft-lucas-generalised-committing-aead.html#section-3) as outlined in that link.
{% endhint %}

{% hint style="info" %}
Consider [ChaCha20-Poly1305](/authenticated-encryption/chacha20-poly1305) if you do not need random nonces with the same key. For example, if you can use a [counter](/constant-time#increment) nonce or rotate the key for each encryption operation. It is marginally more efficient.
{% endhint %}


# Key encapsulation mechanisms


# X-Wing

## Purpose

When communicating with another party, you often need a way to establish a shared secret (symmetric) key without having an existing secure channel. An algorithm that allows this over a public (insecure) channel is called a key-establishment scheme.

Key encapsulation mechanisms (KEMs) are one type of key-establishment scheme, and they're the design of choice for **post-quantum algorithms**. Unlike a [traditional key exchange](/key-exchange), the sender's key pair isn't involved, a ciphertext needs to be sent to the recipient, and the shared secret is uniformly random. From the sending side, the algorithm is randomised rather than deterministic.

Here is how it works when doing one trip of communication:

1. **Key generation**: Alice generates a key pair.
2. **Encapsulation**: Bob uses Alice's public key to generate a shared secret and an associated ciphertext. This ciphertext is sent to Alice.
3. **Decapsulation**: Alice uses the ciphertext and her private key to compute the same shared secret.

[X-Wing](https://datatracker.ietf.org/doc/html/draft-connolly-cfrg-xwing-kem) is designed to be the sensible, interoperable **hybrid (post-quantum + traditional security) KEM** for most applications. There are no variants, and it provides a 128-bit security level. The design includes optimisations, combines popular algorithms, and hedges against cryptanalysis advancements.

{% hint style="danger" %}
Private keys **MUST** **NOT** be shared. They **MUST** remain secret.
{% endhint %}

## Usage

### GenerateKeyPair

Fills a span with a randomly generated private key and another span with the associated public key.

```csharp
XWing.GenerateKeyPair(Span<byte> publicKey, Span<byte> privateKey);
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`publicKey` has a length not equal to `PublicKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`privateKey` has a length not equal to `PrivateKeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error generating key pair.

### GenerateKeyPair

Fills a span with a private key generated using a [random](/random-data#fill) seed and another span with the associated public key.

```csharp
XWing.GenerateKeyPair(Span<byte> publicKey, Span<byte> privateKey, ReadOnlySpan<byte> seed);
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`publicKey` has a length not equal to `PublicKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`privateKey` has a length not equal to `PrivateKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`seed` has a length not equal to `SeedSize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error generating key pair from seed.

### Encapsulate

Fills a span with the computed shared secret and another span with the ciphertext to send to the recipient based on the recipient's public key.

```csharp
XWing.Encapsulate(Span<byte> sharedSecret, Span<byte> ciphertext, ReadOnlySpan<byte> recipientPublicKey);
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`sharedSecret` has a length not equal to `SharedSecretSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`ciphertext` has a length not equal to `CiphertextSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`recipientPublicKey` has a length not equal to `PublicKeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Invalid public key.

### Decapsulate

Fills a span with the computed shared secret based on the recipient's private key and the ciphertext the recipient was sent.

```csharp
XWing.Decapsulate(Span<byte> sharedSecret, ReadOnlySpan<byte> ciphertext, ReadOnlySpan<byte> recipientPrivateKey);
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`sharedSecret` has a length not equal to `SharedSecretSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`ciphertext` has a length not equal to `CiphertextSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`recipientPrivateKey` has a length not equal to `PrivateKeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Invalid ciphertext.

## Constants

These are used for validation and/or save you defining your own constants.

```csharp
public const int PublicKeySize = 1216;
public const int PrivateKeySize = 32;
public const int SeedSize = 32;
public const int SharedSecretSize = 32;
public const int CiphertextSize = 1120;
```

## Notes

{% hint style="warning" %}
Not all uses of traditional key exchange can be replaced in a straightforward manner by KEMs. X-Wing doesn't provide non-interactive key exchange (NIKE) or authenticated KEM functionality.
{% endhint %}

{% hint style="warning" %}
Both [X25519](https://elligator.org/) and [ML-KEM](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-kemeleon) are distinguishable from random (e.g., someone can tell that cryptography is being used from looking at public keys/ciphertexts). Because X-Wing does nothing about this, it's not suitable for scenarios like plausible deniability and censorship-resistance.
{% endhint %}

{% hint style="info" %}
If you [read](https://csrc.nist.gov/pubs/sp/800/227/final) about post-quantum algorithms, you may see the terms 'encapsulation key' and 'decapsulation key'. These mean 'public key' and 'private key' but are specific to KEMs.
{% endhint %}

{% hint style="info" %}
X-Wing uses X25519/ML-KEM-768 for key establishment and SHAKE256/SHA3-256 as KDFs.

The private key is a seed that gets expanded to derive ML-KEM and X25519 key pairs. The public key, ciphertext, and shared secret are the result of concatenating X25519 and ML-KEM-768 values together.
{% endhint %}


# ML-KEM-768

## Purpose

When communicating with another party, you often need a way to establish a shared secret (symmetric) key without having an existing secure channel. An algorithm that allows this over a public (insecure) channel is called a key-establishment scheme.

Key encapsulation mechanisms (KEMs) are one type of key-establishment scheme, and they're the interface of choice for **post-quantum algorithms**, which protect against cryptographically relevant quantum computers (CRQCs). Even without a CRQC today, an adversary can capture encrypted data that relies on [traditional key-establishment algorithms](/key-exchange) ready for decryption when such a CRQC becomes available (a [store now, decrypt later attack](https://en.wikipedia.org/wiki/Harvest_now%2C_decrypt_later)).

Unlike a traditional key exchange, the sender's key pair isn't involved, a ciphertext needs to be sent to the recipient, and the shared secret is uniformly random. From the sending side, the algorithm is randomised rather than deterministic (the sender doesn't choose the shared secret).

Here is how it works when doing one trip of communication (real protocols often do multiple trips):

1. **Key generation**: the recipient generates a key pair and shares the public key with the sender.
2. **Encapsulation**: the sender uses the recipient's public key to generate a shared secret and an associated ciphertext. This ciphertext is sent to the recipient.
3. **Decapsulation**: the recipient uses the ciphertext and their private key to compute the same shared secret.

[ML-KEM-768](https://csrc.nist.gov/pubs/fips/203/final) is the middle (192-bit) security strength variant of ML-KEM, which is one of the algorithms standardised by [NIST](https://csrc.nist.gov/Projects/post-quantum-cryptography/post-quantum-cryptography-standardization/selected-algorithms) as part of the Post-Quantum Cryptography (PQC) Standardization (competition) process. This variant provides protection against cryptanalysis advancements compared to ML-KEM-512 whilst being [lighter](https://pqshield.github.io/nist-sigs-zoo/kems/?s=ML-KEM) (smaller parameters/marginally faster) than ML-KEM-1024. All variants are [faster](https://pqshield.github.io/nist-sigs-zoo/kems/?s=ML-KEM%2CECDH) than traditional key exchange algorithms (e.g., X25519) but have larger parameters.

{% hint style="danger" %}
Private keys **MUST** **NOT** be shared. They **MUST** remain secret and be protected from modification.
{% endhint %}

{% hint style="success" %}
Consider using [X-Wing](/key-encapsulation-mechanisms/x-wing) instead, which uses X25519 + ML-KEM-768 to provide [classical security still](https://soatok.blog/2022/01/27/the-controversy-surrounding-hybrid-cryptography/) if ML-KEM-768 is unexpectedly broken. Whilst this is [arguably unnecessary](https://soatok.blog/2026/04/13/hybrid-constructions-the-post-quantum-safety-blanket/) and has [some drawbacks](https://www.ncsc.gov.uk/paper/next-steps-in-preparing-for-post-quantum-cryptography#section_5), it is a [popular/recommended approach](https://crypto.stackexchange.com/q/119797).

However, X-Wing isn't suitable in all cases, like when doing protocols requiring plausible deniability or censorship-resistance. In such cases, one may need to create their own hybrid KEM.
{% endhint %}

## Usage

### GenerateKeyPair

Fills a span with a randomly generated private key and another span with the associated public key.

```csharp
MLKEM768.GenerateKeyPair(Span<byte> publicKey, Span<byte> privateKey);
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`publicKey` has a length not equal to `PublicKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`privateKey` has a length not equal to `PrivateKeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error generating key pair.

### GenerateKeyPair

Fills a span with a private key generated using a [random](/random-data#fill) seed and another span with the associated public key.

```csharp
MLKEM768.GenerateKeyPair(Span<byte> publicKey, Span<byte> privateKey, ReadOnlySpan<byte> seed);
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`publicKey` has a length not equal to `PublicKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`privateKey` has a length not equal to `PrivateKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`seed` has a length not equal to `SeedSize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error generating key pair from seed.

### Encapsulate

Fills a span with the computed shared secret and another span with the ciphertext to send to the recipient based on the recipient's public key.

```csharp
MLKEM768.Encapsulate(Span<byte> sharedSecret, Span<byte> ciphertext, ReadOnlySpan<byte> recipientPublicKey);
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`sharedSecret` has a length not equal to `SharedSecretSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`ciphertext` has a length not equal to `CiphertextSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`recipientPublicKey` has a length not equal to `PublicKeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Invalid public key.

### Decapsulate

Fills a span with the computed shared secret based on the recipient's private key and the ciphertext the recipient was sent.

```csharp
MLKEM768.Decapsulate(Span<byte> sharedSecret, ReadOnlySpan<byte> ciphertext, ReadOnlySpan<byte> recipientPrivateKey);
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`sharedSecret` has a length not equal to `SharedSecretSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`ciphertext` has a length not equal to `CiphertextSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`recipientPrivateKey` has a length not equal to `PrivateKeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error performing decapsulation.

## Constants

These are used for validation and/or save you defining your own constants.

```csharp
public const int PublicKeySize = 1184;
public const int PrivateKeySize = 2400;
public const int SeedSize = 64;
public const int SharedSecretSize = 32;
public const int CiphertextSize = 1088;
```

## Notes

{% hint style="danger" %}
A KEM does **NOT** provide authentication of either party. It's important to verify that any public keys and ciphertexts came from who you expect, which is typically done via [digital signatures](/digital-signatures). For example, a [certificate](https://csrc.nist.gov/pubs/sp/800/227/final) ties the identity of the key pair owner to the static public key and proves that they possess the corresponding private key (via certificate authority checks).

[Key confirmation](https://csrc.nist.gov/pubs/sp/800/227/final) can also be used for proof of possession of the private key corresponding to the public key, which works with static and ephemeral key pairs. Successful use of the shared secret when decrypting an [AEAD](/authenticated-encryption) ciphertext is one form. Another is having one or both parties exchange and verify [MAC](/message-authentication) tags over data involved in the key establishment, which is more complicated.
{% endhint %}

{% hint style="danger" %}
One would expect that different ciphertexts/public keys produce different shared secrets. However, an adversary that can control a **private key** can cause the [same shared secret](https://eprint.iacr.org/2024/523) to be generated.

Technically, only protocols where the private key cannot be fully trusted (e.g., it's received from a third party or sometimes revealed) are at risk. **However, it's best practice to hash ciphertexts/public keys alongside shared secrets during key derivation**, which mitigates this.

Alternatively, one can store a random 256-bit seed as the private key and use a KDF to expand it to 512 bits for ML-KEM seeded key generation to derive the large private key, as done in [X-Wing](/key-encapsulation-mechanisms/x-wing). This prevents a malformed private key because the attacker can't control the key derivation.
{% endhint %}

{% hint style="warning" %}
Not all uses of traditional key exchange can be replaced in a straightforward manner by KEMs. For example, KEMs don't provide [non-interactive key exchange (NIKE)](https://eprint.iacr.org/2023/271) functionality (where both users can compute the shared secret without interaction if they know each other's public key).

KEMs are inherently synchronous and interactive because of the ciphertext. This [prevents](https://eprint.iacr.org/2022/539) non-interactive authentication via static public keys, which avoids digital signatures. In an offline context, the best you can do is encapsulate to the recipient's static public key (no sender keys can be involved because that would require an interactive protocol).
{% endhint %}

{% hint style="warning" %}
ML-KEM public keys and ciphertexts are [distinguishable from random](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-kemeleon) (e.g., someone can tell that cryptography is being used). Therefore, it's not suitable for protocols requiring plausible deniability or censorship-resistance without using a scheme such as [Kemeleon](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-kemeleon), which is not implemented in libsodium.
{% endhint %}

{% hint style="warning" %}
A collision-resistant [KDF](/key-derivation) should be used to derive keys from shared secrets. There are two main approaches here:

1. For each KEM trip of communication, you can use the shared secret as the KDF key and the public key, ciphertext, and domain separation concatenated together as the message. Each derived key can then be concatenated together and fed to a KDF again to combine the keys and produce usable key material.
2. For all KEM trips of communication, you can concatenate the shared secrets for use as the KDF key and concatenate all public keys, ciphertexts, and some domain separation for the message.

There are multiple KDF designs, so the above may need to be adjusted. For example, [hashing](/hashing) (with a modern hash function) shared secrets concatenated together to derive a KDF key. Alternatively, using an [empty KDF key](https://eprint.iacr.org/2023/861) or [modern hash function](https://crypto.stackexchange.com/a/68047) and feeding everything in as the message (shared secrets first).
{% endhint %}

{% hint style="success" %}
Static (long-term) as well as ephemeral public keys can be used safely. Ephemeral public keys help provide [forward secrecy](https://en.wikipedia.org/wiki/Forward_secrecy), which protects prior communications in the event of a key compromise.
{% endhint %}

{% hint style="success" %}
ML-KEM has a [tiny](https://csrc.nist.gov/pubs/fips/203/final) probability of decapsulation failure. Even if everything is done honestly/correctly, both parties may not derive the same shared secret.

However, practically speaking, this will [never happen](https://datatracker.ietf.org/doc/html/draft-sfluhrer-cfrg-ml-kem-security-considerations). In other words, this isn't something to worry about.
{% endhint %}

{% hint style="success" %}
If an attacker substitutes a public key or modifies/replaces a ciphertext, the derived shared secret will be different between the two parties, which will cause an error in any properly designed protocol.

However, if an attacker can substitute the stored copy of the public key used for encapsulation (by the sender) and decapsulation (by the recipient), they can introduce a malicious public key. This isn't possible if the private key is stored securely and protected against modification (e.g., encrypted with an [AEAD scheme](/authenticated-encryption)).
{% endhint %}

{% hint style="info" %}
Encapsulation can error due to a check that the integers encoded in the public key are in the valid range. This sort of check is not performed for decapsulation.
{% endhint %}

{% hint style="info" %}
Whilst the shared secret is 256 bits long, its security strength is actually 192 bits due to the security level of ML-KEM-768.
{% endhint %}

{% hint style="info" %}
If you [read](https://csrc.nist.gov/pubs/sp/800/227/final) about post-quantum algorithms, you may see the terms 'encapsulation key' and 'decapsulation key'. These mean 'public key' and 'private key' but are specific to KEMs.
{% endhint %}

### Non-Interactive Pattern <a href="#non-interactive-patterns" id="non-interactive-patterns"></a>

This is a one-way pattern, so no back and forth between the sender and recipient is required. It's appropriate for offline applications (e.g., a file encryption program).

The recipient does not know who sent the message, and only the recipient can decrypt the message once the shared secret is erased from memory. This is the [post-quantum equivalent](https://eprint.iacr.org/2022/539) of the [N pattern](https://noiseprotocol.org/noise.html#one-way-handshake-patterns). The security properties are discussed [here](https://noiseexplorer.com/patterns/N/).

{% hint style="warning" %}
It's not possible to get sender authentication in a non-interactive way using only KEMs. Therefore, this is the only non-interactive pattern.
{% endhint %}

1. The sender encapsulates to the recipient's static public key. They use the shared secret as input keying material to a KDF. The output keying material is used as the key to encrypt a message using an AEAD.
2. The KEM ciphertext is sent alongside the AEAD ciphertext to the recipient.
3. The recipient decapsulates using their static private key and the ciphertext from the sender. They use the shared secret to derive the same key and decrypt the AEAD ciphertext.

### Interactive Patterns <a href="#non-interactive-patterns" id="non-interactive-patterns"></a>

These are two-way patterns, so back and forth between the sender and recipient is required. They are appropriate for online applications and can provide [better](https://noiseprotocol.org/noise.html#payload-security-properties) security properties than the one-way patterns.

#### Bilateral Authenticated Key Establishment

Both parties are authenticated using static KEM key pairs (e.g., associated with certificates, with each other's certificates exchanged and verified before the below). This achieves [weak forward secrecy](https://csrc.nist.gov/pubs/sp/800/227/final) due to ephemeral key pair involvement.

1. The sender generates an ephemeral key pair. They then encapsulate to the recipient's static public key. They send the ephemeral public key and static KEM ciphertext to the recipient.
2. The recipient encapsulates to the sender's static public key and encapsulates to the sender's ephemeral public key, sending both KEM ciphertexts to the sender. They also decapsulate the static KEM ciphertext from the sender.
3. The sender decapsulates the static and ephemeral KEM ciphertexts from the recipient.
4. Both parties now have three shared secrets - two static (each other) and one ephemeral (recipient to sender). These are combined using a KDF, with the output keying material used with an AEAD.


# Key exchange

## Purpose

It is generally difficult to share a symmetric key with another party because it needs to remain secret. Fortunately, a sender and recipient can exchange public keys, which do not need to be secret.

Both parties can then compute the same shared secret using their private key and the other party's public key. This shared secret can be turned into a symmetric key (e.g., for [encryption](/authenticated-encryption/xchacha20-poly1305)).

{% hint style="success" %}
For [post-quantum security](https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/Brochure/quantum-safe-cryptography.html?nn=433196), a pre-shared key can also be shared between the two parties via a **secure** (e.g., **encrypted**) channel.

Alternatively, a post-quantum or hybrid KEM can be used, like [X-Wing](/key-encapsulation-mechanisms/x-wing). However, the KEM interface means such algorithms cannot be used in all scenarios (e.g., [non-interactive](#non-interactive-patterns) key exchanges).
{% endhint %}

{% hint style="danger" %}
Private keys **MUST** **NOT** be shared. They **MUST** remain secret.
{% endhint %}

{% hint style="info" %}
You often want to include ephemeral (one-time) key pairs in a key exchange as well. See the [Notes](#notes) for examples of how to do this.
{% endhint %}

## Usage

### GenerateKeyPair

Fills a span with a randomly generated private key and another span with the associated public key.

```csharp
X25519.GenerateKeyPair(Span<byte> publicKey, Span<byte> privateKey)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`publicKey` has a length not equal to `PublicKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`privateKey` has a length not equal to `PrivateKeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Unable to generate key pair.

### GenerateKeyPair

Fills a span with a private key generated using a [random](/random-data#fill) seed and another span with the associated public key.

```csharp
X25519.GenerateKeyPair(Span<byte> publicKey, Span<byte> privateKey, ReadOnlySpan<byte> seed)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`publicKey` has a length not equal to `PublicKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`privateKey` has a length not equal to `PrivateKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`seed` has a length not equal to `SeedSize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Unable to generate key pair from seed.

### ComputePublicKey

Fills a span with the public key computed from a private key.

```csharp
X25519.ComputePublicKey(Span<byte> publicKey, ReadOnlySpan<byte> privateKey)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`publicKey` has a length not equal to `PublicKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`privateKey` has a length not equal to `PrivateKeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Unable to compute public key from private key.

### DeriveSenderSharedKey

Fills a span with the shared key **for the sender** using their private key, a recipient public key, an optional personalization constant, and an optional pre-shared key.

```csharp
X25519.DeriveSenderSharedKey(Span<byte> sharedKey, ReadOnlySpan<byte> senderPrivateKey, ReadOnlySpan<byte> recipientPublicKey, ReadOnlySpan<byte> personalization = default, ReadOnlySpan<byte> preSharedKey = default)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`sharedKey` has a length less than `MinSharedKeySize` or greater than `MaxSharedKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`senderPrivateKey` has a length not equal to `PrivateKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`recipientPublicKey` has a length not equal to `PublicKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

If specified, `personalization` has a length not equal to `PersonalizationSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

If specified, `preSharedKey` has a length less than `MinPreSharedKeySize` or greater than `MaxPreSharedKeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Invalid recipient public key or unable to compute hash.

### DeriveRecipientSharedKey

Fills a span with the shared key **for the recipient** using their private key, the sender's public key, an optional personalization constant, and an optional pre-shared key. The personalization and pre-shared key must match `DeriveSenderSharedKey()`.

```csharp
X25519.DeriveRecipientSharedKey(Span<byte> sharedKey, ReadOnlySpan<byte> recipientPrivateKey, ReadOnlySpan<byte> senderPublicKey, ReadOnlySpan<byte> personalization = default, ReadOnlySpan<byte> preSharedKey = default)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`sharedKey` has a length less than `MinSharedKeySize` or greater than `MaxSharedKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`recipientPrivateKey` has a length not equal to `PrivateKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`senderPublicKey` has a length not equal to `PublicKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

If specified, `personalization` has a length not equal to `PersonalizationSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

If specified, `preSharedKey` has a length less than `MinPreSharedKeySize` or greater than `MaxPreSharedKeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Invalid sender public key or unable to compute hash.

### ComputeSharedSecret

Fills a span with the **unhashed** X25519 shared secret for a given sender private key and recipient public key.

```csharp
X25519.ComputeSharedSecret(Span<byte> sharedSecret, ReadOnlySpan<byte> senderPrivateKey, ReadOnlySpan<byte> recipientPublicKey)
```

{% hint style="danger" %}

1. The number of possible keys is limited to the group size (\~2^252), which is smaller than the key space.
2. Many (public key, private key) pairs produce the same shared secret. This can lead to [vulnerabilities](https://datatracker.ietf.org/doc/html/rfc7748#section-7).

Therefore, it is important to pass the shared secret [concatenated](/advanced/concat) with the sender's public key and recipient's public key through a [KDF](/key-derivation). This is what `DeriveSenderSharedKey()` and `DeriveRecipientSharedKey()` do for you.
{% endhint %}

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`sharedSecret` has a length not equal to `SharedSecretSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`senderPrivateKey` has a length not equal to `PrivateKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`recipientPublicKey` has a length not equal to `PublicKeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Invalid public key.

## Constants

These are used for validation and/or save you defining your own constants.

```csharp
public const int PublicKeySize = 32;
public const int PrivateKeySize = 32;
public const int SeedSize = 32;
public const int SharedSecretSize = 32;
public const int SharedKeySize = 32;
public const int PersonalizationSize = 16;
public const int PreSharedKeySize = 32;
public const int MinSharedKeySize = 16;
public const int MaxSharedKeySize = 64;
public const int MinPreSharedKeySize = 16;
public const int MaxPreSharedKeySize = 64;
```

## Notes

{% hint style="success" %}
You generally want to use key exchange [patterns](https://noiseprotocol.org/noise.html#handshake-patterns) from the [Noise Protocol Framework](https://noiseprotocol.org/noise.html) or a protocol like Signal's [X3DH](https://www.signal.org/docs/specifications/x3dh/). A *rough* summary of some popular Noise patterns can be found below.
{% endhint %}

{% hint style="warning" %}
You should [derive](/key-derivation) different keys for each direction (sending/receiving) if using a [counter](/constant-time#increment) nonce for [encryption](/authenticated-encryption/chacha20-poly1305) so you do not have to wait for an acknowledgment after every message. This can be done by deriving 512 bits of output keying material and using the first 256 bits for the sender and the latter 256 bits for the recipient.
{% endhint %}

{% hint style="warning" %}
X25519 public keys are distinguishable from random. This makes it very difficult to hide that cryptography is being used if that is a goal. [Elligator](https://elligator.org/) can be used to do this, but it is not available in libsodium.
{% endhint %}

{% hint style="danger" %}
Do **NOT** use the same seed for [X25519](/key-exchange#generatekeypair-1) and [Ed25519](/digital-signatures#generatekeypair-1) key generation. Instead, read the [Ed25519 to X25519](/advanced/ed25519-to-x25519) page.
{% endhint %}

### Non-Interactive Patterns

These are one-way patterns, so no back and forth between the sender and recipient is required. They are appropriate for offline applications (e.g., a file encryption program).

#### Authenticated Key Exchange

The recipient knows who sent the message, and only the recipient can decrypt the message. This can be achieved using the [K pattern](https://noiseprotocol.org/noise.html#one-way-handshake-patterns). The security properties are discussed [here](https://noiseexplorer.com/patterns/K/).

1. The sender generates an ephemeral key pair.
2. The sender computes an ephemeral shared secret using their ephemeral private key and the recipient's public key. The ephemeral private key is then erased from memory.
3. The sender also computes a long-term shared secret using their long-term private key and the recipient's public key.
4. The sender concatenates the ephemeral shared secret and long-term shared secret to form the input keying material for a KDF. The output keying material is used as the key to encrypt a message using an AEAD.
5. The sender's ephemeral public key is prepended to the ciphertext, and the ciphertext is sent to the recipient.
6. The recipient reads the ephemeral public key, computes the ephemeral shared secret using their private key and the sender's ephemeral public key, computes the long-term shared secret using their private key and the sender's long-term public key, derives the encryption key using the same KDF, and decrypts the ciphertext.

The [X pattern](https://noiseprotocol.org/noise.html#one-way-handshake-patterns) can also be used, which is identical except that the sender's long-term public key is sent with the message under encryption instead of already being known to the recipient. The security properties are discussed [here](https://noiseexplorer.com/patterns/X/).

#### Deniable Authenticated Key Exchange

The recipient knows who sent the message, but both parties can decrypt the message. This means either party could have encrypted the message.

{% hint style="warning" %}
Noise does not provide a pattern for this, and it is weaker than non-deniable authenticated key exchange because a compromise of either party's private key is problematic.

Furthermore, the same shared secret will always be derived for the same (sender, recipient) pair, so you need to be even more careful about not reusing a nonce with the AEAD.
{% endhint %}

1. The sender computes a shared secret using their long-term private key and the recipient's public key.
2. The sender uses the shared secret as the input keying material for a KDF. The output keying material is used as the key to encrypt a message using an AEAD.
3. The recipient computes the shared secret using their private key and the sender's public key, derives the encryption key using the same KDF, and decrypts the ciphertext.

#### Anonymous Key Exchange

The recipient does not know who sent the message, and only the recipient can decrypt the message. This can be accomplished using the [N pattern](https://noiseprotocol.org/noise.html#one-way-handshake-patterns). The security properties are discussed [here](https://noiseexplorer.com/patterns/N/).

{% hint style="warning" %}
You generally want an **authenticated** key exchange because you often want to be sure that a message came from a certain person. Otherwise, an attacker can replace a message with one of their choosing, and the recipient will be none the wiser.
{% endhint %}

1. The sender generates an ephemeral key pair.
2. The sender computes the shared secret using their ephemeral private key and the recipient's public key. The ephemeral private key is then erased from memory.
3. The sender uses the shared secret as input keying material to a KDF. The output keying material is used as the key to encrypt a message using an AEAD.
4. The sender's ephemeral public key is prepended to the ciphertext, and the ciphertext is sent to the recipient.
5. The recipient reads the ephemeral public key, computes the shared secret using their private key and the sender's ephemeral public key, derives the encryption key using the same KDF, and decrypts the ciphertext.

### Interactive Patterns

These are two-way patterns, so back and forth between the sender and recipient is required. They are appropriate for online applications and can provide [better](https://noiseprotocol.org/noise.html#payload-security-properties) security properties than the one-way patterns.

#### Authenticated Key Exchange

The recipient knows who sent the message, and you [eventually](https://noiseexplorer.com/patterns/KK/) benefit from resistance to key compromise impersonation and strong forward secrecy. This can be achieved using the [KK pattern](https://noiseprotocol.org/noise.html#interactive-handshake-patterns-fundamental).

1. The sender generates an ephemeral key pair.
2. The sender computes an ephemeral shared secret using their ephemeral private key and the recipient's long-term public key.
3. The sender also computes a long-term shared secret using their long-term private key and the recipient's long-term public key.
4. The sender concatenates the ephemeral shared secret and long-term shared secret to form the input keying material for a KDF. The output keying material is used as the key to encrypt a message using an AEAD.
5. The sender's ephemeral public key is prepended to the ciphertext, and the ciphertext is sent to the recipient.
6. The recipient reads the ephemeral public key, computes the ephemeral shared secret using their private key and the sender's ephemeral public key, computes the long-term shared secret using their private key and the sender's long-term public key, derives the encryption key using the same KDF, and decrypts the ciphertext.
7. The recipient generates an ephemeral key pair.
8. The recipient computes an ephemeral shared secret using their ephemeral private key and the sender's ephemeral public key.
9. The recipient computes another ephemeral shared secret using their ephemeral private key and the sender's long-term public key. The ephemeral private key is then erased from memory.
10. The recipient concatenates both ephemeral shared secrets to form the input keying material for a KDF. Importantly, the previous derived encryption key is included in this KDF usage (as a salt by the spec, but the info parameter could be used). The output keying material is used as the key to encrypt a message using an AEAD.
11. The recipient's ephemeral public key is prepended to the ciphertext, and the ciphertext is sent to the sender.
12. The sender reads the ephemeral public key, computes the ephemeral shared secret using their ephemeral private key and the sender's ephemeral public key, erases their ephemeral private key, computes the other shared secret using their long-term private key and the recipient's ephemeral public key, and then derives the new encryption key in the same way the recipient did, which allows them to decrypt the ciphertext.
13. Subsequent messages can be encrypted using the same key and an incremented nonce.

#### Anonymous Key Exchange

The recipient does not know who sent the message. This can be accomplished using the [NK pattern](https://noiseprotocol.org/noise.html#interactive-handshake-patterns-fundamental). The security properties are discussed [here](https://noiseexplorer.com/patterns/NK/).

1. The sender generates an ephemeral key pair.
2. The sender computes the shared secret using their ephemeral private key and the recipient's public key.
3. The sender uses the shared secret as input keying material to a KDF. The output keying material is used as the key to encrypt a message using an AEAD.
4. The sender's ephemeral public key is prepended to the ciphertext, and the ciphertext is sent to the recipient.
5. The recipient reads the ephemeral public key, computes the shared secret using their private key and the sender's ephemeral public key, derives the encryption key using the same KDF, and decrypts the ciphertext.
6. The recipient generates an ephemeral key pair.
7. The recipient computes an ephemeral shared secret using their ephemeral private key and the sender's ephemeral public key. The ephemeral private key is then erased from memory.
8. The recipient uses the ephemeral shared secret as input keying material to a KDF. Importantly, the previous derived encryption key is included in this KDF usage (as a salt by the spec, but the info parameter could be used). The output keying material is used as the key to encrypt a message using an AEAD.
9. The recipient's ephemeral public key is prepended to the ciphertext, and the ciphertext is sent to the sender.
10. The sender reads the ephemeral public key, computes the ephemeral shared secret using their ephemeral private key and the sender's ephemeral public key, erases their ephemeral private key, and then derives the new encryption key in the same way the recipient did, which allows them to decrypt the ciphertext.
11. Subsequent messages can be encrypted using the same key and an incremented nonce.


# Digital signatures

## Purpose

A digital signature verifies the authenticity of a message and provides non-repudiation. This means any change to the message causes signature verification to fail, you know who signed the message, and someone cannot deny having signed a message.

Signing is done using a private key. The associated public key can then be publicly shared to allow others to verify signatures.

{% hint style="danger" %}
Private keys **MUST** **NOT** be shared. They **MUST** remain secret.
{% endhint %}

{% hint style="warning" %}
Generally, **avoid** using signatures with encryption and instead rely on **authenticated** [key exchange](/key-exchange). You can find out more [here](https://neilmadden.blog/2018/11/14/public-key-authenticated-encryption-and-why-you-want-it-part-i/).
{% endhint %}

## Usage

### GenerateKeyPair

Fills a span with a randomly generated private key and another span with the associated public key.

```csharp
Ed25519.GenerateKeyPair(Span<byte> publicKey, Span<byte> privateKey)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`publicKey` has a length not equal to `PublicKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`privateKey` has a length not equal to `PrivateKeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Unable to generate key pair.

### GenerateKeyPair

Fills a span with a private key generated using a [random](/random-data#fill) seed and another span with the associated public key.

```csharp
Ed25519.GenerateKeyPair(Span<byte> publicKey, Span<byte> privateKey, ReadOnlySpan<byte> seed)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`publicKey` has a length not equal to `PublicKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`privateKey` has a length not equal to `PrivateKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`seed` has a length not equal to `SeedSize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Unable to generate key pair from seed.

### GetPublicKey

Fills a span with the public key retrieved from a private key.

```csharp
Ed25519.GetPublicKey(Span<byte> publicKey, ReadOnlySpan<byte> privateKey)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`publicKey` has a length not equal to `PublicKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`privateKey` has a length not equal to `PrivateKeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error retrieving public key from private key.

### GetSeed

Fills a span with the seed retrieved from a private key.

```csharp
Ed25519.GetSeed(Span<byte> seed, ReadOnlySpan<byte> privateKey)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`seed` has a length not equal to `SeedSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`privateKey` has a length not equal to `PrivateKeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error retrieving seed from private key.

### Sign

Fills a span with the signature for a message signed using a private key.

```csharp
Ed25519.Sign(Span<byte> signature, ReadOnlySpan<byte> message, ReadOnlySpan<byte> privateKey)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`signature` has a length not equal to `SignatureSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`privateKey` has a length not equal to `PrivateKeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Unable to compute signature.

### Verify

Determines if a signature is valid for a message and public key. It returns `true` if the signature is valid and `false` otherwise.

```csharp
Ed25519.Verify(ReadOnlySpan<byte> signature, ReadOnlySpan<byte> message, ReadOnlySpan<byte> publicKey)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`signature` has a length not equal to `SignatureSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`publicKey` has a length not equal to `PublicKeySize`.

### IncrementalEd25519ph

Provides support for computing/verifying a signature from a sequence of messages using [Ed25519ph](https://www.rfc-editor.org/rfc/rfc8032).

`IncrementalEd25519ph.Finalize()` fills a span with the signature for a chunked message signed using a private key.

`IncrementalEd25519ph.FinalizeAndVerify()` determines if a signature is valid for a chunked message and public key. It returns `true` if the signature is valid and `false` otherwise.

```csharp
using var ed25519ph = new IncrementalEd25519ph();
ed25519ph.Update(ReadOnlySpan<byte> message1);
ed25519ph.Update(ReadOnlySpan<byte> message2);
// Sign
ed25519ph.Finalize(Span<byte> signature, ReadOnlySpan<byte> privateKey);
// Or verify
bool valid = ed25519ph.FinalizeAndVerify(ReadOnlySpan<byte> signature, ReadOnlySpan<byte> publicKey)

// Avoid another using statement
ed25519ph.Reinitialize();
ed25519ph.Update(ReadOnlySpan<byte> message3);
ed25519ph.Finalize(Span<byte> signature, ReadOnlySpan<byte> privateKey);
```

{% hint style="warning" %}
Do **NOT** use this class with multiple threads. If you absolutely have to, you need to use [lock statements](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/lock) to avoid exceptions being thrown.
{% endhint %}

{% hint style="warning" %}
This should **only** be used when the message is too large to fit into memory because prehashing is [theoretically weaker](https://cryptologie.net/article/497/eddsa-ed25519-ed25519-ietf-ed25519ph-ed25519ctx-hasheddsa-pureeddsa-wtf/) than regular signing.
{% endhint %}

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`signature` has a length not equal to `SignatureSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`privateKey` has a length not equal to `PrivateKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`publicKey` has a length not equal to `PublicKeySize`.

[OutOfMemoryException](https://learn.microsoft.com/en-us/dotnet/api/system.outofmemoryexception)

Allocating memory for the state failed.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

The signature could not be computed.

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Cannot update after finalizing or finalize twice (without reinitializing).

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Methods cannot be called from multiple threads simultaneously.

[ObjectDisposedException](https://learn.microsoft.com/en-us/dotnet/api/system.objectdisposedexception)

The object has been disposed.

## Constants

These are used for validation and/or save you defining your own constants.

```csharp
public const int PublicKeySize = 32;
public const int PrivateKeySize = 64;
public const int SignatureSize = 64;
public const int SeedSize = 32;
```

## Notes

{% hint style="info" %}
If you want to use [BLAKE2b](/hashing#incrementalblake2b) for prehashing instead of Ed25519ph, which uses SHA-512 internally, you can hash a domain separation constant (e.g., the protocol name) concatenated with the message and sign the 512-bit hash.
{% endhint %}

{% hint style="danger" %}
If you want to support prehashing as well as non-prehashed Ed25519 like in [Kryptor](https://www.kryptor.co.uk/)/[Minisign](https://jedisct1.github.io/minisign/), you **MUST** sign some data indicating whether prehashing was used or not. Otherwise, it may be [possible](https://github.com/jedisct1/minisign/issues/104) to create a forgery.
{% endhint %}

{% hint style="warning" %}
Ed25519 is vulnerable to [fault attacks](https://eprint.iacr.org/2017/1014.pdf). Techniques like causing [voltage glitches](https://cybermashup.files.wordpress.com/2017/10/practical-fault-attack-against-eddsa_fdtc-2017.pdf) on a chip (e.g., on an Arduino) can be used to recover the secret key and create valid signatures.

This should generally not concern you as it's mostly relevant for embedded devices and requires physical or remote access to the device. Furthermore, most countermeasures are ineffective. Prehashing or [hedged signatures](https://soatok.blog/2020/05/03/hedged-signatures-with-libsodium-using-dhole/) can help but will not prevent all attacks.
{% endhint %}


# Advanced


# Validation

## Purpose

The validation methods that Geralt uses can be called to avoid ugly if statement validation at the top of functions. This is handy for [custom constructions](https://github.com/samuel-lucas6/kcChaCha20-Poly1305).

## Usage

### EqualTo

Checks that an integer is equal to a required value.

```csharp
Validation.EqualTo<T>(string paramName, T value, T required) where T : IBinaryInteger<T>
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`value` must be equal to `required`.

### Between

Checks that an integer is between a minimum and maximum value (exclusive).

```csharp
Validation.Between<T>(string paramName, T value, T min, T max) where T : IBinaryInteger<T>
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`value` must be between `min` and `max` (exclusive).

### BetweenOrEqualTo

Checks that an integer is between a minimum and maximum value (inclusive).

```csharp
Validation.BetweenOrEqualTo<T>(string paramName, T value, T min, T max) where T : IBinaryInteger<T>)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`value` must be between `min` and `max` (inclusive).

### GreaterThan

Checks that an integer is greater than a minimum value.

```csharp
Validation.GreaterThan<T>(string paramName, T value, T min) where T : IBinaryInteger<T>
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`value` must be greater than `min`.

### GreaterThanOrEqualTo

Checks that an integer is greater than or equal to a minimum value.

```csharp
Validation.GreaterThanOrEqualTo<T>(string paramName, T value, T min) where T : IBinaryInteger<T>
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`value` must be greater than or equal to `min`.

### LessThan

Checks that an integer is less than a maximum value.

```csharp
Validation.LessThan<T>(string paramName, T value, T max) where T : IBinaryInteger<T>
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`value` must be less than `max`.

### LessThanOrEqualTo

Checks that an integer is less than or equal to a maximum value.

```csharp
Validation.LessThanOrEqualTo<T>(string paramName, T value, T max) where T : IBinaryInteger<T>
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`value` must be less than or equal to `max`.

### MultipleOf

Checks that an integer is a multiple of another integer.

```csharp
Validation.MultipleOf<T>(string paramName, T value, T multipleOf) where T : IBinaryInteger<T>
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`value` must be a multiple of `multipleOf` (and not less than or equal to 0).

### NotEmpty

Checks that an integer (e.g., the length of a span) is not equal to zero.

```csharp
Validation.NotEmpty<T>(string paramName, T size) where T : IBinaryInteger<T>
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`size` must not be equal to 0.

### NotNull

Checks that a parameter is not null.

```csharp
Validation.NotNull<T>(string paramName, T? value)
```

#### Exceptions

[ArgumentNullException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentnullexception)

`value` must not be null.

### NotNullOrEmpty

Checks that an enumerable (e.g., array) is not null or empty.

```csharp
Validation.NotNullOrEmpty<T>(string paramName, IEnumerable<T?> enumerable)
```

#### Exceptions

[ArgumentNullException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentnullexception)

`enumerable` must not be null.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`enumerable` must not be empty.

### HasNoNullValues

Checks that an enumerable (e.g., array) is not null and has no null values.

```csharp
Validation.HasNoNullValues<T>(string paramName, IEnumerable<T?> enumerable)
```

#### Exceptions

[ArgumentNullException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentnullexception)

`enumerable` must not be null.

[ArgumentException](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception)

`enumerable` must not contain any null values.

### HasNoNullOrEmptyValues

Checks that an enumerable (e.g., string array) is not null/empty and has no null/empty values.

```csharp
Validation.HasNoNullOrEmptyValues<T>(string paramName, IEnumerable<T?> enumerable)
```

#### Exceptions

[ArgumentNullException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentnullexception)

`enumerable` must not be null.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`enumerable` must not be empty.

[ArgumentException](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception)

`enumerable` must not contain any null or empty values.

### HasNoNullValues

Checks that a jagged enumerable (e.g., jagged array) is not null and has no null values.

```csharp
Validation.HasNoNullValues<T>(string paramName, IEnumerable<T[]?> jaggedEnumerable)
```

#### Exceptions

[ArgumentNullException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentnullexception)

`jaggedEnumerable` must not be null.

[ArgumentException](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception)

`jaggedEnumerable` must not contain any null collections/values.

### HasNoNullOrEmptyValues

Checks that a jagged enumerable (e.g., jagged string array) is not null/empty and has no null/empty values.

```csharp
Validation.HasNoNullOrEmptyValues<T>(string paramName, IEnumerable<T[]?> jaggedEnumerable)
```

#### Exceptions

[ArgumentNullException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentnullexception)

`jaggedEnumerable` must not be null.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`jaggedEnumerable` must not be empty.

[ArgumentException](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception)

`jaggedEnumerable` must not contain any null or empty collections/values.


# Concat

## Purpose

Sometimes it is necessary to concatenate data together to form an input or output. This is represented via the `||` symbol in pseudocode.

For example, an AEAD ciphertext is usually a ciphertext concatenated with a tag.

{% hint style="danger" %}
Concatenation is often not as simple as it sounds. You **MUST** be careful to avoid [canonicalization attacks](https://soatok.blog/2021/07/30/canonicalization-attacks-against-macs-and-signatures/).

This is important for [hashing](/hashing), [message authentication](/message-authentication) (e.g., [Encrypt-then-MAC](https://samuellucas.com/draft-lucas-generalised-committing-aead/draft-lucas-generalised-committing-aead.html#section-3)), associated data in [AEADs](/authenticated-encryption), and info in [key derivation](/key-derivation).

Two solutions are:

1. Only ever concatenate fixed-length inputs. With two inputs, only one needs to be fixed-length. With three inputs, only two need to be fixed-length.
2. For variable-length inputs, also concatenate the length of each input as a 64-bit unsigned integer ([ulong](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/integral-numeric-types)) [converted to bytes in little-endian](https://docs.microsoft.com/en-us/dotnet/api/system.buffers.binary.binaryprimitives.writeuint64littleendian?). See [this](https://github.com/samuel-lucas6/draft-lucas-generalised-committing-aead/blob/a8feb3e077a89ba6660b3cc21ca0b4f3780aeca8/reference-implementation/cAEAD/cAEAD/ChaCha20BLAKE2b.cs#L88) C# code example. This is done internally in AEADs like [ChaCha20-Poly1305](https://datatracker.ietf.org/doc/html/rfc8439#section-2.8.1).
   {% endhint %}

## Usage

### Concat

Fills a span with the concatenation of two spans.

```csharp
Spans.Concat(Span<byte> buffer, ReadOnlySpan<byte> a, ReadOnlySpan<byte> b)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`buffer` has a length not equal to `a.Length + b.Length`.

[OverflowException](https://learn.microsoft.com/en-us/dotnet/api/system.overflowexception)

`a.Length + b.Length` has resulted in an overflow.

[ArgumentException](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception)

`buffer` cannot overlap with the spans being copied.

### Concat

Fills a span with the concatenation of three spans.

```csharp
Spans.Concat(Span<byte> buffer, ReadOnlySpan<byte> a, ReadOnlySpan<byte> b, ReadOnlySpan<byte> c)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`buffer` has a length not equal to `a.Length + b.Length + c.Length`.

[OverflowException](https://learn.microsoft.com/en-us/dotnet/api/system.overflowexception)

`a.Length + b.Length + c.Length` has resulted in an overflow.

[ArgumentException](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception)

`buffer` cannot overlap with the spans being copied.

### Concat

Fills a span with the concatenation of four spans.

```csharp
Spans.Concat(Span<byte> buffer, ReadOnlySpan<byte> a, ReadOnlySpan<byte> b, ReadOnlySpan<byte> c, ReadOnlySpan<byte> d)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`buffer` has a length not equal to `a.Length + b.Length + c.Length + d.Length`.

[OverflowException](https://learn.microsoft.com/en-us/dotnet/api/system.overflowexception)

`a.Length + b.Length + c.Length + d.Length` has resulted in an overflow.

[ArgumentException](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception)

`buffer` cannot overlap with the spans being copied.

### Concat

Fills a span with the concatenation of five spans.

```csharp
Spans.Concat(Span<byte> buffer, ReadOnlySpan<byte> a, ReadOnlySpan<byte> b, ReadOnlySpan<byte> c, ReadOnlySpan<byte> d, ReadOnlySpan<byte> e)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`buffer` has a length not equal to `a.Length + b.Length + c.Length + d.Length + e.Length`.

[OverflowException](https://learn.microsoft.com/en-us/dotnet/api/system.overflowexception)

`a.Length + b.Length + c.Length + d.Length + e.Length` has resulted in an overflow.

[ArgumentException](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception)

`buffer` cannot overlap with the spans being copied.

### Concat

Fills a span with the concatenation of six spans.

```csharp
Spans.Concat(Span<byte> buffer, ReadOnlySpan<byte> a, ReadOnlySpan<byte> b, ReadOnlySpan<byte> c, ReadOnlySpan<byte> d, ReadOnlySpan<byte> e, ReadOnlySpan<byte> f)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`buffer` has a length not equal to `a.Length + b.Length + c.Length + d.Length + e.Length + f.Length`.

[OverflowException](https://learn.microsoft.com/en-us/dotnet/api/system.overflowexception)

`a.Length + b.Length + c.Length + d.Length + e.Length + f.Length` has resulted in an overflow.

[ArgumentException](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception)

`buffer` cannot overlap with the spans being copied.

## Notes

{% hint style="info" %}
There is unfortunately no `Concat()` function for arrays or spans in .NET, and there is not even a `Span<T>.CopyTo(Span<T> destination, int index)`. This class fills that gap.
{% endhint %}


# ChaCha20

## Purpose

[ChaCha20](https://datatracker.ietf.org/doc/html/rfc8439#section-2.4) is an **unauthenticated** stream cipher that uses a 256-bit key and 96-bit nonce (**number used only once**) to encrypt/decrypt a message.

The 32-bit internal counter can be changed from the default of 0 to 1 for constructing [ChaCha20-Poly1305](https://datatracker.ietf.org/doc/html/rfc8439). Otherwise, it should generally be left alone.

{% hint style="warning" %}
You probably want [ChaCha20-Poly1305](/authenticated-encryption/chacha20-poly1305) instead, which also ensures a message has not been tampered with. This class **MUST** only be used for custom constructions (e.g., [Encrypt-then-MAC](https://samuellucas.com/draft-lucas-generalised-committing-aead/draft-lucas-generalised-committing-aead.html#section-3)).
{% endhint %}

{% hint style="danger" %}
The nonce **MUST NOT** be repeated or reused with the same key. You **MUST** [increment](/constant-time#increment) the nonce for each plaintext message encrypted using the same key.
{% endhint %}

## Usage

### Fill

Fills a span with pseudorandom bytes computed from a nonce and key. This can be used to compute the Poly1305 key for constructing [ChaCha20-Poly1305](https://datatracker.ietf.org/doc/html/rfc8439).

```csharp
ChaCha20.Fill(Span<byte> buffer, ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> key)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`buffer` has a length of 0.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`nonce` has a length not equal to `NonceSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`key` has a length not equal to `KeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error computing pseudorandom bytes.

### Encrypt

Fills a span with ciphertext computed from a plaintext message, nonce, and key.

```csharp
ChaCha20.Encrypt(Span<byte> ciphertext, ReadOnlySpan<byte> plaintext, ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> key, uint counter = 0)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`ciphertext` has a length not equal to `plaintext.Length`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`nonce` has a length not equal to `NonceSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`key` has a length not equal to `KeySize`.

[OverflowException](https://learn.microsoft.com/en-us/dotnet/api/system.overflowexception)

Counter overflow prevented.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error encrypting plaintext.

### Decrypt

Fills a span with plaintext computed from a ciphertext message, nonce, and key.

```csharp
ChaCha20.Decrypt(Span<byte> plaintext, ReadOnlySpan<byte> ciphertext, ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> key, uint counter = 0)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`plaintext` has a length not equal to `ciphertext.Length`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`nonce` has a length not equal to `NonceSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`key` has a length not equal to `KeySize`.

[OverflowException](https://learn.microsoft.com/en-us/dotnet/api/system.overflowexception)

Counter overflow prevented.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error decrypting ciphertext.

## Constants

These are used for validation and/or save you defining your own constants.

```csharp
public const int KeySize = 32;
public const int NonceSize = 12;
public const int BlockSize = 64;
```

## Notes

{% hint style="danger" %}
This is **NOT** an authenticated encryption algorithm. This class **MUST** only be used if you know what you are doing and apply authentication [yourself](https://samuellucas.com/draft-lucas-generalised-committing-aead/draft-lucas-generalised-committing-aead.html#name-the-generalised-caead-schem) using keyed [BLAKE2b](/message-authentication) as a MAC.
{% endhint %}

{% hint style="danger" %}
The key **MUST** be uniformly random. It can either be [randomly generated](/random-data#fill) or the output of a [KDF](/key-derivation). Furthermore, it **SHOULD** be rotated periodically (e.g., a different key per file).
{% endhint %}

{% hint style="warning" %}
As a *general* rule, avoid compression before encryption. It can [leak information](https://iacr.org/archive/fse2002/23650264/23650264.pdf) and has been the cause of [several attacks](https://crypto.stackexchange.com/a/38055).
{% endhint %}

{% hint style="warning" %}
Even with [Encrypt-then-MAC](https://samuellucas.com/draft-lucas-generalised-committing-aead/draft-lucas-generalised-committing-aead.html#section-3), it is recommended to encrypt data in 16-64 KiB chunks instead of as a single plaintext message. Read the [ChaCha20-Poly1305 Notes](/authenticated-encryption/chacha20-poly1305#notes) for more information.
{% endhint %}


# XChaCha20

## Purpose

[XChaCha20](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha#section-2.3) is an **unauthenticated** stream cipher constructed using [ChaCha20](/advanced/chacha20) and [HChaCha20](/advanced/hchacha20). It takes a 256-bit key and 192-bit nonce (**number used only once**) to encrypt/decrypt a message.

The 64-bit internal counter can be changed from the default of 0 to access any block without computing previous ones. However, it should generally not be touched.

{% hint style="warning" %}
You probably want [XChaCha20-Poly1305](/authenticated-encryption/xchacha20-poly1305) instead, which also ensures a message has not been tampered with. This class **MUST** only be used for custom constructions (e.g., [Encrypt-then-MAC](https://samuellucas.com/draft-lucas-generalised-committing-aead/draft-lucas-generalised-committing-aead.html#section-3)).
{% endhint %}

{% hint style="danger" %}
The nonce **MUST NOT** be repeated or reused with the same key. You **MUST** [increment](/constant-time#increment) or [randomly generate](/random-data#fill) the nonce for each plaintext message encrypted using the same key.
{% endhint %}

## Usage

### Fill

Fills a span with pseudorandom bytes computed from a nonce and key. This can be used to compute the Poly1305 key for constructing [XChaCha20-Poly1305](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha).

```csharp
XChaCha20.Fill(Span<byte> buffer, ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> key)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`buffer` has a length of 0.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`nonce` has a length not equal to `NonceSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`key` has a length not equal to `KeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error computing pseudorandom bytes.

### Encrypt

Fills a span with ciphertext computed from a plaintext message, nonce, and key.

```csharp
XChaCha20.Encrypt(Span<byte> ciphertext, ReadOnlySpan<byte> plaintext, ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> key, ulong counter = 0)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`ciphertext` has a length not equal to `plaintext.Length`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`nonce` has a length not equal to `NonceSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`key` has a length not equal to `KeySize`.

[OverflowException](https://learn.microsoft.com/en-us/dotnet/api/system.overflowexception)

Counter overflow prevented.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error encrypting plaintext.

### Decrypt

Fills a span with plaintext computed from a ciphertext message, nonce, and key.

```csharp
XChaCha20.Decrypt(Span<byte> plaintext, ReadOnlySpan<byte> ciphertext, ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> key, ulong counter = 0)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`plaintext` has a length not equal to `ciphertext.Length`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`nonce` has a length not equal to `NonceSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`key` has a length not equal to `KeySize`.

[OverflowException](https://learn.microsoft.com/en-us/dotnet/api/system.overflowexception)

Counter overflow prevented.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error decrypting ciphertext.

## Constants

These are used for validation and/or save you defining your own constants.

```csharp
public const int KeySize = 32;
public const int NonceSize = 24;
public const int BlockSize = 64;
```

## Notes

{% hint style="danger" %}
This is **NOT** an authenticated encryption algorithm. This class **MUST** only be used if you know what you are doing and apply authentication [yourself](https://samuellucas.com/draft-lucas-generalised-committing-aead/draft-lucas-generalised-committing-aead.html#name-the-generalised-caead-schem) using keyed [BLAKE2b](/message-authentication) as a MAC.
{% endhint %}

{% hint style="danger" %}
The key **MUST** be uniformly random. It can either be [randomly generated](/random-data#fill) or the output of a [KDF](/key-derivation). Furthermore, it **SHOULD** be rotated periodically (e.g., a different key per file).
{% endhint %}

{% hint style="warning" %}
As a *general* rule, avoid compression before encryption. It can [leak information](https://iacr.org/archive/fse2002/23650264/23650264.pdf) and has been the cause of [several attacks](https://crypto.stackexchange.com/a/38055).
{% endhint %}

{% hint style="warning" %}
Even with [Encrypt-then-MAC](https://samuellucas.com/draft-lucas-generalised-committing-aead/draft-lucas-generalised-committing-aead.html#section-3), it is recommended to encrypt data in 16-64 KiB chunks instead of as a single plaintext message. Read the [XChaCha20-Poly1305 Notes](/authenticated-encryption/xchacha20-poly1305#notes) for more information.
{% endhint %}


# HChaCha20

## Purpose

[HChaCha20](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha#section-2.2) is a pseudorandom function (PRF) based on [ChaCha20](https://cr.yp.to/chacha/chacha-20080128.pdf) and [HSalsa20](https://cr.yp.to/snuffle/xsalsa-20110204.pdf). It takes a 512-bit input and produces a 256-bit output. This makes it suitable for fast key derivation from **high-entropy** input keying material, as done for [XChaCha20](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha#section-2.3).

{% hint style="warning" %}
You probably want [BLAKE2b](/key-derivation) instead because it is much more flexible. [Argon2id](/password-hashing) **MUST** be used for password-based key derivation.
{% endhint %}

## Usage

### DeriveKey

Fills a span with output keying material derived from **high-entropy** input keying material, a nonce, and an optional personalization constant for domain separation.

```csharp
HChaCha20.DeriveKey(Span<byte> outputKeyingMaterial, ReadOnlySpan<byte> inputKeyingMaterial, ReadOnlySpan<byte> nonce, ReadOnlySpan<byte> personalization = default)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`outputKeyingMaterial` has a length not equal to `OutputSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`inputKeyingMaterial` has a length not equal to `KeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`nonce` has a length not equal to `NonceSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`personalization` has a length greater than 0 but not equal to `PersonalizationSize`.

[ArgumentException](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception)

`personalization` cannot be all-zero and must have [asymmetry](https://link.springer.com/article/10.1007/s00145-018-9297-9).

## Constants

These are used for validation and/or save you defining your own constants.

```csharp
public const int OutputSize = 32;
public const int KeySize = 32;
public const int NonceSize = 16;
public const int PersonalizationSize = 16;
```

## Notes

{% hint style="danger" %}
HChaCha20 is **NOT** a general-purpose hash function. Use [BLAKE2b](/hashing).
{% endhint %}

{% hint style="danger" %}
The input keying material **MUST** be a uniformly random key, **NOT** a password, public key, or X25519 shared secret.
{% endhint %}

{% hint style="success" %}
'Asymmetry' for the personalization means the first and last 8 bytes must be different. This is important for [security](https://link.springer.com/article/10.1007/s00145-018-9297-9) (avoiding a fixed-point/weak states).
{% endhint %}


# Keccak-f\[1600]

## Purpose

[Keccak-f\[1600\]](https://keccak.team/keccak.html) is the largest variant of the Keccak-f permutation, which is the building block for the [SHA-3](https://csrc.nist.gov/pubs/fips/202/final) [family](https://csrc.nist.gov/pubs/sp/800/185/final) and [KangarooTwelve family](https://datatracker.ietf.org/doc/html/rfc9861) of constructions. It has a 1600-bit state and can be used to construct hash functions, XOFs, MACs, KDFs, stream ciphers, and AEAD schemes.

{% hint style="danger" %}
**This API is more dangerous than the others**. It should only be used by advanced users who want to create [custom constructions](https://eprint.iacr.org/2024/858).

If you don't meet this criteria, please use .NET's [SHA-3](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.sha3_256)/[SHAKE](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.shake128) implementations or wait for [libsodium](https://libsodium.gitbook.io/doc/roadmap) to implement [KangarooTwelve](https://keccak.team/kangarootwelve.html) (KT128/KT256), which should be favoured over all other hash functions/XOFs at the time of writing.
{% endhint %}

## Usage

### IncrementalKeccakf1600

Provides access to the Keccak-f\[1600] permutation. Setting the state to all-zero, absorbing (XORing) data into the state, permuting the state (with 24 or 12 rounds), and squeezing output from the state are supported.

The entire state can be accessed, permuting the state is a separate function, and there's no concept of finalization. This enables flexibility for different custom constructions.

{% hint style="danger" %}
You **MUST** define a secure rate/capacity size. The minimum capacity should be 256 bits for unkeyed hashing/collision resistance scenarios but can be reduced to 192 bits for keyed scenarios (if you don't care about [collision resistance](https://tosc.iacr.org/index.php/ToSC/article/view/11295)). This is because the attacker has less control in keyed modes.

Also, you **MUST** implement proper padding (e.g., [pad10\*1](https://keccak.team/keccak_bits_and_bytes.html) or [pad10\*](https://ascon.isec.tugraz.at/specification.html)) and domain separation (e.g., [XORing a constant](https://competitions.cr.yp.to/round3/norxv30.pdf) after absorbing associated data before absorbing plaintext).

It is possible to do [full-state](https://eprint.iacr.org/2025/2038) [keyed](https://eprint.iacr.org/2023/1520) absorbing/squeezing, but this is not recommended because it's [brittle](https://eprint.iacr.org/2023/1525).
{% endhint %}

```csharp
// Initialize the state to all-zero
using var keccak = new IncrementalKeccakf1600();

// IMPORTANT: Pad/domain separate the message (not shown here)
// Process the message in blocks
foreach (var messageBlock in messageBlocks) {
    // Absorb (XOR) data into the state at offset (default of 0)
    keccak.XorBytes(messageBlock, offset);
    // Permute the state (full or half rounds)
    if (fullRounds) {
        keccak.Permute24(); // Like SHA-3/SHAKE
    }
    else {
        keccak.Permute12(); // Like TurboSHAKE
    }
}

// Squeeze output from the state (once or multiple blocks)
foreach (var outputBlock in output) {
    keccak.ExtractBytes(outputBlock, offset);
    // Permute the state (full or half rounds)
    if (fullRounds) {
        keccak.Permute24(); // Like SHA-3/SHAKE
    }
    else {
        keccak.Permute12(); // Like TurboSHAKE
    }
}

// Reset the state to all-zero
keccak.Reinitialize();
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`offset` is less than 0 or greater than `StateSize - 1`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`bytes.Length + offset` is greater than `StateSize`.

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Methods cannot be called from multiple threads simultaneously.

[ObjectDisposedException](https://learn.microsoft.com/en-us/dotnet/api/system.objectdisposedexception)

The object has been disposed.

## Constants

These are used for validation and/or save you defining your own constants.

```csharp
public const int StateSize = 200;
```

## Notes

{% hint style="info" %}
Keccak's strengths are explained [here](https://keccak.team/keccak_strengths.html), and a list of third-party cryptanalysis can be found [here](https://keccak.team/third_party.html).
{% endhint %}


# Poly1305

## Purpose

[Poly1305](https://datatracker.ietf.org/doc/html/rfc8439#section-2.5) is a fast **one-time** message authentication code (MAC). It takes a 256-bit key **that can only be used once** and produces a 128-bit tag.

{% hint style="danger" %}
You almost definitely want [BLAKE2b](/message-authentication) instead. **Poly1305 is easy to misuse and less secure due to the short tag length**.
{% endhint %}

## Usage

### ComputeTag

Fills a span with a tag computed from a message and a **one-time** key.

```csharp
Poly1305.ComputeTag(Span<byte> tag, ReadOnlySpan<byte> message, ReadOnlySpan<byte> oneTimeKey)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`tag` has a length not equal to `TagSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`oneTimeKey` has a length not equal to `KeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

The tag could not be computed.

### VerifyTag

Verifies that a tag is correct in constant time for a given message and **one-time** key. It returns `true` if the tag is valid and `false` otherwise.

```csharp
Poly1305.VerifyTag(ReadOnlySpan<byte> tag, ReadOnlySpan<byte> message, ReadOnlySpan<byte> oneTimeKey)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`tag` has a length not equal to `TagSize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`oneTimeKey` has a length not equal to `KeySize`.

### IncrementalPoly1305

Provides support for computing a tag from several messages and a **one-time** key.

```csharp
using var poly1305 = new IncrementalPoly1305(ReadOnlySpan<byte> oneTimeKey);
poly1305.Update(ReadOnlySpan<byte> message1);
poly1305.Update(ReadOnlySpan<byte> message2);
// Compute
poly1305.Finalize(Span<byte> tag);
// Or verify
bool valid = poly1305.FinalizeAndVerify(ReadOnlySpan<byte> tag);

// Avoid another using statement
// WARNING: Do NOT reuse the same key
poly1305.Reinitialize(ReadOnlySpan<byte> differentOneTimeKey);
poly1305.Update(ReadOnlySpan<byte> message3);
poly1305.Finalize(Span<byte> tag);
```

{% hint style="warning" %}
Do **NOT** use this class with multiple threads. If you absolutely have to, you need to use [lock statements](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/lock) to avoid exceptions being thrown.
{% endhint %}

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`oneTimeKey` has a length not equal to `KeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`tag` has a length not equal to `TagSize`.

[OutOfMemoryException](https://learn.microsoft.com/en-us/dotnet/api/system.outofmemoryexception)

Allocating memory for the state failed.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

The tag could not be computed.

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Cannot update after finalizing or finalize twice (without reinitializing).

[InvalidOperationException](https://learn.microsoft.com/en-us/dotnet/api/system.invalidoperationexception)

Methods cannot be called from multiple threads simultaneously.

[ObjectDisposedException](https://learn.microsoft.com/en-us/dotnet/api/system.objectdisposedexception)

The object has been disposed.

## Constants

These are used for validation and/or save you defining your own constants.

```csharp
public const int KeySize = 32;
public const int TagSize = 16;
public const int BlockSize = 16;
```

## Notes

{% hint style="danger" %}
Each key **MUST** be uniformly random, unpredictable, and unique. You **MUST NOT** reuse a key or use the same key for multiple purposes (e.g., encryption and Poly1305).
{% endhint %}

{% hint style="danger" %}
Do **NOT** use Poly1305 as a hash function or key derivation function (KDF). Use [BLAKE2b](/key-derivation).
{% endhint %}

{% hint style="danger" %}
Tags **MUST** be compared in constant time to avoid leaking information, so use the `VerifyTag()` or `FinalizeAndVerify()` function.​
{% endhint %}

{% hint style="danger" %}
Tags **MUST NOT** be truncated to minimise the opportunity for forgery.
{% endhint %}

{% hint style="warning" %}
[BLAKE2b](/message-authentication) is **strongly recommended** over Poly1305 as a MAC because it has better security guarantees. Due to the 128-bit tag length, Poly1305 should only ever be used for online protocols and small messages.
{% endhint %}


# Ed25519 to X25519

## Purpose

[Ed25519](/digital-signatures) keys can be converted to [X25519](/key-exchange) keys. This should **ONLY** be done under either of the following circumstances:

* When you are forced to use the same key pair for key exchange and signing due to resource constraints (unlikely if using this library but possible on embedded devices).
* When you only have access to signing keys.

For example, you could retrieve someone's Ed25519 SSH public key from [GitHub](https://docs.github.com/en/authentication/connecting-to-github-with-ssh/adding-a-new-ssh-key-to-your-github-account) and use it as an X25519 public key for key exchange to encrypt a file for them.

{% hint style="danger" %}
**It is bad practice to reuse the same key for different purposes**. Please generate separate Ed25519 and X25519 key pairs unless your circumstances match the above.
{% endhint %}

## Usage

### ComputeX25519PublicKey

Fills a span with the X25519 public key for a given Ed25519 public key.

```csharp
Ed25519.ComputeX25519PublicKey(Span<byte> x25519PublicKey, ReadOnlySpan<byte> ed25519PublicKey)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`x25519PublicKey` has a length not equal to `X25519.PublicKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`ed25519PublicKey` has a length not equal to `Ed25519.PublicKeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error computing X25519 public key.

### ComputeX25519PrivateKey

Fills a span with the X25519 private key for a given Ed25519 private key.

```csharp
Ed25519.ComputeX25519PrivateKey(Span<byte> x25519PrivateKey, ReadOnlySpan<byte> ed25519PrivateKey)
```

#### Exceptions

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`x25519PrivateKey` has a length not equal to `X25519.PrivateKeySize`.

[ArgumentOutOfRangeException](https://docs.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception)

`ed25519PrivateKey` has a length not equal to `Ed25519.PrivateKeySize`.

[CryptographicException](https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.cryptographicexception)

Error computing X25519 private key.

## Notes

{% hint style="info" %}
There has not been much research on using the same key pair for X25519 and Ed25519. However, it should be fine for an [X25519-based KEM](https://crypto.stackexchange.com/a/42536). There is a nice summary of what a KEM is [here](https://neilmadden.blog/2021/01/22/hybrid-encryption-and-the-kem-dem-paradigm/).
{% endhint %}


# Getting libsodium to work on Windows

libsodium requires the [latest Microsoft Visual C++ Redistributable](https://learn.microsoft.com/en-US/cpp/windows/latest-supported-vc-redist?view=msvc-170) on Windows. This dependency is included in the .NET SDK. When publishing software, there are three ways to deal with this:

1. Install this as part of your application setup/as a [package manager dependency](https://docs.chocolatey.org/en-us/create/package-dependencies/).
2. Ask the user to manually install this.
3. Bundle the `vcruntime140.dll` file with your executable.

If you want your program to be portable (e.g., [self-contained](https://learn.microsoft.com/en-us/dotnet/core/deploying/?pivots=visualstudio#self-contained-deployment)), you have to take the third approach. This can be done using the following steps:

1. Download the `VisualCppRedist_AIO_x86_x64.exe` file from the [latest release](https://github.com/abbodi1406/vcredist/releases) of [this](https://github.com/abbodi1406/vcredist) GitHub repo.
2. Use [7-Zip](https://www.7-zip.org/) to extract the downloaded executable (e.g., right click, hover over 7-Zip, and click Extract to "VisualCppRedist\_AIO\_x86\_x64\\").
3. In the extracted folder, navigate to `2022`, `x86` and/or `x64` (depending on which platform your application targets), `System` or `System64`, and copy the `vcruntime140.dll` file(s) to your project folder.
4. Add the `vcruntime140.dll` file(s) as [embedded resources](https://stackoverflow.com/questions/4111160/resources-where-to-put-them-and-how-to-reference-them-in-c-sharp) in your project.
5. Write some code that extracts the relevant (x86 or x64) file to the [location of your executable](https://github.com/samuel-lucas6/Kryptor/blob/v4.1.0/src/Kryptor/Program.cs#L148) or to the [directory where the `libsodium.dll` file is located](https://github.com/samuel-lucas6/Kryptor/commit/57e72364b865ffcdf22d9050eb711b6b873b3277) when your application starts.
6. Test that libsodium in your application works/doesn't throw a `PlatformNotSupportedException` on a Windows machine that doesn't have the Visual C++ Redistributable installed (e.g., in [Windows Sandbox](https://learn.microsoft.com/en-us/windows/security/application-security/application-isolation/windows-sandbox/windows-sandbox-overview) or a [virtual machine](https://www.virtualbox.org/)).


