diff --git a/cmd/ck-server/ck-server.go b/cmd/ck-server/ck-server.go index 0f58ea1..f000732 100644 --- a/cmd/ck-server/ck-server.go +++ b/cmd/ck-server/ck-server.go @@ -71,8 +71,8 @@ func dispatchConnection(conn net.Conn, sta *server.State) { return } - sessionKey := make([]byte, 32) - util.CryptoRandRead(sessionKey) + var sessionKey [32]byte + util.CryptoRandRead(sessionKey[:]) obfuscator, err := mux.GenerateObfs(ci.EncryptionMethod, sessionKey, ci.Transport.HasRecordLayer()) if err != nil { log.Error(err) diff --git a/internal/client/TLS.go b/internal/client/TLS.go index a344cc0..48c9985 100644 --- a/internal/client/TLS.go +++ b/internal/client/TLS.go @@ -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 // 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 payload, sharedSecret := makeAuthenticationPayload(authInfo, rand.Reader, time.Now()) 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]...) nonce := encrypted[0:12] ciphertextWithTag := encrypted[12:60] - sessionKey, err = util.AESGCMDecrypt(nonce, sharedSecret, ciphertextWithTag) + sessionKeySlice, err := util.AESGCMDecrypt(nonce, sharedSecret[:], ciphertextWithTag) if err != nil { return } + copy(sessionKey[:], sessionKeySlice) for i := 0; i < 2; i++ { // ChangeCipherSpec and EncryptedCert (in the format of application data) diff --git a/internal/client/auth.go b/internal/client/auth.go index 0fe126b..ad75061 100644 --- a/internal/client/auth.go +++ b/internal/client/auth.go @@ -30,7 +30,7 @@ type authInfo struct { // makeAuthenticationPayload generates the ephemeral key pair, calculates the shared secret, and then compose and // 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: +----------+----------------+---------------------+-------------+--------------+--------+------------+ @@ -53,8 +53,8 @@ func makeAuthenticationPayload(authInfo *authInfo, randReader io.Reader, time ti plaintext[41] |= UNORDERED_FLAG } - sharedSecret = ecdh.GenerateSharedSecret(ephPv, authInfo.ServerPubKey) - ciphertextWithTag, _ := util.AESGCMEncrypt(ret.randPubKey[:12], sharedSecret, plaintext) + copy(sharedSecret[:], ecdh.GenerateSharedSecret(ephPv, authInfo.ServerPubKey)) + ciphertextWithTag, _ := util.AESGCMEncrypt(ret.randPubKey[:12], sharedSecret[:], plaintext) copy(ret.ciphertextWithTag[:], ciphertextWithTag[:]) return } diff --git a/internal/client/connector.go b/internal/client/connector.go index 583bbdb..6ece9a8 100644 --- a/internal/client/connector.go +++ b/internal/client/connector.go @@ -48,7 +48,7 @@ func MakeSession(connConfig *remoteConnConfig, authInfo *authInfo, isAdmin bool) time.Sleep(time.Second * 3) goto makeconn } - var sk []byte + var sk [32]byte remoteConn, sk, err = connConfig.Transport.PrepareConnection(authInfo, remoteConn) if err != nil { remoteConn.Close() @@ -64,7 +64,7 @@ func MakeSession(connConfig *remoteConnConfig, authInfo *authInfo, isAdmin bool) wg.Wait() 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()) if err != nil { log.Fatal(err) diff --git a/internal/client/transport.go b/internal/client/transport.go index 7f983c6..a361011 100644 --- a/internal/client/transport.go +++ b/internal/client/transport.go @@ -3,7 +3,7 @@ package client import "net" type Transport interface { - PrepareConnection(*authInfo, net.Conn) (net.Conn, []byte, error) + PrepareConnection(*authInfo, net.Conn) (net.Conn, [32]byte, error) HasRecordLayer() bool UnitReadFunc() func(net.Conn, []byte) (int, error) } diff --git a/internal/client/websocket.go b/internal/client/websocket.go index 51c3e73..202b639 100644 --- a/internal/client/websocket.go +++ b/internal/client/websocket.go @@ -22,7 +22,7 @@ type WSOverTLS struct { func (WSOverTLS) HasRecordLayer() bool { return false } 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{ ServerName: authInfo.MockDomain, InsecureSkipVerify: true, @@ -36,7 +36,7 @@ func (ws WSOverTLS) PrepareConnection(authInfo *authInfo, cdnConn net.Conn) (pre u, err := url.Parse("ws://" + ws.cdnDomainPort) 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()) @@ -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[:]...))) c, _, err := websocket.NewClient(preparedConn, u, header, 16480, 16480) 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} @@ -52,15 +52,19 @@ func (ws WSOverTLS) PrepareConnection(authInfo *authInfo, cdnConn net.Conn) (pre buf := make([]byte, 128) n, err := preparedConn.Read(buf) 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 { - return preparedConn, nil, errors.New("reply must be 60 bytes") + return preparedConn, sessionKey, errors.New("reply must be 60 bytes") } 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 } diff --git a/internal/multiplex/obfs.go b/internal/multiplex/obfs.go index 78ac335..75667fd 100644 --- a/internal/multiplex/obfs.go +++ b/internal/multiplex/obfs.go @@ -144,21 +144,14 @@ func MakeDeobfs(salsaKey [32]byte, payloadCipher cipher.AEAD, hasRecordLayer boo return deobfs } -func GenerateObfs(encryptionMethod byte, sessionKey []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) - +func GenerateObfs(encryptionMethod byte, sessionKey [32]byte, hasRecordLayer bool) (obfuscator *Obfuscator, err error) { var payloadCipher cipher.AEAD switch encryptionMethod { case E_METHOD_PLAIN: payloadCipher = nil case E_METHOD_AES_GCM: var c cipher.Block - c, err = aes.NewCipher(sessionKey) + c, err = aes.NewCipher(sessionKey[:]) if err != nil { return } @@ -167,7 +160,7 @@ func GenerateObfs(encryptionMethod byte, sessionKey []byte, hasRecordLayer bool) return } case E_METHOD_CHACHA20_POLY1305: - payloadCipher, err = chacha20poly1305.New(sessionKey) + payloadCipher, err = chacha20poly1305.New(sessionKey[:]) if err != nil { return } @@ -176,8 +169,8 @@ func GenerateObfs(encryptionMethod byte, sessionKey []byte, hasRecordLayer bool) } obfuscator = &Obfuscator{ - MakeObfs(salsaKey, payloadCipher, hasRecordLayer), - MakeDeobfs(salsaKey, payloadCipher, hasRecordLayer), + MakeObfs(sessionKey, payloadCipher, hasRecordLayer), + MakeDeobfs(sessionKey, payloadCipher, hasRecordLayer), sessionKey, } return diff --git a/internal/multiplex/session.go b/internal/multiplex/session.go index eb82052..64de8d6 100644 --- a/internal/multiplex/session.go +++ b/internal/multiplex/session.go @@ -25,7 +25,7 @@ type Obfuscator struct { Obfs Obfser // Remove TLS header, decrypt and unmarshall frames Deobfs Deobfser - SessionKey []byte + SessionKey [32]byte } type switchboardStrategy int diff --git a/internal/server/TLS.go b/internal/server/TLS.go index 5111901..7cdcf47 100644 --- a/internal/server/TLS.go +++ b/internal/server/TLS.go @@ -33,13 +33,13 @@ func (TLS) processFirstPacket(clientHello []byte, privateKey crypto.PrivateKey) return } - respond = TLS{}.makeResponder(ch.sessionId, fragments.sharedSecret[:]) + respond = TLS{}.makeResponder(ch.sessionId, fragments.sharedSecret) return } -func (TLS) makeResponder(clientHelloSessionId []byte, sharedSecret []byte) Responder { - respond := func(originalConn net.Conn, sessionKey []byte) (preparedConn net.Conn, err error) { +func (TLS) makeResponder(clientHelloSessionId []byte, sharedSecret [32]byte) Responder { + respond := func(originalConn net.Conn, sessionKey [32]byte) (preparedConn net.Conn, err error) { preparedConn = originalConn reply, err := composeReply(clientHelloSessionId, sharedSecret, sessionKey) if err != nil { diff --git a/internal/server/TLSAux.go b/internal/server/TLSAux.go index 26a8095..fb201da 100644 --- a/internal/server/TLSAux.go +++ b/internal/server/TLSAux.go @@ -162,11 +162,11 @@ func parseClientHello(data []byte) (ret *ClientHello, err error) { 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) 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 { return nil, err } @@ -198,7 +198,7 @@ func composeServerHello(sessionId []byte, sharedSecret []byte, sessionKey []byte // composeReply composes the ServerHello, ChangeCipherSpec and an ApplicationData messages // 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} sh, err := composeServerHello(clientHelloSessionId, sharedSecret, sessionKey) if err != nil { diff --git a/internal/server/transport.go b/internal/server/transport.go index 87daf76..375a016 100644 --- a/internal/server/transport.go +++ b/internal/server/transport.go @@ -6,7 +6,7 @@ import ( "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 { HasRecordLayer() bool UnitReadFunc() func(net.Conn, []byte) (int, error) diff --git a/internal/server/websocket.go b/internal/server/websocket.go index 03961fd..a05bcb2 100644 --- a/internal/server/websocket.go +++ b/internal/server/websocket.go @@ -35,13 +35,13 @@ func (WebSocket) processFirstPacket(reqPacket []byte, privateKey crypto.PrivateK return } - respond = WebSocket{}.makeResponder(reqPacket, fragments.sharedSecret[:]) + respond = WebSocket{}.makeResponder(reqPacket, fragments.sharedSecret) return } -func (WebSocket) makeResponder(reqPacket []byte, sharedSecret []byte) Responder { - respond := func(originalConn net.Conn, sessionKey []byte) (preparedConn net.Conn, err error) { +func (WebSocket) makeResponder(reqPacket []byte, sharedSecret [32]byte) Responder { + respond := func(originalConn net.Conn, sessionKey [32]byte) (preparedConn net.Conn, err error) { handler := newWsHandshakeHandler() // 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) // 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 { err = fmt.Errorf("failed to encrypt reply: %v", err) return