diff --git a/CONTRIBUTORS_GUIDE.md b/CONTRIBUTORS_GUIDE.md new file mode 100644 index 0000000..ea5dc82 --- /dev/null +++ b/CONTRIBUTORS_GUIDE.md @@ -0,0 +1,60 @@ +# How this package works +### Chapter 1: [Making private things public](./u_public.go) +There are numerous handshake-related structs in crypto/tls, most of which are either private or have private fields. +One of them — `clientHandshakeState` — has private function `handshake()`, +which is called in the beginning of default handshake. +Unfortunately, user will not be able to directly access this struct outside of tls package. +As a result, we decided to employ following workaround: declare public copies of private structs. +Now user is free to manipulate fields of public `ClientHandshakeState`. +Then, right before handshake, we can shallow-copy public state into private `clientHandshakeState`, +call `handshake()` on it and carry on with default Golang handshake process. +After handshake is done we shallow-copy private state back to public, allowing user to read results of handshake. + +### Chapter 2: [TLSExtension](./u_tls_extensions.go) +The way we achieve reasonable flexibilty with extensions is inspired by +[ztls'](https://github.com/zmap/zcrypto/blob/master/tls/handshake_extensions.go) design. +However, our design has several differences, so we wrote it from scratch. +This design allows us to have an array of `TLSExtension` objects and then marshal them in order: +```Golang +type TLSExtension interface { + writeToUConn(*UConn) error + + Len() int // includes header + + // Read reads up to len(p) bytes into p. + // It returns the number of bytes read (0 <= n <= len(p)) and any error encountered. + Read(p []byte) (n int, err error) // implements io.Reader +} +``` +`writeToUConn()` applies appropriate per-extension changes to `UConn`. + +`Len()` provides the size of marshaled extension, so we can allocate appropriate buffer beforehand, +catch out-of-bound errors easily and guide size-dependent extensions such as padding. + +`Read(buffer []byte)` _writes(see: io.Reader interface)_ marshaled extensions into provided buffer. +This avoids extra allocations. + +### Chapter 3: [UConn](./u_conn.go) +`UConn` extends standard `tls.Conn`. Most notably, it stores slice with `TLSExtension`s and public +`ClientHandshakeState`. +Whenever `UConn.BuildHandshakeState()` gets called (happens automatically in `UConn.Handshake()` +or could be called manually), config will be applied according to chosen `ClientHelloID`. +From contributor's view there are 2 main behaviors: + * `HelloGolang` simply calls default Golang's [`makeClientHello()`](./handshake_client.go) + and directly stores it into `HandshakeState.Hello`. utls-specific stuff is ignored. + * Other ClientHelloIDs fill `UConn.Hello.{Random, CipherSuites, CompressionMethods}` and `UConn.Extensions` with +per-parrot setup, which then gets applied to appropriate standard tls structs, +and then marshaled by utls into `HandshakeState.Hello`. + +### Chapter 4: Tests + +Tests exist, but coverage is very limited. What's covered is a conjunction of + * TLS 1.2 + * Working parrots without any unsupported extensions (only Android 5.1 at this time) + * Ciphersuites offered by parrot. + * Ciphersuites supported by Golang + * Simple conversation with reference implementation of OpenSSL. +(e.g. no automatic checks for renegotiations, parroting quality and such) + +plus we test some other minor things. +Basically, current tests aim to provide a sanity check. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..75ac6da --- /dev/null +++ b/README.md @@ -0,0 +1,141 @@ +# 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 parrotsThis 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](https://tools.ietf.org/html/draft-davidben-tls-grease-01) and stuff? + +It LGTM, but please open up Wireshark and check. If you see something — [say something](issues). + +> Aren't there side channels? Everybody knows that the ~~bird is a word~~[parrot is dead](https://people.cs.umass.edu/~amir/papers/parrot.pdf) + +There sure are. If you found one that approaches practicality at line speed — [please tell us](issues). + +##### 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 +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](u_tls_extensions.go) 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. +## 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: + +```Golang +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: + +```Golang +func (c *ExtendedConfig) SetSessionCache(cache ClientSessionCache) +``` + +## Usage + +Find other examples [here](examples/examples.go). + +For a reference, here's how default "crypto/tls" is used: +```Golang + 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: + +1. ```utls.HelloRandomized``` adds/reorders extensions, ciphersuites, etc. randomly. +`HelloRandomized` adds ALPN in 50% 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. + * `utls.HelloChrome_Auto`- parrots recommended(latest) Google Chrome version + * `utls.HelloChrome_58` - parrots Google Chrome 58 + * `utls.HelloFirefox_Auto` - parrots recommended(latest) Firefox version + * `utls.HelloFirefox_53_WIP` - parrots Firefox 53 (Work in progress!) + * `utls.HelloAndroid_Auto` + * `utls.HelloAndroid_6_0_Browser` + * `utls.HelloAndroid_5_1_Browser` + +##### Customizing handshake + +Before doing `Handshake()` you can also set fake session ticket, set clientHello or change uconn in other ways: +```Golang + 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) +``` diff --git a/cipher_suites.go b/cipher_suites.go index beb0f19..89979ab 100644 --- a/cipher_suites.go +++ b/cipher_suites.go @@ -15,7 +15,7 @@ import ( "crypto/x509" "hash" - "golang_org/x/crypto/chacha20poly1305" + "golang.org/x/crypto/chacha20poly1305" ) // a keyAgreement implements the client and server side of a TLS key agreement diff --git a/cipherhw/asm_amd64.s b/cipherhw/asm_amd64.s new file mode 100644 index 0000000..dd1afd4 --- /dev/null +++ b/cipherhw/asm_amd64.s @@ -0,0 +1,17 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build amd64,!gccgo,!appengine + +#include "textflag.h" + +// func hasAESNI() bool +TEXT ·hasAESNI(SB),NOSPLIT,$0 + XORQ AX, AX + INCL AX + CPUID + SHRQ $25, CX + ANDQ $1, CX + MOVB CX, ret+0(FP) + RET diff --git a/cipherhw/asm_s390x.s b/cipherhw/asm_s390x.s new file mode 100644 index 0000000..51dc1c6 --- /dev/null +++ b/cipherhw/asm_s390x.s @@ -0,0 +1,44 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build s390x,!gccgo,!appengine + +#include "textflag.h" + +// func hasHWSupport() bool +TEXT ·hasHWSupport(SB),NOSPLIT,$16-1 + XOR R0, R0 // set function code to 0 (query) + LA mask-16(SP), R1 // 16-byte stack variable for mask + MOVD $(0x38<<40), R3 // mask for bits 18-20 (big endian) + + // check for KM AES functions + WORD $0xB92E0024 // cipher message (KM) + MOVD mask-16(SP), R2 + AND R3, R2 + CMPBNE R2, R3, notfound + + // check for KMC AES functions + WORD $0xB92F0024 // cipher message with chaining (KMC) + MOVD mask-16(SP), R2 + AND R3, R2 + CMPBNE R2, R3, notfound + + // check for KMCTR AES functions + WORD $0xB92D4024 // cipher message with counter (KMCTR) + MOVD mask-16(SP), R2 + AND R3, R2 + CMPBNE R2, R3, notfound + + // check for KIMD GHASH function + WORD $0xB93E0024 // compute intermediate message digest (KIMD) + MOVD mask-8(SP), R2 // bits 64-127 + MOVD $(1<<62), R5 + AND R5, R2 + CMPBNE R2, R5, notfound + + MOVB $1, ret+0(FP) + RET +notfound: + MOVB $0, ret+0(FP) + RET diff --git a/cipherhw/cipherhw_amd64.go b/cipherhw/cipherhw_amd64.go new file mode 100644 index 0000000..be0d490 --- /dev/null +++ b/cipherhw/cipherhw_amd64.go @@ -0,0 +1,16 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build amd64,!gccgo,!appengine + +package cipherhw + +// defined in asm_amd64.s +func hasAESNI() bool + +// AESGCMSupport returns true if the Go standard library supports AES-GCM in +// hardware. +func AESGCMSupport() bool { + return hasAESNI() +} diff --git a/cipherhw/cipherhw_s390x.go b/cipherhw/cipherhw_s390x.go new file mode 100644 index 0000000..9cd7679 --- /dev/null +++ b/cipherhw/cipherhw_s390x.go @@ -0,0 +1,18 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build s390x,!gccgo,!appengine + +package cipherhw + +// hasHWSupport reports whether the AES-128, AES-192 and AES-256 cipher message +// (KM) function codes are supported. Note that this function is expensive. +// defined in asm_s390x.s +func hasHWSupport() bool + +var hwSupport = hasHWSupport() + +func AESGCMSupport() bool { + return hwSupport +} diff --git a/cipherhw/doc.go b/cipherhw/doc.go new file mode 100644 index 0000000..a75fcf6 --- /dev/null +++ b/cipherhw/doc.go @@ -0,0 +1,7 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package cipherhw exposes common functions for detecting whether hardware +// support for certain ciphers and authenticators is present. +package cipherhw diff --git a/cipherhw/generic.go b/cipherhw/generic.go new file mode 100644 index 0000000..64d90d3 --- /dev/null +++ b/cipherhw/generic.go @@ -0,0 +1,11 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !amd64,!s390x gccgo appengine + +package cipherhw + +func AESGCMSupport() bool { + return false +} diff --git a/common.go b/common.go index 5860838..1166c93 100644 --- a/common.go +++ b/common.go @@ -7,12 +7,12 @@ package tls import ( "container/list" "crypto" - "crypto/internal/cipherhw" "crypto/rand" "crypto/sha512" "crypto/x509" "errors" "fmt" + "github.com/Jigsaw-Code/utls/cipherhw" "io" "math/big" "net" diff --git a/examples/examples.go b/examples/examples.go new file mode 100644 index 0000000..7dfe86a --- /dev/null +++ b/examples/examples.go @@ -0,0 +1,224 @@ +package main + +import ( + "encoding/hex" + "fmt" + tls "github.com/Jigsaw-Code/utls" + "net" + "strings" + "time" +) + +var ( + dialTimeout = time.Duration(15) * time.Second + sessionTicket = []uint8(`Here goes phony session ticket: phony enough to get into ASCII range +Ticket could be of any length, but for camouflage purposes it's better to use uniformly random contents +and standard length such as 228`) +) + +func HttpGetDefault(hostname string, addr string) (string, error) { + config := tls.Config{ServerName: hostname} + dialConn, err := net.DialTimeout("tcp", addr, dialTimeout) + if err != nil { + return "", fmt.Errorf("net.DialTimeout error: %+v", err) + } + tlsConn := tls.Client(dialConn, &config) + defer tlsConn.Close() + + err = tlsConn.Handshake() + if err != nil { + return "", fmt.Errorf("tlsConn.Handshake() error: %+v", err) + } + tlsConn.Write([]byte("GET / HTTP/1.1\r\nHost: " + hostname + "\r\n\r\n")) + buf := make([]byte, 14096) + tlsConn.Read(buf) + return string(buf), nil +} + +func HttpGetByHelloID(hostname string, addr string, helloID tls.ClientHelloID) (string, error) { + config := tls.Config{ServerName: hostname} + dialConn, err := net.DialTimeout("tcp", addr, dialTimeout) + if err != nil { + return "", fmt.Errorf("net.DialTimeout error: %+v", err) + } + uTlsConn := tls.UClient(dialConn, &config, helloID) + defer uTlsConn.Close() + + err = uTlsConn.Handshake() + if err != nil { + return "", fmt.Errorf("uTlsConn.Handshake() error: %+v", err) + } + uTlsConn.Write([]byte("GET / HTTP/1.1\r\nHost: " + hostname + "\r\n\r\n")) + buf := make([]byte, 14096) + uTlsConn.Read(buf) + return string(buf), nil +} + +func HttpGetExplicitRandom(hostname string, addr string) (string, error) { + dialConn, err := net.DialTimeout("tcp", addr, dialTimeout) + if err != nil { + return "", fmt.Errorf("net.DialTimeout error: %+v", err) + } + uTlsConn := tls.UClient(dialConn, nil, tls.HelloGolang) + defer uTlsConn.Close() + + uTlsConn.SetSNI(hostname) // have to set SNI, if config was nil + err = uTlsConn.BuildHandshakeState() + if err != nil { + // have to call BuildHandshakeState() first, when using default UClient, to avoid settings' overwriting + return "", fmt.Errorf("uTlsConn.BuildHandshakeState() error: %+v", err) + } + + 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} + uTlsConn.SetClientRandom(cRandom) + err = uTlsConn.Handshake() + if err != nil { + return "", fmt.Errorf("uTlsConn.Handshake() error: %+v", err) + } + // These fields are accessible regardless of setting client hello explicitly + fmt.Printf("#> MasterSecret:\n%s", hex.Dump(uTlsConn.HandshakeState.MasterSecret)) + fmt.Printf("#> ClientHello Random:\n%s", hex.Dump(uTlsConn.HandshakeState.Hello.Random)) + fmt.Printf("#> ServerHello Random:\n%s", hex.Dump(uTlsConn.HandshakeState.ServerHello.Random)) + + uTlsConn.Write([]byte("GET / HTTP/1.1\r\nHost: " + hostname + "\r\n\r\n")) + buf := make([]byte, 14096) + uTlsConn.Read(buf) + return string(buf), nil +} + +// Note that the server will reject the fake ticket(unless you set up your server to accept them) and do full handshake +func HttpGetTicket(hostname string, addr string) (string, error) { + config := tls.Config{ServerName: hostname} + dialConn, err := net.DialTimeout("tcp", addr, dialTimeout) + if err != nil { + return "", fmt.Errorf("net.DialTimeout error: %+v", err) + } + uTlsConn := tls.UClient(dialConn, &config, tls.HelloGolang) + defer uTlsConn.Close() + + err = uTlsConn.BuildHandshakeState() + if err != nil { + // have to call BuildHandshakeState() first, when using default UClient, to avoid settings' overwriting + return "", fmt.Errorf("uTlsConn.BuildHandshakeState() error: %+v", err) + } + + 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 := tls.MakeClientSessionState(sessionTicket, uint16(tls.VersionTLS12), + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + masterSecret, + nil, nil) + + uTlsConn.SetSessionState(sessionState) + + err = uTlsConn.Handshake() + if err != nil { + return "", fmt.Errorf("uTlsConn.Handshake() error: %+v", err) + } + fmt.Println("#> This is how client hello with session ticket looked:") + fmt.Print(hex.Dump(uTlsConn.HandshakeState.Hello.Raw)) + + uTlsConn.Write([]byte("GET / HTTP/1.1\r\nHost: " + hostname + "\r\n\r\n")) + buf := make([]byte, 14096) + uTlsConn.Read(buf) + return string(buf), nil +} + +// Note that the server will reject the fake ticket(unless you set up your server to accept them) and do full handshake +func HttpGetTicketHelloID(hostname string, addr string, helloID tls.ClientHelloID) (string, error) { + config := tls.Config{ServerName: hostname} + dialConn, err := net.DialTimeout("tcp", addr, dialTimeout) + if err != nil { + return "", fmt.Errorf("net.DialTimeout error: %+v", err) + } + uTlsConn := tls.UClient(dialConn, &config, helloID) + defer uTlsConn.Close() + + 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 := tls.MakeClientSessionState(sessionTicket, uint16(tls.VersionTLS12), + tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + masterSecret, + nil, nil) + + uTlsConn.SetSessionState(sessionState) + err = uTlsConn.Handshake() + if err != nil { + return "", fmt.Errorf("uTlsConn.Handshake() error: %+v", err) + } + + fmt.Println("#> This is how client hello with session ticket looked:") + fmt.Print(hex.Dump(uTlsConn.HandshakeState.Hello.Raw)) + + uTlsConn.Write([]byte("GET / HTTP/1.1\r\nHost: " + hostname + "\r\n\r\n")) + buf := make([]byte, 14096) + uTlsConn.Read(buf) + return string(buf), nil +} + +func main() { + var response string + var err error + requestHostname := "www.google.com" + requestAddr := "172.217.11.46:443" + + response, err = HttpGetDefault(requestHostname, requestAddr) + if err != nil { + fmt.Printf("#> HttpGetDefault failed: %+v\n", err) + } else { + fmt.Printf("#> HttpGetDefault response: %+s\n", getFirstLine(response)) + } + + response, err = HttpGetByHelloID(requestHostname, requestAddr, tls.HelloAndroid_5_1_Browser) + if err != nil { + fmt.Printf("#> HttpGetByHelloID(Android_5_1) failed: %+v\n", err) + } else { + fmt.Printf("#> HttpGetByHelloID(Android_5_1) response: %+s\n", getFirstLine(response)) + } + + response, err = HttpGetByHelloID(requestHostname, requestAddr, tls.HelloRandomizedNoALPN) + if err != nil { + fmt.Printf("#> HttpGetByHelloID(Randomized) failed: %+v\n", err) + } else { + fmt.Printf("#> HttpGetByHelloID(Randomized) response: %+s\n", getFirstLine(response)) + } + + response, err = HttpGetExplicitRandom(requestHostname, requestAddr) + if err != nil { + fmt.Printf("#> HttpGetExplicitRandom failed: %+v\n", err) + } else { + fmt.Printf("#> HttpGetExplicitRandom response: %+s\n", getFirstLine(response)) + } + + response, err = HttpGetTicket(requestHostname, requestAddr) + if err != nil { + fmt.Printf("#> HttpGetTicket failed: %+v\n", err) + } else { + fmt.Printf("#> HttpGetTicket response: %+s\n", getFirstLine(response)) + } + + response, err = HttpGetTicketHelloID(requestHostname, requestAddr, tls.HelloAndroid_5_1_Browser) + if err != nil { + fmt.Printf("#> HttpGetTicketHelloID(Android_5_1) failed: %+v\n", err) + } else { + fmt.Printf("#> HttpGetTicketHelloID(Android_5_1) response: %+s\n", getFirstLine(response)) + } + + return +} + +func getFirstLine(s string) string { + ss := strings.Split(s, "\r\n") + if len(ss) == 0 { + return "" + } else { + return ss[0] + } +} diff --git a/key_agreement.go b/key_agreement.go index 1b27c04..ba41749 100644 --- a/key_agreement.go +++ b/key_agreement.go @@ -17,7 +17,7 @@ import ( "io" "math/big" - "golang_org/x/crypto/curve25519" + "golang.org/x/crypto/curve25519" ) var errClientKeyExchange = errors.New("tls: invalid ClientKeyExchange message") diff --git a/prf.go b/prf.go index 5833fc1..a978616 100644 --- a/prf.go +++ b/prf.go @@ -189,6 +189,8 @@ func lookupTLSHash(hash uint8) (crypto.Hash, error) { return crypto.SHA256, nil case hashSHA384: return crypto.SHA384, nil + case disabledHashSHA512: + return crypto.SHA512, nil default: return 0, errors.New("tls: unsupported hash algorithm") } diff --git a/testdata/Client-TLSv12-UTLS-AES128-GCM-SHA256-Android-22 b/testdata/Client-TLSv12-UTLS-AES128-GCM-SHA256-Android-22 new file mode 100644 index 0000000..2f6fe6f --- /dev/null +++ b/testdata/Client-TLSv12-UTLS-AES128-GCM-SHA256-Android-22 @@ -0,0 +1,93 @@ +>>> Flow 1 (client to server) +00000000 16 03 01 00 c0 01 00 00 bc 03 03 00 00 00 00 00 |................| +00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 2a cc 14 |.............*..| +00000030 cc 13 cc 15 c0 2f c0 2b 00 9e c0 14 c0 0a 00 39 |...../.+.......9| +00000040 c0 13 c0 09 00 33 c0 11 c0 07 00 9c 00 35 00 2f |.....3.......5./| +00000050 00 05 00 04 00 0a 00 ff 01 00 00 69 00 00 00 05 |...........i....| +00000060 00 03 00 00 00 00 23 00 00 00 0d 00 16 00 14 06 |......#.........| +00000070 01 06 03 05 01 05 03 04 01 04 03 03 01 03 03 02 |................| +00000080 01 02 03 00 05 00 05 01 00 00 00 00 33 74 00 00 |............3t..| +00000090 00 12 00 00 00 10 00 1b 00 19 08 68 74 74 70 2f |...........http/| +000000a0 31 2e 31 06 73 70 64 79 2f 33 08 73 70 64 79 2f |1.1.spdy/3.spdy/| +000000b0 33 2e 31 00 0b 00 02 01 00 00 0a 00 08 00 06 00 |3.1.............| +000000c0 17 00 18 00 19 |.....| +>>> Flow 2 (server to client) +00000000 16 03 03 00 35 02 00 00 31 03 03 cb 9e 94 a0 b8 |....5...1.......| +00000010 29 60 4d d5 b3 20 b5 12 b3 9c 5c 50 7b 2e e0 93 |)`M.. ....\P{...| +00000020 7b d2 ad 74 89 b8 fe 8a 05 93 da 00 00 9c 00 00 |{..t............| +00000030 09 ff 01 00 01 00 00 23 00 00 16 03 03 02 59 0b |.......#......Y.| +00000040 00 02 55 00 02 52 00 02 4f 30 82 02 4b 30 82 01 |..U..R..O0..K0..| +00000050 b4 a0 03 02 01 02 02 09 00 e8 f0 9d 3f e2 5b ea |............?.[.| +00000060 a6 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 00 |.0...*.H........| +00000070 30 1f 31 0b 30 09 06 03 55 04 0a 13 02 47 6f 31 |0.1.0...U....Go1| +00000080 10 30 0e 06 03 55 04 03 13 07 47 6f 20 52 6f 6f |.0...U....Go Roo| +00000090 74 30 1e 17 0d 31 36 30 31 30 31 30 30 30 30 30 |t0...16010100000| +000000a0 30 5a 17 0d 32 35 30 31 30 31 30 30 30 30 30 30 |0Z..250101000000| +000000b0 5a 30 1a 31 0b 30 09 06 03 55 04 0a 13 02 47 6f |Z0.1.0...U....Go| +000000c0 31 0b 30 09 06 03 55 04 03 13 02 47 6f 30 81 9f |1.0...U....Go0..| +000000d0 30 0d 06 09 2a 86 48 86 f7 0d 01 01 01 05 00 03 |0...*.H.........| +000000e0 81 8d 00 30 81 89 02 81 81 00 db 46 7d 93 2e 12 |...0.......F}...| +000000f0 27 06 48 bc 06 28 21 ab 7e c4 b6 a2 5d fe 1e 52 |'.H..(!.~...]..R| +00000100 45 88 7a 36 47 a5 08 0d 92 42 5b c2 81 c0 be 97 |E.z6G....B[.....| +00000110 79 98 40 fb 4f 6d 14 fd 2b 13 8b c2 a5 2e 67 d8 |y.@.Om..+.....g.| +00000120 d4 09 9e d6 22 38 b7 4a 0b 74 73 2b c2 34 f1 d1 |...."8.J.ts+.4..| +00000130 93 e5 96 d9 74 7b f3 58 9f 6c 61 3c c0 b0 41 d4 |....t{.X.la<..A.| +00000140 d9 2b 2b 24 23 77 5b 1c 3b bd 75 5d ce 20 54 cf |.++$#w[.;.u]. T.| +00000150 a1 63 87 1d 1e 24 c4 f3 1d 1a 50 8b aa b6 14 43 |.c...$....P....C| +00000160 ed 97 a7 75 62 f4 14 c8 52 d7 02 03 01 00 01 a3 |...ub...R.......| +00000170 81 93 30 81 90 30 0e 06 03 55 1d 0f 01 01 ff 04 |..0..0...U......| +00000180 04 03 02 05 a0 30 1d 06 03 55 1d 25 04 16 30 14 |.....0...U.%..0.| +00000190 06 08 2b 06 01 05 05 07 03 01 06 08 2b 06 01 05 |..+.........+...| +000001a0 05 07 03 02 30 0c 06 03 55 1d 13 01 01 ff 04 02 |....0...U.......| +000001b0 30 00 30 19 06 03 55 1d 0e 04 12 04 10 9f 91 16 |0.0...U.........| +000001c0 1f 43 43 3e 49 a6 de 6d b6 80 d7 9f 60 30 1b 06 |.CC>I..m....`0..| +000001d0 03 55 1d 23 04 14 30 12 80 10 48 13 49 4d 13 7e |.U.#..0...H.IM.~| +000001e0 16 31 bb a3 01 d5 ac ab 6e 7b 30 19 06 03 55 1d |.1......n{0...U.| +000001f0 11 04 12 30 10 82 0e 65 78 61 6d 70 6c 65 2e 67 |...0...example.g| +00000200 6f 6c 61 6e 67 30 0d 06 09 2a 86 48 86 f7 0d 01 |olang0...*.H....| +00000210 01 0b 05 00 03 81 81 00 9d 30 cc 40 2b 5b 50 a0 |.........0.@+[P.| +00000220 61 cb ba e5 53 58 e1 ed 83 28 a9 58 1a a9 38 a4 |a...SX...(.X..8.| +00000230 95 a1 ac 31 5a 1a 84 66 3d 43 d3 2d d9 0b f2 97 |...1Z..f=C.-....| +00000240 df d3 20 64 38 92 24 3a 00 bc cf 9c 7d b7 40 20 |.. d8.$:....}.@ | +00000250 01 5f aa d3 16 61 09 a2 76 fd 13 c3 cc e1 0c 5c |._...a..v......\| +00000260 ee b1 87 82 f1 6c 04 ed 73 bb b3 43 77 8d 0c 1c |.....l..s..Cw...| +00000270 f1 0f a1 d8 40 83 61 c9 4c 72 2b 9d ae db 46 06 |....@.a.Lr+...F.| +00000280 06 4d f4 c1 b3 3e c0 d1 bd 42 d4 db fe 3d 13 60 |.M...>...B...=.`| +00000290 84 5c 21 d3 3b e9 fa e7 16 03 03 00 04 0e 00 00 |.\!.;...........| +000002a0 00 |.| +>>> Flow 3 (client to server) +00000000 16 03 03 00 86 10 00 00 82 00 80 b9 65 8d bf a7 |............e...| +00000010 c8 4b 79 ce 6f cb 8b 13 1c ac b9 7d 66 5e e9 ba |.Ky.o......}f^..| +00000020 1d 71 4e a9 e9 34 ae f6 64 65 90 3b d8 16 52 a2 |.qN..4..de.;..R.| +00000030 6f f4 cb 8a 13 74 a2 ee b7 27 69 b4 41 c0 90 68 |o....t...'i.A..h| +00000040 bc 02 69 e1 c6 48 4f 39 36 30 25 ca 4c 17 ce 83 |..i..HO960%.L...| +00000050 9e 08 56 e3 05 49 93 9e 2e c4 fb e6 c8 01 f1 0f |..V..I..........| +00000060 c5 70 0f 08 83 48 e9 48 ef 6e 50 8b 05 7e e5 84 |.p...H.H.nP..~..| +00000070 25 fa 55 c7 ae 31 02 27 00 ef 3f 98 86 20 12 89 |%.U..1.'..?.. ..| +00000080 91 59 28 b4 f7 d7 af d2 69 61 35 14 03 03 00 01 |.Y(.....ia5.....| +00000090 01 16 03 03 00 28 00 00 00 00 00 00 00 00 52 db |.....(........R.| +000000a0 dc 3d 1d a6 b3 40 aa ca 3c 0a 4c 1f 97 2b 55 c9 |.=...@..<.L..+U.| +000000b0 ed e6 3d c6 18 7a 6a 72 53 59 93 4d c2 6e |..=..zjrSY.M.n| +>>> Flow 4 (server to client) +00000000 16 03 03 00 aa 04 00 00 a6 00 00 1c 20 00 a0 ac |............ ...| +00000010 ff 5e 61 8a 55 04 80 bf 96 3a c2 70 9a cd 40 c3 |.^a.U....:.p..@.| +00000020 7b bb 9d 30 15 d9 bd 23 60 6a 6f 30 8b 2d 88 22 |{..0...#`jo0.-."| +00000030 0f cc 24 ee a5 a5 ea 0d a4 62 60 ff f0 42 42 59 |..$......b`..BBY| +00000040 a0 b3 56 af 67 20 60 cd 54 c3 09 05 dc 13 91 1b |..V.g `.T.......| +00000050 c8 14 51 7d 7d b2 f3 f0 fe 5d 95 cb 9e 70 62 cb |..Q}}....]...pb.| +00000060 23 8d 7d ab 17 77 96 05 9f e5 0a f2 11 cb 95 27 |#.}..w.........'| +00000070 01 dd 25 ab 56 ce df 6a 2f f5 22 44 59 3a 29 b1 |..%.V..j/."DY:).| +00000080 bf 55 e9 11 76 d0 92 9a 96 ec 60 f8 08 18 8f 0e |.U..v.....`.....| +00000090 66 fc e7 65 e9 91 e8 e9 f1 8d 66 5f b9 73 cc d8 |f..e......f_.s..| +000000a0 ab 8b e0 e3 77 74 53 69 9d 4d f6 f5 a2 54 2c 14 |....wtSi.M...T,.| +000000b0 03 03 00 01 01 16 03 03 00 28 fc 68 19 6d c3 b8 |.........(.h.m..| +000000c0 fb 43 f8 53 d0 be f2 56 52 0a 94 ca 30 6a ee 2a |.C.S...VR...0j.*| +000000d0 05 a3 bd c5 d1 f7 9c 47 6f 59 12 15 0d 9c 60 b2 |.......GoY....`.| +000000e0 7d 6e |}n| +>>> Flow 5 (client to server) +00000000 17 03 03 00 1e 00 00 00 00 00 00 00 01 4c 8c 50 |.............L.P| +00000010 c0 ff dd a5 5f 99 50 46 a4 92 88 d9 15 27 4e b3 |...._.PF.....'N.| +00000020 74 bf 94 15 03 03 00 1a 00 00 00 00 00 00 00 02 |t...............| +00000030 c7 15 f6 3d 97 e6 2c de 60 69 4e 3e ed ca e7 cc |...=..,.`iN>....| +00000040 09 8f |..| diff --git a/testdata/Client-TLSv12-UTLS-ECDHE-ECDSA-AES128-GCM-SHA256-Android-22 b/testdata/Client-TLSv12-UTLS-ECDHE-ECDSA-AES128-GCM-SHA256-Android-22 new file mode 100644 index 0000000..a6f5df7 --- /dev/null +++ b/testdata/Client-TLSv12-UTLS-ECDHE-ECDSA-AES128-GCM-SHA256-Android-22 @@ -0,0 +1,98 @@ +>>> Flow 1 (client to server) +00000000 16 03 01 00 c0 01 00 00 bc 03 03 00 00 00 00 00 |................| +00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 2a cc 14 |.............*..| +00000030 cc 13 cc 15 c0 2f c0 2b 00 9e c0 14 c0 0a 00 39 |...../.+.......9| +00000040 c0 13 c0 09 00 33 c0 11 c0 07 00 9c 00 35 00 2f |.....3.......5./| +00000050 00 05 00 04 00 0a 00 ff 01 00 00 69 00 00 00 05 |...........i....| +00000060 00 03 00 00 00 00 23 00 00 00 0d 00 16 00 14 06 |......#.........| +00000070 01 06 03 05 01 05 03 04 01 04 03 03 01 03 03 02 |................| +00000080 01 02 03 00 05 00 05 01 00 00 00 00 33 74 00 00 |............3t..| +00000090 00 12 00 00 00 10 00 1b 00 19 08 68 74 74 70 2f |...........http/| +000000a0 31 2e 31 06 73 70 64 79 2f 33 08 73 70 64 79 2f |1.1.spdy/3.spdy/| +000000b0 33 2e 31 00 0b 00 02 01 00 00 0a 00 08 00 06 00 |3.1.............| +000000c0 17 00 18 00 19 |.....| +>>> Flow 2 (server to client) +00000000 16 03 03 00 3d 02 00 00 39 03 03 74 61 47 f3 35 |....=...9..taG.5| +00000010 bb c9 02 ae 3f 28 de bb 86 56 7c ce 01 37 bc 6b |....?(...V|..7.k| +00000020 37 bf 72 46 6b 21 c8 6c 47 c6 04 00 c0 2b 00 00 |7.rFk!.lG....+..| +00000030 11 ff 01 00 01 00 00 0b 00 04 03 00 01 02 00 23 |...............#| +00000040 00 00 16 03 03 02 0e 0b 00 02 0a 00 02 07 00 02 |................| +00000050 04 30 82 02 00 30 82 01 62 02 09 00 b8 bf 2d 47 |.0...0..b.....-G| +00000060 a0 d2 eb f4 30 09 06 07 2a 86 48 ce 3d 04 01 30 |....0...*.H.=..0| +00000070 45 31 0b 30 09 06 03 55 04 06 13 02 41 55 31 13 |E1.0...U....AU1.| +00000080 30 11 06 03 55 04 08 13 0a 53 6f 6d 65 2d 53 74 |0...U....Some-St| +00000090 61 74 65 31 21 30 1f 06 03 55 04 0a 13 18 49 6e |ate1!0...U....In| +000000a0 74 65 72 6e 65 74 20 57 69 64 67 69 74 73 20 50 |ternet Widgits P| +000000b0 74 79 20 4c 74 64 30 1e 17 0d 31 32 31 31 32 32 |ty Ltd0...121122| +000000c0 31 35 30 36 33 32 5a 17 0d 32 32 31 31 32 30 31 |150632Z..2211201| +000000d0 35 30 36 33 32 5a 30 45 31 0b 30 09 06 03 55 04 |50632Z0E1.0...U.| +000000e0 06 13 02 41 55 31 13 30 11 06 03 55 04 08 13 0a |...AU1.0...U....| +000000f0 53 6f 6d 65 2d 53 74 61 74 65 31 21 30 1f 06 03 |Some-State1!0...| +00000100 55 04 0a 13 18 49 6e 74 65 72 6e 65 74 20 57 69 |U....Internet Wi| +00000110 64 67 69 74 73 20 50 74 79 20 4c 74 64 30 81 9b |dgits Pty Ltd0..| +00000120 30 10 06 07 2a 86 48 ce 3d 02 01 06 05 2b 81 04 |0...*.H.=....+..| +00000130 00 23 03 81 86 00 04 00 c4 a1 ed be 98 f9 0b 48 |.#.............H| +00000140 73 36 7e c3 16 56 11 22 f2 3d 53 c3 3b 4d 21 3d |s6~..V.".=S.;M!=| +00000150 cd 6b 75 e6 f6 b0 dc 9a df 26 c1 bc b2 87 f0 72 |.ku......&.....r| +00000160 32 7c b3 64 2f 1c 90 bc ea 68 23 10 7e fe e3 25 |2|.d/....h#.~..%| +00000170 c0 48 3a 69 e0 28 6d d3 37 00 ef 04 62 dd 0d a0 |.H:i.(m.7...b...| +00000180 9c 70 62 83 d8 81 d3 64 31 aa 9e 97 31 bd 96 b0 |.pb....d1...1...| +00000190 68 c0 9b 23 de 76 64 3f 1a 5c 7f e9 12 0e 58 58 |h..#.vd?.\....XX| +000001a0 b6 5f 70 dd 9b d8 ea d5 d7 f5 d5 cc b9 b6 9f 30 |._p............0| +000001b0 66 5b 66 9a 20 e2 27 e5 bf fe 3b 30 09 06 07 2a |f[f. .'...;0...*| +000001c0 86 48 ce 3d 04 01 03 81 8c 00 30 81 88 02 42 01 |.H.=......0...B.| +000001d0 88 a2 4f eb e2 45 c5 48 7d 1b ac f5 ed 98 9d ae |..O..E.H}.......| +000001e0 47 70 c0 5e 1b b6 2f bd f1 b6 4d b7 61 40 d3 11 |Gp.^../...M.a@..| +000001f0 a2 ce ee 0b 7e 92 7e ff 76 9d c3 3b 7e a5 3f ce |....~.~.v..;~.?.| +00000200 fa 10 e2 59 ec 47 2d 7c ac da 4e 97 0e 15 a0 6f |...Y.G-|..N....o| +00000210 d0 02 42 01 4d fc be 67 13 9c 2d 05 0e bd 3f a3 |..B.M..g..-...?.| +00000220 8c 25 c1 33 13 83 0d 94 06 bb d4 37 7a f6 ec 7a |.%.3.......7z..z| +00000230 c9 86 2e dd d7 11 69 7f 85 7c 56 de fb 31 78 2b |......i..|V..1x+| +00000240 e4 c7 78 0d ae cb be 9e 4e 36 24 31 7b 6a 0f 39 |..x.....N6$1{j.9| +00000250 95 12 07 8f 2a 16 03 03 00 d7 0c 00 00 d3 03 00 |....*...........| +00000260 17 41 04 32 22 ad 08 03 13 30 89 c0 79 a9 cd 4e |.A.2"....0..y..N| +00000270 b1 6b 6c 20 c1 80 04 2a d2 4f 74 52 b2 4d 21 71 |.kl ...*.OtR.M!q| +00000280 82 5d 31 5a 72 f4 7c 78 da d7 2d e8 55 9a 87 6d |.]1Zr.|x..-.U..m| +00000290 43 e7 13 f1 0e 00 25 e9 e4 ac 45 cd 82 7e 84 28 |C.....%...E..~.(| +000002a0 e2 b4 84 06 03 00 8a 30 81 87 02 42 00 dd 86 fa |.......0...B....| +000002b0 7a a6 8c b3 c6 73 f7 b5 f8 ba a7 16 6d d9 09 ea |z....s......m...| +000002c0 cd dd 82 83 44 0c 3f b1 0f 23 a3 61 76 eb eb 5a |....D.?..#.av..Z| +000002d0 88 d5 65 31 3f ba 81 84 0c 35 17 4f 2f 02 fb 8d |..e1?....5.O/...| +000002e0 21 1b c1 de 42 40 d8 a9 84 4a 44 f1 0c 3c 02 41 |!...B@...JD..<.A| +000002f0 76 8e 43 e9 c4 f7 47 68 03 23 6d 36 8e 9d 30 9a |v.C...Gh.#m6..0.| +00000300 71 6d a5 76 c0 20 d8 0e bd f6 dd 11 34 a8 e7 af |qm.v. ......4...| +00000310 85 a7 01 19 68 c8 df fa 9c 62 e1 71 b2 9b 01 83 |....h....b.q....| +00000320 8e 9b 96 e2 6d 30 71 ff 3b c4 70 16 a3 f8 59 2e |....m0q.;.p...Y.| +00000330 b8 16 03 03 00 04 0e 00 00 00 |..........| +>>> Flow 3 (client to server) +00000000 16 03 03 00 46 10 00 00 42 41 04 1e 18 37 ef 0d |....F...BA...7..| +00000010 19 51 88 35 75 71 b5 e5 54 5b 12 2e 8f 09 67 fd |.Q.5uq..T[....g.| +00000020 a7 24 20 3e b2 56 1c ce 97 28 5e f8 2b 2d 4f 9e |.$ >.V...(^.+-O.| +00000030 f1 07 9f 6c 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 |...lK[.V.2B.X..I| +00000040 a6 b5 68 1a 41 03 56 6b dc 5a 89 14 03 03 00 01 |..h.A.Vk.Z......| +00000050 01 16 03 03 00 28 00 00 00 00 00 00 00 00 de cf |.....(..........| +00000060 13 d9 c7 68 45 02 da b9 8f fc fa 22 cf f1 2c 28 |...hE......"..,(| +00000070 ad f4 41 aa ae be 48 dd 44 51 a7 84 46 2e |..A...H.DQ..F.| +>>> Flow 4 (server to client) +00000000 16 03 03 00 aa 04 00 00 a6 00 00 1c 20 00 a0 74 |............ ..t| +00000010 80 d3 58 e2 3d dc 88 e2 73 88 cf 27 03 c5 a9 b8 |..X.=...s..'....| +00000020 52 09 cc c7 07 9f c5 8e a4 35 d5 fe 52 71 29 00 |R........5..Rq).| +00000030 90 a7 b8 49 1f 17 0b e6 1c b1 4c 05 3a d1 bd 7b |...I......L.:..{| +00000040 c1 1e 24 6e 5f 2e 57 c3 8f 40 a4 a4 e9 05 cb 89 |..$n_.W..@......| +00000050 1a 27 88 ee 20 7e 0f 3c 36 cc bb 1a 9a 5b 57 41 |.'.. ~.<6....[WA| +00000060 c5 a5 c8 ac b8 7b dc 38 04 38 8e 81 7a fd ad e8 |.....{.8.8..z...| +00000070 d4 17 29 70 92 bc 8d 95 1d 8d 11 85 75 e6 1b 4a |..)p........u..J| +00000080 73 b7 75 da ac 63 59 ea 50 e9 ad 18 4f 2f f6 b3 |s.u..cY.P...O/..| +00000090 c3 d7 46 5f f3 c3 de 08 b9 e8 b2 f9 99 33 ef 3d |..F_.........3.=| +000000a0 87 10 5d f0 26 22 4f 34 10 d1 b5 5c eb 46 44 14 |..].&"O4...\.FD.| +000000b0 03 03 00 01 01 16 03 03 00 28 d5 de 79 dc a4 08 |.........(..y...| +000000c0 ef 79 7f 0b e8 dd 11 71 5b 13 9a de 97 d4 35 59 |.y.....q[.....5Y| +000000d0 a0 eb f4 9e 6f 01 a2 4f cc 59 ea 57 dc 56 44 09 |....o..O.Y.W.VD.| +000000e0 2a 92 |*.| +>>> Flow 5 (client to server) +00000000 17 03 03 00 1e 00 00 00 00 00 00 00 01 c6 26 13 |..............&.| +00000010 84 33 41 b2 e6 ab cf d8 b7 bd 7d a2 72 69 05 0d |.3A.......}.ri..| +00000020 8f a3 4c 15 03 03 00 1a 00 00 00 00 00 00 00 02 |..L.............| +00000030 41 d1 25 7e 36 4d 9c 53 38 a6 16 45 67 c5 66 4d |A.%~6M.S8..Eg.fM| +00000040 ff b0 |..| diff --git a/testdata/Client-TLSv12-UTLS-ECDHE-ECDSA-AES128-SHA-Android-22 b/testdata/Client-TLSv12-UTLS-ECDHE-ECDSA-AES128-SHA-Android-22 new file mode 100644 index 0000000..d5976ef --- /dev/null +++ b/testdata/Client-TLSv12-UTLS-ECDHE-ECDSA-AES128-SHA-Android-22 @@ -0,0 +1,103 @@ +>>> Flow 1 (client to server) +00000000 16 03 01 00 c0 01 00 00 bc 03 03 00 00 00 00 00 |................| +00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 2a cc 14 |.............*..| +00000030 cc 13 cc 15 c0 2f c0 2b 00 9e c0 14 c0 0a 00 39 |...../.+.......9| +00000040 c0 13 c0 09 00 33 c0 11 c0 07 00 9c 00 35 00 2f |.....3.......5./| +00000050 00 05 00 04 00 0a 00 ff 01 00 00 69 00 00 00 05 |...........i....| +00000060 00 03 00 00 00 00 23 00 00 00 0d 00 16 00 14 06 |......#.........| +00000070 01 06 03 05 01 05 03 04 01 04 03 03 01 03 03 02 |................| +00000080 01 02 03 00 05 00 05 01 00 00 00 00 33 74 00 00 |............3t..| +00000090 00 12 00 00 00 10 00 1b 00 19 08 68 74 74 70 2f |...........http/| +000000a0 31 2e 31 06 73 70 64 79 2f 33 08 73 70 64 79 2f |1.1.spdy/3.spdy/| +000000b0 33 2e 31 00 0b 00 02 01 00 00 0a 00 08 00 06 00 |3.1.............| +000000c0 17 00 18 00 19 |.....| +>>> Flow 2 (server to client) +00000000 16 03 03 00 3d 02 00 00 39 03 03 05 b2 2d 0b 47 |....=...9....-.G| +00000010 a6 e2 54 d4 e1 9b 7a 41 4f ea 60 5a 1f fd fe 38 |..T...zAO.`Z...8| +00000020 42 3f a3 26 f5 6c 6e 09 eb ee 04 00 c0 09 00 00 |B?.&.ln.........| +00000030 11 ff 01 00 01 00 00 0b 00 04 03 00 01 02 00 23 |...............#| +00000040 00 00 16 03 03 02 0e 0b 00 02 0a 00 02 07 00 02 |................| +00000050 04 30 82 02 00 30 82 01 62 02 09 00 b8 bf 2d 47 |.0...0..b.....-G| +00000060 a0 d2 eb f4 30 09 06 07 2a 86 48 ce 3d 04 01 30 |....0...*.H.=..0| +00000070 45 31 0b 30 09 06 03 55 04 06 13 02 41 55 31 13 |E1.0...U....AU1.| +00000080 30 11 06 03 55 04 08 13 0a 53 6f 6d 65 2d 53 74 |0...U....Some-St| +00000090 61 74 65 31 21 30 1f 06 03 55 04 0a 13 18 49 6e |ate1!0...U....In| +000000a0 74 65 72 6e 65 74 20 57 69 64 67 69 74 73 20 50 |ternet Widgits P| +000000b0 74 79 20 4c 74 64 30 1e 17 0d 31 32 31 31 32 32 |ty Ltd0...121122| +000000c0 31 35 30 36 33 32 5a 17 0d 32 32 31 31 32 30 31 |150632Z..2211201| +000000d0 35 30 36 33 32 5a 30 45 31 0b 30 09 06 03 55 04 |50632Z0E1.0...U.| +000000e0 06 13 02 41 55 31 13 30 11 06 03 55 04 08 13 0a |...AU1.0...U....| +000000f0 53 6f 6d 65 2d 53 74 61 74 65 31 21 30 1f 06 03 |Some-State1!0...| +00000100 55 04 0a 13 18 49 6e 74 65 72 6e 65 74 20 57 69 |U....Internet Wi| +00000110 64 67 69 74 73 20 50 74 79 20 4c 74 64 30 81 9b |dgits Pty Ltd0..| +00000120 30 10 06 07 2a 86 48 ce 3d 02 01 06 05 2b 81 04 |0...*.H.=....+..| +00000130 00 23 03 81 86 00 04 00 c4 a1 ed be 98 f9 0b 48 |.#.............H| +00000140 73 36 7e c3 16 56 11 22 f2 3d 53 c3 3b 4d 21 3d |s6~..V.".=S.;M!=| +00000150 cd 6b 75 e6 f6 b0 dc 9a df 26 c1 bc b2 87 f0 72 |.ku......&.....r| +00000160 32 7c b3 64 2f 1c 90 bc ea 68 23 10 7e fe e3 25 |2|.d/....h#.~..%| +00000170 c0 48 3a 69 e0 28 6d d3 37 00 ef 04 62 dd 0d a0 |.H:i.(m.7...b...| +00000180 9c 70 62 83 d8 81 d3 64 31 aa 9e 97 31 bd 96 b0 |.pb....d1...1...| +00000190 68 c0 9b 23 de 76 64 3f 1a 5c 7f e9 12 0e 58 58 |h..#.vd?.\....XX| +000001a0 b6 5f 70 dd 9b d8 ea d5 d7 f5 d5 cc b9 b6 9f 30 |._p............0| +000001b0 66 5b 66 9a 20 e2 27 e5 bf fe 3b 30 09 06 07 2a |f[f. .'...;0...*| +000001c0 86 48 ce 3d 04 01 03 81 8c 00 30 81 88 02 42 01 |.H.=......0...B.| +000001d0 88 a2 4f eb e2 45 c5 48 7d 1b ac f5 ed 98 9d ae |..O..E.H}.......| +000001e0 47 70 c0 5e 1b b6 2f bd f1 b6 4d b7 61 40 d3 11 |Gp.^../...M.a@..| +000001f0 a2 ce ee 0b 7e 92 7e ff 76 9d c3 3b 7e a5 3f ce |....~.~.v..;~.?.| +00000200 fa 10 e2 59 ec 47 2d 7c ac da 4e 97 0e 15 a0 6f |...Y.G-|..N....o| +00000210 d0 02 42 01 4d fc be 67 13 9c 2d 05 0e bd 3f a3 |..B.M..g..-...?.| +00000220 8c 25 c1 33 13 83 0d 94 06 bb d4 37 7a f6 ec 7a |.%.3.......7z..z| +00000230 c9 86 2e dd d7 11 69 7f 85 7c 56 de fb 31 78 2b |......i..|V..1x+| +00000240 e4 c7 78 0d ae cb be 9e 4e 36 24 31 7b 6a 0f 39 |..x.....N6$1{j.9| +00000250 95 12 07 8f 2a 16 03 03 00 d7 0c 00 00 d3 03 00 |....*...........| +00000260 17 41 04 c6 83 33 2b 47 c8 d2 a0 60 b9 6d 8e e8 |.A...3+G...`.m..| +00000270 b2 6a 81 54 56 42 3c c9 72 17 7e a7 4c 5c 55 b9 |.j.TVB<.r.~.L\U.| +00000280 65 f2 e8 3c 11 5a a1 06 75 ed b1 27 f7 42 88 5c |e..<.Z..u..'.B.\| +00000290 a0 cb df 9d 0d fc 47 2d f3 b2 6b c6 92 9f 68 f9 |......G-..k...h.| +000002a0 14 a3 df 06 03 00 8a 30 81 87 02 41 54 1e 82 48 |.......0...AT..H| +000002b0 9f eb 40 47 20 81 25 ad b7 59 3b c6 97 0b 8d 1a |..@G .%..Y;.....| +000002c0 40 97 dd 4a 3f 76 f6 d8 65 29 b7 a9 06 57 33 cb |@..J?v..e)...W3.| +000002d0 a1 ef cf 02 55 17 81 ad 89 c1 9f 6b a0 23 f9 62 |....U......k.#.b| +000002e0 4c d4 07 68 91 fe 6e c0 8d 40 eb 25 4a 02 42 01 |L..h..n..@.%J.B.| +000002f0 18 94 e9 e1 41 ec 4b 84 8f 58 2d 3d 39 81 f2 e5 |....A.K..X-=9...| +00000300 9b 9e d9 ab 5e 60 34 f1 67 12 6b 66 92 d3 6f 45 |....^`4.g.kf..oE| +00000310 d7 5a ed a2 2a a5 80 4b 76 04 76 41 c2 95 24 8a |.Z..*..Kv.vA..$.| +00000320 bd 1b 53 06 ff d9 d0 c1 de ac c2 22 d1 1a c4 84 |..S........"....| +00000330 a1 16 03 03 00 04 0e 00 00 00 |..........| +>>> Flow 3 (client to server) +00000000 16 03 03 00 46 10 00 00 42 41 04 1e 18 37 ef 0d |....F...BA...7..| +00000010 19 51 88 35 75 71 b5 e5 54 5b 12 2e 8f 09 67 fd |.Q.5uq..T[....g.| +00000020 a7 24 20 3e b2 56 1c ce 97 28 5e f8 2b 2d 4f 9e |.$ >.V...(^.+-O.| +00000030 f1 07 9f 6c 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 |...lK[.V.2B.X..I| +00000040 a6 b5 68 1a 41 03 56 6b dc 5a 89 14 03 03 00 01 |..h.A.Vk.Z......| +00000050 01 16 03 03 00 40 00 00 00 00 00 00 00 00 00 00 |.....@..........| +00000060 00 00 00 00 00 00 5d 20 87 20 34 da 31 2e 3b 9b |......] . 4.1.;.| +00000070 ba 60 10 1c 41 39 2e 09 a9 1e 9b c5 57 e6 30 2b |.`..A9......W.0+| +00000080 11 bb 00 a7 b3 26 61 58 49 2b 7d 36 2f fc 91 47 |.....&aXI+}6/..G| +00000090 8e 34 ad a0 f0 70 |.4...p| +>>> Flow 4 (server to client) +00000000 16 03 03 00 aa 04 00 00 a6 00 00 1c 20 00 a0 2f |............ ../| +00000010 af 2f 7d 53 be 3c 62 3b 22 82 14 12 58 1c d6 54 |./}S.>> Flow 5 (client to server) +00000000 17 03 03 00 30 00 00 00 00 00 00 00 00 00 00 00 |....0...........| +00000010 00 00 00 00 00 80 ce c3 5d bb 87 a7 c6 63 96 65 |........]....c.e| +00000020 89 0c c1 73 3d 50 b9 14 6c 3f 50 87 09 bc 95 8b |...s=P..l?P.....| +00000030 cd 5b e3 bb 5d 15 03 03 00 30 00 00 00 00 00 00 |.[..]....0......| +00000040 00 00 00 00 00 00 00 00 00 00 73 c7 6a a9 68 9f |..........s.j.h.| +00000050 3e 98 99 da b3 7b aa 82 f7 59 04 cd 14 21 8d f4 |>....{...Y...!..| +00000060 1e 83 b8 f4 4d 7b 4e e9 95 eb |....M{N...| diff --git a/testdata/Client-TLSv12-UTLS-ECDHE-ECDSA-AES256-SHA-Android-22 b/testdata/Client-TLSv12-UTLS-ECDHE-ECDSA-AES256-SHA-Android-22 new file mode 100644 index 0000000..3b7d7bf --- /dev/null +++ b/testdata/Client-TLSv12-UTLS-ECDHE-ECDSA-AES256-SHA-Android-22 @@ -0,0 +1,103 @@ +>>> Flow 1 (client to server) +00000000 16 03 01 00 c0 01 00 00 bc 03 03 00 00 00 00 00 |................| +00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 2a cc 14 |.............*..| +00000030 cc 13 cc 15 c0 2f c0 2b 00 9e c0 14 c0 0a 00 39 |...../.+.......9| +00000040 c0 13 c0 09 00 33 c0 11 c0 07 00 9c 00 35 00 2f |.....3.......5./| +00000050 00 05 00 04 00 0a 00 ff 01 00 00 69 00 00 00 05 |...........i....| +00000060 00 03 00 00 00 00 23 00 00 00 0d 00 16 00 14 06 |......#.........| +00000070 01 06 03 05 01 05 03 04 01 04 03 03 01 03 03 02 |................| +00000080 01 02 03 00 05 00 05 01 00 00 00 00 33 74 00 00 |............3t..| +00000090 00 12 00 00 00 10 00 1b 00 19 08 68 74 74 70 2f |...........http/| +000000a0 31 2e 31 06 73 70 64 79 2f 33 08 73 70 64 79 2f |1.1.spdy/3.spdy/| +000000b0 33 2e 31 00 0b 00 02 01 00 00 0a 00 08 00 06 00 |3.1.............| +000000c0 17 00 18 00 19 |.....| +>>> Flow 2 (server to client) +00000000 16 03 03 00 3d 02 00 00 39 03 03 98 7e 56 60 8a |....=...9...~V`.| +00000010 9a d9 bc 4f 7c 55 c4 3a 5b 2a fa 68 aa aa 63 4e |...O|U.:[*.h..cN| +00000020 b2 d0 05 d3 da ec 3a c8 ff f7 fe 00 c0 0a 00 00 |......:.........| +00000030 11 ff 01 00 01 00 00 0b 00 04 03 00 01 02 00 23 |...............#| +00000040 00 00 16 03 03 02 0e 0b 00 02 0a 00 02 07 00 02 |................| +00000050 04 30 82 02 00 30 82 01 62 02 09 00 b8 bf 2d 47 |.0...0..b.....-G| +00000060 a0 d2 eb f4 30 09 06 07 2a 86 48 ce 3d 04 01 30 |....0...*.H.=..0| +00000070 45 31 0b 30 09 06 03 55 04 06 13 02 41 55 31 13 |E1.0...U....AU1.| +00000080 30 11 06 03 55 04 08 13 0a 53 6f 6d 65 2d 53 74 |0...U....Some-St| +00000090 61 74 65 31 21 30 1f 06 03 55 04 0a 13 18 49 6e |ate1!0...U....In| +000000a0 74 65 72 6e 65 74 20 57 69 64 67 69 74 73 20 50 |ternet Widgits P| +000000b0 74 79 20 4c 74 64 30 1e 17 0d 31 32 31 31 32 32 |ty Ltd0...121122| +000000c0 31 35 30 36 33 32 5a 17 0d 32 32 31 31 32 30 31 |150632Z..2211201| +000000d0 35 30 36 33 32 5a 30 45 31 0b 30 09 06 03 55 04 |50632Z0E1.0...U.| +000000e0 06 13 02 41 55 31 13 30 11 06 03 55 04 08 13 0a |...AU1.0...U....| +000000f0 53 6f 6d 65 2d 53 74 61 74 65 31 21 30 1f 06 03 |Some-State1!0...| +00000100 55 04 0a 13 18 49 6e 74 65 72 6e 65 74 20 57 69 |U....Internet Wi| +00000110 64 67 69 74 73 20 50 74 79 20 4c 74 64 30 81 9b |dgits Pty Ltd0..| +00000120 30 10 06 07 2a 86 48 ce 3d 02 01 06 05 2b 81 04 |0...*.H.=....+..| +00000130 00 23 03 81 86 00 04 00 c4 a1 ed be 98 f9 0b 48 |.#.............H| +00000140 73 36 7e c3 16 56 11 22 f2 3d 53 c3 3b 4d 21 3d |s6~..V.".=S.;M!=| +00000150 cd 6b 75 e6 f6 b0 dc 9a df 26 c1 bc b2 87 f0 72 |.ku......&.....r| +00000160 32 7c b3 64 2f 1c 90 bc ea 68 23 10 7e fe e3 25 |2|.d/....h#.~..%| +00000170 c0 48 3a 69 e0 28 6d d3 37 00 ef 04 62 dd 0d a0 |.H:i.(m.7...b...| +00000180 9c 70 62 83 d8 81 d3 64 31 aa 9e 97 31 bd 96 b0 |.pb....d1...1...| +00000190 68 c0 9b 23 de 76 64 3f 1a 5c 7f e9 12 0e 58 58 |h..#.vd?.\....XX| +000001a0 b6 5f 70 dd 9b d8 ea d5 d7 f5 d5 cc b9 b6 9f 30 |._p............0| +000001b0 66 5b 66 9a 20 e2 27 e5 bf fe 3b 30 09 06 07 2a |f[f. .'...;0...*| +000001c0 86 48 ce 3d 04 01 03 81 8c 00 30 81 88 02 42 01 |.H.=......0...B.| +000001d0 88 a2 4f eb e2 45 c5 48 7d 1b ac f5 ed 98 9d ae |..O..E.H}.......| +000001e0 47 70 c0 5e 1b b6 2f bd f1 b6 4d b7 61 40 d3 11 |Gp.^../...M.a@..| +000001f0 a2 ce ee 0b 7e 92 7e ff 76 9d c3 3b 7e a5 3f ce |....~.~.v..;~.?.| +00000200 fa 10 e2 59 ec 47 2d 7c ac da 4e 97 0e 15 a0 6f |...Y.G-|..N....o| +00000210 d0 02 42 01 4d fc be 67 13 9c 2d 05 0e bd 3f a3 |..B.M..g..-...?.| +00000220 8c 25 c1 33 13 83 0d 94 06 bb d4 37 7a f6 ec 7a |.%.3.......7z..z| +00000230 c9 86 2e dd d7 11 69 7f 85 7c 56 de fb 31 78 2b |......i..|V..1x+| +00000240 e4 c7 78 0d ae cb be 9e 4e 36 24 31 7b 6a 0f 39 |..x.....N6$1{j.9| +00000250 95 12 07 8f 2a 16 03 03 00 d6 0c 00 00 d2 03 00 |....*...........| +00000260 17 41 04 29 e7 e6 da c3 e5 0a 7a 1d 50 22 24 61 |.A.)......z.P"$a| +00000270 69 fd 96 ef 00 8b 7c a2 b9 6d 2b b3 09 0c f4 27 |i.....|..m+....'| +00000280 ad 4d 5e f1 d7 bc b2 97 5b c5 c7 15 3e c9 4e f7 |.M^.....[...>.N.| +00000290 32 ed fc 74 2b 4c b6 e8 b2 e8 33 d2 b4 33 84 c1 |2..t+L....3..3..| +000002a0 1d 29 ef 06 03 00 89 30 81 86 02 41 2b a7 bb a2 |.).....0...A+...| +000002b0 3b 22 0f ef 6f e5 44 ca 42 58 ca 25 60 47 3a 82 |;"..o.D.BX.%`G:.| +000002c0 a8 35 68 fc 83 6e 96 0e d9 11 fb 71 2a 5b e0 0f |.5h..n.....q*[..| +000002d0 ff 2e 17 13 74 8c 2c 39 1a ea d6 f0 38 4b 19 26 |....t.,9....8K.&| +000002e0 95 00 6b fa 61 b8 a9 dc 76 fc 5e da b4 02 41 6b |..k.a...v.^...Ak| +000002f0 86 39 91 a8 66 03 87 08 d5 a5 a0 46 b1 61 3a d4 |.9..f......F.a:.| +00000300 7b 1c 82 21 89 08 56 d0 d2 29 e8 51 b6 4f cc 34 |{..!..V..).Q.O.4| +00000310 f5 cd 23 b6 f0 98 3c 1a 18 79 56 30 08 ea 06 da |..#...<..yV0....| +00000320 12 ee 25 41 27 4e b1 ca 39 03 79 10 ce 31 0d ae |..%A'N..9.y..1..| +00000330 16 03 03 00 04 0e 00 00 00 |.........| +>>> Flow 3 (client to server) +00000000 16 03 03 00 46 10 00 00 42 41 04 1e 18 37 ef 0d |....F...BA...7..| +00000010 19 51 88 35 75 71 b5 e5 54 5b 12 2e 8f 09 67 fd |.Q.5uq..T[....g.| +00000020 a7 24 20 3e b2 56 1c ce 97 28 5e f8 2b 2d 4f 9e |.$ >.V...(^.+-O.| +00000030 f1 07 9f 6c 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 |...lK[.V.2B.X..I| +00000040 a6 b5 68 1a 41 03 56 6b dc 5a 89 14 03 03 00 01 |..h.A.Vk.Z......| +00000050 01 16 03 03 00 40 00 00 00 00 00 00 00 00 00 00 |.....@..........| +00000060 00 00 00 00 00 00 20 ab 93 fa 57 6c 48 73 79 5f |...... ...WlHsy_| +00000070 7a ff 50 f4 a1 9d ec 6a 79 1e 68 87 ea bb 20 0a |z.P....jy.h... .| +00000080 24 97 fa 11 bf d9 ee 4c 2d 2b 80 88 11 23 a0 2e |$......L-+...#..| +00000090 79 87 d3 45 e6 e7 |y..E..| +>>> Flow 4 (server to client) +00000000 16 03 03 00 aa 04 00 00 a6 00 00 1c 20 00 a0 49 |............ ..I| +00000010 9a 8b 29 c7 e6 08 09 1a f9 61 99 ce 4b 4b 78 2e |..)......a..KKx.| +00000020 3d 48 4c 02 d3 3a ff ae dc cb 73 aa 7f 29 aa ec |=HL..:....s..)..| +00000030 e4 76 c0 b6 62 3e e7 a1 30 fa 0a f9 4d 9b ab 59 |.v..b>..0...M..Y| +00000040 8f d0 50 ef ea bd 12 e4 ab 31 b5 21 88 62 47 0e |..P......1.!.bG.| +00000050 37 37 e3 0a 88 ab 8b 88 d3 e9 70 6b 92 b3 72 1a |77........pk..r.| +00000060 f2 9b cb 57 d2 97 69 0f a0 dd cc d0 70 0d 0a 66 |...W..i.....p..f| +00000070 82 7f ea 9b b8 10 22 0e 37 93 6b e4 97 df 5d 9e |......".7.k...].| +00000080 4b 23 b8 fb 79 ff 28 d4 ed 31 fd 9f 01 a7 21 1b |K#..y.(..1....!.| +00000090 db 8b 6f bf c3 92 53 77 24 80 b4 3c f9 7a 2e a2 |..o...Sw$..<.z..| +000000a0 09 8a 6d 6b 47 8b 8e 86 18 c0 fe 78 06 59 47 14 |..mkG......x.YG.| +000000b0 03 03 00 01 01 16 03 03 00 40 78 f8 fb c1 2c 7d |.........@x...,}| +000000c0 18 5d 8a 94 f6 e9 ea c4 6f 71 50 a6 ee 62 3a 7d |.]......oqP..b:}| +000000d0 25 ef c2 6e 11 e3 04 70 76 6b 30 97 ba a1 9d 49 |%..n...pvk0....I| +000000e0 f6 47 64 60 8a ba ea 52 16 10 8d 9a 3c 56 1b 13 |.Gd`...R....>> Flow 5 (client to server) +00000000 17 03 03 00 30 00 00 00 00 00 00 00 00 00 00 00 |....0...........| +00000010 00 00 00 00 00 5f 1b 2b 17 f6 a2 53 36 49 ad e0 |....._.+...S6I..| +00000020 15 dd 38 f0 db ed c4 c2 31 76 b2 4b 11 b9 3f 72 |..8.....1v.K..?r| +00000030 48 f6 cd 2c c9 15 03 03 00 30 00 00 00 00 00 00 |H..,.....0......| +00000040 00 00 00 00 00 00 00 00 00 00 29 31 29 ca 84 78 |..........)1)..x| +00000050 c8 ed 7d 56 4d d2 d2 0d 47 f0 5a 7c 1f aa 6c d6 |..}VM...G.Z|..l.| +00000060 3b 41 b5 26 9d 05 f3 28 2f fe |;A.&...(/.| diff --git a/testdata/Client-TLSv12-UTLS-ECDHE-RSA-AES128-GCM-SHA256-Android-22 b/testdata/Client-TLSv12-UTLS-ECDHE-RSA-AES128-GCM-SHA256-Android-22 new file mode 100644 index 0000000..eda50a6 --- /dev/null +++ b/testdata/Client-TLSv12-UTLS-ECDHE-RSA-AES128-GCM-SHA256-Android-22 @@ -0,0 +1,102 @@ +>>> Flow 1 (client to server) +00000000 16 03 01 00 c0 01 00 00 bc 03 03 00 00 00 00 00 |................| +00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 2a cc 14 |.............*..| +00000030 cc 13 cc 15 c0 2f c0 2b 00 9e c0 14 c0 0a 00 39 |...../.+.......9| +00000040 c0 13 c0 09 00 33 c0 11 c0 07 00 9c 00 35 00 2f |.....3.......5./| +00000050 00 05 00 04 00 0a 00 ff 01 00 00 69 00 00 00 05 |...........i....| +00000060 00 03 00 00 00 00 23 00 00 00 0d 00 16 00 14 06 |......#.........| +00000070 01 06 03 05 01 05 03 04 01 04 03 03 01 03 03 02 |................| +00000080 01 02 03 00 05 00 05 01 00 00 00 00 33 74 00 00 |............3t..| +00000090 00 12 00 00 00 10 00 1b 00 19 08 68 74 74 70 2f |...........http/| +000000a0 31 2e 31 06 73 70 64 79 2f 33 08 73 70 64 79 2f |1.1.spdy/3.spdy/| +000000b0 33 2e 31 00 0b 00 02 01 00 00 0a 00 08 00 06 00 |3.1.............| +000000c0 17 00 18 00 19 |.....| +>>> Flow 2 (server to client) +00000000 16 03 03 00 3d 02 00 00 39 03 03 83 de 75 c9 5f |....=...9....u._| +00000010 67 39 d4 57 19 ca d9 1c 3a ff 53 d8 aa 5a 4f 33 |g9.W....:.S..ZO3| +00000020 4e e7 aa ca 8d e4 1a cf 5a 98 e0 00 c0 2f 00 00 |N.......Z..../..| +00000030 11 ff 01 00 01 00 00 0b 00 04 03 00 01 02 00 23 |...............#| +00000040 00 00 16 03 03 02 59 0b 00 02 55 00 02 52 00 02 |......Y...U..R..| +00000050 4f 30 82 02 4b 30 82 01 b4 a0 03 02 01 02 02 09 |O0..K0..........| +00000060 00 e8 f0 9d 3f e2 5b ea a6 30 0d 06 09 2a 86 48 |....?.[..0...*.H| +00000070 86 f7 0d 01 01 0b 05 00 30 1f 31 0b 30 09 06 03 |........0.1.0...| +00000080 55 04 0a 13 02 47 6f 31 10 30 0e 06 03 55 04 03 |U....Go1.0...U..| +00000090 13 07 47 6f 20 52 6f 6f 74 30 1e 17 0d 31 36 30 |..Go Root0...160| +000000a0 31 30 31 30 30 30 30 30 30 5a 17 0d 32 35 30 31 |101000000Z..2501| +000000b0 30 31 30 30 30 30 30 30 5a 30 1a 31 0b 30 09 06 |01000000Z0.1.0..| +000000c0 03 55 04 0a 13 02 47 6f 31 0b 30 09 06 03 55 04 |.U....Go1.0...U.| +000000d0 03 13 02 47 6f 30 81 9f 30 0d 06 09 2a 86 48 86 |...Go0..0...*.H.| +000000e0 f7 0d 01 01 01 05 00 03 81 8d 00 30 81 89 02 81 |...........0....| +000000f0 81 00 db 46 7d 93 2e 12 27 06 48 bc 06 28 21 ab |...F}...'.H..(!.| +00000100 7e c4 b6 a2 5d fe 1e 52 45 88 7a 36 47 a5 08 0d |~...]..RE.z6G...| +00000110 92 42 5b c2 81 c0 be 97 79 98 40 fb 4f 6d 14 fd |.B[.....y.@.Om..| +00000120 2b 13 8b c2 a5 2e 67 d8 d4 09 9e d6 22 38 b7 4a |+.....g....."8.J| +00000130 0b 74 73 2b c2 34 f1 d1 93 e5 96 d9 74 7b f3 58 |.ts+.4......t{.X| +00000140 9f 6c 61 3c c0 b0 41 d4 d9 2b 2b 24 23 77 5b 1c |.la<..A..++$#w[.| +00000150 3b bd 75 5d ce 20 54 cf a1 63 87 1d 1e 24 c4 f3 |;.u]. T..c...$..| +00000160 1d 1a 50 8b aa b6 14 43 ed 97 a7 75 62 f4 14 c8 |..P....C...ub...| +00000170 52 d7 02 03 01 00 01 a3 81 93 30 81 90 30 0e 06 |R.........0..0..| +00000180 03 55 1d 0f 01 01 ff 04 04 03 02 05 a0 30 1d 06 |.U...........0..| +00000190 03 55 1d 25 04 16 30 14 06 08 2b 06 01 05 05 07 |.U.%..0...+.....| +000001a0 03 01 06 08 2b 06 01 05 05 07 03 02 30 0c 06 03 |....+.......0...| +000001b0 55 1d 13 01 01 ff 04 02 30 00 30 19 06 03 55 1d |U.......0.0...U.| +000001c0 0e 04 12 04 10 9f 91 16 1f 43 43 3e 49 a6 de 6d |.........CC>I..m| +000001d0 b6 80 d7 9f 60 30 1b 06 03 55 1d 23 04 14 30 12 |....`0...U.#..0.| +000001e0 80 10 48 13 49 4d 13 7e 16 31 bb a3 01 d5 ac ab |..H.IM.~.1......| +000001f0 6e 7b 30 19 06 03 55 1d 11 04 12 30 10 82 0e 65 |n{0...U....0...e| +00000200 78 61 6d 70 6c 65 2e 67 6f 6c 61 6e 67 30 0d 06 |xample.golang0..| +00000210 09 2a 86 48 86 f7 0d 01 01 0b 05 00 03 81 81 00 |.*.H............| +00000220 9d 30 cc 40 2b 5b 50 a0 61 cb ba e5 53 58 e1 ed |.0.@+[P.a...SX..| +00000230 83 28 a9 58 1a a9 38 a4 95 a1 ac 31 5a 1a 84 66 |.(.X..8....1Z..f| +00000240 3d 43 d3 2d d9 0b f2 97 df d3 20 64 38 92 24 3a |=C.-...... d8.$:| +00000250 00 bc cf 9c 7d b7 40 20 01 5f aa d3 16 61 09 a2 |....}.@ ._...a..| +00000260 76 fd 13 c3 cc e1 0c 5c ee b1 87 82 f1 6c 04 ed |v......\.....l..| +00000270 73 bb b3 43 77 8d 0c 1c f1 0f a1 d8 40 83 61 c9 |s..Cw.......@.a.| +00000280 4c 72 2b 9d ae db 46 06 06 4d f4 c1 b3 3e c0 d1 |Lr+...F..M...>..| +00000290 bd 42 d4 db fe 3d 13 60 84 5c 21 d3 3b e9 fa e7 |.B...=.`.\!.;...| +000002a0 16 03 03 00 cd 0c 00 00 c9 03 00 17 41 04 2c 27 |............A.,'| +000002b0 ff f2 bb 63 4b 54 d6 8a 89 9a f7 9f 02 6c 14 85 |...cKT.......l..| +000002c0 c2 a2 a7 cd 9d 79 5a 53 fb 4c a4 5e fc b2 9d 12 |.....yZS.L.^....| +000002d0 19 c7 63 d7 85 3b 1c 24 ce ea 76 24 c3 74 73 55 |..c..;.$..v$.tsU| +000002e0 29 fa cc 24 15 9a db f6 4d 5a 2c 2c d8 cc 06 01 |)..$....MZ,,....| +000002f0 00 80 b1 49 d5 7c 2d 46 2d c5 56 0c 74 72 a7 8f |...I.|-F-.V.tr..| +00000300 2a 7b 5f 72 55 b8 36 dd eb 04 8b f7 ca 84 b6 ce |*{_rU.6.........| +00000310 c9 a0 f4 c5 e1 7f d4 9c ff e5 20 ff eb 28 cb be |.......... ..(..| +00000320 21 cb 85 ef 5b e3 60 da 38 39 57 6b 8c 55 40 e1 |!...[.`.89Wk.U@.| +00000330 84 6a d2 87 20 31 39 17 2e 09 e9 99 51 42 97 23 |.j.. 19.....QB.#| +00000340 c8 72 5b 82 70 6a ca 0e 19 28 51 ae 94 81 c8 6a |.r[.pj...(Q....j| +00000350 fe 6d 3d 9e cd e4 55 03 84 47 c8 41 91 1c dc e4 |.m=...U..G.A....| +00000360 3b 77 44 6c 88 7e 6c 28 98 e1 57 e0 b4 fa 6c b1 |;wDl.~l(..W...l.| +00000370 2f c8 16 03 03 00 04 0e 00 00 00 |/..........| +>>> Flow 3 (client to server) +00000000 16 03 03 00 46 10 00 00 42 41 04 1e 18 37 ef 0d |....F...BA...7..| +00000010 19 51 88 35 75 71 b5 e5 54 5b 12 2e 8f 09 67 fd |.Q.5uq..T[....g.| +00000020 a7 24 20 3e b2 56 1c ce 97 28 5e f8 2b 2d 4f 9e |.$ >.V...(^.+-O.| +00000030 f1 07 9f 6c 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 |...lK[.V.2B.X..I| +00000040 a6 b5 68 1a 41 03 56 6b dc 5a 89 14 03 03 00 01 |..h.A.Vk.Z......| +00000050 01 16 03 03 00 28 00 00 00 00 00 00 00 00 01 ff |.....(..........| +00000060 f8 92 3a 63 d4 c5 e9 17 62 88 6a c3 26 72 58 76 |..:c....b.j.&rXv| +00000070 ca 63 8f 0d 1b f1 af 75 7c cd 93 35 a3 54 |.c.....u|..5.T| +>>> Flow 4 (server to client) +00000000 16 03 03 00 aa 04 00 00 a6 00 00 1c 20 00 a0 3c |............ ..<| +00000010 f5 21 c4 db 76 eb 6e c6 76 ff b0 01 de 03 ae c8 |.!..v.n.v.......| +00000020 2d d2 3a 76 a8 53 75 e1 0b 23 c8 82 23 90 f9 51 |-.:v.Su..#..#..Q| +00000030 f5 cb 47 63 38 e7 8f d7 9e e0 88 1d f3 63 b5 a2 |..Gc8........c..| +00000040 ed 27 90 02 21 df 0b e1 f3 3c b7 dd e0 d8 34 44 |.'..!....<....4D| +00000050 9f 13 6f 8a a0 01 91 ce 7e d1 3f b3 74 2c f6 3a |..o.....~.?.t,.:| +00000060 91 1b 98 86 62 d1 56 f7 3f 68 47 25 6a ed a0 ca |....b.V.?hG%j...| +00000070 c8 81 1a ae 94 15 db 3c db ee 25 dd 4c 6f 32 98 |.......<..%.Lo2.| +00000080 fa a0 bf 37 9b 9c 3b f1 18 94 4a 02 c1 d4 c6 67 |...7..;...J....g| +00000090 22 24 22 e8 d9 24 ad 7f 6c 34 37 50 3f c9 ac 45 |"$"..$..l47P?..E| +000000a0 f9 8e 88 d0 41 ff e8 8f c6 83 38 eb 9d 65 f1 14 |....A.....8..e..| +000000b0 03 03 00 01 01 16 03 03 00 28 03 46 d2 af 96 b2 |.........(.F....| +000000c0 2d 6a 2e 24 be 99 f2 d7 53 e8 cc 5d 33 50 19 25 |-j.$....S..]3P.%| +000000d0 0f 4d 8a ec 8d 77 d5 58 67 be cd b6 80 5c f8 5d |.M...w.Xg....\.]| +000000e0 a1 4e |.N| +>>> Flow 5 (client to server) +00000000 17 03 03 00 1e 00 00 00 00 00 00 00 01 66 61 98 |.............fa.| +00000010 10 ed c0 5d 9d 1d 0e 9f 35 c9 50 fc 4a d9 39 06 |...]....5.P.J.9.| +00000020 63 73 e7 15 03 03 00 1a 00 00 00 00 00 00 00 02 |cs..............| +00000030 5b 0c 8d f5 e0 88 97 5e ba a7 78 cc f1 f2 69 19 |[......^..x...i.| +00000040 44 83 |D.| diff --git a/testdata/Client-TLSv12-UTLS-ECDHE-RSA-AES128-SHA-Android-22 b/testdata/Client-TLSv12-UTLS-ECDHE-RSA-AES128-SHA-Android-22 new file mode 100644 index 0000000..3537e6d --- /dev/null +++ b/testdata/Client-TLSv12-UTLS-ECDHE-RSA-AES128-SHA-Android-22 @@ -0,0 +1,107 @@ +>>> Flow 1 (client to server) +00000000 16 03 01 00 c0 01 00 00 bc 03 03 00 00 00 00 00 |................| +00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 2a cc 14 |.............*..| +00000030 cc 13 cc 15 c0 2f c0 2b 00 9e c0 14 c0 0a 00 39 |...../.+.......9| +00000040 c0 13 c0 09 00 33 c0 11 c0 07 00 9c 00 35 00 2f |.....3.......5./| +00000050 00 05 00 04 00 0a 00 ff 01 00 00 69 00 00 00 05 |...........i....| +00000060 00 03 00 00 00 00 23 00 00 00 0d 00 16 00 14 06 |......#.........| +00000070 01 06 03 05 01 05 03 04 01 04 03 03 01 03 03 02 |................| +00000080 01 02 03 00 05 00 05 01 00 00 00 00 33 74 00 00 |............3t..| +00000090 00 12 00 00 00 10 00 1b 00 19 08 68 74 74 70 2f |...........http/| +000000a0 31 2e 31 06 73 70 64 79 2f 33 08 73 70 64 79 2f |1.1.spdy/3.spdy/| +000000b0 33 2e 31 00 0b 00 02 01 00 00 0a 00 08 00 06 00 |3.1.............| +000000c0 17 00 18 00 19 |.....| +>>> Flow 2 (server to client) +00000000 16 03 03 00 3d 02 00 00 39 03 03 a5 6d 48 df 33 |....=...9...mH.3| +00000010 e4 75 4b 37 b4 36 84 c4 ff 32 b0 3f cb ff 3f d6 |.uK7.6...2.?..?.| +00000020 a6 f6 b2 a0 d7 84 f6 c0 70 d0 ad 00 c0 13 00 00 |........p.......| +00000030 11 ff 01 00 01 00 00 0b 00 04 03 00 01 02 00 23 |...............#| +00000040 00 00 16 03 03 02 59 0b 00 02 55 00 02 52 00 02 |......Y...U..R..| +00000050 4f 30 82 02 4b 30 82 01 b4 a0 03 02 01 02 02 09 |O0..K0..........| +00000060 00 e8 f0 9d 3f e2 5b ea a6 30 0d 06 09 2a 86 48 |....?.[..0...*.H| +00000070 86 f7 0d 01 01 0b 05 00 30 1f 31 0b 30 09 06 03 |........0.1.0...| +00000080 55 04 0a 13 02 47 6f 31 10 30 0e 06 03 55 04 03 |U....Go1.0...U..| +00000090 13 07 47 6f 20 52 6f 6f 74 30 1e 17 0d 31 36 30 |..Go Root0...160| +000000a0 31 30 31 30 30 30 30 30 30 5a 17 0d 32 35 30 31 |101000000Z..2501| +000000b0 30 31 30 30 30 30 30 30 5a 30 1a 31 0b 30 09 06 |01000000Z0.1.0..| +000000c0 03 55 04 0a 13 02 47 6f 31 0b 30 09 06 03 55 04 |.U....Go1.0...U.| +000000d0 03 13 02 47 6f 30 81 9f 30 0d 06 09 2a 86 48 86 |...Go0..0...*.H.| +000000e0 f7 0d 01 01 01 05 00 03 81 8d 00 30 81 89 02 81 |...........0....| +000000f0 81 00 db 46 7d 93 2e 12 27 06 48 bc 06 28 21 ab |...F}...'.H..(!.| +00000100 7e c4 b6 a2 5d fe 1e 52 45 88 7a 36 47 a5 08 0d |~...]..RE.z6G...| +00000110 92 42 5b c2 81 c0 be 97 79 98 40 fb 4f 6d 14 fd |.B[.....y.@.Om..| +00000120 2b 13 8b c2 a5 2e 67 d8 d4 09 9e d6 22 38 b7 4a |+.....g....."8.J| +00000130 0b 74 73 2b c2 34 f1 d1 93 e5 96 d9 74 7b f3 58 |.ts+.4......t{.X| +00000140 9f 6c 61 3c c0 b0 41 d4 d9 2b 2b 24 23 77 5b 1c |.la<..A..++$#w[.| +00000150 3b bd 75 5d ce 20 54 cf a1 63 87 1d 1e 24 c4 f3 |;.u]. T..c...$..| +00000160 1d 1a 50 8b aa b6 14 43 ed 97 a7 75 62 f4 14 c8 |..P....C...ub...| +00000170 52 d7 02 03 01 00 01 a3 81 93 30 81 90 30 0e 06 |R.........0..0..| +00000180 03 55 1d 0f 01 01 ff 04 04 03 02 05 a0 30 1d 06 |.U...........0..| +00000190 03 55 1d 25 04 16 30 14 06 08 2b 06 01 05 05 07 |.U.%..0...+.....| +000001a0 03 01 06 08 2b 06 01 05 05 07 03 02 30 0c 06 03 |....+.......0...| +000001b0 55 1d 13 01 01 ff 04 02 30 00 30 19 06 03 55 1d |U.......0.0...U.| +000001c0 0e 04 12 04 10 9f 91 16 1f 43 43 3e 49 a6 de 6d |.........CC>I..m| +000001d0 b6 80 d7 9f 60 30 1b 06 03 55 1d 23 04 14 30 12 |....`0...U.#..0.| +000001e0 80 10 48 13 49 4d 13 7e 16 31 bb a3 01 d5 ac ab |..H.IM.~.1......| +000001f0 6e 7b 30 19 06 03 55 1d 11 04 12 30 10 82 0e 65 |n{0...U....0...e| +00000200 78 61 6d 70 6c 65 2e 67 6f 6c 61 6e 67 30 0d 06 |xample.golang0..| +00000210 09 2a 86 48 86 f7 0d 01 01 0b 05 00 03 81 81 00 |.*.H............| +00000220 9d 30 cc 40 2b 5b 50 a0 61 cb ba e5 53 58 e1 ed |.0.@+[P.a...SX..| +00000230 83 28 a9 58 1a a9 38 a4 95 a1 ac 31 5a 1a 84 66 |.(.X..8....1Z..f| +00000240 3d 43 d3 2d d9 0b f2 97 df d3 20 64 38 92 24 3a |=C.-...... d8.$:| +00000250 00 bc cf 9c 7d b7 40 20 01 5f aa d3 16 61 09 a2 |....}.@ ._...a..| +00000260 76 fd 13 c3 cc e1 0c 5c ee b1 87 82 f1 6c 04 ed |v......\.....l..| +00000270 73 bb b3 43 77 8d 0c 1c f1 0f a1 d8 40 83 61 c9 |s..Cw.......@.a.| +00000280 4c 72 2b 9d ae db 46 06 06 4d f4 c1 b3 3e c0 d1 |Lr+...F..M...>..| +00000290 bd 42 d4 db fe 3d 13 60 84 5c 21 d3 3b e9 fa e7 |.B...=.`.\!.;...| +000002a0 16 03 03 00 cd 0c 00 00 c9 03 00 17 41 04 1c fd |............A...| +000002b0 f6 6e e5 35 c4 18 ae c3 d9 4d 97 de 78 f0 44 73 |.n.5.....M..x.Ds| +000002c0 a8 76 85 0e 41 5c 49 5b 10 5b 90 c4 7b 9b f9 3c |.v..A\I[.[..{..<| +000002d0 9c dd 42 26 4f 47 1b 09 56 b5 19 27 a4 21 9c 2d |..B&OG..V..'.!.-| +000002e0 00 4d 92 1c 6c ed 00 9b 31 1b cb 90 ad e4 06 01 |.M..l...1.......| +000002f0 00 80 67 1d 29 1a 14 5a cb be b5 f8 11 a7 93 5e |..g.)..Z.......^| +00000300 da e2 c2 81 ed 3a 33 c0 32 c4 05 c5 75 5f de 15 |.....:3.2...u_..| +00000310 f3 b9 46 95 aa ee c7 67 34 2b 40 cc 22 e1 12 a4 |..F....g4+@."...| +00000320 a6 59 29 e7 54 30 f8 8e 08 e6 38 1d 37 f4 50 08 |.Y).T0....8.7.P.| +00000330 d3 3d 28 9b 9c c7 c7 ab 2c 6a 6e db 2e 57 84 e8 |.=(.....,jn..W..| +00000340 a5 77 97 ca d8 29 77 94 93 04 6f da 50 fc 41 c4 |.w...)w...o.P.A.| +00000350 d6 d7 1a ec b9 66 2a ed 17 4f 01 d2 94 7f 82 e4 |.....f*..O......| +00000360 e2 ea c5 30 12 b5 1e c3 44 cc 03 ba 34 12 47 e8 |...0....D...4.G.| +00000370 9e fc 16 03 03 00 04 0e 00 00 00 |...........| +>>> Flow 3 (client to server) +00000000 16 03 03 00 46 10 00 00 42 41 04 1e 18 37 ef 0d |....F...BA...7..| +00000010 19 51 88 35 75 71 b5 e5 54 5b 12 2e 8f 09 67 fd |.Q.5uq..T[....g.| +00000020 a7 24 20 3e b2 56 1c ce 97 28 5e f8 2b 2d 4f 9e |.$ >.V...(^.+-O.| +00000030 f1 07 9f 6c 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 |...lK[.V.2B.X..I| +00000040 a6 b5 68 1a 41 03 56 6b dc 5a 89 14 03 03 00 01 |..h.A.Vk.Z......| +00000050 01 16 03 03 00 40 00 00 00 00 00 00 00 00 00 00 |.....@..........| +00000060 00 00 00 00 00 00 bf 98 78 3b 51 81 70 2d 1c 5d |........x;Q.p-.]| +00000070 a6 1b ec ef 0a ae a0 52 dd 25 2a 26 45 29 58 93 |.......R.%*&E)X.| +00000080 59 b1 32 eb 83 8e 41 44 0d 57 b7 93 2a 13 c5 e2 |Y.2...AD.W..*...| +00000090 a1 7c 47 87 b2 0a |.|G...| +>>> Flow 4 (server to client) +00000000 16 03 03 00 aa 04 00 00 a6 00 00 1c 20 00 a0 ec |............ ...| +00000010 56 10 cc 76 e5 78 39 22 8e 76 53 03 db 34 5e e3 |V..v.x9".vS..4^.| +00000020 a3 dd e5 fb 3f ba 38 a5 e3 8d 87 7a f3 b0 88 2d |....?.8....z...-| +00000030 81 ca 7c 0c da 56 5d 42 1e 45 a5 41 9d 52 3c 98 |..|..V]B.E.A.R<.| +00000040 bb 42 ad 90 51 06 95 ab 2b 7c f0 9f fa 54 1f 6d |.B..Q...+|...T.m| +00000050 55 a3 5e 77 1a b1 41 0e 30 4d 6c 06 af cf 13 99 |U.^w..A.0Ml.....| +00000060 89 47 40 77 f3 5f 15 81 e9 05 8d 23 31 00 a0 f4 |.G@w._.....#1...| +00000070 ab 02 90 03 f5 67 cb 93 9d f0 28 a6 0f 1d 7b ca |.....g....(...{.| +00000080 71 2a 59 2e b1 6d 32 de 11 99 11 2e 58 f3 c5 de |q*Y..m2.....X...| +00000090 cb b8 d3 35 b9 53 5a 44 c9 49 d0 a6 f8 d0 7b aa |...5.SZD.I....{.| +000000a0 f8 95 38 5b 61 78 21 1c be 2a c8 01 20 03 be 14 |..8[ax!..*.. ...| +000000b0 03 03 00 01 01 16 03 03 00 40 a0 82 06 07 49 de |.........@....I.| +000000c0 e9 85 67 1b 26 20 7c 1b ef 0c c8 68 c3 7b 81 8e |..g.& |....h.{..| +000000d0 e4 a7 4e 94 ff 5e 45 ce 6a a3 7a 29 ab 4c 28 7f |..N..^E.j.z).L(.| +000000e0 69 ec da ca 4f 3e 98 1c 8a 41 5f ba 7d 2d 13 28 |i...O>...A_.}-.(| +000000f0 a2 72 a3 14 da 98 7a 6c ef 77 |.r....zl.w| +>>> Flow 5 (client to server) +00000000 17 03 03 00 30 00 00 00 00 00 00 00 00 00 00 00 |....0...........| +00000010 00 00 00 00 00 a9 57 aa 50 33 9b 04 8d db 4b cd |......W.P3....K.| +00000020 0d e6 ca fc e8 58 24 d1 f4 d7 13 69 5a d0 a8 2b |.....X$....iZ..+| +00000030 ec ea ab 1b d2 15 03 03 00 30 00 00 00 00 00 00 |.........0......| +00000040 00 00 00 00 00 00 00 00 00 00 3c b1 d0 db c9 4a |..........<....J| +00000050 fd a3 8e 90 cd 86 49 a5 9d c6 95 bf 98 59 45 48 |......I......YEH| +00000060 ad 08 5c ea 60 24 fa 9e 6c d6 |..\.`$..l.| diff --git a/testdata/Client-TLSv12-UTLS-ECDHE-RSA-AES256-SHA-Android-22 b/testdata/Client-TLSv12-UTLS-ECDHE-RSA-AES256-SHA-Android-22 new file mode 100644 index 0000000..95833b9 --- /dev/null +++ b/testdata/Client-TLSv12-UTLS-ECDHE-RSA-AES256-SHA-Android-22 @@ -0,0 +1,107 @@ +>>> Flow 1 (client to server) +00000000 16 03 01 00 c0 01 00 00 bc 03 03 00 00 00 00 00 |................| +00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| +00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 2a cc 14 |.............*..| +00000030 cc 13 cc 15 c0 2f c0 2b 00 9e c0 14 c0 0a 00 39 |...../.+.......9| +00000040 c0 13 c0 09 00 33 c0 11 c0 07 00 9c 00 35 00 2f |.....3.......5./| +00000050 00 05 00 04 00 0a 00 ff 01 00 00 69 00 00 00 05 |...........i....| +00000060 00 03 00 00 00 00 23 00 00 00 0d 00 16 00 14 06 |......#.........| +00000070 01 06 03 05 01 05 03 04 01 04 03 03 01 03 03 02 |................| +00000080 01 02 03 00 05 00 05 01 00 00 00 00 33 74 00 00 |............3t..| +00000090 00 12 00 00 00 10 00 1b 00 19 08 68 74 74 70 2f |...........http/| +000000a0 31 2e 31 06 73 70 64 79 2f 33 08 73 70 64 79 2f |1.1.spdy/3.spdy/| +000000b0 33 2e 31 00 0b 00 02 01 00 00 0a 00 08 00 06 00 |3.1.............| +000000c0 17 00 18 00 19 |.....| +>>> Flow 2 (server to client) +00000000 16 03 03 00 3d 02 00 00 39 03 03 8b 3e 63 de d4 |....=...9...>c..| +00000010 76 f3 88 6f 0e 35 0f 6a 18 03 2a b1 10 11 82 ea |v..o.5.j..*.....| +00000020 a5 c2 8b 6c 77 35 01 71 13 b7 f2 00 c0 14 00 00 |...lw5.q........| +00000030 11 ff 01 00 01 00 00 0b 00 04 03 00 01 02 00 23 |...............#| +00000040 00 00 16 03 03 02 59 0b 00 02 55 00 02 52 00 02 |......Y...U..R..| +00000050 4f 30 82 02 4b 30 82 01 b4 a0 03 02 01 02 02 09 |O0..K0..........| +00000060 00 e8 f0 9d 3f e2 5b ea a6 30 0d 06 09 2a 86 48 |....?.[..0...*.H| +00000070 86 f7 0d 01 01 0b 05 00 30 1f 31 0b 30 09 06 03 |........0.1.0...| +00000080 55 04 0a 13 02 47 6f 31 10 30 0e 06 03 55 04 03 |U....Go1.0...U..| +00000090 13 07 47 6f 20 52 6f 6f 74 30 1e 17 0d 31 36 30 |..Go Root0...160| +000000a0 31 30 31 30 30 30 30 30 30 5a 17 0d 32 35 30 31 |101000000Z..2501| +000000b0 30 31 30 30 30 30 30 30 5a 30 1a 31 0b 30 09 06 |01000000Z0.1.0..| +000000c0 03 55 04 0a 13 02 47 6f 31 0b 30 09 06 03 55 04 |.U....Go1.0...U.| +000000d0 03 13 02 47 6f 30 81 9f 30 0d 06 09 2a 86 48 86 |...Go0..0...*.H.| +000000e0 f7 0d 01 01 01 05 00 03 81 8d 00 30 81 89 02 81 |...........0....| +000000f0 81 00 db 46 7d 93 2e 12 27 06 48 bc 06 28 21 ab |...F}...'.H..(!.| +00000100 7e c4 b6 a2 5d fe 1e 52 45 88 7a 36 47 a5 08 0d |~...]..RE.z6G...| +00000110 92 42 5b c2 81 c0 be 97 79 98 40 fb 4f 6d 14 fd |.B[.....y.@.Om..| +00000120 2b 13 8b c2 a5 2e 67 d8 d4 09 9e d6 22 38 b7 4a |+.....g....."8.J| +00000130 0b 74 73 2b c2 34 f1 d1 93 e5 96 d9 74 7b f3 58 |.ts+.4......t{.X| +00000140 9f 6c 61 3c c0 b0 41 d4 d9 2b 2b 24 23 77 5b 1c |.la<..A..++$#w[.| +00000150 3b bd 75 5d ce 20 54 cf a1 63 87 1d 1e 24 c4 f3 |;.u]. T..c...$..| +00000160 1d 1a 50 8b aa b6 14 43 ed 97 a7 75 62 f4 14 c8 |..P....C...ub...| +00000170 52 d7 02 03 01 00 01 a3 81 93 30 81 90 30 0e 06 |R.........0..0..| +00000180 03 55 1d 0f 01 01 ff 04 04 03 02 05 a0 30 1d 06 |.U...........0..| +00000190 03 55 1d 25 04 16 30 14 06 08 2b 06 01 05 05 07 |.U.%..0...+.....| +000001a0 03 01 06 08 2b 06 01 05 05 07 03 02 30 0c 06 03 |....+.......0...| +000001b0 55 1d 13 01 01 ff 04 02 30 00 30 19 06 03 55 1d |U.......0.0...U.| +000001c0 0e 04 12 04 10 9f 91 16 1f 43 43 3e 49 a6 de 6d |.........CC>I..m| +000001d0 b6 80 d7 9f 60 30 1b 06 03 55 1d 23 04 14 30 12 |....`0...U.#..0.| +000001e0 80 10 48 13 49 4d 13 7e 16 31 bb a3 01 d5 ac ab |..H.IM.~.1......| +000001f0 6e 7b 30 19 06 03 55 1d 11 04 12 30 10 82 0e 65 |n{0...U....0...e| +00000200 78 61 6d 70 6c 65 2e 67 6f 6c 61 6e 67 30 0d 06 |xample.golang0..| +00000210 09 2a 86 48 86 f7 0d 01 01 0b 05 00 03 81 81 00 |.*.H............| +00000220 9d 30 cc 40 2b 5b 50 a0 61 cb ba e5 53 58 e1 ed |.0.@+[P.a...SX..| +00000230 83 28 a9 58 1a a9 38 a4 95 a1 ac 31 5a 1a 84 66 |.(.X..8....1Z..f| +00000240 3d 43 d3 2d d9 0b f2 97 df d3 20 64 38 92 24 3a |=C.-...... d8.$:| +00000250 00 bc cf 9c 7d b7 40 20 01 5f aa d3 16 61 09 a2 |....}.@ ._...a..| +00000260 76 fd 13 c3 cc e1 0c 5c ee b1 87 82 f1 6c 04 ed |v......\.....l..| +00000270 73 bb b3 43 77 8d 0c 1c f1 0f a1 d8 40 83 61 c9 |s..Cw.......@.a.| +00000280 4c 72 2b 9d ae db 46 06 06 4d f4 c1 b3 3e c0 d1 |Lr+...F..M...>..| +00000290 bd 42 d4 db fe 3d 13 60 84 5c 21 d3 3b e9 fa e7 |.B...=.`.\!.;...| +000002a0 16 03 03 00 cd 0c 00 00 c9 03 00 17 41 04 2e c8 |............A...| +000002b0 2d cc 7a 32 59 32 54 a7 2a fc bd 80 b3 b0 29 a9 |-.z2Y2T.*.....).| +000002c0 a0 a8 3f 9d 5e 35 c5 7c 3d a2 7e 02 85 56 06 66 |..?.^5.|=.~..V.f| +000002d0 e4 6a 50 bd d1 6a b9 2f 83 cf bb 95 a8 47 15 0b |.jP..j./.....G..| +000002e0 96 63 0d eb 3b 51 cf e0 f4 a9 ce 85 b7 0c 06 01 |.c..;Q..........| +000002f0 00 80 53 54 11 b7 1b b4 bd 59 7e 65 04 8a 12 7b |..ST.....Y~e...{| +00000300 c1 de 4f 71 32 a8 b8 21 08 05 e1 33 bf 83 c4 3e |..Oq2..!...3...>| +00000310 27 10 e7 5b 16 7a ac 6b ee a1 92 ea ea bb 22 5b |'..[.z.k......"[| +00000320 6c 68 ce b1 e8 a2 51 a8 07 1c c0 98 fa 3c 01 59 |lh....Q......<.Y| +00000330 9e f0 ee 13 51 7c 09 78 d2 33 7b 2b 8c da 85 f1 |....Q|.x.3{+....| +00000340 96 05 8e ae 4d 92 51 1f fb 48 03 66 99 28 cb 2e |....M.Q..H.f.(..| +00000350 9f 63 47 51 ba 5b 28 d0 3b 8a 9e d9 c8 51 a8 af |.cGQ.[(.;....Q..| +00000360 dd a6 2e d5 84 0d c2 e4 f8 f4 2b 97 b1 ba 7d e8 |..........+...}.| +00000370 45 0d 16 03 03 00 04 0e 00 00 00 |E..........| +>>> Flow 3 (client to server) +00000000 16 03 03 00 46 10 00 00 42 41 04 1e 18 37 ef 0d |....F...BA...7..| +00000010 19 51 88 35 75 71 b5 e5 54 5b 12 2e 8f 09 67 fd |.Q.5uq..T[....g.| +00000020 a7 24 20 3e b2 56 1c ce 97 28 5e f8 2b 2d 4f 9e |.$ >.V...(^.+-O.| +00000030 f1 07 9f 6c 4b 5b 83 56 e2 32 42 e9 58 b6 d7 49 |...lK[.V.2B.X..I| +00000040 a6 b5 68 1a 41 03 56 6b dc 5a 89 14 03 03 00 01 |..h.A.Vk.Z......| +00000050 01 16 03 03 00 40 00 00 00 00 00 00 00 00 00 00 |.....@..........| +00000060 00 00 00 00 00 00 77 04 90 a4 32 b4 73 6b 41 4e |......w...2.skAN| +00000070 7b 25 a4 62 24 d6 8d 43 0d 45 19 51 99 53 f5 45 |{%.b$..C.E.Q.S.E| +00000080 76 26 5f 32 d0 ff cd 0f 86 a3 0e f8 7e 2f 09 8b |v&_2........~/..| +00000090 2e 1c 03 9c 25 f4 |....%.| +>>> Flow 4 (server to client) +00000000 16 03 03 00 aa 04 00 00 a6 00 00 1c 20 00 a0 79 |............ ..y| +00000010 f1 01 24 4b 47 9e 51 dc 90 98 cc 88 09 66 2b c0 |..$KG.Q......f+.| +00000020 ca b4 fc be 56 da d1 0c 86 61 91 a2 36 e2 55 91 |....V....a..6.U.| +00000030 f7 a2 d5 8c e8 4c 76 4b 3e 19 b2 33 fd 43 c1 5c |.....LvK>..3.C.\| +00000040 2a 67 9d 21 c3 2c 65 52 0e 6b bb 5f ca c6 1a fe |*g.!.,eR.k._....| +00000050 29 1e 42 10 56 4b 7b 58 93 72 5a 78 13 4c 42 cc |).B.VK{X.rZx.LB.| +00000060 17 f7 33 06 41 b7 f3 49 f0 45 f7 0a ca 16 d7 8c |..3.A..I.E......| +00000070 f5 60 ea 17 cf a0 b5 c2 3a c3 6b e6 65 75 48 c4 |.`......:.k.euH.| +00000080 76 37 37 28 ca f9 ae 43 ca 64 d1 33 21 02 fd 95 |v77(...C.d.3!...| +00000090 f1 75 15 ea b1 44 94 61 21 c4 4b 33 38 b9 cf cb |.u...D.a!.K38...| +000000a0 29 8f 91 12 25 11 93 d3 c3 5e 2d 48 fa ac 1b 14 |)...%....^-H....| +000000b0 03 03 00 01 01 16 03 03 00 40 f8 ca ea 95 92 6b |.........@.....k| +000000c0 c5 51 28 f6 01 e1 f1 77 f2 b8 55 3c f2 9b dd 4f |.Q(....w..U<...O| +000000d0 82 6d 16 17 02 bf ba 05 c7 bf 16 b2 68 fe d8 e3 |.m..........h...| +000000e0 b7 ad 2a 49 87 15 b4 25 7b bf 15 f4 2c 6b cd e9 |..*I...%{...,k..| +000000f0 7e ba c9 22 88 b4 d7 fa 3a 4b |~.."....:K| +>>> Flow 5 (client to server) +00000000 17 03 03 00 30 00 00 00 00 00 00 00 00 00 00 00 |....0...........| +00000010 00 00 00 00 00 68 8e 89 29 68 99 fb 6e 8b e3 fb |.....h..)h..n...| +00000020 7b 1f b1 d3 ca 0b 0c 6f 23 dd ca 02 0a cf 93 11 |{......o#.......| +00000030 fc e7 3d 18 ce 15 03 03 00 30 00 00 00 00 00 00 |..=......0......| +00000040 00 00 00 00 00 00 00 00 00 00 f6 50 1a 7b 60 a1 |...........P.{`.| +00000050 8c 41 63 4d 0f cc d5 94 81 d9 e2 7d 43 1d 5f 7a |.AcM.......}C._z| +00000060 c1 9b d9 8d f7 4d 1e d0 7d 96 |.....M..}.| diff --git a/testenv/testenv.go b/testenv/testenv.go new file mode 100644 index 0000000..f7c4ad2 --- /dev/null +++ b/testenv/testenv.go @@ -0,0 +1,193 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package testenv provides information about what functionality +// is available in different testing environments run by the Go team. +// +// It is an internal package because these details are specific +// to the Go team's test setup (on build.golang.org) and not +// fundamental to tests in general. +package testenv + +import ( + "errors" + "flag" + "os" + "os/exec" + "path/filepath" + "runtime" + "strconv" + "strings" + "testing" +) + +// Builder reports the name of the builder running this test +// (for example, "linux-amd64" or "windows-386-gce"). +// If the test is not running on the build infrastructure, +// Builder returns the empty string. +func Builder() string { + return os.Getenv("GO_BUILDER_NAME") +} + +// HasGoBuild reports whether the current system can build programs with ``go build'' +// and then run them with os.StartProcess or exec.Command. +func HasGoBuild() bool { + switch runtime.GOOS { + case "android", "nacl": + return false + case "darwin": + if strings.HasPrefix(runtime.GOARCH, "arm") { + return false + } + } + return true +} + +// MustHaveGoBuild checks that the current system can build programs with ``go build'' +// and then run them with os.StartProcess or exec.Command. +// If not, MustHaveGoBuild calls t.Skip with an explanation. +func MustHaveGoBuild(t *testing.T) { + if !HasGoBuild() { + t.Skipf("skipping test: 'go build' not available on %s/%s", runtime.GOOS, runtime.GOARCH) + } +} + +// HasGoRun reports whether the current system can run programs with ``go run.'' +func HasGoRun() bool { + // For now, having go run and having go build are the same. + return HasGoBuild() +} + +// MustHaveGoRun checks that the current system can run programs with ``go run.'' +// If not, MustHaveGoRun calls t.Skip with an explanation. +func MustHaveGoRun(t *testing.T) { + if !HasGoRun() { + t.Skipf("skipping test: 'go run' not available on %s/%s", runtime.GOOS, runtime.GOARCH) + } +} + +// GoToolPath reports the path to the Go tool. +// It is a convenience wrapper around GoTool. +// If the tool is unavailable GoToolPath calls t.Skip. +// If the tool should be available and isn't, GoToolPath calls t.Fatal. +func GoToolPath(t *testing.T) string { + MustHaveGoBuild(t) + path, err := GoTool() + if err != nil { + t.Fatal(err) + } + return path +} + +// GoTool reports the path to the Go tool. +func GoTool() (string, error) { + if !HasGoBuild() { + return "", errors.New("platform cannot run go tool") + } + var exeSuffix string + if runtime.GOOS == "windows" { + exeSuffix = ".exe" + } + path := filepath.Join(runtime.GOROOT(), "bin", "go"+exeSuffix) + if _, err := os.Stat(path); err == nil { + return path, nil + } + goBin, err := exec.LookPath("go" + exeSuffix) + if err != nil { + return "", errors.New("cannot find go tool: " + err.Error()) + } + return goBin, nil +} + +// HasExec reports whether the current system can start new processes +// using os.StartProcess or (more commonly) exec.Command. +func HasExec() bool { + switch runtime.GOOS { + case "nacl": + return false + case "darwin": + if strings.HasPrefix(runtime.GOARCH, "arm") { + return false + } + } + return true +} + +// MustHaveExec checks that the current system can start new processes +// using os.StartProcess or (more commonly) exec.Command. +// If not, MustHaveExec calls t.Skip with an explanation. +func MustHaveExec(t *testing.T) { + if !HasExec() { + t.Skipf("skipping test: cannot exec subprocess on %s/%s", runtime.GOOS, runtime.GOARCH) + } +} + +// HasExternalNetwork reports whether the current system can use +// external (non-localhost) networks. +func HasExternalNetwork() bool { + return !testing.Short() +} + +// MustHaveExternalNetwork checks that the current system can use +// external (non-localhost) networks. +// If not, MustHaveExternalNetwork calls t.Skip with an explanation. +func MustHaveExternalNetwork(t *testing.T) { + if testing.Short() { + t.Skipf("skipping test: no external network in -short mode") + } +} + +var haveCGO bool + +// MustHaveCGO calls t.Skip if cgo is not available. +func MustHaveCGO(t *testing.T) { + if !haveCGO { + t.Skipf("skipping test: no cgo") + } +} + +// HasSymlink reports whether the current system can use os.Symlink. +func HasSymlink() bool { + ok, _ := hasSymlink() + return ok +} + +// MustHaveSymlink reports whether the current system can use os.Symlink. +// If not, MustHaveSymlink calls t.Skip with an explanation. +func MustHaveSymlink(t *testing.T) { + ok, reason := hasSymlink() + if !ok { + t.Skipf("skipping test: cannot make symlinks on %s/%s%s", runtime.GOOS, runtime.GOARCH, reason) + } +} + +// HasLink reports whether the current system can use os.Link. +func HasLink() bool { + // From Android release M (Marshmallow), hard linking files is blocked + // and an attempt to call link() on a file will return EACCES. + // - https://code.google.com/p/android-developer-preview/issues/detail?id=3150 + return runtime.GOOS != "plan9" && runtime.GOOS != "android" +} + +// MustHaveLink reports whether the current system can use os.Link. +// If not, MustHaveLink calls t.Skip with an explanation. +func MustHaveLink(t *testing.T) { + if !HasLink() { + t.Skipf("skipping test: hardlinks are not supported on %s/%s", runtime.GOOS, runtime.GOARCH) + } +} + +var flaky = flag.Bool("flaky", false, "run known-flaky tests too") + +func SkipFlaky(t *testing.T, issue int) { + if !*flaky { + t.Skipf("skipping known flaky test without the -flaky flag; see golang.org/issue/%d", issue) + } +} + +func SkipFlakyNet(t *testing.T) { + if v, _ := strconv.ParseBool(os.Getenv("GO_BUILDER_FLAKY_NET")); v { + t.Skip("skipping test on builder known to have frequent network failures") + } +} diff --git a/testenv/testenv_cgo.go b/testenv/testenv_cgo.go new file mode 100644 index 0000000..e3d4d16 --- /dev/null +++ b/testenv/testenv_cgo.go @@ -0,0 +1,11 @@ +// Copyright 2017 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build cgo + +package testenv + +func init() { + haveCGO = true +} diff --git a/testenv/testenv_notwin.go b/testenv/testenv_notwin.go new file mode 100644 index 0000000..d8ce6cd --- /dev/null +++ b/testenv/testenv_notwin.go @@ -0,0 +1,20 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !windows + +package testenv + +import ( + "runtime" +) + +func hasSymlink() (ok bool, reason string) { + switch runtime.GOOS { + case "android", "nacl", "plan9": + return false, "" + } + + return true, "" +} diff --git a/testenv/testenv_windows.go b/testenv/testenv_windows.go new file mode 100644 index 0000000..e593f64 --- /dev/null +++ b/testenv/testenv_windows.go @@ -0,0 +1,49 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package testenv + +import ( + "io/ioutil" + "os" + "path/filepath" + "sync" + "syscall" +) + +var symlinkOnce sync.Once +var winSymlinkErr error + +func initWinHasSymlink() { + tmpdir, err := ioutil.TempDir("", "symtest") + if err != nil { + panic("failed to create temp directory: " + err.Error()) + } + defer os.RemoveAll(tmpdir) + + err = os.Symlink("target", filepath.Join(tmpdir, "symlink")) + if err != nil { + err = err.(*os.LinkError).Err + switch err { + case syscall.EWINDOWS, syscall.ERROR_PRIVILEGE_NOT_HELD: + winSymlinkErr = err + } + } + os.Remove("target") +} + +func hasSymlink() (ok bool, reason string) { + symlinkOnce.Do(initWinHasSymlink) + + switch winSymlinkErr { + case nil: + return true, "" + case syscall.EWINDOWS: + return false, ": symlinks are not supported on your version of Windows" + case syscall.ERROR_PRIVILEGE_NOT_HELD: + return false, ": you don't have enough privileges to create symlinks" + } + + return false, "" +} diff --git a/tls_test.go b/tls_test.go index 86812f0..8ad0173 100644 --- a/tls_test.go +++ b/tls_test.go @@ -9,7 +9,7 @@ import ( "crypto/x509" "errors" "fmt" - "internal/testenv" + "github.com/Jigsaw-Code/utls/testenv" "io" "io/ioutil" "math" diff --git a/u_common.go b/u_common.go new file mode 100644 index 0000000..8b0bc27 --- /dev/null +++ b/u_common.go @@ -0,0 +1,121 @@ +package tls + +import "fmt" + +// Naming convention: +// Unsupported things are prefixed with "Fake" +// Supported things, that have changed their ID are prefixed with "Old" +// Supported but disabled things are prefixed with "Disabled". We will _enable_ them. +const ( + // padding isn't quite a 'fake' extension, as uTLS provides full implementation + // just denotes that crypto/tls doesn't provide it + fakeExtensionPadding uint16 = 21 + + // extensions below break connection, if server echoes them back + fakeExtensionExtendedMasterSecret uint16 = 23 + fakeExtensionChannelID uint16 = 30032 // not IANA assigned +) + +const ( + OLD_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = uint16(0xcc13) + OLD_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = uint16(0xcc14) + + FAKE_OLD_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = uint16(0xcc15) // we can try to craft these ciphersuites + FAKE_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = uint16(0x009e) // from existing pieces, if needed + + FAKE_TLS_DHE_RSA_WITH_AES_128_CBC_SHA = uint16(0x0033) + FAKE_TLS_DHE_RSA_WITH_AES_256_CBC_SHA = uint16(0x0039) + FAKE_TLS_RSA_WITH_RC4_128_MD5 = uint16(0x0004) + FAKE_TLS_EMPTY_RENEGOTIATION_INFO_SCSV = uint16(0x00ff) +) + +// newest signatures +var ( + fakeRsaPssSha256 = SignatureAndHash{0x08, 0x04} // also declared in common.go as type SignatureScheme, + fakeRsaPssSha384 = SignatureAndHash{0x08, 0x05} // but not used by default and not implemented + fakeRsaPssSha512 = SignatureAndHash{0x08, 0x06} + // fakeEd25519 = SignatureAndHash{0x08, 0x07} + // fakeEd448 = SignatureAndHash{0x08, 0x08} +) + +// IDs of hash functions in signatures +const ( + disabledHashSHA512 uint8 = 6 // Supported, but disabled by default. Will be enabled, as needed + fakeHashSHA224 uint8 = 3 // Supported, but we won't enable it: sounds esoteric and fishy + +) + +type ClientHelloID struct { + Browser string + Version uint16 + // TODO: consider adding OS? +} + +func (p *ClientHelloID) Str() string { + return fmt.Sprintf("%s-%d", p.Browser, p.Version) +} + +const ( + helloGolang = "Golang" + helloRandomized = "Randomized" + helloCustom = "Custom" + helloFirefox = "Firefox" + helloChrome = "Chrome" + helloAndroid = "Android" +) + +const ( + helloAutoVers = iota + helloRandomizedALPN + helloRandomizedNoALPN +) + +var ( + // 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. + HelloGolang ClientHelloID = ClientHelloID{helloGolang, helloAutoVers} + + // HelloCustom will prepare ClientHello with empty uconn.Extensions so you can fill it with TLSExtension's manually + HelloCustom ClientHelloID = ClientHelloID{helloCustom, helloAutoVers} + + // HelloRandomized* randomly adds/reorders extensions, ciphersuites, etc. + HelloRandomized ClientHelloID = ClientHelloID{helloRandomized, helloAutoVers} + HelloRandomizedALPN ClientHelloID = ClientHelloID{helloRandomized, helloRandomizedALPN} + HelloRandomizedNoALPN ClientHelloID = ClientHelloID{helloRandomized, helloRandomizedNoALPN} + + // The rest will will parrot given browser. + HelloFirefox_Auto ClientHelloID = ClientHelloID{helloFirefox, helloAutoVers} + HelloFirefox_53_WIP = ClientHelloID{helloFirefox, 53} + + HelloChrome_Auto ClientHelloID = ClientHelloID{helloChrome, helloAutoVers} + HelloChrome_58 ClientHelloID = ClientHelloID{helloChrome, 58} + + HelloAndroid_Auto ClientHelloID = ClientHelloID{helloAndroid, helloAutoVers} + HelloAndroid_6_0_Browser ClientHelloID = ClientHelloID{helloAndroid, 23} + HelloAndroid_5_1_Browser ClientHelloID = ClientHelloID{helloAndroid, 22} +) + +// Appends newCipher to cipherSuites, if not there already +// Used to add old cipher ids +func appendToGlobalCipherSuites(newCipher *cipherSuite) { + for _, c := range cipherSuites { + if c.id == newCipher.id { + return + } + } + cipherSuites = append(cipherSuites, newCipher) +} + +// Appends {hash, sig} to supportedSignatureAlgorithms, if not there already +// Used to enable already supported but disabled signatures +func appendToGlobalSigAlgs(hash uint8, sig uint8) { + s := signatureAndHash{hash, sig} + for _, c := range supportedSignatureAlgorithms { + if c.hash == s.hash && c.signature == s.signature { + return + } + } + supportedSignatureAlgorithms = append(supportedSignatureAlgorithms, s) +} diff --git a/u_conn.go b/u_conn.go new file mode 100644 index 0000000..ae80794 --- /dev/null +++ b/u_conn.go @@ -0,0 +1,373 @@ +package tls + +import ( + "bufio" + "bytes" + "encoding/binary" + "errors" + "io" + "net" + "strconv" + "sync" +) + +type UConn struct { + *Conn + + Extensions []TLSExtension + clientHelloID ClientHelloID + + HandshakeState ClientHandshakeState + + HandshakeStateBuilt bool +} + +// UClient returns a new uTLS client, with behavior depending on clientHelloID. +// Config CAN be nil, but make sure to eventually specify ServerName. +func UClient(conn net.Conn, config *Config, clientHelloID ClientHelloID) *UConn { + if config == nil { + config = &Config{} + } + tlsConn := Conn{conn: conn, config: config, isClient: true} + handshakeState := ClientHandshakeState{C: &tlsConn, Hello: &ClientHelloMsg{}} + uconn := UConn{Conn: &tlsConn, clientHelloID: clientHelloID, HandshakeState: handshakeState} + return &uconn +} + +// BuildHandshakeState() overwrites most fields, therefore, it is advised to manually call this function, +// if you need to inspect/change contents after parroting/making default Golang ClientHello. +// Otherwise, there is no need to call this function explicitly. +func (uconn *UConn) BuildHandshakeState() error { + if uconn.clientHelloID == HelloGolang { + // use default Golang ClientHello. + hello, err := makeClientHello(uconn.config) + if uconn.HandshakeState.Session != nil { + // session is lost at makeClientHello(), let's reapply + uconn.SetSessionState(uconn.HandshakeState.Session) + } + if err != nil { + return err + } + uconn.HandshakeState.Hello = hello.getPublicPtr() + } else { + err := uconn.generateClientHelloConfig(uconn.clientHelloID) + if err != nil { + return err + } + err = uconn.applyConfig() + if err != nil { + return err + } + err = uconn.marshalClientHello() + if err != nil { + return err + } + } + uconn.HandshakeStateBuilt = true + return nil +} + +// If you want you session tickets to be reused - use same cache on following connections +func (uconn *UConn) SetSessionState(session *ClientSessionState) { + uconn.HandshakeState.Session = session + if session != nil { + uconn.HandshakeState.Hello.SessionTicket = session.sessionTicket + } + uconn.HandshakeState.Hello.TicketSupported = true + for _, ext := range uconn.Extensions { + st, ok := ext.(*SessionTicketExtension) + if ok { + st.Session = session + } + } +} + +// If you want you session tickets to be reused - use same cache on following connections +func (uconn *UConn) SetSessionCache(cache ClientSessionCache) { + uconn.config.ClientSessionCache = cache + uconn.HandshakeState.Hello.TicketSupported = true +} + +// r has to be 32 bytes long +func (uconn *UConn) SetClientRandom(r []byte) error { + if len(r) != 32 { + return errors.New("Incorrect client random length! Expected: 32, got: " + strconv.Itoa(len(r))) + } else { + uconn.HandshakeState.Hello.Random = make([]byte, 32) + copy(uconn.HandshakeState.Hello.Random, r) + return nil + } +} + +func (uconn *UConn) SetSNI(sni string) { + hname := hostnameInSNI(sni) + uconn.config.ServerName = hname + for _, ext := range uconn.Extensions { + sniExt, ok := ext.(*SNIExtension) + if ok { + sniExt.ServerName = hname + } + } +} + +// Handshake runs the client handshake using given clientHandshakeState +// Requires hs.hello, and, optionally, hs.session to be set. +func (c *UConn) Handshake() error { + // This code was copied almost as is from tls/conn.go + // c.handshakeErr and c.handshakeComplete are protected by + // c.handshakeMutex. In order to perform a handshake, we need to lock + // c.in also and c.handshakeMutex must be locked after c.in. + // + // However, if a Read() operation is hanging then it'll be holding the + // lock on c.in and so taking it here would cause all operations that + // need to check whether a handshake is pending (such as Write) to + // block. + // + // Thus we first take c.handshakeMutex to check whether a handshake is + // needed. + // + // If so then, previously, this code would unlock handshakeMutex and + // then lock c.in and handshakeMutex in the correct order to run the + // handshake. The problem was that it was possible for a Read to + // complete the handshake once handshakeMutex was unlocked and then + // keep c.in while waiting for network data. Thus a concurrent + // operation could be blocked on c.in. + // + // Thus handshakeCond is used to signal that a goroutine is committed + // to running the handshake and other goroutines can wait on it if they + // need. handshakeCond is protected by handshakeMutex. + c.handshakeMutex.Lock() + defer c.handshakeMutex.Unlock() + + for { + if err := c.handshakeErr; err != nil { + return err + } + if c.handshakeComplete { + return nil + } + if c.handshakeCond == nil { + break + } + + c.handshakeCond.Wait() + } + + // Set handshakeCond to indicate that this goroutine is committing to + // running the handshake. + c.handshakeCond = sync.NewCond(&c.handshakeMutex) + c.handshakeMutex.Unlock() + + c.in.Lock() + defer c.in.Unlock() + + c.handshakeMutex.Lock() + + // The handshake cannot have completed when handshakeMutex was unlocked + // because this goroutine set handshakeCond. + if c.handshakeErr != nil || c.handshakeComplete { + panic("handshake should not have been able to complete after handshakeCond was set") + } + + if !c.isClient { + panic("Servers should not call ClientHandshakeWithState()") + } + + if !c.HandshakeStateBuilt { + err := c.BuildHandshakeState() + if err != nil { + return err + } + } + + privateState := c.HandshakeState.getPrivatePtr() + c.handshakeErr = c.clientHandshakeWithState(privateState) + c.HandshakeState = *privateState.getPublicPtr() + + if c.handshakeErr == nil { + c.handshakes++ + } else { + // If an error occurred during the hadshake try to flush the + // alert that might be left in the buffer. + c.flush() + } + + if c.handshakeErr == nil && !c.handshakeComplete { + panic("handshake should have had a result.") + } + + // Wake any other goroutines that are waiting for this handshake to complete. + c.handshakeCond.Broadcast() + c.handshakeCond = nil + + return c.handshakeErr +} + +// c.out.Mutex <= L; c.handshakeMutex <= L. +func (c *UConn) clientHandshakeWithState(hs *clientHandshakeState) error { + // This code was copied almost as is from tls/handshake_client.go + if c.config == nil { + c.config = &Config{} + } + + // This may be a renegotiation handshake, in which case some fields + // need to be reset. + c.didResume = false + + if len(c.config.ServerName) == 0 && !c.config.InsecureSkipVerify { + return errors.New("tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config") + } + + nextProtosLength := 0 + for _, proto := range c.config.NextProtos { + if l := len(proto); l == 0 || l > 255 { + return errors.New("tls: invalid NextProtos value") + } else { + nextProtosLength += 1 + l + } + } + if nextProtosLength > 0xffff { + return errors.New("tls: NextProtos values too large") + } + + var session *ClientSessionState + sessionCache := c.config.ClientSessionCache + cacheKey := clientSessionCacheKey(c.conn.RemoteAddr(), c.config) + + // If sessionCache is set but session itself isn't - try to retrieve session from cache + if sessionCache != nil && hs.session != nil { + hs.hello.ticketSupported = true + // Session resumption is not allowed if renegotiating because + // renegotiation is primarily used to allow a client to send a client + // certificate, which would be skipped if session resumption occurred. + if c.handshakes == 0 { + // Try to resume a previously negotiated TLS session, if + // available. + candidateSession, ok := sessionCache.Get(cacheKey) + if ok { + // Check that the ciphersuite/version used for the + // previous session are still valid. + cipherSuiteOk := false + for _, id := range hs.hello.cipherSuites { + if id == candidateSession.cipherSuite { + cipherSuiteOk = true + break + } + } + + versOk := candidateSession.vers >= c.config.minVersion() && + candidateSession.vers <= c.config.maxVersion() + if versOk && cipherSuiteOk { + session = candidateSession + } + if session != nil { + hs.hello.sessionTicket = session.sessionTicket + // A random session ID is used to detect when the + // server accepted the ticket and is resuming a session + // (see RFC 5077). + hs.hello.sessionId = make([]byte, 16) + if _, err := io.ReadFull(c.config.rand(), hs.hello.sessionId); err != nil { + return errors.New("tls: short read from Rand: " + err.Error()) + } + } + hs.session = session + } + } + } + + if err := hs.handshake(); err != nil { + return err + } + // If we had a successful handshake and hs.session is different from the one already cached - cache a new one + if sessionCache != nil && hs.session != nil && hs.session != session { + sessionCache.Put(cacheKey, hs.session) + } + return nil +} + +func (uconn *UConn) applyConfig() error { + for _, ext := range uconn.Extensions { + err := ext.writeToUConn(uconn) + if err != nil { + return err + } + } + return nil +} + +func (uconn *UConn) marshalClientHello() error { + hello := uconn.HandshakeState.Hello + headerLength := 2 + 32 + 1 + len(hello.SessionId) + + 2 + len(hello.CipherSuites)*2 + + 1 + len(hello.CompressionMethods) + + extensionsLen := 0 + var paddingExt *FakePaddingExtension + for _, ext := range uconn.Extensions { + if pe, ok := ext.(*FakePaddingExtension); !ok { + // If not padding - just add length of extension to total length + extensionsLen += ext.Len() + } else { + // If padding - process it later + if paddingExt == nil { + paddingExt = pe + } else { + return errors.New("Multiple padding extensions!") + } + } + } + + if paddingExt != nil { + // determine padding extension presence and length + paddingExt.Update(headerLength + 4 + extensionsLen + 2) + extensionsLen += paddingExt.Len() + } + + helloLen := headerLength + if len(uconn.Extensions) > 0 { + helloLen += 2 + extensionsLen // 2 bytes for extensions' length + } + + helloBuffer := bytes.Buffer{} + bufferedWriter := bufio.NewWriterSize(&helloBuffer, helloLen+4) // 1 byte for tls record type, 3 for length + // We use buffered Writer to avoid checking write errors after every Write(): whenever first error happens + // Write() will become noop, and error will be accessible via Flush(), which is called once in the end + + binary.Write(bufferedWriter, binary.BigEndian, typeClientHello) + helloLenBytes := []byte{byte(helloLen >> 16), byte(helloLen >> 8), byte(helloLen)} // poor man's uint24 + binary.Write(bufferedWriter, binary.BigEndian, helloLenBytes) + binary.Write(bufferedWriter, binary.BigEndian, hello.Vers) + + binary.Write(bufferedWriter, binary.BigEndian, hello.Random) + + binary.Write(bufferedWriter, binary.BigEndian, uint8(len(hello.SessionId))) + binary.Write(bufferedWriter, binary.BigEndian, hello.SessionId) + + binary.Write(bufferedWriter, binary.BigEndian, uint16(len(hello.CipherSuites)<<1)) + for _, suite := range hello.CipherSuites { + binary.Write(bufferedWriter, binary.BigEndian, suite) + } + + binary.Write(bufferedWriter, binary.BigEndian, uint8(len(hello.CompressionMethods))) + binary.Write(bufferedWriter, binary.BigEndian, hello.CompressionMethods) + + if len(uconn.Extensions) > 0 { + binary.Write(bufferedWriter, binary.BigEndian, uint16(extensionsLen)) + for _, ext := range uconn.Extensions { + bufferedWriter.ReadFrom(ext) + } + } + + if helloBuffer.Len() != 4+helloLen { + return errors.New("utls: unexpected ClientHello length. Expected: " + strconv.Itoa(4+helloLen) + + ". Got: " + strconv.Itoa(helloBuffer.Len())) + } + + err := bufferedWriter.Flush() + if err != nil { + return err + } + + hello.Raw = helloBuffer.Bytes() + return nil +} diff --git a/u_conn_test.go b/u_conn_test.go new file mode 100644 index 0000000..6858833 --- /dev/null +++ b/u_conn_test.go @@ -0,0 +1,447 @@ +package tls + +import ( + "bytes" + "fmt" + "io" + "net" + "os" + "os/exec" + "strings" + "testing" + "time" +) + +/* +TODO: +Reuse examples in tests? +Add tests for randomized and no parrot +Add session ticket tests +Add set client random tests +*/ + +func TestUTLSMarshalNoOp(t *testing.T) { + // we rely on + str := "We rely on clientHelloMsg.marshal() not doing anything if clientHelloMsg.raw is set" + cHello, err := makeClientHello(getUTLSTestConfig()) + if err != nil { + t.Errorf("Got error: %s; expected to succeed", err) + } + cHello.raw = []byte(str) + marshalledHello := cHello.marshal() + if strings.Compare(string(marshalledHello), str) != 0 { + t.Errorf("clientHelloMsg.marshal() is not NOOP! Expected to get: %s, got: %s", str, string(marshalledHello)) + } +} + +func runUTLSClientTestForVersion(t *testing.T, template *clientTest, prefix, option string, helloID ClientHelloID) { + test := *template + test.name = prefix + test.name + if len(test.command) == 0 { + test.command = defaultClientCommand + } + test.command = append([]string(nil), test.command...) + test.command = append(test.command, option) + test.runUTLS(t, *update, helloID) +} + +func runUTLSClientTestTLS12(t *testing.T, template *clientTest, helloID ClientHelloID) { + runUTLSClientTestForVersion(t, template, "TLSv12-", "-tls1_2", helloID) +} + +func (test *clientTest) runUTLS(t *testing.T, write bool, helloID ClientHelloID) { + checkOpenSSLVersion(t) + + var clientConn, serverConn net.Conn + var recordingConn *recordingConn + var childProcess *exec.Cmd + var stdin opensslInput + var stdout *opensslOutputSink + + if write { + var err error + recordingConn, childProcess, stdin, stdout, err = test.connFromCommand() + if err != nil { + t.Fatalf("Failed to start subcommand: %s", err) + } + clientConn = recordingConn + } else { + clientConn, serverConn = net.Pipe() + } + + config := test.config + if config == nil { + t.Error("Explicit config is mandatory") + return + } + client := UClient(clientConn, config, helloID) + + doneChan := make(chan bool) + go func() { + defer func() { doneChan <- true }() + defer clientConn.Close() + defer client.Close() + + err := client.Handshake() + if err != nil { + t.Errorf("Client.Handshake() failed: %s", err) + return + } + + if _, err := client.Write([]byte("hello\n")); err != nil { + t.Errorf("Client.Write failed: %s", err) + return + } + + for i := 1; i <= test.numRenegotiations; i++ { + // The initial handshake will generate a + // handshakeComplete signal which needs to be quashed. + if i == 1 && write { + <-stdout.handshakeComplete + } + + // OpenSSL will try to interleave application data and + // a renegotiation if we send both concurrently. + // Therefore: ask OpensSSL to start a renegotiation, run + // a goroutine to call client.Read and thus process the + // renegotiation request, watch for OpenSSL's stdout to + // indicate that the handshake is complete and, + // finally, have OpenSSL write something to cause + // client.Read to complete. + if write { + stdin <- opensslRenegotiate + } + + signalChan := make(chan struct{}) + + go func() { + defer func() { signalChan <- struct{}{} }() + + buf := make([]byte, 256) + n, err := client.Read(buf) + + if test.checkRenegotiationError != nil { + newErr := test.checkRenegotiationError(i, err) + if err != nil && newErr == nil { + return + } + err = newErr + } + + if err != nil { + t.Errorf("Client.Read failed after renegotiation #%d: %s", i, err) + return + } + + buf = buf[:n] + if !bytes.Equal([]byte(opensslSentinel), buf) { + t.Errorf("Client.Read returned %q, but wanted %q", string(buf), opensslSentinel) + } + + if expected := i + 1; client.handshakes != expected { + t.Errorf("client should have recorded %d handshakes, but believes that %d have occurred", expected, client.handshakes) + } + }() + + if write && test.renegotiationExpectedToFail != i { + <-stdout.handshakeComplete + stdin <- opensslSendSentinel + } + <-signalChan + } + + if test.validate != nil { + if err := test.validate(client.ConnectionState()); err != nil { + t.Errorf("validate callback returned error: %s", err) + } + } + }() + + if !write { + flows, err := test.loadData() + if err != nil { + t.Fatalf("%s: failed to load data from %s: %v", test.name, test.dataPath(), err) + } + for i, b := range flows { + if i%2 == 1 { + serverConn.Write(b) + continue + } + bb := make([]byte, len(b)) + _, err := io.ReadFull(serverConn, bb) + if err != nil { + t.Fatalf("%s #%d: %s", test.name, i, err) + } + if !bytes.Equal(b, bb) { + t.Fatalf("%s #%d: mismatch on read: got:%x want:%x", test.name, i, bb, b) + } + } + serverConn.Close() + } + + <-doneChan + + if write { + path := test.dataPath() + out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + t.Fatalf("Failed to create output file: %s", err) + } + defer out.Close() + recordingConn.Close() + close(stdin) + childProcess.Process.Kill() + childProcess.Wait() + if len(recordingConn.flows) < 3 { + os.Stdout.Write(childProcess.Stdout.(*opensslOutputSink).all) + t.Fatalf("Client connection didn't work") + } + recordingConn.WriteTo(out) + fmt.Printf("Wrote %s\n", path) + } +} + +func TestUTLSHandshakeClientParrotAndroid_5_1(t *testing.T) { + helloID := HelloAndroid_5_1_Browser + + // As this package sometimes has to modify global vars cipherSuites and supportedSignatureAlgorithms, + // we'll back them up and restore after running the tests. + cipherSuitesBackup := make([]*cipherSuite, len(cipherSuites)) + supportedSignatureAlgorithmsBackup := make([]signatureAndHash, len(supportedSignatureAlgorithms)) + copy(cipherSuitesBackup, cipherSuites) + copy(supportedSignatureAlgorithmsBackup, supportedSignatureAlgorithms) + defer func() { + cipherSuites = cipherSuitesBackup + supportedSignatureAlgorithms = supportedSignatureAlgorithmsBackup + }() + + // Android 5.1 offers old cipher ids for these, but current versions of OpenSSL no longer recognize old ids + // testUTLSHandshakeClientECDHE_ECDSA_WITH_CHACHA20_POLY1305(t, helloID) + // testUTLSHandshakeClientECDHE_RSA_WITH_CHACHA20_POLY1305(t, helloID) + + testUTLSHandshakeClientECDHE_RSA_AES128_GCM_SHA256(t, helloID) + testUTLSHandshakeClientECDHE_ECDSA_AES128_GCM_SHA256(t, helloID) + testUTLSHandshakeClientECDHE_RSA_AES256_CBC_SHA(t, helloID) + testUTLSHandshakeClientECDHE_ECDSA_AES256_CBC_SHA(t, helloID) + testUTLSHandshakeClientECDHE_RSA_AES128_CBC_SHA(t, helloID) + testUTLSHandshakeClientECDHE_ECDSA_AES128_CBC_SHA(t, helloID) + + testUTLSHandshakeClientRSA_AES128_GCM_SHA256(t, helloID) +} + +// Enable whenever EMS is implemented +func disabledtestUTLSHandshakeClientParrotAndroid_6_0(t *testing.T) { + helloID := HelloAndroid_6_0_Browser + + // As this package sometimes has to modify global vars cipherSuites and supportedSignatureAlgorithms, + // we'll back them up and restore after running the tests. + cipherSuitesBackup := make([]*cipherSuite, len(cipherSuites)) + supportedSignatureAlgorithmsBackup := make([]signatureAndHash, len(supportedSignatureAlgorithms)) + copy(cipherSuitesBackup, cipherSuites) + copy(supportedSignatureAlgorithmsBackup, supportedSignatureAlgorithms) + defer func() { + cipherSuites = cipherSuitesBackup + supportedSignatureAlgorithms = supportedSignatureAlgorithmsBackup + }() + + // Android 6.0 offers old cipher ids for these, but current versions of OpenSSL no longer recognize old ids + // testUTLSHandshakeClientECDHE_ECDSA_WITH_CHACHA20_POLY1305(t, helloID) + // testUTLSHandshakeClientECDHE_RSA_WITH_CHACHA20_POLY1305(t, helloID) + + testUTLSHandshakeClientECDHE_ECDSA_AES128_GCM_SHA256(t, helloID) + testUTLSHandshakeClientECDHE_RSA_AES128_GCM_SHA256(t, helloID) + testUTLSHandshakeClientECDHE_ECDSA_AES256_CBC_SHA(t, helloID) + testUTLSHandshakeClientECDHE_RSA_AES256_CBC_SHA(t, helloID) + testUTLSHandshakeClientECDHE_ECDSA_AES128_CBC_SHA(t, helloID) + testUTLSHandshakeClientECDHE_RSA_AES128_CBC_SHA(t, helloID) + + testUTLSHandshakeClientRSA_AES128_GCM_SHA256(t, helloID) +} + +// Enable whenever EMS is implemented +func disabledtestUTLSHandshakeClientParrotChrome_58(t *testing.T) { + helloID := HelloChrome_58 + + // As this package sometimes has to modify global vars cipherSuites and supportedSignatureAlgorithms, + // we'll back them up and restore after running the tests. + cipherSuitesBackup := make([]*cipherSuite, len(cipherSuites)) + supportedSignatureAlgorithmsBackup := make([]signatureAndHash, len(supportedSignatureAlgorithms)) + copy(cipherSuitesBackup, cipherSuites) + copy(supportedSignatureAlgorithmsBackup, supportedSignatureAlgorithms) + defer func() { + cipherSuites = cipherSuitesBackup + supportedSignatureAlgorithms = supportedSignatureAlgorithmsBackup + }() + + testUTLSHandshakeClientECDHE_ECDSA_AES128_GCM_SHA256(t, helloID) + testUTLSHandshakeClientECDHE_RSA_AES128_GCM_SHA256(t, helloID) + testUTLSHandshakeClientECDHE_ECDSA_AES256_GCM_SHA256(t, helloID) + testUTLSHandshakeClientECDHE_RSA_AES256_GCM_SHA256(t, helloID) + + testUTLSHandshakeClientECDHE_ECDSA_WITH_CHACHA20_POLY1305(t, helloID) + testUTLSHandshakeClientECDHE_RSA_WITH_CHACHA20_POLY1305(t, helloID) + + testUTLSHandshakeClientECDHE_RSA_AES128_CBC_SHA(t, helloID) + testUTLSHandshakeClientECDHE_RSA_AES256_CBC_SHA(t, helloID) + + testUTLSHandshakeClientRSA_AES128_GCM_SHA256(t, helloID) +} + +func getUTLSTestConfig() *Config { + testUTLSConfig := &Config{ + Time: func() time.Time { return time.Unix(0, 0) }, + Rand: zeroSource{}, + InsecureSkipVerify: true, + MinVersion: VersionSSL30, + MaxVersion: VersionTLS12, + CipherSuites: allCipherSuites(), + } + return testUTLSConfig +} + +func testUTLSHandshakeClientECDHE_RSA_AES128_CBC_SHA(t *testing.T, helloID ClientHelloID) { + config := getUTLSTestConfig() + opensslCipherName := "ECDHE-RSA-AES128-SHA" + test := &clientTest{ + name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), + command: []string{"openssl", "s_server", "-cipher", opensslCipherName}, + config: config, + } + + runUTLSClientTestTLS12(t, test, helloID) +} + +func testUTLSHandshakeClientECDHE_RSA_AES256_CBC_SHA(t *testing.T, helloID ClientHelloID) { + config := getUTLSTestConfig() + opensslCipherName := "ECDHE-RSA-AES256-SHA" + test := &clientTest{ + name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), + command: []string{"openssl", "s_server", "-cipher", opensslCipherName}, + config: config, + } + + runUTLSClientTestTLS12(t, test, helloID) +} + +func testUTLSHandshakeClientECDHE_ECDSA_AES128_CBC_SHA(t *testing.T, helloID ClientHelloID) { + config := getUTLSTestConfig() + opensslCipherName := "ECDHE-ECDSA-AES128-SHA" + test := &clientTest{ + name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), + command: []string{"openssl", "s_server", "-cipher", opensslCipherName}, + cert: testECDSACertificate, + key: testECDSAPrivateKey, + config: config, + } + + runUTLSClientTestTLS12(t, test, helloID) +} + +func testUTLSHandshakeClientECDHE_ECDSA_AES256_CBC_SHA(t *testing.T, helloID ClientHelloID) { + config := getUTLSTestConfig() + opensslCipherName := "ECDHE-ECDSA-AES256-SHA" + test := &clientTest{ + name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), + command: []string{"openssl", "s_server", "-cipher", opensslCipherName}, + cert: testECDSACertificate, + key: testECDSAPrivateKey, + config: config, + } + + runUTLSClientTestTLS12(t, test, helloID) +} + +func testUTLSHandshakeClientRSA_AES128_GCM_SHA256(t *testing.T, helloID ClientHelloID) { + config := getUTLSTestConfig() + opensslCipherName := "AES128-GCM-SHA256" + test := &clientTest{ + name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), + command: []string{"openssl", "s_server", "-cipher", opensslCipherName}, + config: config, + } + + runUTLSClientTestTLS12(t, test, helloID) +} + +func testUTLSHandshakeClientECDHE_ECDSA_AES128_GCM_SHA256(t *testing.T, helloID ClientHelloID) { + config := getUTLSTestConfig() + + opensslCipherName := "ECDHE-ECDSA-AES128-GCM-SHA256" + test := &clientTest{ + name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), + command: []string{"openssl", "s_server", "-cipher", opensslCipherName}, + cert: testECDSACertificate, + key: testECDSAPrivateKey, + config: config, + } + + runUTLSClientTestTLS12(t, test, helloID) +} + +func testUTLSHandshakeClientECDHE_RSA_AES128_GCM_SHA256(t *testing.T, helloID ClientHelloID) { + config := getUTLSTestConfig() + + opensslCipherName := "ECDHE-RSA-AES128-GCM-SHA256" + test := &clientTest{ + name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), + command: []string{"openssl", "s_server", "-cipher", opensslCipherName}, + config: config, + } + + runUTLSClientTestTLS12(t, test, helloID) +} + +func testUTLSHandshakeClientECDHE_ECDSA_AES256_GCM_SHA256(t *testing.T, helloID ClientHelloID) { + config := getUTLSTestConfig() + opensslCipherName := "ECDHE-ECDSA-AES256-GCM-SHA256" + test := &clientTest{ + name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), + command: []string{"openssl", "s_server", "-cipher", opensslCipherName}, + cert: testECDSACertificate, + key: testECDSAPrivateKey, + config: config, + } + + runUTLSClientTestTLS12(t, test, helloID) +} + +func testUTLSHandshakeClientECDHE_RSA_AES256_GCM_SHA256(t *testing.T, helloID ClientHelloID) { + config := getUTLSTestConfig() + + opensslCipherName := "ECDHE-RSA-AES128-GCM-SHA256" + test := &clientTest{ + name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), + command: []string{"openssl", "s_server", "-cipher", opensslCipherName}, + config: config, + } + + runUTLSClientTestTLS12(t, test, helloID) +} + +func testUTLSHandshakeClientECDHE_RSA_WITH_CHACHA20_POLY1305(t *testing.T, helloID ClientHelloID) { + config := getUTLSTestConfig() + config.CipherSuites = []uint16{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305} + opensslCipherName := "ECDHE-RSA-CHACHA20-POLY1305" + test := &clientTest{ + name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), + command: []string{"openssl", "s_server", "-cipher", opensslCipherName}, + config: config, + } + + runUTLSClientTestTLS12(t, test, helloID) +} + +func testUTLSHandshakeClientECDHE_ECDSA_WITH_CHACHA20_POLY1305(t *testing.T, helloID ClientHelloID) { + config := getUTLSTestConfig() + config.CipherSuites = []uint16{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305} + opensslCipherName := "ECDHE-ECDSA-CHACHA20-POLY1305" + test := &clientTest{ + name: "UTLS-" + opensslCipherName + "-" + helloID.Str(), + command: []string{"openssl", "s_server", "-cipher", opensslCipherName}, + config: config, + cert: testECDSACertificate, + key: testECDSAPrivateKey, + } + + runUTLSClientTestTLS12(t, test, helloID) +} diff --git a/u_parrots.go b/u_parrots.go new file mode 100644 index 0000000..266405b --- /dev/null +++ b/u_parrots.go @@ -0,0 +1,644 @@ +package tls + +import ( + "crypto/rand" + "crypto/sha256" + "errors" + "io" + "math/big" + "sort" + "strconv" + "time" +) + +func (uconn *UConn) generateClientHelloConfig(id ClientHelloID) error { + uconn.clientHelloID = id + switch uconn.clientHelloID { + case HelloFirefox_53_WIP: + return uconn.parrotFirefox_53_WIP() + + case HelloAndroid_6_0_Browser: + return uconn.parrotAndroid_6_0() + case HelloAndroid_5_1_Browser: + return uconn.parrotAndroid_5_1() + + case HelloChrome_58: + return uconn.parrotChrome_58() + + case HelloRandomizedALPN: + return uconn.parrotRandomizedALPN() + case HelloRandomizedNoALPN: + return uconn.parrotRandomizedNoALPN() + + case HelloCustom: + return uconn.parrotCustom() + + // following ClientHello's are aliases, so we call generateClientHelloConfig() again to set the correct id + case HelloRandomized: + if tossBiasedCoin(0.5) { + return uconn.generateClientHelloConfig(HelloRandomizedALPN) + } else { + return uconn.generateClientHelloConfig(HelloRandomizedNoALPN) + } + case HelloAndroid_Auto: + return uconn.generateClientHelloConfig(HelloAndroid_6_0_Browser) + case HelloFirefox_Auto: + return uconn.generateClientHelloConfig(HelloFirefox_53_WIP) + case HelloChrome_Auto: + return uconn.generateClientHelloConfig(HelloChrome_58) + + default: + return errors.New("Unknown ParrotID: " + id.Str()) + } + return nil +} + +// Fills clientHello header(everything but extensions) fields, which are not set explicitly yet, with defaults +func (uconn *UConn) fillClientHelloHeader() error { + hello := uconn.HandshakeState.Hello + if hello.Vers == 0 { + hello.Vers = VersionTLS12 + } + switch len(hello.Random) { + case 0: + hello.Random = make([]byte, 32) + _, err := io.ReadFull(uconn.config.rand(), hello.Random) + if err != nil { + return errors.New("tls: short read from Rand: " + err.Error()) + } + case 32: + // carry on + default: + return errors.New("ClientHello expected length: 32 bytes. Got: " + + strconv.Itoa(len(hello.Random)) + " bytes") + } + if len(hello.CipherSuites) == 0 { + hello.CipherSuites = defaultCipherSuites() + } + if len(hello.CompressionMethods) == 0 { + hello.CompressionMethods = []uint8{compressionNone} + } + return nil +} + +func (uconn *UConn) parrotFirefox_53_WIP() error { + /* + Work in progress! + TODO: double check session id generation + TODO: add firefox-style padding + */ + hello := uconn.HandshakeState.Hello + session := uconn.HandshakeState.Session + hello.CipherSuites = []uint16{ + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + FAKE_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + FAKE_TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + } + err := uconn.fillClientHelloHeader() + if err != nil { + return err + } + + sni := SNIExtension{uconn.config.ServerName} + ems := FakeExtendedMasterSecretExtension{} + reneg := RenegotiationInfoExtension{renegotiation: RenegotiateOnceAsClient} + curves := SupportedCurvesExtension{[]CurveID{X25519, CurveP256, CurveP384, CurveP521}} + points := SupportedPointsExtension{SupportedPoints: []byte{pointFormatUncompressed}} + sessionTicket := SessionTicketExtension{Session: session} + if session != nil { + sessionTicket.Session = session + if len(session.SessionTicket()) > 0 { + hello.SessionId = make([]byte, 32) + _, err := io.ReadFull(uconn.config.rand(), hello.SessionId) + if err != nil { + return errors.New("tls: short read from Rand: " + err.Error()) + } + } + } + alpn := ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}} + status := StatusRequestExtension{} + sigAndHash := SignatureAlgorithmsExtension{SignatureAndHashes: []SignatureAndHash{ + {hashSHA256, signatureECDSA}, + {hashSHA384, signatureECDSA}, + {disabledHashSHA512, signatureECDSA}, + fakeRsaPssSha256, + fakeRsaPssSha384, + fakeRsaPssSha512, + {hashSHA256, signatureRSA}, + {hashSHA384, signatureRSA}, + {disabledHashSHA512, signatureRSA}, + {hashSHA1, signatureECDSA}, + {hashSHA1, signatureRSA}}, + } + uconn.Extensions = []TLSExtension{ + &sni, + &ems, + &reneg, + &curves, + &points, + &sessionTicket, + &alpn, + &status, + &sigAndHash, + } + return nil +} + +func (uconn *UConn) parrotAndroid_6_0() error { + hello := uconn.HandshakeState.Hello + session := uconn.HandshakeState.Session + + appendToGlobalCipherSuites(&cipherSuite{OLD_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 12, + ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadChaCha20Poly1305}) + appendToGlobalCipherSuites(&cipherSuite{OLD_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 12, + ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadChaCha20Poly1305}) + hello.CipherSuites = []uint16{ + OLD_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + OLD_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + FAKE_OLD_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + FAKE_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + FAKE_TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + FAKE_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + FAKE_TLS_EMPTY_RENEGOTIATION_INFO_SCSV, + } + err := uconn.fillClientHelloHeader() + if err != nil { + return err + } + + sni := SNIExtension{uconn.config.ServerName} + ems := FakeExtendedMasterSecretExtension{} + sessionTicket := SessionTicketExtension{Session: session} + if session != nil { + sessionTicket.Session = session + if len(session.SessionTicket()) > 0 { + sessionId := sha256.Sum256(session.SessionTicket()) + hello.SessionId = sessionId[:] + } + } + appendToGlobalSigAlgs(disabledHashSHA512, signatureRSA) + appendToGlobalSigAlgs(disabledHashSHA512, signatureECDSA) + sigAndHash := SignatureAlgorithmsExtension{SignatureAndHashes: []SignatureAndHash{ + {disabledHashSHA512, signatureRSA}, + {disabledHashSHA512, signatureECDSA}, + {hashSHA384, signatureRSA}, + {hashSHA384, signatureECDSA}, + {hashSHA256, signatureRSA}, + {hashSHA256, signatureECDSA}, + {fakeHashSHA224, signatureRSA}, + {fakeHashSHA224, signatureECDSA}, + {hashSHA1, signatureRSA}, + {hashSHA1, signatureECDSA}}, + } + status := StatusRequestExtension{} + npn := NPNExtension{} + sct := SCTExtension{} + alpn := ALPNExtension{AlpnProtocols: []string{"http/1.1", "spdy/8.1"}} + points := SupportedPointsExtension{SupportedPoints: []byte{pointFormatUncompressed}} + curves := SupportedCurvesExtension{[]CurveID{CurveP256, CurveP384}} + padding := FakePaddingExtension{GetPaddingLen: boringPaddingStyle} + + uconn.Extensions = []TLSExtension{ + &sni, + &ems, + &sessionTicket, + &sigAndHash, + &status, + &npn, + &sct, + &alpn, + &points, + &curves, + &padding, + } + return nil +} +func (uconn *UConn) parrotAndroid_5_1() error { + hello := uconn.HandshakeState.Hello + session := uconn.HandshakeState.Session + + appendToGlobalCipherSuites(&cipherSuite{OLD_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 12, + ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadChaCha20Poly1305}) + appendToGlobalCipherSuites(&cipherSuite{OLD_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, 12, + ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadChaCha20Poly1305}) + hello.CipherSuites = []uint16{ + OLD_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + OLD_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + FAKE_OLD_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + FAKE_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + FAKE_TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + FAKE_TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_RSA_WITH_RC4_128_SHA, + TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_RC4_128_SHA, + FAKE_TLS_RSA_WITH_RC4_128_MD5, + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + FAKE_TLS_EMPTY_RENEGOTIATION_INFO_SCSV, + } + err := uconn.fillClientHelloHeader() + if err != nil { + return err + } + + sni := SNIExtension{uconn.config.ServerName} + sessionTicket := SessionTicketExtension{Session: session} + if session != nil { + sessionTicket.Session = session + if len(session.SessionTicket()) > 0 { + sessionId := sha256.Sum256(session.SessionTicket()) + hello.SessionId = sessionId[:] + } + } + appendToGlobalSigAlgs(disabledHashSHA512, signatureRSA) + appendToGlobalSigAlgs(disabledHashSHA512, signatureECDSA) + sigAndHash := SignatureAlgorithmsExtension{SignatureAndHashes: []SignatureAndHash{ + {disabledHashSHA512, signatureRSA}, + {disabledHashSHA512, signatureECDSA}, + {hashSHA384, signatureRSA}, + {hashSHA384, signatureECDSA}, + {hashSHA256, signatureRSA}, + {hashSHA256, signatureECDSA}, + {fakeHashSHA224, signatureRSA}, + {fakeHashSHA224, signatureECDSA}, + {hashSHA1, signatureRSA}, + {hashSHA1, signatureECDSA}}, + } + status := StatusRequestExtension{} + npn := NPNExtension{} + sct := SCTExtension{} + alpn := ALPNExtension{AlpnProtocols: []string{"http/1.1", "spdy/3", "spdy/3.1"}} + points := SupportedPointsExtension{SupportedPoints: []byte{pointFormatUncompressed}} + curves := SupportedCurvesExtension{[]CurveID{CurveP256, CurveP384, CurveP521}} + padding := FakePaddingExtension{GetPaddingLen: boringPaddingStyle} + + uconn.Extensions = []TLSExtension{ + &sni, + &sessionTicket, + &sigAndHash, + &status, + &npn, + &sct, + &alpn, + &points, + &curves, + &padding, + } + return nil +} + +func (uconn *UConn) parrotChrome_58() error { + hello := uconn.HandshakeState.Hello + session := uconn.HandshakeState.Session + + hello.CipherSuites = []uint16{ + GetBoringGREASEValue(hello.Random, ssl_grease_cipher), + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + } + err := uconn.fillClientHelloHeader() + if err != nil { + return err + } + + grease_ext1 := GetBoringGREASEValue(hello.Random, ssl_grease_extension1) + grease_ext2 := GetBoringGREASEValue(hello.Random, ssl_grease_extension2) + if grease_ext1 == grease_ext2 { + grease_ext2 ^= 0x1010 + } + + grease1 := FakeGREASEExtension{Value: grease_ext1} + reneg := RenegotiationInfoExtension{renegotiation: RenegotiateOnceAsClient} + sni := SNIExtension{uconn.config.ServerName} + ems := FakeExtendedMasterSecretExtension{} + sessionTicket := SessionTicketExtension{Session: session} + if session != nil { + sessionTicket.Session = session + if len(session.SessionTicket()) > 0 { + sessionId := sha256.Sum256(session.SessionTicket()) + hello.SessionId = sessionId[:] + } + } + sigAndHash := SignatureAlgorithmsExtension{SignatureAndHashes: []SignatureAndHash{ + {hashSHA256, signatureECDSA}, + fakeRsaPssSha256, + {hashSHA256, signatureRSA}, + {hashSHA384, signatureECDSA}, + fakeRsaPssSha384, + {hashSHA384, signatureRSA}, + fakeRsaPssSha512, + {disabledHashSHA512, signatureRSA}, + {hashSHA1, signatureRSA}}, + } + status := StatusRequestExtension{} + sct := SCTExtension{} + alpn := ALPNExtension{AlpnProtocols: []string{"h2", "http/1.1"}} + channelId := FakeChannelIDExtension{} + points := SupportedPointsExtension{SupportedPoints: []byte{pointFormatUncompressed}} + curves := SupportedCurvesExtension{[]CurveID{CurveID(GetBoringGREASEValue(hello.Random, ssl_grease_group)), + X25519, CurveP256, CurveP384}} + grease2 := FakeGREASEExtension{Value: grease_ext2, Body: []byte{0}} + padding := FakePaddingExtension{GetPaddingLen: boringPaddingStyle} + + uconn.Extensions = []TLSExtension{ + &grease1, + &reneg, + &sni, + &ems, + &sessionTicket, + &sigAndHash, + &status, + &sct, + &alpn, + &channelId, + &points, + &curves, + &grease2, + &padding, + } + return nil +} + +func (uconn *UConn) parrotRandomizedALPN() error { + err := uconn.parrotRandomizedNoALPN() + if len(uconn.config.NextProtos) == 0 { + // if user didn't specify alpn, choose something popular + uconn.config.NextProtos = []string{"h2", "http/1.1"} + } + alpn := ALPNExtension{AlpnProtocols: uconn.config.NextProtos} + uconn.Extensions = append(uconn.Extensions, &alpn) + return err +} + +func (uconn *UConn) parrotRandomizedNoALPN() error { + hello := uconn.HandshakeState.Hello + session := uconn.HandshakeState.Session + + hello.CipherSuites = make([]uint16, len(defaultCipherSuites())) + copy(hello.CipherSuites, defaultCipherSuites()) + hello.CipherSuites = removeRandomCiphers(hello.CipherSuites, 0.4) + err := shuffleCiphers(hello.CipherSuites) + if err != nil { + return err + } + err = uconn.fillClientHelloHeader() + if err != nil { + return err + } + + sni := SNIExtension{uconn.config.ServerName} + sessionTicket := SessionTicketExtension{Session: session} + if session != nil { + sessionTicket.Session = session + if len(session.SessionTicket()) > 0 { + sessionId := sha256.Sum256(session.SessionTicket()) + hello.SessionId = sessionId[:] + } + } + sigAndHashAlgos := []SignatureAndHash{ + {hashSHA256, signatureECDSA}, + {hashSHA256, signatureRSA}, + {hashSHA384, signatureECDSA}, + {hashSHA384, signatureRSA}, + {hashSHA1, signatureRSA}, + } + if tossBiasedCoin(0.5) { + sigAndHashAlgos = append(sigAndHashAlgos, SignatureAndHash{disabledHashSHA512, signatureECDSA}) + appendToGlobalSigAlgs(disabledHashSHA512, signatureECDSA) + } + if tossBiasedCoin(0.5) { + sigAndHashAlgos = append(sigAndHashAlgos, SignatureAndHash{disabledHashSHA512, signatureRSA}) + appendToGlobalSigAlgs(disabledHashSHA512, signatureRSA) + } + if tossBiasedCoin(0.5) { + sigAndHashAlgos = append(sigAndHashAlgos, SignatureAndHash{hashSHA1, signatureECDSA}) + } + err = shuffleSignatures(sigAndHashAlgos) + if err != nil { + return err + } + sigAndHash := SignatureAlgorithmsExtension{SignatureAndHashes: sigAndHashAlgos} + + status := StatusRequestExtension{} + sct := SCTExtension{} + points := SupportedPointsExtension{SupportedPoints: []byte{pointFormatUncompressed}} + + curveIDs := []CurveID{} + if tossBiasedCoin(0.7) { + curveIDs = append(curveIDs, X25519) + } + curveIDs = append(curveIDs, CurveP256, CurveP384) + if tossBiasedCoin(0.3) { + curveIDs = append(curveIDs, CurveP521) + } + curves := SupportedCurvesExtension{curveIDs} + + padding := FakePaddingExtension{GetPaddingLen: boringPaddingStyle} + reneg := RenegotiationInfoExtension{renegotiation: RenegotiateOnceAsClient} + + uconn.Extensions = []TLSExtension{ + &sni, + &sessionTicket, + &sigAndHash, + &points, + &curves, + } + + if tossBiasedCoin(0.66) { + uconn.Extensions = append(uconn.Extensions, &padding) + } + if tossBiasedCoin(0.66) { + uconn.Extensions = append(uconn.Extensions, &status) + } + if tossBiasedCoin(0.55) { + uconn.Extensions = append(uconn.Extensions, &sct) + } + if tossBiasedCoin(0.44) { + uconn.Extensions = append(uconn.Extensions, &reneg) + } + err = shuffleTLSExtensions(uconn.Extensions) + if err != nil { + return err + } + return nil +} + +func (uconn *UConn) parrotCustom() error { + return uconn.fillClientHelloHeader() +} + +func tossBiasedCoin(probability float32) bool { + // probability is expected to be in [0,1] + // this function never returns errors for ease of use + const precision = 0xffff + threshold := float32(precision) * probability + value, err := getRandInt(precision) + if err != nil { + // I doubt that this code will ever actually be used, as other functions are expected to complain + // about used source of entropy. Nonetheless, this is more than enough for given purpose + return ((time.Now().Unix() & 1) == 0) + } + + if float32(value) <= threshold { + return true + } else { + return false + } +} + +func removeRandomCiphers(s []uint16, maxRemovalProbability float32) []uint16 { + // removes elements in place + // probability to remove increases for further elements + // never remove first cipher + if len(s) <= 1 { + return s + } + + // remove random elements + floatLen := float32(len(s)) + sliceLen := len(s) + for i := 1; i < sliceLen; i++ { + if tossBiasedCoin(maxRemovalProbability * float32(i) / floatLen) { + s = append(s[:i], s[i+1:]...) + sliceLen-- + i-- + } + } + return s +} + +func getRandInt(max int) (int, error) { + bigInt, err := rand.Int(rand.Reader, big.NewInt(int64(max))) + return int(bigInt.Int64()), err +} + +func getRandPerm(n int) ([]int, error) { + permArray := make([]int, n) + for i := 1; i < n; i++ { + j, err := getRandInt(i + 1) + if err != nil { + return permArray, err + } + permArray[i] = permArray[j] + permArray[j] = i + } + return permArray, nil +} + +func shuffleCiphers(s []uint16) error { + // shuffles array in place + ciphers := make(sortableCiphers, len(cipherSuites)) + perm, err := getRandPerm(len(cipherSuites)) + if err != nil { + return err + } + for i, suite := range cipherSuites { + ciphers[i] = sortableCipher{suite: suite.id, + isObsolete: ((suite.flags & suiteTLS12) == 0), + randomTag: perm[i]} + } + sort.Sort(ciphers) + s = ciphers.GetCiphers() + return nil +} + +type sortableCipher struct { + isObsolete bool + randomTag int + suite uint16 +} + +type sortableCiphers []sortableCipher + +func (ciphers sortableCiphers) Len() int { + return len(ciphers) +} + +func (ciphers sortableCiphers) Less(i, j int) bool { + if ciphers[i].isObsolete && !ciphers[j].isObsolete { + return false + } + if ciphers[j].isObsolete && !ciphers[i].isObsolete { + return true + } + return ciphers[i].randomTag < ciphers[j].randomTag +} + +func (ciphers sortableCiphers) Swap(i, j int) { + ciphers[i], ciphers[j] = ciphers[j], ciphers[i] +} + +func (ciphers sortableCiphers) GetCiphers() []uint16 { + cipherIDs := make([]uint16, len(ciphers)) + for i := range ciphers { + cipherIDs[i] = ciphers[i].suite + } + return cipherIDs +} + +// so much for generics +func shuffleTLSExtensions(s []TLSExtension) error { + // shuffles array in place + perm, err := getRandPerm(len(s)) + if err != nil { + return err + } + for i := range s { + s[i], s[perm[i]] = s[perm[i]], s[i] + } + return nil +} + +// so much for generics +func shuffleSignatures(s []SignatureAndHash) error { + // shuffles array in place + perm, err := getRandPerm(len(s)) + if err != nil { + return err + } + for i := range s { + s[i], s[perm[i]] = s[perm[i]], s[i] + } + return nil +} diff --git a/u_public.go b/u_public.go new file mode 100644 index 0000000..53f2049 --- /dev/null +++ b/u_public.go @@ -0,0 +1,383 @@ +package tls + +import ( + "crypto/cipher" + "crypto/x509" + "hash" +) + +type ClientHandshakeState struct { + C *Conn + ServerHello *ServerHelloMsg + Hello *ClientHelloMsg + Suite *CipherSuite + FinishedHash FinishedHash + MasterSecret []byte + Session *ClientSessionState +} + +// getPrivatePtr() methods make shallow copies + +func (chs *ClientHandshakeState) getPrivatePtr() *clientHandshakeState { + if chs == nil { + return nil + } else { + return &clientHandshakeState{ + c: chs.C, + serverHello: chs.ServerHello.getPrivatePtr(), + hello: chs.Hello.getPrivatePtr(), + suite: chs.Suite.getPrivatePtr(), + finishedHash: *chs.FinishedHash.getPrivatePtr(), + masterSecret: chs.MasterSecret, + session: chs.Session, + } + } +} + +func (chs *clientHandshakeState) getPublicPtr() *ClientHandshakeState { + if chs == nil { + return nil + } else { + return &ClientHandshakeState{ + C: chs.c, + ServerHello: chs.serverHello.getPublicPtr(), + Hello: chs.hello.getPublicPtr(), + Suite: chs.suite.getPublicPtr(), + FinishedHash: *chs.finishedHash.getPublicPtr(), + MasterSecret: chs.masterSecret, + Session: chs.session, + } + } +} + +type BensStruct serverHelloMsg + +type ServerHelloMsg struct { + Raw []byte + Vers uint16 + Random []byte + SessionId []byte + CipherSuite uint16 + CompressionMethod uint8 + NextProtoNeg bool + NextProtos []string + OcspStapling bool + Scts [][]byte + TicketSupported bool + SecureRenegotiation []byte + SecureRenegotiationSupported bool + AlpnProtocol string +} + +func (shm *ServerHelloMsg) getPrivatePtr() *serverHelloMsg { + if shm == nil { + return nil + } else { + return &serverHelloMsg{ + raw: shm.Raw, + vers: shm.Vers, + random: shm.Random, + sessionId: shm.SessionId, + cipherSuite: shm.CipherSuite, + compressionMethod: shm.CompressionMethod, + nextProtoNeg: shm.NextProtoNeg, + nextProtos: shm.NextProtos, + ocspStapling: shm.OcspStapling, + scts: shm.Scts, + ticketSupported: shm.TicketSupported, + secureRenegotiation: shm.SecureRenegotiation, + secureRenegotiationSupported: shm.SecureRenegotiationSupported, + alpnProtocol: shm.AlpnProtocol, + } + } +} + +func (shm *serverHelloMsg) getPublicPtr() *ServerHelloMsg { + if shm == nil { + return nil + } else { + return &ServerHelloMsg{ + Raw: shm.raw, + Vers: shm.vers, + Random: shm.random, + SessionId: shm.sessionId, + CipherSuite: shm.cipherSuite, + CompressionMethod: shm.compressionMethod, + NextProtoNeg: shm.nextProtoNeg, + NextProtos: shm.nextProtos, + OcspStapling: shm.ocspStapling, + Scts: shm.scts, + TicketSupported: shm.ticketSupported, + SecureRenegotiation: shm.secureRenegotiation, + SecureRenegotiationSupported: shm.secureRenegotiationSupported, + AlpnProtocol: shm.alpnProtocol, + } + } +} + +type ClientHelloMsg struct { + Raw []byte + Vers uint16 + Random []byte + SessionId []byte + CipherSuites []uint16 + CompressionMethods []uint8 + NextProtoNeg bool + ServerName string + OcspStapling bool + Scts bool + SupportedCurves []CurveID + SupportedPoints []uint8 + TicketSupported bool + SessionTicket []uint8 + SignatureAndHashes []SignatureAndHash + SecureRenegotiation []byte + SecureRenegotiationSupported bool + AlpnProtocols []string +} + +func (chm *ClientHelloMsg) getPrivatePtr() *clientHelloMsg { + if chm == nil { + return nil + } else { + return &clientHelloMsg{ + raw: chm.Raw, + vers: chm.Vers, + random: chm.Random, + sessionId: chm.SessionId, + cipherSuites: chm.CipherSuites, + compressionMethods: chm.CompressionMethods, + nextProtoNeg: chm.NextProtoNeg, + serverName: chm.ServerName, + ocspStapling: chm.OcspStapling, + scts: chm.Scts, + supportedCurves: chm.SupportedCurves, + supportedPoints: chm.SupportedPoints, + ticketSupported: chm.TicketSupported, + sessionTicket: chm.SessionTicket, + signatureAndHashes: sigAndHashGetMakePrivate(chm.SignatureAndHashes), + secureRenegotiation: chm.SecureRenegotiation, + secureRenegotiationSupported: chm.SecureRenegotiationSupported, + alpnProtocols: chm.AlpnProtocols, + } + } +} + +func (chm *clientHelloMsg) getPublicPtr() *ClientHelloMsg { + if chm == nil { + return nil + } else { + return &ClientHelloMsg{ + Raw: chm.raw, + Vers: chm.vers, + Random: chm.random, + SessionId: chm.sessionId, + CipherSuites: chm.cipherSuites, + CompressionMethods: chm.compressionMethods, + NextProtoNeg: chm.nextProtoNeg, + ServerName: chm.serverName, + OcspStapling: chm.ocspStapling, + Scts: chm.scts, + SupportedCurves: chm.supportedCurves, + SupportedPoints: chm.supportedPoints, + TicketSupported: chm.ticketSupported, + SessionTicket: chm.sessionTicket, + SignatureAndHashes: sigAndHashMakePublic(chm.signatureAndHashes), + SecureRenegotiation: chm.secureRenegotiation, + SecureRenegotiationSupported: chm.secureRenegotiationSupported, + AlpnProtocols: chm.alpnProtocols, + } + } +} + +// SignatureAndHash mirrors the TLS 1.2, SignatureAndHashAlgorithm struct. See +// RFC 5246, section A.4.1. +type SignatureAndHash struct { + Hash, Signature uint8 +} + +func sigAndHashGetMakePrivate(sahSlice []SignatureAndHash) []signatureAndHash { + res := []signatureAndHash{} + for _, sah := range sahSlice { + res = append(res, signatureAndHash{hash: sah.Hash, + signature: sah.Signature}) + } + return res +} + +func sigAndHashMakePublic(sahSlice []signatureAndHash) []SignatureAndHash { + res := []SignatureAndHash{} + for _, sah := range sahSlice { + res = append(res, SignatureAndHash{Hash: sah.hash, + Signature: sah.signature}) + } + return res +} + +// A CipherSuite is a specific combination of key agreement, cipher and MAC +// function. All cipher suites currently assume RSA key agreement. +type CipherSuite struct { + Id uint16 + // the lengths, in bytes, of the key material needed for each component. + KeyLen int + MacLen int + IvLen int + Ka func(version uint16) keyAgreement + // flags is a bitmask of the suite* values, above. + Flags int + Cipher func(key, iv []byte, isRead bool) interface{} + Mac func(version uint16, macKey []byte) macFunction + Aead func(key, fixedNonce []byte) cipher.AEAD +} + +func (cs *CipherSuite) getPrivatePtr() *cipherSuite { + if cs == nil { + return nil + } else { + return &cipherSuite{ + id: cs.Id, + keyLen: cs.KeyLen, + macLen: cs.MacLen, + ivLen: cs.IvLen, + ka: cs.Ka, + flags: cs.Flags, + cipher: cs.Cipher, + mac: cs.Mac, + aead: cs.Aead, + } + } +} + +func (cs *cipherSuite) getPublicPtr() *CipherSuite { + if cs == nil { + return nil + } else { + return &CipherSuite{ + Id: cs.id, + KeyLen: cs.keyLen, + MacLen: cs.macLen, + IvLen: cs.ivLen, + Ka: cs.ka, + Flags: cs.flags, + Cipher: cs.cipher, + Mac: cs.mac, + Aead: cs.aead, + } + } +} + +// A FinishedHash calculates the hash of a set of handshake messages suitable +// for including in a Finished message. +type FinishedHash struct { + Client hash.Hash + Server hash.Hash + + // Prior to TLS 1.2, an additional MD5 hash is required. + ClientMD5 hash.Hash + ServerMD5 hash.Hash + + // In TLS 1.2, a full buffer is sadly required. + Buffer []byte + + Version uint16 + Prf func(result, secret, label, seed []byte) +} + +func (fh *FinishedHash) getPrivatePtr() *finishedHash { + if fh == nil { + return nil + } else { + return &finishedHash{ + client: fh.Client, + server: fh.Server, + clientMD5: fh.ClientMD5, + serverMD5: fh.ServerMD5, + buffer: fh.Buffer, + version: fh.Version, + prf: fh.Prf, + } + } +} + +func (fh *finishedHash) getPublicPtr() *FinishedHash { + if fh == nil { + return nil + } else { + return &FinishedHash{ + Client: fh.client, + Server: fh.server, + ClientMD5: fh.clientMD5, + ServerMD5: fh.serverMD5, + Buffer: fh.buffer, + Version: fh.version, + Prf: fh.prf} + } +} + +// ClientSessionState is public, but all its fields are private. Let's add setters, getters and constructor + +// ClientSessionState contains the state needed by clients to resume TLS sessions. +func MakeClientSessionState( + SessionTicket []uint8, + Vers uint16, + CipherSuite uint16, + MasterSecret []byte, + ServerCertificates []*x509.Certificate, + VerifiedChains [][]*x509.Certificate) *ClientSessionState { + css := ClientSessionState{sessionTicket: SessionTicket, + vers: Vers, + cipherSuite: CipherSuite, + masterSecret: MasterSecret, + serverCertificates: ServerCertificates, + verifiedChains: VerifiedChains} + return &css +} + +// Encrypted ticket used for session resumption with server +func (css *ClientSessionState) SessionTicket() []uint8 { + return css.sessionTicket +} + +// SSL/TLS version negotiated for the session +func (css *ClientSessionState) Vers() uint16 { + return css.vers +} + +// Ciphersuite negotiated for the session +func (css *ClientSessionState) CipherSuite() uint16 { + return css.cipherSuite +} + +// MasterSecret generated by client on a full handshake +func (css *ClientSessionState) MasterSecret() []byte { + return css.masterSecret +} + +// Certificate chain presented by the server +func (css *ClientSessionState) ServerCertificates() []*x509.Certificate { + return css.serverCertificates +} + +// Certificate chains we built for verification +func (css *ClientSessionState) VerifiedChains() [][]*x509.Certificate { + return css.verifiedChains +} + +func (css *ClientSessionState) SetSessionTicket(SessionTicket []uint8) { + css.sessionTicket = SessionTicket +} +func (css *ClientSessionState) SetVers(Vers uint16) { + css.vers = Vers +} +func (css *ClientSessionState) SetCipherSuite(CipherSuite uint16) { + css.cipherSuite = CipherSuite +} +func (css *ClientSessionState) SetMasterSecret(MasterSecret []byte) { + css.masterSecret = MasterSecret +} +func (css *ClientSessionState) SetServerCertificates(ServerCertificates []*x509.Certificate) { + css.serverCertificates = ServerCertificates +} +func (css *ClientSessionState) SetVerifiedChains(VerifiedChains [][]*x509.Certificate) { + css.verifiedChains = VerifiedChains +} diff --git a/u_tls_extensions.go b/u_tls_extensions.go new file mode 100644 index 0000000..16263cd --- /dev/null +++ b/u_tls_extensions.go @@ -0,0 +1,506 @@ +package tls + +import ( + "io" +) + +type TLSExtension interface { + writeToUConn(*UConn) error + + Len() int // includes header + + // Read reads up to len(p) bytes into p. + // It returns the number of bytes read (0 <= n <= len(p)) and any error encountered. + Read(p []byte) (n int, err error) // implements io.Reader +} + +type NPNExtension struct { + NextProtos []string +} + +func (e *NPNExtension) writeToUConn(uc *UConn) error { + uc.config.NextProtos = e.NextProtos + uc.HandshakeState.Hello.NextProtoNeg = true + return nil +} + +func (e *NPNExtension) Len() int { + return 4 +} + +func (e *NPNExtension) Read(b []byte) (int, error) { + if len(b) < e.Len() { + return 0, io.ErrShortBuffer + } + b[0] = byte(extensionNextProtoNeg >> 8) + b[1] = byte(extensionNextProtoNeg & 0xff) + // The length is always 0 + return e.Len(), io.EOF +} + +type SNIExtension struct { + ServerName string // not an array because go crypto/tls doesn't support multiple SNIs +} + +func (e *SNIExtension) writeToUConn(uc *UConn) error { + uc.config.ServerName = e.ServerName + uc.HandshakeState.Hello.ServerName = e.ServerName + return nil +} + +func (e *SNIExtension) Len() int { + return 4 + 2 + 1 + 2 + len(e.ServerName) +} + +func (e *SNIExtension) Read(b []byte) (int, error) { + if len(b) < e.Len() { + return 0, io.ErrShortBuffer + } + // RFC 3546, section 3.1 + b[0] = byte(extensionServerName >> 8) + b[1] = byte(extensionServerName) + b[2] = byte((len(e.ServerName) + 5) >> 8) + b[3] = byte((len(e.ServerName) + 5)) + b[4] = byte((len(e.ServerName) + 3) >> 8) + b[5] = byte(len(e.ServerName) + 3) + // b[6] Server Name Type: host_name (0) + b[7] = byte(len(e.ServerName) >> 8) + b[8] = byte(len(e.ServerName)) + copy(b[9:], []byte(e.ServerName)) + return e.Len(), io.EOF +} + +type StatusRequestExtension struct { +} + +func (e *StatusRequestExtension) writeToUConn(uc *UConn) error { + uc.HandshakeState.Hello.OcspStapling = true + return nil +} + +func (e *StatusRequestExtension) Len() int { + return 9 +} + +func (e *StatusRequestExtension) Read(b []byte) (int, error) { + if len(b) < e.Len() { + return 0, io.ErrShortBuffer + } + // RFC 4366, section 3.6 + b[0] = byte(extensionStatusRequest >> 8) + b[1] = byte(extensionStatusRequest) + b[2] = 0 + b[3] = 5 + b[4] = 1 // OCSP type + // Two zero valued uint16s for the two lengths. + return e.Len(), io.EOF +} + +type SupportedCurvesExtension struct { + Curves []CurveID +} + +func (e *SupportedCurvesExtension) writeToUConn(uc *UConn) error { + uc.config.CurvePreferences = e.Curves + uc.HandshakeState.Hello.SupportedCurves = e.Curves + return nil +} + +func (e *SupportedCurvesExtension) Len() int { + return 6 + 2*len(e.Curves) +} + +func (e *SupportedCurvesExtension) Read(b []byte) (int, error) { + if len(b) < e.Len() { + return 0, io.ErrShortBuffer + } + // http://tools.ietf.org/html/rfc4492#section-5.5.1 + b[0] = byte(extensionSupportedCurves >> 8) + b[1] = byte(extensionSupportedCurves) + b[2] = byte((2 + 2*len(e.Curves)) >> 8) + b[3] = byte((2 + 2*len(e.Curves))) + b[4] = byte((2 * len(e.Curves)) >> 8) + b[5] = byte((2 * len(e.Curves))) + for i, curve := range e.Curves { + b[6+2*i] = byte(curve >> 8) + b[7+2*i] = byte(curve) + } + return e.Len(), io.EOF +} + +type SupportedPointsExtension struct { + SupportedPoints []uint8 +} + +func (e *SupportedPointsExtension) writeToUConn(uc *UConn) error { + uc.HandshakeState.Hello.SupportedPoints = e.SupportedPoints + return nil +} + +func (e *SupportedPointsExtension) Len() int { + return 5 + len(e.SupportedPoints) +} + +func (e *SupportedPointsExtension) Read(b []byte) (int, error) { + if len(b) < e.Len() { + return 0, io.ErrShortBuffer + } + // http://tools.ietf.org/html/rfc4492#section-5.5.2 + b[0] = byte(extensionSupportedPoints >> 8) + b[1] = byte(extensionSupportedPoints) + b[2] = byte((1 + len(e.SupportedPoints)) >> 8) + b[3] = byte((1 + len(e.SupportedPoints))) + b[4] = byte((len(e.SupportedPoints))) + for i, pointFormat := range e.SupportedPoints { + b[5+i] = pointFormat + } + return e.Len(), io.EOF +} + +type SignatureAlgorithmsExtension struct { + SignatureAndHashes []SignatureAndHash +} + +func (e *SignatureAlgorithmsExtension) writeToUConn(uc *UConn) error { + uc.HandshakeState.Hello.SignatureAndHashes = e.SignatureAndHashes + return nil +} + +func (e *SignatureAlgorithmsExtension) Len() int { + return 6 + 2*len(e.SignatureAndHashes) +} + +func (e *SignatureAlgorithmsExtension) Read(b []byte) (int, error) { + if len(b) < e.Len() { + return 0, io.ErrShortBuffer + } + // https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 + b[0] = byte(extensionSignatureAlgorithms >> 8) + b[1] = byte(extensionSignatureAlgorithms) + b[2] = byte((2 + 2*len(e.SignatureAndHashes)) >> 8) + b[3] = byte((2 + 2*len(e.SignatureAndHashes))) + b[4] = byte((2 * len(e.SignatureAndHashes)) >> 8) + b[5] = byte((2 * len(e.SignatureAndHashes))) + for i, sigAndHash := range e.SignatureAndHashes { + b[6+2*i] = byte(sigAndHash.Hash) + b[7+2*i] = byte(sigAndHash.Signature) + } + return e.Len(), io.EOF +} + +type RenegotiationInfoExtension struct { + renegotiation RenegotiationSupport + SecureRenegotiation []byte // you probably want to leave it empty +} + +func (e *RenegotiationInfoExtension) writeToUConn(uc *UConn) error { + uc.config.Renegotiation = e.renegotiation + switch e.renegotiation { + case RenegotiateOnceAsClient: + fallthrough + case RenegotiateFreelyAsClient: + uc.HandshakeState.Hello.SecureRenegotiationSupported = true + // Note that if we manage to use this in renegotiation(currently only in initial handshake), we'd have to point + // uc.HandshakeState.Hello.SecureRenegotiation = chs.C.clientFinished + // and probably do something else. It's a mess. + case RenegotiateNever: + default: + } + return nil +} + +func (e *RenegotiationInfoExtension) Len() int { + switch e.renegotiation { + case RenegotiateOnceAsClient: + fallthrough + case RenegotiateFreelyAsClient: + return 5 + len(e.SecureRenegotiation) + case RenegotiateNever: + default: + } + return 0 +} + +func (e *RenegotiationInfoExtension) Read(b []byte) (int, error) { + if len(b) < e.Len() { + return 0, io.ErrShortBuffer + } + switch e.renegotiation { + case RenegotiateOnceAsClient: + fallthrough + case RenegotiateFreelyAsClient: + b[0] = byte(extensionRenegotiationInfo >> 8) + b[1] = byte(extensionRenegotiationInfo & 0xff) + b[2] = 0 // TODO: this is not what Chrome does :( + b[3] = byte(len(e.SecureRenegotiation) + 1) + b[4] = byte(len(e.SecureRenegotiation)) + if len(e.SecureRenegotiation) != 0 { + copy(b[5:], e.SecureRenegotiation) + } + case RenegotiateNever: + default: + } + return e.Len(), io.EOF +} + +type ALPNExtension struct { + AlpnProtocols []string +} + +func (e *ALPNExtension) writeToUConn(uc *UConn) error { + uc.config.NextProtos = e.AlpnProtocols + uc.HandshakeState.Hello.AlpnProtocols = e.AlpnProtocols + return nil +} + +func (e *ALPNExtension) Len() int { + bLen := 2 + 2 + 2 + for _, s := range e.AlpnProtocols { + bLen += 1 + len(s) + } + return bLen +} + +func (e *ALPNExtension) Read(b []byte) (int, error) { + if len(b) < e.Len() { + return 0, io.ErrShortBuffer + } + + b[0] = byte(extensionALPN >> 8) + b[1] = byte(extensionALPN & 0xff) + lengths := b[2:] + b = b[6:] + + stringsLength := 0 + for _, s := range e.AlpnProtocols { + l := len(s) + b[0] = byte(l) + copy(b[1:], s) + b = b[1+l:] + stringsLength += 1 + l + } + + lengths[2] = byte(stringsLength >> 8) + lengths[3] = byte(stringsLength) + stringsLength += 2 + lengths[0] = byte(stringsLength >> 8) + lengths[1] = byte(stringsLength) + + return e.Len(), io.EOF +} + +type SCTExtension struct { +} + +func (e *SCTExtension) writeToUConn(uc *UConn) error { + uc.HandshakeState.Hello.Scts = true + return nil +} + +func (e *SCTExtension) Len() int { + return 4 +} + +func (e *SCTExtension) Read(b []byte) (int, error) { + if len(b) < e.Len() { + return 0, io.ErrShortBuffer + } + // https://tools.ietf.org/html/rfc6962#section-3.3.1 + b[0] = byte(extensionSCT >> 8) + b[1] = byte(extensionSCT) + // zero uint16 for the zero-length extension_data + return e.Len(), io.EOF +} + +type SessionTicketExtension struct { + Session *ClientSessionState +} + +func (e *SessionTicketExtension) writeToUConn(uc *UConn) error { + if e.Session != nil { + uc.HandshakeState.Session = e.Session + uc.HandshakeState.Hello.SessionTicket = e.Session.sessionTicket + } + return nil +} + +func (e *SessionTicketExtension) Len() int { + if e.Session != nil { + return 4 + len(e.Session.sessionTicket) + } + return 4 +} + +func (e *SessionTicketExtension) Read(b []byte) (int, error) { + if len(b) < e.Len() { + return 0, io.ErrShortBuffer + } + + extBodyLen := e.Len() - 4 + + b[0] = byte(extensionSessionTicket >> 8) + b[1] = byte(extensionSessionTicket) + b[2] = byte(extBodyLen >> 8) + b[3] = byte(extBodyLen) + if extBodyLen > 0 { + copy(b[4:], e.Session.sessionTicket) + } + return e.Len(), io.EOF +} + +/* +FAKE EXTENSIONS +*/ + +type FakeChannelIDExtension struct { +} + +func (e *FakeChannelIDExtension) writeToUConn(uc *UConn) error { + return nil +} + +func (e *FakeChannelIDExtension) Len() int { + return 4 +} + +func (e *FakeChannelIDExtension) Read(b []byte) (int, error) { + if len(b) < e.Len() { + return 0, io.ErrShortBuffer + } + // https://tools.ietf.org/html/draft-balfanz-tls-channelid-00 + b[0] = byte(fakeExtensionChannelID >> 8) + b[1] = byte(fakeExtensionChannelID & 0xff) + // The length is 0 + return e.Len(), io.EOF +} + +type FakeExtendedMasterSecretExtension struct { +} + +// TODO: update when Cloudflare upstreams this extension to crypto/tls +// but we probably won't have to enable it in Config +func (e *FakeExtendedMasterSecretExtension) writeToUConn(uc *UConn) error { + return nil +} + +func (e *FakeExtendedMasterSecretExtension) Len() int { + return 4 +} + +func (e *FakeExtendedMasterSecretExtension) Read(b []byte) (int, error) { + if len(b) < e.Len() { + return 0, io.ErrShortBuffer + } + // https://tools.ietf.org/html/rfc7627 + b[0] = byte(fakeExtensionExtendedMasterSecret >> 8) + b[1] = byte(fakeExtensionExtendedMasterSecret) + // The length is 0 + return e.Len(), io.EOF +} + +// GREASE stinks with dead parrots, have to be super careful, and, if possible, not include GREASE +const ( + ssl_grease_cipher = iota + ssl_grease_group + ssl_grease_extension1 + ssl_grease_extension2 + ssl_grease_version + ssl_grease_ticket_extension +) + +// it is responsibility of user not to generate multiple grease extensions with same value +type FakeGREASEExtension struct { + Value uint16 + Body []byte // in Chrome first grease has empty body, second grease has a single zero byte +} + +func (e *FakeGREASEExtension) writeToUConn(uc *UConn) error { + return nil +} + +// will panic if clientRandom[index] is out of bounds. +func GetBoringGREASEValue(clientRandom []byte, index int) uint16 { + // Get GREASE value BoringSSL-style. Unfortunately, this value isn't really boring and is quite interesting :( + // https://github.com/google/boringssl/blob/a365138ac60f38b64bfc608b493e0f879845cb88/ssl/handshake_client.c#L530 + ret := uint16(clientRandom[index]) + /* This generates a random value of the form 0xωaωa, for all 0 ≤ ω < 16. */ + ret = (ret & 0xf0) | 0x0a + ret |= ret << 8 + return ret +} + +func (e *FakeGREASEExtension) Len() int { + return 4 + len(e.Body) +} + +func (e *FakeGREASEExtension) Read(b []byte) (int, error) { + if len(b) < e.Len() { + return 0, io.ErrShortBuffer + } + + b[0] = byte(e.Value >> 8) + b[1] = byte(e.Value) + b[2] = byte(len(e.Body) >> 8) + b[3] = byte(len(e.Body)) + if len(e.Body) > 0 { + copy(b[4:], e.Body) + } + return e.Len(), io.EOF +} + +// +type FakePaddingExtension struct { + PaddingLen int + WillPad bool // set to false to disable extension + + // Functor for deciding on padding length based on unpadded ClientHello length. + // If willPad is false, then this extension should not be included. + GetPaddingLen func(clientHelloUnpaddedLen int) (paddingLen int, willPad bool) +} + +func (e *FakePaddingExtension) writeToUConn(uc *UConn) error { + return nil +} + +func (e *FakePaddingExtension) Len() int { + if e.WillPad { + return 4 + e.PaddingLen + } else { + return 0 + } +} + +func (e *FakePaddingExtension) Update(clientHelloUnpaddedLen int) { + if e.GetPaddingLen != nil { + e.PaddingLen, e.WillPad = e.GetPaddingLen(clientHelloUnpaddedLen) + } +} + +func (e *FakePaddingExtension) Read(b []byte) (int, error) { + if !e.WillPad { + return 0, io.EOF + } + if len(b) < e.Len() { + return 0, io.ErrShortBuffer + } + // https://tools.ietf.org/html/rfc7627 + b[0] = byte(fakeExtensionPadding >> 8) + b[1] = byte(fakeExtensionPadding) + b[2] = byte(e.PaddingLen >> 8) + b[3] = byte(e.PaddingLen) + return e.Len(), io.EOF +} + +// https://github.com/google/boringssl/blob/7d7554b6b3c79e707e25521e61e066ce2b996e4c/ssl/t1_lib.c#L2803 +func boringPaddingStyle(unpaddedLen int) (int, bool) { + if unpaddedLen > 0xff && unpaddedLen < 0x200 { + paddingLen := 0x200 - unpaddedLen + if paddingLen >= 4+1 { + paddingLen -= 4 + } else { + paddingLen = 1 + } + return paddingLen, true + } + return 0, false +}