summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2022-10-05 00:44:55 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2022-10-05 00:44:55 +0700
commit55df84d7ae0c22c3459f322d6845e7ce321b379a (patch)
treed1bc2ce6c4dfa59c2b96826c24274b3c061e25dd
parentSpan-based constructors in parameter classes (diff)
downloadBouncyCastle.NET-ed25519-55df84d7ae0c22c3459f322d6845e7ce321b379a.tar.xz
Various span usage in TLS code
-rw-r--r--crypto/src/tls/TlsProtocol.cs10
-rw-r--r--crypto/src/tls/TlsUtilities.cs41
-rw-r--r--crypto/src/tls/crypto/TlsCryptoUtilities.cs47
-rw-r--r--crypto/src/tls/crypto/TlsMac.cs4
-rw-r--r--crypto/src/tls/crypto/TlsSecret.cs14
-rw-r--r--crypto/src/tls/crypto/impl/AbstractTlsSecret.cs41
-rw-r--r--crypto/src/tls/crypto/impl/TlsAeadCipher.cs28
-rw-r--r--crypto/src/tls/crypto/impl/TlsAeadCipherImpl.cs4
-rw-r--r--crypto/src/tls/crypto/impl/TlsBlockCipher.cs61
-rw-r--r--crypto/src/tls/crypto/impl/TlsBlockCipherImpl.cs8
-rw-r--r--crypto/src/tls/crypto/impl/TlsImplUtilities.cs18
-rw-r--r--crypto/src/tls/crypto/impl/TlsNullCipher.cs29
-rw-r--r--crypto/src/tls/crypto/impl/bc/BcChaCha20Poly1305.cs8
-rw-r--r--crypto/src/tls/crypto/impl/bc/BcSsl3Hmac.cs10
-rw-r--r--crypto/src/tls/crypto/impl/bc/BcTlsAeadCipherImpl.cs7
-rw-r--r--crypto/src/tls/crypto/impl/bc/BcTlsBlockCipherImpl.cs14
-rw-r--r--crypto/src/tls/crypto/impl/bc/BcTlsHmac.cs7
-rw-r--r--crypto/src/tls/crypto/impl/bc/BcTlsSecret.cs144
18 files changed, 465 insertions, 30 deletions
diff --git a/crypto/src/tls/TlsProtocol.cs b/crypto/src/tls/TlsProtocol.cs
index b5a955bed..3461e9b58 100644
--- a/crypto/src/tls/TlsProtocol.cs
+++ b/crypto/src/tls/TlsProtocol.cs
@@ -1362,7 +1362,12 @@ namespace Org.BouncyCastle.Tls
             SecurityParameters securityParameters = context.SecurityParameters;
             bool isServerContext = context.IsServer;
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Span<byte> verify_data = stackalloc byte[securityParameters.VerifyDataLength];
+            TlsUtilities.ReadFully(verify_data, buf);
+#else
             byte[] verify_data = TlsUtilities.ReadFully(securityParameters.VerifyDataLength, buf);
+#endif
 
             AssertEmpty(buf);
 
@@ -1397,7 +1402,12 @@ namespace Org.BouncyCastle.Tls
             SecurityParameters securityParameters = context.SecurityParameters;
             bool isServerContext = context.IsServer;
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Span<byte> verify_data = stackalloc byte[securityParameters.VerifyDataLength];
+            TlsUtilities.ReadFully(verify_data, buf);
+#else
             byte[] verify_data = TlsUtilities.ReadFully(securityParameters.VerifyDataLength, buf);
+#endif
 
             AssertEmpty(buf);
 
diff --git a/crypto/src/tls/TlsUtilities.cs b/crypto/src/tls/TlsUtilities.cs
index a417336be..f12198082 100644
--- a/crypto/src/tls/TlsUtilities.cs
+++ b/crypto/src/tls/TlsUtilities.cs
@@ -309,6 +309,13 @@ namespace Org.BouncyCastle.Tls
             buf[offset] = (byte)i;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public static void WriteUint8(int i, Span<byte> buf)
+        {
+            buf[0] = (byte)i;
+        }
+#endif
+
         public static void WriteUint16(int i, Stream output)
         {
             output.WriteByte((byte)(i >> 8));
@@ -321,6 +328,14 @@ namespace Org.BouncyCastle.Tls
             buf[offset + 1] = (byte)i;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public static void WriteUint16(int i, Span<byte> buf)
+        {
+            buf[0] = (byte)(i >> 8);
+            buf[1] = (byte)i;
+        }
+#endif
+
         public static void WriteUint24(int i, Stream output)
         {
             output.WriteByte((byte)(i >> 16));
@@ -409,6 +424,15 @@ namespace Org.BouncyCastle.Tls
             Array.Copy(data, 0, buf, off + 1, data.Length);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public static void WriteOpaque8(ReadOnlySpan<byte> data, Span<byte> buf)
+        {
+            CheckUint8(data.Length);
+            WriteUint8(data.Length, buf);
+            data.CopyTo(buf[1..]);
+        }
+#endif
+
         public static void WriteOpaque16(byte[] buf, Stream output)
         {
             CheckUint16(buf.Length);
@@ -826,6 +850,15 @@ namespace Org.BouncyCastle.Tls
                 throw new EndOfStreamException();
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public static void ReadFully(Span<byte> buf, Stream input)
+        {
+            int length = buf.Length;
+            if (length > 0 && length != Streams.ReadFully(input, buf))
+                throw new EndOfStreamException();
+        }
+#endif
+
         public static byte[] ReadOpaque8(Stream input)
         {
             short length = ReadUint8(input);
@@ -1382,6 +1415,14 @@ namespace Org.BouncyCastle.Tls
             return secret.DeriveUsingPrf(securityParameters.PrfAlgorithm, asciiLabel, seed, length);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public static TlsSecret Prf(SecurityParameters securityParameters, TlsSecret secret,
+            ReadOnlySpan<char> asciiLabel, ReadOnlySpan<byte> seed, int length)
+        {
+            return secret.DeriveUsingPrf(securityParameters.PrfAlgorithm, asciiLabel, seed, length);
+        }
+#endif
+
         public static byte[] Clone(byte[] data)
         {
             return null == data ? null : data.Length == 0 ? EmptyBytes : (byte[])data.Clone();
diff --git a/crypto/src/tls/crypto/TlsCryptoUtilities.cs b/crypto/src/tls/crypto/TlsCryptoUtilities.cs
index 98ac87a83..b1b42f4bf 100644
--- a/crypto/src/tls/crypto/TlsCryptoUtilities.cs
+++ b/crypto/src/tls/crypto/TlsCryptoUtilities.cs
@@ -183,6 +183,9 @@ namespace Org.BouncyCastle.Tls.Crypto
         public static TlsSecret HkdfExpandLabel(TlsSecret secret, int cryptoHashAlgorithm, string label,
             byte[] context, int length)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return HkdfExpandLabel(secret, cryptoHashAlgorithm, label.AsSpan(), context.AsSpan(), length);
+#else
             int labelLength = label.Length;
             if (labelLength < 1)
                 throw new TlsFatalAlert(AlertDescription.internal_error);
@@ -219,6 +222,50 @@ namespace Org.BouncyCastle.Tls.Crypto
             }
 
             return secret.HkdfExpand(cryptoHashAlgorithm, hkdfLabel, length);
+#endif
         }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        /// <exception cref="IOException"/>
+        public static TlsSecret HkdfExpandLabel(TlsSecret secret, int cryptoHashAlgorithm, ReadOnlySpan<char> label,
+            ReadOnlySpan<byte> context, int length)
+        {
+            int labelLength = label.Length;
+            if (labelLength < 1)
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+
+            int contextLength = context.Length;
+            int expandedLabelLength = Tls13Prefix.Length + labelLength;
+
+            Span<byte> hkdfLabel = stackalloc byte[2 + (1 + expandedLabelLength) + (1 + contextLength)];
+
+            // uint16 length
+            {
+                TlsUtilities.CheckUint16(length);
+                TlsUtilities.WriteUint16(length, hkdfLabel);
+            }
+
+            // opaque label<7..255>
+            {
+                TlsUtilities.CheckUint8(expandedLabelLength);
+                TlsUtilities.WriteUint8(expandedLabelLength, hkdfLabel[2..]);
+
+                Tls13Prefix.CopyTo(hkdfLabel[3..]);
+
+                int labelPos = 2 + (1 + Tls13Prefix.Length);
+                for (int i = 0; i < labelLength; ++i)
+                {
+                    hkdfLabel[labelPos + i] = (byte)label[i];
+                }
+            }
+
+            // context
+            {
+                TlsUtilities.WriteOpaque8(context, hkdfLabel.Slice(2 + (1 + expandedLabelLength)));
+            }
+
+            return secret.HkdfExpand(cryptoHashAlgorithm, hkdfLabel, length);
+        }
+#endif
     }
 }
diff --git a/crypto/src/tls/crypto/TlsMac.cs b/crypto/src/tls/crypto/TlsMac.cs
index f92d946f1..a898a9bcc 100644
--- a/crypto/src/tls/crypto/TlsMac.cs
+++ b/crypto/src/tls/crypto/TlsMac.cs
@@ -11,6 +11,10 @@ namespace Org.BouncyCastle.Tls.Crypto
         /// <param name="keyLen">length of the key in the array.</param>
         void SetKey(byte[] key, int keyOff, int keyLen);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        void SetKey(ReadOnlySpan<byte> key);
+#endif
+
         /// <summary>Update the MAC with the passed in input.</summary>
         /// <param name="input">input array containing the data.</param>
         /// <param name="inOff">offset into the input array the input starts at.</param>
diff --git a/crypto/src/tls/crypto/TlsSecret.cs b/crypto/src/tls/crypto/TlsSecret.cs
index 8aea34bcf..a404b9901 100644
--- a/crypto/src/tls/crypto/TlsSecret.cs
+++ b/crypto/src/tls/crypto/TlsSecret.cs
@@ -23,6 +23,10 @@ namespace Org.BouncyCastle.Tls.Crypto
         /// <returns>the new secret.</returns>
         TlsSecret DeriveUsingPrf(int prfAlgorithm, string label, byte[] seed, int length);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        TlsSecret DeriveUsingPrf(int prfAlgorithm, ReadOnlySpan<char> label, ReadOnlySpan<byte> seed, int length);
+#endif
+
         /// <summary>Destroy the internal state of the secret.</summary>
         /// <remarks>
         /// After this call, any attempt to use the <see cref="TlsSecret"/> will result in an
@@ -44,6 +48,10 @@ namespace Org.BouncyCastle.Tls.Crypto
         /// <returns>the secret's internal data.</returns>
         byte[] Extract();
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        void ExtractTo(Span<byte> output);
+#endif
+
         /// <summary>RFC 5869 HKDF-Expand function, with this secret's data as the pseudo-random key ('prk').</summary>
         /// <param name="cryptoHashAlgorithm">the hash algorithm to instantiate HMAC with. See
         /// <see cref="CryptoHashAlgorithm"/> for values.</param>
@@ -52,6 +60,10 @@ namespace Org.BouncyCastle.Tls.Crypto
         /// <returns> output keying material (of 'length' octets).</returns>
         TlsSecret HkdfExpand(int cryptoHashAlgorithm, byte[] info, int length);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        TlsSecret HkdfExpand(int cryptoHashAlgorithm, ReadOnlySpan<byte> info, int length);
+#endif
+
         /// <summary>RFC 5869 HKDF-Extract function, with this secret's data as the 'salt'.</summary>
         /// <remarks>
         /// The <see cref="TlsSecret"/> does not keep a copy of the data. After this call, any attempt to use
@@ -64,5 +76,7 @@ namespace Org.BouncyCastle.Tls.Crypto
         TlsSecret HkdfExtract(int cryptoHashAlgorithm, TlsSecret ikm);
 
         bool IsAlive();
+
+        int Length { get; }
     }
 }
diff --git a/crypto/src/tls/crypto/impl/AbstractTlsSecret.cs b/crypto/src/tls/crypto/impl/AbstractTlsSecret.cs
index cc07f978f..2a7ffe116 100644
--- a/crypto/src/tls/crypto/impl/AbstractTlsSecret.cs
+++ b/crypto/src/tls/crypto/impl/AbstractTlsSecret.cs
@@ -20,7 +20,7 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
         /// <param name="data">the byte[] making up the secret value.</param>
         protected AbstractTlsSecret(byte[] data)
         {
-            this.m_data = data;
+            m_data = data;
         }
 
         protected virtual void CheckAlive()
@@ -46,6 +46,11 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
 
         public abstract TlsSecret DeriveUsingPrf(int prfAlgorithm, string label, byte[] seed, int length);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public abstract TlsSecret DeriveUsingPrf(int prfAlgorithm, ReadOnlySpan<char> label, ReadOnlySpan<byte> seed,
+            int length);
+#endif
+
         public virtual void Destroy()
         {
             lock (this)
@@ -54,7 +59,7 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
                 {
                     // TODO Is there a way to ensure the data is really overwritten?
                     Array.Clear(m_data, 0, m_data.Length);
-                    this.m_data = null;
+                    m_data = null;
                 }
             }
         }
@@ -77,13 +82,30 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
                 CheckAlive();
 
                 byte[] result = m_data;
-                this.m_data = null;
+                m_data = null;
                 return result;
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public virtual void ExtractTo(Span<byte> output)
+        {
+            lock (this)
+            {
+                CheckAlive();
+
+                m_data.CopyTo(output);
+                m_data = null;
+            }
+        }
+#endif
+
         public abstract TlsSecret HkdfExpand(int cryptoHashAlgorithm, byte[] info, int length);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public abstract TlsSecret HkdfExpand(int cryptoHashAlgorithm, ReadOnlySpan<byte> info, int length);
+#endif
+
         public abstract TlsSecret HkdfExtract(int cryptoHashAlgorithm, TlsSecret ikm);
 
         public virtual bool IsAlive()
@@ -94,6 +116,19 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
             }
         }
 
+        public virtual int Length
+        {
+            get
+            {
+                lock (this)
+                {
+                    CheckAlive();
+
+                    return m_data.Length;
+                }
+            }
+        }
+
         internal virtual byte[] CopyData()
         {
             lock (this)
diff --git a/crypto/src/tls/crypto/impl/TlsAeadCipher.cs b/crypto/src/tls/crypto/impl/TlsAeadCipher.cs
index 04f9ce80f..73fc9e98a 100644
--- a/crypto/src/tls/crypto/impl/TlsAeadCipher.cs
+++ b/crypto/src/tls/crypto/impl/TlsAeadCipher.cs
@@ -72,6 +72,31 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
             }
 
             int keyBlockSize = (2 * keySize) + (2 * m_fixed_iv_length);
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Span<byte> keyBlock = stackalloc byte[keyBlockSize];
+            TlsImplUtilities.CalculateKeyBlock(cryptoParams, keyBlock);
+
+            if (isServer)
+            {
+                decryptCipher.SetKey(keyBlock[..keySize]); keyBlock = keyBlock[keySize..];
+                encryptCipher.SetKey(keyBlock[..keySize]); keyBlock = keyBlock[keySize..];
+
+                keyBlock[..m_fixed_iv_length].CopyTo(m_decryptNonce); keyBlock = keyBlock[m_fixed_iv_length..];
+                keyBlock[..m_fixed_iv_length].CopyTo(m_encryptNonce); keyBlock = keyBlock[m_fixed_iv_length..];
+            }
+            else
+            {
+                encryptCipher.SetKey(keyBlock[..keySize]); keyBlock = keyBlock[keySize..];
+                decryptCipher.SetKey(keyBlock[..keySize]); keyBlock = keyBlock[keySize..];
+
+                keyBlock[..m_fixed_iv_length].CopyTo(m_encryptNonce); keyBlock = keyBlock[m_fixed_iv_length..];
+                keyBlock[..m_fixed_iv_length].CopyTo(m_decryptNonce); keyBlock = keyBlock[m_fixed_iv_length..];
+            }
+
+            if (!keyBlock.IsEmpty)
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+#else
             byte[] keyBlock = TlsImplUtilities.CalculateKeyBlock(cryptoParams, keyBlockSize);
             int pos = 0;
 
@@ -92,8 +117,9 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
                 Array.Copy(keyBlock, pos, m_decryptNonce, 0, m_fixed_iv_length); pos += m_fixed_iv_length;
             }
 
-            if (keyBlockSize != pos)
+            if (pos != keyBlockSize)
                 throw new TlsFatalAlert(AlertDescription.internal_error);
+#endif
 
             int nonceLength = m_fixed_iv_length + m_record_iv_length;
 
diff --git a/crypto/src/tls/crypto/impl/TlsAeadCipherImpl.cs b/crypto/src/tls/crypto/impl/TlsAeadCipherImpl.cs
index 44e6fda84..4c69c0b72 100644
--- a/crypto/src/tls/crypto/impl/TlsAeadCipherImpl.cs
+++ b/crypto/src/tls/crypto/impl/TlsAeadCipherImpl.cs
@@ -13,6 +13,10 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
         /// <exception cref="IOException"/>
         void SetKey(byte[] key, int keyOff, int keyLen);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        void SetKey(ReadOnlySpan<byte> key);
+#endif
+
         /// <summary>Initialise the parameters for the AEAD operator.</summary>
         /// <param name="nonce">the nonce.</param>
         /// <param name="macSize">MAC size in bytes.</param>
diff --git a/crypto/src/tls/crypto/impl/TlsBlockCipher.cs b/crypto/src/tls/crypto/impl/TlsBlockCipher.cs
index 64cfc752a..c8774f9bb 100644
--- a/crypto/src/tls/crypto/impl/TlsBlockCipher.cs
+++ b/crypto/src/tls/crypto/impl/TlsBlockCipher.cs
@@ -65,27 +65,53 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
                 serverCipher = decryptCipher;
             }
 
-            int key_block_size = (2 * cipherKeySize) + clientMac.MacLength + serverMac.MacLength;
+            int keyBlockSize = (2 * cipherKeySize) + clientMac.MacLength + serverMac.MacLength;
 
             // From TLS 1.1 onwards, block ciphers don't need IVs from the key_block
             if (!m_useExplicitIV)
             {
-                key_block_size += clientCipher.GetBlockSize() + serverCipher.GetBlockSize();
+                keyBlockSize += clientCipher.GetBlockSize() + serverCipher.GetBlockSize();
             }
 
-            byte[] key_block = TlsImplUtilities.CalculateKeyBlock(cryptoParams, key_block_size);
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Span<byte> keyBlock = stackalloc byte[keyBlockSize];
+            TlsImplUtilities.CalculateKeyBlock(cryptoParams, keyBlock);
 
-            int offset = 0;
+            clientMac.SetKey(keyBlock[..clientMac.MacLength]); keyBlock = keyBlock[clientMac.MacLength..];
+            serverMac.SetKey(keyBlock[..serverMac.MacLength]); keyBlock = keyBlock[serverMac.MacLength..];
 
-            clientMac.SetKey(key_block, offset, clientMac.MacLength);
-            offset += clientMac.MacLength;
-            serverMac.SetKey(key_block, offset, serverMac.MacLength);
-            offset += serverMac.MacLength;
+            clientCipher.SetKey(keyBlock[..cipherKeySize]); keyBlock = keyBlock[cipherKeySize..];
+            serverCipher.SetKey(keyBlock[..cipherKeySize]); keyBlock = keyBlock[cipherKeySize..];
 
-            clientCipher.SetKey(key_block, offset, cipherKeySize);
-            offset += cipherKeySize;
-            serverCipher.SetKey(key_block, offset, cipherKeySize);
-            offset += cipherKeySize;
+            int clientIVLength = clientCipher.GetBlockSize();
+            int serverIVLength = serverCipher.GetBlockSize();
+
+            if (m_useExplicitIV)
+            {
+                clientCipher.Init(stackalloc byte[clientIVLength]);
+                serverCipher.Init(stackalloc byte[serverIVLength]);
+            }
+            else
+            {
+                clientCipher.Init(keyBlock[..clientIVLength]); keyBlock = keyBlock[clientIVLength..];
+                serverCipher.Init(keyBlock[..serverIVLength]); keyBlock = keyBlock[serverIVLength..];
+            }
+
+            if (!keyBlock.IsEmpty)
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+#else
+            byte[] keyBlock = TlsImplUtilities.CalculateKeyBlock(cryptoParams, keyBlockSize);
+            int pos = 0;
+
+            clientMac.SetKey(keyBlock, pos, clientMac.MacLength);
+            pos += clientMac.MacLength;
+            serverMac.SetKey(keyBlock, pos, serverMac.MacLength);
+            pos += serverMac.MacLength;
+
+            clientCipher.SetKey(keyBlock, pos, cipherKeySize);
+            pos += cipherKeySize;
+            serverCipher.SetKey(keyBlock, pos, cipherKeySize);
+            pos += cipherKeySize;
 
             int clientIVLength = clientCipher.GetBlockSize();
             int serverIVLength = serverCipher.GetBlockSize();
@@ -97,14 +123,15 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
             }
             else
             {
-                clientCipher.Init(key_block, offset, clientIVLength);
-                offset += clientIVLength;
-                serverCipher.Init(key_block, offset, serverIVLength);
-                offset += serverIVLength;
+                clientCipher.Init(keyBlock, pos, clientIVLength);
+                pos += clientIVLength;
+                serverCipher.Init(keyBlock, pos, serverIVLength);
+                pos += serverIVLength;
             }
 
-            if (offset != key_block_size)
+            if (pos != keyBlockSize)
                 throw new TlsFatalAlert(AlertDescription.internal_error);
+#endif
 
             if (cryptoParams.IsServer)
             {
diff --git a/crypto/src/tls/crypto/impl/TlsBlockCipherImpl.cs b/crypto/src/tls/crypto/impl/TlsBlockCipherImpl.cs
index 7df2ed70f..41fd9b9bf 100644
--- a/crypto/src/tls/crypto/impl/TlsBlockCipherImpl.cs
+++ b/crypto/src/tls/crypto/impl/TlsBlockCipherImpl.cs
@@ -13,6 +13,10 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
         /// <exception cref="IOException"/>
         void SetKey(byte[] key, int keyOff, int keyLen);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        void SetKey(ReadOnlySpan<byte> key);
+#endif
+
         /// <summary>Initialise the parameters for operator.</summary>
         /// <param name="iv">array holding the initialization vector (IV).</param>
         /// <param name="ivOff">offset into the array the IV starts at.</param>
@@ -20,6 +24,10 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
         /// <exception cref="IOException">if the parameters are inappropriate.</exception>
         void Init(byte[] iv, int ivOff, int ivLen);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        void Init(ReadOnlySpan<byte> iv);
+#endif
+
         /// <summary>Perform the cipher encryption/decryption returning the output in output.</summary>
         /// <remarks>
         /// Note: we have to use DoFinal() here as it is the only way to guarantee output from the underlying cipher.
diff --git a/crypto/src/tls/crypto/impl/TlsImplUtilities.cs b/crypto/src/tls/crypto/impl/TlsImplUtilities.cs
index dc5a96288..8f7b75cc6 100644
--- a/crypto/src/tls/crypto/impl/TlsImplUtilities.cs
+++ b/crypto/src/tls/crypto/impl/TlsImplUtilities.cs
@@ -60,5 +60,23 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
             byte[] seed = Arrays.Concatenate(securityParameters.ServerRandom, securityParameters.ClientRandom);
             return master_secret.DeriveUsingPrf(prfAlgorithm, ExporterLabel.key_expansion, seed, length).Extract();
         }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public static void CalculateKeyBlock(TlsCryptoParameters cryptoParams, Span<byte> keyBlock)
+        {
+            SecurityParameters securityParameters = cryptoParams.SecurityParameters;
+            TlsSecret master_secret = securityParameters.MasterSecret;
+            int prfAlgorithm = securityParameters.PrfAlgorithm;
+
+            Span<byte> cr = securityParameters.ClientRandom, sr = securityParameters.ServerRandom;
+            Span<byte> seed = stackalloc byte[sr.Length + cr.Length];
+            sr.CopyTo(seed);
+            cr.CopyTo(seed[sr.Length..]);
+
+            TlsSecret derived = master_secret.DeriveUsingPrf(prfAlgorithm, ExporterLabel.key_expansion, seed,
+                keyBlock.Length);
+            derived.ExtractTo(keyBlock);
+        }
+#endif
     }
 }
diff --git a/crypto/src/tls/crypto/impl/TlsNullCipher.cs b/crypto/src/tls/crypto/impl/TlsNullCipher.cs
index 3ca4951a6..b21e46eed 100644
--- a/crypto/src/tls/crypto/impl/TlsNullCipher.cs
+++ b/crypto/src/tls/crypto/impl/TlsNullCipher.cs
@@ -16,20 +16,31 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl
             if (TlsImplUtilities.IsTlsV13(cryptoParams))
                 throw new TlsFatalAlert(AlertDescription.internal_error);
 
-            this.m_cryptoParams = cryptoParams;
+            m_cryptoParams = cryptoParams;
 
-            int key_block_size = clientMac.MacLength + serverMac.MacLength;
-            byte[] key_block = TlsImplUtilities.CalculateKeyBlock(cryptoParams, key_block_size);
+            int keyBlockSize = clientMac.MacLength + serverMac.MacLength;
 
-            int offset = 0;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Span<byte> keyBlock = stackalloc byte[keyBlockSize];
+            TlsImplUtilities.CalculateKeyBlock(cryptoParams, keyBlock);
 
-            clientMac.SetKey(key_block, offset, clientMac.MacLength);
-            offset += clientMac.MacLength;
-            serverMac.SetKey(key_block, offset, serverMac.MacLength);
-            offset += serverMac.MacLength;
+            clientMac.SetKey(keyBlock[..clientMac.MacLength]); keyBlock = keyBlock[clientMac.MacLength..];
+            serverMac.SetKey(keyBlock[..serverMac.MacLength]); keyBlock = keyBlock[serverMac.MacLength..];
 
-            if (offset != key_block_size)
+            if (!keyBlock.IsEmpty)
                 throw new TlsFatalAlert(AlertDescription.internal_error);
+#else
+            byte[] keyBlock = TlsImplUtilities.CalculateKeyBlock(cryptoParams, keyBlockSize);
+            int pos = 0;
+
+            clientMac.SetKey(keyBlock, pos, clientMac.MacLength);
+            pos += clientMac.MacLength;
+            serverMac.SetKey(keyBlock, pos, serverMac.MacLength);
+            pos += serverMac.MacLength;
+
+            if (pos != keyBlockSize)
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+#endif
 
             if (cryptoParams.IsServer)
             {
diff --git a/crypto/src/tls/crypto/impl/bc/BcChaCha20Poly1305.cs b/crypto/src/tls/crypto/impl/bc/BcChaCha20Poly1305.cs
index ab78d0ce2..6b87c100a 100644
--- a/crypto/src/tls/crypto/impl/bc/BcChaCha20Poly1305.cs
+++ b/crypto/src/tls/crypto/impl/bc/BcChaCha20Poly1305.cs
@@ -102,6 +102,14 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
             m_cipher.Init(m_isEncrypting, new ParametersWithIV(cipherKey, Zeroes, 0, 12));
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void SetKey(ReadOnlySpan<byte> key)
+        {
+            KeyParameter cipherKey = new KeyParameter(key);
+            m_cipher.Init(m_isEncrypting, new ParametersWithIV(cipherKey, Zeroes[..12]));
+        }
+#endif
+
         private void InitMac()
         {
             byte[] firstBlock = new byte[64];
diff --git a/crypto/src/tls/crypto/impl/bc/BcSsl3Hmac.cs b/crypto/src/tls/crypto/impl/bc/BcSsl3Hmac.cs
index df2ccd2c1..f26a50d46 100644
--- a/crypto/src/tls/crypto/impl/bc/BcSsl3Hmac.cs
+++ b/crypto/src/tls/crypto/impl/bc/BcSsl3Hmac.cs
@@ -1,6 +1,7 @@
 using System;
 
 using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
@@ -51,6 +52,15 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
             Reset();
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void SetKey(ReadOnlySpan<byte> key)
+        {
+            this.m_secret = key.ToArray();
+
+            Reset();
+        }
+#endif
+
         public virtual void Update(byte[] input, int inOff, int len)
         {
             m_digest.BlockUpdate(input, inOff, len);
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsAeadCipherImpl.cs b/crypto/src/tls/crypto/impl/bc/BcTlsAeadCipherImpl.cs
index 1667e5d6f..0b2781326 100644
--- a/crypto/src/tls/crypto/impl/bc/BcTlsAeadCipherImpl.cs
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsAeadCipherImpl.cs
@@ -25,6 +25,13 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
             this.key = new KeyParameter(key, keyOff, keyLen);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void SetKey(ReadOnlySpan<byte> key)
+        {
+            this.key = new KeyParameter(key);
+        }
+#endif
+
         public void Init(byte[] nonce, int macSize, byte[] additionalData)
         {
             m_cipher.Init(m_isEncrypting, new AeadParameters(key, macSize * 8, nonce, additionalData));
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsBlockCipherImpl.cs b/crypto/src/tls/crypto/impl/bc/BcTlsBlockCipherImpl.cs
index b51d38c0d..b7421dfb9 100644
--- a/crypto/src/tls/crypto/impl/bc/BcTlsBlockCipherImpl.cs
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsBlockCipherImpl.cs
@@ -24,11 +24,25 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
             this.key = new KeyParameter(key, keyOff, keyLen);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void SetKey(ReadOnlySpan<byte> key)
+        {
+            this.key = new KeyParameter(key);
+        }
+#endif
+
         public void Init(byte[] iv, int ivOff, int ivLen)
         {
             m_cipher.Init(m_isEncrypting, new ParametersWithIV(key, iv, ivOff, ivLen));
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void Init(ReadOnlySpan<byte> iv)
+        {
+            m_cipher.Init(m_isEncrypting, new ParametersWithIV(key, iv));
+        }
+#endif
+
         public int DoFinal(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset)
         {
             int blockSize = m_cipher.GetBlockSize();
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsHmac.cs b/crypto/src/tls/crypto/impl/bc/BcTlsHmac.cs
index 485a3f744..7a2318a31 100644
--- a/crypto/src/tls/crypto/impl/bc/BcTlsHmac.cs
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsHmac.cs
@@ -20,6 +20,13 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
             m_hmac.Init(new KeyParameter(key, keyOff, keyLen));
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public void SetKey(ReadOnlySpan<byte> key)
+        {
+            m_hmac.Init(new KeyParameter(key));
+        }
+#endif
+
         public void Update(byte[] input, int inOff, int length)
         {
             m_hmac.BlockUpdate(input, inOff, length);
diff --git a/crypto/src/tls/crypto/impl/bc/BcTlsSecret.cs b/crypto/src/tls/crypto/impl/bc/BcTlsSecret.cs
index 9cd060d18..6fe2da491 100644
--- a/crypto/src/tls/crypto/impl/bc/BcTlsSecret.cs
+++ b/crypto/src/tls/crypto/impl/bc/BcTlsSecret.cs
@@ -74,8 +74,34 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
             }
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override TlsSecret DeriveUsingPrf(int prfAlgorithm, ReadOnlySpan<char> label, ReadOnlySpan<byte> seed,
+            int length)
+        {
+            lock (this)
+            {
+                CheckAlive();
+
+                switch (prfAlgorithm)
+                {
+                case PrfAlgorithm.tls13_hkdf_sha256:
+                    return TlsCryptoUtilities.HkdfExpandLabel(this, CryptoHashAlgorithm.sha256, label, seed, length);
+                case PrfAlgorithm.tls13_hkdf_sha384:
+                    return TlsCryptoUtilities.HkdfExpandLabel(this, CryptoHashAlgorithm.sha384, label, seed, length);
+                case PrfAlgorithm.tls13_hkdf_sm3:
+                    return TlsCryptoUtilities.HkdfExpandLabel(this, CryptoHashAlgorithm.sm3, label, seed, length);
+                default:
+                    return m_crypto.AdoptLocalSecret(Prf(prfAlgorithm, label, seed, length));
+                }
+            }
+        }
+#endif
+
         public override TlsSecret HkdfExpand(int cryptoHashAlgorithm, byte[] info, int length)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return HkdfExpand(cryptoHashAlgorithm, info.AsSpan(), length);
+#else
             lock (this)
             {
                 if (length < 1)
@@ -118,7 +144,56 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
 
                 return m_crypto.AdoptLocalSecret(okm);
             }
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override TlsSecret HkdfExpand(int cryptoHashAlgorithm, ReadOnlySpan<byte> info, int length)
+        {
+            lock (this)
+            {
+                if (length < 1)
+                    return m_crypto.AdoptLocalSecret(TlsUtilities.EmptyBytes);
+
+                int hashLen = TlsCryptoUtilities.GetHashOutputSize(cryptoHashAlgorithm);
+                if (length > (255 * hashLen))
+                    throw new ArgumentException("must be <= 255 * (output size of 'hashAlgorithm')", "length");
+
+                CheckAlive();
+
+                ReadOnlySpan<byte> prk = m_data;
+
+                HMac hmac = new HMac(m_crypto.CreateDigest(cryptoHashAlgorithm));
+                hmac.Init(new KeyParameter(prk));
+
+                byte[] okm = new byte[length];
+
+                Span<byte> t = stackalloc byte[hashLen];
+                byte counter = 0x00;
+
+                int pos = 0;
+                for (;;)
+                {
+                    hmac.BlockUpdate(info);
+                    hmac.Update(++counter);
+                    hmac.DoFinal(t);
+
+                    int remaining = length - pos;
+                    if (remaining <= hashLen)
+                    {
+                        t[..remaining].CopyTo(okm.AsSpan(pos));
+                        break;
+                    }
+
+                    t.CopyTo(okm.AsSpan(pos));
+                    pos += hashLen;
+                    hmac.BlockUpdate(t);
+                }
+
+                return m_crypto.AdoptLocalSecret(okm);
+            }
         }
+#endif
 
         public override TlsSecret HkdfExtract(int cryptoHashAlgorithm, TlsSecret ikm)
         {
@@ -187,8 +262,33 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
             return Prf_1_2(prfAlgorithm, labelSeed, length);
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        protected virtual byte[] Prf(int prfAlgorithm, ReadOnlySpan<char> label, ReadOnlySpan<byte> seed, int length)
+        {
+            if (PrfAlgorithm.ssl_prf_legacy == prfAlgorithm)
+                return Prf_Ssl(seed, length);
+
+            byte[] labelSeed = new byte[label.Length + seed.Length];
+
+            for (int i = 0; i < label.Length; ++i)
+            {
+                labelSeed[i] = (byte)label[i];
+            }
+
+            seed.CopyTo(labelSeed.AsSpan(label.Length));
+
+            if (PrfAlgorithm.tls_prf_legacy == prfAlgorithm)
+                return Prf_1_0(labelSeed, length);
+
+            return Prf_1_2(prfAlgorithm, labelSeed, length);
+        }
+#endif
+
         protected virtual byte[] Prf_Ssl(byte[] seed, int length)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return Prf_Ssl(seed.AsSpan(), length);
+#else
             IDigest md5 = m_crypto.CreateDigest(CryptoHashAlgorithm.md5);
             IDigest sha1 = m_crypto.CreateDigest(CryptoHashAlgorithm.sha1);
 
@@ -226,7 +326,51 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl.BC
             }
 
             return result;
+#endif
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        protected virtual byte[] Prf_Ssl(ReadOnlySpan<byte> seed, int length)
+        {
+            IDigest md5 = m_crypto.CreateDigest(CryptoHashAlgorithm.md5);
+            IDigest sha1 = m_crypto.CreateDigest(CryptoHashAlgorithm.sha1);
+
+            int md5Size = md5.GetDigestSize();
+            int sha1Size = sha1.GetDigestSize();
+
+            Span<byte> tmp = stackalloc byte[System.Math.Max(md5Size, sha1Size)];
+            byte[] result = new byte[length];
+
+            int constLen = 1, constPos = 0, resultPos = 0;
+            while (resultPos < length)
+            {
+                sha1.BlockUpdate(Ssl3Const.AsSpan(constPos, constLen));
+                constPos += constLen++;
+
+                sha1.BlockUpdate(m_data);
+                sha1.BlockUpdate(seed);
+                sha1.DoFinal(tmp);
+
+                md5.BlockUpdate(m_data);
+                md5.BlockUpdate(tmp[..sha1Size]);
+
+                int remaining = length - resultPos;
+                if (remaining < md5Size)
+                {
+                    md5.DoFinal(tmp);
+                    tmp[..remaining].CopyTo(result.AsSpan(resultPos));
+                    resultPos += remaining;
+                }
+                else
+                {
+                    md5.DoFinal(result.AsSpan(resultPos));
+                    resultPos += md5Size;
+                }
+            }
+
+            return result;
         }
+#endif
 
         protected virtual byte[] Prf_1_0(byte[] labelSeed, int length)
         {