summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2022-09-30 13:40:07 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2022-09-30 13:40:07 +0700
commitf8ada894d0a9760cc9c3333e33a6de33124bb495 (patch)
tree5cd2d3ecd86807fa68ae0fe357ae5747c8c29905
parentfixed parameter name to h value (diff)
downloadBouncyCastle.NET-ed25519-f8ada894d0a9760cc9c3333e33a6de33124bb495.tar.xz
Derivation function work
- Add span-based variant of IDerivationFunction.GenerateBytes
- IMacDerivation.GetMac() => Mac property
- Refactor implementations
-rw-r--r--crypto/src/crypto/IDerivationFunction.cs11
-rw-r--r--crypto/src/crypto/IMacDerivationFunction.cs7
-rw-r--r--crypto/src/crypto/agreement/kdf/ConcatenationKdfGenerator.cs130
-rw-r--r--crypto/src/crypto/agreement/kdf/DHKekGenerator.cs118
-rw-r--r--crypto/src/crypto/agreement/kdf/ECDHKekGenerator.cs36
-rw-r--r--crypto/src/crypto/generators/BaseKdfBytesGenerator.cs110
-rw-r--r--crypto/src/crypto/generators/HKdfBytesGenerator.cs78
-rw-r--r--crypto/src/crypto/generators/KDFCounterBytesGenerator.cs134
-rw-r--r--crypto/src/crypto/generators/KDFDoublePipelineIterationBytesGenerator.cs135
-rw-r--r--crypto/src/crypto/generators/KDFFeedbackBytesGenerator.cs130
-rw-r--r--crypto/src/crypto/generators/Kdf1BytesGenerator.cs9
-rw-r--r--crypto/src/crypto/generators/Kdf2BytesGenerator.cs7
-rw-r--r--crypto/src/crypto/generators/Mgf1BytesGenerator.cs159
-rw-r--r--crypto/src/crypto/parameters/KdfParameters.cs20
-rw-r--r--crypto/src/crypto/parameters/MgfParameters.cs35
15 files changed, 656 insertions, 463 deletions
diff --git a/crypto/src/crypto/IDerivationFunction.cs b/crypto/src/crypto/IDerivationFunction.cs
index 7f289f790..9c0228ab0 100644
--- a/crypto/src/crypto/IDerivationFunction.cs
+++ b/crypto/src/crypto/IDerivationFunction.cs
@@ -12,13 +12,12 @@ namespace Org.BouncyCastle.Crypto
         /**
          * return the message digest used as the basis for the function
          */
-        IDigest Digest
-        {
-            get;
-        }
+        IDigest Digest { get; }
 
         int GenerateBytes(byte[] output, int outOff, int length);
-        //throws DataLengthException, ArgumentException;
-    }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        int GenerateBytes(Span<byte> output);
+#endif
+    }
 }
diff --git a/crypto/src/crypto/IMacDerivationFunction.cs b/crypto/src/crypto/IMacDerivationFunction.cs
index 7297cd854..354524e5a 100644
--- a/crypto/src/crypto/IMacDerivationFunction.cs
+++ b/crypto/src/crypto/IMacDerivationFunction.cs
@@ -1,7 +1,8 @@
 namespace Org.BouncyCastle.Crypto
 {
-    public interface IMacDerivationFunction:IDerivationFunction
+    public interface IMacDerivationFunction
+        : IDerivationFunction
     {
-        IMac GetMac();
+        IMac Mac { get; }
     }
-}
\ No newline at end of file
+}
diff --git a/crypto/src/crypto/agreement/kdf/ConcatenationKdfGenerator.cs b/crypto/src/crypto/agreement/kdf/ConcatenationKdfGenerator.cs
index d88f4dfdb..207c795da 100644
--- a/crypto/src/crypto/agreement/kdf/ConcatenationKdfGenerator.cs
+++ b/crypto/src/crypto/agreement/kdf/ConcatenationKdfGenerator.cs
@@ -5,96 +5,110 @@ using Org.BouncyCastle.Crypto.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Agreement.Kdf
 {
-    /**
-     * Generator for Concatenation Key Derivation Function defined in NIST SP 800-56A, Sect 5.8.1
-     */
-    public class ConcatenationKdfGenerator
+    /// <summary>Generator for Concatenation Key Derivation Function defined in NIST SP 800-56A, Sect 5.8.1</summary>
+    public sealed class ConcatenationKdfGenerator
         :   IDerivationFunction
     {
-        private readonly IDigest mDigest;
+        private readonly IDigest m_digest;
+        private readonly int m_hLen;
 
-        private byte[] mShared;
-        private byte[] mOtherInfo;
-        private int mHLen;
+        private byte[] m_buffer;
 
-        /**
-         * @param digest the digest to be used as the source of generated bytes
-         */
+        /// <param name="digest">the digest to be used as the source of generated bytes</param>
         public ConcatenationKdfGenerator(IDigest digest)
         {
-            this.mDigest = digest;
-            this.mHLen = digest.GetDigestSize();
+            m_digest = digest;
+            m_hLen = digest.GetDigestSize();
         }
 
-        public virtual void Init(IDerivationParameters param)
+        public void Init(IDerivationParameters param)
         {
-            if (!(param is KdfParameters))
+            if (!(param is KdfParameters kdfParameters))
                 throw new ArgumentException("KDF parameters required for ConcatenationKdfGenerator");
 
-            KdfParameters p = (KdfParameters)param;
+            byte[] sharedSecret = kdfParameters.GetSharedSecret();
+            byte[] otherInfo = kdfParameters.GetIV();
 
-            mShared = p.GetSharedSecret();
-            mOtherInfo = p.GetIV();
+            m_buffer = new byte[4 + sharedSecret.Length + otherInfo.Length + m_hLen];
+            sharedSecret.CopyTo(m_buffer, 4);
+            otherInfo.CopyTo(m_buffer, 4 + sharedSecret.Length);
         }
 
-        /**
-         * return the underlying digest.
-         */
-        public virtual IDigest Digest
-        {
-            get { return mDigest; }
-        }
+        /// <summary>the underlying digest.</summary>
+        public IDigest Digest => m_digest;
 
-        /**
-         * fill len bytes of the output buffer with bytes generated from
-         * the derivation function.
-         *
-         * @throws DataLengthException if the out buffer is too small.
-         */
-        public virtual int GenerateBytes(byte[]	outBytes, int outOff, int len)
+        /// <summary>Fill <c>len</c> bytes of the output buffer with bytes generated from the derivation function.
+        /// </summary>
+        public int GenerateBytes(byte[]	output, int outOff, int length)
         {
-            if ((outBytes.Length - len) < outOff)
-                throw new DataLengthException("output buffer too small");
+            Check.OutputLength(output, outOff, length, "output buffer too small");
 
-            byte[] hashBuf = new byte[mHLen];
-            byte[] C = new byte[4];
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return GenerateBytes(output.AsSpan(outOff, length));
+#else
+            int hashPos = m_buffer.Length - m_hLen;
             uint counter = 1;
-            int outputLen = 0;
 
-            mDigest.Reset();
+            m_digest.Reset();
+
+            int end = outOff + length;
+            int limit = end - m_hLen;
 
-            if (len > mHLen)
+            while (outOff <= limit)
             {
-                do
-                {
-                    Pack.UInt32_To_BE(counter, C);
+                Pack.UInt32_To_BE(counter++, m_buffer, 0);
 
-                    mDigest.BlockUpdate(C, 0, C.Length);
-                    mDigest.BlockUpdate(mShared, 0, mShared.Length);
-                    mDigest.BlockUpdate(mOtherInfo, 0, mOtherInfo.Length);
+                m_digest.BlockUpdate(m_buffer, 0, hashPos);
+                m_digest.DoFinal(output, outOff);
 
-                    mDigest.DoFinal(hashBuf, 0);
+                outOff += m_hLen;
+            }
 
-                    Array.Copy(hashBuf, 0, outBytes, outOff + outputLen, mHLen);
-                    outputLen += mHLen;
-                }
-                while ((counter++) < (len / mHLen));
+            if (outOff < end)
+            {
+                Pack.UInt32_To_BE(counter, m_buffer, 0);
+
+                m_digest.BlockUpdate(m_buffer, 0, hashPos);
+                m_digest.DoFinal(m_buffer, hashPos);
+
+                Array.Copy(m_buffer, hashPos, output, outOff, end - outOff);
             }
 
-            if (outputLen < len)
+            return length;
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int GenerateBytes(Span<byte> output)
+        {
+            int hashPos = m_buffer.Length - m_hLen;
+            uint counter = 1;
+
+            m_digest.Reset();
+
+            int pos = 0, length = output.Length, limit = length - m_hLen;
+
+            while (pos <= limit)
             {
-                Pack.UInt32_To_BE(counter, C);
+                Pack.UInt32_To_BE(counter++, m_buffer.AsSpan());
+
+                m_digest.BlockUpdate(m_buffer.AsSpan(0, hashPos));
+                m_digest.DoFinal(output[pos..]);
 
-                mDigest.BlockUpdate(C, 0, C.Length);
-                mDigest.BlockUpdate(mShared, 0, mShared.Length);
-                mDigest.BlockUpdate(mOtherInfo, 0, mOtherInfo.Length);
+                pos += m_hLen;
+            }
 
-                mDigest.DoFinal(hashBuf, 0);
+            if (pos < length)
+            {
+                Pack.UInt32_To_BE(counter, m_buffer.AsSpan());
 
-                Array.Copy(hashBuf, 0, outBytes, outOff + outputLen, len - outputLen);
+                m_digest.BlockUpdate(m_buffer.AsSpan(0, hashPos));
+                m_digest.DoFinal(m_buffer.AsSpan(hashPos));
+                m_buffer.AsSpan(hashPos, length - pos).CopyTo(output[pos..]);
             }
 
-            return len;
+            return length;
         }
+#endif
     }
 }
diff --git a/crypto/src/crypto/agreement/kdf/DHKekGenerator.cs b/crypto/src/crypto/agreement/kdf/DHKekGenerator.cs
index 259e21e69..7d75c2224 100644
--- a/crypto/src/crypto/agreement/kdf/DHKekGenerator.cs
+++ b/crypto/src/crypto/agreement/kdf/DHKekGenerator.cs
@@ -1,17 +1,19 @@
 using System;
 
 using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Crypto.IO;
 using Org.BouncyCastle.Crypto.Utilities;
+using Org.BouncyCastle.Security;
 
 namespace Org.BouncyCastle.Crypto.Agreement.Kdf
 {
     /**
     * RFC 2631 Diffie-hellman KEK derivation function.
     */
-    public class DHKekGenerator
+    public sealed class DHKekGenerator
         : IDerivationFunction
     {
-        private readonly IDigest digest;
+        private readonly IDigest m_digest;
 
         private DerObjectIdentifier	algorithm;
         private int					keySize;
@@ -20,10 +22,10 @@ namespace Org.BouncyCastle.Crypto.Agreement.Kdf
 
         public DHKekGenerator(IDigest digest)
         {
-            this.digest = digest;
+            m_digest = digest;
         }
 
-        public virtual void Init(IDerivationParameters param)
+        public void Init(IDerivationParameters param)
         {
             DHKdfParameters parameters = (DHKdfParameters)param;
 
@@ -33,20 +35,79 @@ namespace Org.BouncyCastle.Crypto.Agreement.Kdf
             this.partyAInfo = parameters.GetExtraInfo(); // TODO Clone?
         }
 
-        public virtual IDigest Digest
-        {
-            get { return digest; }
-        }
+        public IDigest Digest => m_digest;
 
-        public virtual int GenerateBytes(byte[]	outBytes, int outOff, int len)
+        public int GenerateBytes(byte[]	outBytes, int outOff, int length)
         {
-            if ((outBytes.Length - len) < outOff)
+            Check.OutputLength(outBytes, outOff, length, "output buffer too small");
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return GenerateBytes(outBytes.AsSpan(outOff, length));
+#else
+            long oBytes = length;
+            int digestSize = m_digest.GetDigestSize();
+
+            //
+            // this is at odds with the standard implementation, the
+            // maximum value should be hBits * (2^32 - 1) where hBits
+            // is the digest output size in bits. We can't have an
+            // array with a long index at the moment...
+            //
+            if (oBytes > ((2L << 32) - 1))
+                throw new ArgumentException("Output length too large");
+
+            int cThreshold = (int)((oBytes + digestSize - 1) / digestSize);
+
+            byte[] dig = new byte[digestSize];
+
+            uint counter = 1;
+
+            for (int i = 0; i < cThreshold; i++)
             {
-                throw new DataLengthException("output buffer too small");
+                // KeySpecificInfo
+                DerSequence keyInfo = new DerSequence(algorithm, new DerOctetString(Pack.UInt32_To_BE(counter)));
+
+                // OtherInfo
+                Asn1EncodableVector v1 = new Asn1EncodableVector(keyInfo);
+
+                if (partyAInfo != null)
+                {
+                    v1.Add(new DerTaggedObject(true, 0, new DerOctetString(partyAInfo)));
+                }
+
+                v1.Add(new DerTaggedObject(true, 2, new DerOctetString(Pack.UInt32_To_BE((uint)keySize))));
+
+                byte[] other = new DerSequence(v1).GetDerEncoded();
+
+                m_digest.BlockUpdate(z, 0, z.Length);
+                m_digest.BlockUpdate(other, 0, other.Length);
+                m_digest.DoFinal(dig, 0);
+
+                if (length > digestSize)
+                {
+                    Array.Copy(dig, 0, outBytes, outOff, digestSize);
+                    outOff += digestSize;
+                    length -= digestSize;
+                }
+                else
+                {
+                    Array.Copy(dig, 0, outBytes, outOff, length);
+                }
+
+                counter++;
             }
 
-            long oBytes = len;
-            int outLen = digest.GetDigestSize();
+            m_digest.Reset();
+
+            return (int)oBytes;
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int GenerateBytes(Span<byte> output)
+        {
+            long oBytes = output.Length;
+            int digestSize = m_digest.GetDigestSize();
 
             //
             // this is at odds with the standard implementation, the
@@ -55,24 +116,18 @@ namespace Org.BouncyCastle.Crypto.Agreement.Kdf
             // array with a long index at the moment...
             //
             if (oBytes > ((2L << 32) - 1))
-            {
                 throw new ArgumentException("Output length too large");
-            }
 
-            int cThreshold = (int)((oBytes + outLen - 1) / outLen);
+            int cThreshold = (int)((oBytes + digestSize - 1) / digestSize);
 
-            byte[] dig = new byte[digest.GetDigestSize()];
+            Span<byte> dig = stackalloc byte[digestSize];
 
             uint counter = 1;
 
             for (int i = 0; i < cThreshold; i++)
             {
-                digest.BlockUpdate(z, 0, z.Length);
-
                 // KeySpecificInfo
-                DerSequence keyInfo = new DerSequence(
-                    algorithm,
-                    new DerOctetString(Pack.UInt32_To_BE(counter)));
+                DerSequence keyInfo = new DerSequence(algorithm, new DerOctetString(Pack.UInt32_To_BE(counter)));
 
                 // OtherInfo
                 Asn1EncodableVector v1 = new Asn1EncodableVector(keyInfo);
@@ -86,27 +141,28 @@ namespace Org.BouncyCastle.Crypto.Agreement.Kdf
 
                 byte[] other = new DerSequence(v1).GetDerEncoded();
 
-                digest.BlockUpdate(other, 0, other.Length);
-
-                digest.DoFinal(dig, 0);
+                m_digest.BlockUpdate(z);
+                m_digest.BlockUpdate(other);
+                m_digest.DoFinal(dig);
 
-                if (len > outLen)
+                int remaining = output.Length;
+                if (remaining > digestSize)
                 {
-                    Array.Copy(dig, 0, outBytes, outOff, outLen);
-                    outOff += outLen;
-                    len -= outLen;
+                    dig.CopyTo(output);
+                    output = output[digestSize..];
                 }
                 else
                 {
-                    Array.Copy(dig, 0, outBytes, outOff, len);
+                    dig[..remaining].CopyTo(output);
                 }
 
                 counter++;
             }
 
-            digest.Reset();
+            m_digest.Reset();
 
             return (int)oBytes;
         }
+#endif
     }
 }
diff --git a/crypto/src/crypto/agreement/kdf/ECDHKekGenerator.cs b/crypto/src/crypto/agreement/kdf/ECDHKekGenerator.cs
index 74464574c..dbb7ad535 100644
--- a/crypto/src/crypto/agreement/kdf/ECDHKekGenerator.cs
+++ b/crypto/src/crypto/agreement/kdf/ECDHKekGenerator.cs
@@ -11,10 +11,10 @@ namespace Org.BouncyCastle.Crypto.Agreement.Kdf
     /**
     * X9.63 based key derivation function for ECDH CMS.
     */
-    public class ECDHKekGenerator
+    public sealed class ECDHKekGenerator
         : IDerivationFunction
     {
-        private readonly IDerivationFunction kdf;
+        private readonly IDerivationFunction m_kdf;
 
         private DerObjectIdentifier	algorithm;
         private int					keySize;
@@ -22,10 +22,10 @@ namespace Org.BouncyCastle.Crypto.Agreement.Kdf
 
         public ECDHKekGenerator(IDigest digest)
         {
-            this.kdf = new Kdf2BytesGenerator(digest);
+            m_kdf = new Kdf2BytesGenerator(digest);
         }
 
-        public virtual void Init(IDerivationParameters param)
+        public void Init(IDerivationParameters param)
         {
             DHKdfParameters parameters = (DHKdfParameters)param;
 
@@ -34,12 +34,29 @@ namespace Org.BouncyCastle.Crypto.Agreement.Kdf
             this.z = parameters.GetZ(); // TODO Clone?
         }
 
-        public virtual IDigest Digest
+        public IDigest Digest => m_kdf.Digest;
+
+        public int GenerateBytes(byte[]	outBytes, int outOff, int length)
         {
-            get { return kdf.Digest; }
+            Check.OutputLength(outBytes, outOff, length, "output buffer too small");
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return GenerateBytes(outBytes.AsSpan(outOff, length));
+#else
+            // TODO Create an ASN.1 class for this (RFC3278)
+            // ECC-CMS-SharedInfo
+            DerSequence s = new DerSequence(
+                new AlgorithmIdentifier(algorithm, DerNull.Instance),
+                new DerTaggedObject(true, 2, new DerOctetString(Pack.UInt32_To_BE((uint)keySize))));
+
+            m_kdf.Init(new KdfParameters(z, s.GetDerEncoded()));
+
+            return m_kdf.GenerateBytes(outBytes, outOff, length);
+#endif
         }
 
-        public virtual int GenerateBytes(byte[]	outBytes, int outOff, int len)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int GenerateBytes(Span<byte> output)
         {
             // TODO Create an ASN.1 class for this (RFC3278)
             // ECC-CMS-SharedInfo
@@ -47,9 +64,10 @@ namespace Org.BouncyCastle.Crypto.Agreement.Kdf
                 new AlgorithmIdentifier(algorithm, DerNull.Instance),
                 new DerTaggedObject(true, 2, new DerOctetString(Pack.UInt32_To_BE((uint)keySize))));
 
-            kdf.Init(new KdfParameters(z, s.GetDerEncoded()));
+            m_kdf.Init(new KdfParameters(z, s.GetDerEncoded()));
 
-            return kdf.GenerateBytes(outBytes, outOff, len);
+            return m_kdf.GenerateBytes(output);
         }
+#endif
     }
 }
diff --git a/crypto/src/crypto/generators/BaseKdfBytesGenerator.cs b/crypto/src/crypto/generators/BaseKdfBytesGenerator.cs
index bca420711..c0c4be217 100644
--- a/crypto/src/crypto/generators/BaseKdfBytesGenerator.cs
+++ b/crypto/src/crypto/generators/BaseKdfBytesGenerator.cs
@@ -11,7 +11,7 @@ namespace Org.BouncyCastle.Crypto.Generators
     * <br/>
     * This implementation is based on ISO 18033/P1363a.
     */
-    public class BaseKdfBytesGenerator
+    public abstract class BaseKdfBytesGenerator
         : IDerivationFunction
     {
         private int     counterStart;
@@ -25,26 +25,22 @@ namespace Org.BouncyCastle.Crypto.Generators
         * @param counterStart value of counter.
         * @param digest the digest to be used as the source of derived keys.
         */
-        public BaseKdfBytesGenerator(int counterStart, IDigest digest)
+        protected BaseKdfBytesGenerator(int counterStart, IDigest digest)
         {
             this.counterStart = counterStart;
             this.digest = digest;
         }
 
-        public virtual void Init(IDerivationParameters parameters)
+        public void Init(IDerivationParameters parameters)
         {
-            if (parameters is KdfParameters)
+            if (parameters is KdfParameters kdfParameters)
             {
-                KdfParameters   p = (KdfParameters)parameters;
-
-                shared = p.GetSharedSecret();
-                iv = p.GetIV();
+                shared = kdfParameters.GetSharedSecret();
+                iv = kdfParameters.GetIV();
             }
-            else if (parameters is Iso18033KdfParameters)
+            else if (parameters is Iso18033KdfParameters iso18033KdfParameters)
             {
-                Iso18033KdfParameters p = (Iso18033KdfParameters)parameters;
-
-                shared = p.GetSeed();
+                shared = iso18033KdfParameters.GetSeed();
                 iv = null;
             }
             else
@@ -56,10 +52,7 @@ namespace Org.BouncyCastle.Crypto.Generators
         /**
         * return the underlying digest.
         */
-        public virtual IDigest Digest
-        {
-            get { return digest; }
-        }
+        public IDigest Digest => digest;
 
         /**
         * fill len bytes of the output buffer with bytes generated from
@@ -68,13 +61,15 @@ namespace Org.BouncyCastle.Crypto.Generators
         * @throws ArgumentException if the size of the request will cause an overflow.
         * @throws DataLengthException if the out buffer is too small.
         */
-        public virtual int GenerateBytes(byte[] output, int outOff, int length)
+        public int GenerateBytes(byte[] output, int outOff, int length)
         {
-            if ((output.Length - length) < outOff)
-                throw new DataLengthException("output buffer too small");
+            Check.OutputLength(output, outOff, length, "output buffer too small");
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return GenerateBytes(output.AsSpan(outOff, length));
+#else
             long oBytes = length;
-            int outLen = digest.GetDigestSize();
+            int digestSize = digest.GetDigestSize();
 
             //
             // this is at odds with the standard implementation, the
@@ -85,9 +80,9 @@ namespace Org.BouncyCastle.Crypto.Generators
             if (oBytes > ((2L << 32) - 1))
                 throw new ArgumentException("Output length too large");
 
-            int cThreshold = (int)((oBytes + outLen - 1) / outLen);
+            int cThreshold = (int)((oBytes + digestSize - 1) / digestSize);
 
-            byte[] dig = new byte[digest.GetDigestSize()];
+            byte[] dig = new byte[digestSize];
 
             byte[] C = new byte[4];
             Pack.UInt32_To_BE((uint)counterStart, C, 0);
@@ -106,11 +101,11 @@ namespace Org.BouncyCastle.Crypto.Generators
 
                 digest.DoFinal(dig, 0);
 
-                if (length > outLen)
+                if (length > digestSize)
                 {
-                    Array.Copy(dig, 0, output, outOff, outLen);
-                    outOff += outLen;
-                    length -= outLen;
+                    Array.Copy(dig, 0, output, outOff, digestSize);
+                    outOff += digestSize;
+                    length -= digestSize;
                 }
                 else
                 {
@@ -127,6 +122,67 @@ namespace Org.BouncyCastle.Crypto.Generators
             digest.Reset();
 
             return (int)oBytes;
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int GenerateBytes(Span<byte> output)
+        {
+            long oBytes = output.Length;
+            int digestSize = digest.GetDigestSize();
+
+            //
+            // this is at odds with the standard implementation, the
+            // maximum value should be hBits * (2^32 - 1) where hBits
+            // is the digest output size in bits. We can't have an
+            // array with a long index at the moment...
+            //
+            if (oBytes > ((2L << 32) - 1))
+                throw new ArgumentException("Output length too large");
+
+            int cThreshold = (int)((oBytes + digestSize - 1) / digestSize);
+
+            Span<byte> dig = stackalloc byte[digestSize];
+
+            Span<byte> C = stackalloc byte[4];
+            Pack.UInt32_To_BE((uint)counterStart, C);
+
+            uint counterBase = (uint)(counterStart & ~0xFF);
+
+            for (int i = 0; i < cThreshold; i++)
+            {
+                digest.BlockUpdate(shared);
+                digest.BlockUpdate(C);
+
+                if (iv != null)
+                {
+                    digest.BlockUpdate(iv);
+                }
+
+                digest.DoFinal(dig);
+
+                int remaining = output.Length;
+                if (remaining > digestSize)
+                {
+                    dig.CopyTo(output);
+                    output = output[digestSize..];
+                }
+                else
+                {
+                    dig[..remaining].CopyTo(output);
+                }
+
+                if (++C[3] == 0)
+                {
+                    counterBase += 0x100;
+                    Pack.UInt32_To_BE(counterBase, C);
+                }
+            }
+
+            digest.Reset();
+
+            return (int)oBytes;
         }
+#endif
     }
-}
\ No newline at end of file
+}
diff --git a/crypto/src/crypto/generators/HKdfBytesGenerator.cs b/crypto/src/crypto/generators/HKdfBytesGenerator.cs
index 6f36a6faa..e5c122049 100644
--- a/crypto/src/crypto/generators/HKdfBytesGenerator.cs
+++ b/crypto/src/crypto/generators/HKdfBytesGenerator.cs
@@ -13,7 +13,7 @@ namespace Org.BouncyCastle.Crypto.Generators
      * (output keying material) and is likely to have better security properties
      * than KDF's based on just a hash function.
      */
-    public class HkdfBytesGenerator
+    public sealed class HkdfBytesGenerator
         : IDerivationFunction
     {
         private HMac hMacHash;
@@ -35,12 +35,11 @@ namespace Org.BouncyCastle.Crypto.Generators
             this.hashLen = hash.GetDigestSize();
         }
 
-        public virtual void Init(IDerivationParameters parameters)
+        public void Init(IDerivationParameters parameters)
         {
-            if (!(parameters is HkdfParameters))
+            if (!(parameters is HkdfParameters hkdfParameters))
                 throw new ArgumentException("HKDF parameters required for HkdfBytesGenerator", "parameters");
 
-            HkdfParameters hkdfParameters = (HkdfParameters)parameters;
             if (hkdfParameters.SkipExtract)
             {
                 // use IKM directly as PRK
@@ -108,45 +107,70 @@ namespace Org.BouncyCastle.Crypto.Generators
             hMacHash.DoFinal(currentT, 0);
         }
 
-        public virtual IDigest Digest
-        {
-            get { return hMacHash.GetUnderlyingDigest(); }
-        }
+        public IDigest Digest => hMacHash.GetUnderlyingDigest();
 
-        public virtual int GenerateBytes(byte[] output, int outOff, int len)
+        public int GenerateBytes(byte[] output, int outOff, int length)
         {
-            if (generatedBytes + len > 255 * hashLen)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return GenerateBytes(output.AsSpan(outOff, length));
+#else
+            if (generatedBytes > 255 * hashLen - length)
+                throw new DataLengthException("HKDF may only be used for 255 * HashLen bytes of output");
+
+            int toGenerate = length;
+            int posInT = generatedBytes % hashLen;
+            if (posInT != 0)
             {
-                throw new DataLengthException(
-                    "HKDF may only be used for 255 * HashLen bytes of output");
+                // copy what is left in the currentT (1..hash
+                int toCopy = System.Math.Min(hashLen - posInT, toGenerate);
+                Array.Copy(currentT, posInT, output, outOff, toCopy);
+                generatedBytes += toCopy;
+                toGenerate -= toCopy;
+                outOff += toCopy;
             }
 
-            if (generatedBytes % hashLen == 0)
+            while (toGenerate > 0)
             {
                 ExpandNext();
+                int toCopy = System.Math.Min(hashLen, toGenerate);
+                Array.Copy(currentT, 0, output, outOff, toCopy);
+                generatedBytes += toCopy;
+                toGenerate -= toCopy;
+                outOff += toCopy;
             }
 
-            // copy what is left in the currentT (1..hash
-            int toGenerate = len;
+            return length;
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int GenerateBytes(Span<byte> outputX)
+        {
+            int length = outputX.Length;
+            if (generatedBytes > 255 * hashLen - length)
+                throw new DataLengthException("HKDF may only be used for 255 * HashLen bytes of output");
+
             int posInT = generatedBytes % hashLen;
-            int leftInT = hashLen - generatedBytes % hashLen;
-            int toCopy = System.Math.Min(leftInT, toGenerate);
-            Array.Copy(currentT, posInT, output, outOff, toCopy);
-            generatedBytes += toCopy;
-            toGenerate -= toCopy;
-            outOff += toCopy;
+            if (posInT != 0)
+            {
+                // copy what is left in the currentT (1..hash
+                int toCopy = System.Math.Min(hashLen - posInT, outputX.Length);
+                currentT.AsSpan(posInT, toCopy).CopyTo(outputX);
+                generatedBytes += toCopy;
+                outputX = outputX[toCopy..];
+            }
 
-            while (toGenerate > 0)
+            while (!outputX.IsEmpty)
             {
                 ExpandNext();
-                toCopy = System.Math.Min(hashLen, toGenerate);
-                Array.Copy(currentT, 0, output, outOff, toCopy);
+                int toCopy = System.Math.Min(hashLen, outputX.Length);
+                currentT.AsSpan(0, toCopy).CopyTo(outputX);
                 generatedBytes += toCopy;
-                toGenerate -= toCopy;
-                outOff += toCopy;
+                outputX = outputX[toCopy..];
             }
 
-            return len;
+            return length;
         }
+#endif
     }
 }
diff --git a/crypto/src/crypto/generators/KDFCounterBytesGenerator.cs b/crypto/src/crypto/generators/KDFCounterBytesGenerator.cs
index 0d7647289..7fa50e2fa 100644
--- a/crypto/src/crypto/generators/KDFCounterBytesGenerator.cs
+++ b/crypto/src/crypto/generators/KDFCounterBytesGenerator.cs
@@ -3,17 +3,12 @@
 using Org.BouncyCastle.Crypto.Macs;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Math;
-using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Generators
 {
-    public class KdfCounterBytesGenerator : IMacDerivationFunction
+    public sealed class KdfCounterBytesGenerator
+        : IMacDerivationFunction
     {
-
-        private static readonly BigInteger IntegerMax = BigInteger.ValueOf(0x7fffffff);
-        private static readonly BigInteger Two = BigInteger.Two;
-
-
         private readonly IMac prf;
         private readonly int h;
 
@@ -37,13 +32,8 @@ namespace Org.BouncyCastle.Crypto.Generators
 
         public void Init(IDerivationParameters param)
         {
-            KdfCounterParameters kdfParams = param as KdfCounterParameters;
-            if (kdfParams == null)
-            {
+            if (!(param is KdfCounterParameters kdfParams))
                 throw new ArgumentException("Wrong type of arguments given");
-            }
-
-
 
             // --- init mac based PRF ---
 
@@ -57,54 +47,45 @@ namespace Org.BouncyCastle.Crypto.Generators
             int r = kdfParams.R;
             this.ios = new byte[r / 8];
 
-            BigInteger maxSize = Two.Pow(r).Multiply(BigInteger.ValueOf(h));
-            this.maxSizeExcl = maxSize.CompareTo(IntegerMax) == 1 ?
-                int.MaxValue : maxSize.IntValue;
+            BigInteger maxSize = BigInteger.One.ShiftLeft(r).Multiply(BigInteger.ValueOf(h));
+            this.maxSizeExcl = maxSize.BitLength > 31 ? int.MaxValue : maxSize.IntValueExact;
 
             // --- set operational state ---
 
             generatedBytes = 0;
         }
 
-
-        public IMac GetMac()
-        {
-            return prf;
-        }
+        public IMac Mac => prf;
 
         public IDigest Digest
         {
-            get { return prf is HMac ? ((HMac)prf).GetUnderlyingDigest() : null; }
+            get { return (prf as HMac)?.GetUnderlyingDigest(); }
         }
 
         public int GenerateBytes(byte[] output, int outOff, int length)
         {
-            int generatedBytesAfter = generatedBytes + length;
-            if (generatedBytesAfter < 0 || generatedBytesAfter >= maxSizeExcl)
-            {
-                throw new DataLengthException(
-                    "Current KDFCTR may only be used for " + maxSizeExcl + " bytes");
-            }
-
-            if (generatedBytes % h == 0)
-            {
-                generateNext();
-            }
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return GenerateBytes(output.AsSpan(outOff, length));
+#else
+            if (generatedBytes >= maxSizeExcl - length)
+                throw new DataLengthException("Current KDFCTR may only be used for " + maxSizeExcl + " bytes");
 
-            // copy what is left in the currentT (1..hash
             int toGenerate = length;
             int posInK = generatedBytes % h;
-            int leftInK = h - generatedBytes % h;
-            int toCopy = System.Math.Min(leftInK, toGenerate);
-            Array.Copy(k, posInK, output, outOff, toCopy);
-            generatedBytes += toCopy;
-            toGenerate -= toCopy;
-            outOff += toCopy;
+            if (posInK != 0)
+            {
+                // copy what is left in the currentT (1..hash
+                int toCopy = System.Math.Min(h - posInK, toGenerate);
+                Array.Copy(k, posInK, output, outOff, toCopy);
+                generatedBytes += toCopy;
+                toGenerate -= toCopy;
+                outOff += toCopy;
+            }
 
             while (toGenerate > 0)
             {
-                generateNext();
-                toCopy = System.Math.Min(h, toGenerate);
+                GenerateNext();
+                int toCopy = System.Math.Min(h, toGenerate);
                 Array.Copy(k, 0, output, outOff, toCopy);
                 generatedBytes += toCopy;
                 toGenerate -= toCopy;
@@ -112,38 +93,65 @@ namespace Org.BouncyCastle.Crypto.Generators
             }
 
             return length;
-
+#endif
         }
 
-        private void generateNext()
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int GenerateBytes(Span<byte> output)
         {
+            int length = output.Length;
+            if (generatedBytes >= maxSizeExcl - length)
+                throw new DataLengthException("Current KDFCTR may only be used for " + maxSizeExcl + " bytes");
+
+            int posInK = generatedBytes % h;
+            if (posInK != 0)
+            {
+                // copy what is left in the currentT (1..hash
+                int toCopy = System.Math.Min(h - posInK, output.Length);
+                k.AsSpan(posInK, toCopy).CopyTo(output);
+                generatedBytes += toCopy;
+                output = output[toCopy..];
+            }
 
+            while (!output.IsEmpty)
+            {
+                GenerateNext();
+                int toCopy = System.Math.Min(h, output.Length);
+                k.AsSpan(0, toCopy).CopyTo(output);
+                generatedBytes += toCopy;
+                output = output[toCopy..];
+            }
+
+            return length;
+        }
+#endif
+
+        private void GenerateNext()
+        {
             int i = generatedBytes / h + 1;
 
             // encode i into counter buffer
             switch (ios.Length)
             {
-                case 4:
-                    ios[0] = (byte)(i >> 24);
-                    goto case 3;
+            case 4:
+                ios[0] = (byte)(i >> 24);
                 // fall through
-                case 3:
-                    ios[ios.Length - 3] = (byte)(i >> 16);
-                    // fall through
-                    goto case 2;
-                case 2:
-                    ios[ios.Length - 2] = (byte)(i >> 8);
-                    // fall through
-                    goto case 1;
-                case 1:
-                    ios[ios.Length - 1] = (byte)i;
-                    break;
-                default:
-                    throw new InvalidOperationException("Unsupported size of counter i");
+                goto case 3;
+            case 3:
+                ios[ios.Length - 3] = (byte)(i >> 16);
+                // fall through
+                goto case 2;
+            case 2:
+                ios[ios.Length - 2] = (byte)(i >> 8);
+                // fall through
+                goto case 1;
+            case 1:
+                ios[ios.Length - 1] = (byte)i;
+                break;
+            default:
+                throw new InvalidOperationException("Unsupported size of counter i");
             }
 
-
-
             // special case for K(0): K(0) is empty, so no update
             prf.BlockUpdate(fixedInputDataCtrPrefix, 0, fixedInputDataCtrPrefix.Length);
             prf.BlockUpdate(ios, 0, ios.Length);
@@ -151,4 +159,4 @@ namespace Org.BouncyCastle.Crypto.Generators
             prf.DoFinal(k, 0);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/crypto/src/crypto/generators/KDFDoublePipelineIterationBytesGenerator.cs b/crypto/src/crypto/generators/KDFDoublePipelineIterationBytesGenerator.cs
index 63c0787f3..01feda6f4 100644
--- a/crypto/src/crypto/generators/KDFDoublePipelineIterationBytesGenerator.cs
+++ b/crypto/src/crypto/generators/KDFDoublePipelineIterationBytesGenerator.cs
@@ -6,11 +6,9 @@ using Org.BouncyCastle.Math;
 
 namespace Org.BouncyCastle.Crypto.Generators
 {
-    public class KdfDoublePipelineIterationBytesGenerator : IMacDerivationFunction
+    public sealed class KdfDoublePipelineIterationBytesGenerator
+        : IMacDerivationFunction
     {
-        private static readonly BigInteger IntegerMax = BigInteger.ValueOf(0x7fffffff);
-        private static readonly BigInteger Two = BigInteger.Two;
-
         // fields set by the constructor       
         private readonly IMac prf;
         private readonly int h;
@@ -28,7 +26,6 @@ namespace Org.BouncyCastle.Crypto.Generators
         private byte[] a;
         private byte[] k;
 
-
         public KdfDoublePipelineIterationBytesGenerator(IMac prf)
         {
             this.prf = prf;
@@ -37,16 +34,10 @@ namespace Org.BouncyCastle.Crypto.Generators
             this.k = new byte[h];
         }
 
-
         public void Init(IDerivationParameters parameters)
         {
-            KdfDoublePipelineIterationParameters dpiParams = parameters as KdfDoublePipelineIterationParameters;
-            if (dpiParams == null)
-            {
+            if (!(parameters is KdfDoublePipelineIterationParameters dpiParams))
                 throw new ArgumentException("Wrong type of arguments given");
-            }
-
-
 
             // --- init mac based PRF ---
 
@@ -62,13 +53,12 @@ namespace Org.BouncyCastle.Crypto.Generators
             if (dpiParams.UseCounter)
             {
                 // this is more conservative than the spec
-                BigInteger maxSize = Two.Pow(r).Multiply(BigInteger.ValueOf(h));
-                this.maxSizeExcl = maxSize.CompareTo(IntegerMax) == 1 ?
-                    int.MaxValue : maxSize.IntValue;
+                BigInteger maxSize = BigInteger.One.ShiftLeft(r).Multiply(BigInteger.ValueOf(h));
+                this.maxSizeExcl = maxSize.BitLength > 31 ? int.MaxValue : maxSize.IntValueExact;
             }
             else
             {
-                this.maxSizeExcl = IntegerMax.IntValue;
+                this.maxSizeExcl = int.MaxValue;
             }
 
             this.useCounter = dpiParams.UseCounter;
@@ -78,12 +68,8 @@ namespace Org.BouncyCastle.Crypto.Generators
             generatedBytes = 0;
         }
 
-
-
-
-        private void generateNext()
+        private void GenerateNext()
         {
-
             if (generatedBytes == 0)
             {
                 // --- step 4 ---
@@ -107,23 +93,23 @@ namespace Org.BouncyCastle.Crypto.Generators
                 // encode i into counter buffer
                 switch (ios.Length)
                 {
-                    case 4:
-                        ios[0] = (byte)(i >> 24);
-                        // fall through
-                        goto case 3;
-                    case 3:
-                        ios[ios.Length - 3] = (byte)(i >> 16);
-                        // fall through
-                        goto case 2;
-                    case 2:
-                        ios[ios.Length - 2] = (byte)(i >> 8);
-                        // fall through
-                        goto case 1;
-                    case 1:
-                        ios[ios.Length - 1] = (byte)i;
-                        break;
-                    default:
-                        throw new InvalidOperationException("Unsupported size of counter i");
+                case 4:
+                    ios[0] = (byte)(i >> 24);
+                    // fall through
+                    goto case 3;
+                case 3:
+                    ios[ios.Length - 3] = (byte)(i >> 16);
+                    // fall through
+                    goto case 2;
+                case 2:
+                    ios[ios.Length - 2] = (byte)(i >> 8);
+                    // fall through
+                    goto case 1;
+                case 1:
+                    ios[ios.Length - 1] = (byte)i;
+                    break;
+                default:
+                    throw new InvalidOperationException("Unsupported size of counter i");
                 }
                 prf.BlockUpdate(ios, 0, ios.Length);
             }
@@ -134,37 +120,33 @@ namespace Org.BouncyCastle.Crypto.Generators
 
         public IDigest Digest
         {
-            get { return prf is HMac ? ((HMac)prf).GetUnderlyingDigest() : null; }
+            get { return (prf as HMac)?.GetUnderlyingDigest(); }
         }
 
         public int GenerateBytes(byte[] output, int outOff, int length)
         {
-            int generatedBytesAfter = generatedBytes + length;
-            if (generatedBytesAfter < 0 || generatedBytesAfter >= maxSizeExcl)
-            {
-                throw new DataLengthException(
-                    "Current KDFCTR may only be used for " + maxSizeExcl + " bytes");
-            }
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return GenerateBytes(output.AsSpan(outOff, length));
+#else
+            if (generatedBytes >= maxSizeExcl - length)
+                throw new DataLengthException("Current KDFCTR may only be used for " + maxSizeExcl + " bytes");
 
-            if (generatedBytes % h == 0)
-            {
-                generateNext();
-            }
-
-            // copy what is left in the currentT (1..hash
             int toGenerate = length;
             int posInK = generatedBytes % h;
-            int leftInK = h - generatedBytes % h;
-            int toCopy = System.Math.Min(leftInK, toGenerate);
-            Array.Copy(k, posInK, output, outOff, toCopy);
-            generatedBytes += toCopy;
-            toGenerate -= toCopy;
-            outOff += toCopy;
+            if (posInK != 0)
+            {
+                // copy what is left in the currentT (1..hash
+                int toCopy = System.Math.Min(h - posInK, toGenerate);
+                Array.Copy(k, posInK, output, outOff, toCopy);
+                generatedBytes += toCopy;
+                toGenerate -= toCopy;
+                outOff += toCopy;
+            }
 
             while (toGenerate > 0)
             {
-                generateNext();
-                toCopy = System.Math.Min(h, toGenerate);
+                GenerateNext();
+                int toCopy = System.Math.Min(h, toGenerate);
                 Array.Copy(k, 0, output, outOff, toCopy);
                 generatedBytes += toCopy;
                 toGenerate -= toCopy;
@@ -172,11 +154,40 @@ namespace Org.BouncyCastle.Crypto.Generators
             }
 
             return length;
+#endif
         }
 
-        public IMac GetMac()
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int GenerateBytes(Span<byte> output)
         {
-            return prf;
+            int length = output.Length;
+            if (generatedBytes >= maxSizeExcl - length)
+                throw new DataLengthException("Current KDFCTR may only be used for " + maxSizeExcl + " bytes");
+
+            int posInK = generatedBytes % h;
+            if (posInK != 0)
+            {
+                // copy what is left in the currentT (1..hash
+                GenerateNext();
+                int toCopy = System.Math.Min(h - posInK, output.Length);
+                k.AsSpan(posInK, toCopy).CopyTo(output);
+                generatedBytes += toCopy;
+                output = output[toCopy..];
+            }
+
+            while (!output.IsEmpty)
+            {
+                GenerateNext();
+                int toCopy = System.Math.Min(h, output.Length);
+                k.AsSpan(0, toCopy).CopyTo(output);
+                generatedBytes += toCopy;
+                output = output[toCopy..];
+            }
+
+            return length;
         }
+#endif
+
+        public IMac Mac => prf;
     }
-}
\ No newline at end of file
+}
diff --git a/crypto/src/crypto/generators/KDFFeedbackBytesGenerator.cs b/crypto/src/crypto/generators/KDFFeedbackBytesGenerator.cs
index 11a5552fe..58a035ef6 100644
--- a/crypto/src/crypto/generators/KDFFeedbackBytesGenerator.cs
+++ b/crypto/src/crypto/generators/KDFFeedbackBytesGenerator.cs
@@ -6,11 +6,9 @@ using Org.BouncyCastle.Math;
 
 namespace Org.BouncyCastle.Crypto.Generators
 {
-    public class KdfFeedbackBytesGenerator : IMacDerivationFunction
+    public sealed class KdfFeedbackBytesGenerator
+        : IMacDerivationFunction
     {
-        private static readonly BigInteger IntegerMax = BigInteger.ValueOf(0x7fffffff);
-        private static readonly BigInteger Two = BigInteger.Two;
-
         // please refer to the standard for the meaning of the variable names
         // all field lengths are in bytes, not in bits as specified by the standard
 
@@ -38,15 +36,10 @@ namespace Org.BouncyCastle.Crypto.Generators
             this.k = new byte[h];
         }
 
-
         public void Init(IDerivationParameters parameters)
         {
-            KdfFeedbackParameters feedbackParams = parameters as KdfFeedbackParameters;
-            if (feedbackParams == null)
-            {
+            if (!(parameters is KdfFeedbackParameters feedbackParams))
                 throw new ArgumentException("Wrong type of arguments given");
-            }
-
 
             // --- init mac based PRF ---
 
@@ -62,9 +55,8 @@ namespace Org.BouncyCastle.Crypto.Generators
             if (feedbackParams.UseCounter)
             {
                 // this is more conservative than the spec
-                BigInteger maxSize = Two.Pow(r).Multiply(BigInteger.ValueOf(h));
-                this.maxSizeExcl = maxSize.CompareTo(IntegerMax) == 1 ?
-                    int.MaxValue : maxSize.IntValue;
+                BigInteger maxSize = BigInteger.One.ShiftLeft(r).Multiply(BigInteger.ValueOf(h));
+                this.maxSizeExcl = maxSize.BitLength > 31 ? int.MaxValue : maxSize.IntValueExact;
             }
             else
             {
@@ -81,40 +73,33 @@ namespace Org.BouncyCastle.Crypto.Generators
 
         public IDigest Digest
         {
-            get { return prf is HMac ? ((HMac)prf).GetUnderlyingDigest() : null; }
+            get { return (prf as HMac)?.GetUnderlyingDigest(); }
         }
 
         public int GenerateBytes(byte[] output, int outOff, int length)
         {
-            int generatedBytesAfter = generatedBytes + length;
-            if (generatedBytesAfter < 0 || generatedBytesAfter >= maxSizeExcl)
-            {
-                throw new DataLengthException(
-                    "Current KDFCTR may only be used for " + maxSizeExcl + " bytes");
-            }
-
-            if (generatedBytes % h == 0)
-            {
-                generateNext();
-            }
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return GenerateBytes(output.AsSpan(outOff, length));
+#else
+            if (generatedBytes >= maxSizeExcl - length)
+                throw new DataLengthException("Current KDFCTR may only be used for " + maxSizeExcl + " bytes");
 
-            // copy what is left in the currentT (1..hash
             int toGenerate = length;
             int posInK = generatedBytes % h;
-            int leftInK = h - generatedBytes % h;
-
-
-            int toCopy = System.Math.Min(leftInK, toGenerate);
-            Array.Copy(k, posInK, output, outOff, toCopy);
-
-            generatedBytes += toCopy;
-            toGenerate -= toCopy;
-            outOff += toCopy;
+            if (posInK != 0)
+            {
+                // copy what is left in the currentT (1..hash
+                int toCopy = System.Math.Min(h - posInK, toGenerate);
+                Array.Copy(k, posInK, output, outOff, toCopy);
+                generatedBytes += toCopy;
+                toGenerate -= toCopy;
+                outOff += toCopy;
+            }
 
             while (toGenerate > 0)
             {
-                generateNext();
-                toCopy = System.Math.Min(h, toGenerate);
+                GenerateNext();
+                int toCopy = System.Math.Min(h, toGenerate);
                 Array.Copy(k, 0, output, outOff, toCopy);
                 generatedBytes += toCopy;
                 toGenerate -= toCopy;
@@ -122,11 +107,41 @@ namespace Org.BouncyCastle.Crypto.Generators
             }
 
             return length;
+#endif
         }
 
-        private void generateNext()
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int GenerateBytes(Span<byte> output)
         {
+            int length = output.Length;
+            if (generatedBytes >= maxSizeExcl - length)
+                throw new DataLengthException("Current KDFCTR may only be used for " + maxSizeExcl + " bytes");
 
+            int posInK = generatedBytes % h;
+            if (posInK != 0)
+            {
+                // copy what is left in the currentT (1..hash
+                int toCopy = System.Math.Min(h - posInK, output.Length);
+                k.AsSpan(posInK, toCopy).CopyTo(output);
+                generatedBytes += toCopy;
+                output = output[toCopy..];
+            }
+
+            while (!output.IsEmpty)
+            {
+                GenerateNext();
+                int toCopy = System.Math.Min(h, output.Length);
+                k.AsSpan(0, toCopy).CopyTo(output);
+                generatedBytes += toCopy;
+                output = output[toCopy..];
+            }
+
+            return length;
+        }
+#endif
+
+        private void GenerateNext()
+        {
             // TODO enable IV
             if (generatedBytes == 0)
             {
@@ -144,23 +159,23 @@ namespace Org.BouncyCastle.Crypto.Generators
                 // encode i into counter buffer
                 switch (ios.Length)
                 {
-                    case 4:
-                        ios[0] = (byte)(i >> 24);
-                        goto case 3;
+                case 4:
+                    ios[0] = (byte)(i >> 24);
                     // fall through
-                    case 3:
-                        ios[ios.Length - 3] = (byte)(i >> 16);
-                        // fall through
-                        goto case 2;
-                    case 2:
-                        ios[ios.Length - 2] = (byte)(i >> 8);
-                        // fall through
-                        goto case 1;
-                    case 1:
-                        ios[ios.Length - 1] = (byte)i;
-                        break;
-                    default:
-                        throw new InvalidOperationException("Unsupported size of counter i");
+                    goto case 3;
+                case 3:
+                    ios[ios.Length - 3] = (byte)(i >> 16);
+                    // fall through
+                    goto case 2;
+                case 2:
+                    ios[ios.Length - 2] = (byte)(i >> 8);
+                    // fall through
+                    goto case 1;
+                case 1:
+                    ios[ios.Length - 1] = (byte)i;
+                    break;
+                default:
+                    throw new InvalidOperationException("Unsupported size of counter i");
                 }
                 prf.BlockUpdate(ios, 0, ios.Length);
             }
@@ -169,9 +184,6 @@ namespace Org.BouncyCastle.Crypto.Generators
             prf.DoFinal(k, 0);
         }
 
-        public IMac GetMac()
-        {
-            return prf;
-        }
+        public IMac Mac => prf;
     }
-}
\ No newline at end of file
+}
diff --git a/crypto/src/crypto/generators/Kdf1BytesGenerator.cs b/crypto/src/crypto/generators/Kdf1BytesGenerator.cs
index 0ddf6c166..a43fa06d1 100644
--- a/crypto/src/crypto/generators/Kdf1BytesGenerator.cs
+++ b/crypto/src/crypto/generators/Kdf1BytesGenerator.cs
@@ -1,16 +1,11 @@
-using System;
-
-using Org.BouncyCastle.Crypto;
-using Org.BouncyCastle.Crypto.Parameters;
-
 namespace Org.BouncyCastle.Crypto.Generators
 {
 	/**
-	 * KFD2 generator for derived keys and ivs as defined by IEEE P1363a/ISO 18033
+	 * KFD1 generator for derived keys and ivs as defined by IEEE P1363a/ISO 18033
 	 * <br/>
 	 * This implementation is based on IEEE P1363/ISO 18033.
 	 */
-	public class Kdf1BytesGenerator
+	public sealed class Kdf1BytesGenerator
 		: BaseKdfBytesGenerator
 	{
 		/**
diff --git a/crypto/src/crypto/generators/Kdf2BytesGenerator.cs b/crypto/src/crypto/generators/Kdf2BytesGenerator.cs
index 8a6821980..2dc1d4301 100644
--- a/crypto/src/crypto/generators/Kdf2BytesGenerator.cs
+++ b/crypto/src/crypto/generators/Kdf2BytesGenerator.cs
@@ -1,8 +1,3 @@
-using System;
-
-using Org.BouncyCastle.Crypto;
-using Org.BouncyCastle.Crypto.Parameters;
-
 namespace Org.BouncyCastle.Crypto.Generators
 {
 	/**
@@ -10,7 +5,7 @@ namespace Org.BouncyCastle.Crypto.Generators
 	 * <br/>
 	 * This implementation is based on IEEE P1363/ISO 18033.
 	 */
-	public class Kdf2BytesGenerator
+	public sealed class Kdf2BytesGenerator
 		: BaseKdfBytesGenerator
 	{
 		/**
diff --git a/crypto/src/crypto/generators/Mgf1BytesGenerator.cs b/crypto/src/crypto/generators/Mgf1BytesGenerator.cs
index 23a3aca25..7b4bb3c0b 100644
--- a/crypto/src/crypto/generators/Mgf1BytesGenerator.cs
+++ b/crypto/src/crypto/generators/Mgf1BytesGenerator.cs
@@ -1,117 +1,112 @@
 using System;
-//using Org.BouncyCastle.Math;
-//using Org.BouncyCastle.Security;
-using Org.BouncyCastle.Crypto;
+
 using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Crypto.Utilities;
 
 namespace Org.BouncyCastle.Crypto.Generators
 {
-    /**
-    * Generator for MGF1 as defined in Pkcs 1v2
-    */
-    public class Mgf1BytesGenerator : IDerivationFunction
+    /// <summary>Generator for MGF1 as defined in Pkcs 1v2</summary>
+    public sealed class Mgf1BytesGenerator
+        : IDerivationFunction
     {
-        private IDigest digest;
-        private byte[]  seed;
-        private int     hLen;
-
-        /**
-        * @param digest the digest to be used as the source of Generated bytes
-        */
-        public Mgf1BytesGenerator(
-            IDigest  digest)
+        private readonly IDigest m_digest;
+        private readonly int m_hLen;
+
+        private byte[] m_buffer;
+
+        /// <param name="digest">the digest to be used as the source of generated bytes</param>
+        public Mgf1BytesGenerator(IDigest digest)
         {
-            this.digest = digest;
-            this.hLen = digest.GetDigestSize();
+            m_digest = digest;
+            m_hLen = digest.GetDigestSize();
         }
 
-        public void Init(
-            IDerivationParameters    parameters)
+        public void Init(IDerivationParameters parameters)
         {
-            if (!(typeof(MgfParameters).IsInstanceOfType(parameters)))
-            {
+            if (!(parameters is MgfParameters mgfParameters))
                 throw new ArgumentException("MGF parameters required for MGF1Generator");
-            }
-
-            MgfParameters   p = (MgfParameters)parameters;
 
-            seed = p.GetSeed();
+            m_buffer = new byte[mgfParameters.SeedLength + 4 + m_hLen];
+            mgfParameters.GetSeed(m_buffer, 0);
         }
 
-        /**
-        * return the underlying digest.
-        */
-        public IDigest Digest
+        /// <summary>the underlying digest.</summary>
+        public IDigest Digest => m_digest;
+
+        /// <summary>Fill <c>len</c> bytes of the output buffer with bytes generated from the derivation function.
+        /// </summary>
+        public int GenerateBytes(byte[] output, int outOff, int length)
         {
-            get
+            Check.OutputLength(output, outOff, length, "output buffer too small");
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return GenerateBytes(output.AsSpan(outOff, length));
+#else
+            int hashPos = m_buffer.Length - m_hLen;
+            int counterPos = hashPos - 4;
+            uint counter = 0;
+
+            m_digest.Reset();
+
+            int end = outOff + length;
+            int limit = end - m_hLen;
+
+            while (outOff <= limit)
             {
-                return digest;
+                Pack.UInt32_To_BE(counter++, m_buffer, counterPos);
+
+                m_digest.BlockUpdate(m_buffer, 0, hashPos);
+                m_digest.DoFinal(output, outOff);
+
+                outOff += m_hLen;
             }
-        }
 
-        /**
-        * int to octet string.
-        */
-        private void ItoOSP(
-            int     i,
-            byte[]  sp)
-        {
-            sp[0] = (byte)((uint) i >> 24);
-            sp[1] = (byte)((uint) i >> 16);
-            sp[2] = (byte)((uint) i >> 8);
-            sp[3] = (byte)((uint) i >> 0);
+            if (outOff < end)
+            {
+                Pack.UInt32_To_BE(counter, m_buffer, counterPos);
+
+                m_digest.BlockUpdate(m_buffer, 0, hashPos);
+                m_digest.DoFinal(m_buffer, hashPos);
+
+                Array.Copy(m_buffer, hashPos, output, outOff, end - outOff);
+            }
+
+            return length;
+#endif
         }
 
-        /**
-        * fill len bytes of the output buffer with bytes Generated from
-        * the derivation function.
-        *
-        * @throws DataLengthException if the out buffer is too small.
-        */
-        public int GenerateBytes(
-            byte[]  output,
-            int     outOff,
-            int     length)
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public int GenerateBytes(Span<byte> output)
         {
-			if ((output.Length - length) < outOff)
-			{
-				throw new DataLengthException("output buffer too small");
-			}
+            int hashPos = m_buffer.Length - m_hLen;
+            int counterPos = hashPos - 4;
+            uint counter = 0;
 
-			byte[]  hashBuf = new byte[hLen];
-            byte[]  C = new byte[4];
-            int     counter = 0;
+            m_digest.Reset();
 
-            digest.Reset();
+            int pos = 0, length = output.Length, limit = length - m_hLen;
 
-			if (length > hLen)
-			{
-				do
-				{
-					ItoOSP(counter, C);
+            while (pos <= limit)
+            {
+                Pack.UInt32_To_BE(counter++, m_buffer.AsSpan(counterPos));
 
-					digest.BlockUpdate(seed, 0, seed.Length);
-					digest.BlockUpdate(C, 0, C.Length);
-					digest.DoFinal(hashBuf, 0);
+                m_digest.BlockUpdate(m_buffer.AsSpan(0, hashPos));
+                m_digest.DoFinal(output[pos..]);
 
-					Array.Copy(hashBuf, 0, output, outOff + counter * hLen, hLen);
-				}
-				while (++counter < (length / hLen));
-			}
+                pos += m_hLen;
+            }
 
-            if ((counter * hLen) < length)
+            if (pos < length)
             {
-                ItoOSP(counter, C);
-
-                digest.BlockUpdate(seed, 0, seed.Length);
-                digest.BlockUpdate(C, 0, C.Length);
-                digest.DoFinal(hashBuf, 0);
+                Pack.UInt32_To_BE(counter, m_buffer.AsSpan(counterPos));
 
-                Array.Copy(hashBuf, 0, output, outOff + counter * hLen, length - (counter * hLen));
+                m_digest.BlockUpdate(m_buffer.AsSpan(0, hashPos));
+                m_digest.DoFinal(m_buffer.AsSpan(hashPos));
+                m_buffer.AsSpan(hashPos, length - pos).CopyTo(output[pos..]);
             }
 
             return length;
         }
+#endif
     }
-
 }
diff --git a/crypto/src/crypto/parameters/KdfParameters.cs b/crypto/src/crypto/parameters/KdfParameters.cs
index bc5c905d0..78cf81855 100644
--- a/crypto/src/crypto/parameters/KdfParameters.cs
+++ b/crypto/src/crypto/parameters/KdfParameters.cs
@@ -6,28 +6,26 @@ namespace Org.BouncyCastle.Crypto.Parameters
     /**
      * parameters for Key derivation functions for IEEE P1363a
      */
-    public class KdfParameters : IDerivationParameters
+    public class KdfParameters
+        : IDerivationParameters
     {
-        byte[]  iv;
-        byte[]  shared;
+        private readonly byte[] m_iv;
+        private readonly byte[] m_shared;
 
-        public KdfParameters(
-            byte[]  shared,
-            byte[]  iv)
+        public KdfParameters(byte[] shared, byte[] iv)
         {
-            this.shared = shared;
-            this.iv = iv;
+            m_shared = shared;
+            m_iv = iv;
         }
 
         public byte[] GetSharedSecret()
         {
-            return shared;
+            return m_shared;
         }
 
         public byte[] GetIV()
         {
-            return iv;
+            return m_iv;
         }
     }
-
 }
diff --git a/crypto/src/crypto/parameters/MgfParameters.cs b/crypto/src/crypto/parameters/MgfParameters.cs
index 11983b877..7915567e2 100644
--- a/crypto/src/crypto/parameters/MgfParameters.cs
+++ b/crypto/src/crypto/parameters/MgfParameters.cs
@@ -1,31 +1,42 @@
 using System;
 
+using Org.BouncyCastle.Utilities;
+
 namespace Org.BouncyCastle.Crypto.Parameters
 {
 	/// <remarks>Parameters for mask derivation functions.</remarks>
-    public class MgfParameters
+    public sealed class MgfParameters
 		: IDerivationParameters
     {
-        private readonly byte[] seed;
+        private readonly byte[] m_seed;
 
-		public MgfParameters(
-            byte[] seed)
+		public MgfParameters(byte[] seed)
 			: this(seed, 0, seed.Length)
         {
         }
 
-		public MgfParameters(
-            byte[]  seed,
-            int     off,
-            int     len)
+		public MgfParameters(byte[] seed, int off, int len)
+        {
+            m_seed = Arrays.CopyOfRange(seed, off, len);
+        }
+
+        public byte[] GetSeed()
         {
-            this.seed = new byte[len];
-            Array.Copy(seed, off, this.seed, 0, len);
+            return (byte[])m_seed.Clone();
         }
 
-		public byte[] GetSeed()
+        public void GetSeed(byte[] buffer, int offset)
         {
-            return (byte[]) seed.Clone();
+            m_seed.CopyTo(buffer, offset);
         }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void GetSeed(Span<byte> output)
+        {
+            m_seed.CopyTo(output);
+        }
+#endif
+
+        public int SeedLength => m_seed.Length;
     }
 }