Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

UTLS (Fork, modified by Kirk)

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.

Minimum Go Version: Go 1.21

If you have any questions, bug reports or contributions, you are welcome to publish those on GitHub. If you want to do so in private, you can contact one of developers personally via sergey.frolov@colorado.edu.

You can contact one of developers personally via gaukas.wang@colorado.edu.

Documentation below may not keep up with all the changes and new features at all times, so you are encouraged to use godoc.

Note: Information provided below in this README.md could be obsolete. We welcome any contributions to refresh the documentations in addition to code contributions.

Features

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.

It is highly recommended to use multiple fingeprints, including randomized ones to avoid relying on a single fingerprint.utls.Roller does this automatically.

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.

But note that there's a small chance that generated fingerprint won't work, so you may want to keep generating until a working one is found, and then keep reusing the working fingerprint to avoid suspicious behavior of constantly changing fingerprints. utls.Roller reuses working fingerprint automatically.

Generating randomized fingerprints

To generate a randomized fingerprint, simply do:

uTlsConn:=tls.UClient(tcpConn, &config, tls.HelloRandomized)

you can use helloRandomizedALPN or helloRandomizedNoALPN to ensure presence or absence of ALPN(Application-Layer Protocol Negotiation) extension. It is recommended, but certainly not required to include ALPN (or use helloRandomized which may or may not include ALPN). If you do use ALPN, you will want to correctly handle potential application layer protocols (likely h2 or http/1.1).

Reusing randomized fingerprint

// oldConn is an old connection that worked before, so we want to reuse it// newConn is a new connection we'd like to establishnewConn:=tls.UClient(tcpConn, &config, oldConn.ClientHelloID)

Parroting

This package can be used to parrot ClientHello of popular browsers. There are some caveats to this parroting:

  • We are forced to offer ciphersuites and tls extensions that are not supported by crypto/tls. This is not a problem, if you fully control the server and turn unsupported things off on server side.
  • Parroting could be imperfect, and there is no parroting beyond ClientHello.

Compatibility risks of available parrots

ParrotCiphers*Signature*Unsupported extensionsTLS Fingerprint ID
Chrome 62nonoChannelID0a4a74aeebd1bb66
Chrome 70nonoChannelID, Encrypted Certsbc4c7e42f4961cd7
Chrome 72nonoChannelID, Encrypted Certsbbf04e5f1881f506
Chrome 83nonoChannelID, Encrypted Certs9c673fd64a32c8dc
Firefox 56very lownoNonec884bad7f40bee56
Firefox 65very lownoMaxRecordSize6bfedc5d5c740d58
iOS 11.1low**noNone71a81bafd58e1301
iOS 12.1low**noNoneec55e5b4136c7949

* Denotes very rough guesstimate of likelihood that unsupported things will get echoed back by the server in the wild, visibly breaking the connection.
** No risk, if utls.EnableWeakCiphers() is called prior to using it.

Parrots FAQ

Does it really look like, say, Google Chrome with all the GREASE and stuff?

It LGTM, but please open up Wireshark and check. If you see something — say something.

Aren't there side channels? Everybody knows that the bird is a wordparrot is dead

There sure are. If you found one that approaches practicality at line speed — please tell us.

However, there is a difference between this sort of parroting and techniques like SkypeMorth. Namely, TLS is highly standardized protocol, therefore simply not that many subtle things in TLS protocol could be different and/or suddenly change in one of mimicked implementation(potentially undermining the mimicry). It is possible that we have a distinguisher right now, but amount of those potential distinguishers is limited.

Custom Handshake

It is possible to create custom handshake by

  1. Use HelloCustom as an argument for UClient() to get empty config
  2. Fill tls header fields: UConn.Hello.{Random, CipherSuites, CompressionMethods}, if needed, or stick to defaults.
  3. Configure and add various TLS Extensions to UConn.Extensions: they will be marshaled in order.
  4. Set Session and SessionCache, as needed.

If you need to manually control all the bytes on the wire(certainly not recommended!), you can set UConn.HandshakeStateBuilt = true, and marshal clientHello into UConn.HandshakeState.Hello.raw yourself. In this case you will be responsible for modifying other parts of Config and ClientHelloMsg to reflect your setup and not confuse "crypto/tls", which will be processing response from server.

Fingerprinting Captured Client Hello

You can use a captured client hello to generate new ones that mimic/have the same properties as the original. The generated client hellos should look like they were generated from the same client software as the original fingerprinted bytes. In order to do this:

  1. Create a ClientHelloSpec from the raw bytes of the original client hello
  2. Use HelloCustom as an argument for UClient() to get empty config
  3. Use ApplyPreset with the generated ClientHelloSpec to set the appropriate connection properties
uConn := UClient(&net.TCPConn{}, nil, HelloCustom)
fingerprinter := &Fingerprinter{}
generatedSpec, err := fingerprinter.FingerprintClientHello(rawCapturedClientHelloBytes)
if err != nil {
panic("fingerprinting failed: %v", err)
}
if err := uConn.ApplyPreset(generatedSpec); err != nil {
panic("applying generated spec failed: %v", err)
}

The rawCapturedClientHelloBytes should be the full tls record, including the record type/version/length header.

Roller

A simple wrapper, that allows to easily use multiple latest(auto-updated) fingerprints.

// NewRoller creates Roller object with default range of HelloIDs to cycle// through until a working/unblocked one is found.funcNewRoller() (*Roller, error)
// Dial attempts to connect to given address using different HelloIDs.// If a working HelloID is found, it is used again for subsequent Dials.// If tcp connection fails or all HelloIDs are tried, returns with last error.//// Usage examples://// Dial("tcp4", "google.com:443", "google.com")// Dial("tcp", "10.23.144.22:443", "mywebserver.org")func (c*Roller) Dial(network, addr, serverNamestring) (*UConn, error)

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. Currently, there is a simple function to set session ticket to any desired state:

// If you want you session tickets to be reused - use same cache on following connectionsfunc (uconn*UConn) SetSessionState(session*ClientSessionState)

Note that session tickets (fake ones or otherwise) are not reused.
To reuse tickets, create a shared cache and set it on current and further configs:

// If you want you session tickets to be reused - use same cache on following connectionsfunc (uconn*UConn) SetSessionCache(cacheClientSessionCache)

Custom TLS extensions

If you want to add your own fake (placeholder, without added functionality) extension for mimicry purposes, you can embed *tls.GenericExtension into your own struct and override Len() and Read() methods. For example, DelegatedCredentials extension can be implemented as follows:

constFakeDelegatedCredentialsuint16=0x0022typeFakeDelegatedCredentialsExtensionstruct {
*tls.GenericExtensionSignatureAlgorithms []tls.SignatureScheme
}
func (e*FakeDelegatedCredentialsExtension) Len() int {
return6+2*len(e.SignatureAlgorithms)
}
func (e*FakeDelegatedCredentialsExtension) Read(b []byte) (nint, errerror) {
iflen(b) <e.Len() {
return0, io.ErrShortBuffer
}
offset:=0appendUint16:=func(valuint16) {
b[offset] =byte(val>>8)
b[offset+1] =byte(val&0xff)
offset+=2
}
// Extension typeappendUint16(FakeDelegatedCredentials)
algosLength:=2*len(e.SignatureAlgorithms)
// Extension data lengthappendUint16(uint16(algosLength) +2)
// Algorithms list lengthappendUint16(uint16(algosLength))
// Algorithms listfor_, a:=rangee.SignatureAlgorithms {
appendUint16(uint16(a))
}
returne.Len(), io.EOF
}

Then it can be used just like normal extension:

&tls.ClientHelloSpec{
//...Extensions: []tls.TLSExtension{
//...&FakeDelegatedCredentialsExtension{
SignatureAlgorithms: []tls.SignatureScheme{
tls.ECDSAWithP256AndSHA256,
tls.ECDSAWithP384AndSHA384,
tls.ECDSAWithP521AndSHA512,
tls.ECDSAWithSHA1,
},
},
//...
}
//...
}

Client Hello IDs

See full list of clientHelloID values here.
There are different behaviors you can get, depending on your clientHelloID:

  1. utls.HelloRandomized adds/reorders extensions, ciphersuites, etc. randomly.
    HelloRandomized adds ALPN in a percentage of cases, you may want to use HelloRandomizedALPN or HelloRandomizedNoALPN to choose specific behavior explicitly, as ALPN might affect application layer.
  2. utls.HelloGolang HelloGolang will use default "crypto/tls" handshake marshaling codepath, which WILL overwrite your changes to Hello(Config, Session are fine). You might want to call BuildHandshakeState() before applying any changes. UConn.Extensions will be completely ignored.
  3. utls.HelloCustom will prepare ClientHello with empty uconn.Extensions so you can fill it with TLSExtension's manually.
  4. The rest will will parrot given browser. Such parrots include, for example:
    • utls.HelloChrome_Auto- parrots recommended(usually latest) Google Chrome version
    • utls.HelloChrome_58 - parrots Google Chrome 58
    • utls.HelloFirefox_Auto - parrots recommended(usually latest) Firefox version
    • utls.HelloFirefox_55 - parrots Firefox 55

Usage

Examples

Find basic examples here.
Here's a more advanced example showing how to generate randomized ClientHello, modify generated ciphersuites a bit, and proceed with the handshake.

Migrating from "crypto/tls"

Here's how default "crypto/tls" is typically used:

dialConn, err:=net.Dial("tcp", "172.217.11.46:443")
iferr!=nil {
fmt.Printf("net.Dial() failed: %+v\n", err)
return
}
config:= tls.Config{ServerName: "www.google.com"}
tlsConn:=tls.Client(dialConn, &config)
n, err=tlsConn.Write("Hello, World!")
//...

To start using using uTLS:

  1. Import this library (e.g. import tls "github.com/SearchPilot/utls")
  2. Pick the Client Hello ID
  3. Simply substitute tlsConn := tls.Client(dialConn, &config) with tlsConn := tls.UClient(dialConn, &config, tls.clientHelloID)

Customizing handshake

Some customizations(such as setting session ticket/clientHello) have easy-to-use functions for them. The idea is to make common manipulations easy:

cRandom:= []byte{100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
130, 131}
tlsConn.SetClientRandom(cRandom)
masterSecret:=make([]byte, 48)
copy(masterSecret, []byte("masterSecret is NOT sent over the wire")) // you may use it for real security// Create a session ticket that wasn't actually issued by the server.sessionState:=utls.MakeClientSessionState(sessionTicket, uint16(tls.VersionTLS12),
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
masterSecret,
nil, nil)
tlsConn.SetSessionState(sessionState)

For other customizations there are following functions

// you can use this to build the state manually and change it
// for example use Randomized ClientHello, and add more extensions
func (uconn *UConn) BuildHandshakeState() error
// Then apply the changes and marshal final bytes, which will be sent
func (uconn *UConn) MarshalClientHello() error

Contributors' guide

Please refer to this document if you're interested in internals

Credits

The initial development of uTLS was completed during an internship at Google Jigsaw. This is not an official Google product.

About

No description, website, or topics provided.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
GitHub - SearchPilot/utls · GitHub
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

UTLS (Fork, modified by Kirk)

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.

Minimum Go Version: Go 1.21

If you have any questions, bug reports or contributions, you are welcome to publish those on GitHub. If you want to do so in private, you can contact one of developers personally via sergey.frolov@colorado.edu.

You can contact one of developers personally via gaukas.wang@colorado.edu.

Documentation below may not keep up with all the changes and new features at all times, so you are encouraged to use godoc.

Note: Information provided below in this README.md could be obsolete. We welcome any contributions to refresh the documentations in addition to code contributions.

Features

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.

It is highly recommended to use multiple fingeprints, including randomized ones to avoid relying on a single fingerprint.utls.Roller does this automatically.

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.

But note that there's a small chance that generated fingerprint won't work, so you may want to keep generating until a working one is found, and then keep reusing the working fingerprint to avoid suspicious behavior of constantly changing fingerprints. utls.Roller reuses working fingerprint automatically.

Generating randomized fingerprints

To generate a randomized fingerprint, simply do:

uTlsConn:=tls.UClient(tcpConn, &config, tls.HelloRandomized)

you can use helloRandomizedALPN or helloRandomizedNoALPN to ensure presence or absence of ALPN(Application-Layer Protocol Negotiation) extension. It is recommended, but certainly not required to include ALPN (or use helloRandomized which may or may not include ALPN). If you do use ALPN, you will want to correctly handle potential application layer protocols (likely h2 or http/1.1).

Reusing randomized fingerprint

// oldConn is an old connection that worked before, so we want to reuse it// newConn is a new connection we'd like to establishnewConn:=tls.UClient(tcpConn, &config, oldConn.ClientHelloID)

Parroting

This package can be used to parrot ClientHello of popular browsers. There are some caveats to this parroting:

  • We are forced to offer ciphersuites and tls extensions that are not supported by crypto/tls. This is not a problem, if you fully control the server and turn unsupported things off on server side.
  • Parroting could be imperfect, and there is no parroting beyond ClientHello.

Compatibility risks of available parrots

ParrotCiphers*Signature*Unsupported extensionsTLS Fingerprint ID
Chrome 62nonoChannelID0a4a74aeebd1bb66
Chrome 70nonoChannelID, Encrypted Certsbc4c7e42f4961cd7
Chrome 72nonoChannelID, Encrypted Certsbbf04e5f1881f506
Chrome 83nonoChannelID, Encrypted Certs9c673fd64a32c8dc
Firefox 56very lownoNonec884bad7f40bee56
Firefox 65very lownoMaxRecordSize6bfedc5d5c740d58
iOS 11.1low**noNone71a81bafd58e1301
iOS 12.1low**noNoneec55e5b4136c7949

* Denotes very rough guesstimate of likelihood that unsupported things will get echoed back by the server in the wild, visibly breaking the connection.
** No risk, if utls.EnableWeakCiphers() is called prior to using it.

Parrots FAQ

Does it really look like, say, Google Chrome with all the GREASE and stuff?

It LGTM, but please open up Wireshark and check. If you see something — say something.

Aren't there side channels? Everybody knows that the bird is a wordparrot is dead

There sure are. If you found one that approaches practicality at line speed — please tell us.

However, there is a difference between this sort of parroting and techniques like SkypeMorth. Namely, TLS is highly standardized protocol, therefore simply not that many subtle things in TLS protocol could be different and/or suddenly change in one of mimicked implementation(potentially undermining the mimicry). It is possible that we have a distinguisher right now, but amount of those potential distinguishers is limited.

Custom Handshake

It is possible to create custom handshake by

  1. Use HelloCustom as an argument for UClient() to get empty config
  2. Fill tls header fields: UConn.Hello.{Random, CipherSuites, CompressionMethods}, if needed, or stick to defaults.
  3. Configure and add various TLS Extensions to UConn.Extensions: they will be marshaled in order.
  4. Set Session and SessionCache, as needed.

If you need to manually control all the bytes on the wire(certainly not recommended!), you can set UConn.HandshakeStateBuilt = true, and marshal clientHello into UConn.HandshakeState.Hello.raw yourself. In this case you will be responsible for modifying other parts of Config and ClientHelloMsg to reflect your setup and not confuse "crypto/tls", which will be processing response from server.

Fingerprinting Captured Client Hello

You can use a captured client hello to generate new ones that mimic/have the same properties as the original. The generated client hellos should look like they were generated from the same client software as the original fingerprinted bytes. In order to do this:

  1. Create a ClientHelloSpec from the raw bytes of the original client hello
  2. Use HelloCustom as an argument for UClient() to get empty config
  3. Use ApplyPreset with the generated ClientHelloSpec to set the appropriate connection properties
uConn := UClient(&net.TCPConn{}, nil, HelloCustom)
fingerprinter := &Fingerprinter{}
generatedSpec, err := fingerprinter.FingerprintClientHello(rawCapturedClientHelloBytes)
if err != nil {
panic("fingerprinting failed: %v", err)
}
if err := uConn.ApplyPreset(generatedSpec); err != nil {
panic("applying generated spec failed: %v", err)
}

The rawCapturedClientHelloBytes should be the full tls record, including the record type/version/length header.

Roller

A simple wrapper, that allows to easily use multiple latest(auto-updated) fingerprints.

// NewRoller creates Roller object with default range of HelloIDs to cycle// through until a working/unblocked one is found.funcNewRoller() (*Roller, error)
// Dial attempts to connect to given address using different HelloIDs.// If a working HelloID is found, it is used again for subsequent Dials.// If tcp connection fails or all HelloIDs are tried, returns with last error.//// Usage examples://// Dial("tcp4", "google.com:443", "google.com")// Dial("tcp", "10.23.144.22:443", "mywebserver.org")func (c*Roller) Dial(network, addr, serverNamestring) (*UConn, error)

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. Currently, there is a simple function to set session ticket to any desired state:

// If you want you session tickets to be reused - use same cache on following connectionsfunc (uconn*UConn) SetSessionState(session*ClientSessionState)

Note that session tickets (fake ones or otherwise) are not reused.
To reuse tickets, create a shared cache and set it on current and further configs:

// If you want you session tickets to be reused - use same cache on following connectionsfunc (uconn*UConn) SetSessionCache(cacheClientSessionCache)

Custom TLS extensions

If you want to add your own fake (placeholder, without added functionality) extension for mimicry purposes, you can embed *tls.GenericExtension into your own struct and override Len() and Read() methods. For example, DelegatedCredentials extension can be implemented as follows:

constFakeDelegatedCredentialsuint16=0x0022typeFakeDelegatedCredentialsExtensionstruct {
*tls.GenericExtensionSignatureAlgorithms []tls.SignatureScheme
}
func (e*FakeDelegatedCredentialsExtension) Len() int {
return6+2*len(e.SignatureAlgorithms)
}
func (e*FakeDelegatedCredentialsExtension) Read(b []byte) (nint, errerror) {
iflen(b) <e.Len() {
return0, io.ErrShortBuffer
}
offset:=0appendUint16:=func(valuint16) {
b[offset] =byte(val>>8)
b[offset+1] =byte(val&0xff)
offset+=2
}
// Extension typeappendUint16(FakeDelegatedCredentials)
algosLength:=2*len(e.SignatureAlgorithms)
// Extension data lengthappendUint16(uint16(algosLength) +2)
// Algorithms list lengthappendUint16(uint16(algosLength))
// Algorithms listfor_, a:=rangee.SignatureAlgorithms {
appendUint16(uint16(a))
}
returne.Len(), io.EOF
}

Then it can be used just like normal extension:

&tls.ClientHelloSpec{
//...Extensions: []tls.TLSExtension{
//...&FakeDelegatedCredentialsExtension{
SignatureAlgorithms: []tls.SignatureScheme{
tls.ECDSAWithP256AndSHA256,
tls.ECDSAWithP384AndSHA384,
tls.ECDSAWithP521AndSHA512,
tls.ECDSAWithSHA1,
},
},
//...
}
//...
}

Client Hello IDs

See full list of clientHelloID values here.
There are different behaviors you can get, depending on your clientHelloID:

  1. utls.HelloRandomized adds/reorders extensions, ciphersuites, etc. randomly.
    HelloRandomized adds ALPN in a percentage of cases, you may want to use HelloRandomizedALPN or HelloRandomizedNoALPN to choose specific behavior explicitly, as ALPN might affect application layer.
  2. utls.HelloGolang HelloGolang will use default "crypto/tls" handshake marshaling codepath, which WILL overwrite your changes to Hello(Config, Session are fine). You might want to call BuildHandshakeState() before applying any changes. UConn.Extensions will be completely ignored.
  3. utls.HelloCustom will prepare ClientHello with empty uconn.Extensions so you can fill it with TLSExtension's manually.
  4. The rest will will parrot given browser. Such parrots include, for example:
    • utls.HelloChrome_Auto- parrots recommended(usually latest) Google Chrome version
    • utls.HelloChrome_58 - parrots Google Chrome 58
    • utls.HelloFirefox_Auto - parrots recommended(usually latest) Firefox version
    • utls.HelloFirefox_55 - parrots Firefox 55

Usage

Examples

Find basic examples here.
Here's a more advanced example showing how to generate randomized ClientHello, modify generated ciphersuites a bit, and proceed with the handshake.

Migrating from "crypto/tls"

Here's how default "crypto/tls" is typically used:

dialConn, err:=net.Dial("tcp", "172.217.11.46:443")
iferr!=nil {
fmt.Printf("net.Dial() failed: %+v\n", err)
return
}
config:= tls.Config{ServerName: "www.google.com"}
tlsConn:=tls.Client(dialConn, &config)
n, err=tlsConn.Write("Hello, World!")
//...

To start using using uTLS:

  1. Import this library (e.g. import tls "github.com/SearchPilot/utls")
  2. Pick the Client Hello ID
  3. Simply substitute tlsConn := tls.Client(dialConn, &config) with tlsConn := tls.UClient(dialConn, &config, tls.clientHelloID)

Customizing handshake

Some customizations(such as setting session ticket/clientHello) have easy-to-use functions for them. The idea is to make common manipulations easy:

cRandom:= []byte{100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
130, 131}
tlsConn.SetClientRandom(cRandom)
masterSecret:=make([]byte, 48)
copy(masterSecret, []byte("masterSecret is NOT sent over the wire")) // you may use it for real security// Create a session ticket that wasn't actually issued by the server.sessionState:=utls.MakeClientSessionState(sessionTicket, uint16(tls.VersionTLS12),
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
masterSecret,
nil, nil)
tlsConn.SetSessionState(sessionState)

For other customizations there are following functions

// you can use this to build the state manually and change it
// for example use Randomized ClientHello, and add more extensions
func (uconn *UConn) BuildHandshakeState() error
// Then apply the changes and marshal final bytes, which will be sent
func (uconn *UConn) MarshalClientHello() error

Contributors' guide

Please refer to this document if you're interested in internals

Credits

The initial development of uTLS was completed during an internship at Google Jigsaw. This is not an official Google product.

About

No description, website, or topics provided.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - SearchPilot/utls · GitHub
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

UTLS (Fork, modified by Kirk)

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.

Minimum Go Version: Go 1.21

If you have any questions, bug reports or contributions, you are welcome to publish those on GitHub. If you want to do so in private, you can contact one of developers personally via sergey.frolov@colorado.edu.

You can contact one of developers personally via gaukas.wang@colorado.edu.

Documentation below may not keep up with all the changes and new features at all times, so you are encouraged to use godoc.

Note: Information provided below in this README.md could be obsolete. We welcome any contributions to refresh the documentations in addition to code contributions.

Features

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.

It is highly recommended to use multiple fingeprints, including randomized ones to avoid relying on a single fingerprint.utls.Roller does this automatically.

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.

But note that there's a small chance that generated fingerprint won't work, so you may want to keep generating until a working one is found, and then keep reusing the working fingerprint to avoid suspicious behavior of constantly changing fingerprints. utls.Roller reuses working fingerprint automatically.

Generating randomized fingerprints

To generate a randomized fingerprint, simply do:

uTlsConn:=tls.UClient(tcpConn, &config, tls.HelloRandomized)

you can use helloRandomizedALPN or helloRandomizedNoALPN to ensure presence or absence of ALPN(Application-Layer Protocol Negotiation) extension. It is recommended, but certainly not required to include ALPN (or use helloRandomized which may or may not include ALPN). If you do use ALPN, you will want to correctly handle potential application layer protocols (likely h2 or http/1.1).

Reusing randomized fingerprint

// oldConn is an old connection that worked before, so we want to reuse it// newConn is a new connection we'd like to establishnewConn:=tls.UClient(tcpConn, &config, oldConn.ClientHelloID)

Parroting

This package can be used to parrot ClientHello of popular browsers. There are some caveats to this parroting:

  • We are forced to offer ciphersuites and tls extensions that are not supported by crypto/tls. This is not a problem, if you fully control the server and turn unsupported things off on server side.
  • Parroting could be imperfect, and there is no parroting beyond ClientHello.

Compatibility risks of available parrots

ParrotCiphers*Signature*Unsupported extensionsTLS Fingerprint ID
Chrome 62nonoChannelID0a4a74aeebd1bb66
Chrome 70nonoChannelID, Encrypted Certsbc4c7e42f4961cd7
Chrome 72nonoChannelID, Encrypted Certsbbf04e5f1881f506
Chrome 83nonoChannelID, Encrypted Certs9c673fd64a32c8dc
Firefox 56very lownoNonec884bad7f40bee56
Firefox 65very lownoMaxRecordSize6bfedc5d5c740d58
iOS 11.1low**noNone71a81bafd58e1301
iOS 12.1low**noNoneec55e5b4136c7949

* Denotes very rough guesstimate of likelihood that unsupported things will get echoed back by the server in the wild, visibly breaking the connection.
** No risk, if utls.EnableWeakCiphers() is called prior to using it.

Parrots FAQ

Does it really look like, say, Google Chrome with all the GREASE and stuff?

It LGTM, but please open up Wireshark and check. If you see something — say something.

Aren't there side channels? Everybody knows that the bird is a wordparrot is dead

There sure are. If you found one that approaches practicality at line speed — please tell us.

However, there is a difference between this sort of parroting and techniques like SkypeMorth. Namely, TLS is highly standardized protocol, therefore simply not that many subtle things in TLS protocol could be different and/or suddenly change in one of mimicked implementation(potentially undermining the mimicry). It is possible that we have a distinguisher right now, but amount of those potential distinguishers is limited.

Custom Handshake

It is possible to create custom handshake by

  1. Use HelloCustom as an argument for UClient() to get empty config
  2. Fill tls header fields: UConn.Hello.{Random, CipherSuites, CompressionMethods}, if needed, or stick to defaults.
  3. Configure and add various TLS Extensions to UConn.Extensions: they will be marshaled in order.
  4. Set Session and SessionCache, as needed.

If you need to manually control all the bytes on the wire(certainly not recommended!), you can set UConn.HandshakeStateBuilt = true, and marshal clientHello into UConn.HandshakeState.Hello.raw yourself. In this case you will be responsible for modifying other parts of Config and ClientHelloMsg to reflect your setup and not confuse "crypto/tls", which will be processing response from server.

Fingerprinting Captured Client Hello

You can use a captured client hello to generate new ones that mimic/have the same properties as the original. The generated client hellos should look like they were generated from the same client software as the original fingerprinted bytes. In order to do this:

  1. Create a ClientHelloSpec from the raw bytes of the original client hello
  2. Use HelloCustom as an argument for UClient() to get empty config
  3. Use ApplyPreset with the generated ClientHelloSpec to set the appropriate connection properties
uConn := UClient(&net.TCPConn{}, nil, HelloCustom)
fingerprinter := &Fingerprinter{}
generatedSpec, err := fingerprinter.FingerprintClientHello(rawCapturedClientHelloBytes)
if err != nil {
panic("fingerprinting failed: %v", err)
}
if err := uConn.ApplyPreset(generatedSpec); err != nil {
panic("applying generated spec failed: %v", err)
}

The rawCapturedClientHelloBytes should be the full tls record, including the record type/version/length header.

Roller

A simple wrapper, that allows to easily use multiple latest(auto-updated) fingerprints.

// NewRoller creates Roller object with default range of HelloIDs to cycle// through until a working/unblocked one is found.funcNewRoller() (*Roller, error)
// Dial attempts to connect to given address using different HelloIDs.// If a working HelloID is found, it is used again for subsequent Dials.// If tcp connection fails or all HelloIDs are tried, returns with last error.//// Usage examples://// Dial("tcp4", "google.com:443", "google.com")// Dial("tcp", "10.23.144.22:443", "mywebserver.org")func (c*Roller) Dial(network, addr, serverNamestring) (*UConn, error)

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. Currently, there is a simple function to set session ticket to any desired state:

// If you want you session tickets to be reused - use same cache on following connectionsfunc (uconn*UConn) SetSessionState(session*ClientSessionState)

Note that session tickets (fake ones or otherwise) are not reused.
To reuse tickets, create a shared cache and set it on current and further configs:

// If you want you session tickets to be reused - use same cache on following connectionsfunc (uconn*UConn) SetSessionCache(cacheClientSessionCache)

Custom TLS extensions

If you want to add your own fake (placeholder, without added functionality) extension for mimicry purposes, you can embed *tls.GenericExtension into your own struct and override Len() and Read() methods. For example, DelegatedCredentials extension can be implemented as follows:

constFakeDelegatedCredentialsuint16=0x0022typeFakeDelegatedCredentialsExtensionstruct {
*tls.GenericExtensionSignatureAlgorithms []tls.SignatureScheme
}
func (e*FakeDelegatedCredentialsExtension) Len() int {
return6+2*len(e.SignatureAlgorithms)
}
func (e*FakeDelegatedCredentialsExtension) Read(b []byte) (nint, errerror) {
iflen(b) <e.Len() {
return0, io.ErrShortBuffer
}
offset:=0appendUint16:=func(valuint16) {
b[offset] =byte(val>>8)
b[offset+1] =byte(val&0xff)
offset+=2
}
// Extension typeappendUint16(FakeDelegatedCredentials)
algosLength:=2*len(e.SignatureAlgorithms)
// Extension data lengthappendUint16(uint16(algosLength) +2)
// Algorithms list lengthappendUint16(uint16(algosLength))
// Algorithms listfor_, a:=rangee.SignatureAlgorithms {
appendUint16(uint16(a))
}
returne.Len(), io.EOF
}

Then it can be used just like normal extension:

&tls.ClientHelloSpec{
//...Extensions: []tls.TLSExtension{
//...&FakeDelegatedCredentialsExtension{
SignatureAlgorithms: []tls.SignatureScheme{
tls.ECDSAWithP256AndSHA256,
tls.ECDSAWithP384AndSHA384,
tls.ECDSAWithP521AndSHA512,
tls.ECDSAWithSHA1,
},
},
//...
}
//...
}

Client Hello IDs

See full list of clientHelloID values here.
There are different behaviors you can get, depending on your clientHelloID:

  1. utls.HelloRandomized adds/reorders extensions, ciphersuites, etc. randomly.
    HelloRandomized adds ALPN in a percentage of cases, you may want to use HelloRandomizedALPN or HelloRandomizedNoALPN to choose specific behavior explicitly, as ALPN might affect application layer.
  2. utls.HelloGolang HelloGolang will use default "crypto/tls" handshake marshaling codepath, which WILL overwrite your changes to Hello(Config, Session are fine). You might want to call BuildHandshakeState() before applying any changes. UConn.Extensions will be completely ignored.
  3. utls.HelloCustom will prepare ClientHello with empty uconn.Extensions so you can fill it with TLSExtension's manually.
  4. The rest will will parrot given browser. Such parrots include, for example:
    • utls.HelloChrome_Auto- parrots recommended(usually latest) Google Chrome version
    • utls.HelloChrome_58 - parrots Google Chrome 58
    • utls.HelloFirefox_Auto - parrots recommended(usually latest) Firefox version
    • utls.HelloFirefox_55 - parrots Firefox 55

Usage

Examples

Find basic examples here.
Here's a more advanced example showing how to generate randomized ClientHello, modify generated ciphersuites a bit, and proceed with the handshake.

Migrating from "crypto/tls"

Here's how default "crypto/tls" is typically used:

dialConn, err:=net.Dial("tcp", "172.217.11.46:443")
iferr!=nil {
fmt.Printf("net.Dial() failed: %+v\n", err)
return
}
config:= tls.Config{ServerName: "www.google.com"}
tlsConn:=tls.Client(dialConn, &config)
n, err=tlsConn.Write("Hello, World!")
//...

To start using using uTLS:

  1. Import this library (e.g. import tls "github.com/SearchPilot/utls")
  2. Pick the Client Hello ID
  3. Simply substitute tlsConn := tls.Client(dialConn, &config) with tlsConn := tls.UClient(dialConn, &config, tls.clientHelloID)

Customizing handshake

Some customizations(such as setting session ticket/clientHello) have easy-to-use functions for them. The idea is to make common manipulations easy:

cRandom:= []byte{100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
130, 131}
tlsConn.SetClientRandom(cRandom)
masterSecret:=make([]byte, 48)
copy(masterSecret, []byte("masterSecret is NOT sent over the wire")) // you may use it for real security// Create a session ticket that wasn't actually issued by the server.sessionState:=utls.MakeClientSessionState(sessionTicket, uint16(tls.VersionTLS12),
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
masterSecret,
nil, nil)
tlsConn.SetSessionState(sessionState)

For other customizations there are following functions

// you can use this to build the state manually and change it
// for example use Randomized ClientHello, and add more extensions
func (uconn *UConn) BuildHandshakeState() error
// Then apply the changes and marshal final bytes, which will be sent
func (uconn *UConn) MarshalClientHello() error

Contributors' guide

Please refer to this document if you're interested in internals

Credits

The initial development of uTLS was completed during an internship at Google Jigsaw. This is not an official Google product.

About

No description, website, or topics provided.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - SearchPilot/utls · GitHub
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

UTLS (Fork, modified by Kirk)

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.

Minimum Go Version: Go 1.21

If you have any questions, bug reports or contributions, you are welcome to publish those on GitHub. If you want to do so in private, you can contact one of developers personally via sergey.frolov@colorado.edu.

You can contact one of developers personally via gaukas.wang@colorado.edu.

Documentation below may not keep up with all the changes and new features at all times, so you are encouraged to use godoc.

Note: Information provided below in this README.md could be obsolete. We welcome any contributions to refresh the documentations in addition to code contributions.

Features

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.

It is highly recommended to use multiple fingeprints, including randomized ones to avoid relying on a single fingerprint.utls.Roller does this automatically.

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.

But note that there's a small chance that generated fingerprint won't work, so you may want to keep generating until a working one is found, and then keep reusing the working fingerprint to avoid suspicious behavior of constantly changing fingerprints. utls.Roller reuses working fingerprint automatically.

Generating randomized fingerprints

To generate a randomized fingerprint, simply do:

uTlsConn:=tls.UClient(tcpConn, &config, tls.HelloRandomized)

you can use helloRandomizedALPN or helloRandomizedNoALPN to ensure presence or absence of ALPN(Application-Layer Protocol Negotiation) extension. It is recommended, but certainly not required to include ALPN (or use helloRandomized which may or may not include ALPN). If you do use ALPN, you will want to correctly handle potential application layer protocols (likely h2 or http/1.1).

Reusing randomized fingerprint

// oldConn is an old connection that worked before, so we want to reuse it// newConn is a new connection we'd like to establishnewConn:=tls.UClient(tcpConn, &config, oldConn.ClientHelloID)

Parroting

This package can be used to parrot ClientHello of popular browsers. There are some caveats to this parroting:

  • We are forced to offer ciphersuites and tls extensions that are not supported by crypto/tls. This is not a problem, if you fully control the server and turn unsupported things off on server side.
  • Parroting could be imperfect, and there is no parroting beyond ClientHello.

Compatibility risks of available parrots

ParrotCiphers*Signature*Unsupported extensionsTLS Fingerprint ID
Chrome 62nonoChannelID0a4a74aeebd1bb66
Chrome 70nonoChannelID, Encrypted Certsbc4c7e42f4961cd7
Chrome 72nonoChannelID, Encrypted Certsbbf04e5f1881f506
Chrome 83nonoChannelID, Encrypted Certs9c673fd64a32c8dc
Firefox 56very lownoNonec884bad7f40bee56
Firefox 65very lownoMaxRecordSize6bfedc5d5c740d58
iOS 11.1low**noNone71a81bafd58e1301
iOS 12.1low**noNoneec55e5b4136c7949

* Denotes very rough guesstimate of likelihood that unsupported things will get echoed back by the server in the wild, visibly breaking the connection.
** No risk, if utls.EnableWeakCiphers() is called prior to using it.

Parrots FAQ

Does it really look like, say, Google Chrome with all the GREASE and stuff?

It LGTM, but please open up Wireshark and check. If you see something — say something.

Aren't there side channels? Everybody knows that the bird is a wordparrot is dead

There sure are. If you found one that approaches practicality at line speed — please tell us.

However, there is a difference between this sort of parroting and techniques like SkypeMorth. Namely, TLS is highly standardized protocol, therefore simply not that many subtle things in TLS protocol could be different and/or suddenly change in one of mimicked implementation(potentially undermining the mimicry). It is possible that we have a distinguisher right now, but amount of those potential distinguishers is limited.

Custom Handshake

It is possible to create custom handshake by

  1. Use HelloCustom as an argument for UClient() to get empty config
  2. Fill tls header fields: UConn.Hello.{Random, CipherSuites, CompressionMethods}, if needed, or stick to defaults.
  3. Configure and add various TLS Extensions to UConn.Extensions: they will be marshaled in order.
  4. Set Session and SessionCache, as needed.

If you need to manually control all the bytes on the wire(certainly not recommended!), you can set UConn.HandshakeStateBuilt = true, and marshal clientHello into UConn.HandshakeState.Hello.raw yourself. In this case you will be responsible for modifying other parts of Config and ClientHelloMsg to reflect your setup and not confuse "crypto/tls", which will be processing response from server.

Fingerprinting Captured Client Hello

You can use a captured client hello to generate new ones that mimic/have the same properties as the original. The generated client hellos should look like they were generated from the same client software as the original fingerprinted bytes. In order to do this:

  1. Create a ClientHelloSpec from the raw bytes of the original client hello
  2. Use HelloCustom as an argument for UClient() to get empty config
  3. Use ApplyPreset with the generated ClientHelloSpec to set the appropriate connection properties
uConn := UClient(&net.TCPConn{}, nil, HelloCustom)
fingerprinter := &Fingerprinter{}
generatedSpec, err := fingerprinter.FingerprintClientHello(rawCapturedClientHelloBytes)
if err != nil {
panic("fingerprinting failed: %v", err)
}
if err := uConn.ApplyPreset(generatedSpec); err != nil {
panic("applying generated spec failed: %v", err)
}

The rawCapturedClientHelloBytes should be the full tls record, including the record type/version/length header.

Roller

A simple wrapper, that allows to easily use multiple latest(auto-updated) fingerprints.

// NewRoller creates Roller object with default range of HelloIDs to cycle// through until a working/unblocked one is found.funcNewRoller() (*Roller, error)
// Dial attempts to connect to given address using different HelloIDs.// If a working HelloID is found, it is used again for subsequent Dials.// If tcp connection fails or all HelloIDs are tried, returns with last error.//// Usage examples://// Dial("tcp4", "google.com:443", "google.com")// Dial("tcp", "10.23.144.22:443", "mywebserver.org")func (c*Roller) Dial(network, addr, serverNamestring) (*UConn, error)

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. Currently, there is a simple function to set session ticket to any desired state:

// If you want you session tickets to be reused - use same cache on following connectionsfunc (uconn*UConn) SetSessionState(session*ClientSessionState)

Note that session tickets (fake ones or otherwise) are not reused.
To reuse tickets, create a shared cache and set it on current and further configs:

// If you want you session tickets to be reused - use same cache on following connectionsfunc (uconn*UConn) SetSessionCache(cacheClientSessionCache)

Custom TLS extensions

If you want to add your own fake (placeholder, without added functionality) extension for mimicry purposes, you can embed *tls.GenericExtension into your own struct and override Len() and Read() methods. For example, DelegatedCredentials extension can be implemented as follows:

constFakeDelegatedCredentialsuint16=0x0022typeFakeDelegatedCredentialsExtensionstruct {
*tls.GenericExtensionSignatureAlgorithms []tls.SignatureScheme
}
func (e*FakeDelegatedCredentialsExtension) Len() int {
return6+2*len(e.SignatureAlgorithms)
}
func (e*FakeDelegatedCredentialsExtension) Read(b []byte) (nint, errerror) {
iflen(b) <e.Len() {
return0, io.ErrShortBuffer
}
offset:=0appendUint16:=func(valuint16) {
b[offset] =byte(val>>8)
b[offset+1] =byte(val&0xff)
offset+=2
}
// Extension typeappendUint16(FakeDelegatedCredentials)
algosLength:=2*len(e.SignatureAlgorithms)
// Extension data lengthappendUint16(uint16(algosLength) +2)
// Algorithms list lengthappendUint16(uint16(algosLength))
// Algorithms listfor_, a:=rangee.SignatureAlgorithms {
appendUint16(uint16(a))
}
returne.Len(), io.EOF
}

Then it can be used just like normal extension:

&tls.ClientHelloSpec{
//...Extensions: []tls.TLSExtension{
//...&FakeDelegatedCredentialsExtension{
SignatureAlgorithms: []tls.SignatureScheme{
tls.ECDSAWithP256AndSHA256,
tls.ECDSAWithP384AndSHA384,
tls.ECDSAWithP521AndSHA512,
tls.ECDSAWithSHA1,
},
},
//...
}
//...
}

Client Hello IDs

See full list of clientHelloID values here.
There are different behaviors you can get, depending on your clientHelloID:

  1. utls.HelloRandomized adds/reorders extensions, ciphersuites, etc. randomly.
    HelloRandomized adds ALPN in a percentage of cases, you may want to use HelloRandomizedALPN or HelloRandomizedNoALPN to choose specific behavior explicitly, as ALPN might affect application layer.
  2. utls.HelloGolang HelloGolang will use default "crypto/tls" handshake marshaling codepath, which WILL overwrite your changes to Hello(Config, Session are fine). You might want to call BuildHandshakeState() before applying any changes. UConn.Extensions will be completely ignored.
  3. utls.HelloCustom will prepare ClientHello with empty uconn.Extensions so you can fill it with TLSExtension's manually.
  4. The rest will will parrot given browser. Such parrots include, for example:
    • utls.HelloChrome_Auto- parrots recommended(usually latest) Google Chrome version
    • utls.HelloChrome_58 - parrots Google Chrome 58
    • utls.HelloFirefox_Auto - parrots recommended(usually latest) Firefox version
    • utls.HelloFirefox_55 - parrots Firefox 55

Usage

Examples

Find basic examples here.
Here's a more advanced example showing how to generate randomized ClientHello, modify generated ciphersuites a bit, and proceed with the handshake.

Migrating from "crypto/tls"

Here's how default "crypto/tls" is typically used:

dialConn, err:=net.Dial("tcp", "172.217.11.46:443")
iferr!=nil {
fmt.Printf("net.Dial() failed: %+v\n", err)
return
}
config:= tls.Config{ServerName: "www.google.com"}
tlsConn:=tls.Client(dialConn, &config)
n, err=tlsConn.Write("Hello, World!")
//...

To start using using uTLS:

  1. Import this library (e.g. import tls "github.com/SearchPilot/utls")
  2. Pick the Client Hello ID
  3. Simply substitute tlsConn := tls.Client(dialConn, &config) with tlsConn := tls.UClient(dialConn, &config, tls.clientHelloID)

Customizing handshake

Some customizations(such as setting session ticket/clientHello) have easy-to-use functions for them. The idea is to make common manipulations easy:

cRandom:= []byte{100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
130, 131}
tlsConn.SetClientRandom(cRandom)
masterSecret:=make([]byte, 48)
copy(masterSecret, []byte("masterSecret is NOT sent over the wire")) // you may use it for real security// Create a session ticket that wasn't actually issued by the server.sessionState:=utls.MakeClientSessionState(sessionTicket, uint16(tls.VersionTLS12),
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
masterSecret,
nil, nil)
tlsConn.SetSessionState(sessionState)

For other customizations there are following functions

// you can use this to build the state manually and change it
// for example use Randomized ClientHello, and add more extensions
func (uconn *UConn) BuildHandshakeState() error
// Then apply the changes and marshal final bytes, which will be sent
func (uconn *UConn) MarshalClientHello() error

Contributors' guide

Please refer to this document if you're interested in internals

Credits

The initial development of uTLS was completed during an internship at Google Jigsaw. This is not an official Google product.

About

No description, website, or topics provided.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' GitHub - SearchPilot/utls · GitHub
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

UTLS (Fork, modified by Kirk)

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.

Minimum Go Version: Go 1.21

If you have any questions, bug reports or contributions, you are welcome to publish those on GitHub. If you want to do so in private, you can contact one of developers personally via sergey.frolov@colorado.edu.

You can contact one of developers personally via gaukas.wang@colorado.edu.

Documentation below may not keep up with all the changes and new features at all times, so you are encouraged to use godoc.

Note: Information provided below in this README.md could be obsolete. We welcome any contributions to refresh the documentations in addition to code contributions.

Features

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.

It is highly recommended to use multiple fingeprints, including randomized ones to avoid relying on a single fingerprint.utls.Roller does this automatically.

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.

But note that there's a small chance that generated fingerprint won't work, so you may want to keep generating until a working one is found, and then keep reusing the working fingerprint to avoid suspicious behavior of constantly changing fingerprints. utls.Roller reuses working fingerprint automatically.

Generating randomized fingerprints

To generate a randomized fingerprint, simply do:

uTlsConn:=tls.UClient(tcpConn, &config, tls.HelloRandomized)

you can use helloRandomizedALPN or helloRandomizedNoALPN to ensure presence or absence of ALPN(Application-Layer Protocol Negotiation) extension. It is recommended, but certainly not required to include ALPN (or use helloRandomized which may or may not include ALPN). If you do use ALPN, you will want to correctly handle potential application layer protocols (likely h2 or http/1.1).

Reusing randomized fingerprint

// oldConn is an old connection that worked before, so we want to reuse it// newConn is a new connection we'd like to establishnewConn:=tls.UClient(tcpConn, &config, oldConn.ClientHelloID)

Parroting

This package can be used to parrot ClientHello of popular browsers. There are some caveats to this parroting:

  • We are forced to offer ciphersuites and tls extensions that are not supported by crypto/tls. This is not a problem, if you fully control the server and turn unsupported things off on server side.
  • Parroting could be imperfect, and there is no parroting beyond ClientHello.

Compatibility risks of available parrots

ParrotCiphers*Signature*Unsupported extensionsTLS Fingerprint ID
Chrome 62nonoChannelID0a4a74aeebd1bb66
Chrome 70nonoChannelID, Encrypted Certsbc4c7e42f4961cd7
Chrome 72nonoChannelID, Encrypted Certsbbf04e5f1881f506
Chrome 83nonoChannelID, Encrypted Certs9c673fd64a32c8dc
Firefox 56very lownoNonec884bad7f40bee56
Firefox 65very lownoMaxRecordSize6bfedc5d5c740d58
iOS 11.1low**noNone71a81bafd58e1301
iOS 12.1low**noNoneec55e5b4136c7949

* Denotes very rough guesstimate of likelihood that unsupported things will get echoed back by the server in the wild, visibly breaking the connection.
** No risk, if utls.EnableWeakCiphers() is called prior to using it.

Parrots FAQ

Does it really look like, say, Google Chrome with all the GREASE and stuff?

It LGTM, but please open up Wireshark and check. If you see something — say something.

Aren't there side channels? Everybody knows that the bird is a wordparrot is dead

There sure are. If you found one that approaches practicality at line speed — please tell us.

However, there is a difference between this sort of parroting and techniques like SkypeMorth. Namely, TLS is highly standardized protocol, therefore simply not that many subtle things in TLS protocol could be different and/or suddenly change in one of mimicked implementation(potentially undermining the mimicry). It is possible that we have a distinguisher right now, but amount of those potential distinguishers is limited.

Custom Handshake

It is possible to create custom handshake by

  1. Use HelloCustom as an argument for UClient() to get empty config
  2. Fill tls header fields: UConn.Hello.{Random, CipherSuites, CompressionMethods}, if needed, or stick to defaults.
  3. Configure and add various TLS Extensions to UConn.Extensions: they will be marshaled in order.
  4. Set Session and SessionCache, as needed.

If you need to manually control all the bytes on the wire(certainly not recommended!), you can set UConn.HandshakeStateBuilt = true, and marshal clientHello into UConn.HandshakeState.Hello.raw yourself. In this case you will be responsible for modifying other parts of Config and ClientHelloMsg to reflect your setup and not confuse "crypto/tls", which will be processing response from server.

Fingerprinting Captured Client Hello

You can use a captured client hello to generate new ones that mimic/have the same properties as the original. The generated client hellos should look like they were generated from the same client software as the original fingerprinted bytes. In order to do this:

  1. Create a ClientHelloSpec from the raw bytes of the original client hello
  2. Use HelloCustom as an argument for UClient() to get empty config
  3. Use ApplyPreset with the generated ClientHelloSpec to set the appropriate connection properties
uConn := UClient(&net.TCPConn{}, nil, HelloCustom)
fingerprinter := &Fingerprinter{}
generatedSpec, err := fingerprinter.FingerprintClientHello(rawCapturedClientHelloBytes)
if err != nil {
panic("fingerprinting failed: %v", err)
}
if err := uConn.ApplyPreset(generatedSpec); err != nil {
panic("applying generated spec failed: %v", err)
}

The rawCapturedClientHelloBytes should be the full tls record, including the record type/version/length header.

Roller

A simple wrapper, that allows to easily use multiple latest(auto-updated) fingerprints.

// NewRoller creates Roller object with default range of HelloIDs to cycle// through until a working/unblocked one is found.funcNewRoller() (*Roller, error)
// Dial attempts to connect to given address using different HelloIDs.// If a working HelloID is found, it is used again for subsequent Dials.// If tcp connection fails or all HelloIDs are tried, returns with last error.//// Usage examples://// Dial("tcp4", "google.com:443", "google.com")// Dial("tcp", "10.23.144.22:443", "mywebserver.org")func (c*Roller) Dial(network, addr, serverNamestring) (*UConn, error)

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. Currently, there is a simple function to set session ticket to any desired state:

// If you want you session tickets to be reused - use same cache on following connectionsfunc (uconn*UConn) SetSessionState(session*ClientSessionState)

Note that session tickets (fake ones or otherwise) are not reused.
To reuse tickets, create a shared cache and set it on current and further configs:

// If you want you session tickets to be reused - use same cache on following connectionsfunc (uconn*UConn) SetSessionCache(cacheClientSessionCache)

Custom TLS extensions

If you want to add your own fake (placeholder, without added functionality) extension for mimicry purposes, you can embed *tls.GenericExtension into your own struct and override Len() and Read() methods. For example, DelegatedCredentials extension can be implemented as follows:

constFakeDelegatedCredentialsuint16=0x0022typeFakeDelegatedCredentialsExtensionstruct {
*tls.GenericExtensionSignatureAlgorithms []tls.SignatureScheme
}
func (e*FakeDelegatedCredentialsExtension) Len() int {
return6+2*len(e.SignatureAlgorithms)
}
func (e*FakeDelegatedCredentialsExtension) Read(b []byte) (nint, errerror) {
iflen(b) <e.Len() {
return0, io.ErrShortBuffer
}
offset:=0appendUint16:=func(valuint16) {
b[offset] =byte(val>>8)
b[offset+1] =byte(val&0xff)
offset+=2
}
// Extension typeappendUint16(FakeDelegatedCredentials)
algosLength:=2*len(e.SignatureAlgorithms)
// Extension data lengthappendUint16(uint16(algosLength) +2)
// Algorithms list lengthappendUint16(uint16(algosLength))
// Algorithms listfor_, a:=rangee.SignatureAlgorithms {
appendUint16(uint16(a))
}
returne.Len(), io.EOF
}

Then it can be used just like normal extension:

&tls.ClientHelloSpec{
//...Extensions: []tls.TLSExtension{
//...&FakeDelegatedCredentialsExtension{
SignatureAlgorithms: []tls.SignatureScheme{
tls.ECDSAWithP256AndSHA256,
tls.ECDSAWithP384AndSHA384,
tls.ECDSAWithP521AndSHA512,
tls.ECDSAWithSHA1,
},
},
//...
}
//...
}

Client Hello IDs

See full list of clientHelloID values here.
There are different behaviors you can get, depending on your clientHelloID:

  1. utls.HelloRandomized adds/reorders extensions, ciphersuites, etc. randomly.
    HelloRandomized adds ALPN in a percentage of cases, you may want to use HelloRandomizedALPN or HelloRandomizedNoALPN to choose specific behavior explicitly, as ALPN might affect application layer.
  2. utls.HelloGolang HelloGolang will use default "crypto/tls" handshake marshaling codepath, which WILL overwrite your changes to Hello(Config, Session are fine). You might want to call BuildHandshakeState() before applying any changes. UConn.Extensions will be completely ignored.
  3. utls.HelloCustom will prepare ClientHello with empty uconn.Extensions so you can fill it with TLSExtension's manually.
  4. The rest will will parrot given browser. Such parrots include, for example:
    • utls.HelloChrome_Auto- parrots recommended(usually latest) Google Chrome version
    • utls.HelloChrome_58 - parrots Google Chrome 58
    • utls.HelloFirefox_Auto - parrots recommended(usually latest) Firefox version
    • utls.HelloFirefox_55 - parrots Firefox 55

Usage

Examples

Find basic examples here.
Here's a more advanced example showing how to generate randomized ClientHello, modify generated ciphersuites a bit, and proceed with the handshake.

Migrating from "crypto/tls"

Here's how default "crypto/tls" is typically used:

dialConn, err:=net.Dial("tcp", "172.217.11.46:443")
iferr!=nil {
fmt.Printf("net.Dial() failed: %+v\n", err)
return
}
config:= tls.Config{ServerName: "www.google.com"}
tlsConn:=tls.Client(dialConn, &config)
n, err=tlsConn.Write("Hello, World!")
//...

To start using using uTLS:

  1. Import this library (e.g. import tls "github.com/SearchPilot/utls")
  2. Pick the Client Hello ID
  3. Simply substitute tlsConn := tls.Client(dialConn, &config) with tlsConn := tls.UClient(dialConn, &config, tls.clientHelloID)

Customizing handshake

Some customizations(such as setting session ticket/clientHello) have easy-to-use functions for them. The idea is to make common manipulations easy:

cRandom:= []byte{100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
130, 131}
tlsConn.SetClientRandom(cRandom)
masterSecret:=make([]byte, 48)
copy(masterSecret, []byte("masterSecret is NOT sent over the wire")) // you may use it for real security// Create a session ticket that wasn't actually issued by the server.sessionState:=utls.MakeClientSessionState(sessionTicket, uint16(tls.VersionTLS12),
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
masterSecret,
nil, nil)
tlsConn.SetSessionState(sessionState)

For other customizations there are following functions

// you can use this to build the state manually and change it
// for example use Randomized ClientHello, and add more extensions
func (uconn *UConn) BuildHandshakeState() error
// Then apply the changes and marshal final bytes, which will be sent
func (uconn *UConn) MarshalClientHello() error

Contributors' guide

Please refer to this document if you're interested in internals

Credits

The initial development of uTLS was completed during an internship at Google Jigsaw. This is not an official Google product.

About

No description, website, or topics provided.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - SearchPilot/utls · GitHub
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

UTLS (Fork, modified by Kirk)

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.

Minimum Go Version: Go 1.21

If you have any questions, bug reports or contributions, you are welcome to publish those on GitHub. If you want to do so in private, you can contact one of developers personally via sergey.frolov@colorado.edu.

You can contact one of developers personally via gaukas.wang@colorado.edu.

Documentation below may not keep up with all the changes and new features at all times, so you are encouraged to use godoc.

Note: Information provided below in this README.md could be obsolete. We welcome any contributions to refresh the documentations in addition to code contributions.

Features

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.

It is highly recommended to use multiple fingeprints, including randomized ones to avoid relying on a single fingerprint.utls.Roller does this automatically.

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.

But note that there's a small chance that generated fingerprint won't work, so you may want to keep generating until a working one is found, and then keep reusing the working fingerprint to avoid suspicious behavior of constantly changing fingerprints. utls.Roller reuses working fingerprint automatically.

Generating randomized fingerprints

To generate a randomized fingerprint, simply do:

uTlsConn:=tls.UClient(tcpConn, &config, tls.HelloRandomized)

you can use helloRandomizedALPN or helloRandomizedNoALPN to ensure presence or absence of ALPN(Application-Layer Protocol Negotiation) extension. It is recommended, but certainly not required to include ALPN (or use helloRandomized which may or may not include ALPN). If you do use ALPN, you will want to correctly handle potential application layer protocols (likely h2 or http/1.1).

Reusing randomized fingerprint

// oldConn is an old connection that worked before, so we want to reuse it// newConn is a new connection we'd like to establishnewConn:=tls.UClient(tcpConn, &config, oldConn.ClientHelloID)

Parroting

This package can be used to parrot ClientHello of popular browsers. There are some caveats to this parroting:

  • We are forced to offer ciphersuites and tls extensions that are not supported by crypto/tls. This is not a problem, if you fully control the server and turn unsupported things off on server side.
  • Parroting could be imperfect, and there is no parroting beyond ClientHello.

Compatibility risks of available parrots

ParrotCiphers*Signature*Unsupported extensionsTLS Fingerprint ID
Chrome 62nonoChannelID0a4a74aeebd1bb66
Chrome 70nonoChannelID, Encrypted Certsbc4c7e42f4961cd7
Chrome 72nonoChannelID, Encrypted Certsbbf04e5f1881f506
Chrome 83nonoChannelID, Encrypted Certs9c673fd64a32c8dc
Firefox 56very lownoNonec884bad7f40bee56
Firefox 65very lownoMaxRecordSize6bfedc5d5c740d58
iOS 11.1low**noNone71a81bafd58e1301
iOS 12.1low**noNoneec55e5b4136c7949

* Denotes very rough guesstimate of likelihood that unsupported things will get echoed back by the server in the wild, visibly breaking the connection.
** No risk, if utls.EnableWeakCiphers() is called prior to using it.

Parrots FAQ

Does it really look like, say, Google Chrome with all the GREASE and stuff?

It LGTM, but please open up Wireshark and check. If you see something — say something.

Aren't there side channels? Everybody knows that the bird is a wordparrot is dead

There sure are. If you found one that approaches practicality at line speed — please tell us.

However, there is a difference between this sort of parroting and techniques like SkypeMorth. Namely, TLS is highly standardized protocol, therefore simply not that many subtle things in TLS protocol could be different and/or suddenly change in one of mimicked implementation(potentially undermining the mimicry). It is possible that we have a distinguisher right now, but amount of those potential distinguishers is limited.

Custom Handshake

It is possible to create custom handshake by

  1. Use HelloCustom as an argument for UClient() to get empty config
  2. Fill tls header fields: UConn.Hello.{Random, CipherSuites, CompressionMethods}, if needed, or stick to defaults.
  3. Configure and add various TLS Extensions to UConn.Extensions: they will be marshaled in order.
  4. Set Session and SessionCache, as needed.

If you need to manually control all the bytes on the wire(certainly not recommended!), you can set UConn.HandshakeStateBuilt = true, and marshal clientHello into UConn.HandshakeState.Hello.raw yourself. In this case you will be responsible for modifying other parts of Config and ClientHelloMsg to reflect your setup and not confuse "crypto/tls", which will be processing response from server.

Fingerprinting Captured Client Hello

You can use a captured client hello to generate new ones that mimic/have the same properties as the original. The generated client hellos should look like they were generated from the same client software as the original fingerprinted bytes. In order to do this:

  1. Create a ClientHelloSpec from the raw bytes of the original client hello
  2. Use HelloCustom as an argument for UClient() to get empty config
  3. Use ApplyPreset with the generated ClientHelloSpec to set the appropriate connection properties
uConn := UClient(&net.TCPConn{}, nil, HelloCustom)
fingerprinter := &Fingerprinter{}
generatedSpec, err := fingerprinter.FingerprintClientHello(rawCapturedClientHelloBytes)
if err != nil {
panic("fingerprinting failed: %v", err)
}
if err := uConn.ApplyPreset(generatedSpec); err != nil {
panic("applying generated spec failed: %v", err)
}

The rawCapturedClientHelloBytes should be the full tls record, including the record type/version/length header.

Roller

A simple wrapper, that allows to easily use multiple latest(auto-updated) fingerprints.

// NewRoller creates Roller object with default range of HelloIDs to cycle// through until a working/unblocked one is found.funcNewRoller() (*Roller, error)
// Dial attempts to connect to given address using different HelloIDs.// If a working HelloID is found, it is used again for subsequent Dials.// If tcp connection fails or all HelloIDs are tried, returns with last error.//// Usage examples://// Dial("tcp4", "google.com:443", "google.com")// Dial("tcp", "10.23.144.22:443", "mywebserver.org")func (c*Roller) Dial(network, addr, serverNamestring) (*UConn, error)

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. Currently, there is a simple function to set session ticket to any desired state:

// If you want you session tickets to be reused - use same cache on following connectionsfunc (uconn*UConn) SetSessionState(session*ClientSessionState)

Note that session tickets (fake ones or otherwise) are not reused.
To reuse tickets, create a shared cache and set it on current and further configs:

// If you want you session tickets to be reused - use same cache on following connectionsfunc (uconn*UConn) SetSessionCache(cacheClientSessionCache)

Custom TLS extensions

If you want to add your own fake (placeholder, without added functionality) extension for mimicry purposes, you can embed *tls.GenericExtension into your own struct and override Len() and Read() methods. For example, DelegatedCredentials extension can be implemented as follows:

constFakeDelegatedCredentialsuint16=0x0022typeFakeDelegatedCredentialsExtensionstruct {
*tls.GenericExtensionSignatureAlgorithms []tls.SignatureScheme
}
func (e*FakeDelegatedCredentialsExtension) Len() int {
return6+2*len(e.SignatureAlgorithms)
}
func (e*FakeDelegatedCredentialsExtension) Read(b []byte) (nint, errerror) {
iflen(b) <e.Len() {
return0, io.ErrShortBuffer
}
offset:=0appendUint16:=func(valuint16) {
b[offset] =byte(val>>8)
b[offset+1] =byte(val&0xff)
offset+=2
}
// Extension typeappendUint16(FakeDelegatedCredentials)
algosLength:=2*len(e.SignatureAlgorithms)
// Extension data lengthappendUint16(uint16(algosLength) +2)
// Algorithms list lengthappendUint16(uint16(algosLength))
// Algorithms listfor_, a:=rangee.SignatureAlgorithms {
appendUint16(uint16(a))
}
returne.Len(), io.EOF
}

Then it can be used just like normal extension:

&tls.ClientHelloSpec{
//...Extensions: []tls.TLSExtension{
//...&FakeDelegatedCredentialsExtension{
SignatureAlgorithms: []tls.SignatureScheme{
tls.ECDSAWithP256AndSHA256,
tls.ECDSAWithP384AndSHA384,
tls.ECDSAWithP521AndSHA512,
tls.ECDSAWithSHA1,
},
},
//...
}
//...
}

Client Hello IDs

See full list of clientHelloID values here.
There are different behaviors you can get, depending on your clientHelloID:

  1. utls.HelloRandomized adds/reorders extensions, ciphersuites, etc. randomly.
    HelloRandomized adds ALPN in a percentage of cases, you may want to use HelloRandomizedALPN or HelloRandomizedNoALPN to choose specific behavior explicitly, as ALPN might affect application layer.
  2. utls.HelloGolang HelloGolang will use default "crypto/tls" handshake marshaling codepath, which WILL overwrite your changes to Hello(Config, Session are fine). You might want to call BuildHandshakeState() before applying any changes. UConn.Extensions will be completely ignored.
  3. utls.HelloCustom will prepare ClientHello with empty uconn.Extensions so you can fill it with TLSExtension's manually.
  4. The rest will will parrot given browser. Such parrots include, for example:
    • utls.HelloChrome_Auto- parrots recommended(usually latest) Google Chrome version
    • utls.HelloChrome_58 - parrots Google Chrome 58
    • utls.HelloFirefox_Auto - parrots recommended(usually latest) Firefox version
    • utls.HelloFirefox_55 - parrots Firefox 55

Usage

Examples

Find basic examples here.
Here's a more advanced example showing how to generate randomized ClientHello, modify generated ciphersuites a bit, and proceed with the handshake.

Migrating from "crypto/tls"

Here's how default "crypto/tls" is typically used:

dialConn, err:=net.Dial("tcp", "172.217.11.46:443")
iferr!=nil {
fmt.Printf("net.Dial() failed: %+v\n", err)
return
}
config:= tls.Config{ServerName: "www.google.com"}
tlsConn:=tls.Client(dialConn, &config)
n, err=tlsConn.Write("Hello, World!")
//...

To start using using uTLS:

  1. Import this library (e.g. import tls "github.com/SearchPilot/utls")
  2. Pick the Client Hello ID
  3. Simply substitute tlsConn := tls.Client(dialConn, &config) with tlsConn := tls.UClient(dialConn, &config, tls.clientHelloID)

Customizing handshake

Some customizations(such as setting session ticket/clientHello) have easy-to-use functions for them. The idea is to make common manipulations easy:

cRandom:= []byte{100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
130, 131}
tlsConn.SetClientRandom(cRandom)
masterSecret:=make([]byte, 48)
copy(masterSecret, []byte("masterSecret is NOT sent over the wire")) // you may use it for real security// Create a session ticket that wasn't actually issued by the server.sessionState:=utls.MakeClientSessionState(sessionTicket, uint16(tls.VersionTLS12),
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
masterSecret,
nil, nil)
tlsConn.SetSessionState(sessionState)

For other customizations there are following functions

// you can use this to build the state manually and change it
// for example use Randomized ClientHello, and add more extensions
func (uconn *UConn) BuildHandshakeState() error
// Then apply the changes and marshal final bytes, which will be sent
func (uconn *UConn) MarshalClientHello() error

Contributors' guide

Please refer to this document if you're interested in internals

Credits

The initial development of uTLS was completed during an internship at Google Jigsaw. This is not an official Google product.

About

No description, website, or topics provided.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' GitHub - SearchPilot/utls · GitHub
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

UTLS (Fork, modified by Kirk)

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.

Minimum Go Version: Go 1.21

If you have any questions, bug reports or contributions, you are welcome to publish those on GitHub. If you want to do so in private, you can contact one of developers personally via sergey.frolov@colorado.edu.

You can contact one of developers personally via gaukas.wang@colorado.edu.

Documentation below may not keep up with all the changes and new features at all times, so you are encouraged to use godoc.

Note: Information provided below in this README.md could be obsolete. We welcome any contributions to refresh the documentations in addition to code contributions.

Features

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.

It is highly recommended to use multiple fingeprints, including randomized ones to avoid relying on a single fingerprint.utls.Roller does this automatically.

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.

But note that there's a small chance that generated fingerprint won't work, so you may want to keep generating until a working one is found, and then keep reusing the working fingerprint to avoid suspicious behavior of constantly changing fingerprints. utls.Roller reuses working fingerprint automatically.

Generating randomized fingerprints

To generate a randomized fingerprint, simply do:

uTlsConn:=tls.UClient(tcpConn, &config, tls.HelloRandomized)

you can use helloRandomizedALPN or helloRandomizedNoALPN to ensure presence or absence of ALPN(Application-Layer Protocol Negotiation) extension. It is recommended, but certainly not required to include ALPN (or use helloRandomized which may or may not include ALPN). If you do use ALPN, you will want to correctly handle potential application layer protocols (likely h2 or http/1.1).

Reusing randomized fingerprint

// oldConn is an old connection that worked before, so we want to reuse it// newConn is a new connection we'd like to establishnewConn:=tls.UClient(tcpConn, &config, oldConn.ClientHelloID)

Parroting

This package can be used to parrot ClientHello of popular browsers. There are some caveats to this parroting:

  • We are forced to offer ciphersuites and tls extensions that are not supported by crypto/tls. This is not a problem, if you fully control the server and turn unsupported things off on server side.
  • Parroting could be imperfect, and there is no parroting beyond ClientHello.

Compatibility risks of available parrots

ParrotCiphers*Signature*Unsupported extensionsTLS Fingerprint ID
Chrome 62nonoChannelID0a4a74aeebd1bb66
Chrome 70nonoChannelID, Encrypted Certsbc4c7e42f4961cd7
Chrome 72nonoChannelID, Encrypted Certsbbf04e5f1881f506
Chrome 83nonoChannelID, Encrypted Certs9c673fd64a32c8dc
Firefox 56very lownoNonec884bad7f40bee56
Firefox 65very lownoMaxRecordSize6bfedc5d5c740d58
iOS 11.1low**noNone71a81bafd58e1301
iOS 12.1low**noNoneec55e5b4136c7949

* Denotes very rough guesstimate of likelihood that unsupported things will get echoed back by the server in the wild, visibly breaking the connection.
** No risk, if utls.EnableWeakCiphers() is called prior to using it.

Parrots FAQ

Does it really look like, say, Google Chrome with all the GREASE and stuff?

It LGTM, but please open up Wireshark and check. If you see something — say something.

Aren't there side channels? Everybody knows that the bird is a wordparrot is dead

There sure are. If you found one that approaches practicality at line speed — please tell us.

However, there is a difference between this sort of parroting and techniques like SkypeMorth. Namely, TLS is highly standardized protocol, therefore simply not that many subtle things in TLS protocol could be different and/or suddenly change in one of mimicked implementation(potentially undermining the mimicry). It is possible that we have a distinguisher right now, but amount of those potential distinguishers is limited.

Custom Handshake

It is possible to create custom handshake by

  1. Use HelloCustom as an argument for UClient() to get empty config
  2. Fill tls header fields: UConn.Hello.{Random, CipherSuites, CompressionMethods}, if needed, or stick to defaults.
  3. Configure and add various TLS Extensions to UConn.Extensions: they will be marshaled in order.
  4. Set Session and SessionCache, as needed.

If you need to manually control all the bytes on the wire(certainly not recommended!), you can set UConn.HandshakeStateBuilt = true, and marshal clientHello into UConn.HandshakeState.Hello.raw yourself. In this case you will be responsible for modifying other parts of Config and ClientHelloMsg to reflect your setup and not confuse "crypto/tls", which will be processing response from server.

Fingerprinting Captured Client Hello

You can use a captured client hello to generate new ones that mimic/have the same properties as the original. The generated client hellos should look like they were generated from the same client software as the original fingerprinted bytes. In order to do this:

  1. Create a ClientHelloSpec from the raw bytes of the original client hello
  2. Use HelloCustom as an argument for UClient() to get empty config
  3. Use ApplyPreset with the generated ClientHelloSpec to set the appropriate connection properties
uConn := UClient(&net.TCPConn{}, nil, HelloCustom)
fingerprinter := &Fingerprinter{}
generatedSpec, err := fingerprinter.FingerprintClientHello(rawCapturedClientHelloBytes)
if err != nil {
panic("fingerprinting failed: %v", err)
}
if err := uConn.ApplyPreset(generatedSpec); err != nil {
panic("applying generated spec failed: %v", err)
}

The rawCapturedClientHelloBytes should be the full tls record, including the record type/version/length header.

Roller

A simple wrapper, that allows to easily use multiple latest(auto-updated) fingerprints.

// NewRoller creates Roller object with default range of HelloIDs to cycle// through until a working/unblocked one is found.funcNewRoller() (*Roller, error)
// Dial attempts to connect to given address using different HelloIDs.// If a working HelloID is found, it is used again for subsequent Dials.// If tcp connection fails or all HelloIDs are tried, returns with last error.//// Usage examples://// Dial("tcp4", "google.com:443", "google.com")// Dial("tcp", "10.23.144.22:443", "mywebserver.org")func (c*Roller) Dial(network, addr, serverNamestring) (*UConn, error)

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. Currently, there is a simple function to set session ticket to any desired state:

// If you want you session tickets to be reused - use same cache on following connectionsfunc (uconn*UConn) SetSessionState(session*ClientSessionState)

Note that session tickets (fake ones or otherwise) are not reused.
To reuse tickets, create a shared cache and set it on current and further configs:

// If you want you session tickets to be reused - use same cache on following connectionsfunc (uconn*UConn) SetSessionCache(cacheClientSessionCache)

Custom TLS extensions

If you want to add your own fake (placeholder, without added functionality) extension for mimicry purposes, you can embed *tls.GenericExtension into your own struct and override Len() and Read() methods. For example, DelegatedCredentials extension can be implemented as follows:

constFakeDelegatedCredentialsuint16=0x0022typeFakeDelegatedCredentialsExtensionstruct {
*tls.GenericExtensionSignatureAlgorithms []tls.SignatureScheme
}
func (e*FakeDelegatedCredentialsExtension) Len() int {
return6+2*len(e.SignatureAlgorithms)
}
func (e*FakeDelegatedCredentialsExtension) Read(b []byte) (nint, errerror) {
iflen(b) <e.Len() {
return0, io.ErrShortBuffer
}
offset:=0appendUint16:=func(valuint16) {
b[offset] =byte(val>>8)
b[offset+1] =byte(val&0xff)
offset+=2
}
// Extension typeappendUint16(FakeDelegatedCredentials)
algosLength:=2*len(e.SignatureAlgorithms)
// Extension data lengthappendUint16(uint16(algosLength) +2)
// Algorithms list lengthappendUint16(uint16(algosLength))
// Algorithms listfor_, a:=rangee.SignatureAlgorithms {
appendUint16(uint16(a))
}
returne.Len(), io.EOF
}

Then it can be used just like normal extension:

&tls.ClientHelloSpec{
//...Extensions: []tls.TLSExtension{
//...&FakeDelegatedCredentialsExtension{
SignatureAlgorithms: []tls.SignatureScheme{
tls.ECDSAWithP256AndSHA256,
tls.ECDSAWithP384AndSHA384,
tls.ECDSAWithP521AndSHA512,
tls.ECDSAWithSHA1,
},
},
//...
}
//...
}

Client Hello IDs

See full list of clientHelloID values here.
There are different behaviors you can get, depending on your clientHelloID:

  1. utls.HelloRandomized adds/reorders extensions, ciphersuites, etc. randomly.
    HelloRandomized adds ALPN in a percentage of cases, you may want to use HelloRandomizedALPN or HelloRandomizedNoALPN to choose specific behavior explicitly, as ALPN might affect application layer.
  2. utls.HelloGolang HelloGolang will use default "crypto/tls" handshake marshaling codepath, which WILL overwrite your changes to Hello(Config, Session are fine). You might want to call BuildHandshakeState() before applying any changes. UConn.Extensions will be completely ignored.
  3. utls.HelloCustom will prepare ClientHello with empty uconn.Extensions so you can fill it with TLSExtension's manually.
  4. The rest will will parrot given browser. Such parrots include, for example:
    • utls.HelloChrome_Auto- parrots recommended(usually latest) Google Chrome version
    • utls.HelloChrome_58 - parrots Google Chrome 58
    • utls.HelloFirefox_Auto - parrots recommended(usually latest) Firefox version
    • utls.HelloFirefox_55 - parrots Firefox 55

Usage

Examples

Find basic examples here.
Here's a more advanced example showing how to generate randomized ClientHello, modify generated ciphersuites a bit, and proceed with the handshake.

Migrating from "crypto/tls"

Here's how default "crypto/tls" is typically used:

dialConn, err:=net.Dial("tcp", "172.217.11.46:443")
iferr!=nil {
fmt.Printf("net.Dial() failed: %+v\n", err)
return
}
config:= tls.Config{ServerName: "www.google.com"}
tlsConn:=tls.Client(dialConn, &config)
n, err=tlsConn.Write("Hello, World!")
//...

To start using using uTLS:

  1. Import this library (e.g. import tls "github.com/SearchPilot/utls")
  2. Pick the Client Hello ID
  3. Simply substitute tlsConn := tls.Client(dialConn, &config) with tlsConn := tls.UClient(dialConn, &config, tls.clientHelloID)

Customizing handshake

Some customizations(such as setting session ticket/clientHello) have easy-to-use functions for them. The idea is to make common manipulations easy:

cRandom:= []byte{100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
130, 131}
tlsConn.SetClientRandom(cRandom)
masterSecret:=make([]byte, 48)
copy(masterSecret, []byte("masterSecret is NOT sent over the wire")) // you may use it for real security// Create a session ticket that wasn't actually issued by the server.sessionState:=utls.MakeClientSessionState(sessionTicket, uint16(tls.VersionTLS12),
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
masterSecret,
nil, nil)
tlsConn.SetSessionState(sessionState)

For other customizations there are following functions

// you can use this to build the state manually and change it
// for example use Randomized ClientHello, and add more extensions
func (uconn *UConn) BuildHandshakeState() error
// Then apply the changes and marshal final bytes, which will be sent
func (uconn *UConn) MarshalClientHello() error

Contributors' guide

Please refer to this document if you're interested in internals

Credits

The initial development of uTLS was completed during an internship at Google Jigsaw. This is not an official Google product.

About

No description, website, or topics provided.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); GitHub - SearchPilot/utls · GitHub
Skip to content

Latest commit

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

UTLS (Fork, modified by Kirk)

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.

Minimum Go Version: Go 1.21

If you have any questions, bug reports or contributions, you are welcome to publish those on GitHub. If you want to do so in private, you can contact one of developers personally via sergey.frolov@colorado.edu.

You can contact one of developers personally via gaukas.wang@colorado.edu.

Documentation below may not keep up with all the changes and new features at all times, so you are encouraged to use godoc.

Note: Information provided below in this README.md could be obsolete. We welcome any contributions to refresh the documentations in addition to code contributions.

Features

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.

It is highly recommended to use multiple fingeprints, including randomized ones to avoid relying on a single fingerprint.utls.Roller does this automatically.

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.

But note that there's a small chance that generated fingerprint won't work, so you may want to keep generating until a working one is found, and then keep reusing the working fingerprint to avoid suspicious behavior of constantly changing fingerprints. utls.Roller reuses working fingerprint automatically.

Generating randomized fingerprints

To generate a randomized fingerprint, simply do:

uTlsConn:=tls.UClient(tcpConn, &config, tls.HelloRandomized)

you can use helloRandomizedALPN or helloRandomizedNoALPN to ensure presence or absence of ALPN(Application-Layer Protocol Negotiation) extension. It is recommended, but certainly not required to include ALPN (or use helloRandomized which may or may not include ALPN). If you do use ALPN, you will want to correctly handle potential application layer protocols (likely h2 or http/1.1).

Reusing randomized fingerprint

// oldConn is an old connection that worked before, so we want to reuse it// newConn is a new connection we'd like to establishnewConn:=tls.UClient(tcpConn, &config, oldConn.ClientHelloID)

Parroting

This package can be used to parrot ClientHello of popular browsers. There are some caveats to this parroting:

  • We are forced to offer ciphersuites and tls extensions that are not supported by crypto/tls. This is not a problem, if you fully control the server and turn unsupported things off on server side.
  • Parroting could be imperfect, and there is no parroting beyond ClientHello.

Compatibility risks of available parrots

ParrotCiphers*Signature*Unsupported extensionsTLS Fingerprint ID
Chrome 62nonoChannelID0a4a74aeebd1bb66
Chrome 70nonoChannelID, Encrypted Certsbc4c7e42f4961cd7
Chrome 72nonoChannelID, Encrypted Certsbbf04e5f1881f506
Chrome 83nonoChannelID, Encrypted Certs9c673fd64a32c8dc
Firefox 56very lownoNonec884bad7f40bee56
Firefox 65very lownoMaxRecordSize6bfedc5d5c740d58
iOS 11.1low**noNone71a81bafd58e1301
iOS 12.1low**noNoneec55e5b4136c7949

* Denotes very rough guesstimate of likelihood that unsupported things will get echoed back by the server in the wild, visibly breaking the connection.
** No risk, if utls.EnableWeakCiphers() is called prior to using it.

Parrots FAQ

Does it really look like, say, Google Chrome with all the GREASE and stuff?

It LGTM, but please open up Wireshark and check. If you see something — say something.

Aren't there side channels? Everybody knows that the bird is a wordparrot is dead

There sure are. If you found one that approaches practicality at line speed — please tell us.

However, there is a difference between this sort of parroting and techniques like SkypeMorth. Namely, TLS is highly standardized protocol, therefore simply not that many subtle things in TLS protocol could be different and/or suddenly change in one of mimicked implementation(potentially undermining the mimicry). It is possible that we have a distinguisher right now, but amount of those potential distinguishers is limited.

Custom Handshake

It is possible to create custom handshake by

  1. Use HelloCustom as an argument for UClient() to get empty config
  2. Fill tls header fields: UConn.Hello.{Random, CipherSuites, CompressionMethods}, if needed, or stick to defaults.
  3. Configure and add various TLS Extensions to UConn.Extensions: they will be marshaled in order.
  4. Set Session and SessionCache, as needed.

If you need to manually control all the bytes on the wire(certainly not recommended!), you can set UConn.HandshakeStateBuilt = true, and marshal clientHello into UConn.HandshakeState.Hello.raw yourself. In this case you will be responsible for modifying other parts of Config and ClientHelloMsg to reflect your setup and not confuse "crypto/tls", which will be processing response from server.

Fingerprinting Captured Client Hello

You can use a captured client hello to generate new ones that mimic/have the same properties as the original. The generated client hellos should look like they were generated from the same client software as the original fingerprinted bytes. In order to do this:

  1. Create a ClientHelloSpec from the raw bytes of the original client hello
  2. Use HelloCustom as an argument for UClient() to get empty config
  3. Use ApplyPreset with the generated ClientHelloSpec to set the appropriate connection properties
uConn := UClient(&net.TCPConn{}, nil, HelloCustom)
fingerprinter := &Fingerprinter{}
generatedSpec, err := fingerprinter.FingerprintClientHello(rawCapturedClientHelloBytes)
if err != nil {
panic("fingerprinting failed: %v", err)
}
if err := uConn.ApplyPreset(generatedSpec); err != nil {
panic("applying generated spec failed: %v", err)
}

The rawCapturedClientHelloBytes should be the full tls record, including the record type/version/length header.

Roller

A simple wrapper, that allows to easily use multiple latest(auto-updated) fingerprints.

// NewRoller creates Roller object with default range of HelloIDs to cycle// through until a working/unblocked one is found.funcNewRoller() (*Roller, error)
// Dial attempts to connect to given address using different HelloIDs.// If a working HelloID is found, it is used again for subsequent Dials.// If tcp connection fails or all HelloIDs are tried, returns with last error.//// Usage examples://// Dial("tcp4", "google.com:443", "google.com")// Dial("tcp", "10.23.144.22:443", "mywebserver.org")func (c*Roller) Dial(network, addr, serverNamestring) (*UConn, error)

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. Currently, there is a simple function to set session ticket to any desired state:

// If you want you session tickets to be reused - use same cache on following connectionsfunc (uconn*UConn) SetSessionState(session*ClientSessionState)

Note that session tickets (fake ones or otherwise) are not reused.
To reuse tickets, create a shared cache and set it on current and further configs:

// If you want you session tickets to be reused - use same cache on following connectionsfunc (uconn*UConn) SetSessionCache(cacheClientSessionCache)

Custom TLS extensions

If you want to add your own fake (placeholder, without added functionality) extension for mimicry purposes, you can embed *tls.GenericExtension into your own struct and override Len() and Read() methods. For example, DelegatedCredentials extension can be implemented as follows:

constFakeDelegatedCredentialsuint16=0x0022typeFakeDelegatedCredentialsExtensionstruct {
*tls.GenericExtensionSignatureAlgorithms []tls.SignatureScheme
}
func (e*FakeDelegatedCredentialsExtension) Len() int {
return6+2*len(e.SignatureAlgorithms)
}
func (e*FakeDelegatedCredentialsExtension) Read(b []byte) (nint, errerror) {
iflen(b) <e.Len() {
return0, io.ErrShortBuffer
}
offset:=0appendUint16:=func(valuint16) {
b[offset] =byte(val>>8)
b[offset+1] =byte(val&0xff)
offset+=2
}
// Extension typeappendUint16(FakeDelegatedCredentials)
algosLength:=2*len(e.SignatureAlgorithms)
// Extension data lengthappendUint16(uint16(algosLength) +2)
// Algorithms list lengthappendUint16(uint16(algosLength))
// Algorithms listfor_, a:=rangee.SignatureAlgorithms {
appendUint16(uint16(a))
}
returne.Len(), io.EOF
}

Then it can be used just like normal extension:

&tls.ClientHelloSpec{
//...Extensions: []tls.TLSExtension{
//...&FakeDelegatedCredentialsExtension{
SignatureAlgorithms: []tls.SignatureScheme{
tls.ECDSAWithP256AndSHA256,
tls.ECDSAWithP384AndSHA384,
tls.ECDSAWithP521AndSHA512,
tls.ECDSAWithSHA1,
},
},
//...
}
//...
}

Client Hello IDs

See full list of clientHelloID values here.
There are different behaviors you can get, depending on your clientHelloID:

  1. utls.HelloRandomized adds/reorders extensions, ciphersuites, etc. randomly.
    HelloRandomized adds ALPN in a percentage of cases, you may want to use HelloRandomizedALPN or HelloRandomizedNoALPN to choose specific behavior explicitly, as ALPN might affect application layer.
  2. utls.HelloGolang HelloGolang will use default "crypto/tls" handshake marshaling codepath, which WILL overwrite your changes to Hello(Config, Session are fine). You might want to call BuildHandshakeState() before applying any changes. UConn.Extensions will be completely ignored.
  3. utls.HelloCustom will prepare ClientHello with empty uconn.Extensions so you can fill it with TLSExtension's manually.
  4. The rest will will parrot given browser. Such parrots include, for example:
    • utls.HelloChrome_Auto- parrots recommended(usually latest) Google Chrome version
    • utls.HelloChrome_58 - parrots Google Chrome 58
    • utls.HelloFirefox_Auto - parrots recommended(usually latest) Firefox version
    • utls.HelloFirefox_55 - parrots Firefox 55

Usage

Examples

Find basic examples here.
Here's a more advanced example showing how to generate randomized ClientHello, modify generated ciphersuites a bit, and proceed with the handshake.

Migrating from "crypto/tls"

Here's how default "crypto/tls" is typically used:

dialConn, err:=net.Dial("tcp", "172.217.11.46:443")
iferr!=nil {
fmt.Printf("net.Dial() failed: %+v\n", err)
return
}
config:= tls.Config{ServerName: "www.google.com"}
tlsConn:=tls.Client(dialConn, &config)
n, err=tlsConn.Write("Hello, World!")
//...

To start using using uTLS:

  1. Import this library (e.g. import tls "github.com/SearchPilot/utls")
  2. Pick the Client Hello ID
  3. Simply substitute tlsConn := tls.Client(dialConn, &config) with tlsConn := tls.UClient(dialConn, &config, tls.clientHelloID)

Customizing handshake

Some customizations(such as setting session ticket/clientHello) have easy-to-use functions for them. The idea is to make common manipulations easy:

cRandom:= []byte{100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127, 128, 129,
130, 131}
tlsConn.SetClientRandom(cRandom)
masterSecret:=make([]byte, 48)
copy(masterSecret, []byte("masterSecret is NOT sent over the wire")) // you may use it for real security// Create a session ticket that wasn't actually issued by the server.sessionState:=utls.MakeClientSessionState(sessionTicket, uint16(tls.VersionTLS12),
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
masterSecret,
nil, nil)
tlsConn.SetSessionState(sessionState)

For other customizations there are following functions

// you can use this to build the state manually and change it
// for example use Randomized ClientHello, and add more extensions
func (uconn *UConn) BuildHandshakeState() error
// Then apply the changes and marshal final bytes, which will be sent
func (uconn *UConn) MarshalClientHello() error

Contributors' guide

Please refer to this document if you're interested in internals

Credits

The initial development of uTLS was completed during an internship at Google Jigsaw. This is not an official Google product.

About

No description, website, or topics provided.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages