diff --git a/cmd/ck-client/ck-client.go b/cmd/ck-client/ck-client.go index b76c5af..635439d 100644 --- a/cmd/ck-client/ck-client.go +++ b/cmd/ck-client/ck-client.go @@ -200,9 +200,10 @@ start: } } - obfs := mux.MakeObfs(sta.UID, crypto) - deobfs := mux.MakeDeobfs(sta.UID, crypto) - sesh := mux.MakeSession(sessionID, valve, obfs, deobfs, util.ReadTLS) + sessionKey := make([]byte, 32) + rand.Read(sessionKey) + sta.SessionKey = sessionKey + sesh := mux.MakeSession(sessionID, valve, mux.MakeObfs(sta.SessionKey, crypto), mux.MakeDeobfs(sta.SessionKey, crypto), util.ReadTLS) var wg sync.WaitGroup for i := 0; i < sta.NumConn; i++ { diff --git a/cmd/ck-server/ck-server.go b/cmd/ck-server/ck-server.go index b4fa737..3a1005c 100644 --- a/cmd/ck-server/ck-server.go +++ b/cmd/ck-server/ck-server.go @@ -70,7 +70,7 @@ func dispatchConnection(conn net.Conn, sta *server.State) { return } - isCloak, UID, sessionID, proxyMethod, encryptionMethod := server.TouchStone(ch, sta) + isCloak, UID, sessionID, proxyMethod, encryptionMethod, sessionKey := server.TouchStone(ch, sta) if !isCloak { log.Printf("+1 non Cloak TLS traffic from %v\n", conn.RemoteAddr()) goWeb(data) @@ -173,7 +173,7 @@ func dispatchConnection(conn net.Conn, sta *server.State) { return } - sesh, existing, err := user.GetSession(sessionID, mux.MakeObfs(UID, crypto), mux.MakeDeobfs(UID, crypto), util.ReadTLS) + sesh, existing, err := user.GetSession(sessionID, mux.MakeObfs(sessionKey, crypto), mux.MakeDeobfs(sessionKey, crypto), util.ReadTLS) if err != nil { user.DelSession(sessionID) log.Println(err) diff --git a/internal/client/auth.go b/internal/client/auth.go index f590e1a..50d9ddd 100644 --- a/internal/client/auth.go +++ b/internal/client/auth.go @@ -34,7 +34,7 @@ func MakeRandomField(sta *State) []byte { } func MakeSessionTicket(sta *State) []byte { - // sessionTicket: [marshalled ephemeral pub key 32 bytes][encrypted UID+sessionID 36 bytes, proxy method 16 bytes, encryption method 1 byte][16 bytes authentication tag][padding 91 bytes] + // sessionTicket: [marshalled ephemeral pub key 32 bytes][encrypted UID+sessionID 36 bytes, proxy method 16 bytes, encryption method 1 byte, sessionKey 32 bytes][16 bytes authentication tag][padding 59 bytes] // The first 12 bytes of the marshalled ephemeral public key is used as the nonce // for encrypting the UID tthInterval := sta.Now().Unix() / int64(sta.TicketTimeHint) @@ -53,14 +53,15 @@ func MakeSessionTicket(sta *State) []byte { copy(ticket[0:32], ecdh.Marshal(ephKP.PublicKey)) key := ecdh.GenerateSharedSecret(ephKP.PrivateKey, sta.staticPub) - plain := make([]byte, 53) + plain := make([]byte, 85) copy(plain, sta.UID) binary.BigEndian.PutUint32(plain[32:36], sta.sessionID) copy(plain[36:52], []byte(sta.ProxyMethod)) plain[52] = sta.EncryptionMethod + copy(plain[53:85], sta.SessionKey) cipher, _ := util.AESGCMEncrypt(ticket[0:12], key, plain) - copy(ticket[32:101], cipher) + copy(ticket[32:133], cipher) // The purpose of adding sessionID is that, the generated padding of sessionTicket needs to be unpredictable. // As shown in auth.go, the padding is generated by a psudo random generator. The seed // needs to be the same for each TicketTimeHint interval. However the value of epoch/TicketTimeHint @@ -71,6 +72,6 @@ func MakeSessionTicket(sta *State) []byte { // With the sessionID value generated at startup of ckclient and used as a part of the seed, the // sessionTicket is still identical for each TicketTimeHint interval, but others won't be able to know // how it was generated. It will also be different for each client. - copy(ticket[101:192], util.PsudoRandBytes(91, tthInterval+int64(sta.sessionID))) + copy(ticket[133:192], util.PsudoRandBytes(59, tthInterval+int64(sta.sessionID))) return ticket } diff --git a/internal/client/state.go b/internal/client/state.go index 1da978d..850d4ac 100644 --- a/internal/client/state.go +++ b/internal/client/state.go @@ -31,12 +31,13 @@ type State struct { RemoteHost string RemotePort string - Now func() time.Time - sessionID uint32 - UID []byte - staticPub crypto.PublicKey - keyPairsM sync.RWMutex - keyPairs map[int64]*keyPair + Now func() time.Time + sessionID uint32 + SessionKey []byte + UID []byte + staticPub crypto.PublicKey + keyPairsM sync.RWMutex + keyPairs map[int64]*keyPair ProxyMethod string EncryptionMethod byte diff --git a/internal/server/auth.go b/internal/server/auth.go index f6b0605..8234bb4 100644 --- a/internal/server/auth.go +++ b/internal/server/auth.go @@ -12,15 +12,15 @@ import ( ) // input ticket, return UID -func decryptSessionTicket(staticPv crypto.PrivateKey, ticket []byte) ([]byte, uint32, string, byte) { +func decryptSessionTicket(staticPv crypto.PrivateKey, ticket []byte) ([]byte, uint32, string, byte, []byte) { ephPub, _ := ecdh.Unmarshal(ticket[0:32]) key := ecdh.GenerateSharedSecret(staticPv, ephPub) - plain, err := util.AESGCMDecrypt(ticket[0:12], key, ticket[32:101]) + plain, err := util.AESGCMDecrypt(ticket[0:12], key, ticket[32:133]) if err != nil { - return nil, 0, "", 0x00 + return nil, 0, "", 0x00, nil } sessionID := binary.BigEndian.Uint32(plain[32:36]) - return plain[0:32], sessionID, string(bytes.Trim(plain[36:52], "\x00")), plain[52] + return plain[0:32], sessionID, string(bytes.Trim(plain[36:52], "\x00")), plain[52], plain[53:85] } func validateRandom(random []byte, UID []byte, time int64) bool { @@ -35,7 +35,7 @@ func validateRandom(random []byte, UID []byte, time int64) bool { h.Write(preHash) return bytes.Equal(h.Sum(nil)[0:16], random[16:32]) } -func TouchStone(ch *ClientHello, sta *State) (isCK bool, UID []byte, sessionID uint32, proxyMethod string, encryptionMethod byte) { +func TouchStone(ch *ClientHello, sta *State) (isCK bool, UID []byte, sessionID uint32, proxyMethod string, encryptionMethod byte, sessionKey []byte) { var random [32]byte copy(random[:], ch.random) @@ -53,7 +53,7 @@ func TouchStone(ch *ClientHello, sta *State) (isCK bool, UID []byte, sessionID u if len(ticket) < 68 { return } - UID, sessionID, proxyMethod, encryptionMethod = decryptSessionTicket(sta.staticPv, ticket) + UID, sessionID, proxyMethod, encryptionMethod, sessionKey = decryptSessionTicket(sta.staticPv, ticket) if len(UID) < 32 { return }