培训考核三期,新版培训,网页版培训登录器
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

430 lines
15 KiB

#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using System.IO;
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls;
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto;
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto.Impl;
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
namespace BestHTTP.Connections.TLS.Crypto.Impl
{
/// <summary>A generic TLS 1.0-1.2 block cipher. This can be used for AES or 3DES for example.</summary>
public class FastTlsBlockCipher
: TlsCipher
{
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, m_encryptCipher;
protected readonly TlsSuiteMac m_readMac, m_writeMac;
/// <exception cref="IOException"/>
public FastTlsBlockCipher(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(AlertDescription.internal_error);
this.m_cryptoParams = cryptoParams;
this.m_randomData = cryptoParams.NonceGenerator.GenerateNonce(256);
this.m_encryptThenMac = securityParameters.IsEncryptThenMac;
this.m_useExplicitIV = TlsImplUtilities.IsTlsV11(negotiatedVersion);
this.m_acceptExtraPadding = !negotiatedVersion.IsSsl;
/*
* Don't use variable-length padding with truncated MACs.
*
* See "Tag Size Does Matter: Attacks and Proofs for the TLS Record Protocol", Paterson,
* Ristenpart, Shrimpton.
*
* TODO[DTLS] Consider supporting in DTLS (without exceeding send limit though)
*/
this.m_useExtraPadding = securityParameters.IsExtendedPadding
&& ProtocolVersion.TLSv10.IsEqualOrEarlierVersionOf(negotiatedVersion)
&& (m_encryptThenMac || !securityParameters.IsTruncatedHmac);
this.m_encryptCipher = encryptCipher;
this.m_decryptCipher = decryptCipher;
TlsBlockCipherImpl clientCipher, serverCipher;
if (cryptoParams.IsServer)
{
clientCipher = decryptCipher;
serverCipher = encryptCipher;
}
else
{
clientCipher = encryptCipher;
serverCipher = decryptCipher;
}
int key_block_size = (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();
}
byte[] key_block = TlsImplUtilities.CalculateKeyBlock(cryptoParams, key_block_size);
int offset = 0;
clientMac.SetKey(key_block, offset, clientMac.MacLength);
offset += clientMac.MacLength;
serverMac.SetKey(key_block, offset, serverMac.MacLength);
offset += serverMac.MacLength;
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(new byte[clientIVLength], 0, clientIVLength);
serverCipher.Init(new byte[serverIVLength], 0, serverIVLength);
}
else
{
clientCipher.Init(key_block, offset, clientIVLength);
offset += clientIVLength;
serverCipher.Init(key_block, offset, serverIVLength);
offset += serverIVLength;
}
if (offset != key_block_size)
throw new TlsFatalAlert(AlertDescription.internal_error);
if (cryptoParams.IsServer)
{
this.m_writeMac = new TlsSuiteHmac(cryptoParams, serverMac);
this.m_readMac = new TlsSuiteHmac(cryptoParams, clientMac);
}
else
{
this.m_writeMac = new TlsSuiteHmac(cryptoParams, clientMac);
this.m_readMac = new TlsSuiteHmac(cryptoParams, serverMac);
}
}
public virtual int GetCiphertextDecodeLimit(int plaintextLimit)
{
int blockSize = m_decryptCipher.GetBlockSize();
int macSize = m_readMac.Size;
int maxPadding = 256;
return GetCiphertextLength(blockSize, macSize, maxPadding, plaintextLimit);
}
public virtual int GetCiphertextEncodeLimit(int plaintextLength, int plaintextLimit)
{
int blockSize = m_encryptCipher.GetBlockSize();
int macSize = m_writeMac.Size;
int maxPadding = m_useExtraPadding ? 256 : blockSize;
return GetCiphertextLength(blockSize, macSize, maxPadding, plaintextLength);
}
public virtual int GetPlaintextLimit(int ciphertextLimit)
{
int blockSize = m_encryptCipher.GetBlockSize();
int macSize = m_writeMac.Size;
int plaintextLimit = ciphertextLimit;
// Leave room for the MAC, and require block-alignment
if (m_encryptThenMac)
{
plaintextLimit -= macSize;
plaintextLimit -= plaintextLimit % blockSize;
}
else
{
plaintextLimit -= plaintextLimit % blockSize;
plaintextLimit -= macSize;
}
// Minimum 1 byte of padding
--plaintextLimit;
// An explicit IV consumes 1 block
if (m_useExplicitIV)
{
plaintextLimit -= blockSize;
}
return plaintextLimit;
}
public virtual TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion,
int headerAllocation, byte[] plaintext, int offset, int len)
{
int blockSize = m_encryptCipher.GetBlockSize();
int macSize = m_writeMac.Size;
int enc_input_length = len;
if (!m_encryptThenMac)
{
enc_input_length += macSize;
}
int padding_length = blockSize - (enc_input_length % blockSize);
if (m_useExtraPadding)
{
// Add a random number of extra blocks worth of padding
int maxExtraPadBlocks = (256 - padding_length) / blockSize;
int actualExtraPadBlocks = ChooseExtraPadBlocks(maxExtraPadBlocks);
padding_length += actualExtraPadBlocks * blockSize;
}
int totalSize = len + macSize + padding_length;
if (m_useExplicitIV)
{
totalSize += blockSize;
}
byte[] outBuf = new byte[headerAllocation + totalSize];
int outOff = headerAllocation;
if (m_useExplicitIV)
{
// Technically the explicit IV will be the encryption of this nonce
byte[] explicitIV = m_cryptoParams.NonceGenerator.GenerateNonce(blockSize);
Array.Copy(explicitIV, 0, outBuf, outOff, blockSize);
outOff += blockSize;
}
Array.Copy(plaintext, offset, outBuf, outOff, len);
outOff += len;
if (!m_encryptThenMac)
{
byte[] mac = m_writeMac.CalculateMac(seqNo, contentType, plaintext, offset, len);
Array.Copy(mac, 0, outBuf, outOff, mac.Length);
outOff += mac.Length;
}
byte padByte = (byte)(padding_length - 1);
for (int i = 0; i < padding_length; ++i)
{
outBuf[outOff++] = padByte;
}
m_encryptCipher.DoFinal(outBuf, headerAllocation, outOff - headerAllocation, outBuf, headerAllocation);
if (m_encryptThenMac)
{
byte[] mac = m_writeMac.CalculateMac(seqNo, contentType, outBuf, headerAllocation,
outOff - headerAllocation);
Array.Copy(mac, 0, outBuf, outOff, mac.Length);
outOff += mac.Length;
}
if (outOff != outBuf.Length)
throw new TlsFatalAlert(AlertDescription.internal_error);
return new TlsEncodeResult(outBuf, 0, outBuf.Length, contentType);
}
public virtual TlsDecodeResult DecodeCiphertext(long seqNo, short recordType, ProtocolVersion recordVersion,
byte[] ciphertext, int offset, int len)
{
int blockSize = m_decryptCipher.GetBlockSize();
int macSize = m_readMac.Size;
int minLen = blockSize;
if (m_encryptThenMac)
{
minLen += macSize;
}
else
{
minLen = System.Math.Max(minLen, macSize + 1);
}
if (m_useExplicitIV)
{
minLen += blockSize;
}
if (len < minLen)
throw new TlsFatalAlert(AlertDescription.decode_error);
int blocks_length = len;
if (m_encryptThenMac)
{
blocks_length -= macSize;
}
if (blocks_length % blockSize != 0)
throw new TlsFatalAlert(AlertDescription.decryption_failed);
if (m_encryptThenMac)
{
byte[] expectedMac = m_readMac.CalculateMac(seqNo, recordType, ciphertext, offset, len - macSize);
bool checkMac = TlsUtilities.ConstantTimeAreEqual(macSize, expectedMac, 0, ciphertext,
offset + len - macSize);
if (!checkMac)
{
/*
* RFC 7366 3. The MAC SHALL be evaluated before any further processing such as
* decryption is performed, and if the MAC verification fails, then processing SHALL
* terminate immediately. For TLS, a fatal bad_record_mac MUST be generated [2]. For
* DTLS, the record MUST be discarded, and a fatal bad_record_mac MAY be generated
* [4]. This immediate response to a bad MAC eliminates any timing channels that may
* be available through the use of manipulated packet data.
*/
throw new TlsFatalAlert(AlertDescription.bad_record_mac);
}
}
m_decryptCipher.DoFinal(ciphertext, offset, blocks_length, ciphertext, offset);
if (m_useExplicitIV)
{
offset += blockSize;
blocks_length -= blockSize;
}
// If there's anything wrong with the padding, this will return zero
int totalPad = CheckPaddingConstantTime(ciphertext, offset, blocks_length, blockSize,
m_encryptThenMac ? 0 : macSize);
bool badMac = (totalPad == 0);
int dec_output_length = blocks_length - totalPad;
if (!m_encryptThenMac)
{
dec_output_length -= macSize;
byte[] expectedMac = m_readMac.CalculateMacConstantTime(seqNo, recordType, ciphertext, offset,
dec_output_length, blocks_length - macSize, m_randomData);
badMac |= !TlsUtilities.ConstantTimeAreEqual(macSize, expectedMac, 0, ciphertext,
offset + dec_output_length);
}
if (badMac)
throw new TlsFatalAlert(AlertDescription.bad_record_mac);
return new TlsDecodeResult(ciphertext, offset, dec_output_length, recordType);
}
public virtual void RekeyDecoder()
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
public virtual void RekeyEncoder()
{
throw new TlsFatalAlert(AlertDescription.internal_error);
}
public virtual bool UsesOpaqueRecordType
{
get { return false; }
}
protected virtual int CheckPaddingConstantTime(byte[] buf, int off, int len, int blockSize, int macSize)
{
int end = off + len;
byte lastByte = buf[end - 1];
int padlen = lastByte & 0xff;
int totalPad = padlen + 1;
int dummyIndex = 0;
byte padDiff = 0;
int totalPadLimit = System.Math.Min(m_acceptExtraPadding ? 256 : blockSize, len - macSize);
if (totalPad > totalPadLimit)
{
totalPad = 0;
}
else
{
int padPos = end - totalPad;
do
{
padDiff |= (byte)(buf[padPos++] ^ lastByte);
}
while (padPos < end);
dummyIndex = totalPad;
if (padDiff != 0)
{
totalPad = 0;
}
}
// Run some extra dummy checks so the number of checks is always constant
{
byte[] dummyPad = m_randomData;
while (dummyIndex < 256)
{
padDiff |= (byte)(dummyPad[dummyIndex++] ^ lastByte);
}
// Ensure the above loop is not eliminated
dummyPad[0] ^= padDiff;
}
return totalPad;
}
protected virtual int ChooseExtraPadBlocks(int max)
{
byte[] random = m_cryptoParams.NonceGenerator.GenerateNonce(4);
int x = (int)Pack.LE_To_UInt32(random, 0);
int n = Integers.NumberOfTrailingZeros(x);
return System.Math.Min(n, max);
}
protected virtual int GetCiphertextLength(int blockSize, int macSize, int maxPadding, int plaintextLength)
{
int ciphertextLength = plaintextLength;
// An explicit IV consumes 1 block
if (m_useExplicitIV)
{
ciphertextLength += blockSize;
}
// Leave room for the MAC and (block-aligning) padding
ciphertextLength += maxPadding;
if (m_encryptThenMac)
{
ciphertextLength -= (ciphertextLength % blockSize);
ciphertextLength += macSize;
}
else
{
ciphertextLength += macSize;
ciphertextLength -= (ciphertextLength % blockSize);
}
return ciphertextLength;
}
}
}
#pragma warning restore
#endif