clienthellod, read as "client-hello-D", is a TLS ClientHello/QUIC Initial Packet reflection service. It can be used to parses TLS ClientHello messages and QUIC Initial Packets into human-readable and highly programmable formats such as JSON.
Is is a part of the TLS fingerprintability research project which spans tlsfingerprint.io and quic.tlsfingerprint.io. It parses the ClientHello messages sent by TLS clients and QUIC Client Initial Packets sent by QUIC clients and display the parsed information in a human-readable format with high programmability.
See tlsfingerprint.io and quic.tlsfingerprint.io for more details about the project.
clienthellod comes as a Go library, which can be used to parse both TLS and QUIC protocols.
tlsFingerprinter:=clienthellod.NewTLSFingerprinter()quicFingerprinter:=clienthellod.NewQUICFingerprinter()tcpLis, err:=net.Listen("tcp", ":443")
defertcpLis.Close()
conn, err:=tcpLis.Accept()
iferr!=nil {
panic(err)
}
deferconn.Close()
ch, err:=clienthellod.ReadClientHello(conn) // reads ClientHello from the connectioniferr!=nil {
panic(err)
}
err:=ch.ParseClientHello() // parses ClientHello's fieldsiferr!=nil {
panic(err)
}
jsonB, err=json.MarshalIndent(ch, "", " ")
iferr!=nil {
panic(err)
}
fmt.Println(string(jsonB))
fmt.Println("ClientHello ID: "+ch.HexID) // prints ClientHello's original fingerprint ID calculated using observed TLS extension orderfmt.Println("ClientHello NormID: "+ch.NormHexID) // prints ClientHello's normalized fingerprint ID calculated using sorted TLS extension listch, err:=clienthellod.UnmarshalClientHello(raw)
iferr!=nil {
panic(err)
}
// err := ch.ParseClientHello() // no need to call again, UnmarshalClientHello automatically calls ParseClientHelloudpConn, err:=net.ListenUDP("udp", ":443")
deferudpConn.Close()
buf:=make([]byte, 65535)
n, addr, err:=udpConn.ReadFromUDP(buf)
iferr!=nil {
panic(err)
}
ci, err:=clienthellod.UnmarshalQUICClientInitialPacket(buf[:n]) // decodes QUIC Client Initial Packetiferr!=nil {
panic(err) }
jsonB, err=json.MarshalIndent(cip, "", " ")
iferr!=nil {
panic(err)
}
fmt.Println(string(jsonB)) // including fingerprint IDs of: ClientInitialPacket, QUIC Header, QUIC ClientHello, QUIC Transport Parameters' combinationImplementations including Chrome/Chromium sends oversized Client Hello which does not fit into one single QUIC packet, in which case multiple QUIC Initial Packets are sent.
gci:=GatherClientInitials() // Each GatherClientInitials reassembles one QUIC Client Initial Packets stream. Use a QUIC Fingerprinter for multiple potential senders, which automatically demultiplexes the packets based on the source address.udpConn, err:=net.ListenUDP("udp", ":443")
deferudpConn.Close()
for {
buf:=make([]byte, 65535)
n, addr, err:=udpConn.ReadFromUDP(buf)
iferr!=nil {
panic(err)
}
ifaddr!=knownSenderAddr {
continue
}
ci, err:=clienthellod.UnmarshalQUICClientInitialPacket(buf[:n]) // decodes QUIC Client Initial Packetiferr!=nil {
panic(err) }
err=gci.AddPacket(ci)
iferr!=nil {
panic(err)
}
}We also provide clienthellod as a Caddy Module in modcaddy, which you can use with Caddy to capture ClientHello messages and QUIC Client Initial Packets. See modcaddy for more details.
This project is developed and distributed under Apache-2.0 license.