parse_tls_client_hello static method
Implementation
static ClientHello parse_tls_client_hello(Uint8List body) {
final view = body;
int ptr = 0;
final legacyVersion = (view[ptr++] << 8) | view[ptr++];
final random = view.sublist(ptr, ptr + 32);
ptr += 32;
final sessionIdLen = view[ptr++];
final sessionId = view.sublist(ptr, ptr + sessionIdLen);
ptr += sessionIdLen;
final cipherSuitesLen = (view[ptr++] << 8) | view[ptr++];
final cipherSuites = <int>[];
for (int i = 0; i < cipherSuitesLen; i += 2) {
final code = (view[ptr++] << 8) | view[ptr++];
cipherSuites.add(code);
}
final compressionMethodsLen = view[ptr++];
final compressionMethods = view.sublist(ptr, ptr + compressionMethodsLen);
ptr += compressionMethodsLen;
final extensionsLen = (view[ptr++] << 8) | view[ptr++];
final extensions = <TlsExtension>[];
final extEnd = ptr + extensionsLen;
while (ptr < extEnd) {
final extType = (view[ptr++] << 8) | view[ptr++];
final extLen = (view[ptr++] << 8) | view[ptr++];
final extData = view.sublist(ptr, ptr + extLen);
ptr += extLen;
extensions.add(
TlsExtension(type: extType, length: extLen, data: extData),
);
}
// ----------------------------------------------------------
// Semantic decoding
// ----------------------------------------------------------
String? sni;
final keyShares = <ParsedKeyShare>[];
final supportedGroups = <int>[];
final supportedVersions = <int>[];
final signatureAlgorithms = <int>[];
final alpnProtocols = <String>[];
Uint8List? cookie;
List<int>? pskKeyExchangeModes;
Uint8List? quicTransportParametersRaw;
for (final ext in extensions) {
final buf = QuicBuffer(data: ext.data);
switch (ext.type) {
// ------------------------------------------------------
// server_name (0x0000)
// ------------------------------------------------------
case 0x0000:
if (buf.remaining < 2) break;
final listLen = buf.pullUint16();
final end = buf.readOffset + listLen;
while (buf.readOffset < end && buf.remaining > 0) {
final nameType = buf.pullUint8();
final nameLen = buf.pullUint16();
final nameBytes = buf.pullBytes(nameLen);
if (nameType == 0x00) {
sni = utf8.decode(nameBytes, allowMalformed: true);
}
}
break;
// ------------------------------------------------------
// supported_groups (0x000a)
// ------------------------------------------------------
case 0x000a:
final len = buf.pullUint16();
for (int i = 0; i < len; i += 2) {
supportedGroups.add(buf.pullUint16());
}
break;
// ------------------------------------------------------
// signature_algorithms (0x000d)
// ------------------------------------------------------
case 0x000d:
final len = buf.pullUint16();
for (int i = 0; i < len; i += 2) {
signatureAlgorithms.add(buf.pullUint16());
}
break;
// ------------------------------------------------------
// ALPN (0x0010)
// ------------------------------------------------------
case 0x0010:
alpnProtocols.addAll(parseAlpnExtensionData(ext.data));
break;
// ------------------------------------------------------
// cookie (0x002c)
// ------------------------------------------------------
case 0x002c:
if (buf.remaining < 2) break;
final len = buf.pullUint16();
cookie = buf.pullBytes(len);
break;
// ------------------------------------------------------
// psk_key_exchange_modes (0x002d)
// ------------------------------------------------------
case 0x002d:
if (buf.remaining < 1) break;
final len = buf.pullUint8();
pskKeyExchangeModes = <int>[];
for (int i = 0; i < len; i++) {
pskKeyExchangeModes.add(buf.pullUint8());
}
break;
// ------------------------------------------------------
// supported_versions (0x002b)
// ------------------------------------------------------
case 0x002b:
final len = buf.pullUint8();
for (int i = 0; i < len; i += 2) {
supportedVersions.add(buf.pullUint16());
}
break;
// ------------------------------------------------------
// key_share (0x0033)
// ------------------------------------------------------
case 0x0033:
final listLen = buf.pullUint16();
final end = buf.readOffset + listLen;
while (buf.readOffset < end) {
final group = buf.pullUint16();
final keyLen = buf.pullUint16();
final key = buf.pullBytes(keyLen);
keyShares.add(ParsedKeyShare(group, key));
}
break;
// ------------------------------------------------------
// QUIC transport parameters (0x0039)
// ------------------------------------------------------
case 0x0039:
quicTransportParametersRaw = ext.data;
break;
default:
break;
}
}
return ClientHello(
type: 'client_hello',
legacyVersion: legacyVersion,
random: random,
sessionId: sessionId,
cipherSuites: cipherSuites,
compressionMethods: compressionMethods,
extensions: extensions,
rawData: body,
sni: sni,
keyShares: keyShares,
supportedGroups: supportedGroups,
supportedVersions: supportedVersions,
signatureAlgorithms: signatureAlgorithms,
alpn: alpnProtocols,
cookie: cookie,
pskKeyExchangeModes: pskKeyExchangeModes,
quicTransportParametersRaw: quicTransportParametersRaw,
);
}