mirror of https://github.com/cbeuw/Cloak
Make key lengths explicit
This commit is contained in:
parent
443aeecf5f
commit
c1f3408c2c
|
|
@ -71,8 +71,8 @@ func dispatchConnection(conn net.Conn, sta *server.State) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionKey := make([]byte, 32)
|
var sessionKey [32]byte
|
||||||
util.CryptoRandRead(sessionKey)
|
util.CryptoRandRead(sessionKey[:])
|
||||||
obfuscator, err := mux.GenerateObfs(ci.EncryptionMethod, sessionKey, ci.Transport.HasRecordLayer())
|
obfuscator, err := mux.GenerateObfs(ci.EncryptionMethod, sessionKey, ci.Transport.HasRecordLayer())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ func (DirectTLS) UnitReadFunc() func(net.Conn, []byte) (int, error) { return uti
|
||||||
|
|
||||||
// PrepareConnection handles the TLS handshake for a given conn and returns the sessionKey
|
// PrepareConnection handles the TLS handshake for a given conn and returns the sessionKey
|
||||||
// if the server proceed with Cloak authentication
|
// if the server proceed with Cloak authentication
|
||||||
func (tls DirectTLS) PrepareConnection(authInfo *authInfo, conn net.Conn) (preparedConn net.Conn, sessionKey []byte, err error) {
|
func (tls DirectTLS) PrepareConnection(authInfo *authInfo, conn net.Conn) (preparedConn net.Conn, sessionKey [32]byte, err error) {
|
||||||
preparedConn = conn
|
preparedConn = conn
|
||||||
payload, sharedSecret := makeAuthenticationPayload(authInfo, rand.Reader, time.Now())
|
payload, sharedSecret := makeAuthenticationPayload(authInfo, rand.Reader, time.Now())
|
||||||
chOnly := tls.browser.composeClientHello(genStegClientHello(payload, authInfo.MockDomain))
|
chOnly := tls.browser.composeClientHello(genStegClientHello(payload, authInfo.MockDomain))
|
||||||
|
|
@ -86,10 +86,11 @@ func (tls DirectTLS) PrepareConnection(authInfo *authInfo, conn net.Conn) (prepa
|
||||||
encrypted := append(buf[11:43], buf[89:121]...)
|
encrypted := append(buf[11:43], buf[89:121]...)
|
||||||
nonce := encrypted[0:12]
|
nonce := encrypted[0:12]
|
||||||
ciphertextWithTag := encrypted[12:60]
|
ciphertextWithTag := encrypted[12:60]
|
||||||
sessionKey, err = util.AESGCMDecrypt(nonce, sharedSecret, ciphertextWithTag)
|
sessionKeySlice, err := util.AESGCMDecrypt(nonce, sharedSecret[:], ciphertextWithTag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
copy(sessionKey[:], sessionKeySlice)
|
||||||
|
|
||||||
for i := 0; i < 2; i++ {
|
for i := 0; i < 2; i++ {
|
||||||
// ChangeCipherSpec and EncryptedCert (in the format of application data)
|
// ChangeCipherSpec and EncryptedCert (in the format of application data)
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ type authInfo struct {
|
||||||
|
|
||||||
// makeAuthenticationPayload 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 authenticationPayload
|
// encrypt the authenticationPayload
|
||||||
func makeAuthenticationPayload(authInfo *authInfo, randReader io.Reader, time time.Time) (ret authenticationPayload, sharedSecret []byte) {
|
func makeAuthenticationPayload(authInfo *authInfo, randReader io.Reader, time time.Time) (ret authenticationPayload, sharedSecret [32]byte) {
|
||||||
/*
|
/*
|
||||||
Authentication data:
|
Authentication data:
|
||||||
+----------+----------------+---------------------+-------------+--------------+--------+------------+
|
+----------+----------------+---------------------+-------------+--------------+--------+------------+
|
||||||
|
|
@ -53,8 +53,8 @@ func makeAuthenticationPayload(authInfo *authInfo, randReader io.Reader, time ti
|
||||||
plaintext[41] |= UNORDERED_FLAG
|
plaintext[41] |= UNORDERED_FLAG
|
||||||
}
|
}
|
||||||
|
|
||||||
sharedSecret = ecdh.GenerateSharedSecret(ephPv, authInfo.ServerPubKey)
|
copy(sharedSecret[:], ecdh.GenerateSharedSecret(ephPv, authInfo.ServerPubKey))
|
||||||
ciphertextWithTag, _ := util.AESGCMEncrypt(ret.randPubKey[:12], sharedSecret, plaintext)
|
ciphertextWithTag, _ := util.AESGCMEncrypt(ret.randPubKey[:12], sharedSecret[:], plaintext)
|
||||||
copy(ret.ciphertextWithTag[:], ciphertextWithTag[:])
|
copy(ret.ciphertextWithTag[:], ciphertextWithTag[:])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ func MakeSession(connConfig *remoteConnConfig, authInfo *authInfo, isAdmin bool)
|
||||||
time.Sleep(time.Second * 3)
|
time.Sleep(time.Second * 3)
|
||||||
goto makeconn
|
goto makeconn
|
||||||
}
|
}
|
||||||
var sk []byte
|
var sk [32]byte
|
||||||
remoteConn, sk, err = connConfig.Transport.PrepareConnection(authInfo, remoteConn)
|
remoteConn, sk, err = connConfig.Transport.PrepareConnection(authInfo, remoteConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
remoteConn.Close()
|
remoteConn.Close()
|
||||||
|
|
@ -64,7 +64,7 @@ func MakeSession(connConfig *remoteConnConfig, authInfo *authInfo, isAdmin bool)
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
log.Debug("All underlying connections established")
|
log.Debug("All underlying connections established")
|
||||||
|
|
||||||
sessionKey := _sessionKey.Load().([]byte)
|
sessionKey := _sessionKey.Load().([32]byte)
|
||||||
obfuscator, err := mux.GenerateObfs(authInfo.EncryptionMethod, sessionKey, connConfig.Transport.HasRecordLayer())
|
obfuscator, err := mux.GenerateObfs(authInfo.EncryptionMethod, sessionKey, connConfig.Transport.HasRecordLayer())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package client
|
||||||
import "net"
|
import "net"
|
||||||
|
|
||||||
type Transport interface {
|
type Transport interface {
|
||||||
PrepareConnection(*authInfo, net.Conn) (net.Conn, []byte, error)
|
PrepareConnection(*authInfo, net.Conn) (net.Conn, [32]byte, error)
|
||||||
HasRecordLayer() bool
|
HasRecordLayer() bool
|
||||||
UnitReadFunc() func(net.Conn, []byte) (int, error)
|
UnitReadFunc() func(net.Conn, []byte) (int, error)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ type WSOverTLS struct {
|
||||||
func (WSOverTLS) HasRecordLayer() bool { return false }
|
func (WSOverTLS) HasRecordLayer() bool { return false }
|
||||||
func (WSOverTLS) UnitReadFunc() func(net.Conn, []byte) (int, error) { return util.ReadWebSocket }
|
func (WSOverTLS) UnitReadFunc() func(net.Conn, []byte) (int, error) { return util.ReadWebSocket }
|
||||||
|
|
||||||
func (ws WSOverTLS) PrepareConnection(authInfo *authInfo, cdnConn net.Conn) (preparedConn net.Conn, sessionKey []byte, err error) {
|
func (ws WSOverTLS) PrepareConnection(authInfo *authInfo, cdnConn net.Conn) (preparedConn net.Conn, sessionKey [32]byte, err error) {
|
||||||
utlsConfig := &utls.Config{
|
utlsConfig := &utls.Config{
|
||||||
ServerName: authInfo.MockDomain,
|
ServerName: authInfo.MockDomain,
|
||||||
InsecureSkipVerify: true,
|
InsecureSkipVerify: true,
|
||||||
|
|
@ -36,7 +36,7 @@ func (ws WSOverTLS) PrepareConnection(authInfo *authInfo, cdnConn net.Conn) (pre
|
||||||
|
|
||||||
u, err := url.Parse("ws://" + ws.cdnDomainPort)
|
u, err := url.Parse("ws://" + ws.cdnDomainPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return preparedConn, nil, fmt.Errorf("failed to parse ws url: %v", err)
|
return preparedConn, sessionKey, fmt.Errorf("failed to parse ws url: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
payload, sharedSecret := makeAuthenticationPayload(authInfo, rand.Reader, time.Now())
|
payload, sharedSecret := makeAuthenticationPayload(authInfo, rand.Reader, time.Now())
|
||||||
|
|
@ -44,7 +44,7 @@ func (ws WSOverTLS) PrepareConnection(authInfo *authInfo, cdnConn net.Conn) (pre
|
||||||
header.Add("hidden", base64.StdEncoding.EncodeToString(append(payload.randPubKey[:], payload.ciphertextWithTag[:]...)))
|
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, sessionKey, fmt.Errorf("failed to handshake: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
preparedConn = &util.WebSocketConn{Conn: c}
|
preparedConn = &util.WebSocketConn{Conn: c}
|
||||||
|
|
@ -52,15 +52,19 @@ func (ws WSOverTLS) PrepareConnection(authInfo *authInfo, cdnConn net.Conn) (pre
|
||||||
buf := make([]byte, 128)
|
buf := make([]byte, 128)
|
||||||
n, err := preparedConn.Read(buf)
|
n, err := preparedConn.Read(buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return preparedConn, nil, fmt.Errorf("failed to read reply: %v", err)
|
return preparedConn, sessionKey, fmt.Errorf("failed to read reply: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n != 60 {
|
if n != 60 {
|
||||||
return preparedConn, nil, errors.New("reply must be 60 bytes")
|
return preparedConn, sessionKey, errors.New("reply must be 60 bytes")
|
||||||
}
|
}
|
||||||
|
|
||||||
reply := buf[:60]
|
reply := buf[:60]
|
||||||
sessionKey, err = util.AESGCMDecrypt(reply[:12], sharedSecret, reply[12:])
|
sessionKeySlice, err := util.AESGCMDecrypt(reply[:12], sharedSecret[:], reply[12:])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
copy(sessionKey[:], sessionKeySlice)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -144,21 +144,14 @@ func MakeDeobfs(salsaKey [32]byte, payloadCipher cipher.AEAD, hasRecordLayer boo
|
||||||
return deobfs
|
return deobfs
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateObfs(encryptionMethod byte, sessionKey []byte, hasRecordLayer bool) (obfuscator *Obfuscator, err error) {
|
func GenerateObfs(encryptionMethod byte, sessionKey [32]byte, hasRecordLayer bool) (obfuscator *Obfuscator, err error) {
|
||||||
if len(sessionKey) != 32 {
|
|
||||||
err = errors.New("sessionKey size must be 32 bytes")
|
|
||||||
}
|
|
||||||
|
|
||||||
var salsaKey [32]byte
|
|
||||||
copy(salsaKey[:], sessionKey)
|
|
||||||
|
|
||||||
var payloadCipher cipher.AEAD
|
var payloadCipher cipher.AEAD
|
||||||
switch encryptionMethod {
|
switch encryptionMethod {
|
||||||
case E_METHOD_PLAIN:
|
case E_METHOD_PLAIN:
|
||||||
payloadCipher = nil
|
payloadCipher = nil
|
||||||
case E_METHOD_AES_GCM:
|
case E_METHOD_AES_GCM:
|
||||||
var c cipher.Block
|
var c cipher.Block
|
||||||
c, err = aes.NewCipher(sessionKey)
|
c, err = aes.NewCipher(sessionKey[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -167,7 +160,7 @@ func GenerateObfs(encryptionMethod byte, sessionKey []byte, hasRecordLayer bool)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
case E_METHOD_CHACHA20_POLY1305:
|
case E_METHOD_CHACHA20_POLY1305:
|
||||||
payloadCipher, err = chacha20poly1305.New(sessionKey)
|
payloadCipher, err = chacha20poly1305.New(sessionKey[:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -176,8 +169,8 @@ func GenerateObfs(encryptionMethod byte, sessionKey []byte, hasRecordLayer bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
obfuscator = &Obfuscator{
|
obfuscator = &Obfuscator{
|
||||||
MakeObfs(salsaKey, payloadCipher, hasRecordLayer),
|
MakeObfs(sessionKey, payloadCipher, hasRecordLayer),
|
||||||
MakeDeobfs(salsaKey, payloadCipher, hasRecordLayer),
|
MakeDeobfs(sessionKey, payloadCipher, hasRecordLayer),
|
||||||
sessionKey,
|
sessionKey,
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ type Obfuscator struct {
|
||||||
Obfs Obfser
|
Obfs Obfser
|
||||||
// Remove TLS header, decrypt and unmarshall frames
|
// Remove TLS header, decrypt and unmarshall frames
|
||||||
Deobfs Deobfser
|
Deobfs Deobfser
|
||||||
SessionKey []byte
|
SessionKey [32]byte
|
||||||
}
|
}
|
||||||
|
|
||||||
type switchboardStrategy int
|
type switchboardStrategy int
|
||||||
|
|
|
||||||
|
|
@ -33,13 +33,13 @@ func (TLS) processFirstPacket(clientHello []byte, privateKey crypto.PrivateKey)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
respond = TLS{}.makeResponder(ch.sessionId, fragments.sharedSecret[:])
|
respond = TLS{}.makeResponder(ch.sessionId, fragments.sharedSecret)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (TLS) makeResponder(clientHelloSessionId []byte, sharedSecret []byte) Responder {
|
func (TLS) makeResponder(clientHelloSessionId []byte, sharedSecret [32]byte) Responder {
|
||||||
respond := func(originalConn net.Conn, sessionKey []byte) (preparedConn net.Conn, err error) {
|
respond := func(originalConn net.Conn, sessionKey [32]byte) (preparedConn net.Conn, err error) {
|
||||||
preparedConn = originalConn
|
preparedConn = originalConn
|
||||||
reply, err := composeReply(clientHelloSessionId, sharedSecret, sessionKey)
|
reply, err := composeReply(clientHelloSessionId, sharedSecret, sessionKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -162,11 +162,11 @@ func parseClientHello(data []byte) (ret *ClientHello, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func composeServerHello(sessionId []byte, sharedSecret []byte, sessionKey []byte) ([]byte, error) {
|
func composeServerHello(sessionId []byte, sharedSecret [32]byte, sessionKey [32]byte) ([]byte, error) {
|
||||||
nonce := make([]byte, 12)
|
nonce := make([]byte, 12)
|
||||||
util.CryptoRandRead(nonce)
|
util.CryptoRandRead(nonce)
|
||||||
|
|
||||||
encryptedKey, err := util.AESGCMEncrypt(nonce, sharedSecret, sessionKey) // 32 + 16 = 48 bytes
|
encryptedKey, err := util.AESGCMEncrypt(nonce, sharedSecret[:], sessionKey[:]) // 32 + 16 = 48 bytes
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -198,7 +198,7 @@ func composeServerHello(sessionId []byte, sharedSecret []byte, sessionKey []byte
|
||||||
|
|
||||||
// composeReply composes the ServerHello, ChangeCipherSpec and an ApplicationData messages
|
// composeReply composes the ServerHello, ChangeCipherSpec and an ApplicationData messages
|
||||||
// together with their respective record layers into one byte slice.
|
// together with their respective record layers into one byte slice.
|
||||||
func composeReply(clientHelloSessionId []byte, sharedSecret []byte, sessionKey []byte) ([]byte, error) {
|
func composeReply(clientHelloSessionId []byte, sharedSecret [32]byte, sessionKey [32]byte) ([]byte, error) {
|
||||||
TLS12 := []byte{0x03, 0x03}
|
TLS12 := []byte{0x03, 0x03}
|
||||||
sh, err := composeServerHello(clientHelloSessionId, sharedSecret, sessionKey)
|
sh, err := composeServerHello(clientHelloSessionId, sharedSecret, sessionKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Responder = func(originalConn net.Conn, sessionKey []byte) (preparedConn net.Conn, err error)
|
type Responder = func(originalConn net.Conn, sessionKey [32]byte) (preparedConn net.Conn, err error)
|
||||||
type Transport interface {
|
type Transport interface {
|
||||||
HasRecordLayer() bool
|
HasRecordLayer() bool
|
||||||
UnitReadFunc() func(net.Conn, []byte) (int, error)
|
UnitReadFunc() func(net.Conn, []byte) (int, error)
|
||||||
|
|
|
||||||
|
|
@ -35,13 +35,13 @@ func (WebSocket) processFirstPacket(reqPacket []byte, privateKey crypto.PrivateK
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
respond = WebSocket{}.makeResponder(reqPacket, fragments.sharedSecret[:])
|
respond = WebSocket{}.makeResponder(reqPacket, fragments.sharedSecret)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (WebSocket) makeResponder(reqPacket []byte, sharedSecret []byte) Responder {
|
func (WebSocket) makeResponder(reqPacket []byte, sharedSecret [32]byte) Responder {
|
||||||
respond := func(originalConn net.Conn, sessionKey []byte) (preparedConn net.Conn, err error) {
|
respond := func(originalConn net.Conn, sessionKey [32]byte) (preparedConn net.Conn, err error) {
|
||||||
handler := newWsHandshakeHandler()
|
handler := newWsHandshakeHandler()
|
||||||
|
|
||||||
// For an explanation of the following 3 lines, see the comments in websocketAux.go
|
// For an explanation of the following 3 lines, see the comments in websocketAux.go
|
||||||
|
|
@ -53,7 +53,7 @@ func (WebSocket) makeResponder(reqPacket []byte, sharedSecret []byte) Responder
|
||||||
util.CryptoRandRead(nonce)
|
util.CryptoRandRead(nonce)
|
||||||
|
|
||||||
// reply: [12 bytes nonce][32 bytes encrypted session key][16 bytes authentication tag]
|
// reply: [12 bytes nonce][32 bytes encrypted session key][16 bytes authentication tag]
|
||||||
encryptedKey, err := util.AESGCMEncrypt(nonce, sharedSecret, sessionKey) // 32 + 16 = 48 bytes
|
encryptedKey, err := util.AESGCMEncrypt(nonce, sharedSecret[:], sessionKey[:]) // 32 + 16 = 48 bytes
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("failed to encrypt reply: %v", err)
|
err = fmt.Errorf("failed to encrypt reply: %v", err)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue