TlsAeadCipher.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.IO;
  5. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Modes;
  6. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  7. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  8. namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Tls
  9. {
  10. public class TlsAeadCipher
  11. : TlsCipher
  12. {
  13. // TODO[draft-zauner-tls-aes-ocb-04] Apply data volume limit described in section 8.4
  14. public const int NONCE_RFC5288 = 1;
  15. /*
  16. * draft-zauner-tls-aes-ocb-04 specifies the nonce construction from draft-ietf-tls-chacha20-poly1305-04
  17. */
  18. internal const int NONCE_DRAFT_CHACHA20_POLY1305 = 2;
  19. protected readonly TlsContext context;
  20. protected readonly int macSize;
  21. // TODO SecurityParameters.record_iv_length
  22. protected readonly int record_iv_length;
  23. protected readonly IAeadBlockCipher encryptCipher;
  24. protected readonly IAeadBlockCipher decryptCipher;
  25. protected readonly byte[] encryptImplicitNonce, decryptImplicitNonce;
  26. protected readonly int nonceMode;
  27. /// <exception cref="IOException"></exception>
  28. public TlsAeadCipher(TlsContext context, IAeadBlockCipher clientWriteCipher, IAeadBlockCipher serverWriteCipher,
  29. int cipherKeySize, int macSize)
  30. : this(context, clientWriteCipher, serverWriteCipher, cipherKeySize, macSize, NONCE_RFC5288)
  31. {
  32. }
  33. /// <exception cref="IOException"></exception>
  34. internal TlsAeadCipher(TlsContext context, IAeadBlockCipher clientWriteCipher, IAeadBlockCipher serverWriteCipher,
  35. int cipherKeySize, int macSize, int nonceMode)
  36. {
  37. if (!TlsUtilities.IsTlsV12(context))
  38. throw new TlsFatalAlert(AlertDescription.internal_error);
  39. this.nonceMode = nonceMode;
  40. // TODO SecurityParameters.fixed_iv_length
  41. int fixed_iv_length;
  42. switch (nonceMode)
  43. {
  44. case NONCE_RFC5288:
  45. fixed_iv_length = 4;
  46. this.record_iv_length = 8;
  47. break;
  48. case NONCE_DRAFT_CHACHA20_POLY1305:
  49. fixed_iv_length = 12;
  50. this.record_iv_length = 0;
  51. break;
  52. default:
  53. throw new TlsFatalAlert(AlertDescription.internal_error);
  54. }
  55. this.context = context;
  56. this.macSize = macSize;
  57. int key_block_size = (2 * cipherKeySize) + (2 * fixed_iv_length);
  58. byte[] key_block = TlsUtilities.CalculateKeyBlock(context, key_block_size);
  59. int offset = 0;
  60. KeyParameter client_write_key = new KeyParameter(key_block, offset, cipherKeySize);
  61. offset += cipherKeySize;
  62. KeyParameter server_write_key = new KeyParameter(key_block, offset, cipherKeySize);
  63. offset += cipherKeySize;
  64. byte[] client_write_IV = Arrays.CopyOfRange(key_block, offset, offset + fixed_iv_length);
  65. offset += fixed_iv_length;
  66. byte[] server_write_IV = Arrays.CopyOfRange(key_block, offset, offset + fixed_iv_length);
  67. offset += fixed_iv_length;
  68. if (offset != key_block_size)
  69. throw new TlsFatalAlert(AlertDescription.internal_error);
  70. KeyParameter encryptKey, decryptKey;
  71. if (context.IsServer)
  72. {
  73. this.encryptCipher = serverWriteCipher;
  74. this.decryptCipher = clientWriteCipher;
  75. this.encryptImplicitNonce = server_write_IV;
  76. this.decryptImplicitNonce = client_write_IV;
  77. encryptKey = server_write_key;
  78. decryptKey = client_write_key;
  79. }
  80. else
  81. {
  82. this.encryptCipher = clientWriteCipher;
  83. this.decryptCipher = serverWriteCipher;
  84. this.encryptImplicitNonce = client_write_IV;
  85. this.decryptImplicitNonce = server_write_IV;
  86. encryptKey = client_write_key;
  87. decryptKey = server_write_key;
  88. }
  89. byte[] dummyNonce = new byte[fixed_iv_length + record_iv_length];
  90. this.encryptCipher.Init(true, new AeadParameters(encryptKey, 8 * macSize, dummyNonce));
  91. this.decryptCipher.Init(false, new AeadParameters(decryptKey, 8 * macSize, dummyNonce));
  92. }
  93. public virtual int GetPlaintextLimit(int ciphertextLimit)
  94. {
  95. // TODO We ought to be able to ask the decryptCipher (independently of it's current state!)
  96. return ciphertextLimit - macSize - record_iv_length;
  97. }
  98. /// <exception cref="IOException"></exception>
  99. public virtual byte[] EncodePlaintext(long seqNo, byte type, byte[] plaintext, int offset, int len)
  100. {
  101. byte[] nonce = new byte[encryptImplicitNonce.Length + record_iv_length];
  102. switch (nonceMode)
  103. {
  104. case NONCE_RFC5288:
  105. Array.Copy(encryptImplicitNonce, 0, nonce, 0, encryptImplicitNonce.Length);
  106. // RFC 5288/6655: The nonce_explicit MAY be the 64-bit sequence number.
  107. TlsUtilities.WriteUint64(seqNo, nonce, encryptImplicitNonce.Length);
  108. break;
  109. case NONCE_DRAFT_CHACHA20_POLY1305:
  110. TlsUtilities.WriteUint64(seqNo, nonce, nonce.Length - 8);
  111. for (int i = 0; i < encryptImplicitNonce.Length; ++i)
  112. {
  113. nonce[i] ^= encryptImplicitNonce[i];
  114. }
  115. break;
  116. default:
  117. throw new TlsFatalAlert(AlertDescription.internal_error);
  118. }
  119. int plaintextOffset = offset;
  120. int plaintextLength = len;
  121. int ciphertextLength = encryptCipher.GetOutputSize(plaintextLength);
  122. byte[] output = new byte[record_iv_length + ciphertextLength];
  123. if (record_iv_length != 0)
  124. {
  125. Array.Copy(nonce, nonce.Length - record_iv_length, output, 0, record_iv_length);
  126. }
  127. int outputPos = record_iv_length;
  128. byte[] additionalData = GetAdditionalData(seqNo, type, plaintextLength);
  129. AeadParameters parameters = new AeadParameters(null, 8 * macSize, nonce, additionalData);
  130. try
  131. {
  132. encryptCipher.Init(true, parameters);
  133. outputPos += encryptCipher.ProcessBytes(plaintext, plaintextOffset, plaintextLength, output, outputPos);
  134. outputPos += encryptCipher.DoFinal(output, outputPos);
  135. }
  136. catch (Exception e)
  137. {
  138. throw new TlsFatalAlert(AlertDescription.internal_error, e);
  139. }
  140. if (outputPos != output.Length)
  141. {
  142. // NOTE: Existing AEAD cipher implementations all give exact output lengths
  143. throw new TlsFatalAlert(AlertDescription.internal_error);
  144. }
  145. return output;
  146. }
  147. /// <exception cref="IOException"></exception>
  148. public virtual byte[] DecodeCiphertext(long seqNo, byte type, byte[] ciphertext, int offset, int len)
  149. {
  150. if (GetPlaintextLimit(len) < 0)
  151. throw new TlsFatalAlert(AlertDescription.decode_error);
  152. byte[] nonce = new byte[decryptImplicitNonce.Length + record_iv_length];
  153. switch (nonceMode)
  154. {
  155. case NONCE_RFC5288:
  156. Array.Copy(decryptImplicitNonce, 0, nonce, 0, decryptImplicitNonce.Length);
  157. Array.Copy(ciphertext, offset, nonce, nonce.Length - record_iv_length, record_iv_length);
  158. break;
  159. case NONCE_DRAFT_CHACHA20_POLY1305:
  160. TlsUtilities.WriteUint64(seqNo, nonce, nonce.Length - 8);
  161. for (int i = 0; i < decryptImplicitNonce.Length; ++i)
  162. {
  163. nonce[i] ^= decryptImplicitNonce[i];
  164. }
  165. break;
  166. default:
  167. throw new TlsFatalAlert(AlertDescription.internal_error);
  168. }
  169. int ciphertextOffset = offset + record_iv_length;
  170. int ciphertextLength = len - record_iv_length;
  171. int plaintextLength = decryptCipher.GetOutputSize(ciphertextLength);
  172. byte[] output = new byte[plaintextLength];
  173. int outputPos = 0;
  174. byte[] additionalData = GetAdditionalData(seqNo, type, plaintextLength);
  175. AeadParameters parameters = new AeadParameters(null, 8 * macSize, nonce, additionalData);
  176. try
  177. {
  178. decryptCipher.Init(false, parameters);
  179. outputPos += decryptCipher.ProcessBytes(ciphertext, ciphertextOffset, ciphertextLength, output, outputPos);
  180. outputPos += decryptCipher.DoFinal(output, outputPos);
  181. }
  182. catch (Exception e)
  183. {
  184. throw new TlsFatalAlert(AlertDescription.bad_record_mac, e);
  185. }
  186. if (outputPos != output.Length)
  187. {
  188. // NOTE: Existing AEAD cipher implementations all give exact output lengths
  189. throw new TlsFatalAlert(AlertDescription.internal_error);
  190. }
  191. return output;
  192. }
  193. /// <exception cref="IOException"></exception>
  194. protected virtual byte[] GetAdditionalData(long seqNo, byte type, int len)
  195. {
  196. /*
  197. * additional_data = seq_num + TLSCompressed.type + TLSCompressed.version +
  198. * TLSCompressed.length
  199. */
  200. byte[] additional_data = new byte[13];
  201. TlsUtilities.WriteUint64(seqNo, additional_data, 0);
  202. TlsUtilities.WriteUint8(type, additional_data, 8);
  203. TlsUtilities.WriteVersion(context.ServerVersion, additional_data, 9);
  204. TlsUtilities.WriteUint16(len, additional_data, 11);
  205. return additional_data;
  206. }
  207. }
  208. }
  209. #pragma warning restore
  210. #endif