main function
void
main()
Implementation
void main() {
// ==========================================================
// 1️⃣ Parse ClientHello (already validated earlier)
// ==========================================================
final clientHelloWire = Uint8List.fromList(
HEX.decode(
"01 00 00 ea 03 03 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 00 00 06 13 01 13 02 13 03 01 00 00 bb 00 00 00 18 00 16 00 00 13 65 78 61 6d 70 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e 65 74 00 0a 00 08 00 06 00 1d 00 17 00 18 00 10 00 0b 00 09 08 70 69 6e 67 2f 31 2e 30 00 0d 00 14 00 12 04 03 08 04 04 01 05 03 08 05 05 01 08 06 06 01 02 01 00 33 00 26 00 24 00 1d 00 20 35 80 72 d6 36 58 80 d1 ae ea 32 9a df 91 21 38 38 51 ed 21 a2 8e 3b 75 e9 65 d0 d2 cd 16 62 54 00 2d 00 02 01 01 00 2b 00 03 02 03 04 00 39 00 31 03 04 80 00 ff f7 04 04 80 a0 00 00 05 04 80 10 00 00 06 04 80 10 00 00 07 04 80 10 00 00 08 01 0a 09 01 0a 0a 01 03 0b 01 19 0f 05 63 5f 63 69 64"
.replaceAll(" ", ""),
),
);
final clientHello = ClientHello.parse_tls_client_hello(
clientHelloWire.sublist(4),
);
final keyShare = clientHello.keyShares!.firstWhere(
(ks) => ks.group == 0x001d,
orElse: () => throw StateError("Missing X25519 key share"),
);
// ==========================================================
// 2️⃣ Create fixed server crypto (ONE TIME)
// ==========================================================
final serverKeyPair = KeyPair.generate();
final serverRandom = Uint8List(32);
for (int i = 0; i < 32; i++) {
serverRandom[i] = i;
}
// ==========================================================
// 3️⃣ Build initial ServerHello ONCE
// ==========================================================
Uint8List current = buildServerHello(
serverRandom: serverRandom,
publicKey: serverKeyPair.publicKeyBytes,
sessionId: Uint8List(0),
cipherSuite: 0x1301,
group: keyShare.group,
);
print("✅ Initial ServerHello length = ${current.length}");
const iterations = 10;
for (int i = 0; i < iterations; i++) {
// --------------------------------------------------
// 4️⃣ Parse ServerHello (skip handshake header)
// --------------------------------------------------
if (current.length < 4 || current[0] != 0x02) {
throw StateError("Iteration $i: not a ServerHello handshake");
}
final buf = QuicBuffer(data: current.sublist(4));
final parsed = ServerHello.parse(buf);
// --------------------------------------------------
// 5️⃣ Validate parsed ServerHello
// --------------------------------------------------
if (parsed.legacyVersion != 0x0303) {
throw StateError("Iteration $i: invalid legacy_version");
}
if (parsed.cipherSuite != 0x1301) {
throw StateError("Iteration $i: cipher_suite mismatch");
}
if (parsed.keyShareEntry == null || parsed.keyShareEntry!.group != 0x001d) {
throw StateError("Iteration $i: key_share mismatch");
}
// --------------------------------------------------
// 6️⃣ Rebuild ServerHello from parsed fields
// --------------------------------------------------
final rebuilt = buildServerHello(
serverRandom: parsed.random,
publicKey: parsed.keyShareEntry!.pub,
sessionId: parsed.sessionId,
cipherSuite: parsed.cipherSuite,
group: parsed.keyShareEntry!.group,
);
// --------------------------------------------------
// 7️⃣ Byte‑for‑byte compare
// --------------------------------------------------
if (rebuilt.length != current.length) {
throw StateError(
"Iteration $i: length mismatch "
"${rebuilt.length} != ${current.length}",
);
}
for (int j = 0; j < rebuilt.length; j++) {
if (rebuilt[j] != current[j]) {
throw StateError(
"Iteration $i: byte mismatch at offset $j "
"(0x${current[j].toRadixString(16)} != "
"0x${rebuilt[j].toRadixString(16)})",
);
}
}
print("✅ Iteration $i OK");
current = rebuilt;
}
print("✅ ServerHello stable after $iterations parse ⇄ build cycles");
}