From abc39e4e906e0928909ef644625202461f91d937 Mon Sep 17 00:00:00 2001 From: Andy Wang Date: Fri, 24 Jan 2020 16:44:29 +0000 Subject: [PATCH] Refactor authentication data representations --- internal/client/TLS.go | 21 ++++++++++++++++++--- internal/client/auth.go | 26 +++++++++++--------------- internal/client/chrome.go | 8 ++++---- internal/client/firefox.go | 8 ++++---- internal/client/websocket.go | 4 ++-- 5 files changed, 39 insertions(+), 28 deletions(-) diff --git a/internal/client/TLS.go b/internal/client/TLS.go index 7fd9f75..aa2d5bb 100644 --- a/internal/client/TLS.go +++ b/internal/client/TLS.go @@ -8,8 +8,15 @@ import ( log "github.com/sirupsen/logrus" ) +type clientHelloFields struct { + random []byte + sessionId []byte + x25519KeyShare []byte + sni []byte +} + type browser interface { - composeClientHello(chHiddenData) []byte + composeClientHello(clientHelloFields) []byte } func makeServerName(serverName string) []byte { @@ -37,6 +44,14 @@ func addExtRec(typ []byte, data []byte) []byte { return ret } +func unmarshalAuthenticationInfo(ai authenticationPayload, serverName string) (ret clientHelloFields) { + ret.random = ai.randPubKey[:] + ret.sessionId = ai.ciphertextWithTag[0:32] + ret.x25519KeyShare = ai.ciphertextWithTag[32:64] + ret.sni = makeServerName(serverName) + return +} + type DirectTLS struct { Transport } @@ -48,8 +63,8 @@ func (DirectTLS) UnitReadFunc() func(net.Conn, []byte) (int, error) { return uti // if the server proceed with Cloak authentication func (DirectTLS) PrepareConnection(sta *State, conn net.Conn) (preparedConn net.Conn, sessionKey []byte, err error) { preparedConn = conn - hd, sharedSecret := makeHiddenData(sta) - chOnly := sta.browser.composeClientHello(hd) + payload, sharedSecret := makeAuthenticationPayload(sta) + chOnly := sta.browser.composeClientHello(unmarshalAuthenticationInfo(payload, sta.ServerName)) chWithRecordLayer := util.AddRecordLayer(chOnly, []byte{0x16}, []byte{0x03, 0x01}) _, err = preparedConn.Write(chWithRecordLayer) if err != nil { diff --git a/internal/client/auth.go b/internal/client/auth.go index b32d761..5613cfb 100644 --- a/internal/client/auth.go +++ b/internal/client/auth.go @@ -3,8 +3,10 @@ package client import ( "crypto/rand" "encoding/binary" + "encoding/hex" "github.com/cbeuw/Cloak/internal/ecdh" "github.com/cbeuw/Cloak/internal/util" + "log" "sync/atomic" ) @@ -12,17 +14,14 @@ const ( UNORDERED_FLAG = 0x01 // 0000 0001 ) -type chHiddenData struct { - fullRaw []byte // pubkey, ciphertext, tag - chRandom []byte - chSessionId []byte - chX25519KeyShare []byte - chExtSNI []byte +type authenticationPayload struct { + randPubKey [32]byte + ciphertextWithTag [64]byte } -// makeHiddenData generates the ephemeral key pair, calculates the shared secret, and then compose and +// makeAuthenticationPayload generates the ephemeral key pair, calculates the shared secret, and then compose and // encrypt the Authentication data. It also composes SNI extension. -func makeHiddenData(sta *State) (ret chHiddenData, sharedSecret []byte) { +func makeAuthenticationPayload(sta *State) (ret authenticationPayload, sharedSecret []byte) { // random is marshalled ephemeral pub key 32 bytes /* Authentication data: @@ -34,7 +33,7 @@ func makeHiddenData(sta *State) (ret chHiddenData, sharedSecret []byte) { */ // The authentication ciphertext and its tag are then distributed among SessionId and X25519KeyShare ephPv, ephPub, _ := ecdh.GenerateKey(rand.Reader) - ret.chRandom = ecdh.Marshal(ephPub) + copy(ret.randPubKey[:], ecdh.Marshal(ephPub)) plaintext := make([]byte, 48) copy(plaintext, sta.UID) @@ -48,11 +47,8 @@ func makeHiddenData(sta *State) (ret chHiddenData, sharedSecret []byte) { } sharedSecret = ecdh.GenerateSharedSecret(ephPv, sta.staticPub) - nonce := ret.chRandom[0:12] - ciphertextWithTag, _ := util.AESGCMEncrypt(nonce, sharedSecret, plaintext) - ret.fullRaw = append(ret.chRandom, ciphertextWithTag...) - ret.chSessionId = ciphertextWithTag[0:32] - ret.chX25519KeyShare = ciphertextWithTag[32:64] - ret.chExtSNI = makeServerName(sta.ServerName) + ciphertextWithTag, _ := util.AESGCMEncrypt(ret.randPubKey[:12], sharedSecret, plaintext) + log.Print(hex.EncodeToString(sharedSecret)) + copy(ret.ciphertextWithTag[:], ciphertextWithTag[:]) return } diff --git a/internal/client/chrome.go b/internal/client/chrome.go index 02544e9..c0a78d8 100644 --- a/internal/client/chrome.go +++ b/internal/client/chrome.go @@ -79,20 +79,20 @@ func (c *Chrome) composeExtensions(sni []byte, keyShare []byte) []byte { return ret } -func (c *Chrome) composeClientHello(hd chHiddenData) (ch []byte) { +func (c *Chrome) composeClientHello(hd clientHelloFields) (ch []byte) { var clientHello [12][]byte clientHello[0] = []byte{0x01} // handshake type clientHello[1] = []byte{0x00, 0x01, 0xfc} // length 508 clientHello[2] = []byte{0x03, 0x03} // client version - clientHello[3] = hd.chRandom // random + clientHello[3] = hd.random // random clientHello[4] = []byte{0x20} // session id length 32 - clientHello[5] = hd.chSessionId // session id + clientHello[5] = hd.sessionId // session id clientHello[6] = []byte{0x00, 0x22} // cipher suites length 34 cipherSuites, _ := hex.DecodeString("130113021303c02bc02fc02cc030cca9cca8c013c014009c009d002f0035000a") 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(hd.chExtSNI, hd.chX25519KeyShare) + clientHello[11] = c.composeExtensions(hd.sni, hd.x25519KeyShare) 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/firefox.go b/internal/client/firefox.go index b7547ac..fe26468 100644 --- a/internal/client/firefox.go +++ b/internal/client/firefox.go @@ -51,21 +51,21 @@ func (f *Firefox) composeExtensions(SNI []byte, keyShare []byte) []byte { return ret } -func (f *Firefox) composeClientHello(hd chHiddenData) (ch []byte) { +func (f *Firefox) composeClientHello(hd clientHelloFields) (ch []byte) { var clientHello [12][]byte clientHello[0] = []byte{0x01} // handshake type clientHello[1] = []byte{0x00, 0x01, 0xfc} // length 508 clientHello[2] = []byte{0x03, 0x03} // client version - clientHello[3] = hd.chRandom // random + clientHello[3] = hd.random // random clientHello[4] = []byte{0x20} // session id length 32 - clientHello[5] = hd.chSessionId // session id + clientHello[5] = hd.sessionId // session id clientHello[6] = []byte{0x00, 0x24} // cipher suites length 36 cipherSuites, _ := hex.DecodeString("130113031302c02bc02fcca9cca8c02cc030c00ac009c013c01400330039002f0035000a") clientHello[7] = cipherSuites // cipher suites clientHello[8] = []byte{0x01} // compression methods length 1 clientHello[9] = []byte{0x00} // compression methods - clientHello[11] = f.composeExtensions(hd.chExtSNI, hd.chX25519KeyShare) + clientHello[11] = f.composeExtensions(hd.sni, hd.x25519KeyShare) clientHello[10] = []byte{0x00, 0x00} // extensions length binary.BigEndian.PutUint16(clientHello[10], uint16(len(clientHello[11]))) diff --git a/internal/client/websocket.go b/internal/client/websocket.go index 64094e2..f15b00b 100644 --- a/internal/client/websocket.go +++ b/internal/client/websocket.go @@ -37,9 +37,9 @@ func (WSOverTLS) PrepareConnection(sta *State, conn net.Conn) (preparedConn net. return preparedConn, nil, fmt.Errorf("failed to parse ws url: %v", err) } - hd, sharedSecret := makeHiddenData(sta) + payload, sharedSecret := makeAuthenticationPayload(sta) header := http.Header{} - header.Add("hidden", base64.StdEncoding.EncodeToString(hd.fullRaw)) + header.Add("hidden", base64.StdEncoding.EncodeToString(append(payload.randPubKey[:], payload.ciphertextWithTag[:]...))) c, _, err := websocket.NewClient(preparedConn, u, header, 16480, 16480) if err != nil { return preparedConn, nil, fmt.Errorf("failed to handshake: %v", err)