uTLS—Fork of the Go standard TLS library, providing low-level access to the ClientHello for mimicry purposes

uTLS is a fork of “crypto/tls”, which provides ClientHello fingerprinting resistance, low-level access to handshake, fake session tickets and some other features. Handshake is still performed by “crypto/tls”, this library merely changes ClientHello part of it and provides low-level access.


Low-level access to handshake

  • Read/write access to all bits of client hello message.
  • Read access to fields of ClientHandshakeState, which, among other things, includes ServerHello and MasterSecret.
  • Read keystream. Can be used, for example, to “write” something in ciphertext.

ClientHello fingerprinting resistance

Golang’s ClientHello has a very unique fingerprint, which especially sticks out on mobile clients, where Golang is not too popular yet. Some members of anti-censorship community are concerned that their tools could be trivially blocked based on ClientHello with relatively small collateral damage. There are multiple solutions to this issue.

Randomized Fingerprint

Randomized Fingerprints are supposedly good at defeating blacklists, since those fingerprints have random ciphersuites and extensions in random order. Note that all used ciphersuites and extensions are fully supported by uTLS, which provides a solid moving target without any compatibility or parrot-is-dead attack risks.

Fake Session Tickets

Fake session tickets is a very nifty trick that allows power users to hide parts of handshake, which may have some very fingerprintable features of handshake, and saves 1 RTT.

The author, @_sf, told me that uTLS allows very low-level ClientHello access, so it could be used for censorship circumvention is some cases.

So, uTLS has a bunch of built-in mimicry ClientHelloSpec’s, for example, HelloChrome_62, which includes all the information that uTLS needs to marshal a Chrome-like ClientHello, such as ciphers and extensions. Let’s say you want to send a huge ClientHello by including an extension with ID someID and a 16KB body. You can copy-paste the generation function for that spec or any other ClientHelloSpec (uTLS also supports generation of randomized ClientHellos), then add your utls.GenericExtension{Id: someID, Data: make([]byte, 1024*16)} to a desired place in the ordered list of ClientHelloSpec.Extensions, and apply the resulting spec with uconn.ApplyPreset() function.

The “fake SNI in the beginning and real SNI before/after padding” may require a roundabout implementation: I try to prevent users from shooting themselves in the foot, and including 2 SNI extensions with different values is precisely one of the things I wouldn’t want to happen accidentally. However, I believe you still should be able to do that: just include second SNI extension as utls.GenericExtension{}, so uTLS doesn’t recognize the the type of the second SNI extension, and simply marshals it as-is.