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