Merge pull request #2 from Jigsaw-Code/utls

uTLS: initial commit
This commit is contained in:
sergeyfrolov 2017-07-07 11:05:18 -04:00 committed by GitHub
commit 736a2caf18
31 changed files with 4004 additions and 4 deletions

60
CONTRIBUTORS_GUIDE.md Normal file
View file

@ -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.

141
README.md Normal file
View file

@ -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)
```

View file

@ -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

17
cipherhw/asm_amd64.s Normal file
View file

@ -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

44
cipherhw/asm_s390x.s Normal file
View file

@ -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

View file

@ -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()
}

View file

@ -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
}

7
cipherhw/doc.go Normal file
View file

@ -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

11
cipherhw/generic.go Normal file
View file

@ -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
}

View file

@ -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"

224
examples/examples.go Normal file
View file

@ -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]
}
}

View file

@ -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")

2
prf.go
View file

@ -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")
}

View file

@ -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 |..|

View file

@ -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 |..|

View file

@ -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.<b;"...X..T|
00000020 d2 2a 8e 2e 73 27 92 f0 04 8e 57 fa b3 e1 1a 1b |.*..s'....W.....|
00000030 c8 db 92 59 90 ef fa b7 42 a3 72 f2 2a c6 da 85 |...Y....B.r.*...|
00000040 00 78 c2 18 11 4b 14 50 d9 16 ed 02 86 83 16 ec |.x...K.P........|
00000050 79 86 39 5e 9b db 2d c2 7a 67 5a 3c 33 d0 b2 1d |y.9^..-.zgZ<3...|
00000060 67 c9 be 29 af 6d e3 4e 33 eb b5 31 51 2a 17 42 |g..).m.N3..1Q*.B|
00000070 9b a5 0d 7d 1e 72 e6 91 fc 66 78 d9 a2 46 99 65 |...}.r...fx..F.e|
00000080 61 fd d6 bc 96 20 fa 1a c8 f1 01 3d 92 0e c7 df |a.... .....=....|
00000090 a3 8a 39 ba 2b 67 b7 f2 71 e1 aa 61 2b 67 42 1e |..9.+g..q..a+gB.|
000000a0 e6 ef b9 08 a1 c6 d8 38 78 60 0f 64 30 cf 13 14 |.......8x`.d0...|
000000b0 03 03 00 01 01 16 03 03 00 40 d4 08 81 64 cd d2 |.........@...d..|
000000c0 e4 c7 fe f0 61 fa 96 25 94 44 9e 25 41 72 db 90 |....a..%.D.%Ar..|
000000d0 63 24 52 34 5e e9 43 0b 41 c1 96 b3 79 b0 81 b0 |c$R4^.C.A...y...|
000000e0 cc f6 78 d9 97 68 c6 2b e2 34 9d 7f f3 d8 e6 1d |..x..h.+.4......|
000000f0 29 ab 50 97 e1 a0 29 a1 3d 45 |).P...).=E|
>>> 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...|

View file

@ -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....<V..|
000000f0 1e 2c a0 f9 f7 e5 df a1 fc f8 |.,........|
>>> 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.&...(/.|

View file

@ -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.|

View file

@ -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.|

View file

@ -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..}.|

193
testenv/testenv.go Normal file
View file

@ -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")
}
}

11
testenv/testenv_cgo.go Normal file
View file

@ -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
}

20
testenv/testenv_notwin.go Normal file
View file

@ -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, ""
}

View file

@ -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, ""
}

View file

@ -9,7 +9,7 @@ import (
"crypto/x509"
"errors"
"fmt"
"internal/testenv"
"github.com/Jigsaw-Code/utls/testenv"
"io"
"io/ioutil"
"math"

121
u_common.go Normal file
View file

@ -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)
}

373
u_conn.go Normal file
View file

@ -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
}

447
u_conn_test.go Normal file
View file

@ -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)
}

644
u_parrots.go Normal file
View file

@ -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
}

383
u_public.go Normal file
View file

@ -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
}

506
u_tls_extensions.go Normal file
View file

@ -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
}