From 88c141ee70aed4f7e6599b467de5b776f8fe9490 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Sat, 25 Mar 2023 14:52:49 +0700 Subject: RFC 9146: TlsNullCipher support for connection ID --- crypto/src/tls/DtlsRecordLayer.cs | 5 ++ crypto/src/tls/crypto/impl/TlsAeadCipher.cs | 9 +- crypto/src/tls/crypto/impl/TlsBlockCipher.cs | 21 ++--- crypto/src/tls/crypto/impl/TlsNullCipher.cs | 123 ++++++++++++++++++++++----- 4 files changed, 118 insertions(+), 40 deletions(-) diff --git a/crypto/src/tls/DtlsRecordLayer.cs b/crypto/src/tls/DtlsRecordLayer.cs index 860c2dc31..e68470adb 100644 --- a/crypto/src/tls/DtlsRecordLayer.cs +++ b/crypto/src/tls/DtlsRecordLayer.cs @@ -681,6 +681,11 @@ namespace Org.BouncyCastle.Tls if (!Arrays.FixedTimeEquals(connectionID.Length, connectionID, 0, record, 11)) return -1; } + else + { + if (ContentType.tls12_cid == recordType) + return -1; + } int length = TlsUtilities.ReadUint16(record, recordHeaderLength - 2); if (received != (length + recordHeaderLength)) diff --git a/crypto/src/tls/crypto/impl/TlsAeadCipher.cs b/crypto/src/tls/crypto/impl/TlsAeadCipher.cs index f5ebd7eba..f8f01f380 100644 --- a/crypto/src/tls/crypto/impl/TlsAeadCipher.cs +++ b/crypto/src/tls/crypto/impl/TlsAeadCipher.cs @@ -154,14 +154,9 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl public override int GetCiphertextEncodeLimit(int plaintextLength, int plaintextLimit) { - int innerPlaintextLimit = plaintextLength; - if (m_encryptUseInnerPlaintext) - { - // TODO[tls13] Add support for padding - int maxPadding = 0; + plaintextLimit = System.Math.Min(plaintextLength, plaintextLimit); - innerPlaintextLimit = 1 + System.Math.Min(plaintextLimit, plaintextLength + maxPadding); - } + int innerPlaintextLimit = plaintextLimit + (m_encryptUseInnerPlaintext ? 1 : 0); return innerPlaintextLimit + m_macSize + m_record_iv_length; } diff --git a/crypto/src/tls/crypto/impl/TlsBlockCipher.cs b/crypto/src/tls/crypto/impl/TlsBlockCipher.cs index 63d4826b3..bc04caee1 100644 --- a/crypto/src/tls/crypto/impl/TlsBlockCipher.cs +++ b/crypto/src/tls/crypto/impl/TlsBlockCipher.cs @@ -159,29 +159,22 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl { int blockSize = m_decryptCipher.GetBlockSize(); int macSize = m_readMac.Size; - int maxExtraPadding = 256; - + int maxPadding = 256; int innerPlaintextLimit = plaintextLimit + (m_decryptUseInnerPlaintext ? 1 : 0); - return GetCiphertextLength(blockSize, macSize, maxExtraPadding, innerPlaintextLimit); + return GetCiphertextLength(blockSize, macSize, maxPadding, innerPlaintextLimit); } public override int GetCiphertextEncodeLimit(int plaintextLength, int plaintextLimit) { + plaintextLimit = System.Math.Min(plaintextLength, plaintextLimit); + int blockSize = m_encryptCipher.GetBlockSize(); int macSize = m_writeMac.Size; - int maxExtraPadding = m_useExtraPadding ? 256 : blockSize; - - int innerPlaintextLimit = plaintextLength; - if (m_encryptUseInnerPlaintext) - { - // TODO[cid] Add support for padding - int maxPadding = 0; - - innerPlaintextLimit = 1 + System.Math.Min(plaintextLimit, plaintextLength + maxPadding); - } + int maxPadding = m_useExtraPadding ? 256 : blockSize; + int innerPlaintextLimit = plaintextLimit + (m_encryptUseInnerPlaintext ? 1 : 0); - return GetCiphertextLength(blockSize, macSize, maxExtraPadding, innerPlaintextLimit); + return GetCiphertextLength(blockSize, macSize, maxPadding, innerPlaintextLimit); } public override int GetPlaintextDecodeLimit(int ciphertextLimit) diff --git a/crypto/src/tls/crypto/impl/TlsNullCipher.cs b/crypto/src/tls/crypto/impl/TlsNullCipher.cs index 2008fd1d6..8e52f6374 100644 --- a/crypto/src/tls/crypto/impl/TlsNullCipher.cs +++ b/crypto/src/tls/crypto/impl/TlsNullCipher.cs @@ -1,6 +1,8 @@ using System; using System.IO; +using Org.BouncyCastle.Utilities; + namespace Org.BouncyCastle.Tls.Crypto.Impl { /// The NULL cipher. @@ -9,13 +11,24 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl { protected readonly TlsCryptoParameters m_cryptoParams; protected readonly TlsSuiteHmac m_readMac, m_writeMac; + protected readonly byte[] m_decryptConnectionID, m_encryptConnectionID; + protected readonly bool m_decryptUseInnerPlaintext, m_encryptUseInnerPlaintext; /// public TlsNullCipher(TlsCryptoParameters cryptoParams, TlsHmac clientMac, TlsHmac serverMac) { - if (TlsImplUtilities.IsTlsV13(cryptoParams)) + SecurityParameters securityParameters = cryptoParams.SecurityParameters; + ProtocolVersion negotiatedVersion = securityParameters.NegotiatedVersion; + + if (TlsImplUtilities.IsTlsV13(negotiatedVersion)) throw new TlsFatalAlert(AlertDescription.internal_error); + m_decryptConnectionID = securityParameters.ConnectionIDPeer; + m_encryptConnectionID = securityParameters.ConnectionIDLocal; + + m_decryptUseInnerPlaintext = !Arrays.IsNullOrEmpty(m_decryptConnectionID); + m_encryptUseInnerPlaintext = !Arrays.IsNullOrEmpty(m_encryptConnectionID); + m_cryptoParams = cryptoParams; int keyBlockSize = clientMac.MacLength + serverMac.MacLength; @@ -58,38 +71,87 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl public override int GetCiphertextDecodeLimit(int plaintextLimit) { - return plaintextLimit + m_writeMac.Size; + int innerPlaintextLimit = plaintextLimit + (m_decryptUseInnerPlaintext ? 1 : 0); + + return innerPlaintextLimit + m_readMac.Size; } public override int GetCiphertextEncodeLimit(int plaintextLength, int plaintextLimit) { - return plaintextLength + m_writeMac.Size; + plaintextLimit = System.Math.Min(plaintextLength, plaintextLimit); + + int innerPlaintextLimit = plaintextLimit + (m_encryptUseInnerPlaintext ? 1 : 0); + + return innerPlaintextLimit + m_writeMac.Size; + } + + public override int GetPlaintextDecodeLimit(int ciphertextLimit) + { + int innerPlaintextLimit = ciphertextLimit - m_readMac.Size; + + return innerPlaintextLimit - (m_decryptUseInnerPlaintext ? 1 : 0); } - public override int GetPlaintextLimit(int ciphertextLimit) + public override int GetPlaintextEncodeLimit(int ciphertextLimit) { - return ciphertextLimit - m_writeMac.Size; + int innerPlaintextLimit = ciphertextLimit - m_writeMac.Size; + + return innerPlaintextLimit - (m_encryptUseInnerPlaintext ? 1 : 0); } public override TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion, int headerAllocation, byte[] plaintext, int offset, int len) { - byte[] mac = m_writeMac.CalculateMac(seqNo, contentType, plaintext, offset, len); - byte[] ciphertext = new byte[headerAllocation + len + mac.Length]; +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return EncodePlaintext(seqNo, contentType, recordVersion, headerAllocation, plaintext.AsSpan(offset, len)); +#else + int macSize = m_writeMac.Size; + + // TODO[cid] If we support adding padding to DTLSInnerPlaintext, this will need review + int innerPlaintextLength = len + (m_encryptUseInnerPlaintext ? 1 : 0); + + byte[] ciphertext = new byte[headerAllocation + innerPlaintextLength + macSize]; Array.Copy(plaintext, offset, ciphertext, headerAllocation, len); - Array.Copy(mac, 0, ciphertext, headerAllocation + len, mac.Length); - return new TlsEncodeResult(ciphertext, 0, ciphertext.Length, contentType); + + short recordType = contentType; + if (m_encryptUseInnerPlaintext) + { + ciphertext[headerAllocation + len] = (byte)contentType; + recordType = ContentType.tls12_cid; + } + + byte[] mac = m_writeMac.CalculateMac(seqNo, recordType, m_encryptConnectionID, ciphertext, headerAllocation, + innerPlaintextLength); + Array.Copy(mac, 0, ciphertext, headerAllocation + innerPlaintextLength, mac.Length); + + return new TlsEncodeResult(ciphertext, 0, ciphertext.Length, recordType); +#endif } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER public override TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion, int headerAllocation, ReadOnlySpan plaintext) { - byte[] mac = m_writeMac.CalculateMac(seqNo, contentType, plaintext); - byte[] ciphertext = new byte[headerAllocation + plaintext.Length + mac.Length]; + int macSize = m_writeMac.Size; + + // TODO[cid] If we support adding padding to DTLSInnerPlaintext, this will need review + int innerPlaintextLength = plaintext.Length + (m_encryptUseInnerPlaintext ? 1 : 0); + + byte[] ciphertext = new byte[headerAllocation + innerPlaintextLength + macSize]; plaintext.CopyTo(ciphertext.AsSpan(headerAllocation)); - mac.CopyTo(ciphertext.AsSpan(headerAllocation + plaintext.Length)); - return new TlsEncodeResult(ciphertext, 0, ciphertext.Length, contentType); + + short recordType = contentType; + if (m_encryptUseInnerPlaintext) + { + ciphertext[headerAllocation + plaintext.Length] = (byte)contentType; + recordType = ContentType.tls12_cid; + } + + byte[] mac = m_writeMac.CalculateMac(seqNo, recordType, m_encryptConnectionID, + ciphertext.AsSpan(headerAllocation, innerPlaintextLength)); + mac.CopyTo(ciphertext.AsSpan(headerAllocation + innerPlaintextLength)); + + return new TlsEncodeResult(ciphertext, 0, ciphertext.Length, recordType); } #endif @@ -97,18 +159,41 @@ namespace Org.BouncyCastle.Tls.Crypto.Impl byte[] ciphertext, int offset, int len) { int macSize = m_readMac.Size; - if (len < macSize) - throw new TlsFatalAlert(AlertDescription.decode_error); - int macInputLen = len - macSize; + int innerPlaintextLength = len - macSize; + + if (innerPlaintextLength < (m_decryptUseInnerPlaintext ? 1 : 0)) + throw new TlsFatalAlert(AlertDescription.decode_error); - byte[] expectedMac = m_readMac.CalculateMac(seqNo, recordType, ciphertext, offset, macInputLen); + byte[] expectedMac = m_readMac.CalculateMac(seqNo, recordType, m_decryptConnectionID, ciphertext, offset, + innerPlaintextLength); - bool badMac = !TlsUtilities.ConstantTimeAreEqual(macSize, expectedMac, 0, ciphertext, offset + macInputLen); + bool badMac = !TlsUtilities.ConstantTimeAreEqual(macSize, expectedMac, 0, ciphertext, + offset + innerPlaintextLength); if (badMac) throw new TlsFatalAlert(AlertDescription.bad_record_mac); - return new TlsDecodeResult(ciphertext, offset, macInputLen, recordType); + short contentType = recordType; + int plaintextLength = innerPlaintextLength; + + if (m_decryptUseInnerPlaintext) + { + // Strip padding and read true content type from DTLSInnerPlaintext + for (;;) + { + if (--plaintextLength < 0) + throw new TlsFatalAlert(AlertDescription.unexpected_message); + + byte octet = ciphertext[offset + plaintextLength]; + if (0 != octet) + { + contentType = (short)(octet & 0xFF); + break; + } + } + } + + return new TlsDecodeResult(ciphertext, offset, plaintextLength, contentType); } public override bool UsesOpaqueRecordType -- cgit 1.4.1