培训考核三期,新版培训,网页版培训登录器
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.
 
 

400 lines
16 KiB

#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
using System;
using System.IO;
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto.Impl;
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto;
using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls;
namespace BestHTTP.Connections.TLS.Crypto.Impl
{
/// <summary>A generic TLS 1.2 AEAD cipher.</summary>
[BestHTTP.PlatformSupport.IL2CPP.Il2CppEagerStaticClassConstructionAttribute]
public sealed class FastTlsAeadCipher
: TlsCipher
{
public const int AEAD_CCM = 1;
public const int AEAD_CHACHA20_POLY1305 = 2;
public const int AEAD_GCM = 3;
private const int NONCE_RFC5288 = 1;
private const int NONCE_RFC7905 = 2;
private readonly TlsCryptoParameters m_cryptoParams;
private readonly int m_keySize;
private readonly int m_macSize;
private readonly int m_fixed_iv_length;
private readonly int m_record_iv_length;
private readonly TlsAeadCipherImpl m_decryptCipher, m_encryptCipher;
private readonly byte[] m_decryptNonce, m_encryptNonce;
private readonly bool m_isTlsV13;
private readonly int m_nonceMode;
/// <exception cref="IOException"/>
public FastTlsAeadCipher(TlsCryptoParameters cryptoParams, TlsAeadCipherImpl encryptCipher,
TlsAeadCipherImpl decryptCipher, int keySize, int macSize, int aeadType)
{
SecurityParameters securityParameters = cryptoParams.SecurityParameters;
ProtocolVersion negotiatedVersion = securityParameters.NegotiatedVersion;
if (!TlsImplUtilities.IsTlsV12(negotiatedVersion))
throw new TlsFatalAlert(AlertDescription.internal_error);
this.m_isTlsV13 = TlsImplUtilities.IsTlsV13(negotiatedVersion);
this.m_nonceMode = GetNonceMode(m_isTlsV13, aeadType);
switch (m_nonceMode)
{
case NONCE_RFC5288:
this.m_fixed_iv_length = 4;
this.m_record_iv_length = 8;
break;
case NONCE_RFC7905:
this.m_fixed_iv_length = 12;
this.m_record_iv_length = 0;
break;
default:
throw new TlsFatalAlert(AlertDescription.internal_error);
}
this.m_cryptoParams = cryptoParams;
this.m_keySize = keySize;
this.m_macSize = macSize;
this.m_decryptCipher = decryptCipher;
this.m_encryptCipher = encryptCipher;
this.m_decryptNonce = new byte[m_fixed_iv_length];
this.m_encryptNonce = new byte[m_fixed_iv_length];
bool isServer = cryptoParams.IsServer;
if (m_isTlsV13)
{
RekeyCipher(securityParameters, decryptCipher, m_decryptNonce, !isServer);
RekeyCipher(securityParameters, encryptCipher, m_encryptNonce, isServer);
return;
}
int keyBlockSize = (2 * keySize) + (2 * m_fixed_iv_length);
byte[] keyBlock = TlsImplUtilities.CalculateKeyBlock(cryptoParams, keyBlockSize);
int pos = 0;
if (isServer)
{
decryptCipher.SetKey(keyBlock, pos, keySize); pos += keySize;
encryptCipher.SetKey(keyBlock, pos, keySize); pos += keySize;
Array.Copy(keyBlock, pos, m_decryptNonce, 0, m_fixed_iv_length); pos += m_fixed_iv_length;
Array.Copy(keyBlock, pos, m_encryptNonce, 0, m_fixed_iv_length); pos += m_fixed_iv_length;
}
else
{
encryptCipher.SetKey(keyBlock, pos, keySize); pos += keySize;
decryptCipher.SetKey(keyBlock, pos, keySize); pos += keySize;
Array.Copy(keyBlock, pos, m_encryptNonce, 0, m_fixed_iv_length); pos += m_fixed_iv_length;
Array.Copy(keyBlock, pos, m_decryptNonce, 0, m_fixed_iv_length); pos += m_fixed_iv_length;
}
if (keyBlockSize != pos)
throw new TlsFatalAlert(AlertDescription.internal_error);
int nonceLength = m_fixed_iv_length + m_record_iv_length;
// NOTE: Ensure dummy nonce is not part of the generated sequence(s)
byte[] dummyNonce = new byte[nonceLength];
dummyNonce[0] = (byte)~m_encryptNonce[0];
dummyNonce[1] = (byte)~m_decryptNonce[1];
encryptCipher.Init(dummyNonce, macSize, null);
decryptCipher.Init(dummyNonce, macSize, null);
}
public int GetCiphertextDecodeLimit(int plaintextLimit)
{
return plaintextLimit + m_macSize + m_record_iv_length + (m_isTlsV13 ? 1 : 0);
}
public int GetCiphertextEncodeLimit(int plaintextLength, int plaintextLimit)
{
int innerPlaintextLimit = plaintextLength;
if (m_isTlsV13)
{
// TODO[tls13] Add support for padding
int maxPadding = 0;
innerPlaintextLimit = 1 + System.Math.Min(plaintextLimit, plaintextLength + maxPadding);
}
return innerPlaintextLimit + m_macSize + m_record_iv_length;
}
public int GetPlaintextLimit(int ciphertextLimit)
{
return ciphertextLimit - m_macSize - m_record_iv_length - (m_isTlsV13 ? 1 : 0);
}
public TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion,
int headerAllocation, byte[] plaintext, int plaintextOffset, int plaintextLength)
{
byte[] nonce = new byte[m_encryptNonce.Length + m_record_iv_length];
switch (m_nonceMode)
{
case NONCE_RFC5288:
Array.Copy(m_encryptNonce, 0, nonce, 0, m_encryptNonce.Length);
// RFC 5288/6655: The nonce_explicit MAY be the 64-bit sequence number.
TlsUtilities.WriteUint64(seqNo, nonce, m_encryptNonce.Length);
break;
case NONCE_RFC7905:
TlsUtilities.WriteUint64(seqNo, nonce, nonce.Length - 8);
for (int i = 0; i < m_encryptNonce.Length; ++i)
{
nonce[i] ^= m_encryptNonce[i];
}
break;
default:
throw new TlsFatalAlert(AlertDescription.internal_error);
}
int extraLength = m_isTlsV13 ? 1 : 0;
// TODO[tls13] If we support adding padding to TLSInnerPlaintext, this will need review
int encryptionLength = m_encryptCipher.GetOutputSize(plaintextLength + extraLength);
int ciphertextLength = m_record_iv_length + encryptionLength;
byte[] output = new byte[headerAllocation + ciphertextLength];
int outputPos = headerAllocation;
if (m_record_iv_length != 0)
{
Array.Copy(nonce, nonce.Length - m_record_iv_length, output, outputPos, m_record_iv_length);
outputPos += m_record_iv_length;
}
short recordType = m_isTlsV13 ? ContentType.application_data : contentType;
byte[] additionalData = GetAdditionalData(seqNo, recordType, recordVersion, ciphertextLength,
plaintextLength);
try
{
m_encryptCipher.Init(nonce, m_macSize, additionalData);
Array.Copy(plaintext, plaintextOffset, output, outputPos, plaintextLength);
if (m_isTlsV13)
{
output[outputPos + plaintextLength] = (byte)contentType;
}
outputPos += m_encryptCipher.DoFinal(output, outputPos, plaintextLength + extraLength, output,
outputPos);
}
catch (IOException e)
{
throw e;
}
catch (Exception e)
{
throw new TlsFatalAlert(AlertDescription.internal_error, e);
}
if (outputPos != output.Length)
{
// NOTE: The additional data mechanism for AEAD ciphers requires exact output size prediction.
throw new TlsFatalAlert(AlertDescription.internal_error);
}
return new TlsEncodeResult(output, 0, output.Length, recordType);
}
byte[] decode_nonce = null;
public TlsDecodeResult DecodeCiphertext(long seqNo, short recordType, ProtocolVersion recordVersion,
byte[] ciphertext, int ciphertextOffset, int ciphertextLength)
{
if (GetPlaintextLimit(ciphertextLength) < 0)
throw new TlsFatalAlert(AlertDescription.decode_error);
if (decode_nonce == null || decode_nonce.Length != m_decryptNonce.Length + m_record_iv_length)
decode_nonce = new byte[m_decryptNonce.Length + m_record_iv_length];
else
Array.Clear(decode_nonce, 0, decode_nonce.Length);
switch (m_nonceMode)
{
case NONCE_RFC5288:
Array.Copy(m_decryptNonce, 0, decode_nonce, 0, m_decryptNonce.Length);
Array.Copy(ciphertext, ciphertextOffset, decode_nonce, decode_nonce.Length - m_record_iv_length,
m_record_iv_length);
break;
case NONCE_RFC7905:
TlsUtilities.WriteUint64(seqNo, decode_nonce, decode_nonce.Length - 8);
for (int i = 0; i < m_decryptNonce.Length; ++i)
{
decode_nonce[i] ^= m_decryptNonce[i];
}
break;
default:
throw new TlsFatalAlert(AlertDescription.internal_error);
}
int encryptionOffset = ciphertextOffset + m_record_iv_length;
int encryptionLength = ciphertextLength - m_record_iv_length;
int plaintextLength = m_decryptCipher.GetOutputSize(encryptionLength);
byte[] additionalData = GetAdditionalData(seqNo, recordType, recordVersion, ciphertextLength,
plaintextLength);
int outputPos;
try
{
m_decryptCipher.Init(decode_nonce, m_macSize, additionalData);
outputPos = m_decryptCipher.DoFinal(ciphertext, encryptionOffset, encryptionLength, ciphertext,
encryptionOffset);
}
catch (IOException e)
{
throw e;
}
catch (Exception e)
{
throw new TlsFatalAlert(AlertDescription.bad_record_mac, e);
}
if (outputPos != plaintextLength)
{
// NOTE: The additional data mechanism for AEAD ciphers requires exact output size prediction.
throw new TlsFatalAlert(AlertDescription.internal_error);
}
short contentType = recordType;
if (m_isTlsV13)
{
// Strip padding and read true content type from TLSInnerPlaintext
int pos = plaintextLength;
for (; ; )
{
if (--pos < 0)
throw new TlsFatalAlert(AlertDescription.unexpected_message);
byte octet = ciphertext[encryptionOffset + pos];
if (0 != octet)
{
contentType = (short)(octet & 0xFF);
plaintextLength = pos;
break;
}
}
}
return new TlsDecodeResult(ciphertext, encryptionOffset, plaintextLength, contentType);
}
public void RekeyDecoder()
{
RekeyCipher(m_cryptoParams.SecurityParameters, m_decryptCipher, m_decryptNonce, !m_cryptoParams.IsServer);
}
public void RekeyEncoder()
{
RekeyCipher(m_cryptoParams.SecurityParameters, m_encryptCipher, m_encryptNonce, m_cryptoParams.IsServer);
}
public bool UsesOpaqueRecordType
{
get { return m_isTlsV13; }
}
byte[] additional_data = null;
private byte[] GetAdditionalData(long seqNo, short recordType, ProtocolVersion recordVersion,
int ciphertextLength, int plaintextLength)
{
if (m_isTlsV13)
{
/*
* TLSCiphertext.opaque_type || TLSCiphertext.legacy_record_version || TLSCiphertext.length
*/
if (additional_data == null || additional_data.Length != 5)
additional_data = new byte[5];
else
Array.Clear(additional_data, 0, additional_data.Length);
TlsUtilities.WriteUint8(recordType, additional_data, 0);
TlsUtilities.WriteVersion(recordVersion, additional_data, 1);
TlsUtilities.WriteUint16(ciphertextLength, additional_data, 3);
return additional_data;
}
else
{
/*
* seq_num + TLSCompressed.type + TLSCompressed.version + TLSCompressed.length
*/
if (additional_data == null || additional_data.Length != 13)
additional_data = new byte[13];
else
Array.Clear(additional_data, 0, additional_data.Length);
TlsUtilities.WriteUint64(seqNo, additional_data, 0);
TlsUtilities.WriteUint8(recordType, additional_data, 8);
TlsUtilities.WriteVersion(recordVersion, additional_data, 9);
TlsUtilities.WriteUint16(plaintextLength, additional_data, 11);
return additional_data;
}
}
private void RekeyCipher(SecurityParameters securityParameters, TlsAeadCipherImpl cipher,
byte[] nonce, bool serverSecret)
{
if (!m_isTlsV13)
throw new TlsFatalAlert(AlertDescription.internal_error);
TlsSecret secret = serverSecret
? securityParameters.TrafficSecretServer
: securityParameters.TrafficSecretClient;
// TODO[tls13] For early data, have to disable server->client
if (null == secret)
throw new TlsFatalAlert(AlertDescription.internal_error);
Setup13Cipher(cipher, nonce, secret, securityParameters.PrfCryptoHashAlgorithm);
}
private void Setup13Cipher(TlsAeadCipherImpl cipher, byte[] nonce, TlsSecret secret,
int cryptoHashAlgorithm)
{
byte[] key = TlsCryptoUtilities.HkdfExpandLabel(secret, cryptoHashAlgorithm, "key",
TlsUtilities.EmptyBytes, m_keySize).Extract();
byte[] iv = TlsCryptoUtilities.HkdfExpandLabel(secret, cryptoHashAlgorithm, "iv", TlsUtilities.EmptyBytes,
m_fixed_iv_length).Extract();
cipher.SetKey(key, 0, m_keySize);
Array.Copy(iv, 0, nonce, 0, m_fixed_iv_length);
// NOTE: Ensure dummy nonce is not part of the generated sequence(s)
iv[0] ^= 0x80;
cipher.Init(iv, m_macSize, null);
}
private static int GetNonceMode(bool isTLSv13, int aeadType)
{
switch (aeadType)
{
case AEAD_CCM:
case AEAD_GCM:
return isTLSv13 ? NONCE_RFC7905 : NONCE_RFC5288;
case AEAD_CHACHA20_POLY1305:
return NONCE_RFC7905;
default:
throw new TlsFatalAlert(AlertDescription.internal_error);
}
}
}
}
#endif