<PackageReference Include="BouncyCastle.Cryptography" Version="2.5.1" />

TlsBlockCipher

A generic TLS 1.0-1.2 block cipher. This can be used for AES or 3DES for example.
using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Utilities; using System; namespace Org.BouncyCastle.Tls.Crypto.Impl { public class TlsBlockCipher : TlsCipher, TlsCipherExt { protected readonly TlsCryptoParameters m_cryptoParams; protected readonly byte[] m_randomData; protected readonly bool m_encryptThenMac; protected readonly bool m_useExplicitIV; protected readonly bool m_acceptExtraPadding; protected readonly bool m_useExtraPadding; protected readonly TlsBlockCipherImpl m_decryptCipher; protected readonly TlsBlockCipherImpl m_encryptCipher; protected readonly TlsSuiteHmac m_readMac; protected readonly TlsSuiteHmac m_writeMac; protected readonly byte[] m_decryptConnectionID; protected readonly byte[] m_encryptConnectionID; protected readonly bool m_decryptUseInnerPlaintext; protected readonly bool m_encryptUseInnerPlaintext; public virtual bool UsesOpaqueRecordType => false; public unsafe TlsBlockCipher(TlsCryptoParameters cryptoParams, TlsBlockCipherImpl encryptCipher, TlsBlockCipherImpl decryptCipher, TlsHmac clientMac, TlsHmac serverMac, int cipherKeySize) { SecurityParameters securityParameters = cryptoParams.SecurityParameters; ProtocolVersion negotiatedVersion = securityParameters.NegotiatedVersion; if (TlsImplUtilities.IsTlsV13(negotiatedVersion)) throw new TlsFatalAlert(80); m_decryptConnectionID = securityParameters.ConnectionIDPeer; m_encryptConnectionID = securityParameters.ConnectionIDLocal; m_decryptUseInnerPlaintext = !Arrays.IsNullOrEmpty(m_decryptConnectionID); m_encryptUseInnerPlaintext = !Arrays.IsNullOrEmpty(m_encryptConnectionID); m_cryptoParams = cryptoParams; m_randomData = cryptoParams.NonceGenerator.GenerateNonce(256); m_encryptThenMac = securityParameters.IsEncryptThenMac; m_useExplicitIV = TlsImplUtilities.IsTlsV11(negotiatedVersion); m_acceptExtraPadding = !negotiatedVersion.IsSsl; m_useExtraPadding = (securityParameters.IsExtendedPadding && ProtocolVersion.TLSv10.IsEqualOrEarlierVersionOf(negotiatedVersion) && (m_encryptThenMac || !securityParameters.IsTruncatedHmac)); m_encryptCipher = encryptCipher; m_decryptCipher = decryptCipher; TlsBlockCipherImpl tlsBlockCipherImpl; TlsBlockCipherImpl tlsBlockCipherImpl2; if (cryptoParams.IsServer) { tlsBlockCipherImpl = decryptCipher; tlsBlockCipherImpl2 = encryptCipher; } else { tlsBlockCipherImpl = encryptCipher; tlsBlockCipherImpl2 = decryptCipher; } int num = 2 * cipherKeySize + clientMac.MacLength + serverMac.MacLength; if (!m_useExplicitIV) num += tlsBlockCipherImpl.GetBlockSize() + tlsBlockCipherImpl2.GetBlockSize(); Span<byte> span; int num2; if (num <= 512) { num2 = num; span = new Span<byte>(stackalloc byte[(int)(uint)num2], num2); } else span = new byte[num]; Span<byte> keyBlock = span; TlsImplUtilities.CalculateKeyBlock(cryptoParams, keyBlock); clientMac.SetKey(keyBlock.Slice(0, clientMac.MacLength)); num2 = clientMac.MacLength; keyBlock = keyBlock.Slice(num2, keyBlock.Length - num2); serverMac.SetKey(keyBlock.Slice(0, serverMac.MacLength)); num2 = serverMac.MacLength; keyBlock = keyBlock.Slice(num2, keyBlock.Length - num2); tlsBlockCipherImpl.SetKey(keyBlock.Slice(0, cipherKeySize)); keyBlock = keyBlock.Slice(cipherKeySize, keyBlock.Length - cipherKeySize); tlsBlockCipherImpl2.SetKey(keyBlock.Slice(0, cipherKeySize)); keyBlock = keyBlock.Slice(cipherKeySize, keyBlock.Length - cipherKeySize); int blockSize = tlsBlockCipherImpl.GetBlockSize(); int blockSize2 = tlsBlockCipherImpl2.GetBlockSize(); if (m_useExplicitIV) { TlsBlockCipherImpl tlsBlockCipherImpl3 = tlsBlockCipherImpl; if (blockSize <= 64) { num2 = blockSize; span = new Span<byte>(stackalloc byte[(int)(uint)num2], num2); } else span = new byte[blockSize]; tlsBlockCipherImpl3.Init(span); tlsBlockCipherImpl3 = tlsBlockCipherImpl2; if (blockSize2 <= 64) { num2 = blockSize2; span = new Span<byte>(stackalloc byte[(int)(uint)num2], num2); } else span = new byte[blockSize2]; tlsBlockCipherImpl3.Init(span); } else { tlsBlockCipherImpl.Init(keyBlock.Slice(0, blockSize)); num2 = blockSize; keyBlock = keyBlock.Slice(num2, keyBlock.Length - num2); tlsBlockCipherImpl2.Init(keyBlock.Slice(0, blockSize2)); num2 = blockSize2; keyBlock = keyBlock.Slice(num2, keyBlock.Length - num2); } if (!keyBlock.IsEmpty) throw new TlsFatalAlert(80); if (cryptoParams.IsServer) { m_writeMac = new TlsSuiteHmac(cryptoParams, serverMac); m_readMac = new TlsSuiteHmac(cryptoParams, clientMac); } else { m_writeMac = new TlsSuiteHmac(cryptoParams, clientMac); m_readMac = new TlsSuiteHmac(cryptoParams, serverMac); } } public virtual int GetCiphertextDecodeLimit(int plaintextLimit) { int blockSize = m_decryptCipher.GetBlockSize(); int size = m_readMac.Size; int maxPadding = 256; int plaintextLength = plaintextLimit + (m_decryptUseInnerPlaintext ? 1 : 0); return GetCiphertextLength(blockSize, size, maxPadding, plaintextLength); } public virtual int GetCiphertextEncodeLimit(int plaintextLength, int plaintextLimit) { plaintextLimit = System.Math.Min(plaintextLength, plaintextLimit); int blockSize = m_encryptCipher.GetBlockSize(); int size = m_writeMac.Size; int maxPadding = m_useExtraPadding ? 256 : blockSize; int plaintextLength2 = plaintextLimit + (m_encryptUseInnerPlaintext ? 1 : 0); return GetCiphertextLength(blockSize, size, maxPadding, plaintextLength2); } public virtual int GetPlaintextLimit(int ciphertextLimit) { return GetPlaintextEncodeLimit(ciphertextLimit); } public virtual int GetPlaintextDecodeLimit(int ciphertextLimit) { int blockSize = m_decryptCipher.GetBlockSize(); int size = m_readMac.Size; return GetPlaintextLength(blockSize, size, ciphertextLimit) - (m_decryptUseInnerPlaintext ? 1 : 0); } public virtual int GetPlaintextEncodeLimit(int ciphertextLimit) { int blockSize = m_encryptCipher.GetBlockSize(); int size = m_writeMac.Size; return GetPlaintextLength(blockSize, size, ciphertextLimit) - (m_encryptUseInnerPlaintext ? 1 : 0); } public virtual TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion, int headerAllocation, byte[] plaintext, int offset, int len) { return EncodePlaintext(seqNo, contentType, recordVersion, headerAllocation, plaintext.AsSpan(offset, len)); } public virtual TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion, int headerAllocation, ReadOnlySpan<byte> plaintext) { int blockSize = m_encryptCipher.GetBlockSize(); int size = m_writeMac.Size; int num = plaintext.Length + (m_encryptUseInnerPlaintext ? 1 : 0); int num2 = num; if (!m_encryptThenMac) num2 += size; int num3 = blockSize - num2 % blockSize; if (m_useExtraPadding) { int max = (256 - num3) / blockSize; int num4 = ChooseExtraPadBlocks(max); num3 += num4 * blockSize; } int num5 = num + size + num3; if (m_useExplicitIV) num5 += blockSize; byte[] array = new byte[headerAllocation + num5]; int num6 = headerAllocation; if (m_useExplicitIV) { Array.Copy(m_cryptoParams.NonceGenerator.GenerateNonce(blockSize), 0, array, num6, blockSize); num6 += blockSize; } int start = num6; plaintext.CopyTo(array.AsSpan(num6)); num6 += plaintext.Length; short num7 = contentType; if (m_encryptUseInnerPlaintext) { array[num6++] = (byte)contentType; num7 = 25; } if (!m_encryptThenMac) { byte[] array2 = m_writeMac.CalculateMac(seqNo, num7, m_encryptConnectionID, array.AsSpan(start, num)); array2.CopyTo(array.AsSpan(num6)); num6 += array2.Length; } byte b = (byte)(num3 - 1); for (int i = 0; i < num3; i++) { array[num6++] = b; } m_encryptCipher.DoFinal(array, headerAllocation, num6 - headerAllocation, array, headerAllocation); if (m_encryptThenMac) { byte[] array3 = m_writeMac.CalculateMac(seqNo, num7, m_encryptConnectionID, array.AsSpan(headerAllocation, num6 - headerAllocation)); Array.Copy(array3, 0, array, num6, array3.Length); num6 += array3.Length; } if (num6 != array.Length) throw new TlsFatalAlert(80); return new TlsEncodeResult(array, 0, array.Length, num7); } public virtual TlsDecodeResult DecodeCiphertext(long seqNo, short recordType, ProtocolVersion recordVersion, byte[] ciphertext, int offset, int len) { int blockSize = m_decryptCipher.GetBlockSize(); int size = m_readMac.Size; int num = blockSize; num = ((!m_encryptThenMac) ? System.Math.Max(num, size + 1) : (num + size)); if (m_useExplicitIV) num += blockSize; if (len < num) throw new TlsFatalAlert(50); int num2 = len; if (m_encryptThenMac) num2 -= size; if (num2 % blockSize != 0) throw new TlsFatalAlert(21); if (m_encryptThenMac) { byte[] a = m_readMac.CalculateMac(seqNo, recordType, m_decryptConnectionID, ciphertext, offset, len - size); if (!TlsUtilities.ConstantTimeAreEqual(size, a, 0, ciphertext, offset + len - size)) throw new TlsFatalAlert(20); } m_decryptCipher.DoFinal(ciphertext, offset, num2, ciphertext, offset); if (m_useExplicitIV) { offset += blockSize; num2 -= blockSize; } int num3 = CheckPaddingConstantTime(ciphertext, offset, num2, blockSize, (!m_encryptThenMac) ? size : 0); bool flag = num3 == 0; int num4 = num2 - num3; if (!m_encryptThenMac) { num4 -= size; byte[] a2 = m_readMac.CalculateMacConstantTime(seqNo, recordType, m_decryptConnectionID, ciphertext, offset, num4, num2 - size, m_randomData); flag |= !TlsUtilities.ConstantTimeAreEqual(size, a2, 0, ciphertext, offset + num4); } if (flag) throw new TlsFatalAlert(20); short contentType = recordType; int num5 = num4; if (m_decryptUseInnerPlaintext) { byte b; do { if (--num5 < 0) throw new TlsFatalAlert(10); b = ciphertext[offset + num5]; } while (b == 0); contentType = (short)(b & 255); } return new TlsDecodeResult(ciphertext, offset, num5, contentType); } public virtual void RekeyDecoder() { throw new TlsFatalAlert(80); } public virtual void RekeyEncoder() { throw new TlsFatalAlert(80); } protected virtual int CheckPaddingConstantTime(byte[] buf, int off, int len, int blockSize, int macSize) { int num = off + len; byte b = buf[num - 1]; int num2 = (b & 255) + 1; int num3 = 0; byte b2 = 0; int num4 = System.Math.Min(m_acceptExtraPadding ? 256 : blockSize, len - macSize); if (num2 > num4) num2 = 0; else { int num5 = num - num2; do { b2 = (byte)(b2 | (byte)(buf[num5++] ^ b)); } while (num5 < num); num3 = num2; if (b2 != 0) num2 = 0; } byte[] randomData = m_randomData; while (num3 < 256) { b2 = (byte)(b2 | (byte)(randomData[num3++] ^ b)); } randomData[0] ^= b2; return num2; } protected virtual int ChooseExtraPadBlocks(int max) { return System.Math.Min(Integers.NumberOfTrailingZeros((int)Pack.LE_To_UInt32(m_cryptoParams.NonceGenerator.GenerateNonce(4), 0)), max); } protected virtual int GetCiphertextLength(int blockSize, int macSize, int maxPadding, int plaintextLength) { int num = plaintextLength; if (m_useExplicitIV) num += blockSize; num += maxPadding; if (!m_encryptThenMac) { num += macSize; return num - num % blockSize; } num -= num % blockSize; return num + macSize; } protected virtual int GetPlaintextLength(int blockSize, int macSize, int ciphertextLength) { int num; if (m_encryptThenMac) { num = ciphertextLength - macSize; num -= num % blockSize; } else { num = ciphertextLength - ciphertextLength % blockSize; num -= macSize; } num--; if (m_useExplicitIV) num -= blockSize; return num; } } }