summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2023-04-12 20:07:41 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2023-04-12 20:07:41 +0700
commit8df051272b46c358f8edb4cea86b2298ee696deb (patch)
tree80e109791f9307e360ec50872c63a64a07df126b
parentUse KeyParameter.KeyLength (diff)
downloadBouncyCastle.NET-ed25519-8df051272b46c358f8edb4cea86b2298ee696deb.tar.xz
Refactoring around KeyParameter
-rw-r--r--crypto/src/crypto/CipherKeyGenerator.cs54
-rw-r--r--crypto/src/crypto/engines/AesEngine.cs10
-rw-r--r--crypto/src/crypto/engines/AesLightEngine.cs10
-rw-r--r--crypto/src/crypto/engines/RijndaelEngine.cs27
-rw-r--r--crypto/src/crypto/fpe/FpeFf3_1Engine.cs15
-rw-r--r--crypto/src/crypto/generators/DesEdeKeyGenerator.cs15
-rw-r--r--crypto/src/crypto/generators/DesKeyGenerator.cs15
-rw-r--r--crypto/src/crypto/generators/Poly1305KeyGenerator.cs65
-rw-r--r--crypto/src/crypto/macs/Poly1305.cs47
-rw-r--r--crypto/src/crypto/modes/ChaCha20Poly1305.cs12
-rw-r--r--crypto/src/crypto/modes/GCMBlockCipher.cs2
-rw-r--r--crypto/src/crypto/operators/Asn1CipherBuilder.cs2
-rw-r--r--crypto/src/crypto/parameters/DesEdeParameters.cs53
-rw-r--r--crypto/src/crypto/parameters/DesParameters.cs41
-rw-r--r--crypto/src/crypto/parameters/KeyParameter.cs40
-rw-r--r--crypto/src/crypto/util/Pack.cs6
16 files changed, 326 insertions, 88 deletions
diff --git a/crypto/src/crypto/CipherKeyGenerator.cs b/crypto/src/crypto/CipherKeyGenerator.cs
index 2d5d8c2e0..fcf8c324a 100644
--- a/crypto/src/crypto/CipherKeyGenerator.cs
+++ b/crypto/src/crypto/CipherKeyGenerator.cs
@@ -1,13 +1,14 @@
 using System;
 
+using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Security;
 
 namespace Org.BouncyCastle.Crypto
 {
-	/**
+    /**
 	 * The base class for symmetric, or secret, cipher key generators.
 	 */
-	public class CipherKeyGenerator
+    public class CipherKeyGenerator
 	{
 		protected internal SecureRandom	random;
 		protected internal int			strength;
@@ -60,22 +61,51 @@ namespace Org.BouncyCastle.Crypto
 		 */
 		public byte[] GenerateKey()
 		{
-			if (uninitialised)
-			{
-				if (defaultStrength < 1)
-					throw new InvalidOperationException("Generator has not been initialised");
-
-				uninitialised = false;
-
-				EngineInit(new KeyGenerationParameters(CryptoServicesRegistrar.GetSecureRandom(), defaultStrength));
-			}
+			EnsureInitialized();
 
 			return EngineGenerateKey();
 		}
 
+		public KeyParameter GenerateKeyParameter()
+		{
+            EnsureInitialized();
+
+			return EngineGenerateKeyParameter();
+        }
+
         protected virtual byte[] EngineGenerateKey()
 		{
             return SecureRandom.GetNextBytes(random, strength);
 		}
-	}
+
+        protected virtual KeyParameter EngineGenerateKeyParameter()
+        {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            // TODO[api] Redesign to avoid this exceptional case
+            // Avoid problems if EngineGenerateKey() was overridden before this method even existed.
+            if (GetType() == typeof(CipherKeyGenerator))
+			{
+                return KeyParameter.Create(strength, random, (bytes, random) =>
+                {
+                    random.NextBytes(bytes);
+                });
+            }
+#endif
+
+			return new KeyParameter(EngineGenerateKey());
+        }
+
+        protected virtual void EnsureInitialized()
+		{
+            if (uninitialised)
+            {
+                if (defaultStrength < 1)
+                    throw new InvalidOperationException("Generator has not been initialised");
+
+                uninitialised = false;
+
+                EngineInit(new KeyGenerationParameters(CryptoServicesRegistrar.GetSecureRandom(), defaultStrength));
+            }
+        }
+    }
 }
diff --git a/crypto/src/crypto/engines/AesEngine.cs b/crypto/src/crypto/engines/AesEngine.cs
index 3977cb893..0c69969f6 100644
--- a/crypto/src/crypto/engines/AesEngine.cs
+++ b/crypto/src/crypto/engines/AesEngine.cs
@@ -288,8 +288,14 @@ namespace Org.BouncyCastle.Crypto.Engines
         * AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits
         * This code is written assuming those are the only possible values
         */
-        private uint[][] GenerateWorkingKey(byte[] key, bool forEncryption)
+        private uint[][] GenerateWorkingKey(KeyParameter keyParameter, bool forEncryption)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            var key = keyParameter.Key;
+#else
+            byte[] key = keyParameter.GetKey();
+#endif
+
             int keyLen = key.Length;
             if (keyLen < 16 || keyLen > 32 || (keyLen & 7) != 0)
                 throw new ArgumentException("Key length not 128/192/256 bits.");
@@ -452,7 +458,7 @@ namespace Org.BouncyCastle.Crypto.Engines
                 throw new ArgumentException("invalid parameter passed to AES init - "
                     + Platform.GetTypeName(parameters));
 
-            WorkingKey = GenerateWorkingKey(keyParameter.GetKey(), forEncryption);
+            WorkingKey = GenerateWorkingKey(keyParameter, forEncryption);
 
             this.forEncryption = forEncryption;
             this.s = Arrays.Clone(forEncryption ? S : Si);
diff --git a/crypto/src/crypto/engines/AesLightEngine.cs b/crypto/src/crypto/engines/AesLightEngine.cs
index 8d76f4388..42aa74420 100644
--- a/crypto/src/crypto/engines/AesLightEngine.cs
+++ b/crypto/src/crypto/engines/AesLightEngine.cs
@@ -186,8 +186,14 @@ namespace Org.BouncyCastle.Crypto.Engines
         * AES specified a fixed block size of 128 bits and key sizes 128/192/256 bits
         * This code is written assuming those are the only possible values
         */
-        private uint[][] GenerateWorkingKey(byte[] key, bool forEncryption)
+        private uint[][] GenerateWorkingKey(KeyParameter keyParameter, bool forEncryption)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            var key = keyParameter.Key;
+#else
+            byte[] key = keyParameter.GetKey();
+#endif
+
             int keyLen = key.Length;
             if (keyLen < 16 || keyLen > 32 || (keyLen & 7) != 0)
                 throw new ArgumentException("Key length not 128/192/256 bits.");
@@ -348,7 +354,7 @@ namespace Org.BouncyCastle.Crypto.Engines
                 throw new ArgumentException("invalid parameter passed to AES init - "
                     + Platform.GetTypeName(parameters));
 
-            WorkingKey = GenerateWorkingKey(keyParameter.GetKey(), forEncryption);
+            WorkingKey = GenerateWorkingKey(keyParameter, forEncryption);
 
             this.forEncryption = forEncryption;
         }
diff --git a/crypto/src/crypto/engines/RijndaelEngine.cs b/crypto/src/crypto/engines/RijndaelEngine.cs
index 422664ce0..81aa012e5 100644
--- a/crypto/src/crypto/engines/RijndaelEngine.cs
+++ b/crypto/src/crypto/engines/RijndaelEngine.cs
@@ -379,10 +379,15 @@ namespace Org.BouncyCastle.Crypto.Engines
 		* Calculate the necessary round keys
 		* The number of calculations depends on keyBits and blockBits
 		*/
-		private long[][] GenerateWorkingKey(
-			byte[]      key)
+		private long[][] GenerateWorkingKey(KeyParameter keyParameter)
 		{
-			int         KC;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            var key = keyParameter.Key;
+#else
+            byte[] key = keyParameter.GetKey();
+#endif
+
+            int KC;
 			int         t, rconpointer = 0;
 			int         keyBits = key.Length * 8;
 			byte[,]    tk = new byte[4,MAXKC];
@@ -572,18 +577,14 @@ namespace Org.BouncyCastle.Crypto.Engines
 		* @exception ArgumentException if the parameters argument is
 		* inappropriate.
 		*/
-        public virtual void Init(
-			bool           forEncryption,
-			ICipherParameters  parameters)
+        public virtual void Init(bool forEncryption, ICipherParameters parameters)
 		{
-			if (typeof(KeyParameter).IsInstanceOfType(parameters))
-			{
-				workingKey = GenerateWorkingKey(((KeyParameter)parameters).GetKey());
-				this.forEncryption = forEncryption;
-				return;
-			}
+            if (!(parameters is KeyParameter keyParameter))
+                throw new ArgumentException("invalid parameter passed to Rijndael init - "
+					+ Platform.GetTypeName(parameters));
 
-			throw new ArgumentException("invalid parameter passed to Rijndael init - " + Platform.GetTypeName(parameters));
+			this.workingKey = GenerateWorkingKey(keyParameter);
+			this.forEncryption = forEncryption;
 		}
 
         public virtual string AlgorithmName
diff --git a/crypto/src/crypto/fpe/FpeFf3_1Engine.cs b/crypto/src/crypto/fpe/FpeFf3_1Engine.cs
index 71203aecf..bc8d22fae 100644
--- a/crypto/src/crypto/fpe/FpeFf3_1Engine.cs
+++ b/crypto/src/crypto/fpe/FpeFf3_1Engine.cs
@@ -29,7 +29,7 @@ namespace Org.BouncyCastle.Crypto.Fpe
             this.forEncryption = forEncryption;
             this.fpeParameters = (FpeParameters)parameters;
 
-            baseCipher.Init(!fpeParameters.UseInverseFunction, new KeyParameter(Arrays.Reverse(fpeParameters.Key.GetKey())));
+            baseCipher.Init(!fpeParameters.UseInverseFunction, ReverseKey(fpeParameters.Key));
 
             if (fpeParameters.GetTweak().Length != 7)
                 throw new ArgumentException("tweak should be 56 bits");
@@ -82,5 +82,18 @@ namespace Org.BouncyCastle.Crypto.Fpe
 
             return length;
         }
+
+        private static KeyParameter ReverseKey(KeyParameter key)
+        {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return KeyParameter.Create(key.KeyLength, key, (bytes, key) =>
+            {
+                key.Key.CopyTo(bytes);
+                bytes.Reverse();
+            });
+#else
+            return new KeyParameter(Arrays.Reverse(key.GetKey()));
+#endif
+        }
     }
 }
diff --git a/crypto/src/crypto/generators/DesEdeKeyGenerator.cs b/crypto/src/crypto/generators/DesEdeKeyGenerator.cs
index 24197e9da..9e14702d1 100644
--- a/crypto/src/crypto/generators/DesEdeKeyGenerator.cs
+++ b/crypto/src/crypto/generators/DesEdeKeyGenerator.cs
@@ -62,5 +62,20 @@ namespace Org.BouncyCastle.Crypto.Generators
 
             return newKey;
         }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        protected override KeyParameter EngineGenerateKeyParameter()
+        {
+            return KeyParameter.Create(strength, random, (bytes, random) =>
+            {
+                do
+                {
+                    random.NextBytes(bytes);
+                    DesEdeParameters.SetOddParity(bytes);
+                }
+                while (DesEdeParameters.IsWeakKey(bytes) || !DesEdeParameters.IsRealEdeKey(bytes));
+            });
+        }
+#endif
     }
 }
diff --git a/crypto/src/crypto/generators/DesKeyGenerator.cs b/crypto/src/crypto/generators/DesKeyGenerator.cs
index 5a1befd71..7a5a40c5c 100644
--- a/crypto/src/crypto/generators/DesKeyGenerator.cs
+++ b/crypto/src/crypto/generators/DesKeyGenerator.cs
@@ -52,5 +52,20 @@ namespace Org.BouncyCastle.Crypto.Generators
 
 			return newKey;
         }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        protected override KeyParameter EngineGenerateKeyParameter()
+        {
+            return KeyParameter.Create(strength, random, (bytes, random) =>
+            {
+                do
+                {
+                    random.NextBytes(bytes);
+                    DesParameters.SetOddParity(bytes);
+                }
+                while (DesParameters.IsWeakKey(bytes));
+            });
+        }
+#endif
     }
 }
diff --git a/crypto/src/crypto/generators/Poly1305KeyGenerator.cs b/crypto/src/crypto/generators/Poly1305KeyGenerator.cs
index d7827fea9..a84807a76 100644
--- a/crypto/src/crypto/generators/Poly1305KeyGenerator.cs
+++ b/crypto/src/crypto/generators/Poly1305KeyGenerator.cs
@@ -50,18 +50,32 @@ namespace Org.BouncyCastle.Crypto.Generators
 			return key;
 		}
 
-		/// <summary>
-		/// Modifies an existing 32 byte key value to comply with the requirements of the Poly1305 key by
-		/// clearing required bits in the <code>r</code> (second 16 bytes) portion of the key.<br/>
-		/// Specifically:
-		/// <ul>
-		/// <li>r[3], r[7], r[11], r[15] have top four bits clear (i.e., are {0, 1, . . . , 15})</li>
-		/// <li>r[4], r[8], r[12] have bottom two bits clear (i.e., are in {0, 4, 8, . . . , 252})</li>
-		/// </ul>
-		/// </summary>
-		/// <param name="key">a 32 byte key value <code>k[0] ... k[15], r[0] ... r[15]</code></param>
-		public static void Clamp(byte[] key)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        protected override KeyParameter EngineGenerateKeyParameter()
+        {
+            return KeyParameter.Create(strength, random, (bytes, random) =>
+            {
+                random.NextBytes(bytes);
+				Clamp(bytes);
+            });
+        }
+#endif
+
+        /// <summary>
+        /// Modifies an existing 32 byte key value to comply with the requirements of the Poly1305 key by
+        /// clearing required bits in the <code>r</code> (second 16 bytes) portion of the key.<br/>
+        /// Specifically:
+        /// <ul>
+        /// <li>r[3], r[7], r[11], r[15] have top four bits clear (i.e., are {0, 1, . . . , 15})</li>
+        /// <li>r[4], r[8], r[12] have bottom two bits clear (i.e., are in {0, 4, 8, . . . , 252})</li>
+        /// </ul>
+        /// </summary>
+        /// <param name="key">a 32 byte key value <code>k[0] ... k[15], r[0] ... r[15]</code></param>
+        public static void Clamp(byte[] key)
 		{
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			Clamp(key.AsSpan());
+#else
 			/*
 	         * Key is k[0] ... k[15], r[0] ... r[15] as per poly1305_aes_clamp in ref impl.
 	         */
@@ -82,9 +96,36 @@ namespace Org.BouncyCastle.Crypto.Generators
 			key[4] &= R_MASK_LOW_2;
 			key[8] &= R_MASK_LOW_2;
 			key[12] &= R_MASK_LOW_2;
+#endif
 		}
 
-        /// <summary>
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public static void Clamp(Span<byte> key)
+        {
+            /*
+	         * Key is k[0] ... k[15], r[0] ... r[15] as per poly1305_aes_clamp in ref impl.
+	         */
+            if (key.Length != 32)
+                throw new ArgumentException("Poly1305 key must be 256 bits.");
+
+            /*
+	         * r[3], r[7], r[11], r[15] have top four bits clear (i.e., are {0, 1, . . . , 15})
+	         */
+            key[3] &= R_MASK_HIGH_4;
+            key[7] &= R_MASK_HIGH_4;
+            key[11] &= R_MASK_HIGH_4;
+            key[15] &= R_MASK_HIGH_4;
+
+            /*
+	         * r[4], r[8], r[12] have bottom two bits clear (i.e., are in {0, 4, 8, . . . , 252}).
+	         */
+            key[4] &= R_MASK_LOW_2;
+            key[8] &= R_MASK_LOW_2;
+            key[12] &= R_MASK_LOW_2;
+        }
+#endif
+
+		/// <summary>
 		/// Checks a 32 byte key for compliance with the Poly1305 key requirements, e.g.
 		/// <code>k[0] ... k[15], r[0] ... r[15]</code> with the required bits in <code>r</code> cleared
 		/// as per <see cref="Clamp(byte[])"/>.
diff --git a/crypto/src/crypto/macs/Poly1305.cs b/crypto/src/crypto/macs/Poly1305.cs
index adf4975ba..61f13a972 100644
--- a/crypto/src/crypto/macs/Poly1305.cs
+++ b/crypto/src/crypto/macs/Poly1305.cs
@@ -84,26 +84,29 @@ namespace Org.BouncyCastle.Crypto.Macs
 
             if (cipher != null)
             {
-                if (!(parameters is ParametersWithIV))
-                    throw new ArgumentException("Poly1305 requires an IV when used with a block cipher.", "parameters");
+                if (!(parameters is ParametersWithIV ivParams))
+                    throw new ArgumentException("Poly1305 requires an IV when used with a block cipher.", nameof(parameters));
 
-                ParametersWithIV ivParams = (ParametersWithIV)parameters;
                 nonce = ivParams.GetIV();
                 parameters = ivParams.Parameters;
             }
 
-            if (!(parameters is KeyParameter))
+            if (!(parameters is KeyParameter keyParameter))
                 throw new ArgumentException("Poly1305 requires a key.");
 
-            KeyParameter keyParams = (KeyParameter)parameters;
-
-            SetKey(keyParams.GetKey(), nonce);
+            SetKey(keyParameter, nonce);
 
             Reset();
         }
 
-        private void SetKey(byte[] key, byte[] nonce)
+        private void SetKey(KeyParameter keyParameter, byte[] nonce)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            var key = keyParameter.Key;
+#else
+            byte[] key = keyParameter.GetKey();
+#endif
+
             if (key.Length != 32)
                 throw new ArgumentException("Poly1305 key must be 256 bits.");
 
@@ -129,28 +132,32 @@ namespace Org.BouncyCastle.Crypto.Macs
             s3 = r3 * 5;
             s4 = r4 * 5;
 
-            byte[] kBytes;
-            int kOff;
-
             if (cipher == null)
             {
-                kBytes = key;
-                kOff = BlockSize;
+                k0 = Pack.LE_To_UInt32(key, BlockSize + 0);
+                k1 = Pack.LE_To_UInt32(key, BlockSize + 4);
+                k2 = Pack.LE_To_UInt32(key, BlockSize + 8);
+                k3 = Pack.LE_To_UInt32(key, BlockSize + 12);
             }
             else
             {
                 // Compute encrypted nonce
-                kBytes = new byte[BlockSize];
-                kOff = 0;
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                Span<byte> kBytes = stackalloc byte[BlockSize];
+                cipher.Init(true, new KeyParameter(key.Slice(BlockSize, BlockSize)));
+                cipher.ProcessBlock(nonce, kBytes);
+#else
+                byte[] kBytes = new byte[BlockSize];
                 cipher.Init(true, new KeyParameter(key, BlockSize, BlockSize));
                 cipher.ProcessBlock(nonce, 0, kBytes, 0);
-            }
+#endif
 
-            k0 = Pack.LE_To_UInt32(kBytes, kOff + 0);
-            k1 = Pack.LE_To_UInt32(kBytes, kOff + 4);
-            k2 = Pack.LE_To_UInt32(kBytes, kOff + 8);
-            k3 = Pack.LE_To_UInt32(kBytes, kOff + 12);
+                k0 = Pack.LE_To_UInt32(kBytes, 0);
+                k1 = Pack.LE_To_UInt32(kBytes, 4);
+                k2 = Pack.LE_To_UInt32(kBytes, 8);
+                k3 = Pack.LE_To_UInt32(kBytes, 12);
+            }
         }
 
         public string AlgorithmName
diff --git a/crypto/src/crypto/modes/ChaCha20Poly1305.cs b/crypto/src/crypto/modes/ChaCha20Poly1305.cs
index 7e2348932..01bf6ccb8 100644
--- a/crypto/src/crypto/modes/ChaCha20Poly1305.cs
+++ b/crypto/src/crypto/modes/ChaCha20Poly1305.cs
@@ -75,10 +75,8 @@ namespace Org.BouncyCastle.Crypto.Modes
             byte[] initNonce;
             ICipherParameters chacha20Params;
 
-            if (parameters is AeadParameters)
+            if (parameters is AeadParameters aeadParams)
             {
-                AeadParameters aeadParams = (AeadParameters)parameters;
-
                 int macSizeBits = aeadParams.MacSize;
                 if ((MacSize * 8) != macSizeBits)
                     throw new ArgumentException("Invalid value for MAC size: " + macSizeBits);
@@ -89,10 +87,8 @@ namespace Org.BouncyCastle.Crypto.Modes
 
                 this.mInitialAad = aeadParams.GetAssociatedText();
             }
-            else if (parameters is ParametersWithIV)
+            else if (parameters is ParametersWithIV ivParams)
             {
-                ParametersWithIV ivParams = (ParametersWithIV)parameters;
-
                 initKeyParam = (KeyParameter)ivParams.Parameters;
                 initNonce = ivParams.GetIV();
                 chacha20Params = ivParams;
@@ -123,13 +119,13 @@ namespace Org.BouncyCastle.Crypto.Modes
             // Check for encryption with reused nonce
             if (State.Uninitialized != mState && forEncryption && Arrays.AreEqual(mNonce, initNonce))
             {
-                if (null == initKeyParam || Arrays.AreEqual(mKey, initKeyParam.GetKey()))
+                if (null == initKeyParam || initKeyParam.FixedTimeEquals(mKey))
                     throw new ArgumentException("cannot reuse nonce for ChaCha20Poly1305 encryption");
             }
 
             if (null != initKeyParam)
             {
-                Array.Copy(initKeyParam.GetKey(), 0, mKey, 0, KeySize);
+                initKeyParam.CopyTo(mKey, 0, KeySize);
             }
 
             Array.Copy(initNonce, 0, mNonce, 0, NonceSize);
diff --git a/crypto/src/crypto/modes/GCMBlockCipher.cs b/crypto/src/crypto/modes/GCMBlockCipher.cs
index f41214c5d..fef980091 100644
--- a/crypto/src/crypto/modes/GCMBlockCipher.cs
+++ b/crypto/src/crypto/modes/GCMBlockCipher.cs
@@ -156,7 +156,7 @@ namespace Org.BouncyCastle.Crypto.Modes
                     if (keyParam == null)
                         throw new ArgumentException("cannot reuse nonce for GCM encryption");
 
-                    if (lastKey != null && Arrays.AreEqual(lastKey, keyParam.GetKey()))
+                    if (lastKey != null && keyParam.FixedTimeEquals(lastKey))
                         throw new ArgumentException("cannot reuse nonce for GCM encryption");
                 }
             }
diff --git a/crypto/src/crypto/operators/Asn1CipherBuilder.cs b/crypto/src/crypto/operators/Asn1CipherBuilder.cs
index 4f58ef58a..9baca94c2 100644
--- a/crypto/src/crypto/operators/Asn1CipherBuilder.cs
+++ b/crypto/src/crypto/operators/Asn1CipherBuilder.cs
@@ -22,7 +22,7 @@ namespace Org.BouncyCastle.Crypto.Operators
 
             CipherKeyGenerator keyGen = CipherKeyGeneratorFactory.CreateKeyGenerator(encryptionOID, random);
 
-            encKey = new KeyParameter(keyGen.GenerateKey());
+            encKey = keyGen.GenerateKeyParameter();
             algorithmIdentifier = AlgorithmIdentifierFactory.GenerateEncryptionAlgID(encryptionOID, encKey.KeyLength * 8, random);
         }
 
diff --git a/crypto/src/crypto/parameters/DesEdeParameters.cs b/crypto/src/crypto/parameters/DesEdeParameters.cs
index 6be56fb2c..19150c6bb 100644
--- a/crypto/src/crypto/parameters/DesEdeParameters.cs
+++ b/crypto/src/crypto/parameters/DesEdeParameters.cs
@@ -57,17 +57,12 @@ namespace Org.BouncyCastle.Crypto.Parameters
          * @param offset offset into the byte array the key starts at
          * @param length number of bytes making up the key
          */
-        public static bool IsWeakKey(
-            byte[]  key,
-            int     offset,
-            int     length)
+        public static bool IsWeakKey(byte[] key, int offset, int length)
         {
             for (int i = offset; i < length; i += DesKeyLength)
             {
                 if (DesParameters.IsWeakKey(key, i))
-                {
                     return true;
-                }
             }
 
             return false;
@@ -79,9 +74,7 @@ namespace Org.BouncyCastle.Crypto.Parameters
          * @param key bytes making up the key
          * @param offset offset into the byte array the key starts at
          */
-        public static new bool IsWeakKey(
-            byte[]	key,
-            int		offset)
+        public static new bool IsWeakKey(byte[]	key, int offset)
         {
             return IsWeakKey(key, offset, key.Length - offset);
         }
@@ -92,6 +85,19 @@ namespace Org.BouncyCastle.Crypto.Parameters
 			return IsWeakKey(key, 0, key.Length);
 		}
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public static new bool IsWeakKey(ReadOnlySpan<byte> key)
+        {
+            for (int i = 0; i < key.Length; i += DesKeyLength)
+            {
+                if (DesParameters.IsWeakKey(key[i..]))
+                    return true;
+            }
+
+            return false;
+        }
+#endif
+
         /**
          * return true if the passed in key is a real 2/3 part DES-EDE key.
          *
@@ -136,5 +142,34 @@ namespace Org.BouncyCastle.Crypto.Parameters
             }
             return diff12 && diff13 && diff23;
         }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public static bool IsRealEdeKey(ReadOnlySpan<byte> key)
+        {
+            return key.Length == 16 ? IsReal2Key(key) : IsReal3Key(key);
+        }
+
+        public static bool IsReal2Key(ReadOnlySpan<byte> key)
+        {
+            bool isValid = false;
+            for (int i = 0; i != 8; i++)
+            {
+                isValid |= (key[i] != key[i + 8]);
+            }
+            return isValid;
+        }
+
+        public static bool IsReal3Key(ReadOnlySpan<byte> key)
+        {
+            bool diff12 = false, diff13 = false, diff23 = false;
+            for (int i = 0; i != 8; i++)
+            {
+                diff12 |= (key[i] != key[i + 8]);
+                diff13 |= (key[i] != key[i + 16]);
+                diff23 |= (key[i + 8] != key[i + 16]);
+            }
+            return diff12 && diff13 && diff23;
+        }
+#endif
     }
 }
diff --git a/crypto/src/crypto/parameters/DesParameters.cs b/crypto/src/crypto/parameters/DesParameters.cs
index 28881f21c..dd575604c 100644
--- a/crypto/src/crypto/parameters/DesParameters.cs
+++ b/crypto/src/crypto/parameters/DesParameters.cs
@@ -67,10 +67,11 @@ namespace Org.BouncyCastle.Crypto.Parameters
         * @return true if the given DES key material is weak or semi-weak,
         *     false otherwise.
         */
-        public static bool IsWeakKey(
-            byte[]	key,
-            int		offset)
+        public static bool IsWeakKey(byte[]	key, int offset)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return IsWeakKey(key.AsSpan(offset));
+#else
             if (key.Length - offset < DesKeyLength)
                 throw new ArgumentException("key material too short.");
 
@@ -89,20 +90,46 @@ namespace Org.BouncyCastle.Crypto.Parameters
                 }
 
 				if (!unmatch)
-				{
 					return true;
-				}
             }
 
 			return false;
+#endif
         }
 
-		public static bool IsWeakKey(
-			byte[] key)
+        public static bool IsWeakKey(byte[] key)
 		{
 			return IsWeakKey(key, 0);
 		}
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public static bool IsWeakKey(ReadOnlySpan<byte> key)
+        {
+            if (key.Length < DesKeyLength)
+                throw new ArgumentException("key material too short.");
+
+            //nextkey:
+            for (int i = 0; i < N_DES_WEAK_KEYS; i++)
+            {
+                bool unmatch = false;
+                for (int j = 0; j < DesKeyLength; j++)
+                {
+                    if (key[j] != DES_weak_keys[i * DesKeyLength + j])
+                    {
+                        //continue nextkey;
+                        unmatch = true;
+                        break;
+                    }
+                }
+
+                if (!unmatch)
+                    return true;
+            }
+
+            return false;
+        }
+#endif
+
         public static byte SetOddParity(byte b)
         {
             uint parity = b ^ 1U;
diff --git a/crypto/src/crypto/parameters/KeyParameter.cs b/crypto/src/crypto/parameters/KeyParameter.cs
index 7ee47de9a..bd2482f22 100644
--- a/crypto/src/crypto/parameters/KeyParameter.cs
+++ b/crypto/src/crypto/parameters/KeyParameter.cs
@@ -1,10 +1,29 @@
 using System;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+using System.Buffers;
+#endif
+
+using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Parameters
 {
     public class KeyParameter
 		: ICipherParameters
     {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public static KeyParameter Create<TState>(int length, TState state, SpanAction<byte, TState> action)
+        {
+            if (action == null)
+                throw new ArgumentNullException(nameof(action));
+            if (length < 1)
+                throw new ArgumentOutOfRangeException(nameof(length));
+
+            KeyParameter result = new KeyParameter(length);
+            action(result.m_key, state);
+            return result;
+        }
+#endif
+
         private readonly byte[] m_key;
 
 		public KeyParameter(byte[] key)
@@ -35,6 +54,22 @@ namespace Org.BouncyCastle.Crypto.Parameters
         }
 #endif
 
+        private KeyParameter(int length)
+        {
+            if (length < 1)
+                throw new ArgumentOutOfRangeException(nameof(length));
+
+            m_key = new byte[length];
+        }
+
+        internal void CopyTo(byte[] buf, int off, int len)
+        {
+            if (m_key.Length != len)
+                throw new ArgumentOutOfRangeException(nameof(len));
+
+            Array.Copy(m_key, 0, buf, off, len);
+        }
+
         public byte[] GetKey()
         {
 			return (byte[])m_key.Clone();
@@ -42,6 +77,11 @@ namespace Org.BouncyCastle.Crypto.Parameters
 
         public int KeyLength => m_key.Length;
 
+        internal bool FixedTimeEquals(byte[] data)
+        {
+            return Arrays.FixedTimeEquals(m_key, data);
+        }
+
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
         internal ReadOnlySpan<byte> Key => m_key;
 #endif
diff --git a/crypto/src/crypto/util/Pack.cs b/crypto/src/crypto/util/Pack.cs
index cd8010f5a..dc87cf7d8 100644
--- a/crypto/src/crypto/util/Pack.cs
+++ b/crypto/src/crypto/util/Pack.cs
@@ -798,6 +798,12 @@ namespace Org.BouncyCastle.Crypto.Utilities
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static uint LE_To_UInt32(ReadOnlySpan<byte> bs, int off)
+        {
+            return BinaryPrimitives.ReadUInt32LittleEndian(bs[off..]);
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal static void LE_To_UInt32(ReadOnlySpan<byte> bs, Span<uint> ns)
         {
             for (int i = 0; i < ns.Length; ++i)