Reduce the size of UID to 16 bytes

This commit is contained in:
Qian Wang 2019-06-16 11:08:51 +10:00
parent d781c7b1be
commit 710fa6835a
10 changed files with 72 additions and 72 deletions

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
{
"ProxyMethod":"shadowsocks",
"EncryptionMethod":"plain",
"UID":"iGAO85zysIyR4c09CyZSLdNhtP/ckcYu7nIPI082AHA=",
"UID":"5nneblJy6lniPJfr81LuYQ==",
"PublicKey":"IYoUzkle/T/kriE+Ufdm7AHQtIeGnBWbhhlTbmDpUUI=",
"ServerName":"www.bing.com",
"TicketTimeHint":3600,

View File

@ -6,7 +6,7 @@
},
"RedirAddr":"204.79.197.200:443",
"PrivateKey":"EN5aPEpNBO+vw+BtFQY2OnK9bQU7rvEj5qmnmgwEtUc=",
"AdminUID":"ugDmcEmxWf0pKxfkZ/8EoP35Ht+wQnqf3L0xYgyQFlQ=",
"AdminUID":"T6L7MKPOCTQDG7ACFIXJOT25OQ",
"DatabasePath":"userinfo.db",
"BackupDirPath":""
}

View File

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

View File

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

View File

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

View File

@ -25,7 +25,7 @@ type UserInfo struct {
type User struct {
up *Userpanel
arrUID [32]byte
arrUID [16]byte
*UserInfo

View File

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