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

View File

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

View File

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

View File

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

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