summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2023-04-13 16:48:27 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2023-04-13 16:48:27 +0700
commita0b75007cc33d1ead75b2aed6439a7b272469bed (patch)
tree4a831e9dfc6fc39b99246610e878bd7917b67c62
parentGuard against null nonce and clone returned nonce (diff)
downloadBouncyCastle.NET-ed25519-a0b75007cc33d1ead75b2aed6439a7b272469bed.tar.xz
Refactoring around AeadParameters and ParametersWIthIV
-rw-r--r--crypto/src/cms/CMSEnvelopedHelper.cs8
-rw-r--r--crypto/src/cms/PasswordRecipientInfoGenerator.cs20
-rw-r--r--crypto/src/cms/PasswordRecipientInformation.cs13
-rw-r--r--crypto/src/crypto/engines/AsconEngine.cs25
-rw-r--r--crypto/src/crypto/modes/CcmBlockCipher.cs2
-rw-r--r--crypto/src/crypto/modes/ChaCha20Poly1305.cs22
-rw-r--r--crypto/src/crypto/modes/EAXBlockCipher.cs24
-rw-r--r--crypto/src/crypto/modes/GCMBlockCipher.cs22
-rw-r--r--crypto/src/crypto/modes/GcmSivBlockCipher.cs35
-rw-r--r--crypto/src/crypto/modes/KCcmBlockCipher.cs43
-rw-r--r--crypto/src/crypto/modes/OCBBlockCipher.cs55
-rw-r--r--crypto/src/crypto/parameters/ParametersWithIV.cs28
12 files changed, 223 insertions, 74 deletions
diff --git a/crypto/src/cms/CMSEnvelopedHelper.cs b/crypto/src/cms/CMSEnvelopedHelper.cs
index 40c5e6868..c623ec677 100644
--- a/crypto/src/cms/CMSEnvelopedHelper.cs
+++ b/crypto/src/cms/CMSEnvelopedHelper.cs
@@ -1,4 +1,7 @@
 using System;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+using System.Buffers;
+#endif
 using System.Collections.Generic;
 using System.IO;
 
@@ -240,7 +243,12 @@ namespace Org.BouncyCastle.Cms
 							|| alg.Equals(CmsEnvelopedGenerator.IdeaCbc)
 							|| alg.Equals(CmsEnvelopedGenerator.Cast5Cbc))
 						{
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+							cipherParameters = ParametersWithIV.Create<byte>(cipherParameters, 8, 0,
+								(bytes, state) => bytes.Fill(state));
+#else
 							cipherParameters = new ParametersWithIV(cipherParameters, new byte[8]);
+#endif
 						}
 					}
 
diff --git a/crypto/src/cms/PasswordRecipientInfoGenerator.cs b/crypto/src/cms/PasswordRecipientInfoGenerator.cs
index 5bbf8f727..1243bea9f 100644
--- a/crypto/src/cms/PasswordRecipientInfoGenerator.cs
+++ b/crypto/src/cms/PasswordRecipientInfoGenerator.cs
@@ -48,17 +48,29 @@ namespace Org.BouncyCastle.Cms
 
 			// Note: In Java build, the IV is automatically generated in JCE layer
 			int ivLength = Platform.StartsWithIgnoreCase(rfc3211WrapperName, "DES") ? 8 : 16;
-			byte[] iv = new byte[ivLength];
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            var parametersWithIV = ParametersWithIV.Create(keyEncryptionKey, ivLength, random,
+                (bytes, random) => random.NextBytes(bytes));
+#else
+            byte[] iv = new byte[ivLength];
 			random.NextBytes(iv);
 
-			ICipherParameters parameters = new ParametersWithIV(keyEncryptionKey, iv);
-			keyWrapper.Init(true, new ParametersWithRandom(parameters, random));
+			var parametersWithIV = new ParametersWithIV(keyEncryptionKey, iv);
+#endif
+
+            keyWrapper.Init(true, new ParametersWithRandom(parametersWithIV, random));
         	Asn1OctetString encryptedKey = new DerOctetString(
 				keyWrapper.Wrap(keyBytes, 0, keyBytes.Length));
 
 			DerSequence seq = new DerSequence(
 				new DerObjectIdentifier(keyEncryptionKeyOID),
-				new DerOctetString(iv));
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                new DerOctetString(parametersWithIV.IV)
+#else
+                new DerOctetString(iv)
+#endif
+            );
 
 			AlgorithmIdentifier keyEncryptionAlgorithm = new AlgorithmIdentifier(
 				PkcsObjectIdentifiers.IdAlgPwriKek, seq);
diff --git a/crypto/src/cms/PasswordRecipientInformation.cs b/crypto/src/cms/PasswordRecipientInformation.cs
index f629caba6..ff4a20d68 100644
--- a/crypto/src/cms/PasswordRecipientInformation.cs
+++ b/crypto/src/cms/PasswordRecipientInformation.cs
@@ -54,12 +54,17 @@ namespace Org.BouncyCastle.Cms
 				string				cName = CmsEnvelopedHelper.Instance.GetRfc3211WrapperName(kekAlgName);
 				IWrapper			keyWrapper = WrapperUtilities.GetWrapper(cName);
 
-				byte[] iv = Asn1OctetString.GetInstance(kekAlgParams[1]).GetOctets();
+                var iv = Asn1OctetString.GetInstance(kekAlgParams[1]);
 
-				ICipherParameters parameters = ((CmsPbeKey)key).GetEncoded(kekAlgName);
-				parameters = new ParametersWithIV(parameters, iv);
+                ICipherParameters parameters = ((CmsPbeKey)key).GetEncoded(kekAlgName);
 
-				keyWrapper.Init(false, parameters);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                parameters = new ParametersWithIV(parameters, iv.GetOctetsSpan());
+#else
+				parameters = new ParametersWithIV(parameters, iv.GetOctets());
+#endif
+
+                keyWrapper.Init(false, parameters);
 
 				KeyParameter sKey = ParameterUtilities.CreateKeyParameter(
 					GetContentAlgorithmName(), keyWrapper.Unwrap(encryptedKey, 0, encryptedKey.Length));
diff --git a/crypto/src/crypto/engines/AsconEngine.cs b/crypto/src/crypto/engines/AsconEngine.cs
index 3ca5ede60..4bda8a02a 100644
--- a/crypto/src/crypto/engines/AsconEngine.cs
+++ b/crypto/src/crypto/engines/AsconEngine.cs
@@ -114,12 +114,20 @@ namespace Org.BouncyCastle.Crypto.Engines
         public void Init(bool forEncryption, ICipherParameters parameters)
         {
             KeyParameter key;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            ReadOnlySpan<byte> npub;
+#else
             byte[] npub;
+#endif
 
             if (parameters is AeadParameters aeadParameters)
             {
                 key = aeadParameters.Key;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                npub = aeadParameters.Nonce;
+#else
                 npub = aeadParameters.GetNonce();
+#endif
                 initialAssociatedText = aeadParameters.GetAssociatedText();
 
                 int macSizeBits = aeadParameters.MacSize;
@@ -129,7 +137,11 @@ namespace Org.BouncyCastle.Crypto.Engines
             else if (parameters is ParametersWithIV withIV)
             {
                 key = withIV.Parameters as KeyParameter;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                npub = withIV.IV;
+#else
                 npub = withIV.GetIV();
+#endif
                 initialAssociatedText = null;
             }
             else
@@ -139,7 +151,7 @@ namespace Org.BouncyCastle.Crypto.Engines
 
             if (key == null)
                 throw new ArgumentException("Ascon Init parameters must include a key");
-            if (npub == null || npub.Length != CRYPTO_ABYTES)
+            if (npub.Length != CRYPTO_ABYTES)
                 throw new ArgumentException(asconParameters + " requires exactly " + CRYPTO_ABYTES + " bytes of IV");
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
@@ -156,25 +168,14 @@ namespace Org.BouncyCastle.Crypto.Engines
 
             if (CRYPTO_KEYBYTES == 16)
             {
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-                K1 = Pack.BE_To_UInt64(k);
-                K2 = Pack.BE_To_UInt64(k[8..]);
-#else
                 K1 = Pack.BE_To_UInt64(k, 0);
                 K2 = Pack.BE_To_UInt64(k, 8);
-#endif
             }
             else if (CRYPTO_KEYBYTES == 20)
             {
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-                K0 = Pack.BE_To_UInt32(k);
-                K1 = Pack.BE_To_UInt64(k[4..]);
-                K2 = Pack.BE_To_UInt64(k[12..]);
-#else
                 K0 = Pack.BE_To_UInt32(k, 0);
                 K1 = Pack.BE_To_UInt64(k, 4);
                 K2 = Pack.BE_To_UInt64(k, 12);
-#endif
             }
             else
             {
diff --git a/crypto/src/crypto/modes/CcmBlockCipher.cs b/crypto/src/crypto/modes/CcmBlockCipher.cs
index 7784e7cca..b7d90298a 100644
--- a/crypto/src/crypto/modes/CcmBlockCipher.cs
+++ b/crypto/src/crypto/modes/CcmBlockCipher.cs
@@ -81,7 +81,7 @@ namespace Org.BouncyCastle.Crypto.Modes
                 keyParam = cipherParameters;
             }
 
-            if (nonce == null || nonce.Length < 7 || nonce.Length > 13)
+            if (nonce.Length < 7 || nonce.Length > 13)
                 throw new ArgumentException("nonce must have length from 7 to 13 octets");
 
             Reset();
diff --git a/crypto/src/crypto/modes/ChaCha20Poly1305.cs b/crypto/src/crypto/modes/ChaCha20Poly1305.cs
index 01bf6ccb8..56bef5e93 100644
--- a/crypto/src/crypto/modes/ChaCha20Poly1305.cs
+++ b/crypto/src/crypto/modes/ChaCha20Poly1305.cs
@@ -72,7 +72,11 @@ namespace Org.BouncyCastle.Crypto.Modes
         public virtual void Init(bool forEncryption, ICipherParameters parameters)
         {
             KeyParameter initKeyParam;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            ReadOnlySpan<byte> initNonce;
+#else
             byte[] initNonce;
+#endif
             ICipherParameters chacha20Params;
 
             if (parameters is AeadParameters aeadParams)
@@ -82,7 +86,11 @@ namespace Org.BouncyCastle.Crypto.Modes
                     throw new ArgumentException("Invalid value for MAC size: " + macSizeBits);
 
                 initKeyParam = aeadParams.Key;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                initNonce = aeadParams.Nonce;
+#else
                 initNonce = aeadParams.GetNonce();
+#endif
                 chacha20Params = new ParametersWithIV(initKeyParam, initNonce);
 
                 this.mInitialAad = aeadParams.GetAssociatedText();
@@ -90,7 +98,11 @@ namespace Org.BouncyCastle.Crypto.Modes
             else if (parameters is ParametersWithIV ivParams)
             {
                 initKeyParam = (KeyParameter)ivParams.Parameters;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                initNonce = ivParams.IV;
+#else
                 initNonce = ivParams.GetIV();
+#endif
                 chacha20Params = ivParams;
 
                 this.mInitialAad = null;
@@ -113,11 +125,15 @@ namespace Org.BouncyCastle.Crypto.Modes
             }
 
             // Validate nonce
-            if (null == initNonce || NonceSize != initNonce.Length)
+            if (NonceSize != initNonce.Length)
                 throw new ArgumentException("Nonce must be 96 bits");
 
             // Check for encryption with reused nonce
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            if (State.Uninitialized != mState && forEncryption && initNonce.SequenceEqual(mNonce))
+#else
             if (State.Uninitialized != mState && forEncryption && Arrays.AreEqual(mNonce, initNonce))
+#endif
             {
                 if (null == initKeyParam || initKeyParam.FixedTimeEquals(mKey))
                     throw new ArgumentException("cannot reuse nonce for ChaCha20Poly1305 encryption");
@@ -128,7 +144,11 @@ namespace Org.BouncyCastle.Crypto.Modes
                 initKeyParam.CopyTo(mKey, 0, KeySize);
             }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            initNonce.CopyTo(mNonce);
+#else
             Array.Copy(initNonce, 0, mNonce, 0, NonceSize);
+#endif
 
             mChacha20.Init(true, chacha20Params);
 
diff --git a/crypto/src/crypto/modes/EAXBlockCipher.cs b/crypto/src/crypto/modes/EAXBlockCipher.cs
index 3b95cd7f2..858786d10 100644
--- a/crypto/src/crypto/modes/EAXBlockCipher.cs
+++ b/crypto/src/crypto/modes/EAXBlockCipher.cs
@@ -72,19 +72,31 @@ namespace Org.BouncyCastle.Crypto.Modes
 		{
 			this.forEncryption = forEncryption;
 
-			byte[] nonce;
-			ICipherParameters keyParam;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            ReadOnlySpan<byte> nonce;
+#else
+            byte[] nonce;
+#endif
+            ICipherParameters keyParam;
 
 			if (parameters is AeadParameters aeadParameters)
 			{
-				nonce = aeadParameters.GetNonce();
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                nonce = aeadParameters.Nonce;
+#else
+                nonce = aeadParameters.GetNonce();
+#endif
                 initialAssociatedText = aeadParameters.GetAssociatedText();
 				macSize = aeadParameters.MacSize / 8;
 				keyParam = aeadParameters.Key;
 			}
 			else if (parameters is ParametersWithIV parametersWithIV)
 			{
-				nonce = parametersWithIV.GetIV();
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                nonce = parametersWithIV.IV;
+#else
+                nonce = parametersWithIV.GetIV();
+#endif
                 initialAssociatedText = null;
 				macSize = mac.GetMacSize() / 2;
 				keyParam = parametersWithIV.Parameters;
@@ -103,7 +115,11 @@ namespace Org.BouncyCastle.Crypto.Modes
 
             tag[blockSize - 1] = (byte)Tag.N;
             mac.BlockUpdate(tag, 0, blockSize);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            mac.BlockUpdate(nonce);
+#else
             mac.BlockUpdate(nonce, 0, nonce.Length);
+#endif
             mac.DoFinal(nonceMac, 0);
 
             // Same BlockCipher underlies this and the mac, so reuse last key on cipher
diff --git a/crypto/src/crypto/modes/GCMBlockCipher.cs b/crypto/src/crypto/modes/GCMBlockCipher.cs
index fef980091..c592c3af3 100644
--- a/crypto/src/crypto/modes/GCMBlockCipher.cs
+++ b/crypto/src/crypto/modes/GCMBlockCipher.cs
@@ -117,11 +117,19 @@ namespace Org.BouncyCastle.Crypto.Modes
             this.initialised = true;
 
             KeyParameter keyParam;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            ReadOnlySpan<byte> newNonce;
+#else
             byte[] newNonce;
+#endif
 
             if (parameters is AeadParameters aeadParameters)
             {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                newNonce = aeadParameters.Nonce;
+#else
                 newNonce = aeadParameters.GetNonce();
+#endif
                 initialAssociatedText = aeadParameters.GetAssociatedText();
 
                 int macSizeBits = aeadParameters.MacSize;
@@ -133,7 +141,11 @@ namespace Org.BouncyCastle.Crypto.Modes
             }
             else if (parameters is ParametersWithIV withIV)
             {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                newNonce = withIV.IV;
+#else
                 newNonce = withIV.GetIV();
+#endif
                 initialAssociatedText = null;
                 macSize = 16;
                 keyParam = (KeyParameter)withIV.Parameters;
@@ -146,12 +158,16 @@ namespace Org.BouncyCastle.Crypto.Modes
             int bufLength = forEncryption ? BlockSize : (BlockSize + macSize);
             this.bufBlock = new byte[bufLength];
 
-            if (newNonce == null || newNonce.Length < 1)
+            if (newNonce.Length < 1)
                 throw new ArgumentException("IV must be at least 1 byte");
 
             if (forEncryption)
             {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                if (nonce != null && newNonce.SequenceEqual(nonce))
+#else
                 if (nonce != null && Arrays.AreEqual(nonce, newNonce))
+#endif
                 {
                     if (keyParam == null)
                         throw new ArgumentException("cannot reuse nonce for GCM encryption");
@@ -161,7 +177,11 @@ namespace Org.BouncyCastle.Crypto.Modes
                 }
             }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            nonce = newNonce.ToArray();
+#else
             nonce = newNonce;
+#endif
             if (keyParam != null)
             {
                 lastKey = keyParam.GetKey();
diff --git a/crypto/src/crypto/modes/GcmSivBlockCipher.cs b/crypto/src/crypto/modes/GcmSivBlockCipher.cs
index e11411433..d58fc0e65 100644
--- a/crypto/src/crypto/modes/GcmSivBlockCipher.cs
+++ b/crypto/src/crypto/modes/GcmSivBlockCipher.cs
@@ -176,21 +176,31 @@ namespace Org.BouncyCastle.Crypto.Modes
         {
             /* Set defaults */
             byte[] myInitialAEAD = null;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            ReadOnlySpan<byte> myNonce;
+#else
             byte[] myNonce;
+#endif
             KeyParameter myKey;
 
             /* Access parameters */
-            if (cipherParameters is AeadParameters)
+            if (cipherParameters is AeadParameters myAEAD)
             {
-                AeadParameters myAEAD = (AeadParameters)cipherParameters;
                 myInitialAEAD = myAEAD.GetAssociatedText();
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                myNonce = myAEAD.Nonce;
+#else
                 myNonce = myAEAD.GetNonce();
+#endif
                 myKey = myAEAD.Key;
             }
-            else if (cipherParameters is ParametersWithIV)
+            else if (cipherParameters is ParametersWithIV myParms)
             {
-                ParametersWithIV myParms = (ParametersWithIV)cipherParameters;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                myNonce = myParms.IV;
+#else
                 myNonce = myParms.GetIV();
+#endif
                 myKey = (KeyParameter)myParms.Parameters;
             }
             else
@@ -199,28 +209,25 @@ namespace Org.BouncyCastle.Crypto.Modes
             }
 
             /* Check nonceSize */
-            if (myNonce == null || myNonce.Length != NONCELEN)
-            {
+            if (myNonce.Length != NONCELEN)
                 throw new ArgumentException("Invalid nonce");
-            }
 
             /* Check keysize */
             if (myKey == null)
-            {
                 throw new ArgumentException("Invalid key");
-            }
 
-            byte[] k = myKey.GetKey();
-
-            if (k.Length != BUFLEN && k.Length != (BUFLEN << 1))
-            {
+            int keyLength = myKey.KeyLength;
+            if (keyLength != BUFLEN && keyLength != (BUFLEN << 1))
                 throw new ArgumentException("Invalid key");
-            }
 
             /* Reset details */
             forEncryption = pEncrypt;
             theInitialAEAD = myInitialAEAD;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            theNonce = myNonce.ToArray();
+#else
             theNonce = myNonce;
+#endif
 
             /* Initialise the keys */
             DeriveKeys(myKey);
diff --git a/crypto/src/crypto/modes/KCcmBlockCipher.cs b/crypto/src/crypto/modes/KCcmBlockCipher.cs
index c13c41543..6afb617b5 100644
--- a/crypto/src/crypto/modes/KCcmBlockCipher.cs
+++ b/crypto/src/crypto/modes/KCcmBlockCipher.cs
@@ -42,7 +42,7 @@ namespace Org.BouncyCastle.Crypto.Modes
         */
         private int Nb_ = 4;
 
-        private void setNb(int Nb)
+        private void SetNb(int Nb)
         {
             if (Nb == 4 || Nb == 6 || Nb == 8)
             {
@@ -85,38 +85,32 @@ namespace Org.BouncyCastle.Crypto.Modes
             this.buffer = new byte[engine.GetBlockSize()];
             this.s = new byte[engine.GetBlockSize()];
             this.counter = new byte[engine.GetBlockSize()];
-            setNb(Nb);
+            SetNb(Nb);
         }
 
         public virtual void Init(bool forEncryption, ICipherParameters parameters)
         {
-
-                ICipherParameters cipherParameters;
-            if (parameters is AeadParameters)
+            ICipherParameters cipherParameters;
+            if (parameters is AeadParameters param)
             {
+                if (param.MacSize > MAX_MAC_BIT_LENGTH || param.MacSize < MIN_MAC_BIT_LENGTH || param.MacSize % 8 != 0)
+                    throw new ArgumentException("Invalid mac size specified");
 
-                    AeadParameters param = (AeadParameters)parameters;
-
-                    if (param.MacSize > MAX_MAC_BIT_LENGTH || param.MacSize < MIN_MAC_BIT_LENGTH || param.MacSize % 8 != 0)
-                    {
-                        throw new ArgumentException("Invalid mac size specified");
-                    }
-
-                    nonce = param.GetNonce();
-                    macSize = param.MacSize / BITS_IN_BYTE;
-                    initialAssociatedText = param.GetAssociatedText();
-                    cipherParameters = param.Key;
+                nonce = param.GetNonce();
+                macSize = param.MacSize / BITS_IN_BYTE;
+                initialAssociatedText = param.GetAssociatedText();
+                cipherParameters = param.Key;
             }
-            else if (parameters is ParametersWithIV)
+            else if (parameters is ParametersWithIV paramsWithIV)
             {
-                    nonce = ((ParametersWithIV)parameters).GetIV();
-                    macSize = engine.GetBlockSize(); // use default blockSize for MAC if it is not specified
-                    initialAssociatedText = null;
-                    cipherParameters = ((ParametersWithIV)parameters).Parameters;
+                nonce = paramsWithIV.GetIV();
+                macSize = engine.GetBlockSize(); // use default blockSize for MAC if it is not specified
+                initialAssociatedText = null;
+                cipherParameters = paramsWithIV.Parameters;
             }
             else
             {
-                    throw new ArgumentException("Invalid parameters specified");
+                throw new ArgumentException("Invalid parameters specified");
             }
 
             this.mac = new byte[macSize];
@@ -133,10 +127,7 @@ namespace Org.BouncyCastle.Crypto.Modes
 
         public virtual string AlgorithmName => engine.AlgorithmName + "/KCCM";
 
-        public virtual int GetBlockSize()
-        {
-            return engine.GetBlockSize();
-        }
+        public virtual int GetBlockSize() => engine.GetBlockSize();
 
         public virtual IBlockCipher UnderlyingCipher => engine;
 
diff --git a/crypto/src/crypto/modes/OCBBlockCipher.cs b/crypto/src/crypto/modes/OCBBlockCipher.cs
index ce86780c5..c0bca2681 100644
--- a/crypto/src/crypto/modes/OCBBlockCipher.cs
+++ b/crypto/src/crypto/modes/OCBBlockCipher.cs
@@ -93,10 +93,18 @@ namespace Org.BouncyCastle.Crypto.Modes
 
             KeyParameter keyParameter;
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            ReadOnlySpan<byte> N;
+#else
             byte[] N;
+#endif
             if (parameters is AeadParameters aeadParameters)
             {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                N = aeadParameters.Nonce;
+#else
                 N = aeadParameters.GetNonce();
+#endif
                 initialAssociatedText = aeadParameters.GetAssociatedText();
 
                 int macSizeBits = aeadParameters.MacSize;
@@ -108,7 +116,11 @@ namespace Org.BouncyCastle.Crypto.Modes
             }
             else if (parameters is ParametersWithIV parametersWithIV)
             {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                N = parametersWithIV.IV;
+#else
                 N = parametersWithIV.GetIV();
+#endif
                 initialAssociatedText = null;
                 macSize = 16;
                 keyParameter = (KeyParameter) parametersWithIV.Parameters;
@@ -121,15 +133,8 @@ namespace Org.BouncyCastle.Crypto.Modes
             this.hashBlock = new byte[16];
             this.mainBlock = new byte[forEncryption ? BLOCK_SIZE : (BLOCK_SIZE + macSize)];
 
-            if (N == null)
-            {
-                N = new byte[0];
-            }
-
             if (N.Length > 15)
-            {
                 throw new ArgumentException("IV must be no more than 15 bytes");
-            }
 
             /*
              * KEY-DEPENDENT INITIALISATION
@@ -221,6 +226,42 @@ namespace Org.BouncyCastle.Crypto.Modes
             return bottom;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        private int ProcessNonce(ReadOnlySpan<byte> N)
+        {
+            // TODO[api] Redesign to avoid this exceptional case
+            // Avoid problems if ProcessNonce() was overridden before this method even existed.
+            if (GetType() != typeof(OcbBlockCipher))
+                return ProcessNonce(N.ToArray());
+
+            Span<byte> nonce = stackalloc byte[16];
+            N.CopyTo(nonce[^N.Length..]);
+            nonce[0] = (byte)(macSize << 4);
+            nonce[15 - N.Length] |= 1;
+
+            int bottom = nonce[15] & 0x3F;
+            nonce[15] &= 0xC0;
+
+            /*
+             * When used with incrementing nonces, the cipher is only applied once every 64 inits.
+             */
+            if (KtopInput == null || !nonce.SequenceEqual(KtopInput))
+            {
+                KtopInput = nonce.ToArray();
+
+                Span<byte> Ktop = stackalloc byte[16];
+                hashCipher.ProcessBlock(KtopInput, Ktop);
+                Ktop.CopyTo(Stretch);
+                for (int i = 0; i < 8; ++i)
+                {
+                    Stretch[16 + i] = (byte)(Ktop[i] ^ Ktop[i + 1]);
+                }
+            }
+
+            return bottom;
+        }
+#endif
+
         public virtual int GetBlockSize()
         {
             return BLOCK_SIZE;
diff --git a/crypto/src/crypto/parameters/ParametersWithIV.cs b/crypto/src/crypto/parameters/ParametersWithIV.cs
index e642e828a..c5f04aab1 100644
--- a/crypto/src/crypto/parameters/ParametersWithIV.cs
+++ b/crypto/src/crypto/parameters/ParametersWithIV.cs
@@ -1,10 +1,28 @@
 using System;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+using System.Buffers;
+#endif
 
 namespace Org.BouncyCastle.Crypto.Parameters
 {
     public class ParametersWithIV
         : ICipherParameters
     {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public static ParametersWithIV Create<TState>(ICipherParameters parameter, int ivLength, TState state,
+            SpanAction<byte, TState> action)
+        {
+            if (action == null)
+                throw new ArgumentNullException(nameof(action));
+            if (ivLength < 0)
+                throw new ArgumentOutOfRangeException(nameof(ivLength));
+
+            ParametersWithIV result = new ParametersWithIV(parameter, ivLength);
+            action(result.m_iv, state);
+            return result;
+        }
+#endif
+
         internal static ICipherParameters ApplyOptionalIV(ICipherParameters parameters, byte[] iv)
         {
             return iv == null ? parameters : new ParametersWithIV(parameters, iv);
@@ -44,6 +62,16 @@ namespace Org.BouncyCastle.Crypto.Parameters
         }
 #endif
 
+        private ParametersWithIV(ICipherParameters parameters, int ivLength)
+        {
+            if (ivLength < 0)
+                throw new ArgumentOutOfRangeException(nameof(ivLength));
+
+            // NOTE: 'parameters' may be null to imply key re-use
+            m_parameters = parameters;
+            m_iv = new byte[ivLength];
+        }
+
         public byte[] GetIV()
         {
             return (byte[])m_iv.Clone();