From 710fa6835a3ac21d36ea76c4442a19d4b418312e Mon Sep 17 00:00:00 2001 From: Qian Wang Date: Sun, 16 Jun 2019 11:08:51 +1000 Subject: [PATCH] Reduce the size of UID to 16 bytes --- cmd/ck-client/admin.go | 48 ++++++++++++----------- cmd/ck-server/ck-server.go | 5 ++- cmd/ck-server/keygen.go | 9 ++--- config/ckclient.json | 2 +- config/ckserver.json | 2 +- internal/client/auth.go | 16 ++++---- internal/server/auth.go | 8 ++-- internal/server/usermanager/controller.go | 40 +++++++++---------- internal/server/usermanager/user.go | 2 +- internal/server/usermanager/userpanel.go | 12 +++--- 10 files changed, 72 insertions(+), 72 deletions(-) diff --git a/cmd/ck-client/admin.go b/cmd/ck-client/admin.go index 42eee81..da1acf0 100644 --- a/cmd/ck-client/admin.go +++ b/cmd/ck-client/admin.go @@ -34,6 +34,8 @@ type UserInfo struct { ExpiryTime int64 } +var b64 = base64.StdEncoding + type administrator struct { adminConn net.Conn adminUID []byte @@ -87,7 +89,7 @@ func adminHandshake(sta *client.State) (*administrator, error) { fmt.Println("Enter the admin UID") var b64AdminUID string fmt.Scanln(&b64AdminUID) - adminUID, err := base64.StdEncoding.DecodeString(b64AdminUID) + adminUID, err := b64.DecodeString(b64AdminUID) if err != nil { return nil, err } @@ -122,7 +124,7 @@ func (a *administrator) getRequest() (req []byte, err error) { fmt.Println("Enter UID") var b64UID string fmt.Scanln(&b64UID) - ret, _ := base64.StdEncoding.DecodeString(b64UID) + ret, _ := b64.DecodeString(b64UID) return ret } @@ -159,7 +161,7 @@ func (a *administrator) getRequest() (req []byte, err error) { var b64UID string fmt.Printf("UID:") fmt.Scanln(&b64UID) - UID, _ := base64.StdEncoding.DecodeString(b64UID) + UID, _ := b64.DecodeString(b64UID) uinfo.UID = UID fmt.Printf("SessionsCap:") fmt.Scanf("%d", &uinfo.SessionsCap) @@ -188,44 +190,44 @@ func (a *administrator) getRequest() (req []byte, err error) { UID := promptUID() req = a.request(append([]byte{0x06}, UID...)) case "7": - arg := make([]byte, 36) + arg := make([]byte, 20) copy(arg, promptUID()) - copy(arg[32:], promptUint32("SessionsCap")) + copy(arg[16:], promptUint32("SessionsCap")) req = a.request(append([]byte{0x07}, arg...)) case "8": - arg := make([]byte, 40) + arg := make([]byte, 24) copy(arg, promptUID()) - copy(arg[32:], promptInt64("UpRate")) + copy(arg[16:], promptInt64("UpRate")) req = a.request(append([]byte{0x08}, arg...)) case "9": - arg := make([]byte, 40) + arg := make([]byte, 24) copy(arg, promptUID()) - copy(arg[32:], promptInt64("DownRate")) + copy(arg[16:], promptInt64("DownRate")) req = a.request(append([]byte{0x09}, arg...)) case "10": - arg := make([]byte, 40) + arg := make([]byte, 24) copy(arg, promptUID()) - copy(arg[32:], promptInt64("UpCredit")) + copy(arg[16:], promptInt64("UpCredit")) req = a.request(append([]byte{0x0a}, arg...)) case "11": - arg := make([]byte, 40) + arg := make([]byte, 24) copy(arg, promptUID()) - copy(arg[32:], promptInt64("DownCredit")) + copy(arg[16:], promptInt64("DownCredit")) req = a.request(append([]byte{0x0b}, arg...)) case "12": - arg := make([]byte, 40) + arg := make([]byte, 24) copy(arg, promptUID()) - copy(arg[32:], promptInt64("ExpiryTime")) + copy(arg[16:], promptInt64("ExpiryTime")) req = a.request(append([]byte{0x0c}, arg...)) case "13": - arg := make([]byte, 40) + arg := make([]byte, 24) copy(arg, promptUID()) - copy(arg[32:], promptInt64("UpCredit to add")) + copy(arg[16:], promptInt64("UpCredit to add")) req = a.request(append([]byte{0x0d}, arg...)) case "14": - arg := make([]byte, 40) + arg := make([]byte, 24) copy(arg, promptUID()) - copy(arg[32:], promptInt64("DownCredit to add")) + copy(arg[16:], promptInt64("DownCredit to add")) req = a.request(append([]byte{0x0e}, arg...)) default: return nil, errors.New("Unreconised cmd") @@ -245,11 +247,11 @@ func (a *administrator) request(data []byte) []byte { rand.Read(buf[5:21]) //iv copy(buf[21:], data) - block, _ := aes.NewCipher(a.adminUID[0:16]) + block, _ := aes.NewCipher(a.adminUID) stream := cipher.NewCTR(block, buf[5:21]) stream.XORKeyStream(buf[21:21+dataLen], buf[21:21+dataLen]) - mac := hmac.New(sha256.New, a.adminUID[16:32]) + mac := hmac.New(sha256.New, a.adminUID) mac.Write(buf[5 : 21+dataLen]) copy(buf[21+dataLen:], mac.Sum(nil)) @@ -260,7 +262,7 @@ var ErrInvalidMac = errors.New("Mac mismatch") func (a *administrator) checkAndDecrypt(data []byte) ([]byte, error) { macIndex := len(data) - 32 - mac := hmac.New(sha256.New, a.adminUID[16:32]) + mac := hmac.New(sha256.New, a.adminUID) mac.Write(data[5:macIndex]) expected := mac.Sum(nil) if !hmac.Equal(data[macIndex:], expected) { @@ -269,7 +271,7 @@ func (a *administrator) checkAndDecrypt(data []byte) ([]byte, error) { iv := data[5:21] ret := data[21:macIndex] - block, _ := aes.NewCipher(a.adminUID[0:16]) + block, _ := aes.NewCipher(a.adminUID) stream := cipher.NewCTR(block, iv) stream.XORKeyStream(ret, ret) return ret, nil diff --git a/cmd/ck-server/ck-server.go b/cmd/ck-server/ck-server.go index 3a1005c..7499f18 100644 --- a/cmd/ck-server/ck-server.go +++ b/cmd/ck-server/ck-server.go @@ -18,6 +18,7 @@ import ( "github.com/cbeuw/Cloak/internal/util" ) +var b64 = base64.StdEncoding var version string func pipe(dst io.ReadWriteCloser, src io.ReadWriteCloser) { @@ -184,13 +185,13 @@ func dispatchConnection(conn net.Conn, sta *server.State) { sesh.AddConnection(conn) return } else { - log.Printf("New session from UID:%v, sessionID:%v\n", base64.StdEncoding.EncodeToString(UID), sessionID) + log.Printf("New session from UID:%v, sessionID:%v\n", b64.EncodeToString(UID), sessionID) sesh.AddConnection(conn) for { newStream, err := sesh.AcceptStream() if err != nil { if err == mux.ErrBrokenSession { - log.Printf("Session closed for UID:%v, sessionID:%v\n", base64.StdEncoding.EncodeToString(UID), sessionID) + log.Printf("Session closed for UID:%v, sessionID:%v\n", b64.EncodeToString(UID), sessionID) user.DelSession(sessionID) return } else { diff --git a/cmd/ck-server/keygen.go b/cmd/ck-server/keygen.go index 77db0a6..a3093dc 100644 --- a/cmd/ck-server/keygen.go +++ b/cmd/ck-server/keygen.go @@ -2,21 +2,18 @@ package main import ( "crypto/rand" - "encoding/base64" "github.com/cbeuw/Cloak/internal/ecdh" ) -var b64 = base64.StdEncoding.EncodeToString - func generateUID() string { - UID := make([]byte, 32) + UID := make([]byte, 16) rand.Read(UID) - return b64(UID) + return b64.EncodeToString(UID) } func generateKeyPair() (string, string) { staticPv, staticPub, _ := ecdh.GenerateKey(rand.Reader) marshPub := ecdh.Marshal(staticPub) marshPv := staticPv.(*[32]byte)[:] - return b64(marshPub), b64(marshPv) + return b64.EncodeToString(marshPub), b64.EncodeToString(marshPv) } diff --git a/config/ckclient.json b/config/ckclient.json index 3927f69..05e944b 100644 --- a/config/ckclient.json +++ b/config/ckclient.json @@ -1,7 +1,7 @@ { "ProxyMethod":"shadowsocks", "EncryptionMethod":"plain", - "UID":"iGAO85zysIyR4c09CyZSLdNhtP/ckcYu7nIPI082AHA=", + "UID":"5nneblJy6lniPJfr81LuYQ==", "PublicKey":"IYoUzkle/T/kriE+Ufdm7AHQtIeGnBWbhhlTbmDpUUI=", "ServerName":"www.bing.com", "TicketTimeHint":3600, diff --git a/config/ckserver.json b/config/ckserver.json index 8637a76..d8e056c 100644 --- a/config/ckserver.json +++ b/config/ckserver.json @@ -6,7 +6,7 @@ }, "RedirAddr":"204.79.197.200:443", "PrivateKey":"EN5aPEpNBO+vw+BtFQY2OnK9bQU7rvEj5qmnmgwEtUc=", - "AdminUID":"ugDmcEmxWf0pKxfkZ/8EoP35Ht+wQnqf3L0xYgyQFlQ=", + "AdminUID":"T6L7MKPOCTQDG7ACFIXJOT25OQ", "DatabasePath":"userinfo.db", "BackupDirPath":"" } diff --git a/internal/client/auth.go b/internal/client/auth.go index 50d9ddd..7850d51 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, sessionKey 32 bytes][16 bytes authentication tag][padding 59 bytes] + // sessionTicket: [marshalled ephemeral pub key 32 bytes][encrypted UID+sessionID 20 bytes, proxy method 16 bytes, encryption method 1 byte, sessionKey 32 bytes][16 bytes authentication tag][padding 75 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,15 +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, 85) + plain := make([]byte, 69) 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) + binary.BigEndian.PutUint32(plain[16:20], sta.sessionID) + copy(plain[20:36], []byte(sta.ProxyMethod)) + plain[36] = sta.EncryptionMethod + copy(plain[37:69], sta.SessionKey) cipher, _ := util.AESGCMEncrypt(ticket[0:12], key, plain) - copy(ticket[32:133], cipher) + copy(ticket[32:117], 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 @@ -72,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[133:192], util.PsudoRandBytes(59, tthInterval+int64(sta.sessionID))) + copy(ticket[117:192], util.PsudoRandBytes(75, tthInterval+int64(sta.sessionID))) return ticket } diff --git a/internal/server/auth.go b/internal/server/auth.go index 8234bb4..2bed132 100644 --- a/internal/server/auth.go +++ b/internal/server/auth.go @@ -15,12 +15,12 @@ import ( 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:133]) + plain, err := util.AESGCMDecrypt(ticket[0:12], key, ticket[32:117]) if err != nil { 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], plain[53:85] + sessionID := binary.BigEndian.Uint32(plain[16:20]) + return plain[0:16], sessionID, string(bytes.Trim(plain[20:36], "\x00")), plain[36], plain[37:69] } func validateRandom(random []byte, UID []byte, time int64) bool { @@ -54,7 +54,7 @@ func TouchStone(ch *ClientHello, sta *State) (isCK bool, UID []byte, sessionID u return } UID, sessionID, proxyMethod, encryptionMethod, sessionKey = decryptSessionTicket(sta.staticPv, ticket) - if len(UID) < 32 { + if len(UID) < 16 { return } isCK = validateRandom(ch.random, UID, sta.Now().Unix()) diff --git a/internal/server/usermanager/controller.go b/internal/server/usermanager/controller.go index 7a611ef..f62e82e 100644 --- a/internal/server/usermanager/controller.go +++ b/internal/server/usermanager/controller.go @@ -102,60 +102,60 @@ func (c *controller) HandleRequest(req []byte) (resp []byte, err error) { err = c.syncMemFromDB(arg) resp = check(err) case 7: - if len(arg) < 36 { + if len(arg) < 20 { resp = c.respond([]byte(errInvalidArgument.Error())) break } - err = c.setSessionsCap(arg[0:32], Uint32(arg[32:36])) + err = c.setSessionsCap(arg[0:16], Uint32(arg[16:20])) resp = check(err) case 8: - if len(arg) < 40 { + if len(arg) < 24 { resp = c.respond([]byte(errInvalidArgument.Error())) break } - err = c.setUpRate(arg[0:32], int64(Uint64(arg[32:40]))) + err = c.setUpRate(arg[0:16], int64(Uint64(arg[16:24]))) resp = check(err) case 9: - if len(arg) < 40 { + if len(arg) < 24 { resp = c.respond([]byte(errInvalidArgument.Error())) break } - err = c.setDownRate(arg[0:32], int64(Uint64(arg[32:40]))) + err = c.setDownRate(arg[0:16], int64(Uint64(arg[16:24]))) resp = check(err) case 10: - if len(arg) < 40 { + if len(arg) < 24 { resp = c.respond([]byte(errInvalidArgument.Error())) break } - err = c.setUpCredit(arg[0:32], int64(Uint64(arg[32:40]))) + err = c.setUpCredit(arg[0:16], int64(Uint64(arg[16:24]))) resp = check(err) case 11: - if len(arg) < 40 { + if len(arg) < 24 { resp = c.respond([]byte(errInvalidArgument.Error())) break } - err = c.setDownCredit(arg[0:32], int64(Uint64(arg[32:40]))) + err = c.setDownCredit(arg[0:16], int64(Uint64(arg[16:24]))) resp = check(err) case 12: - if len(arg) < 40 { + if len(arg) < 24 { resp = c.respond([]byte(errInvalidArgument.Error())) break } - err = c.setExpiryTime(arg[0:32], int64(Uint64(arg[32:40]))) + err = c.setExpiryTime(arg[0:16], int64(Uint64(arg[16:24]))) resp = check(err) case 13: - if len(arg) < 40 { + if len(arg) < 24 { resp = c.respond([]byte(errInvalidArgument.Error())) break } - err = c.addUpCredit(arg[0:32], int64(Uint64(arg[32:40]))) + err = c.addUpCredit(arg[0:16], int64(Uint64(arg[16:24]))) resp = check(err) case 14: - if len(arg) < 40 { + if len(arg) < 24 { resp = c.respond([]byte(errInvalidArgument.Error())) break } - err = c.addDownCredit(arg[0:32], int64(Uint64(arg[32:40]))) + err = c.addDownCredit(arg[0:16], int64(Uint64(arg[16:24]))) resp = check(err) default: return c.respond([]byte("Unsupported action")), nil @@ -180,11 +180,11 @@ func (c *controller) respond(resp []byte) []byte { rand.Read(buf[5:21]) //iv copy(buf[21:], resp) - block, _ := aes.NewCipher(c.adminUID[0:16]) + block, _ := aes.NewCipher(c.adminUID) stream := cipher.NewCTR(block, buf[5:21]) stream.XORKeyStream(buf[21:21+respLen], buf[21:21+respLen]) - mac := hmac.New(sha256.New, c.adminUID[16:32]) + mac := hmac.New(sha256.New, c.adminUID) mac.Write(buf[5 : 21+respLen]) copy(buf[21+respLen:], mac.Sum(nil)) @@ -196,7 +196,7 @@ func (c *controller) checkAndDecrypt(data []byte) ([]byte, error) { return nil, errMsgTooShort } macIndex := len(data) - 32 - mac := hmac.New(sha256.New, c.adminUID[16:32]) + mac := hmac.New(sha256.New, c.adminUID) mac.Write(data[5:macIndex]) expected := mac.Sum(nil) if !hmac.Equal(data[macIndex:], expected) { @@ -205,7 +205,7 @@ func (c *controller) checkAndDecrypt(data []byte) ([]byte, error) { iv := data[5:21] ret := data[21:macIndex] - block, _ := aes.NewCipher(c.adminUID[0:16]) + block, _ := aes.NewCipher(c.adminUID) stream := cipher.NewCTR(block, iv) stream.XORKeyStream(ret, ret) return ret, nil diff --git a/internal/server/usermanager/user.go b/internal/server/usermanager/user.go index 813b53a..e4bc020 100644 --- a/internal/server/usermanager/user.go +++ b/internal/server/usermanager/user.go @@ -25,7 +25,7 @@ type UserInfo struct { type User struct { up *Userpanel - arrUID [32]byte + arrUID [16]byte *UserInfo diff --git a/internal/server/usermanager/userpanel.go b/internal/server/usermanager/userpanel.go index d03c2bf..64e2005 100644 --- a/internal/server/usermanager/userpanel.go +++ b/internal/server/usermanager/userpanel.go @@ -24,7 +24,7 @@ type Userpanel struct { bakRoot string activeUsersM sync.RWMutex - activeUsers map[[32]byte]*User + activeUsers map[[16]byte]*User } func MakeUserpanel(dbPath, bakRoot string) (*Userpanel, error) { @@ -40,7 +40,7 @@ func MakeUserpanel(dbPath, bakRoot string) (*Userpanel, error) { up := &Userpanel{ db: db, bakRoot: bakRoot, - activeUsers: make(map[[32]byte]*User), + activeUsers: make(map[[16]byte]*User), } go func() { for { @@ -102,7 +102,7 @@ var ErrUserNotActive = errors.New("User is not active") func (up *Userpanel) GetAndActivateAdminUser(AdminUID []byte) (*User, error) { up.activeUsersM.Lock() - var arrUID [32]byte + var arrUID [16]byte copy(arrUID[:], AdminUID) if user, ok := up.activeUsers[arrUID]; ok { up.activeUsersM.Unlock() @@ -129,7 +129,7 @@ func (up *Userpanel) GetAndActivateAdminUser(AdminUID []byte) (*User, error) { // from the db and mark it as an active user func (up *Userpanel) GetAndActivateUser(UID []byte) (*User, error) { up.activeUsersM.Lock() - var arrUID [32]byte + var arrUID [16]byte copy(arrUID[:], UID) if user, ok := up.activeUsers[arrUID]; ok { up.activeUsersM.Unlock() @@ -191,7 +191,7 @@ func (up *Userpanel) updateDBEntryInt64(UID []byte, key string, value int64) err // This is used when all sessions of a user close func (up *Userpanel) delActiveUser(UID []byte) { - var arrUID [32]byte + var arrUID [16]byte copy(arrUID[:], UID) up.activeUsersM.Lock() delete(up.activeUsers, arrUID) @@ -199,7 +199,7 @@ func (up *Userpanel) delActiveUser(UID []byte) { } func (up *Userpanel) getActiveUser(UID []byte) *User { - var arrUID [32]byte + var arrUID [16]byte copy(arrUID[:], UID) up.activeUsersM.RLock() ret := up.activeUsers[arrUID]