AbstractTlsContext.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.IO;
  5. using System.Threading;
  6. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Utilities;
  7. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Tls.Crypto;
  8. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  9. namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Tls
  10. {
  11. internal abstract class AbstractTlsContext
  12. : TlsContext
  13. {
  14. private static long counter = DateTime.UtcNow.Ticks;
  15. private static long NextCounterValue()
  16. {
  17. return Interlocked.Increment(ref counter);
  18. }
  19. private static TlsNonceGenerator CreateNonceGenerator(TlsCrypto crypto, int connectionEnd)
  20. {
  21. #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER || UNITY_2021_2_OR_NEWER
  22. Span<byte> additionalSeedMaterial = stackalloc byte[16];
  23. Pack.UInt64_To_BE((ulong)NextCounterValue(), additionalSeedMaterial);
  24. Pack.UInt64_To_BE((ulong)DateTime.UtcNow.Ticks, additionalSeedMaterial[8..]);
  25. #else
  26. byte[] additionalSeedMaterial = new byte[16];
  27. Pack.UInt64_To_BE((ulong)NextCounterValue(), additionalSeedMaterial, 0);
  28. Pack.UInt64_To_BE((ulong)DateTime.UtcNow.Ticks, additionalSeedMaterial, 8);
  29. #endif
  30. additionalSeedMaterial[0] &= 0x7F;
  31. additionalSeedMaterial[0] |= (byte)(connectionEnd << 7);
  32. return crypto.CreateNonceGenerator(additionalSeedMaterial);
  33. }
  34. private readonly TlsCrypto m_crypto;
  35. private readonly int m_connectionEnd;
  36. private readonly TlsNonceGenerator m_nonceGenerator;
  37. private SecurityParameters m_securityParameters = null;
  38. private ProtocolVersion[] m_clientSupportedVersions = null;
  39. private ProtocolVersion m_clientVersion = null;
  40. private ProtocolVersion m_rsaPreMasterSecretVersion = null;
  41. private TlsSession m_session = null;
  42. private object m_userObject = null;
  43. private bool m_connected = false;
  44. internal AbstractTlsContext(TlsCrypto crypto, int connectionEnd)
  45. {
  46. this.m_crypto = crypto;
  47. this.m_connectionEnd = connectionEnd;
  48. this.m_nonceGenerator = CreateNonceGenerator(crypto, connectionEnd);
  49. }
  50. /// <exception cref="IOException"/>
  51. internal void HandshakeBeginning(TlsPeer peer)
  52. {
  53. lock (this)
  54. {
  55. //if (null != m_securityParameters)
  56. // throw new TlsFatalAlert(AlertDescription.internal_error, "Handshake already started");
  57. var tmp = this.m_securityParameters;
  58. m_securityParameters = new SecurityParameters();
  59. m_securityParameters.m_entity = m_connectionEnd;
  60. if (tmp != null)
  61. {
  62. this.m_securityParameters.IsRenegotiating = true;
  63. this.m_securityParameters.m_secureRenegotiation = tmp.m_secureRenegotiation;
  64. this.m_securityParameters.m_negotiatedVersion = tmp.m_negotiatedVersion;
  65. this.m_securityParameters.m_localVerifyData = tmp.m_localVerifyData;
  66. this.m_securityParameters.m_peerVerifyData = tmp.m_peerVerifyData;
  67. this.m_securityParameters.PreRenegotiatingServerCert = tmp.m_peerCertificate;
  68. }
  69. }
  70. peer.NotifyHandshakeBeginning();
  71. }
  72. /// <exception cref="IOException"/>
  73. internal void HandshakeComplete(TlsPeer peer, TlsSession session)
  74. {
  75. lock (this)
  76. {
  77. if (null == m_securityParameters)
  78. throw new TlsFatalAlert(AlertDescription.internal_error);
  79. this.m_session = session;
  80. this.m_connected = true;
  81. }
  82. peer.NotifyHandshakeComplete();
  83. }
  84. internal bool IsConnected
  85. {
  86. get { lock (this) return m_connected; }
  87. }
  88. internal bool IsHandshaking
  89. {
  90. get { lock (this) return !m_connected && null != m_securityParameters; }
  91. }
  92. public TlsCrypto Crypto
  93. {
  94. get { return m_crypto; }
  95. }
  96. public virtual TlsNonceGenerator NonceGenerator
  97. {
  98. get { return m_nonceGenerator; }
  99. }
  100. public SecurityParameters SecurityParameters
  101. {
  102. get { lock (this) return m_securityParameters; }
  103. }
  104. public abstract bool IsServer { get; }
  105. public virtual ProtocolVersion[] ClientSupportedVersions
  106. {
  107. get { return m_clientSupportedVersions; }
  108. }
  109. internal void SetClientSupportedVersions(ProtocolVersion[] clientSupportedVersions)
  110. {
  111. this.m_clientSupportedVersions = clientSupportedVersions;
  112. }
  113. public virtual ProtocolVersion ClientVersion
  114. {
  115. get { return m_clientVersion; }
  116. }
  117. internal void SetClientVersion(ProtocolVersion clientVersion)
  118. {
  119. this.m_clientVersion = clientVersion;
  120. }
  121. public virtual ProtocolVersion RsaPreMasterSecretVersion
  122. {
  123. get { return m_rsaPreMasterSecretVersion; }
  124. }
  125. internal void SetRsaPreMasterSecretVersion(ProtocolVersion rsaPreMasterSecretVersion)
  126. {
  127. this.m_rsaPreMasterSecretVersion = rsaPreMasterSecretVersion;
  128. }
  129. public virtual ProtocolVersion ServerVersion
  130. {
  131. get { return SecurityParameters.NegotiatedVersion; }
  132. }
  133. public virtual TlsSession ResumableSession
  134. {
  135. get
  136. {
  137. TlsSession session = Session;
  138. if (session == null || !session.IsResumable)
  139. return null;
  140. return session;
  141. }
  142. }
  143. public virtual TlsSession Session
  144. {
  145. get { return m_session; }
  146. }
  147. public virtual object UserObject
  148. {
  149. get { return m_userObject; }
  150. set { this.m_userObject = value; }
  151. }
  152. public virtual byte[] ExportChannelBinding(int channelBinding)
  153. {
  154. if (!IsConnected)
  155. throw new InvalidOperationException("Export of channel bindings unavailable before handshake completion");
  156. SecurityParameters securityParameters = SecurityParameters;
  157. if (ChannelBinding.tls_exporter == channelBinding)
  158. return ExportKeyingMaterial("EXPORTER-Channel-Binding", TlsUtilities.EmptyBytes, 32);
  159. if (TlsUtilities.IsTlsV13(securityParameters.NegotiatedVersion))
  160. return null;
  161. switch (channelBinding)
  162. {
  163. case ChannelBinding.tls_server_end_point:
  164. {
  165. byte[] tlsServerEndPoint = securityParameters.TlsServerEndPoint;
  166. return TlsUtilities.IsNullOrEmpty(tlsServerEndPoint) ? null : Arrays.Clone(tlsServerEndPoint);
  167. }
  168. case ChannelBinding.tls_unique:
  169. {
  170. return Arrays.Clone(securityParameters.TlsUnique);
  171. }
  172. case ChannelBinding.tls_unique_for_telnet:
  173. default:
  174. throw new NotSupportedException();
  175. }
  176. }
  177. public virtual byte[] ExportEarlyKeyingMaterial(string asciiLabel, byte[] context, int length)
  178. {
  179. // TODO[tls13] Ensure early_exporter_master_secret is available suitably early!
  180. if (!IsConnected)
  181. throw new InvalidOperationException("Export of early key material only available during handshake");
  182. SecurityParameters sp = SecurityParameters;
  183. return ExportKeyingMaterial13(CheckEarlyExportSecret(sp.EarlyExporterMasterSecret),
  184. sp.PrfCryptoHashAlgorithm, asciiLabel, context, length);
  185. }
  186. public virtual byte[] ExportKeyingMaterial(string asciiLabel, byte[] context, int length)
  187. {
  188. if (!IsConnected)
  189. throw new InvalidOperationException("Export of key material unavailable before handshake completion");
  190. /*
  191. * TODO[tls13] Introduce a TlsExporter interface? Avoid calculating (early) exporter
  192. * secret(s) unless the peer actually uses it.
  193. */
  194. SecurityParameters sp = SecurityParameters;
  195. if (!sp.IsExtendedMasterSecret)
  196. {
  197. /*
  198. * RFC 7627 5.4. If a client or server chooses to continue with a full handshake without
  199. * the extended master secret extension, [..] the client or server MUST NOT export any
  200. * key material based on the new master secret for any subsequent application-level
  201. * authentication. In particular, it MUST disable [RFC5705] [..].
  202. */
  203. throw new InvalidOperationException("Export of key material requires extended_master_secret");
  204. }
  205. if (TlsUtilities.IsTlsV13(sp.NegotiatedVersion))
  206. {
  207. return ExportKeyingMaterial13(CheckExportSecret(sp.ExporterMasterSecret), sp.PrfCryptoHashAlgorithm,
  208. asciiLabel, context, length);
  209. }
  210. byte[] seed = TlsUtilities.CalculateExporterSeed(sp, context);
  211. return TlsUtilities.Prf(sp, CheckExportSecret(sp.MasterSecret), asciiLabel, seed, length).Extract();
  212. }
  213. protected virtual byte[] ExportKeyingMaterial13(TlsSecret secret, int cryptoHashAlgorithm, string asciiLabel,
  214. byte[] context, int length)
  215. {
  216. if (null == context)
  217. {
  218. context = TlsUtilities.EmptyBytes;
  219. }
  220. else if (!TlsUtilities.IsValidUint16(context.Length))
  221. {
  222. throw new ArgumentException("must have length less than 2^16 (or be null)", "context");
  223. }
  224. TlsHash exporterHash = Crypto.CreateHash(cryptoHashAlgorithm);
  225. byte[] emptyTranscriptHash = exporterHash.CalculateHash();
  226. TlsSecret exporterSecret = TlsUtilities.DeriveSecret(SecurityParameters, secret, asciiLabel,
  227. emptyTranscriptHash);
  228. byte[] exporterContext = emptyTranscriptHash;
  229. if (context.Length > 0)
  230. {
  231. exporterHash.Update(context, 0, context.Length);
  232. exporterContext = exporterHash.CalculateHash();
  233. }
  234. return TlsCryptoUtilities
  235. .HkdfExpandLabel(exporterSecret, cryptoHashAlgorithm, "exporter", exporterContext, length).Extract();
  236. }
  237. protected virtual TlsSecret CheckEarlyExportSecret(TlsSecret secret)
  238. {
  239. if (null == secret)
  240. {
  241. // TODO[tls13] For symmetry with normal export, ideally available for NotifyHandshakeBeginning() only
  242. //throw new InvalidOperationException("Export of early key material only available from NotifyHandshakeBeginning()");
  243. throw new InvalidOperationException("Export of early key material not available for this handshake");
  244. }
  245. return secret;
  246. }
  247. protected virtual TlsSecret CheckExportSecret(TlsSecret secret)
  248. {
  249. if (null == secret)
  250. throw new InvalidOperationException(
  251. "Export of key material only available from NotifyHandshakeComplete()");
  252. return secret;
  253. }
  254. }
  255. }
  256. #pragma warning restore
  257. #endif