Terraria v1.4.4.9
Terraria source code documentation
Loading...
Searching...
No Matches
TlsFrameHelper.cs
Go to the documentation of this file.
4using System.Text;
5
6namespace System.Net.Security;
7
8internal static class TlsFrameHelper
9{
10 [Flags]
12 {
13 All = 0,
14 ServerName = 1,
16 Versions = 4
17 }
18
19 [Flags]
21 {
22 None = 0,
23 Http11 = 1,
24 Http2 = 2,
25 Other = 0x80
26 }
27
28 public struct TlsFrameInfo
29 {
31
33
35
36 public string TargetName;
37
39
41
42 public override string ToString()
43 {
44 if (Header.Type == TlsContentType.Handshake)
45 {
46 if (HandshakeType != TlsHandshakeType.ClientHello)
47 {
48 if (HandshakeType != TlsHandshakeType.ServerHello)
49 {
50 return $"{Header.Version}:{HandshakeType}[{Header.Length}] SupportedVersion='{SupportedVersions}'";
51 }
52 return $"{Header.Version}:{HandshakeType}[{Header.Length}] SupportedVersion='{SupportedVersions}' ApplicationProtocols='{ApplicationProtocols}'";
53 }
54 return $"{Header.Version}:{HandshakeType}[{Header.Length}] TargetName='{TargetName}' SupportedVersion='{SupportedVersions}' ApplicationProtocols='{ApplicationProtocols}'";
55 }
56 return $"{Header.Version}:{Header.Type}[{Header.Length}]";
57 }
58 }
59
61
62 private enum NameType : byte
63 {
65 }
66
67 private static byte[] s_protocolMismatch13 = new byte[7] { 21, 3, 4, 0, 2, 2, 70 };
68
69 private static byte[] s_protocolMismatch12 = new byte[7] { 21, 3, 3, 0, 2, 2, 70 };
70
71 private static byte[] s_protocolMismatch11 = new byte[7] { 21, 3, 2, 0, 2, 2, 70 };
72
73 private static byte[] s_protocolMismatch10 = new byte[7] { 21, 3, 1, 0, 2, 2, 70 };
74
75 private static byte[] s_protocolMismatch30 = new byte[7] { 21, 3, 0, 0, 2, 2, 40 };
76
77 private static readonly IdnMapping s_idnMapping = new IdnMapping
78 {
79 AllowUnassigned = true
80 };
81
83
84 public static bool TryGetFrameHeader(ReadOnlySpan<byte> frame, ref TlsFrameHeader header)
85 {
86 bool result = frame.Length > 4;
87 if (frame.Length >= 1)
88 {
89 header.Type = (TlsContentType)frame[0];
90 if (frame.Length >= 3)
91 {
92 if (frame[1] == 3)
93 {
94 if (frame.Length > 4)
95 {
96 header.Length = (frame[3] << 8) | frame[4];
97 }
98 header.Version = TlsMinorVersionToProtocol(frame[2]);
99 }
100 else
101 {
102 header.Length = -1;
103 header.Version = SslProtocols.None;
104 }
105 }
106 }
107 return result;
108 }
109
111 {
112 if (frame.Length < 5)
113 {
114 return false;
115 }
116 bool flag = TryGetFrameHeader(frame, ref info.Header);
117 info.SupportedVersions = info.Header.Version;
118 if (info.Header.Type == TlsContentType.Alert)
119 {
120 TlsAlertLevel level = (TlsAlertLevel)0;
121 TlsAlertDescription description = TlsAlertDescription.CloseNotify;
122 if (TryGetAlertInfo(frame, ref level, ref description))
123 {
124 info.AlertDescription = description;
125 return true;
126 }
127 return false;
128 }
129 if (info.Header.Type != TlsContentType.Handshake || frame.Length <= 5)
130 {
131 return false;
132 }
133 info.HandshakeType = (TlsHandshakeType)frame[5];
134 bool result = frame.Length >= 5 + info.Header.Length;
135 if (info.Header.Version >= SslProtocols.Tls && (info.HandshakeType == TlsHandshakeType.ClientHello || info.HandshakeType == TlsHandshakeType.ServerHello) && !TryParseHelloFrame(frame.Slice(5), ref info, options, callback))
136 {
137 result = false;
138 }
139 return result;
140 }
141
142 public static bool TryGetAlertInfo(ReadOnlySpan<byte> frame, ref TlsAlertLevel level, ref TlsAlertDescription description)
143 {
144 if (frame.Length < 7 || frame[0] != 21)
145 {
146 return false;
147 }
148 level = (TlsAlertLevel)frame[5];
149 description = (TlsAlertDescription)frame[6];
150 return true;
151 }
152
153 private static byte[] CreateProtocolVersionAlert(SslProtocols version)
154 {
155 return version switch
156 {
157 SslProtocols.Tls13 => s_protocolMismatch13,
158 SslProtocols.Tls12 => s_protocolMismatch12,
159 SslProtocols.Tls11 => s_protocolMismatch11,
160 SslProtocols.Tls => s_protocolMismatch10,
161 SslProtocols.Ssl3 => s_protocolMismatch30,
162 _ => Array.Empty<byte>(),
163 };
164 }
165
166 public static byte[] CreateAlertFrame(SslProtocols version, TlsAlertDescription reason)
167 {
168 if (reason == TlsAlertDescription.ProtocolVersion)
169 {
170 return CreateProtocolVersionAlert(version);
171 }
172 if (version > SslProtocols.Tls)
173 {
174 byte[] obj = new byte[7] { 21, 3, 3, 0, 2, 2, 0 };
175 obj[6] = (byte)reason;
176 byte[] array = obj;
177 switch (version)
178 {
179 case SslProtocols.Tls13:
180 array[2] = 4;
181 break;
182 case SslProtocols.Tls11:
183 array[2] = 2;
184 break;
185 case SslProtocols.Tls:
186 array[2] = 1;
187 break;
188 }
189 return array;
190 }
191 return Array.Empty<byte>();
192 }
193
195 {
196 if (sslHandshake.Length < 4 || (sslHandshake[0] != 1 && sslHandshake[0] != 2))
197 {
198 return false;
199 }
200 int num = ReadUInt24BigEndian(sslHandshake.Slice(1));
201 ReadOnlySpan<byte> readOnlySpan = sslHandshake.Slice(4);
202 if (readOnlySpan.Length < num)
203 {
204 return false;
205 }
206 if (readOnlySpan[0] == 3)
207 {
208 info.SupportedVersions |= TlsMinorVersionToProtocol(readOnlySpan[1]);
209 }
210 if (sslHandshake[0] != 1)
211 {
212 return TryParseServerHello(readOnlySpan.Slice(0, num), ref info, options, callback);
213 }
214 return TryParseClientHello(readOnlySpan.Slice(0, num), ref info, options, callback);
215 }
216
218 {
219 ReadOnlySpan<byte> bytes = SkipBytes(clientHello, 34);
223 if (bytes.IsEmpty)
224 {
225 return false;
226 }
228 bytes = SkipBytes(bytes, 2);
229 if (num != bytes.Length)
230 {
231 return false;
232 }
233 return TryParseHelloExtensions(bytes, ref info, options, callback);
234 }
235
237 {
238 ReadOnlySpan<byte> bytes = SkipBytes(serverHello, 34);
240 bytes = SkipBytes(bytes, 3);
241 if (bytes.IsEmpty)
242 {
243 return false;
244 }
246 bytes = SkipBytes(bytes, 2);
247 if (num != bytes.Length)
248 {
249 return false;
250 }
251 return TryParseHelloExtensions(bytes, ref info, options, callback);
252 }
253
255 {
256 bool result = true;
257 while (extensions.Length >= 4)
258 {
260 extensions = SkipBytes(extensions, 2);
261 ushort num = BinaryPrimitives.ReadUInt16BigEndian(extensions);
262 extensions = SkipBytes(extensions, 2);
263 if (extensions.Length < num)
264 {
265 result = false;
266 break;
267 }
268 ReadOnlySpan<byte> readOnlySpan = extensions.Slice(0, num);
269 if (extensionType == ExtensionType.ServerName && (options == ProcessingOptions.All || (options & ProcessingOptions.ServerName) == ProcessingOptions.ServerName))
270 {
271 if (!TryGetSniFromServerNameList(readOnlySpan, out var sni))
272 {
273 return false;
274 }
275 info.TargetName = sni;
276 }
277 else if (extensionType == ExtensionType.SupportedVersions && (options == ProcessingOptions.All || (options & ProcessingOptions.Versions) == ProcessingOptions.Versions))
278 {
279 if (!TryGetSupportedVersionsFromExtension(readOnlySpan, out var protocols))
280 {
281 return false;
282 }
283 info.SupportedVersions |= protocols;
284 }
285 else if (extensionType == ExtensionType.ApplicationProtocols && (options == ProcessingOptions.All || (options & ProcessingOptions.ApplicationProtocol) == ProcessingOptions.ApplicationProtocol))
286 {
287 if (!TryGetApplicationProtocolsFromExtension(readOnlySpan, out var alpn))
288 {
289 return false;
290 }
291 info.ApplicationProtocols |= alpn;
292 }
293 callback?.Invoke(ref info, extensionType, readOnlySpan);
294 extensions = extensions.Slice(num);
295 }
296 return result;
297 }
298
299 private static bool TryGetSniFromServerNameList(ReadOnlySpan<byte> serverNameListExtension, out string sni)
300 {
301 sni = null;
302 if (serverNameListExtension.Length < 2)
303 {
304 return false;
305 }
306 int num = BinaryPrimitives.ReadUInt16BigEndian(serverNameListExtension);
307 ReadOnlySpan<byte> readOnlySpan = serverNameListExtension.Slice(2);
308 if (num != readOnlySpan.Length)
309 {
310 return false;
311 }
312 ReadOnlySpan<byte> serverName = readOnlySpan.Slice(0, num);
313 sni = GetSniFromServerName(serverName, out var invalid);
314 return !invalid;
315 }
316
317 private static string GetSniFromServerName(ReadOnlySpan<byte> serverName, out bool invalid)
318 {
319 if (serverName.Length < 1)
320 {
321 invalid = true;
322 return null;
323 }
324 NameType nameType = (NameType)serverName[0];
325 ReadOnlySpan<byte> hostNameStruct = serverName.Slice(1);
326 if (nameType != 0)
327 {
328 invalid = true;
329 return null;
330 }
331 return GetSniFromHostNameStruct(hostNameStruct, out invalid);
332 }
333
334 private static string GetSniFromHostNameStruct(ReadOnlySpan<byte> hostNameStruct, out bool invalid)
335 {
336 int num = BinaryPrimitives.ReadUInt16BigEndian(hostNameStruct);
337 ReadOnlySpan<byte> bytes = hostNameStruct.Slice(2);
338 if (num != bytes.Length)
339 {
340 invalid = true;
341 return null;
342 }
343 invalid = false;
344 return DecodeString(bytes);
345 }
346
347 private static bool TryGetSupportedVersionsFromExtension(ReadOnlySpan<byte> extensionData, out SslProtocols protocols)
348 {
349 protocols = SslProtocols.None;
350 byte b = extensionData[0];
351 extensionData = extensionData.Slice(1);
352 if (extensionData.Length != b)
353 {
354 return false;
355 }
356 while (extensionData.Length >= 2)
357 {
358 if (extensionData[0] == 3)
359 {
360 protocols |= TlsMinorVersionToProtocol(extensionData[1]);
361 }
362 extensionData = extensionData.Slice(2);
363 }
364 return true;
365 }
366
368 {
369 alpn = ApplicationProtocolInfo.None;
370 if (extensionData.Length < 2)
371 {
372 return false;
373 }
374 int num = BinaryPrimitives.ReadUInt16BigEndian(extensionData);
375 ReadOnlySpan<byte> readOnlySpan = extensionData.Slice(2);
376 if (num != readOnlySpan.Length)
377 {
378 return false;
379 }
380 while (!readOnlySpan.IsEmpty)
381 {
382 byte b = readOnlySpan[0];
383 if (readOnlySpan.Length < b + 1)
384 {
385 return false;
386 }
387 ReadOnlySpan<byte> span = readOnlySpan.Slice(1, b);
388 if (b == 2)
389 {
390 if (span.SequenceEqual(SslApplicationProtocol.Http2.Protocol.Span))
391 {
392 alpn |= ApplicationProtocolInfo.Http2;
393 }
394 else
395 {
396 alpn |= ApplicationProtocolInfo.Other;
397 }
398 }
399 else if (b == SslApplicationProtocol.Http11.Protocol.Length && span.SequenceEqual(SslApplicationProtocol.Http11.Protocol.Span))
400 {
401 alpn |= ApplicationProtocolInfo.Http11;
402 }
403 else
404 {
405 alpn |= ApplicationProtocolInfo.Other;
406 }
407 readOnlySpan = readOnlySpan.Slice(b + 1);
408 }
409 return true;
410 }
411
413 {
414 return value switch
415 {
416 4 => SslProtocols.Tls13,
417 3 => SslProtocols.Tls12,
418 2 => SslProtocols.Tls11,
419 1 => SslProtocols.Tls,
420 0 => SslProtocols.Ssl3,
421 _ => SslProtocols.None,
422 };
423 }
424
425 private static string DecodeString(ReadOnlySpan<byte> bytes)
426 {
427 string @string;
428 try
429 {
430 @string = s_encoding.GetString(bytes);
431 }
433 {
434 return null;
435 }
436 try
437 {
438 return s_idnMapping.GetUnicode(@string);
439 }
440 catch (ArgumentException)
441 {
442 return @string;
443 }
444 }
445
447 {
448 return (bytes[0] << 16) | (bytes[1] << 8) | bytes[2];
449 }
450
451 private static ReadOnlySpan<byte> SkipBytes(ReadOnlySpan<byte> bytes, int numberOfBytesToSkip)
452 {
453 if (numberOfBytesToSkip >= bytes.Length)
454 {
456 }
457 return bytes.Slice(numberOfBytesToSkip);
458 }
459
461 {
462 if (bytes.Length < 1)
463 {
465 }
466 byte b = bytes[0];
467 int numberOfBytesToSkip = 1 + b;
468 return SkipBytes(bytes, numberOfBytesToSkip);
469 }
470
472 {
473 if (bytes.Length < 2)
474 {
476 }
478 int numberOfBytesToSkip = 2 + num;
479 return SkipBytes(bytes, numberOfBytesToSkip);
480 }
481}
static ushort ReadUInt16BigEndian(ReadOnlySpan< byte > source)
static ReadOnlySpan< byte > SkipOpaqueType1(ReadOnlySpan< byte > bytes)
static int ReadUInt24BigEndian(ReadOnlySpan< byte > bytes)
static string DecodeString(ReadOnlySpan< byte > bytes)
static bool TryGetAlertInfo(ReadOnlySpan< byte > frame, ref TlsAlertLevel level, ref TlsAlertDescription description)
static readonly Encoding s_encoding
static bool TryGetSniFromServerNameList(ReadOnlySpan< byte > serverNameListExtension, out string sni)
delegate bool HelloExtensionCallback(ref TlsFrameInfo info, ExtensionType type, ReadOnlySpan< byte > extensionsData)
static bool TryGetApplicationProtocolsFromExtension(ReadOnlySpan< byte > extensionData, out ApplicationProtocolInfo alpn)
static string GetSniFromServerName(ReadOnlySpan< byte > serverName, out bool invalid)
static string GetSniFromHostNameStruct(ReadOnlySpan< byte > hostNameStruct, out bool invalid)
static ReadOnlySpan< byte > SkipOpaqueType2(ReadOnlySpan< byte > bytes)
static byte[] CreateAlertFrame(SslProtocols version, TlsAlertDescription reason)
static bool TryParseHelloFrame(ReadOnlySpan< byte > sslHandshake, ref TlsFrameInfo info, ProcessingOptions options, HelloExtensionCallback callback)
static readonly IdnMapping s_idnMapping
static bool TryGetFrameInfo(ReadOnlySpan< byte > frame, ref TlsFrameInfo info, ProcessingOptions options=ProcessingOptions.All, HelloExtensionCallback callback=null)
static bool TryGetFrameHeader(ReadOnlySpan< byte > frame, ref TlsFrameHeader header)
static bool TryParseClientHello(ReadOnlySpan< byte > clientHello, ref TlsFrameInfo info, ProcessingOptions options, HelloExtensionCallback callback)
static bool TryParseServerHello(ReadOnlySpan< byte > serverHello, ref TlsFrameInfo info, ProcessingOptions options, HelloExtensionCallback callback)
static bool TryGetSupportedVersionsFromExtension(ReadOnlySpan< byte > extensionData, out SslProtocols protocols)
static SslProtocols TlsMinorVersionToProtocol(byte value)
static ReadOnlySpan< byte > SkipBytes(ReadOnlySpan< byte > bytes, int numberOfBytesToSkip)
static byte[] CreateProtocolVersionAlert(SslProtocols version)
static bool TryParseHelloExtensions(ReadOnlySpan< byte > extensions, ref TlsFrameInfo info, ProcessingOptions options, HelloExtensionCallback callback)
static Encoding GetEncoding(int codepage)
Definition Encoding.cs:593
static readonly SslApplicationProtocol Http2
static readonly SslApplicationProtocol Http11
static ReadOnlySpan< T > Empty
ReadOnlySpan< T > Slice(int start)