mirror of https://github.com/cbeuw/Cloak
Refactor authentication data representations
This commit is contained in:
parent
fa1c109d90
commit
abc39e4e90
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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])))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue