cipherhw | ||
examples | ||
testdata | ||
testenv | ||
alert.go | ||
cipher_suites.go | ||
common.go | ||
conn.go | ||
conn_test.go | ||
CONTRIBUTING.md | ||
CONTRIBUTORS_GUIDE.md | ||
example_test.go | ||
generate_cert.go | ||
handshake_client.go | ||
handshake_client_test.go | ||
handshake_messages.go | ||
handshake_messages_test.go | ||
handshake_server.go | ||
handshake_server_test.go | ||
handshake_test.go | ||
key_agreement.go | ||
LICENSE | ||
prf.go | ||
prf_test.go | ||
README.md | ||
ticket.go | ||
tls.go | ||
tls_test.go | ||
u_common.go | ||
u_conn.go | ||
u_conn_test.go | ||
u_parrots.go | ||
u_public.go | ||
u_tls_extensions.go |
uTLS
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.
ClientHello fingerprinting resistance
Golang's ClientHello has a very unique fingerprint, which especially sticks out on mobile clients, where Golang is not too popular yet. Some members of anti-censorship community are concerned that their tools could be trivially blocked based on ClientHello with relatively small collateral damage. There are multiple solutions to this issue.
Randomized handshake
This package can be used to generate randomized ClientHello.
Provides a moving target without any compatibility or parrot-is-dead attack risks.
Feedback about implementation details of randomized handshake is extremely appreciated.
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
This package allows ClientHello messages to parrot popular browsers. There are few caveats to this parroting:
- We are forced to offer ciphersuites and tls extensions setups that are not supported by crypto/tls. This is not a problem, if you fully control the server.
- Parroting could be imperfect, and there is no parroting beyond ClientHello.\
Parrot | Ciphers* | Signature* | Unsupported extensions |
---|---|---|---|
Android 5.1 | low | very low | None |
Android 6.0 | low | very low | Extended Master Secret** |
Chrome 58 | no | low | Extended Master Secret**, ChannelID |
* Denotes very rough guesstimate of likelihood that unsupported things will get echoed back by the server in the wild,
visibly breaking the connection.
** New extensions such as EMS become popular quickly, so it's not recommended to use with servers you don't own.
As seen in table, many good parrots will become available once EMS is implemented in crypto/tls.
Work-in-progress parrots
Not finished yet!
Parrot | Ciphers* | Signature* | Unsupported extensions |
---|---|---|---|
Firefox 53 | low | low | Extended Master Secret** |
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.
Things to implement in Golang to make parrots better
- Extended Master Secret and ChannelID extensions
- Enable sha512 and sha224 hashes by default
- Implement RSA PSS signature algorithms
- In general, any modern crypto is likely to be useful going forward.
Custom Handshake
It is possible to create custom handshake by
- Use
HelloCustom
as an argument forUClient()
to get empty config - Fill tls header fields: UConn.Hello.{Random, CipherSuites, CompressionMethods}, if needed, or stick to defaults.
- Configure and add various TLS Extensions to UConn.Extensions: they will be marshaled in order.
- 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.
Fake Session Tickets
Set of provided functions is likely to change, as use-cases aren't fully worked out yet. Currently, there is a simple function to set session ticket to any desired state:
func (c *ExtendedConfig) 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:
func (c *ExtendedConfig) SetSessionCache(cache ClientSessionCache)
Usage
Find other examples here.
For a reference, here's how default "crypto/tls" is used:
config := tls.Config{ServerName: "www.google.com"}
dialConn, err := net.Dial("tcp", "172.217.11.46:443")
if err != nil {
fmt.Printf("net.Dial() failed: %+v\n", err)
return
}
tlsConn := tls.Client(dialConn, &config)
err = tlsConn.Handshake()
if err != nil {
fmt.Printf("tlsConn.Handshake() error: %+v", err)
return
}
Now, if you want to use uTLS, simply substitute tlsConn := tls.Client(dialConn, &config)
with tlsConn := utls.UClient(dialConn, &config, clientHelloID)
where clientHelloID is one of the following:
utls.HelloRandomized
adds/reorders extensions, ciphersuites, etc. randomly.
HelloRandomized
adds ALPN in 50% of cases, you may want to useHelloRandomizedALPN
orHelloRandomizedNoALPN
to choose specific behavior explicitly, as ALPN might affect application layer.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.utls.HelloCustom
will prepare ClientHello with empty uconn.Extensions so you can fill it with TLSExtension's manually.- The rest will will parrot given browser.
utls.HelloChrome_Auto
- parrots recommended(latest) Google Chrome versionutls.HelloChrome_58
- parrots Google Chrome 58utls.HelloFirefox_Auto
- parrots recommended(latest) Firefox versionutls.HelloFirefox_53_WIP
- parrots Firefox 53 (Work in progress!)utls.HelloAndroid_Auto
utls.HelloAndroid_6_0_Browser
utls.HelloAndroid_5_1_Browser
Note: it is mandatory to manually call tlsConn.Handshake()
afterwards.
Otherwise, eventual tls.Write()
is going to call non-overridden version of handshake.
Customizing handshake
Before doing Handshake()
you can also set fake session ticket, set clientHello or change uconn in other ways:
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)