ClientHello.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.Collections;
  5. using System.IO;
  6. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  7. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.IO;
  8. namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Tls
  9. {
  10. public sealed class ClientHello
  11. {
  12. private readonly ProtocolVersion m_version;
  13. private readonly byte[] m_random;
  14. private readonly byte[] m_sessionID;
  15. private readonly byte[] m_cookie;
  16. private readonly int[] m_cipherSuites;
  17. private readonly IDictionary m_extensions;
  18. private readonly int m_bindersSize;
  19. public ClientHello(ProtocolVersion version, byte[] random, byte[] sessionID, byte[] cookie,
  20. int[] cipherSuites, IDictionary extensions, int bindersSize)
  21. {
  22. this.m_version = version;
  23. this.m_random = random;
  24. this.m_sessionID = sessionID;
  25. this.m_cookie = cookie;
  26. this.m_cipherSuites = cipherSuites;
  27. this.m_extensions = extensions;
  28. this.m_bindersSize = bindersSize;
  29. }
  30. public int BindersSize
  31. {
  32. get { return m_bindersSize; }
  33. }
  34. public int[] CipherSuites
  35. {
  36. get { return m_cipherSuites; }
  37. }
  38. public byte[] Cookie
  39. {
  40. get { return m_cookie; }
  41. }
  42. public IDictionary Extensions
  43. {
  44. get { return m_extensions; }
  45. }
  46. public byte[] Random
  47. {
  48. get { return m_random; }
  49. }
  50. public byte[] SessionID
  51. {
  52. get { return m_sessionID; }
  53. }
  54. public ProtocolVersion Version
  55. {
  56. get { return m_version; }
  57. }
  58. /// <summary>Encode this <see cref="ClientHello"/> to a <see cref="Stream"/>.</summary>
  59. /// <param name="context">the <see cref="TlsContext"/> of the current connection.</param>
  60. /// <param name="output">the <see cref="Stream"/> to encode to.</param>
  61. /// <exception cref="IOException"/>
  62. public void Encode(TlsContext context, Stream output)
  63. {
  64. if (m_bindersSize < 0)
  65. throw new TlsFatalAlert(AlertDescription.internal_error);
  66. TlsUtilities.WriteVersion(m_version, output);
  67. output.Write(m_random, 0, m_random.Length);
  68. TlsUtilities.WriteOpaque8(m_sessionID, output);
  69. if (null != m_cookie)
  70. {
  71. TlsUtilities.WriteOpaque8(m_cookie, output);
  72. }
  73. TlsUtilities.WriteUint16ArrayWithUint16Length(m_cipherSuites, output);
  74. TlsUtilities.WriteUint8ArrayWithUint8Length(new short[]{ CompressionMethod.cls_null }, output);
  75. TlsProtocol.WriteExtensions(output, m_extensions, m_bindersSize);
  76. }
  77. /// <summary>Parse a <see cref="ClientHello"/> from a <see cref="MemoryStream"/>.</summary>
  78. /// <param name="messageInput">the <see cref="MemoryStream"/> to parse from.</param>
  79. /// <param name="dtlsOutput">for DTLS this should be non-null; the input is copied to this
  80. /// <see cref="Stream"/>, minus the cookie field.</param>
  81. /// <returns>a <see cref="ClientHello"/> object.</returns>
  82. /// <exception cref="TlsFatalAlert"/>
  83. public static ClientHello Parse(MemoryStream messageInput, Stream dtlsOutput)
  84. {
  85. try
  86. {
  87. return ImplParse(messageInput, dtlsOutput);
  88. }
  89. catch (TlsFatalAlert e)
  90. {
  91. throw e;
  92. }
  93. catch (IOException e)
  94. {
  95. throw new TlsFatalAlert(AlertDescription.decode_error, e);
  96. }
  97. }
  98. /// <exception cref="IOException"/>
  99. private static ClientHello ImplParse(MemoryStream messageInput, Stream dtlsOutput)
  100. {
  101. Stream input = messageInput;
  102. if (null != dtlsOutput)
  103. {
  104. input = new TeeInputStream(input, dtlsOutput);
  105. }
  106. ProtocolVersion clientVersion = TlsUtilities.ReadVersion(input);
  107. byte[] random = TlsUtilities.ReadFully(32, input);
  108. byte[] sessionID = TlsUtilities.ReadOpaque8(input, 0, 32);
  109. byte[] cookie = null;
  110. if (null != dtlsOutput)
  111. {
  112. /*
  113. * RFC 6347 This specification increases the cookie size limit to 255 bytes for greater
  114. * future flexibility. The limit remains 32 for previous versions of DTLS.
  115. */
  116. int maxCookieLength = ProtocolVersion.DTLSv12.IsEqualOrEarlierVersionOf(clientVersion) ? 255 : 32;
  117. cookie = TlsUtilities.ReadOpaque8(messageInput, 0, maxCookieLength);
  118. }
  119. int cipher_suites_length = TlsUtilities.ReadUint16(input);
  120. if (cipher_suites_length < 2 || (cipher_suites_length & 1) != 0
  121. || (int)(messageInput.Length - messageInput.Position) < cipher_suites_length)
  122. {
  123. throw new TlsFatalAlert(AlertDescription.decode_error);
  124. }
  125. /*
  126. * NOTE: "If the session_id field is not empty (implying a session resumption request) this
  127. * vector must include at least the cipher_suite from that session."
  128. */
  129. int[] cipherSuites = TlsUtilities.ReadUint16Array(cipher_suites_length / 2, input);
  130. short[] compressionMethods = TlsUtilities.ReadUint8ArrayWithUint8Length(input, 1);
  131. if (!Arrays.Contains(compressionMethods, CompressionMethod.cls_null))
  132. throw new TlsFatalAlert(AlertDescription.handshake_failure);
  133. /*
  134. * NOTE: Can't use TlsProtocol.ReadExtensions directly because TeeInputStream a) won't have
  135. * 'Length' or 'Position' properties in the FIPS provider, b) isn't a MemoryStream.
  136. */
  137. IDictionary extensions = null;
  138. if (messageInput.Position < messageInput.Length)
  139. {
  140. byte[] extBytes = TlsUtilities.ReadOpaque16(input);
  141. TlsProtocol.AssertEmpty(messageInput);
  142. extensions = TlsProtocol.ReadExtensionsDataClientHello(extBytes);
  143. }
  144. return new ClientHello(clientVersion, random, sessionID, cookie, cipherSuites, extensions, -1);
  145. }
  146. }
  147. }
  148. #pragma warning restore
  149. #endif