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)
{
|