diff --git a/AesExtra/AesCmac.cs b/AesExtra/AesCmac.cs
index 63cadfe..6a25c13 100644
--- a/AesExtra/AesCmac.cs
+++ b/AesExtra/AesCmac.cs
@@ -6,6 +6,7 @@
 using System.Buffers;
 using System.Diagnostics.CodeAnalysis;
 using System.Runtime.CompilerServices;
+using System.Runtime.Versioning;
 using System.Security.Cryptography;
 
 namespace Dorssel.Security.Cryptography;
@@ -21,8 +22,35 @@ public sealed class AesCmac
     const int BLOCKSIZE = 16; // bytes
     const int BitsPerByte = 8;
 
+    #region CryptoConfig
+    static readonly object RegistrationLock = new();
+    static bool TriedRegisterOnce;
+
+    /// <summary>
+    /// Registers the <see cref="AesCmac"/> class with <see cref="CryptoConfig"/>, such that it can be created by name.
+    /// </summary>
+    /// <seealso cref="CryptoConfig.CreateFromName(string)"/>
+    /// <remarks>
+    /// <see cref="CryptoConfig"/> is not supported in browsers.
+    /// </remarks>
+#if !NETSTANDARD2_0
+    [UnsupportedOSPlatform("browser")]
+#endif
+    public static void RegisterWithCryptoConfig()
+    {
+        lock (RegistrationLock)
+        {
+            if (!TriedRegisterOnce)
+            {
+                TriedRegisterOnce = true;
+                CryptoConfig.AddAlgorithm(typeof(AesCmac), nameof(AesCmac), typeof(AesCmac).FullName!);
+            }
+        }
+    }
+
     /// <inheritdoc cref="KeyedHashAlgorithm.Create()" path="/summary" />
     /// <returns>A new <see cref="AesCmac" /> instance.</returns>
+    [Obsolete("Use one of the constructors instead.")]
     public static new AesCmac Create()
     {
         return new AesCmac();
@@ -33,86 +61,196 @@ public sealed class AesCmac
 #if !NETSTANDARD2_0
     [RequiresUnreferencedCode("The default algorithm implementations might be removed, use strong type references like 'RSA.Create()' instead.")]
 #endif
-    public static new KeyedHashAlgorithm? Create(string algorithmName)
+    public static new AesCmac? Create(string algorithmName)
+    {
+        if (algorithmName is null)
+        {
+            throw new ArgumentNullException(nameof(algorithmName));
+        }
+        // Our class is sealed, so there definitely is no other implementation.
+        return algorithmName == nameof(AesCmac) || algorithmName == typeof(AesCmac).FullName! ? new AesCmac() : null;
+    }
+    #endregion
+
+    /// <exception cref="CryptographicException"><paramref name="keySize"/> is other than 128, 192, or 256 bits.</exception>
+    static void ThrowIfInvalidKeySize(int keySize)
+    {
+        if (keySize is not (128 or 192 or 256))
+        {
+            throw new CryptographicException("Specified key size is valid for this algorithm.");
+        }
+    }
+
+    /// <exception cref="CryptographicException">The <paramref name="key"/> length is other than 16, 24, or 32 bytes (128, 192, or 256 bits).</exception>
+    static void ThrowIfInvalidKey(ReadOnlySpan<byte> key)
+    {
+        if (key.Length is not (16 or 24 or 32))
+        {
+            throw new CryptographicException("Specified key is not a valid size for this algorithm.");
+        }
+    }
+
+    /// <exception cref="ArgumentNullException"><paramref name="key"/> is <see langword="null"/>.</exception>
+    /// <inheritdoc cref="ThrowIfInvalidKey(ReadOnlySpan{byte})"/>
+    static void ThrowIfInvalidKey(byte[] key)
+    {
+        if (key is null)
+        {
+            throw new ArgumentNullException(nameof(key));
+        }
+        ThrowIfInvalidKey(key.AsSpan());
+    }
+
+    void InitializeFixedValues()
+    {
+        HashSizeValue = BLOCKSIZE * 8;
+    }
+
+    /// <summary>
+    /// Initializes a new instance of the <see cref="AesCmac" /> class with a randomly generated 256-bit key.
+    /// </summary>
+    public AesCmac()
+        : this(256)
     {
-        return algorithmName == nameof(AesCmac) ? Create() : KeyedHashAlgorithm.Create(algorithmName);
     }
 
     /// <summary>
     /// Initializes a new instance of the <see cref="AesCmac" /> class with a randomly generated key.
     /// </summary>
     /// <param name="keySize">The size, in bits, of the randomly generated key.</param>
-    public AesCmac(int keySize = 256)
+    /// <inheritdoc cref="ThrowIfInvalidKeySize(int)"/>
+    public AesCmac(int keySize)
     {
-        AesEcb = Aes.Create();
-        AesEcb.Mode = CipherMode.ECB;  // DevSkim: ignore DS187371
-        AesEcb.BlockSize = BLOCKSIZE * BitsPerByte;
-        AesEcb.KeySize = keySize;
-        HashSizeValue = BLOCKSIZE * 8;
+        ThrowIfInvalidKeySize(keySize);
+
+        InitializeFixedValues();
+
+        KeyValue = new byte[keySize / BitsPerByte];
+        using var rng = RandomNumberGenerator.Create();
+        rng.GetBytes(KeyValue);
     }
 
     /// <summary>
     /// Initializes a new instance of the <see cref="AesCmac" /> class with the specified key data.
     /// </summary>
     /// <param name="key">The secret key for AES-CMAC algorithm.</param>
+    /// <exception cref="ArgumentNullException"><paramref name="key"/> is <see langword="null"/>.</exception>
+    /// <inheritdoc cref="AesCmac(ReadOnlySpan{byte})" path="/exception"/>
     public AesCmac(byte[] key)
-        : this()
+        : this(new ReadOnlySpan<byte>(key ?? throw new ArgumentNullException(nameof(key))))
     {
-        Key = key;
+    }
+
+    /// <summary>
+    /// Initializes a new instance of the <see cref="AesCmac" /> class with the specified key data.
+    /// </summary>
+    /// <param name="key">The secret key for AES-CMAC algorithm.</param>
+    /// <inheritdoc cref="ThrowIfInvalidKey(ReadOnlySpan{byte})"/>
+    public AesCmac(ReadOnlySpan<byte> key)
+    {
+        ThrowIfInvalidKey(key);
+
+        InitializeFixedValues();
+
+        KeyValue = key.ToArray();
     }
 
     #region IDisposable
+    bool IsDisposed;
+
     /// <inheritdoc cref="KeyedHashAlgorithm.Dispose(bool)" />
     protected override void Dispose(bool disposing)
     {
+        if (IsDisposed)
+        {
+            return;
+        }
+
         if (disposing)
         {
-            CryptographicOperations.ZeroMemory(KeyValue);
-            CryptographicOperations.ZeroMemory(K1Value);
-            CryptographicOperations.ZeroMemory(K2Value);
-            CryptographicOperations.ZeroMemory(C);
-            CryptographicOperations.ZeroMemory(Partial);
-            AesEcb.Dispose();
-            CryptoTransformValue?.Dispose();
-            CryptoTransformValue = null;
-            K1Value = null;
-            K2Value = null;
-            PartialLength = 0;
-            State = 0;
+            AesEcbTransformValue?.Dispose();
         }
+
+        CryptographicOperations.ZeroMemory(KeyValue);
+        CryptographicOperations.ZeroMemory(K1Value);
+        CryptographicOperations.ZeroMemory(K2Value);
+        CryptographicOperations.ZeroMemory(C);
+        CryptographicOperations.ZeroMemory(Partial);
+        K1Value = null;
+        K2Value = null;
+        PartialLength = 0;
+        State = 0;
+        AesEcbTransformValue = null;
+        IsDisposed = true;
+
         base.Dispose(disposing);
     }
     #endregion
 
+    /// <exception cref="ObjectDisposedException">The <see cref="AesCmac"/> instance has been disposed.</exception>
+    void ThrowIfDisposed()
+    {
+        if (IsDisposed)
+        {
+            throw new ObjectDisposedException(nameof(AesCmac));
+        }
+    }
+
     /// <inheritdoc />
+    /// <inheritdoc cref="ThrowIfInvalidKey(byte[])"/>
+    /// <inheritdoc cref="ThrowIfDisposed"/>
+    /// <exception cref="InvalidOperationException">An attempt was made to change the <see cref="Key"/> during a computation.</exception>
     public override byte[] Key
     {
-        get => AesEcb.Key;
+        get
+        {
+            ThrowIfDisposed();
+
+            return (byte[])KeyValue.Clone();
+        }
         set
         {
+            // Input validation
+
+            ThrowIfInvalidKey(value);
+
+            // State validation
+
+            ThrowIfDisposed();
+
             if (State != 0)
             {
                 throw new InvalidOperationException("Key cannot be changed during a computation.");
             }
-            AesEcb.Key = value;
+
+            // Side effects
+
+            CryptographicOperations.ZeroMemory(KeyValue);
             CryptographicOperations.ZeroMemory(K1Value);
             CryptographicOperations.ZeroMemory(K2Value);
-            CryptoTransformValue?.Dispose();
-            CryptoTransformValue = null;
+            AesEcbTransformValue?.Dispose();
+            AesEcbTransformValue = null;
             K1Value = null;
             K2Value = null;
+
+            KeyValue = (byte[])value.Clone();
         }
     }
 
-    readonly Aes AesEcb;
-    ICryptoTransform? CryptoTransformValue;
+    ICryptoTransform? AesEcbTransformValue;
 
-    ICryptoTransform CryptoTransform
+    ICryptoTransform AesEcbTransform
     {
         get
         {
-            CryptoTransformValue ??= AesEcb.CreateEncryptor();
-            return CryptoTransformValue;
+            if (AesEcbTransformValue is null)
+            {
+                using var aes = Aes.Create();
+                aes.Mode = CipherMode.ECB;  // DevSkim: ignore DS187371
+                aes.BlockSize = BLOCKSIZE * BitsPerByte;
+                AesEcbTransformValue = aes.CreateEncryptor(KeyValue, null);
+            }
+            return AesEcbTransformValue;
         }
     }
 
@@ -161,12 +299,15 @@ byte[] K2
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
     void CIPH_K_InPlace(byte[] X)
     {
-        _ = CryptoTransform.TransformBlock(X, 0, BLOCKSIZE, X, 0);
+        _ = AesEcbTransform.TransformBlock(X, 0, BLOCKSIZE, X, 0);
     }
 
     /// <inheritdoc cref="HashAlgorithm.Initialize" />
+    /// <inheritdoc cref="ThrowIfDisposed"/>
     public override void Initialize()
     {
+        ThrowIfDisposed();
+
         // See: NIST SP 800-38B, Section 6.2, Step 5
         C.AsSpan().Clear();
         PartialLength = 0;
@@ -187,6 +328,9 @@ void AddBlock(ReadOnlySpan<byte> block)
     /// <inheritdoc />
     protected override void HashCore(byte[] array, int ibStart, int cbSize)
     {
+        // This is only called by HashAlgorithm, which is known to behave well.
+        // We skip input validation for performance reasons; there is no unsafe code.
+
         UncheckedHashCore(array.AsSpan(ibStart, cbSize));
     }
 
@@ -196,6 +340,9 @@ protected override
 #endif
     void HashCore(ReadOnlySpan<byte> source)
     {
+        // This is only called by HashAlgorithm, which is known to behave well.
+        // We skip input validation for performance reasons; there is no unsafe code.
+
         UncheckedHashCore(source);
     }
 
@@ -258,6 +405,9 @@ protected override
 #endif
     bool TryHashFinal(Span<byte> destination, out int bytesWritten)
     {
+        // This is only called by HashAlgorithm, which promises to never call us with a destination that is too short.
+        // We skip input validation for performance reasons; there is no unsafe code.
+
         UncheckedHashFinal(destination);
         bytesWritten = BLOCKSIZE;
         return true;
@@ -345,26 +495,6 @@ static async ValueTask UncheckedOneShotAsync(byte[] key, Stream source, Memory<b
         }
     }
 
-    /// <exception cref="CryptographicException">The <paramref name="key"/> length is other than 16, 24, or 32 bytes (128, 192, or 256 bits).</exception>
-    static void ThrowIfInvalidKey(ReadOnlySpan<byte> key)
-    {
-        if (key.Length is not (16 or 24 or 32))
-        {
-            throw new CryptographicException("Specified key is not a valid size for this algorithm.", nameof(key));
-        }
-    }
-
-    /// <exception cref="ArgumentNullException"><paramref name="key"/> is <see langword="null"/>.</exception>
-    /// <inheritdoc cref="ThrowIfInvalidKey(ReadOnlySpan{byte})"/>
-    static void ThrowIfInvalidKey(byte[] key)
-    {
-        if (key is null)
-        {
-            throw new ArgumentNullException(nameof(key));
-        }
-        ThrowIfInvalidKey(key.AsSpan());
-    }
-
     /// <exception cref="ArgumentNullException"><paramref name="source"/> is <see langword="null"/>.</exception>
     static void ThrowIfInvalidSource(byte[] source)
     {
diff --git a/AesExtra/AesCtr.cs b/AesExtra/AesCtr.cs
index cb96209..87c66e2 100644
--- a/AesExtra/AesCtr.cs
+++ b/AesExtra/AesCtr.cs
@@ -2,8 +2,8 @@
 //
 // SPDX-License-Identifier: MIT
 
-using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
+using System.Runtime.Versioning;
 using System.Security.Cryptography;
 
 namespace Dorssel.Security.Cryptography;
@@ -21,7 +21,34 @@ public sealed class AesCtr
     const PaddingMode FixedPaddingValue = PaddingMode.None;
     const int FixedFeedbackSizeValue = BLOCKSIZE * BitsPerByte;
 
+    #region CryptoConfig
+    static readonly object RegistrationLock = new();
+    static bool TriedRegisterOnce;
+
+    /// <summary>
+    /// Registers the <see cref="AesCtr"/> class with <see cref="CryptoConfig"/>, such that it can be created by name.
+    /// </summary>
+    /// <seealso cref="CryptoConfig.CreateFromName(string)"/>
+    /// <remarks>
+    /// <see cref="CryptoConfig"/> is not supported in browsers.
+    /// </remarks>
+#if !NETSTANDARD2_0
+    [UnsupportedOSPlatform("browser")]
+#endif
+    public static void RegisterWithCryptoConfig()
+    {
+        lock (RegistrationLock)
+        {
+            if (!TriedRegisterOnce)
+            {
+                TriedRegisterOnce = true;
+                CryptoConfig.AddAlgorithm(typeof(AesCtr), nameof(AesCtr), typeof(AesCtr).FullName!);
+            }
+        }
+    }
+
     /// <inheritdoc cref="Aes.Create()" />
+    [Obsolete("Use one of the constructors instead.")]
     public static new AesCtr Create()
     {
         return new AesCtr();
@@ -32,14 +59,72 @@ public sealed class AesCtr
 #if !NETSTANDARD2_0
     [RequiresUnreferencedCode("The default algorithm implementations might be removed, use strong type references like 'RSA.Create()' instead.")]
 #endif
-    public static new Aes? Create(string algorithmName)
+    public static new AesCtr? Create(string algorithmName)
+    {
+        if (algorithmName is null)
+        {
+            throw new ArgumentNullException(nameof(algorithmName));
+        }
+        // Our class is sealed, so there definitely is no other implementation.
+        return algorithmName == nameof(AesCtr) || algorithmName == typeof(AesCtr).FullName! ? new AesCtr() : null;
+    }
+    #endregion
+
+    /// <exception cref="CryptographicException"><paramref name="keySize"/> is other than 128, 192, or 256 bits.</exception>
+    static void ThrowIfInvalidKeySize(int keySize)
     {
-        return algorithmName == nameof(AesCtr) ? Create() : Aes.Create(algorithmName);
+        if (keySize is not (128 or 192 or 256))
+        {
+            throw new CryptographicException("Specified key size is valid for this algorithm.");
+        }
     }
 
-    AesCtr()
+    /// <exception cref="CryptographicException">The <paramref name="key"/> length is other than 16, 24, or 32 bytes (128, 192, or 256 bits).</exception>
+    static void ThrowIfInvalidKey(ReadOnlySpan<byte> key)
+    {
+        if (key.Length is not (16 or 24 or 32))
+        {
+            throw new CryptographicException("Specified key is not a valid size for this algorithm.");
+        }
+    }
+
+    /// <exception cref="ArgumentNullException"><paramref name="key"/> is <see langword="null"/>.</exception>
+    /// <inheritdoc cref="ThrowIfInvalidKey(ReadOnlySpan{byte})"/>
+    static void ThrowIfInvalidKey(byte[] key, string argumentName = "key")
+    {
+        if (key is null)
+        {
+            throw new ArgumentNullException(argumentName);
+        }
+        ThrowIfInvalidKey(key.AsSpan());
+    }
+
+    /// <exception cref="ArgumentNullException"><paramref name="iv"/> is <see langword="null"/>.</exception>
+    /// <inheritdoc cref="ThrowIfInvalidIV(ReadOnlySpan{byte},string)"/>
+    static void ThrowIfInvalidIV(byte[] iv, string argumentName = "iv")
+    {
+        if (iv is null)
+        {
+            throw new ArgumentNullException(argumentName);
+        }
+        ThrowIfInvalidIV(iv.AsSpan());
+    }
+
+    /// <exception cref="ArgumentException">
+    /// <paramref name="iv"/> is the incorrect length.
+    /// Callers are expected to pass an initialization vector that is exactly <see cref="SymmetricAlgorithm.BlockSize"/> in length,
+    /// converted to bytes (`BlockSize / 8`).
+    /// </exception>
+    static void ThrowIfInvalidIV(ReadOnlySpan<byte> iv, string argumentName = "iv")
+    {
+        if (iv.Length != BLOCKSIZE)
+        {
+            throw new ArgumentException("Specified initial counter (IV) does not match the block size for this algorithm.", argumentName);
+        }
+    }
+
+    void InitializeFixedValues()
     {
-        KeySizeValue = 256;
         ModeValue = FixedModeValue;
         PaddingValue = FixedPaddingValue;
         FeedbackSizeValue = FixedFeedbackSizeValue;
@@ -48,18 +133,211 @@ public sealed class AesCtr
         LegalKeySizesValue = [new(128, 256, 64)];
     }
 
-    #region IDisposable
-    /// <inheritdoc cref="SymmetricAlgorithm.Dispose(bool)" />
-    protected override void Dispose(bool disposing)
+    /// <summary>
+    /// Initializes a new instance of the <see cref="AesCtr" /> class with a randomly generated 256-bit key and an initial counter of zero.
+    /// </summary>
+    public AesCtr()
+        : this(256)
+    {
+    }
+
+    /// <summary>
+    /// Initializes a new instance of the <see cref="AesCtr" /> class with a randomly generated key and an initial counter of zero.
+    /// </summary>
+    /// <param name="keySize">The size, in bits, of the randomly generated key.</param>
+    /// <inheritdoc cref="ThrowIfInvalidKeySize(int)"/>
+    public AesCtr(int keySize)
+    {
+        ThrowIfInvalidKeySize(keySize);
+
+        InitializeFixedValues();
+
+        KeySizeValue = keySize;
+        IVValue = new byte[BLOCKSIZE];
+    }
+
+    /// <summary>
+    /// Initializes a new instance of the <see cref="AesCtr" /> class with the specified key data and a randomly generated initial counter.
+    /// </summary>
+    /// <param name="key">The secret key for the AES-CTR algorithm.</param>
+    /// <exception cref="ArgumentNullException"><paramref name="key"/> is <see langword="null"/>.</exception>
+    /// <inheritdoc cref="AesCtr(ReadOnlySpan{byte})" path="/exception"/>
+    public AesCtr(byte[] key)
+        : this(new ReadOnlySpan<byte>(key ?? throw new ArgumentNullException(nameof(key))))
+    {
+    }
+
+    /// <summary>
+    /// Initializes a new instance of the <see cref="AesCtr" /> class with the specified key data and a randomly generated initial counter.
+    /// </summary>
+    /// <param name="key">The secret key for the AES-CTR algorithm.</param>
+    public AesCtr(ReadOnlySpan<byte> key)
+    {
+        ThrowIfInvalidKey(key);
+
+        InitializeFixedValues();
+
+        KeyValue = key.ToArray();
+        KeySizeValue = key.Length * BitsPerByte;
+    }
+
+    /// <summary>
+    /// Initializes a new instance of the <see cref="AesCtr" /> class with the specified key data and initial counter.
+    /// </summary>
+    /// <param name="key">The secret key for the AES-CTR algorithm.</param>
+    /// <param name="iv">The initialization vector (initial counter).</param>
+    /// <exception cref="ArgumentNullException"><paramref name="key"/> is <see langword="null"/>.</exception>
+    /// <exception cref="ArgumentNullException"><paramref name="iv"/> is <see langword="null"/>.</exception>
+    /// <inheritdoc cref="AesCtr(ReadOnlySpan{byte}, ReadOnlySpan{byte})" path="/exception"/>
+    public AesCtr(byte[] key, byte[] iv) :
+        this(new ReadOnlySpan<byte>(key ?? throw new ArgumentNullException(nameof(key))),
+            new ReadOnlySpan<byte>(iv ?? throw new ArgumentNullException(nameof(iv))))
+    {
+    }
+
+    /// <summary>
+    /// Initializes a new instance of the <see cref="AesCtr" /> class with the specified key data and initial counter.
+    /// </summary>
+    /// <param name="key">The secret key for the AES-CTR algorithm.</param>
+    /// <param name="iv">The initialization vector (initial counter).</param>
+    /// <inheritdoc cref="ThrowIfInvalidKey(ReadOnlySpan{byte})"/>
+    /// <inheritdoc cref="ThrowIfInvalidIV(ReadOnlySpan{byte},string)"/>
+    public AesCtr(ReadOnlySpan<byte> key, ReadOnlySpan<byte> iv)
+    {
+        ThrowIfInvalidKey(key);
+        ThrowIfInvalidIV(iv);
+
+        InitializeFixedValues();
+
+        KeyValue = key.ToArray();
+        KeySizeValue = key.Length * BitsPerByte;
+        IVValue = iv.ToArray();
+    }
+
+    void PurgeKeyValue()
     {
         CryptographicOperations.ZeroMemory(KeyValue);
         KeyValue = null;
+    }
+
+    void PurgeIVValue()
+    {
         CryptographicOperations.ZeroMemory(IVValue);
         IVValue = null;
+    }
+
+    #region IDisposable
+    bool IsDisposed;
+
+    /// <inheritdoc cref="SymmetricAlgorithm.Dispose(bool)" />
+    protected override void Dispose(bool disposing)
+    {
+        if (IsDisposed)
+        {
+            return;
+        }
+
+        PurgeKeyValue();
+        PurgeIVValue();
+        IsDisposed = true;
+
         base.Dispose(disposing);
     }
     #endregion
 
+    /// <exception cref="ObjectDisposedException">The <see cref="AesCtr"/> instance has been disposed.</exception>
+    void ThrowIfDisposed()
+    {
+        if (IsDisposed)
+        {
+            throw new ObjectDisposedException(nameof(AesCtr));
+        }
+    }
+
+    /// <inheritdoc path="/summary"/>
+    /// <inheritdoc path="/returns"/>
+    /// <remarks>
+    /// Setting this property always resets the key to a new random value, even if the key size
+    /// is set to current value.
+    /// </remarks>
+    /// <inheritdoc cref="ThrowIfInvalidKeySize"/>
+    /// <inheritdoc cref="ThrowIfDisposed"/>
+    public override int KeySize
+    {
+        get
+        {
+            ThrowIfDisposed();
+
+            return KeySizeValue;
+        }
+
+        set
+        {
+            ThrowIfInvalidKeySize(value);
+
+            ThrowIfDisposed();
+
+            PurgeKeyValue();
+            KeySizeValue = value;
+        }
+    }
+
+    /// <inheritdoc path="/summary"/>
+    /// <inheritdoc path="/returns"/>
+    /// <inheritdoc cref="ThrowIfInvalidKey(byte[], string)"/>
+    /// <inheritdoc cref="ThrowIfDisposed"/>
+    public override byte[] Key
+    {
+        get
+        {
+            ThrowIfDisposed();
+
+            UncheckedGenerateKeyValueIfNull();
+            return (byte[])KeyValue!.Clone();
+        }
+
+        set
+        {
+            ThrowIfInvalidKey(value, nameof(Key));
+
+            ThrowIfDisposed();
+
+            PurgeKeyValue();
+            KeySizeValue = value.Length * 8;
+
+            KeyValue = (byte[])value.Clone();
+        }
+    }
+
+    /// <inheritdoc path="/summary"/>
+    /// <inheritdoc path="/returns"/>
+    /// <remarks>
+    /// For AES-CTR, the initialization vector (IV) is the initial counter.
+    /// </remarks>
+    /// <inheritdoc cref="ThrowIfInvalidIV(byte[], string)"/>
+    /// <inheritdoc cref="ThrowIfDisposed"/>
+    public override byte[] IV
+    {
+        get
+        {
+            ThrowIfDisposed();
+
+            UncheckedGenerateIVValueIfNull();
+            return (byte[])IVValue!.Clone();
+        }
+
+        set
+        {
+            ThrowIfInvalidIV(value, nameof(IV));
+
+            ThrowIfDisposed();
+
+            PurgeIVValue();
+
+            IVValue = (byte[])value.Clone();
+        }
+    }
+
     /// <inheritdoc cref="AesManaged.Mode" />
     /// <remarks><see cref="AesCtr"/> always pretends to use <see cref="CipherMode.CTS" />.</remarks>
     public override CipherMode Mode
@@ -102,41 +380,106 @@ public override int FeedbackSize
         }
     }
 
-    // CTR.Encrypt === CTR.Decrypt; the transform is entirely symmetric.
-    static AesCtrTransform CreateTransform(byte[] rgbKey, byte[]? rgbIV)
+    /// <inheritdoc/>
+    /// <inheritdoc cref="ThrowIfDisposed"/>
+    public override ICryptoTransform CreateDecryptor()
     {
-        return rgbIV is not null ? new(rgbKey, rgbIV)
-            : throw new CryptographicException("The cipher mode specified requires that an initialization vector(IV) be used.");
+        ThrowIfDisposed();
+
+        UncheckedGenerateKeyValueIfNull();
+        UncheckedGenerateIVValueIfNull();
+        return new AesCtrTransform(KeyValue!, IVValue!);
     }
 
-    /// <inheritdoc cref="AesManaged.CreateDecryptor(byte[], byte[])" />
+    /// <summary>
+    /// Creates a symmetric decryptor object with the specified key and initial counter (IV).
+    /// </summary>
+    /// <param name="rgbKey">The secret key to use for the symmetric algorithm.</param>
+    /// <param name="rgbIV">The initialization vector (initial counter).</param>
+    /// <returns>A symmetric decryptor object.</returns>
+    /// <inheritdoc cref="ThrowIfDisposed"/>
+    /// <inheritdoc cref="ThrowIfInvalidKey(byte[],string)"/>
+    /// <inheritdoc cref="ThrowIfInvalidIV(ReadOnlySpan{byte},string)"/>
     public override ICryptoTransform CreateDecryptor(byte[] rgbKey, byte[]? rgbIV)
     {
-        return CreateTransform(rgbKey, rgbIV);
+        ThrowIfDisposed();
+
+        ThrowIfInvalidKey(rgbKey, nameof(rgbKey));
+        ThrowIfInvalidIV(rgbIV ?? throw new CryptographicException("The cipher mode specified requires that an initialization vector (IV) be used."),
+            nameof(rgbIV));
+
+        return new AesCtrTransform(rgbKey, rgbIV);
     }
 
-    /// <inheritdoc cref="AesManaged.CreateEncryptor(byte[], byte[])" />
+    /// <inheritdoc/>
+    /// <inheritdoc cref="ThrowIfDisposed"/>
+    public override ICryptoTransform CreateEncryptor()
+    {
+        ThrowIfDisposed();
+
+        UncheckedGenerateKeyValueIfNull();
+        UncheckedGenerateIVValueIfNull();
+        return new AesCtrTransform(KeyValue!, IVValue!);
+    }
+
+    /// <summary>
+    /// Creates a symmetric encryptor object with the specified key and initial counter (IV).
+    /// </summary>
+    /// <param name="rgbKey">The secret key to use for the symmetric algorithm.</param>
+    /// <param name="rgbIV">The initialization vector (initial counter).</param>
+    /// <returns>A symmetric encryptor object.</returns>
+    /// <inheritdoc cref="ThrowIfDisposed"/>
+    /// <inheritdoc cref="ThrowIfInvalidKey(byte[],string)"/>
+    /// <inheritdoc cref="ThrowIfInvalidIV(ReadOnlySpan{byte},string)"/>
     public override ICryptoTransform CreateEncryptor(byte[] rgbKey, byte[]? rgbIV)
     {
-        return CreateTransform(rgbKey, rgbIV);
+        ThrowIfDisposed();
+
+        ThrowIfInvalidKey(rgbKey, nameof(rgbKey));
+        ThrowIfInvalidIV(rgbIV ?? throw new CryptographicException("The cipher mode specified requires that an initialization vector (IV) be used."),
+            nameof(rgbIV));
+
+        return new AesCtrTransform(rgbKey, rgbIV);
+    }
+
+    void UncheckedGenerateIVValueIfNull()
+    {
+        if (IVValue is null)
+        {
+            IVValue = new byte[BLOCKSIZE];
+            using var randomNumberGenerator = RandomNumberGenerator.Create();
+            randomNumberGenerator.GetBytes(IVValue);
+        }
     }
 
     /// <inheritdoc cref="AesManaged.GenerateIV" />
+    /// <inheritdoc cref="ThrowIfDisposed"/>
     public override void GenerateIV()
     {
-        CryptographicOperations.ZeroMemory(IVValue);
-        using var randomNumberGenerator = RandomNumberGenerator.Create();
-        IVValue = new byte[BLOCKSIZE];
-        randomNumberGenerator.GetBytes(IVValue);
+        ThrowIfDisposed();
+
+        PurgeIVValue();
+        UncheckedGenerateIVValueIfNull();
+    }
+
+    void UncheckedGenerateKeyValueIfNull()
+    {
+        if (KeyValue is null)
+        {
+            KeyValue = new byte[KeySizeValue / BitsPerByte];
+            using var randomNumberGenerator = RandomNumberGenerator.Create();
+            randomNumberGenerator.GetBytes(KeyValue);
+        }
     }
 
     /// <inheritdoc cref="AesManaged.GenerateKey" />
+    /// <inheritdoc cref="ThrowIfDisposed"/>
     public override void GenerateKey()
     {
-        CryptographicOperations.ZeroMemory(KeyValue);
-        using var randomNumberGenerator = RandomNumberGenerator.Create();
-        KeyValue = new byte[KeySize / BitsPerByte];
-        randomNumberGenerator.GetBytes(KeyValue);
+        ThrowIfDisposed();
+
+        PurgeKeyValue();
+        UncheckedGenerateKeyValueIfNull();
     }
 
     #region Modern_SymmetricAlgorithm
@@ -149,30 +492,6 @@ static void ThrowIfInvalidInput(byte[] input)
         }
     }
 
-    /// <exception cref="ArgumentNullException"><paramref name="iv"/> is <see langword="null"/>.</exception>
-    /// <inheritdoc cref="ThrowIfInvalidIV(ReadOnlySpan{byte})"/>
-    static void ThrowIfInvalidIV(byte[] iv)
-    {
-        if (iv is null)
-        {
-            throw new ArgumentNullException(nameof(iv));
-        }
-        ThrowIfInvalidIV(iv.AsSpan());
-    }
-
-    /// <exception cref="ArgumentException">
-    /// <paramref name="iv"/> is the incorrect length.
-    /// Callers are expected to pass an initialization vector that is exactly <see cref="SymmetricAlgorithm.BlockSize"/> in length,
-    /// converted to bytes (`BlockSize / 8`).
-    /// </exception>
-    static void ThrowIfInvalidIV(ReadOnlySpan<byte> iv)
-    {
-        if (iv.Length != BLOCKSIZE)
-        {
-            throw new ArgumentException("Specified initial counter (IV) does not match the block size for this algorithm.", nameof(iv));
-        }
-    }
-
     /// <exception cref="ArgumentException">The buffer in <paramref name="destination"/> is too small to hold the transformed data.</exception>
     static void ThrowIfInvalidDestination(Span<byte> destination, int requiredLength)
     {
@@ -189,14 +508,17 @@ static void ThrowIfInvalidDestination(Span<byte> destination, int requiredLength
     /// <param name="iv">The initialization vector (initial counter).</param>
     /// <returns>The transformed data.</returns>
     /// <inheritdoc cref="ThrowIfInvalidInput(byte[])"/>
-    /// <inheritdoc cref="ThrowIfInvalidIV(byte[])"/>
+    /// <inheritdoc cref="ThrowIfInvalidIV(byte[],string)"/>
     public byte[] TransformCtr(byte[] input, byte[] iv)
     {
         ThrowIfInvalidInput(input);
         ThrowIfInvalidIV(iv);
 
+        ThrowIfDisposed();
+
         var output = new byte[input.Length];
-        using var transform = new AesCtrTransform(Key, iv);
+        UncheckedGenerateKeyValueIfNull();
+        using var transform = new AesCtrTransform(KeyValue!, iv);
         transform.UncheckedTransform(input, output);
         return output;
     }
@@ -207,13 +529,16 @@ public byte[] TransformCtr(byte[] input, byte[] iv)
     /// <param name="input">The data to transform.</param>
     /// <param name="iv">The initialization vector (initial counter).</param>
     /// <returns>The transformed data.</returns>
-    /// <inheritdoc cref="ThrowIfInvalidIV(ReadOnlySpan{byte})"/>
+    /// <inheritdoc cref="ThrowIfInvalidIV(ReadOnlySpan{byte},string)"/>
     public byte[] TransformCtr(ReadOnlySpan<byte> input, ReadOnlySpan<byte> iv)
     {
         ThrowIfInvalidIV(iv);
 
+        ThrowIfDisposed();
+
         var output = new byte[input.Length];
-        using var transform = new AesCtrTransform(Key, iv);
+        UncheckedGenerateKeyValueIfNull();
+        using var transform = new AesCtrTransform(KeyValue!, iv);
         transform.UncheckedTransform(input, output);
         return output;
     }
@@ -225,14 +550,17 @@ public byte[] TransformCtr(ReadOnlySpan<byte> input, ReadOnlySpan<byte> iv)
     /// <param name="iv">The initialization vector (initial counter).</param>
     /// <param name="destination">The buffer to receive the transformed data.</param>
     /// <returns>The total number of bytes written to <paramref name="destination"/>.</returns>
-    /// <inheritdoc cref="ThrowIfInvalidIV(ReadOnlySpan{byte})"/>
+    /// <inheritdoc cref="ThrowIfInvalidIV(ReadOnlySpan{byte},string)"/>
     /// <inheritdoc cref="ThrowIfInvalidDestination(Span{byte}, int)"/>
     public int TransformCtr(ReadOnlySpan<byte> input, ReadOnlySpan<byte> iv, Span<byte> destination)
     {
         ThrowIfInvalidIV(iv);
         ThrowIfInvalidDestination(destination, input.Length);
 
-        using var transform = new AesCtrTransform(Key, iv);
+        ThrowIfDisposed();
+
+        UncheckedGenerateKeyValueIfNull();
+        using var transform = new AesCtrTransform(KeyValue!, iv);
         transform.UncheckedTransform(input, destination);
         return input.Length;
     }
@@ -247,18 +575,21 @@ public int TransformCtr(ReadOnlySpan<byte> input, ReadOnlySpan<byte> iv, Span<by
     /// <returns>
     /// <see langword="true"/> if <paramref name="destination"/> was large enough to receive the transformed data; otherwise, <see langword="false"/>.
     /// </returns>
-    /// <inheritdoc cref="ThrowIfInvalidIV(ReadOnlySpan{byte})"/>
+    /// <inheritdoc cref="ThrowIfInvalidIV(ReadOnlySpan{byte},string)"/>
     public bool TryTransformCtr(ReadOnlySpan<byte> input, ReadOnlySpan<byte> iv, Span<byte> destination, out int bytesWritten)
     {
         ThrowIfInvalidIV(iv);
 
+        ThrowIfDisposed();
+
         if (destination.Length < input.Length)
         {
             bytesWritten = 0;
             return false;
         }
 
-        using var transform = new AesCtrTransform(Key, iv);
+        UncheckedGenerateKeyValueIfNull();
+        using var transform = new AesCtrTransform(KeyValue!, iv);
         transform.UncheckedTransform(input, destination);
         bytesWritten = input.Length;
         return true;
diff --git a/AesExtra/AesSiv.cs b/AesExtra/AesSiv.cs
index de7d98c..e856b8f 100644
--- a/AesExtra/AesSiv.cs
+++ b/AesExtra/AesSiv.cs
@@ -17,7 +17,7 @@ public sealed class AesSiv
     /// Initializes a new instance of the <see cref="AesSiv"/> class with a provided key.
     /// </summary>
     /// <param name="key">The secret key to use for this instance.</param>
-    /// <exception cref="ArgumentNullException" />
+    /// <exception cref="ArgumentNullException"><paramref name="key"/> is <see langword="null"/>.</exception>
     public AesSiv(byte[] key)
         : this(new ReadOnlySpan<byte>(key ?? throw new ArgumentNullException(nameof(key))))
     {
@@ -31,7 +31,7 @@ public AesSiv(ReadOnlySpan<byte> key)
     {
         if (key.Length is not (32 or 48 or 64))
         {
-            throw new CryptographicException("Specified key is not a valid size for this algorithm.", nameof(key));
+            throw new CryptographicException("Specified key is not a valid size for this algorithm.");
         }
 
         using var cmacKey = new SecureByteArray(key[..(key.Length / 2)]);
@@ -64,6 +64,7 @@ public void Dispose()
     }
     #endregion
 
+    /// <exception cref="ObjectDisposedException">The <see cref="AesSiv"/> instance has been disposed.</exception>
     void ThrowIfDisposed()
     {
         if (IsDisposed)
@@ -219,6 +220,7 @@ static void ThrowIfInvalidAssociatedData(ReadOnlySpan<ReadOnlyMemory<byte>> asso
     /// <inheritdoc cref="ThrowIfInvalidCiphertext(byte[])"/>
     /// <inheritdoc cref="ThrowIfInvalidCiphertext(Span{byte}, ReadOnlySpan{byte})"/>
     /// <inheritdoc cref="ThrowIfInvalidAssociatedData(byte[][])"/>
+    /// <inheritdoc cref="ThrowIfDisposed"/>
     public void Encrypt(byte[] plaintext, byte[] ciphertext, params byte[][] associatedData)
     {
         // Input validation
@@ -258,6 +260,7 @@ public void Encrypt(byte[] plaintext, byte[] ciphertext, params byte[][] associa
     /// <param name="ciphertext">The byte array to receive the encrypted contents, prepended with the synthetic IV.</param>
     /// <param name="associatedData">Extra data associated with this message, which must also be provided during decryption.</param>
     /// <inheritdoc cref="ThrowIfInvalidCiphertext(Span{byte}, ReadOnlySpan{byte})"/>
+    /// <inheritdoc cref="ThrowIfDisposed"/>
     public void Encrypt(ReadOnlySpan<byte> plaintext, Span<byte> ciphertext, ReadOnlySpan<byte> associatedData)
     {
         // Input validation
@@ -289,6 +292,7 @@ public void Encrypt(ReadOnlySpan<byte> plaintext, Span<byte> ciphertext, ReadOnl
     /// <param name="associatedData">Extra data associated with this message, which must also be provided during decryption.</param>
     /// <inheritdoc cref="ThrowIfInvalidCiphertext(Span{byte}, ReadOnlySpan{byte})"/>
     /// <inheritdoc cref="ThrowIfInvalidAssociatedData(ReadOnlySpan{ReadOnlyMemory{byte}})"/>
+    /// <inheritdoc cref="ThrowIfDisposed"/>
     public void Encrypt(ReadOnlySpan<byte> plaintext, Span<byte> ciphertext, params ReadOnlySpan<ReadOnlyMemory<byte>> associatedData)
     {
         // Input validation
@@ -326,6 +330,7 @@ public void Encrypt(ReadOnlySpan<byte> plaintext, Span<byte> ciphertext, params
     /// <inheritdoc cref="ThrowIfInvalidPlaintext(byte[])"/>
     /// <inheritdoc cref="ThrowIfInvalidPlaintext(Span{byte}, ReadOnlySpan{byte})"/>
     /// <inheritdoc cref="ThrowIfInvalidAssociatedData(byte[][])"/>
+    /// <inheritdoc cref="ThrowIfDisposed"/>
     /// <exception cref="CryptographicException">The tag value could not be verified.</exception>
     public void Decrypt(byte[] ciphertext, byte[] plaintext, params byte[][] associatedData)
     {
@@ -373,6 +378,7 @@ public void Decrypt(byte[] ciphertext, byte[] plaintext, params byte[][] associa
     /// <param name="associatedData">Extra data associated with this message, which must match the value provided during encryption.</param>
     /// <inheritdoc cref="ThrowIfInvalidCiphertext(ReadOnlySpan{byte})"/>
     /// <inheritdoc cref="ThrowIfInvalidPlaintext(Span{byte}, ReadOnlySpan{byte})"/>
+    /// <inheritdoc cref="ThrowIfDisposed"/>
     /// <exception cref="CryptographicException">The tag value could not be verified.</exception>
     public void Decrypt(ReadOnlySpan<byte> ciphertext, Span<byte> plaintext, ReadOnlySpan<byte> associatedData)
     {
@@ -413,6 +419,7 @@ public void Decrypt(ReadOnlySpan<byte> ciphertext, Span<byte> plaintext, ReadOnl
     /// <inheritdoc cref="ThrowIfInvalidCiphertext(ReadOnlySpan{byte})"/>
     /// <inheritdoc cref="ThrowIfInvalidPlaintext(Span{byte}, ReadOnlySpan{byte})"/>
     /// <inheritdoc cref="ThrowIfInvalidAssociatedData(ReadOnlySpan{ReadOnlyMemory{byte}})"/>
+    /// <inheritdoc cref="ThrowIfDisposed"/>
     /// <exception cref="CryptographicException">The tag value could not be verified.</exception>
     public void Decrypt(ReadOnlySpan<byte> ciphertext, Span<byte> plaintext, params ReadOnlySpan<ReadOnlyMemory<byte>> associatedData)
     {
diff --git a/UnitTests/AesCmac_KAT.cs b/UnitTests/AesCmac_KAT.cs
index 84c971a..d1eb952 100644
--- a/UnitTests/AesCmac_KAT.cs
+++ b/UnitTests/AesCmac_KAT.cs
@@ -12,8 +12,7 @@ sealed class AesCmac_KAT
     [NistAesCmacSampleDataSource]
     public void NistExample_ComputeHash(NistAesCmacSampleTestVector testVector)
     {
-        using var aesCmac = AesCmac.Create();
-        aesCmac.Key = testVector.Key.ToArray();
+        using var aesCmac = new AesCmac(testVector.Key.Span);
         var tag = aesCmac.ComputeHash(testVector.PT.ToArray());
         CollectionAssert.AreEqual(testVector.Tag.ToArray(), tag);
     }
diff --git a/UnitTests/AesCmac_Tests.cs b/UnitTests/AesCmac_Tests.cs
index 8a38ea4..0dcde0a 100644
--- a/UnitTests/AesCmac_Tests.cs
+++ b/UnitTests/AesCmac_Tests.cs
@@ -68,20 +68,48 @@ public override void Write(byte[] buffer, int offset, int count)
         }
     }
 
+    [TestMethod]
+    public void RegisterWithCryptoConfig()
+    {
+        AesCmac.RegisterWithCryptoConfig();
+        using var aesCmac = (AesCmac?)CryptoConfig.CreateFromName("AesCmac");
+        Assert.IsNotNull(aesCmac);
+    }
+
+    [TestMethod]
+    public void RegisterWithCryptoConfig_Twice()
+    {
+        AesCmac.RegisterWithCryptoConfig();
+        AesCmac.RegisterWithCryptoConfig();
+        using var aesCmac = (AesCmac?)CryptoConfig.CreateFromName("Dorssel.Security.Cryptography.AesCmac");
+        Assert.IsNotNull(aesCmac);
+    }
+
     [TestMethod]
     public void Create()
     {
-        using var keyedHashAlgorithm = AesCmac.Create();
-        Assert.IsNotNull(keyedHashAlgorithm);
+#pragma warning disable CS0618 // Type or member is obsolete
+        using var aesCmac = AesCmac.Create();
+#pragma warning restore CS0618 // Type or member is obsolete
+        Assert.IsNotNull(aesCmac);
     }
 
     [TestMethod]
     public void Create_Name()
     {
 #pragma warning disable CS0618 // Type or member is obsolete
-        using var keyedHashAlgorithm = AesCmac.Create("AesCmac");
+        using var aesCmac = AesCmac.Create("AesCmac");
+#pragma warning restore CS0618 // Type or member is obsolete
+        Assert.IsNotNull(aesCmac);
+    }
+
+    [TestMethod]
+    public void Create_FullName()
+    {
+#pragma warning disable CS0618 // Type or member is obsolete
+        using var aesCmac = AesCmac.Create("Dorssel.Security.Cryptography.AesCmac");
 #pragma warning restore CS0618 // Type or member is obsolete
-        Assert.IsNotNull(keyedHashAlgorithm);
+        Assert.IsNotNull(aesCmac);
     }
 
     [TestMethod]
@@ -90,7 +118,7 @@ public void Create_NullNameFails()
         Assert.ThrowsException<ArgumentNullException>(() =>
         {
 #pragma warning disable CS0618 // Type or member is obsolete
-            using var keyedHashAlgorithm = AesCmac.Create(null!);
+            using var aesCmac = AesCmac.Create(null!);
 #pragma warning restore CS0618 // Type or member is obsolete
         });
     }
@@ -99,37 +127,70 @@ public void Create_NullNameFails()
     public void Create_OtherNameReturnsNull()
     {
 #pragma warning disable CS0618 // Type or member is obsolete
-        using var keyedHashAlgorithm = AesCmac.Create("SomeOtherName");
+        using var aesCmac = AesCmac.Create("SomeOtherName");
 #pragma warning restore CS0618 // Type or member is obsolete
-        Assert.IsNull(keyedHashAlgorithm);
+        Assert.IsNull(aesCmac);
     }
 
     [TestMethod]
     public void Constructor_Default()
     {
         using var aesCmac = new AesCmac();
+
+        Assert.AreEqual(256, aesCmac.Key.Length * 8);
+        CollectionAssert.AreNotEqual(new byte[aesCmac.Key.Length], aesCmac.Key);
     }
 
     [TestMethod]
     [DataRow(128)]
     [DataRow(192)]
     [DataRow(256)]
-    public void Constructor_WithKey(int keySize)
+    public void Constructor_Int(int keySize)
+    {
+        using var aesCmac = new AesCmac(keySize);
+
+        Assert.AreEqual(keySize, aesCmac.Key.Length * 8);
+        CollectionAssert.AreNotEqual(new byte[aesCmac.Key.Length], aesCmac.Key);
+    }
+
+    [TestMethod]
+    [DataRow(0)]
+    [DataRow(1)]
+    [DataRow(16)]
+    [DataRow(24)]
+    [DataRow(32)]
+    public void Constructor_Int_Invalid(int keySize)
+    {
+        Assert.ThrowsException<CryptographicException>(() =>
+        {
+            using var aesCmac = new AesCmac(keySize);
+        });
+    }
+
+    [TestMethod]
+    [DataRow(128)]
+    [DataRow(192)]
+    [DataRow(256)]
+    public void Constructor_Array(int keySize)
     {
         using var aesCmac = new AesCmac(new byte[keySize / 8]);
     }
 
     [TestMethod]
-    public void Constructor_WithInvalidKeySize()
+    [DataRow(0)]
+    [DataRow(16)]
+    [DataRow(24)]
+    [DataRow(32)]
+    public void Constructor_Array_Invalid(int keySize)
     {
         Assert.ThrowsException<CryptographicException>(() =>
         {
-            using var aesCmac = new AesCmac(new byte[42]);
+            using var aesCmac = new AesCmac(new byte[keySize / 8]);
         });
     }
 
     [TestMethod]
-    public void Constructor_WithNullKey()
+    public void Constructor_Array_Null()
     {
         Assert.ThrowsException<ArgumentNullException>(() =>
         {
@@ -137,6 +198,28 @@ public void Constructor_WithNullKey()
         });
     }
 
+    [TestMethod]
+    [DataRow(128)]
+    [DataRow(192)]
+    [DataRow(256)]
+    public void Constructor_ReadOnlySpan(int keySize)
+    {
+        using var aesCmac = new AesCmac(new byte[keySize / 8].AsSpan());
+    }
+
+    [TestMethod]
+    [DataRow(0)]
+    [DataRow(16)]
+    [DataRow(24)]
+    [DataRow(32)]
+    public void Constructor_ReadOnlySpan_Invalid(int keySize)
+    {
+        Assert.ThrowsException<CryptographicException>(() =>
+        {
+            using var aesCmac = new AesCmac(new byte[keySize / 8].AsSpan());
+        });
+    }
+
     [TestMethod]
     public void Dispose()
     {
@@ -179,6 +262,18 @@ public void Key_ChangeWhileBusy()
         });
     }
 
+    [TestMethod]
+    public void Key_ChangeAfterDispose()
+    {
+        using var aesCmac = new AesCmac();
+        aesCmac.Dispose();
+
+        Assert.ThrowsException<ObjectDisposedException>(() =>
+        {
+            aesCmac.Key = TestKey;
+        });
+    }
+
     [TestMethod]
     public void ComputeHash_Segmented()
     {
diff --git a/UnitTests/AesCtr_KAT.cs b/UnitTests/AesCtr_KAT.cs
index 0a98408..e36af73 100644
--- a/UnitTests/AesCtr_KAT.cs
+++ b/UnitTests/AesCtr_KAT.cs
@@ -12,9 +12,7 @@ sealed class AesCtr_KAT
     [NistAesCtrSampleDataSource]
     public void Encrypt_Write(NistAesCtrSampleTestVector testVector)
     {
-        using var aes = AesCtr.Create();
-        aes.Key = testVector.Key.ToArray();
-        aes.IV = testVector.InitialCounter.ToArray();
+        using var aes = new AesCtr(testVector.Key.Span, testVector.InitialCounter.Span);
         using var plaintextStream = new MemoryStream(testVector.Plaintext.ToArray());
         using var ciphertextStream = new MemoryStream();
         {
@@ -30,9 +28,7 @@ public void Encrypt_Write(NistAesCtrSampleTestVector testVector)
     [NistAesCtrSampleDataSource]
     public void Encrypt_Read(NistAesCtrSampleTestVector testVector)
     {
-        using var aes = AesCtr.Create();
-        aes.Key = testVector.Key.ToArray();
-        aes.IV = testVector.InitialCounter.ToArray();
+        using var aes = new AesCtr(testVector.Key.Span, testVector.InitialCounter.Span);
         using var plaintextStream = new MemoryStream(testVector.Plaintext.ToArray());
         using var ciphertextStream = new MemoryStream();
         {
@@ -48,9 +44,7 @@ public void Encrypt_Read(NistAesCtrSampleTestVector testVector)
     [NistAesCtrSampleDataSource]
     public void Decrypt_Write(NistAesCtrSampleTestVector testVector)
     {
-        using var aes = AesCtr.Create();
-        aes.Key = testVector.Key.ToArray();
-        aes.IV = testVector.InitialCounter.ToArray();
+        using var aes = new AesCtr(testVector.Key.Span, testVector.InitialCounter.Span);
         using var ciphertextStream = new MemoryStream(testVector.Ciphertext.ToArray());
         using var plaintextStream = new MemoryStream();
         {
@@ -66,9 +60,7 @@ public void Decrypt_Write(NistAesCtrSampleTestVector testVector)
     [NistAesCtrSampleDataSource]
     public void Decrypt_Read(NistAesCtrSampleTestVector testVector)
     {
-        using var aes = AesCtr.Create();
-        aes.Key = testVector.Key.ToArray();
-        aes.IV = testVector.InitialCounter.ToArray();
+        using var aes = new AesCtr(testVector.Key.Span, testVector.InitialCounter.Span);
         using var ciphertextStream = new MemoryStream(testVector.Ciphertext.ToArray());
         using var plaintextStream = new MemoryStream();
         {
@@ -84,8 +76,7 @@ public void Decrypt_Read(NistAesCtrSampleTestVector testVector)
     [NistAesCtrSampleDataSource]
     public void Encrypt_TransformCtr_Array_Array(NistAesCtrSampleTestVector testVector)
     {
-        using var aes = AesCtr.Create();
-        aes.Key = testVector.Key.ToArray();
+        using var aes = new AesCtr(testVector.Key.Span);
 
         var destination = aes.TransformCtr(testVector.Plaintext.ToArray(), testVector.InitialCounter.ToArray());
 
@@ -97,8 +88,7 @@ public void Encrypt_TransformCtr_Array_Array(NistAesCtrSampleTestVector testVect
     [NistAesCtrSampleDataSource]
     public void Encrypt_TransformCtr_ReadOnlySpan_ReadOnlySpan(NistAesCtrSampleTestVector testVector)
     {
-        using var aes = AesCtr.Create();
-        aes.Key = testVector.Key.ToArray();
+        using var aes = new AesCtr(testVector.Key.Span);
 
         var destination = aes.TransformCtr(testVector.Plaintext.Span, testVector.InitialCounter.Span);
 
@@ -110,8 +100,7 @@ public void Encrypt_TransformCtr_ReadOnlySpan_ReadOnlySpan(NistAesCtrSampleTestV
     [NistAesCtrSampleDataSource]
     public void Encrypt_TransformCtr_ReadOnlySpan_ReadOnlySpan_Span(NistAesCtrSampleTestVector testVector)
     {
-        using var aes = AesCtr.Create();
-        aes.Key = testVector.Key.ToArray();
+        using var aes = new AesCtr(testVector.Key.Span);
         var destination = new byte[testVector.Ciphertext.Length];
 
         var count = aes.TransformCtr(testVector.Plaintext.Span, testVector.InitialCounter.Span, destination);
@@ -125,8 +114,7 @@ public void Encrypt_TransformCtr_ReadOnlySpan_ReadOnlySpan_Span(NistAesCtrSample
     [NistAesCtrSampleDataSource]
     public void Encrypt_TryTransformCtr(NistAesCtrSampleTestVector testVector)
     {
-        using var aes = AesCtr.Create();
-        aes.Key = testVector.Key.ToArray();
+        using var aes = new AesCtr(testVector.Key.Span);
         var destination = new byte[testVector.Ciphertext.Length];
 
         var success = aes.TryTransformCtr(testVector.Plaintext.Span, testVector.InitialCounter.Span, destination, out var bytesWritten);
@@ -141,8 +129,7 @@ public void Encrypt_TryTransformCtr(NistAesCtrSampleTestVector testVector)
     [NistAesCtrSampleDataSource]
     public void Decrypt_TransformCtr_Array_Array(NistAesCtrSampleTestVector testVector)
     {
-        using var aes = AesCtr.Create();
-        aes.Key = testVector.Key.ToArray();
+        using var aes = new AesCtr(testVector.Key.Span);
 
         var destination = aes.TransformCtr(testVector.Ciphertext.ToArray(), testVector.InitialCounter.ToArray());
 
@@ -154,8 +141,7 @@ public void Decrypt_TransformCtr_Array_Array(NistAesCtrSampleTestVector testVect
     [NistAesCtrSampleDataSource]
     public void Decrypt_TransformCtr_ReadOnlySpan_ReadOnlySpan(NistAesCtrSampleTestVector testVector)
     {
-        using var aes = AesCtr.Create();
-        aes.Key = testVector.Key.ToArray();
+        using var aes = new AesCtr(testVector.Key.Span);
 
         var destination = aes.TransformCtr(testVector.Ciphertext.Span, testVector.InitialCounter.Span);
 
@@ -167,8 +153,7 @@ public void Decrypt_TransformCtr_ReadOnlySpan_ReadOnlySpan(NistAesCtrSampleTestV
     [NistAesCtrSampleDataSource]
     public void Decrypt_TransformCtr_ReadOnlySpan_ReadOnlySpan_Span(NistAesCtrSampleTestVector testVector)
     {
-        using var aes = AesCtr.Create();
-        aes.Key = testVector.Key.ToArray();
+        using var aes = new AesCtr(testVector.Key.Span);
         var destination = new byte[testVector.Plaintext.Length];
 
         var count = aes.TransformCtr(testVector.Ciphertext.Span, testVector.InitialCounter.Span, destination);
@@ -182,8 +167,7 @@ public void Decrypt_TransformCtr_ReadOnlySpan_ReadOnlySpan_Span(NistAesCtrSample
     [NistAesCtrSampleDataSource]
     public void Decrypt_TryTransformCtr(NistAesCtrSampleTestVector testVector)
     {
-        using var aes = AesCtr.Create();
-        aes.Key = testVector.Key.ToArray();
+        using var aes = new AesCtr(testVector.Key.Span);
         var destination = new byte[testVector.Plaintext.Length];
 
         var success = aes.TryTransformCtr(testVector.Ciphertext.Span, testVector.InitialCounter.Span, destination, out var bytesWritten);
diff --git a/UnitTests/AesCtr_Tests.cs b/UnitTests/AesCtr_Tests.cs
index b891a67..8f48aeb 100644
--- a/UnitTests/AesCtr_Tests.cs
+++ b/UnitTests/AesCtr_Tests.cs
@@ -24,15 +24,38 @@ sealed class AesCtr_Tests
 
     static readonly byte[] TestMessage = [1, 2, 3, 4, 5];
 
-    static readonly byte[] TestInvalidIV = new byte[BLOCKSIZE - 1];
+    static readonly byte[] TestIVInvalid = new byte[BLOCKSIZE - 1];
+
+    static byte[] TestKeyNull => null!;
+
+    static byte[] TestIVNull => null!;
 
     [TestMethod]
-    public void Create()
+    public void RegisterWithCryptoConfig()
+    {
+        AesCtr.RegisterWithCryptoConfig();
+        using var aes = (AesCtr?)CryptoConfig.CreateFromName("AesCtr");
+        Assert.IsNotNull(aes);
+    }
+
+    [TestMethod]
+    public void RegisterWithCryptoConfig_Twice()
     {
-        using var aes = AesCtr.Create();
+        AesCtr.RegisterWithCryptoConfig();
+        AesCtr.RegisterWithCryptoConfig();
+        using var aes = (AesCtr?)CryptoConfig.CreateFromName("Dorssel.Security.Cryptography.AesCtr");
         Assert.IsNotNull(aes);
     }
 
+    [TestMethod]
+    public void Create()
+    {
+#pragma warning disable CS0618 // Type or member is obsolete
+        using var aesCtr = AesCtr.Create();
+#pragma warning restore CS0618 // Type or member is obsolete
+        Assert.IsNotNull(aesCtr);
+    }
+
     [TestMethod]
     public void Create_Name()
     {
@@ -42,6 +65,16 @@ public void Create_Name()
         Assert.IsNotNull(aes);
     }
 
+    [TestMethod]
+    public void Create_FullName()
+    {
+#pragma warning disable CS0618 // Type or member is obsolete
+        using var aes = AesCtr.Create("Dorssel.Security.Cryptography.AesCtr");
+#pragma warning restore CS0618 // Type or member is obsolete
+        Assert.IsNotNull(aes);
+    }
+
+
     [TestMethod]
     public void Create_NullNameFails()
     {
@@ -62,17 +95,205 @@ public void Create_OtherNameReturnsNull()
         Assert.IsNull(aes);
     }
 
+    [TestMethod]
+    public void Constructor()
+    {
+        using var aes = new AesCtr();
+
+        Assert.AreEqual(256, aes.KeySize);
+        Assert.AreEqual(256 / 8, aes.Key.Length);
+        CollectionAssert.AreNotEqual(new byte[aes.Key.Length], aes.Key);
+        Assert.AreEqual(256, aes.KeySize);
+    }
+
+    [TestMethod]
+    [DataRow(128)]
+    [DataRow(192)]
+    [DataRow(256)]
+    public void Constructor_Int(int keySize)
+    {
+        using var aes = new AesCtr(keySize);
+
+        Assert.AreEqual(keySize, aes.KeySize);
+        Assert.AreEqual(keySize / 8, aes.Key.Length);
+        CollectionAssert.AreNotEqual(new byte[aes.Key.Length], aes.Key);
+        Assert.AreEqual(keySize, aes.KeySize);
+    }
+
+    [TestMethod]
+    [DataRow(0)]
+    [DataRow(1)]
+    [DataRow(16)]
+    [DataRow(24)]
+    [DataRow(32)]
+    public void Constructor_Int_Invalid(int keySize)
+    {
+        Assert.ThrowsException<CryptographicException>(() =>
+        {
+            using var aes = new AesCtr(keySize);
+        });
+    }
+
+    [TestMethod]
+    [DataRow(128)]
+    [DataRow(192)]
+    [DataRow(256)]
+    public void Constructor_Array(int keySize)
+    {
+        using var aes = new AesCtr(new byte[keySize / 8]);
+
+        Assert.AreEqual(keySize, aes.KeySize);
+        Assert.AreEqual(keySize / 8, aes.Key.Length);
+        Assert.AreEqual(keySize, aes.KeySize);
+    }
+
+    [TestMethod]
+    [DataRow(0)]
+    [DataRow(16)]
+    [DataRow(24)]
+    [DataRow(32)]
+    public void Constructor_Array_Invalid(int keySize)
+    {
+        Assert.ThrowsException<CryptographicException>(() =>
+        {
+            using var aes = new AesCtr(new byte[keySize / 8]);
+        });
+    }
+
+    [TestMethod]
+    public void Constructor_Array_Null()
+    {
+        Assert.ThrowsException<ArgumentNullException>(() =>
+        {
+            using var aes = new AesCtr(TestKeyNull);
+        });
+    }
+
+    [TestMethod]
+    [DataRow(128)]
+    [DataRow(192)]
+    [DataRow(256)]
+    public void Constructor_ReadOnlySpan(int keySize)
+    {
+        using var aes = new AesCtr(new byte[keySize / 8].AsSpan());
+
+        Assert.AreEqual(keySize, aes.KeySize);
+        Assert.AreEqual(keySize / 8, aes.Key.Length);
+        Assert.AreEqual(keySize, aes.KeySize);
+    }
+
+    [TestMethod]
+    [DataRow(0)]
+    [DataRow(16)]
+    [DataRow(24)]
+    [DataRow(32)]
+    public void Constructor_ReadOnlySpan_Invalid(int keySize)
+    {
+        Assert.ThrowsException<CryptographicException>(() =>
+        {
+            using var aes = new AesCtr(new byte[keySize / 8].AsSpan());
+        });
+    }
+
+    [TestMethod]
+    [DataRow(128)]
+    [DataRow(192)]
+    [DataRow(256)]
+    public void Constructor_Array_Array(int keySize)
+    {
+        using var aes = new AesCtr(new byte[keySize / 8], TestIV);
+
+        Assert.AreEqual(keySize, aes.KeySize);
+        Assert.AreEqual(keySize / 8, aes.Key.Length);
+        Assert.AreEqual(keySize, aes.KeySize);
+    }
+
+    [TestMethod]
+    [DataRow(0)]
+    [DataRow(16)]
+    [DataRow(24)]
+    [DataRow(32)]
+    public void Constructor_Array_Array_KeyInvalid(int keySize)
+    {
+        Assert.ThrowsException<CryptographicException>(() =>
+        {
+            using var aes = new AesCtr(new byte[keySize / 8], TestIV);
+        });
+    }
+
+    [TestMethod]
+    public void Constructor_Array_Array_KeyNull()
+    {
+        Assert.ThrowsException<ArgumentNullException>(() =>
+        {
+            using var aes = new AesCtr(TestKeyNull, TestIV);
+        });
+    }
+
+    [TestMethod]
+    public void Constructor_Array_Array_IVInvalid()
+    {
+        Assert.ThrowsException<ArgumentException>(() =>
+        {
+            using var aes = new AesCtr(TestKey, TestIVInvalid);
+        });
+    }
+
+    [TestMethod]
+    public void Constructor_Array_Array_IVNull()
+    {
+        Assert.ThrowsException<ArgumentNullException>(() =>
+        {
+            using var aes = new AesCtr(TestKey, TestIVNull);
+        });
+    }
+
+    [TestMethod]
+    [DataRow(128)]
+    [DataRow(192)]
+    [DataRow(256)]
+    public void Constructor_ReadOnlySpan_ReadOnlySpan(int keySize)
+    {
+        using var aes = new AesCtr(new byte[keySize / 8].AsSpan(), TestIV.AsSpan());
+
+        Assert.AreEqual(keySize, aes.KeySize);
+        Assert.AreEqual(keySize / 8, aes.Key.Length);
+        Assert.AreEqual(keySize, aes.KeySize);
+    }
+
+    [TestMethod]
+    [DataRow(0)]
+    [DataRow(16)]
+    [DataRow(24)]
+    [DataRow(32)]
+    public void Constructor_ReadOnlySpan_ReadOnlySpan_KeyInvalid(int keySize)
+    {
+        Assert.ThrowsException<CryptographicException>(() =>
+        {
+            using var aes = new AesCtr(new byte[keySize / 8].AsSpan(), TestIV.AsSpan());
+        });
+    }
+
+    [TestMethod]
+    public void Constructor_ReadOnlySpan_ReadOnlySpan_IVInvalid()
+    {
+        Assert.ThrowsException<ArgumentException>(() =>
+        {
+            using var aes = new AesCtr(TestKey.AsSpan(), TestIVInvalid.AsSpan());
+        });
+    }
+
     [TestMethod]
     public void Dispose()
     {
-        var aes = AesCtr.Create();
+        var aes = new AesCtr();
         aes.Dispose();
     }
 
     [TestMethod]
     public void Dispose_Double()
     {
-        var aes = AesCtr.Create();
+        var aes = new AesCtr();
         aes.Dispose();
         aes.Dispose();
     }
@@ -80,7 +301,7 @@ public void Dispose_Double()
     [TestMethod]
     public void Mode_SetUnchanged()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
         Assert.AreEqual(CipherMode.CTS, aes.Mode);  // DevSkim: ignore DS187371
         aes.Mode = CipherMode.CTS;  // DevSkim: ignore DS187371
         Assert.AreEqual(CipherMode.CTS, aes.Mode);  // DevSkim: ignore DS187371
@@ -89,7 +310,7 @@ public void Mode_SetUnchanged()
     [TestMethod]
     public void Mode_CannotChange()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
         Assert.AreEqual(CipherMode.CTS, aes.Mode);  // DevSkim: ignore DS187371
         Assert.ThrowsException<CryptographicException>(() =>
         {
@@ -101,7 +322,7 @@ public void Mode_CannotChange()
     [TestMethod]
     public void Padding_SetUnchanged()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
         Assert.AreEqual(PaddingMode.None, aes.Padding);
         aes.Padding = PaddingMode.None;
         Assert.AreEqual(PaddingMode.None, aes.Padding);
@@ -110,7 +331,7 @@ public void Padding_SetUnchanged()
     [TestMethod]
     public void Padding_CannotChange()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
         var padding = aes.Padding;
         Assert.AreEqual(PaddingMode.None, padding);
         Assert.ThrowsException<CryptographicException>(() =>
@@ -123,7 +344,7 @@ public void Padding_CannotChange()
     [TestMethod]
     public void FeedbackSize_SetUnchanged()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
         Assert.AreEqual(aes.BlockSize, aes.FeedbackSize);
         aes.FeedbackSize = aes.BlockSize;
         Assert.AreEqual(aes.BlockSize, aes.FeedbackSize);
@@ -132,7 +353,7 @@ public void FeedbackSize_SetUnchanged()
     [TestMethod]
     public void FeedbackSize_CannotChange()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
         Assert.AreEqual(aes.BlockSize, aes.FeedbackSize);
         Assert.ThrowsException<CryptographicException>(() =>
         {
@@ -144,7 +365,7 @@ public void FeedbackSize_CannotChange()
     [TestMethod]
     public void KeySize_AllValid()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
         foreach (var legalKeySize in aes.LegalKeySizes)
         {
             for (var keySize = legalKeySize.MinSize; keySize <= legalKeySize.MaxSize; keySize += Math.Max(legalKeySize.SkipSize, 1))
@@ -156,10 +377,80 @@ public void KeySize_AllValid()
         }
     }
 
+    [TestMethod]
+    public void Key_AllValid()
+    {
+        using var aes = new AesCtr();
+        foreach (var legalKeySize in aes.LegalKeySizes)
+        {
+            for (var keySize = legalKeySize.MinSize; keySize <= legalKeySize.MaxSize; keySize += Math.Max(legalKeySize.SkipSize, 1))
+            {
+                aes.Key = new byte[keySize / 8];
+                Assert.AreEqual(keySize, aes.KeySize);
+                Assert.AreEqual(keySize, aes.Key.Length * 8);
+            }
+        }
+    }
+
+    [TestMethod]
+    public void Key_Null()
+    {
+        using var aes = new AesCtr();
+
+        Assert.ThrowsException<ArgumentNullException>(() =>
+        {
+            aes.Key = TestKeyNull;
+        });
+    }
+
+    [TestMethod]
+    public void Key_AfterDispose()
+    {
+        using var aes = new AesCtr();
+        aes.Dispose();
+
+        Assert.ThrowsException<ObjectDisposedException>(() =>
+        {
+            aes.Key = TestKey;
+        });
+    }
+
+    [TestMethod]
+    public void IV()
+    {
+        using var aes = new AesCtr();
+
+        aes.IV = TestIV;
+        CollectionAssert.AreEqual(TestIV, aes.IV);
+    }
+
+    [TestMethod]
+    public void IV_Null()
+    {
+        using var aes = new AesCtr();
+
+        Assert.ThrowsException<ArgumentNullException>(() =>
+        {
+            aes.IV = TestIVNull;
+        });
+    }
+
+    [TestMethod]
+    public void IV_AfterDispose()
+    {
+        using var aes = new AesCtr();
+        aes.Dispose();
+
+        Assert.ThrowsException<ObjectDisposedException>(() =>
+        {
+            aes.IV = TestIV;
+        });
+    }
+
     [TestMethod]
     public void BlockSize_AllValid()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
         foreach (var legalBlockSize in aes.LegalBlockSizes)
         {
             for (var blockSize = legalBlockSize.MinSize; blockSize <= legalBlockSize.MaxSize; blockSize += Math.Max(legalBlockSize.SkipSize, 1))
@@ -176,7 +467,7 @@ public void BlockSize_AllValid()
     [DataRow(256)]
     public void GenerateIV_HasCorrectLength(int keySize)
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
         aes.KeySize = keySize;
         aes.GenerateIV();
         Assert.AreEqual(aes.BlockSize, aes.IV.Length * 8);
@@ -188,7 +479,7 @@ public void GenerateIV_HasCorrectLength(int keySize)
     [DataRow(256)]
     public void GenerateKey_HasCorrectLength(int keySize)
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
         aes.KeySize = keySize;
         aes.GenerateKey();
         Assert.AreEqual(keySize, aes.Key.Length * 8);
@@ -197,41 +488,75 @@ public void GenerateKey_HasCorrectLength(int keySize)
     [TestMethod]
     public void CreateEncryptor()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
         using var _ = aes.CreateEncryptor();
     }
 
     [TestMethod]
-    public void CreateEncryptor_Null()
+    public void CreateEncryptor_Array_Array()
+    {
+        using var aes = new AesCtr();
+        using var _ = aes.CreateEncryptor(TestKey, TestIV);
+    }
+
+    [TestMethod]
+    public void CreateEncryptor_Array_Array_KeyNull()
+    {
+        using var aes = new AesCtr();
+        Assert.ThrowsException<ArgumentNullException>(() =>
+        {
+            using var _ = aes.CreateEncryptor(TestKeyNull, TestIV);
+        });
+    }
+
+    [TestMethod]
+    public void CreateEncryptor_Array_Array_IVNull()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
         Assert.ThrowsException<CryptographicException>(() =>
         {
-            using var _ = aes.CreateEncryptor(TestKey, null);
+            using var _ = aes.CreateEncryptor(TestKey, TestIVNull);
         });
     }
 
     [TestMethod]
     public void CreateDecryptor()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
         using var _ = aes.CreateDecryptor();
     }
 
     [TestMethod]
-    public void CreateDecryptor_Null()
+    public void CreateDecryptor_Array_Array()
+    {
+        using var aes = new AesCtr();
+        using var _ = aes.CreateDecryptor(TestKey, TestIV);
+    }
+
+    [TestMethod]
+    public void CreateDecryptor_Array_Array_KeyNull()
+    {
+        using var aes = new AesCtr();
+        Assert.ThrowsException<ArgumentNullException>(() =>
+        {
+            using var _ = aes.CreateDecryptor(TestKeyNull, TestIV);
+        });
+    }
+
+    [TestMethod]
+    public void CreateDecryptor_Array_Array_IVNull()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
         Assert.ThrowsException<CryptographicException>(() =>
         {
-            using var _ = aes.CreateDecryptor(TestKey, null);
+            using var _ = aes.CreateDecryptor(TestKey, TestIVNull);
         });
     }
 
     [TestMethod]
     public void TransformCtr_Array_Array()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
 
         aes.TransformCtr(TestMessage, TestIV);
     }
@@ -239,40 +564,40 @@ public void TransformCtr_Array_Array()
     [TestMethod]
     public void TransformCtr_Null_Array()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
 
         Assert.ThrowsException<ArgumentNullException>(() =>
         {
-            aes.TransformCtr(null!, TestIV);
+            aes.TransformCtr(TestKeyNull, TestIV);
         });
     }
 
     [TestMethod]
     public void TransformCtr_Array_Null()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
 
         Assert.ThrowsException<ArgumentNullException>(() =>
         {
-            aes.TransformCtr(TestMessage, null!);
+            aes.TransformCtr(TestMessage, TestIVNull);
         });
     }
 
     [TestMethod]
     public void TransformCtr_Array_Array_InvalidIV()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
 
         Assert.ThrowsException<ArgumentException>(() =>
         {
-            aes.TransformCtr(TestMessage, TestInvalidIV);
+            aes.TransformCtr(TestMessage, TestIVInvalid);
         });
     }
 
     [TestMethod]
     public void TransformCtr_ReadOnlySpan_ReadOnlySpan()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
 
         aes.TransformCtr(TestMessage.AsSpan(), TestIV.AsSpan());
     }
@@ -280,18 +605,18 @@ public void TransformCtr_ReadOnlySpan_ReadOnlySpan()
     [TestMethod]
     public void TransformCtr_ReadOnlySpan_ReadOnlySpan_InvalidIV()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
 
         Assert.ThrowsException<ArgumentException>(() =>
         {
-            aes.TransformCtr(TestMessage.AsSpan(), TestInvalidIV.AsSpan());
+            aes.TransformCtr(TestMessage.AsSpan(), TestIVInvalid.AsSpan());
         });
     }
 
     [TestMethod]
     public void TransformCtr_ReadOnlySpan_ReadOnlySpan_Span()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
         var destination = new byte[TestMessage.Length];
 
         aes.TransformCtr(TestMessage.AsSpan(), TestIV.AsSpan(), destination);
@@ -300,19 +625,19 @@ public void TransformCtr_ReadOnlySpan_ReadOnlySpan_Span()
     [TestMethod]
     public void TransformCtr_ReadOnlySpan_ReadOnlySpan_Span_InvalidIV()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
         var destination = new byte[TestMessage.Length];
 
         Assert.ThrowsException<ArgumentException>(() =>
         {
-            aes.TransformCtr(TestMessage, TestInvalidIV, destination);
+            aes.TransformCtr(TestMessage, TestIVInvalid, destination);
         });
     }
 
     [TestMethod]
     public void TransformCtr_ReadOnlySpan_ReadOnlySpan_Span_Short()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
         var destination = new byte[TestMessage.Length - 1];
 
         Assert.ThrowsException<ArgumentException>(() =>
@@ -324,7 +649,7 @@ public void TransformCtr_ReadOnlySpan_ReadOnlySpan_Span_Short()
     [TestMethod]
     public void TryTransformCtr()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
         var destination = new byte[TestMessage.Length];
 
         aes.TryTransformCtr(TestMessage.AsSpan(), TestIV.AsSpan(), destination, out _);
@@ -333,19 +658,19 @@ public void TryTransformCtr()
     [TestMethod]
     public void TryTransformCtr_InvalidIV()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
         var destination = new byte[TestMessage.Length];
 
         Assert.ThrowsException<ArgumentException>(() =>
         {
-            aes.TryTransformCtr(TestMessage.AsSpan(), TestInvalidIV, destination, out _);
+            aes.TryTransformCtr(TestMessage.AsSpan(), TestIVInvalid, destination, out _);
         });
     }
 
     [TestMethod]
     public void TryTransformCtr_Short()
     {
-        using var aes = AesCtr.Create();
+        using var aes = new AesCtr();
         var destination = new byte[TestMessage.Length - 1];
 
         var success = aes.TryTransformCtr(TestMessage, TestIV, destination, out var bytesWritten);
diff --git a/UnitTests/NistAesCtrSampleTestVector.cs b/UnitTests/NistAesCtrSampleTestVector.cs
index 5443fef..ebd992d 100644
--- a/UnitTests/NistAesCtrSampleTestVector.cs
+++ b/UnitTests/NistAesCtrSampleTestVector.cs
@@ -93,79 +93,79 @@ static NistAesCtrSampleTestVector()
         var testVectors = new List<NistAesCtrSampleTestVector>
         {
             new("F.5.1", "CTR-AES128.Encrypt", @"
-                    Key             2b7e151628aed2a6abf7158809cf4f3c
-                    Init. Counter   f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff
+                    Key             2B7E151628AED2A6ABF7158809CF4F3C
+                    Init. Counter   F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF
                     Block #1
-                    Input Block     f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff
-                    Output Block    ec8cdf7398607cb0f2d21675ea9ea1e4
-                    Plaintext       6bc1bee22e409f96e93d7e117393172a
-                    Ciphertext      874d6191b620e3261bef6864990db6ce
+                    Input Block     F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF
+                    Output Block    EC8CDF7398607CB0F2D21675EA9EA1E4
+                    Plaintext       6BC1BEE22E409F96E93D7E117393172A
+                    Ciphertext      874D6191B620E3261BEF6864990DB6CE
                     Block #2
-                    Input Block     f0f1f2f3f4f5f6f7f8f9fafbfcfdff00
-                    Output Block    362b7c3c6773516318a077d7fc5073ae
-                    Plaintext       ae2d8a571e03ac9c9eb76fac45af8e51
-                    Ciphertext      9806f66b7970fdff8617187bb9fffdff
+                    Input Block     F0F1F2F3F4F5F6F7F8F9FAFBFCFDFF00
+                    Output Block    362B7C3C6773516318A077D7FC5073AE
+                    Plaintext       AE2D8A571E03AC9C9EB76FAC45AF8E51
+                    Ciphertext      9806F66B7970FDFF8617187BB9FFFDFF
                     Block #3
-                    Input Block     f0f1f2f3f4f5f6f7f8f9fafbfcfdff01
-                    Output Block    6a2cc3787889374fbeb4c81b17ba6c44
-                    Plaintext       30c81c46a35ce411e5fbc1191a0a52ef
-                    Ciphertext      5ae4df3edbd5d35e5b4f09020db03eab
+                    Input Block     F0F1F2F3F4F5F6F7F8F9FAFBFCFDFF01
+                    Output Block    6A2CC3787889374FBEB4C81B17BA6C44
+                    Plaintext       30C81C46A35CE411E5FBC1191A0A52EF
+                    Ciphertext      5AE4DF3EDBD5D35E5B4F09020DB03EAB
                     Block #4
-                    Input Block     f0f1f2f3f4f5f6f7f8f9fafbfcfdff02
-                    Output Block    e89c399ff0f198c6d40a31db156cabfe
-                    Plaintext       f69f2445df4f9b17ad2b417be66c3710
-                    Ciphertext      1e031dda2fbe03d1792170a0f3009cee
+                    Input Block     F0F1F2F3F4F5F6F7F8F9FAFBFCFDFF02
+                    Output Block    E89C399FF0F198C6D40A31DB156CABFE
+                    Plaintext       F69F2445DF4F9B17AD2B417BE66C3710
+                    Ciphertext      1E031DDA2FBE03D1792170A0F3009CEE
                 "),
 
             new("F.5.3", "CTR-AES192.Encrypt", @"
-                    Key             8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b
-                    Init. Counter   f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff
+                    Key             8E73B0F7DA0E6452C810F32B809079E562F8EAD2522C6B7B
+                    Init. Counter   F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF
                     Block #1
-                    Input Block     f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff
-                    Output Block    717d2dc639128334a6167a488ded7921
-                    Plaintext       6bc1bee22e409f96e93d7e117393172a
-                    Ciphertext      1abc932417521ca24f2b0459fe7e6e0b
+                    Input Block     F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF
+                    Output Block    717D2DC639128334A6167A488DED7921
+                    Plaintext       6BC1BEE22E409F96E93D7E117393172A
+                    Ciphertext      1ABC932417521CA24F2B0459FE7E6E0B
                     Block #2
-                    Input Block     f0f1f2f3f4f5f6f7f8f9fafbfcfdff00
-                    Output Block    a72eb3bb14a556734b7bad6ab16100c5
-                    Plaintext       ae2d8a571e03ac9c9eb76fac45af8e51
-                    Ciphertext      090339ec0aa6faefd5ccc2c6f4ce8e94
+                    Input Block     F0F1F2F3F4F5F6F7F8F9FAFBFCFDFF00
+                    Output Block    A72EB3BB14A556734B7BAD6AB16100C5
+                    Plaintext       AE2D8A571E03AC9C9EB76FAC45AF8E51
+                    Ciphertext      090339EC0AA6FAEFD5CCC2C6F4CE8E94
                     Block #3
-                    Input Block     f0f1f2f3f4f5f6f7f8f9fafbfcfdff01
-                    Output Block    2efeae2d72b722613446dc7f4c2af918
-                    Plaintext       30c81c46a35ce411e5fbc1191a0a52ef
-                    Ciphertext      1e36b26bd1ebc670d1bd1d665620abf7
+                    Input Block     F0F1F2F3F4F5F6F7F8F9FAFBFCFDFF01
+                    Output Block    2EFEAE2D72B722613446DC7F4C2AF918
+                    Plaintext       30C81C46A35CE411E5FBC1191A0A52EF
+                    Ciphertext      1E36B26BD1EBC670D1BD1D665620ABF7
                     Block #4
-                    Input Block     f0f1f2f3f4f5f6f7f8f9fafbfcfdff02
-                    Output Block    b9e783b30dd7924ff7bc9b97beaa8740
-                    Plaintext       f69f2445df4f9b17ad2b417be66c3710
-                    Ciphertext      4f78a7f6d29809585a97daec58c6b050
+                    Input Block     F0F1F2F3F4F5F6F7F8F9FAFBFCFDFF02
+                    Output Block    B9E783B30DD7924FF7BC9B97BEAA8740
+                    Plaintext       F69F2445DF4F9B17AD2B417BE66C3710
+                    Ciphertext      4F78A7F6D29809585A97DAEC58C6B050
             "),
 
             new("F.5.5", "CTR-AES256.Encrypt", @"
-                    Key             603deb1015ca71be2b73aef0857d7781
-                                    1f352c073b6108d72d9810a30914dff4
-                    Init. Counter   f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff
+                    Key             603DEB1015CA71BE2B73AEF0857D7781
+                                    1F352C073B6108D72D9810A30914DFF4
+                    Init. Counter   F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF
                     Block #1
-                    Input Block     f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff
-                    Output Block    0bdf7df1591716335e9a8b15c860c502
-                    Plaintext       6bc1bee22e409f96e93d7e117393172a
-                    Ciphertext      601ec313775789a5b7a7f504bbf3d228
+                    Input Block     F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF
+                    Output Block    0BDF7DF1591716335E9A8B15C860C502
+                    Plaintext       6BC1BEE22E409F96E93D7E117393172A
+                    Ciphertext      601EC313775789A5B7A7F504BBF3D228
                     Block #2
-                    Input Block     f0f1f2f3f4f5f6f7f8f9fafbfcfdff00
-                    Output Block    5a6e699d536119065433863c8f657b94
-                    Plaintext       ae2d8a571e03ac9c9eb76fac45af8e51
-                    Ciphertext      f443e3ca4d62b59aca84e990cacaf5c5
+                    Input Block     F0F1F2F3F4F5F6F7F8F9FAFBFCFDFF00
+                    Output Block    5A6E699D536119065433863C8F657B94
+                    Plaintext       AE2D8A571E03AC9C9EB76FAC45AF8E51
+                    Ciphertext      F443E3CA4D62B59ACA84E990CACAF5C5
                     Block #3
-                    Input Block     f0f1f2f3f4f5f6f7f8f9fafbfcfdff01
-                    Output Block    1bc12c9c01610d5d0d8bd6a3378eca62
-                    Plaintext       30c81c46a35ce411e5fbc1191a0a52ef
-                    Ciphertext      2b0930daa23de94ce87017ba2d84988d
+                    Input Block     F0F1F2F3F4F5F6F7F8F9FAFBFCFDFF01
+                    Output Block    1BC12C9C01610D5D0D8BD6A3378ECA62
+                    Plaintext       30C81C46A35CE411E5FBC1191A0A52EF
+                    Ciphertext      2B0930DAA23DE94CE87017BA2D84988D
                     Block #4
-                    Input Block     f0f1f2f3f4f5f6f7f8f9fafbfcfdff02
-                    Output Block    2956e1c8693536b1bee99c73a31576b6
-                    Plaintext       f69f2445df4f9b17ad2b417be66c3710
-                    Ciphertext      dfc9c58db67aada613c2dd08457941a6
+                    Input Block     F0F1F2F3F4F5F6F7F8F9FAFBFCFDFF02
+                    Output Block    2956E1C8693536B1BEE99C73A31576B6
+                    Plaintext       F69F2445DF4F9B17AD2B417BE66C3710
+                    Ciphertext      DFC9C58DB67AADA613C2DD08457941A6
             "),
 
         };
diff --git a/UnitTests/RfcAesSivTestVector.cs b/UnitTests/RfcAesSivTestVector.cs
index 749dd90..450042d 100644
--- a/UnitTests/RfcAesSivTestVector.cs
+++ b/UnitTests/RfcAesSivTestVector.cs
@@ -74,41 +74,41 @@ static RfcAesSivTestVector()
         var testVectors = new List<RfcAesSivTestVector>
         {
             new("Deterministic", @"
-                    fffefdfc fbfaf9f8 f7f6f5f4 f3f2f1f0
-                    f0f1f2f3 f4f5f6f7 f8f9fafb fcfdfeff
+                    FFFEFDFC FBFAF9F8 F7F6F5F4 F3F2F1F0
+                    F0F1F2F3 F4F5F6F7 F8F9FAFB FCFDFEFF
                 ", [ @"
-                        10111213 14151617 18191a1b 1c1d1e1f
+                        10111213 14151617 18191A1B 1C1D1E1F
                         20212223 24252627
                 " ],
                 null
                 , @"
-                    11223344 55667788 99aabbcc ddee
+                    11223344 55667788 99AABBCC DDEE
                 ", @"
-                    85632d07 c6e8f37f 950acd32 0a2ecc93
-                    40c02b96 90c4dc04 daef7f6a fe5c
+                    85632D07 C6E8F37F 950ACD32 0A2ECC93
+                    40C02B96 90C4DC04 DAEF7F6A FE5C
                 "
             ),
 
             new("Nonce-Based", @"
-                    7f7e7d7c 7b7a7978 77767574 73727170
-                    40414243 44454647 48494a4b 4c4d4e4f
+                    7F7E7D7C 7B7A7978 77767574 73727170
+                    40414243 44454647 48494A4B 4C4D4E4F
                 ", [ @"
-                        00112233 44556677 8899aabb ccddeeff
-                        deaddada deaddada ffeeddcc bbaa9988
+                        00112233 44556677 8899AABB CCDDEEFF
+                        DEADDADA DEADDADA FFEEDDCC BBAA9988
                         77665544 33221100
                     ", @"
-                        10203040 50607080 90a0
+                        10203040 50607080 90A0
                 " ], @"
-                    09f91102 9d74e35b d84156c5 635688c0
+                    09F91102 9D74E35B D84156C5 635688C0
                 ", @"
-                    74686973 20697320 736f6d65 20706c61
-                    696e7465 78742074 6f20656e 63727970
-                    74207573 696e6720 5349562d 414553
+                    74686973 20697320 736F6D65 20706C61
+                    696E7465 78742074 6F20656E 63727970
+                    74207573 696E6720 5349562D 414553
                 ", @"
-                    7bdb6e3b 432667eb 06f4d14b ff2fbd0f
-                    cb900f2f ddbe4043 26601965 c889bf17
-                    dba77ceb 094fa663 b7a3f748 ba8af829
-                    ea64ad54 4a272e9c 485b62a3 fd5c0d
+                    7BDB6E3B 432667EB 06F4D14B FF2FBD0F
+                    CB900F2F DDBE4043 26601965 C889BF17
+                    DBA77CEB 094FA663 B7A3F748 BA8AF829
+                    EA64AD54 4A272E9C 485B62A3 FD5C0D
                 "
             ),
         };