FastTlsAeadCipher.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. using System;
  3. using System.IO;
  4. using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto.Impl;
  5. using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto;
  6. using BestHTTP.SecureProtocol.Org.BouncyCastle.Tls;
  7. namespace BestHTTP.Connections.TLS.Crypto.Impl
  8. {
  9. /// <summary>A generic TLS 1.2 AEAD cipher.</summary>
  10. [BestHTTP.PlatformSupport.IL2CPP.Il2CppSetOption(BestHTTP.PlatformSupport.IL2CPP.Option.NullChecks, false)]
  11. [BestHTTP.PlatformSupport.IL2CPP.Il2CppSetOption(BestHTTP.PlatformSupport.IL2CPP.Option.ArrayBoundsChecks, false)]
  12. [BestHTTP.PlatformSupport.IL2CPP.Il2CppSetOption(BestHTTP.PlatformSupport.IL2CPP.Option.DivideByZeroChecks, false)]
  13. [BestHTTP.PlatformSupport.IL2CPP.Il2CppEagerStaticClassConstructionAttribute]
  14. public sealed class FastTlsAeadCipher
  15. : TlsCipher
  16. {
  17. public const int AEAD_CCM = 1;
  18. public const int AEAD_CHACHA20_POLY1305 = 2;
  19. public const int AEAD_GCM = 3;
  20. private const int NONCE_RFC5288 = 1;
  21. private const int NONCE_RFC7905 = 2;
  22. private readonly TlsCryptoParameters m_cryptoParams;
  23. private readonly int m_keySize;
  24. private readonly int m_macSize;
  25. private readonly int m_fixed_iv_length;
  26. private readonly int m_record_iv_length;
  27. private readonly TlsAeadCipherImpl m_decryptCipher, m_encryptCipher;
  28. private readonly byte[] m_decryptNonce, m_encryptNonce;
  29. private readonly bool m_isTlsV13;
  30. private readonly int m_nonceMode;
  31. /// <exception cref="IOException"/>
  32. public FastTlsAeadCipher(TlsCryptoParameters cryptoParams, TlsAeadCipherImpl encryptCipher,
  33. TlsAeadCipherImpl decryptCipher, int keySize, int macSize, int aeadType)
  34. {
  35. SecurityParameters securityParameters = cryptoParams.SecurityParameters;
  36. ProtocolVersion negotiatedVersion = securityParameters.NegotiatedVersion;
  37. if (!TlsImplUtilities.IsTlsV12(negotiatedVersion))
  38. throw new TlsFatalAlert(AlertDescription.internal_error);
  39. this.m_isTlsV13 = TlsImplUtilities.IsTlsV13(negotiatedVersion);
  40. this.m_nonceMode = GetNonceMode(m_isTlsV13, aeadType);
  41. switch (m_nonceMode)
  42. {
  43. case NONCE_RFC5288:
  44. this.m_fixed_iv_length = 4;
  45. this.m_record_iv_length = 8;
  46. break;
  47. case NONCE_RFC7905:
  48. this.m_fixed_iv_length = 12;
  49. this.m_record_iv_length = 0;
  50. break;
  51. default:
  52. throw new TlsFatalAlert(AlertDescription.internal_error);
  53. }
  54. this.m_cryptoParams = cryptoParams;
  55. this.m_keySize = keySize;
  56. this.m_macSize = macSize;
  57. this.m_decryptCipher = decryptCipher;
  58. this.m_encryptCipher = encryptCipher;
  59. this.m_decryptNonce = new byte[m_fixed_iv_length];
  60. this.m_encryptNonce = new byte[m_fixed_iv_length];
  61. bool isServer = cryptoParams.IsServer;
  62. if (m_isTlsV13)
  63. {
  64. RekeyCipher(securityParameters, decryptCipher, m_decryptNonce, !isServer);
  65. RekeyCipher(securityParameters, encryptCipher, m_encryptNonce, isServer);
  66. return;
  67. }
  68. int keyBlockSize = (2 * keySize) + (2 * m_fixed_iv_length);
  69. byte[] keyBlock = TlsImplUtilities.CalculateKeyBlock(cryptoParams, keyBlockSize);
  70. int pos = 0;
  71. if (isServer)
  72. {
  73. decryptCipher.SetKey(keyBlock, pos, keySize); pos += keySize;
  74. encryptCipher.SetKey(keyBlock, pos, keySize); pos += keySize;
  75. Array.Copy(keyBlock, pos, m_decryptNonce, 0, m_fixed_iv_length); pos += m_fixed_iv_length;
  76. Array.Copy(keyBlock, pos, m_encryptNonce, 0, m_fixed_iv_length); pos += m_fixed_iv_length;
  77. }
  78. else
  79. {
  80. encryptCipher.SetKey(keyBlock, pos, keySize); pos += keySize;
  81. decryptCipher.SetKey(keyBlock, pos, keySize); pos += keySize;
  82. Array.Copy(keyBlock, pos, m_encryptNonce, 0, m_fixed_iv_length); pos += m_fixed_iv_length;
  83. Array.Copy(keyBlock, pos, m_decryptNonce, 0, m_fixed_iv_length); pos += m_fixed_iv_length;
  84. }
  85. if (keyBlockSize != pos)
  86. throw new TlsFatalAlert(AlertDescription.internal_error);
  87. int nonceLength = m_fixed_iv_length + m_record_iv_length;
  88. // NOTE: Ensure dummy nonce is not part of the generated sequence(s)
  89. byte[] dummyNonce = new byte[nonceLength];
  90. dummyNonce[0] = (byte)~m_encryptNonce[0];
  91. dummyNonce[1] = (byte)~m_decryptNonce[1];
  92. encryptCipher.Init(dummyNonce, macSize, null);
  93. decryptCipher.Init(dummyNonce, macSize, null);
  94. }
  95. public int GetCiphertextDecodeLimit(int plaintextLimit)
  96. {
  97. return plaintextLimit + m_macSize + m_record_iv_length + (m_isTlsV13 ? 1 : 0);
  98. }
  99. public int GetCiphertextEncodeLimit(int plaintextLength, int plaintextLimit)
  100. {
  101. int innerPlaintextLimit = plaintextLength;
  102. if (m_isTlsV13)
  103. {
  104. // TODO[tls13] Add support for padding
  105. int maxPadding = 0;
  106. innerPlaintextLimit = 1 + System.Math.Min(plaintextLimit, plaintextLength + maxPadding);
  107. }
  108. return innerPlaintextLimit + m_macSize + m_record_iv_length;
  109. }
  110. public int GetPlaintextLimit(int ciphertextLimit)
  111. {
  112. return ciphertextLimit - m_macSize - m_record_iv_length - (m_isTlsV13 ? 1 : 0);
  113. }
  114. public TlsEncodeResult EncodePlaintext(long seqNo, short contentType, ProtocolVersion recordVersion,
  115. int headerAllocation, byte[] plaintext, int plaintextOffset, int plaintextLength)
  116. {
  117. byte[] nonce = new byte[m_encryptNonce.Length + m_record_iv_length];
  118. switch (m_nonceMode)
  119. {
  120. case NONCE_RFC5288:
  121. Array.Copy(m_encryptNonce, 0, nonce, 0, m_encryptNonce.Length);
  122. // RFC 5288/6655: The nonce_explicit MAY be the 64-bit sequence number.
  123. TlsUtilities.WriteUint64(seqNo, nonce, m_encryptNonce.Length);
  124. break;
  125. case NONCE_RFC7905:
  126. TlsUtilities.WriteUint64(seqNo, nonce, nonce.Length - 8);
  127. for (int i = 0; i < m_encryptNonce.Length; ++i)
  128. {
  129. nonce[i] ^= m_encryptNonce[i];
  130. }
  131. break;
  132. default:
  133. throw new TlsFatalAlert(AlertDescription.internal_error);
  134. }
  135. int extraLength = m_isTlsV13 ? 1 : 0;
  136. // TODO[tls13] If we support adding padding to TLSInnerPlaintext, this will need review
  137. int encryptionLength = m_encryptCipher.GetOutputSize(plaintextLength + extraLength);
  138. int ciphertextLength = m_record_iv_length + encryptionLength;
  139. byte[] output = new byte[headerAllocation + ciphertextLength];
  140. int outputPos = headerAllocation;
  141. if (m_record_iv_length != 0)
  142. {
  143. Array.Copy(nonce, nonce.Length - m_record_iv_length, output, outputPos, m_record_iv_length);
  144. outputPos += m_record_iv_length;
  145. }
  146. short recordType = m_isTlsV13 ? ContentType.application_data : contentType;
  147. byte[] additionalData = GetAdditionalData(seqNo, recordType, recordVersion, ciphertextLength,
  148. plaintextLength);
  149. try
  150. {
  151. m_encryptCipher.Init(nonce, m_macSize, additionalData);
  152. Array.Copy(plaintext, plaintextOffset, output, outputPos, plaintextLength);
  153. if (m_isTlsV13)
  154. {
  155. output[outputPos + plaintextLength] = (byte)contentType;
  156. }
  157. outputPos += m_encryptCipher.DoFinal(output, outputPos, plaintextLength + extraLength, output,
  158. outputPos);
  159. }
  160. catch (IOException e)
  161. {
  162. throw e;
  163. }
  164. catch (Exception e)
  165. {
  166. throw new TlsFatalAlert(AlertDescription.internal_error, e);
  167. }
  168. if (outputPos != output.Length)
  169. {
  170. // NOTE: The additional data mechanism for AEAD ciphers requires exact output size prediction.
  171. throw new TlsFatalAlert(AlertDescription.internal_error);
  172. }
  173. return new TlsEncodeResult(output, 0, output.Length, recordType);
  174. }
  175. byte[] decode_nonce = null;
  176. public TlsDecodeResult DecodeCiphertext(long seqNo, short recordType, ProtocolVersion recordVersion,
  177. byte[] ciphertext, int ciphertextOffset, int ciphertextLength)
  178. {
  179. if (GetPlaintextLimit(ciphertextLength) < 0)
  180. throw new TlsFatalAlert(AlertDescription.decode_error);
  181. if (decode_nonce == null || decode_nonce.Length != m_decryptNonce.Length + m_record_iv_length)
  182. decode_nonce = new byte[m_decryptNonce.Length + m_record_iv_length];
  183. else
  184. Array.Clear(decode_nonce, 0, decode_nonce.Length);
  185. switch (m_nonceMode)
  186. {
  187. case NONCE_RFC5288:
  188. Array.Copy(m_decryptNonce, 0, decode_nonce, 0, m_decryptNonce.Length);
  189. Array.Copy(ciphertext, ciphertextOffset, decode_nonce, decode_nonce.Length - m_record_iv_length,
  190. m_record_iv_length);
  191. break;
  192. case NONCE_RFC7905:
  193. TlsUtilities.WriteUint64(seqNo, decode_nonce, decode_nonce.Length - 8);
  194. for (int i = 0; i < m_decryptNonce.Length; ++i)
  195. {
  196. decode_nonce[i] ^= m_decryptNonce[i];
  197. }
  198. break;
  199. default:
  200. throw new TlsFatalAlert(AlertDescription.internal_error);
  201. }
  202. int encryptionOffset = ciphertextOffset + m_record_iv_length;
  203. int encryptionLength = ciphertextLength - m_record_iv_length;
  204. int plaintextLength = m_decryptCipher.GetOutputSize(encryptionLength);
  205. byte[] additionalData = GetAdditionalData(seqNo, recordType, recordVersion, ciphertextLength,
  206. plaintextLength);
  207. int outputPos;
  208. try
  209. {
  210. m_decryptCipher.Init(decode_nonce, m_macSize, additionalData);
  211. outputPos = m_decryptCipher.DoFinal(ciphertext, encryptionOffset, encryptionLength, ciphertext,
  212. encryptionOffset);
  213. }
  214. catch (IOException e)
  215. {
  216. throw e;
  217. }
  218. catch (Exception e)
  219. {
  220. throw new TlsFatalAlert(AlertDescription.bad_record_mac, e);
  221. }
  222. if (outputPos != plaintextLength)
  223. {
  224. // NOTE: The additional data mechanism for AEAD ciphers requires exact output size prediction.
  225. throw new TlsFatalAlert(AlertDescription.internal_error);
  226. }
  227. short contentType = recordType;
  228. if (m_isTlsV13)
  229. {
  230. // Strip padding and read true content type from TLSInnerPlaintext
  231. int pos = plaintextLength;
  232. for (; ; )
  233. {
  234. if (--pos < 0)
  235. throw new TlsFatalAlert(AlertDescription.unexpected_message);
  236. byte octet = ciphertext[encryptionOffset + pos];
  237. if (0 != octet)
  238. {
  239. contentType = (short)(octet & 0xFF);
  240. plaintextLength = pos;
  241. break;
  242. }
  243. }
  244. }
  245. return new TlsDecodeResult(ciphertext, encryptionOffset, plaintextLength, contentType);
  246. }
  247. public void RekeyDecoder()
  248. {
  249. RekeyCipher(m_cryptoParams.SecurityParameters, m_decryptCipher, m_decryptNonce, !m_cryptoParams.IsServer);
  250. }
  251. public void RekeyEncoder()
  252. {
  253. RekeyCipher(m_cryptoParams.SecurityParameters, m_encryptCipher, m_encryptNonce, m_cryptoParams.IsServer);
  254. }
  255. public bool UsesOpaqueRecordType
  256. {
  257. get { return m_isTlsV13; }
  258. }
  259. byte[] additional_data = null;
  260. private byte[] GetAdditionalData(long seqNo, short recordType, ProtocolVersion recordVersion,
  261. int ciphertextLength, int plaintextLength)
  262. {
  263. if (m_isTlsV13)
  264. {
  265. /*
  266. * TLSCiphertext.opaque_type || TLSCiphertext.legacy_record_version || TLSCiphertext.length
  267. */
  268. if (additional_data == null || additional_data.Length != 5)
  269. additional_data = new byte[5];
  270. else
  271. Array.Clear(additional_data, 0, additional_data.Length);
  272. TlsUtilities.WriteUint8(recordType, additional_data, 0);
  273. TlsUtilities.WriteVersion(recordVersion, additional_data, 1);
  274. TlsUtilities.WriteUint16(ciphertextLength, additional_data, 3);
  275. return additional_data;
  276. }
  277. else
  278. {
  279. /*
  280. * seq_num + TLSCompressed.type + TLSCompressed.version + TLSCompressed.length
  281. */
  282. if (additional_data == null || additional_data.Length != 13)
  283. additional_data = new byte[13];
  284. else
  285. Array.Clear(additional_data, 0, additional_data.Length);
  286. TlsUtilities.WriteUint64(seqNo, additional_data, 0);
  287. TlsUtilities.WriteUint8(recordType, additional_data, 8);
  288. TlsUtilities.WriteVersion(recordVersion, additional_data, 9);
  289. TlsUtilities.WriteUint16(plaintextLength, additional_data, 11);
  290. return additional_data;
  291. }
  292. }
  293. private void RekeyCipher(SecurityParameters securityParameters, TlsAeadCipherImpl cipher,
  294. byte[] nonce, bool serverSecret)
  295. {
  296. if (!m_isTlsV13)
  297. throw new TlsFatalAlert(AlertDescription.internal_error);
  298. TlsSecret secret = serverSecret
  299. ? securityParameters.TrafficSecretServer
  300. : securityParameters.TrafficSecretClient;
  301. // TODO[tls13] For early data, have to disable server->client
  302. if (null == secret)
  303. throw new TlsFatalAlert(AlertDescription.internal_error);
  304. Setup13Cipher(cipher, nonce, secret, securityParameters.PrfCryptoHashAlgorithm);
  305. }
  306. private void Setup13Cipher(TlsAeadCipherImpl cipher, byte[] nonce, TlsSecret secret,
  307. int cryptoHashAlgorithm)
  308. {
  309. byte[] key = TlsCryptoUtilities.HkdfExpandLabel(secret, cryptoHashAlgorithm, "key",
  310. TlsUtilities.EmptyBytes, m_keySize).Extract();
  311. byte[] iv = TlsCryptoUtilities.HkdfExpandLabel(secret, cryptoHashAlgorithm, "iv", TlsUtilities.EmptyBytes,
  312. m_fixed_iv_length).Extract();
  313. cipher.SetKey(key, 0, m_keySize);
  314. Array.Copy(iv, 0, nonce, 0, m_fixed_iv_length);
  315. // NOTE: Ensure dummy nonce is not part of the generated sequence(s)
  316. iv[0] ^= 0x80;
  317. cipher.Init(iv, m_macSize, null);
  318. }
  319. private static int GetNonceMode(bool isTLSv13, int aeadType)
  320. {
  321. switch (aeadType)
  322. {
  323. case AEAD_CCM:
  324. case AEAD_GCM:
  325. return isTLSv13 ? NONCE_RFC7905 : NONCE_RFC5288;
  326. case AEAD_CHACHA20_POLY1305:
  327. return NONCE_RFC7905;
  328. default:
  329. throw new TlsFatalAlert(AlertDescription.internal_error);
  330. }
  331. }
  332. }
  333. }
  334. #endif