Refactor authentication data representations

This commit is contained in:
Andy Wang 2020-01-24 16:44:29 +00:00
parent fa1c109d90
commit abc39e4e90
5 changed files with 39 additions and 28 deletions

View File

@ -8,8 +8,15 @@ import (
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
type clientHelloFields struct {
random []byte
sessionId []byte
x25519KeyShare []byte
sni []byte
}
type browser interface { type browser interface {
composeClientHello(chHiddenData) []byte composeClientHello(clientHelloFields) []byte
} }
func makeServerName(serverName string) []byte { func makeServerName(serverName string) []byte {
@ -37,6 +44,14 @@ func addExtRec(typ []byte, data []byte) []byte {
return ret 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 { type DirectTLS struct {
Transport Transport
} }
@ -48,8 +63,8 @@ func (DirectTLS) UnitReadFunc() func(net.Conn, []byte) (int, error) { return uti
// if the server proceed with Cloak authentication // if the server proceed with Cloak authentication
func (DirectTLS) PrepareConnection(sta *State, conn net.Conn) (preparedConn net.Conn, sessionKey []byte, err error) { func (DirectTLS) PrepareConnection(sta *State, conn net.Conn) (preparedConn net.Conn, sessionKey []byte, err error) {
preparedConn = conn preparedConn = conn
hd, sharedSecret := makeHiddenData(sta) payload, sharedSecret := makeAuthenticationPayload(sta)
chOnly := sta.browser.composeClientHello(hd) chOnly := sta.browser.composeClientHello(unmarshalAuthenticationInfo(payload, sta.ServerName))
chWithRecordLayer := util.AddRecordLayer(chOnly, []byte{0x16}, []byte{0x03, 0x01}) chWithRecordLayer := util.AddRecordLayer(chOnly, []byte{0x16}, []byte{0x03, 0x01})
_, err = preparedConn.Write(chWithRecordLayer) _, err = preparedConn.Write(chWithRecordLayer)
if err != nil { if err != nil {

View File

@ -3,8 +3,10 @@ package client
import ( import (
"crypto/rand" "crypto/rand"
"encoding/binary" "encoding/binary"
"encoding/hex"
"github.com/cbeuw/Cloak/internal/ecdh" "github.com/cbeuw/Cloak/internal/ecdh"
"github.com/cbeuw/Cloak/internal/util" "github.com/cbeuw/Cloak/internal/util"
"log"
"sync/atomic" "sync/atomic"
) )
@ -12,17 +14,14 @@ const (
UNORDERED_FLAG = 0x01 // 0000 0001 UNORDERED_FLAG = 0x01 // 0000 0001
) )
type chHiddenData struct { type authenticationPayload struct {
fullRaw []byte // pubkey, ciphertext, tag randPubKey [32]byte
chRandom []byte ciphertextWithTag [64]byte
chSessionId []byte
chX25519KeyShare []byte
chExtSNI []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. // 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 // random is marshalled ephemeral pub key 32 bytes
/* /*
Authentication data: 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 // The authentication ciphertext and its tag are then distributed among SessionId and X25519KeyShare
ephPv, ephPub, _ := ecdh.GenerateKey(rand.Reader) ephPv, ephPub, _ := ecdh.GenerateKey(rand.Reader)
ret.chRandom = ecdh.Marshal(ephPub) copy(ret.randPubKey[:], ecdh.Marshal(ephPub))
plaintext := make([]byte, 48) plaintext := make([]byte, 48)
copy(plaintext, sta.UID) copy(plaintext, sta.UID)
@ -48,11 +47,8 @@ func makeHiddenData(sta *State) (ret chHiddenData, sharedSecret []byte) {
} }
sharedSecret = ecdh.GenerateSharedSecret(ephPv, sta.staticPub) sharedSecret = ecdh.GenerateSharedSecret(ephPv, sta.staticPub)
nonce := ret.chRandom[0:12] ciphertextWithTag, _ := util.AESGCMEncrypt(ret.randPubKey[:12], sharedSecret, plaintext)
ciphertextWithTag, _ := util.AESGCMEncrypt(nonce, sharedSecret, plaintext) log.Print(hex.EncodeToString(sharedSecret))
ret.fullRaw = append(ret.chRandom, ciphertextWithTag...) copy(ret.ciphertextWithTag[:], ciphertextWithTag[:])
ret.chSessionId = ciphertextWithTag[0:32]
ret.chX25519KeyShare = ciphertextWithTag[32:64]
ret.chExtSNI = makeServerName(sta.ServerName)
return return
} }

View File

@ -79,20 +79,20 @@ func (c *Chrome) composeExtensions(sni []byte, keyShare []byte) []byte {
return ret return ret
} }
func (c *Chrome) composeClientHello(hd chHiddenData) (ch []byte) { func (c *Chrome) composeClientHello(hd clientHelloFields) (ch []byte) {
var clientHello [12][]byte var clientHello [12][]byte
clientHello[0] = []byte{0x01} // handshake type clientHello[0] = []byte{0x01} // handshake type
clientHello[1] = []byte{0x00, 0x01, 0xfc} // length 508 clientHello[1] = []byte{0x00, 0x01, 0xfc} // length 508
clientHello[2] = []byte{0x03, 0x03} // client version 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[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 clientHello[6] = []byte{0x00, 0x22} // cipher suites length 34
cipherSuites, _ := hex.DecodeString("130113021303c02bc02fc02cc030cca9cca8c013c014009c009d002f0035000a") cipherSuites, _ := hex.DecodeString("130113021303c02bc02fc02cc030cca9cca8c013c014009c009d002f0035000a")
clientHello[7] = append(makeGREASE(), cipherSuites...) // cipher suites clientHello[7] = append(makeGREASE(), cipherSuites...) // cipher suites
clientHello[8] = []byte{0x01} // compression methods length 1 clientHello[8] = []byte{0x01} // compression methods length 1
clientHello[9] = []byte{0x00} // compression methods 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 clientHello[10] = []byte{0x00, 0x00} // extensions length 401
binary.BigEndian.PutUint16(clientHello[10], uint16(len(clientHello[11]))) binary.BigEndian.PutUint16(clientHello[10], uint16(len(clientHello[11])))
var ret []byte var ret []byte

View File

@ -51,21 +51,21 @@ func (f *Firefox) composeExtensions(SNI []byte, keyShare []byte) []byte {
return ret return ret
} }
func (f *Firefox) composeClientHello(hd chHiddenData) (ch []byte) { func (f *Firefox) composeClientHello(hd clientHelloFields) (ch []byte) {
var clientHello [12][]byte var clientHello [12][]byte
clientHello[0] = []byte{0x01} // handshake type clientHello[0] = []byte{0x01} // handshake type
clientHello[1] = []byte{0x00, 0x01, 0xfc} // length 508 clientHello[1] = []byte{0x00, 0x01, 0xfc} // length 508
clientHello[2] = []byte{0x03, 0x03} // client version 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[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 clientHello[6] = []byte{0x00, 0x24} // cipher suites length 36
cipherSuites, _ := hex.DecodeString("130113031302c02bc02fcca9cca8c02cc030c00ac009c013c01400330039002f0035000a") cipherSuites, _ := hex.DecodeString("130113031302c02bc02fcca9cca8c02cc030c00ac009c013c01400330039002f0035000a")
clientHello[7] = cipherSuites // cipher suites clientHello[7] = cipherSuites // cipher suites
clientHello[8] = []byte{0x01} // compression methods length 1 clientHello[8] = []byte{0x01} // compression methods length 1
clientHello[9] = []byte{0x00} // compression methods 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 clientHello[10] = []byte{0x00, 0x00} // extensions length
binary.BigEndian.PutUint16(clientHello[10], uint16(len(clientHello[11]))) binary.BigEndian.PutUint16(clientHello[10], uint16(len(clientHello[11])))

View File

@ -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) return preparedConn, nil, fmt.Errorf("failed to parse ws url: %v", err)
} }
hd, sharedSecret := makeHiddenData(sta) payload, sharedSecret := makeAuthenticationPayload(sta)
header := http.Header{} 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) c, _, err := websocket.NewClient(preparedConn, u, header, 16480, 16480)
if err != nil { if err != nil {
return preparedConn, nil, fmt.Errorf("failed to handshake: %v", err) return preparedConn, nil, fmt.Errorf("failed to handshake: %v", err)