parse_tls_client_hello static method

ClientHello parse_tls_client_hello(
  1. Uint8List body
)

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,
  );
}