diff --git a/AesExtra/AesCmac.cs b/AesExtra/AesCmac.cs
index e67c3c6..63cadfe 100644
--- a/AesExtra/AesCmac.cs
+++ b/AesExtra/AesCmac.cs
@@ -13,6 +13,8 @@ namespace Dorssel.Security.Cryptography;
///
/// Computes a Cipher-based Message Authentication Code (CMAC) by using the symmetric key AES block cipher.
///
+///
+///
public sealed class AesCmac
: KeyedHashAlgorithm
{
@@ -343,6 +345,7 @@ static async ValueTask UncheckedOneShotAsync(byte[] key, Stream source, MemoryThe length is other than 16, 24, or 32 bytes (128, 192, or 256 bits).
static void ThrowIfInvalidKey(ReadOnlySpan key)
{
if (key.Length is not (16 or 24 or 32))
@@ -351,6 +354,8 @@ static void ThrowIfInvalidKey(ReadOnlySpan key)
}
}
+ /// is .
+ ///
static void ThrowIfInvalidKey(byte[] key)
{
if (key is null)
@@ -360,6 +365,7 @@ static void ThrowIfInvalidKey(byte[] key)
ThrowIfInvalidKey(key.AsSpan());
}
+ /// is .
static void ThrowIfInvalidSource(byte[] source)
{
if (source is null)
@@ -368,6 +374,8 @@ static void ThrowIfInvalidSource(byte[] source)
}
}
+ /// is .
+ /// does not support reading.
static void ThrowIfInvalidSource(Stream source)
{
if (source is null)
@@ -380,6 +388,10 @@ static void ThrowIfInvalidSource(Stream source)
}
}
+ ///
+ /// The buffer in is too small to hold the calculated CMAC.
+ /// The AES-CMAC algorithm always produces a 128-bit CMAC, or 16 bytes.
+ ///
static void ThrowIfInvalidDestination(Span destination)
{
if (destination.Length < BLOCKSIZE)
@@ -395,11 +407,14 @@ static void ThrowIfInvalidDestination(Span destination)
/// The data to CMAC.
/// The buffer to receive the CMAC value.
/// When this method returns, the total number of bytes written into .
- /// if is too small to hold the calculated CMAC, otherwise.
- /// The length is other than 16, 24, or 32 bytes (128, 192, or 256 bits).
+ ///
+ /// if was large enough to receive the calculated CMAC; otherwise, .
+ ///
+ ///
public static bool TryHashData(ReadOnlySpan key, ReadOnlySpan source, Span destination, out int bytesWritten)
{
ThrowIfInvalidKey(key);
+
if (destination.Length < BLOCKSIZE)
{
bytesWritten = 0;
@@ -418,8 +433,8 @@ public static bool TryHashData(ReadOnlySpan key, ReadOnlySpan source
/// The CMAC key.
/// The data to CMAC.
/// The CMAC of the data.
- /// or is .
- /// The length is other than 16, 24, or 32 bytes (128, 192, or 256 bits).
+ ///
+ ///
public static byte[] HashData(byte[] key, byte[] source)
{
ThrowIfInvalidKey(key);
@@ -436,7 +451,7 @@ public static byte[] HashData(byte[] key, byte[] source)
/// The CMAC key.
/// The data to CMAC.
/// The CMAC of the data.
- /// The length is other than 16, 24, or 32 bytes (128, 192, or 256 bits).
+ ///
public static byte[] HashData(ReadOnlySpan key, ReadOnlySpan source)
{
ThrowIfInvalidKey(key);
@@ -454,11 +469,8 @@ public static byte[] HashData(ReadOnlySpan key, ReadOnlySpan source)
/// The data to CMAC.
/// The buffer to receive the CMAC value.
/// The total number of bytes written to .
- ///
- /// The buffer in is too small to hold the calculated CMAC.
- /// The AES-CMAC algorithm always produces a 256-bit CMAC, or 32 bytes.
- ///
- /// The length is other than 16, 24, or 32 bytes (128, 192, or 256 bits).
+ ///
+ ///
public static int HashData(ReadOnlySpan key, ReadOnlySpan source, Span destination)
{
ThrowIfInvalidKey(key);
@@ -475,9 +487,8 @@ public static int HashData(ReadOnlySpan key, ReadOnlySpan source, Sp
/// The CMAC key.
/// The stream to CMAC.
/// The CMAC of the data.
- /// or is .
- /// does not support reading.
- /// The length is other than 16, 24, or 32 bytes (128, 192, or 256 bits).
+ ///
+ ///
public static byte[] HashData(byte[] key, Stream source)
{
ThrowIfInvalidKey(key);
@@ -494,9 +505,8 @@ public static byte[] HashData(byte[] key, Stream source)
/// The CMAC key.
/// The stream to CMAC.
/// The CMAC of the data.
- /// is .
- /// does not support reading.
- /// The length is other than 16, 24, or 32 bytes (128, 192, or 256 bits).
+ ///
+ ///
public static byte[] HashData(ReadOnlySpan key, Stream source)
{
ThrowIfInvalidKey(key);
@@ -515,16 +525,9 @@ public static byte[] HashData(ReadOnlySpan key, Stream source)
/// The stream to CMAC.
/// The buffer to receive the CMAC value.
/// The total number of bytes written to .
- /// is .
- ///
- /// The buffer in is too small to hold the calculated CMAC.
- /// The AES-CMAC algorithm always produces a 256-bit CMAC, or 32 bytes.
- ///
- /// -or-
- ///
- /// does not support reading.
- ///
- /// The length is other than 16, 24, or 32 bytes (128, 192, or 256 bits).
+ ///
+ ///
+ ///
public static int HashData(ReadOnlySpan key, Stream source, Span destination)
{
ThrowIfInvalidKey(key);
@@ -543,8 +546,8 @@ public static int HashData(ReadOnlySpan key, Stream source, Span des
/// The stream to CMAC.
/// The token to monitor for cancellation requests. The default value is .
/// The HMAC of the data.
- /// or is .
- /// does not support reading.
+ ///
+ ///
/// The cancellation token was canceled. This exception is stored into the returned task.
///
/// This method stores in the task it returns all non-usage exceptions that the method's synchronous counterpart can throw.
@@ -552,7 +555,6 @@ public static int HashData(ReadOnlySpan key, Stream source, Span des
/// Usage exceptions, such as , are still thrown synchronously.
/// For the stored exceptions, see the exceptions thrown by .
///
- /// The length is other than 16, 24, or 32 bytes (128, 192, or 256 bits).
public static async ValueTask HashDataAsync(byte[] key, Stream source, CancellationToken cancellationToken = default)
{
ThrowIfInvalidKey(key);
@@ -570,8 +572,8 @@ public static async ValueTask HashDataAsync(byte[] key, Stream source, C
/// The stream to CMAC.
/// The token to monitor for cancellation requests. The default value is .
/// The HMAC of the data.
- /// is .
- /// does not support reading.
+ ///
+ ///
/// The cancellation token was canceled. This exception is stored into the returned task.
///
/// This method stores in the task it returns all non-usage exceptions that the method's synchronous counterpart can throw.
@@ -579,7 +581,6 @@ public static async ValueTask HashDataAsync(byte[] key, Stream source, C
/// Usage exceptions, such as , are still thrown synchronously.
/// For the stored exceptions, see the exceptions thrown by .
///
- /// The length is other than 16, 24, or 32 bytes (128, 192, or 256 bits).
public static async ValueTask HashDataAsync(ReadOnlyMemory key, Stream source, CancellationToken cancellationToken = default)
{
ThrowIfInvalidKey(key.Span);
@@ -599,8 +600,9 @@ public static async ValueTask HashDataAsync(ReadOnlyMemory key, St
/// The buffer to receive the CMAC value.
/// The token to monitor for cancellation requests. The default value is .
/// The total number of bytes written to .
- /// is .
- /// does not support reading.
+ ///
+ ///
+ ///
/// The cancellation token was canceled. This exception is stored into the returned task.
///
/// This method stores in the task it returns all non-usage exceptions that the method's synchronous counterpart can throw.
@@ -608,7 +610,6 @@ public static async ValueTask HashDataAsync(ReadOnlyMemory key, St
/// Usage exceptions, such as , are still thrown synchronously.
/// For the stored exceptions, see the exceptions thrown by .
///
- /// The length is other than 16, 24, or 32 bytes (128, 192, or 256 bits).
public static async ValueTask HashDataAsync(ReadOnlyMemory key, Stream source, Memory destination,
CancellationToken cancellationToken = default)
{
diff --git a/AesExtra/AesCtr.cs b/AesExtra/AesCtr.cs
index f279279..cb96209 100644
--- a/AesExtra/AesCtr.cs
+++ b/AesExtra/AesCtr.cs
@@ -11,6 +11,7 @@ namespace Dorssel.Security.Cryptography;
///
/// Provides an implementation of the Advanced Encryption Standard (AES) symmetric algorithm in CTR mode.
///
+///
public sealed class AesCtr
: Aes
{
@@ -139,26 +140,60 @@ public override void GenerateKey()
}
#region Modern_SymmetricAlgorithm
- ///
- /// TODO
- ///
- /// TODO
- /// TODO
- /// TODO
- public byte[] TransformCtr(byte[] input, byte[] iv)
+ /// is .
+ static void ThrowIfInvalidInput(byte[] input)
{
if (input is null)
{
throw new ArgumentNullException(nameof(input));
}
+ }
+
+ /// is .
+ ///
+ static void ThrowIfInvalidIV(byte[] iv)
+ {
if (iv is null)
{
- throw new ArgumentNullException(nameof(input));
+ throw new ArgumentNullException(nameof(iv));
}
+ ThrowIfInvalidIV(iv.AsSpan());
+ }
+
+ ///
+ /// is the incorrect length.
+ /// Callers are expected to pass an initialization vector that is exactly in length,
+ /// converted to bytes (`BlockSize / 8`).
+ ///
+ static void ThrowIfInvalidIV(ReadOnlySpan iv)
+ {
if (iv.Length != BLOCKSIZE)
{
throw new ArgumentException("Specified initial counter (IV) does not match the block size for this algorithm.", nameof(iv));
}
+ }
+
+ /// The buffer in is too small to hold the transformed data.
+ static void ThrowIfInvalidDestination(Span destination, int requiredLength)
+ {
+ if (destination.Length < requiredLength)
+ {
+ throw new ArgumentException("Destination is too short.", nameof(destination));
+ }
+ }
+
+ ///
+ /// Transforms data using CTR mode.
+ ///
+ /// The data to transform.
+ /// The initialization vector (initial counter).
+ /// The transformed data.
+ ///
+ ///
+ public byte[] TransformCtr(byte[] input, byte[] iv)
+ {
+ ThrowIfInvalidInput(input);
+ ThrowIfInvalidIV(iv);
var output = new byte[input.Length];
using var transform = new AesCtrTransform(Key, iv);
@@ -167,17 +202,15 @@ public byte[] TransformCtr(byte[] input, byte[] iv)
}
///
- /// TODO
+ /// Transforms data using CTR mode.
///
- /// TODO
- /// TODO
- /// TODO
+ /// The data to transform.
+ /// The initialization vector (initial counter).
+ /// The transformed data.
+ ///
public byte[] TransformCtr(ReadOnlySpan input, ReadOnlySpan iv)
{
- if (iv.Length != BLOCKSIZE)
- {
- throw new ArgumentException("Specified initial counter (IV) does not match the block size for this algorithm.", nameof(iv));
- }
+ ThrowIfInvalidIV(iv);
var output = new byte[input.Length];
using var transform = new AesCtrTransform(Key, iv);
@@ -186,22 +219,18 @@ public byte[] TransformCtr(ReadOnlySpan input, ReadOnlySpan iv)
}
///
- /// TODO
+ /// Transforms data into the specified buffer, using CTR mode.
///
- /// TODO
- /// TODO
- /// TODO
- /// TODO
+ /// The data to transform.
+ /// The initialization vector (initial counter).
+ /// The buffer to receive the transformed data.
+ /// The total number of bytes written to .
+ ///
+ ///
public int TransformCtr(ReadOnlySpan input, ReadOnlySpan iv, Span destination)
{
- if (iv.Length != BLOCKSIZE)
- {
- throw new ArgumentException("Specified initial counter (IV) does not match the block size for this algorithm.", nameof(iv));
- }
- if (destination.Length < input.Length)
- {
- throw new ArgumentException("Destination is too short.", nameof(destination));
- }
+ ThrowIfInvalidIV(iv);
+ ThrowIfInvalidDestination(destination, input.Length);
using var transform = new AesCtrTransform(Key, iv);
transform.UncheckedTransform(input, destination);
@@ -209,19 +238,19 @@ public int TransformCtr(ReadOnlySpan input, ReadOnlySpan iv, Span
- /// TODO
+ /// Attempts to transform data into the specified buffer, using CTR mode.
///
- /// TODO
- /// TODO
- /// TODO
- /// TODO
- /// TODO
+ /// The data to transform.
+ /// The initialization vector (initial counter).
+ /// The buffer to receive the transformed data.
+ /// When this method returns, contains the total number of bytes written to .
+ ///
+ /// if was large enough to receive the transformed data; otherwise, .
+ ///
+ ///
public bool TryTransformCtr(ReadOnlySpan input, ReadOnlySpan iv, Span destination, out int bytesWritten)
{
- if (iv.Length != BLOCKSIZE)
- {
- throw new ArgumentException("Specified initial counter (IV) does not match the block size for this algorithm.", nameof(iv));
- }
+ ThrowIfInvalidIV(iv);
if (destination.Length < input.Length)
{
diff --git a/AesExtra/AesExtra.csproj b/AesExtra/AesExtra.csproj
index 749184c..374e068 100644
--- a/AesExtra/AesExtra.csproj
+++ b/AesExtra/AesExtra.csproj
@@ -7,11 +7,12 @@ SPDX-License-Identifier: MIT
- netstandard2.0;net8.0
+ netstandard2.0;net8.0
Dorssel.Security.Cryptography
Dorssel.Security.Cryptography.AesExtra
+ net8.0
preview
true