Make key lengths explicit

This commit is contained in:
Andy Wang 2020-04-07 21:15:28 +01:00
parent 443aeecf5f
commit c1f3408c2c
12 changed files with 38 additions and 40 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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