diff options
-rw-r--r-- | crypto/src/tls/TlsProtocol.cs | 10 | ||||
-rw-r--r-- | crypto/src/tls/TlsUtilities.cs | 41 | ||||
-rw-r--r-- | crypto/src/tls/crypto/TlsCryptoUtilities.cs | 47 | ||||
-rw-r--r-- | crypto/src/tls/crypto/TlsMac.cs | 4 | ||||
-rw-r--r-- | crypto/src/tls/crypto/TlsSecret.cs | 14 | ||||
-rw-r--r-- | crypto/src/tls/crypto/impl/AbstractTlsSecret.cs | 41 | ||||
-rw-r--r-- | crypto/src/tls/crypto/impl/TlsAeadCipher.cs | 28 | ||||
-rw-r--r-- | crypto/src/tls/crypto/impl/TlsAeadCipherImpl.cs | 4 | ||||
-rw-r--r-- | crypto/src/tls/crypto/impl/TlsBlockCipher.cs | 61 | ||||
-rw-r--r-- | crypto/src/tls/crypto/impl/TlsBlockCipherImpl.cs | 8 | ||||
-rw-r--r-- | crypto/src/tls/crypto/impl/TlsImplUtilities.cs | 18 | ||||
-rw-r--r-- | crypto/src/tls/crypto/impl/TlsNullCipher.cs | 29 | ||||
-rw-r--r-- | crypto/src/tls/crypto/impl/bc/BcChaCha20Poly1305.cs | 8 | ||||
-rw-r--r-- | crypto/src/tls/crypto/impl/bc/BcSsl3Hmac.cs | 10 | ||||
-rw-r--r-- | crypto/src/tls/crypto/impl/bc/BcTlsAeadCipherImpl.cs | 7 | ||||
-rw-r--r-- | crypto/src/tls/crypto/impl/bc/BcTlsBlockCipherImpl.cs | 14 | ||||
-rw-r--r-- | crypto/src/tls/crypto/impl/bc/BcTlsHmac.cs | 7 | ||||
-rw-r--r-- | crypto/src/tls/crypto/impl/bc/BcTlsSecret.cs | 144 |
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) { |