Skip to content

Commit

Permalink
Use utls for ClientHello fingerprint
Browse files Browse the repository at this point in the history
Close #223
  • Loading branch information
cbeuw committed Feb 9, 2024
1 parent b3ec1ab commit 6417e33
Show file tree
Hide file tree
Showing 12 changed files with 441 additions and 530 deletions.
18 changes: 9 additions & 9 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ go 1.14

require (
github.com/cbeuw/connutil v0.0.0-20200411215123-966bfaa51ee3
github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.4.2
github.com/juju/ratelimit v1.0.1
github.com/kr/pretty v0.1.0 // indirect
github.com/sirupsen/logrus v1.8.1
github.com/stretchr/testify v1.6.1
github.com/gorilla/mux v1.8.1
github.com/gorilla/websocket v1.5.1
github.com/juju/ratelimit v1.0.2
github.com/refraction-networking/utls v1.6.2
github.com/sirupsen/logrus v1.9.3
github.com/stretchr/testify v1.8.1
gitlab.com/yawning/utls.git v0.0.12-1
go.etcd.io/bbolt v1.3.6
golang.org/x/crypto v0.1.0
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
go.etcd.io/bbolt v1.3.8
golang.org/x/crypto v0.19.0
golang.org/x/net v0.21.0 // indirect
)
381 changes: 365 additions & 16 deletions go.sum

Large diffs are not rendered by default.

106 changes: 63 additions & 43 deletions internal/client/TLS.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package client

import (
"encoding/binary"
"encoding/hex"
utls "github.com/refraction-networking/utls"
log "github.com/sirupsen/logrus"
"net"

"github.com/cbeuw/Cloak/internal/common"
log "github.com/sirupsen/logrus"
)

const appDataMaxLength = 16401
Expand All @@ -18,63 +17,84 @@ type clientHelloFields struct {
serverName string
}

func decodeHex(s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
panic(err)
}
return b
}

type browser interface {
composeClientHello(clientHelloFields) []byte
}

func generateSNI(serverName string) []byte {
serverNameListLength := make([]byte, 2)
binary.BigEndian.PutUint16(serverNameListLength, uint16(len(serverName)+3))
serverNameType := []byte{0x00} // host_name
serverNameLength := make([]byte, 2)
binary.BigEndian.PutUint16(serverNameLength, uint16(len(serverName)))
ret := make([]byte, 2+1+2+len(serverName))
copy(ret[0:2], serverNameListLength)
copy(ret[2:3], serverNameType)
copy(ret[3:5], serverNameLength)
copy(ret[5:], serverName)
return ret
}
type browser int

// addExtensionRecord, add type, length to extension data
func addExtRec(typ []byte, data []byte) []byte {
length := make([]byte, 2)
binary.BigEndian.PutUint16(length, uint16(len(data)))
ret := make([]byte, 2+2+len(data))
copy(ret[0:2], typ)
copy(ret[2:4], length)
copy(ret[4:], data)
return ret
}
const (
chrome = iota
firefox
safari
)

type DirectTLS struct {
*common.TLSConn
browser browser
}

// NewClientTransport handles the TLS handshake for a given conn and returns the sessionKey
func buildClientHello(browser browser, fields clientHelloFields) ([]byte, error) {
// We don't use utls to handle connections (as it'll attempt a real TLS negotiation)
// We only want it to build the ClientHello locally
fakeConn := net.TCPConn{}
var helloID utls.ClientHelloID
switch browser {
case chrome:
helloID = utls.HelloChrome_Auto
case firefox:
helloID = utls.HelloFirefox_Auto
case safari:
helloID = utls.HelloSafari_Auto
}

uclient := utls.UClient(&fakeConn, &utls.Config{ServerName: fields.serverName}, helloID)
if err := uclient.BuildHandshakeState(); err != nil {
return []byte{}, err
}
if err := uclient.SetClientRandom(fields.random); err != nil {
return []byte{}, err
}

uclient.HandshakeState.Hello.SessionId = make([]byte, 32)
copy(uclient.HandshakeState.Hello.SessionId, fields.sessionId)

// Find the X25519 key share and overwrite it
var extIndex int
var keyShareIndex int
for i, ext := range uclient.Extensions {
ext, ok := ext.(*utls.KeyShareExtension)
if ok {
extIndex = i
for j, keyShare := range ext.KeyShares {
if keyShare.Group == utls.X25519 {
keyShareIndex = j
}
}
}
}
copy(uclient.Extensions[extIndex].(*utls.KeyShareExtension).KeyShares[keyShareIndex].Data, fields.x25519KeyShare)

if err := uclient.BuildHandshakeState(); err != nil {
return []byte{}, err
}
return uclient.HandshakeState.Hello.Raw, nil
}

// Handshake handles the TLS handshake for a given conn and returns the sessionKey
// if the server proceed with Cloak authentication
func (tls *DirectTLS) Handshake(rawConn net.Conn, authInfo AuthInfo) (sessionKey [32]byte, err error) {
payload, sharedSecret := makeAuthenticationPayload(authInfo)

// random is marshalled ephemeral pub key 32 bytes
// The authentication ciphertext and its tag are then distributed among SessionId and X25519KeyShare
fields := clientHelloFields{
random: payload.randPubKey[:],
sessionId: payload.ciphertextWithTag[0:32],
x25519KeyShare: payload.ciphertextWithTag[32:64],
serverName: authInfo.MockDomain,
}
chOnly := tls.browser.composeClientHello(fields)
chWithRecordLayer := common.AddRecordLayer(chOnly, common.Handshake, common.VersionTLS11)

var ch []byte
ch, err = buildClientHello(tls.browser, fields)
if err != nil {
return
}
chWithRecordLayer := common.AddRecordLayer(ch, common.Handshake, common.VersionTLS11)
_, err = rawConn.Write(chWithRecordLayer)
if err != nil {
return
Expand Down
39 changes: 0 additions & 39 deletions internal/client/TLS_test.go

This file was deleted.

120 changes: 0 additions & 120 deletions internal/client/chrome.go

This file was deleted.

60 changes: 0 additions & 60 deletions internal/client/chrome_test.go

This file was deleted.

2 changes: 1 addition & 1 deletion internal/client/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ func MakeSession(connConfig RemoteConnConfig, authInfo AuthInfo, dialer common.D
transportConn := connConfig.TransportMaker()
sk, err := transportConn.Handshake(remoteConn, authInfo)
if err != nil {
transportConn.Close()
log.Errorf("Failed to prepare connection to remote: %v", err)
transportConn.Close()
time.Sleep(time.Second * 3)
goto makeconn
}
Expand Down
Loading

0 comments on commit 6417e33

Please sign in to comment.