2018-10-07 17:09:45 +00:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/rand"
|
|
|
|
"crypto/sha256"
|
|
|
|
"encoding/binary"
|
2019-01-25 00:24:47 +00:00
|
|
|
"github.com/cbeuw/Cloak/internal/ecdh"
|
|
|
|
"github.com/cbeuw/Cloak/internal/util"
|
2018-10-07 17:09:45 +00:00
|
|
|
)
|
|
|
|
|
2018-10-14 19:32:54 +00:00
|
|
|
func MakeRandomField(sta *State) []byte {
|
2019-07-26 16:05:46 +00:00
|
|
|
// [4 bytes sessionId] [12 bytes random] [16 bytes hash]
|
2018-10-07 17:09:45 +00:00
|
|
|
t := make([]byte, 8)
|
2018-10-14 19:32:54 +00:00
|
|
|
binary.BigEndian.PutUint64(t, uint64(sta.Now().Unix()/(12*60*60)))
|
2019-07-26 16:05:46 +00:00
|
|
|
|
|
|
|
front := make([]byte, 16)
|
|
|
|
binary.BigEndian.PutUint32(front[0:4], sta.SessionID)
|
|
|
|
rand.Read(front[4:])
|
2018-10-07 17:09:45 +00:00
|
|
|
preHash := make([]byte, 56)
|
2018-11-07 21:16:13 +00:00
|
|
|
copy(preHash[0:32], sta.UID)
|
2018-10-07 17:09:45 +00:00
|
|
|
copy(preHash[32:40], t)
|
2019-07-26 16:05:46 +00:00
|
|
|
copy(preHash[40:56], front)
|
2018-10-07 17:09:45 +00:00
|
|
|
h := sha256.New()
|
|
|
|
h.Write(preHash)
|
2019-07-26 16:05:46 +00:00
|
|
|
|
2018-10-07 17:09:45 +00:00
|
|
|
ret := make([]byte, 32)
|
2019-07-26 16:05:46 +00:00
|
|
|
copy(ret[0:16], front)
|
2018-10-07 17:09:45 +00:00
|
|
|
copy(ret[16:32], h.Sum(nil)[0:16])
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
func MakeSessionTicket(sta *State) []byte {
|
2019-07-26 16:05:46 +00:00
|
|
|
// sessionTicket: [marshalled ephemeral pub key 32 bytes][encrypted UID 16 bytes, proxy method 16 bytes, encryption method 1 byte][16 bytes authentication tag][padding 111 bytes]
|
2019-06-09 14:03:28 +00:00
|
|
|
// The first 12 bytes of the marshalled ephemeral public key is used as the nonce
|
2018-11-07 21:16:13 +00:00
|
|
|
// for encrypting the UID
|
2019-07-26 16:05:46 +00:00
|
|
|
|
2018-10-14 19:32:54 +00:00
|
|
|
ticket := make([]byte, 192)
|
2019-06-09 11:05:41 +00:00
|
|
|
|
2019-07-26 16:05:46 +00:00
|
|
|
//TODO: error when the interval has expired
|
|
|
|
ephPub, intervalKey, seed := sta.GetIntervalKeys()
|
|
|
|
copy(ticket[0:32], ecdh.Marshal(ephPub))
|
|
|
|
|
|
|
|
plain := make([]byte, 33)
|
2019-06-09 06:10:22 +00:00
|
|
|
copy(plain, sta.UID)
|
2019-07-26 16:05:46 +00:00
|
|
|
copy(plain[16:32], []byte(sta.ProxyMethod))
|
|
|
|
plain[32] = sta.EncryptionMethod
|
2019-06-09 11:05:41 +00:00
|
|
|
|
2019-07-26 16:05:46 +00:00
|
|
|
cipher, _ := util.AESGCMEncrypt(ticket[0:12], intervalKey, plain)
|
|
|
|
copy(ticket[32:81], cipher)
|
2018-11-07 21:16:13 +00:00
|
|
|
// 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
|
|
|
|
// is public knowledge, so is the psudo random algorithm used by math/rand. Therefore not only
|
|
|
|
// can the firewall tell that the padding is generated in this specific way, this padding is identical
|
|
|
|
// for all ckclients in the same TicketTimeHint interval. This will expose us.
|
|
|
|
//
|
|
|
|
// 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.
|
2019-07-26 16:05:46 +00:00
|
|
|
copy(ticket[81:192], util.PsudoRandBytes(111, seed))
|
2018-10-14 19:32:54 +00:00
|
|
|
return ticket
|
2018-10-07 17:09:45 +00:00
|
|
|
}
|