From 1a628cb524ffd7d9d26679c718957df107a33e3e Mon Sep 17 00:00:00 2001 From: Qian Wang Date: Fri, 2 Aug 2019 16:02:25 +0100 Subject: [PATCH] Refactor client TLS --- cmd/ck-client/ck-client.go | 3 +-- internal/client/{TLS => }/TLS.go | 21 ++++++------------ internal/client/{TLS => }/chrome.go | 32 ++++++++++++---------------- internal/client/{TLS => }/firefox.go | 14 +++++------- internal/client/state.go | 15 ++++++++++--- 5 files changed, 38 insertions(+), 47 deletions(-) rename internal/client/{TLS => }/TLS.go (69%) rename internal/client/{TLS => }/chrome.go (85%) rename internal/client/{TLS => }/firefox.go (91%) diff --git a/cmd/ck-client/ck-client.go b/cmd/ck-client/ck-client.go index 6ce0157..32cec10 100644 --- a/cmd/ck-client/ck-client.go +++ b/cmd/ck-client/ck-client.go @@ -16,7 +16,6 @@ import ( "time" "github.com/cbeuw/Cloak/internal/client" - "github.com/cbeuw/Cloak/internal/client/TLS" mux "github.com/cbeuw/Cloak/internal/multiplex" "github.com/cbeuw/Cloak/internal/util" log "github.com/sirupsen/logrus" @@ -51,7 +50,7 @@ func makeRemoteConn(sta *client.State) (net.Conn, []byte, error) { // For android d := net.Dialer{Control: protector} - clientHello, sharedSecret := TLS.ComposeClientHello(sta) + clientHello, sharedSecret := client.ComposeClientHello(sta) connectingIP := sta.RemoteHost if net.ParseIP(connectingIP).To4() == nil { // IPv6 needs square brackets diff --git a/internal/client/TLS/TLS.go b/internal/client/TLS.go similarity index 69% rename from internal/client/TLS/TLS.go rename to internal/client/TLS.go index b7f5f1b..a1b6cbb 100644 --- a/internal/client/TLS/TLS.go +++ b/internal/client/TLS.go @@ -1,13 +1,12 @@ -package TLS +package client import ( "encoding/binary" - "github.com/cbeuw/Cloak/internal/client" "github.com/cbeuw/Cloak/internal/util" ) -type browser interface { - composeClientHello() +type Browser interface { + composeClientHello(*State) ([]byte, []byte) } func makeServerName(serverName string) []byte { @@ -43,16 +42,8 @@ func addExtRec(typ []byte, data []byte) []byte { return ret } -// ComposeInitHandshake composes ClientHello with record layer -func ComposeInitHandshake(sta *client.State) ([]byte, []byte) { - var ch, sharedSecret []byte - switch sta.BrowserSig { - case "chrome": - ch, sharedSecret = (&chrome{}).composeClientHello(sta) - case "firefox": - ch, sharedSecret = (&firefox{}).composeClientHello(sta) - default: - panic("Unsupported browser:" + sta.BrowserSig) - } +// ComposeClientHello composes ClientHello with record layer +func ComposeClientHello(sta *State) ([]byte, []byte) { + ch, sharedSecret := sta.Browser.composeClientHello(sta) return util.AddRecordLayer(ch, []byte{0x16}, []byte{0x03, 0x01}), sharedSecret } diff --git a/internal/client/TLS/chrome.go b/internal/client/chrome.go similarity index 85% rename from internal/client/TLS/chrome.go rename to internal/client/chrome.go index 1993800..eb266be 100644 --- a/internal/client/TLS/chrome.go +++ b/internal/client/chrome.go @@ -1,23 +1,19 @@ // Chrome 76 -package TLS +package client import ( "encoding/binary" "encoding/hex" "math/rand" "time" - - "github.com/cbeuw/Cloak/internal/client" ) -type chrome struct { - browser -} +type Chrome struct{} func makeGREASE() []byte { // see https://tools.ietf.org/html/draft-davidben-tls-grease-01 - // This is exclusive to chrome. + // This is exclusive to Chrome. rand.Seed(time.Now().UnixNano()) sixteenth := rand.Intn(16) monoGREASE := byte(sixteenth*16 + 0xA) @@ -25,7 +21,7 @@ func makeGREASE() []byte { return doubleGREASE } -func (c *chrome) composeExtensions(sta *client.State, keyShare []byte) []byte { +func (c *Chrome) composeExtensions(serverName string, keyShare []byte) []byte { makeSupportedGroups := func() []byte { suppGroupListLen := []byte{0x00, 0x08} @@ -51,13 +47,13 @@ func (c *chrome) composeExtensions(sta *client.State, keyShare []byte) []byte { // extension length is always 401, and server name length is variable var ext [17][]byte - ext[0] = addExtRec(makeGREASE(), nil) // First GREASE - ext[1] = addExtRec([]byte{0x00, 0x00}, makeServerName(sta.ServerName)) // server name indication - ext[2] = addExtRec([]byte{0x00, 0x17}, nil) // extended_master_secret - ext[3] = addExtRec([]byte{0xff, 0x01}, []byte{0x00}) // renegotiation_info - ext[4] = addExtRec([]byte{0x00, 0x0a}, makeSupportedGroups()) // supported groups - ext[5] = addExtRec([]byte{0x00, 0x0b}, []byte{0x01, 0x00}) // ec point formats - ext[6] = addExtRec([]byte{0x00, 0x23}, nil) // Session tickets + ext[0] = addExtRec(makeGREASE(), nil) // First GREASE + ext[1] = addExtRec([]byte{0x00, 0x00}, makeServerName(serverName)) // server name indication + ext[2] = addExtRec([]byte{0x00, 0x17}, nil) // extended_master_secret + ext[3] = addExtRec([]byte{0xff, 0x01}, []byte{0x00}) // renegotiation_info + ext[4] = addExtRec([]byte{0x00, 0x0a}, makeSupportedGroups()) // supported groups + ext[5] = addExtRec([]byte{0x00, 0x0b}, []byte{0x01, 0x00}) // ec point formats + ext[6] = addExtRec([]byte{0x00, 0x23}, nil) // Session tickets APLN, _ := hex.DecodeString("000c02683208687474702f312e31") ext[7] = addExtRec([]byte{0x00, 0x10}, APLN) // app layer proto negotiation ext[8] = addExtRec([]byte{0x00, 0x05}, []byte{0x01, 0x00, 0x00, 0x00, 0x00}) // status request @@ -83,8 +79,8 @@ func (c *chrome) composeExtensions(sta *client.State, keyShare []byte) []byte { return ret } -func (c *chrome) composeClientHello(sta *client.State) ([]byte, []byte) { - random, sessionID, keyShare, sharedSecret := client.MakeHiddenData(sta) +func (c *Chrome) composeClientHello(sta *State) (ch []byte, sharedSecret []byte) { + random, sessionID, keyShare, sharedSecret := MakeHiddenData(sta) var clientHello [12][]byte clientHello[0] = []byte{0x01} // handshake type clientHello[1] = []byte{0x00, 0x01, 0xfc} // length 508 @@ -97,7 +93,7 @@ func (c *chrome) composeClientHello(sta *client.State) ([]byte, []byte) { clientHello[7] = append(makeGREASE(), cipherSuites...) // cipher suites clientHello[8] = []byte{0x01} // compression methods length 1 clientHello[9] = []byte{0x00} // compression methods - clientHello[11] = c.composeExtensions(sta, keyShare) + clientHello[11] = c.composeExtensions(sta.ServerName, keyShare) clientHello[10] = []byte{0x00, 0x00} // extensions length 401 binary.BigEndian.PutUint16(clientHello[10], uint16(len(clientHello[11]))) var ret []byte diff --git a/internal/client/TLS/firefox.go b/internal/client/firefox.go similarity index 91% rename from internal/client/TLS/firefox.go rename to internal/client/firefox.go index 2b3c015..380d3a1 100644 --- a/internal/client/TLS/firefox.go +++ b/internal/client/firefox.go @@ -1,19 +1,15 @@ // Firefox 68 -package TLS +package client import ( "crypto/rand" "encoding/binary" "encoding/hex" - - "github.com/cbeuw/Cloak/internal/client" ) -type firefox struct { - browser -} +type Firefox struct{} -func (f *firefox) composeExtensions(serverName string, keyShare []byte) []byte { +func (f *Firefox) composeExtensions(serverName string, keyShare []byte) []byte { composeKeyShare := func(hidden []byte) []byte { ret := make([]byte, 107) ret[0], ret[1] = 0x00, 0x69 // length 105 @@ -54,8 +50,8 @@ func (f *firefox) composeExtensions(serverName string, keyShare []byte) []byte { return ret } -func (f *firefox) composeClientHello(sta *client.State) ([]byte, []byte) { - random, sessionID, keyShare, sharedSecret := client.MakeHiddenData(sta) +func (f *Firefox) composeClientHello(sta *State) (ch []byte, sharedSecret []byte) { + random, sessionID, keyShare, sharedSecret := MakeHiddenData(sta) var clientHello [12][]byte clientHello[0] = []byte{0x01} // handshake type diff --git a/internal/client/state.go b/internal/client/state.go index c676067..d20f44a 100644 --- a/internal/client/state.go +++ b/internal/client/state.go @@ -35,10 +35,11 @@ type State struct { staticPub crypto.PublicKey IsAdmin bool + Browser Browser + ProxyMethod string EncryptionMethod byte ServerName string - BrowserSig string NumConn int } @@ -100,7 +101,7 @@ func (sta *State) ParseConfig(conf string) (err error) { return err } - switch preParse.EncryptionMethod { + switch strings.ToLower(preParse.EncryptionMethod) { case "plain": sta.EncryptionMethod = 0x00 case "aes-gcm": @@ -111,9 +112,17 @@ func (sta *State) ParseConfig(conf string) (err error) { return errors.New("Unknown encryption method") } + switch strings.ToLower(preParse.BrowserSig) { + case "chrome": + sta.Browser = &Chrome{} + case "firefox": + sta.Browser = &Firefox{} + default: + return errors.New("unsupported browser signature") + } + sta.ProxyMethod = preParse.ProxyMethod sta.ServerName = preParse.ServerName - sta.BrowserSig = preParse.BrowserSig sta.NumConn = preParse.NumConn uid, err := base64.StdEncoding.DecodeString(preParse.UID)