FastBcChaCha20Poly1305.cs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Engines;
  5. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Macs;
  6. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  7. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
  8. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto.Impl;
  9. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Tls;
  10. using Best.HTTP.Shared.PlatformSupport.Memory;
  11. namespace Best.HTTP.Shared.TLS.Crypto.Impl
  12. {
  13. public sealed class FastBcChaCha20Poly1305
  14. : TlsAeadCipherImpl
  15. {
  16. private static readonly byte[] Zeroes = new byte[15];
  17. private readonly FastChaCha7539Engine m_cipher = new FastChaCha7539Engine();
  18. private readonly FastPoly1305 m_mac = new FastPoly1305();
  19. private readonly bool m_isEncrypting;
  20. private int m_additionalDataLength;
  21. public FastBcChaCha20Poly1305(bool isEncrypting)
  22. {
  23. this.m_isEncrypting = isEncrypting;
  24. }
  25. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  26. unsafe
  27. #endif
  28. public int DoFinal(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset)
  29. {
  30. if (m_isEncrypting)
  31. {
  32. int ciphertextLength = inputLength;
  33. m_cipher.DoFinal(input, inputOffset, inputLength, output, outputOffset);
  34. int outputLength = inputLength;
  35. if (ciphertextLength != outputLength)
  36. throw new InvalidOperationException();
  37. UpdateMac(output, outputOffset, ciphertextLength);
  38. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  39. Span<byte> lengths = stackalloc byte[16];
  40. Pack.UInt64_To_LE((ulong)m_additionalDataLength, lengths);
  41. Pack.UInt64_To_LE((ulong)ciphertextLength, lengths[8..]);
  42. m_mac.BlockUpdate(lengths);
  43. m_mac.DoFinal(output.AsSpan(outputOffset + ciphertextLength));
  44. #else
  45. byte[] lengths = BufferPool.Get(16, true);
  46. using (var _ = new AutoReleaseBuffer(lengths))
  47. {
  48. Pack.UInt64_To_LE((ulong)m_additionalDataLength, lengths, 0);
  49. Pack.UInt64_To_LE((ulong)ciphertextLength, lengths, 8);
  50. m_mac.BlockUpdate(lengths, 0, 16);
  51. m_mac.DoFinal(output, outputOffset + ciphertextLength);
  52. }
  53. #endif
  54. return ciphertextLength + 16;
  55. }
  56. else
  57. {
  58. int ciphertextLength = inputLength - 16;
  59. UpdateMac(input, inputOffset, ciphertextLength);
  60. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  61. Span<byte> expectedMac = stackalloc byte[16];
  62. Pack.UInt64_To_LE((ulong)m_additionalDataLength, expectedMac);
  63. Pack.UInt64_To_LE((ulong)ciphertextLength, expectedMac[8..]);
  64. m_mac.BlockUpdate(expectedMac);
  65. m_mac.DoFinal(expectedMac);
  66. bool badMac = !TlsUtilities.ConstantTimeAreEqual(16, expectedMac, 0, input, inputOffset + ciphertextLength);
  67. if (badMac)
  68. throw new TlsFatalAlert(AlertDescription.bad_record_mac);
  69. #else
  70. byte[] expectedMac = BufferPool.Get(16, true);
  71. using (var _ = new AutoReleaseBuffer(expectedMac))
  72. {
  73. Pack.UInt64_To_LE((ulong)m_additionalDataLength, expectedMac, 0);
  74. Pack.UInt64_To_LE((ulong)ciphertextLength, expectedMac, 8);
  75. m_mac.BlockUpdate(expectedMac, 0, 16);
  76. m_mac.DoFinal(expectedMac, 0);
  77. bool badMac = !TlsUtilities.ConstantTimeAreEqual(16, expectedMac, 0, input, inputOffset + ciphertextLength);
  78. if (badMac)
  79. throw new TlsFatalAlert(AlertDescription.bad_record_mac);
  80. }
  81. #endif
  82. m_cipher.DoFinal(input, inputOffset, ciphertextLength, output, outputOffset);
  83. int outputLength = ciphertextLength;
  84. if (ciphertextLength != outputLength)
  85. throw new InvalidOperationException();
  86. return ciphertextLength;
  87. }
  88. }
  89. public int GetOutputSize(int inputLength)
  90. {
  91. return m_isEncrypting ? inputLength + 16 : inputLength - 16;
  92. }
  93. public void Init(byte[] nonce, int macSize, byte[] additionalData)
  94. {
  95. if (nonce == null || nonce.Length != 12 || macSize != 16)
  96. throw new TlsFatalAlert(AlertDescription.internal_error);
  97. m_cipher.Init(m_isEncrypting, new ParametersWithIV(null, nonce));
  98. InitMac();
  99. if (additionalData == null)
  100. {
  101. this.m_additionalDataLength = 0;
  102. }
  103. else
  104. {
  105. this.m_additionalDataLength = additionalData.Length;
  106. UpdateMac(additionalData, 0, additionalData.Length);
  107. }
  108. }
  109. public void Reset()
  110. {
  111. m_cipher.Reset();
  112. m_mac.Reset();
  113. }
  114. public void SetKey(byte[] key, int keyOff, int keyLen)
  115. {
  116. KeyParameter cipherKey = new KeyParameter(key, keyOff, keyLen);
  117. m_cipher.Init(m_isEncrypting, new ParametersWithIV(cipherKey, Zeroes, 0, 12));
  118. }
  119. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  120. public void SetKey(ReadOnlySpan<byte> key)
  121. {
  122. KeyParameter cipherKey = new KeyParameter(key);
  123. m_cipher.Init(m_isEncrypting, new ParametersWithIV(cipherKey, Zeroes[..12]));
  124. }
  125. #endif
  126. byte[] firstBlock = new byte[64];
  127. private void InitMac()
  128. {
  129. m_cipher.ProcessBytes(firstBlock, 0, 64, firstBlock, 0);
  130. m_mac.Init(new KeyParameter(firstBlock, 0, 32));
  131. Array.Clear(firstBlock, 0, firstBlock.Length);
  132. }
  133. private void UpdateMac(byte[] buf, int off, int len)
  134. {
  135. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  136. m_mac.BlockUpdate(buf.AsSpan(off, len));
  137. #else
  138. m_mac.BlockUpdate(buf, off, len);
  139. #endif
  140. int partial = len % 16;
  141. if (partial != 0)
  142. {
  143. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  144. m_mac.BlockUpdate(Zeroes.AsSpan(0, 16 - partial));
  145. #else
  146. m_mac.BlockUpdate(Zeroes, 0, 16 - partial);
  147. #endif
  148. }
  149. }
  150. }
  151. }
  152. #pragma warning restore
  153. #endif