Remove psudorandom padding

This commit is contained in:
Qian Wang 2019-07-31 23:58:15 +01:00
parent bb16f92b61
commit 0fbb6011fc
4 changed files with 25 additions and 29 deletions

View File

@ -100,7 +100,7 @@ func makeSession(sta *client.State) *mux.Session {
sta.UpdateIntervalKeys()
_, tthKey, _ := sta.GetIntervalKeys()
_, tthKey := sta.GetIntervalKeys()
sesh := mux.MakeSession(sta.SessionID, mux.UNLIMITED_VALVE, mux.MakeObfs(tthKey, sta.Cipher), mux.MakeDeobfs(tthKey, sta.Cipher), util.ReadTLS)
var wg sync.WaitGroup

View File

@ -29,34 +29,28 @@ func MakeRandomField(sta *State) []byte {
return ret
}
const SESSION_TICKET_LEN = 192
const PUB_KEY_LEN = 32
const AUTH_TAG_LEN = 16
const STEGANO_LEN = SESSION_TICKET_LEN - PUB_KEY_LEN - AUTH_TAG_LEN
func MakeSessionTicket(sta *State) []byte {
// 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]
// sessionTicket: [marshalled ephemeral pub key 32 bytes][encrypted UID 16 bytes, proxy method 16 bytes, encryption method 1 byte][reserved 111 bytes][16 bytes authentication tag]
// The first 12 bytes of the marshalled ephemeral public key is used as the nonce
// for encrypting the UID
ticket := make([]byte, 192)
ticket := make([]byte, SESSION_TICKET_LEN)
//TODO: error when the interval has expired
ephPub, intervalKey, seed := sta.GetIntervalKeys()
copy(ticket[0:32], ecdh.Marshal(ephPub))
ephPub, intervalKey := sta.GetIntervalKeys()
copy(ticket[0:PUB_KEY_LEN], ecdh.Marshal(ephPub))
plain := make([]byte, 33)
plain := make([]byte, STEGANO_LEN)
copy(plain, sta.UID)
copy(plain[16:32], []byte(sta.ProxyMethod))
copy(plain[16:32], sta.ProxyMethod)
plain[32] = sta.EncryptionMethod
cipher, _ := util.AESGCMEncrypt(ticket[0:12], intervalKey, plain)
copy(ticket[32:81], 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
// 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.
copy(ticket[81:192], util.PsudoRandBytes(111, seed))
copy(ticket[PUB_KEY_LEN:], cipher)
return ticket
}

View File

@ -4,7 +4,6 @@ import (
"crypto"
"crypto/rand"
"encoding/base64"
"encoding/binary"
"encoding/json"
"errors"
"io/ioutil"
@ -32,7 +31,6 @@ type tthIntervalKeys struct {
ephPv crypto.PrivateKey
ephPub crypto.PublicKey
intervalKey []byte
seed int64
}
// State stores global variables
@ -82,14 +80,13 @@ func (sta *State) UpdateIntervalKeys() {
sta.intervalData.interval = currentInterval
ephPv, ephPub, _ := ecdh.GenerateKey(rand.Reader)
intervalKey := ecdh.GenerateSharedSecret(ephPv, sta.staticPub)
seed := int64(binary.BigEndian.Uint64(ephPv.(*[32]byte)[0:8]))
sta.intervalData.ephPv, sta.intervalData.ephPub, sta.intervalData.intervalKey, sta.intervalData.seed = ephPv, ephPub, intervalKey, seed
sta.intervalData.ephPv, sta.intervalData.ephPub, sta.intervalData.intervalKey = ephPv, ephPub, intervalKey
}
func (sta *State) GetIntervalKeys() (crypto.PublicKey, []byte, int64) {
func (sta *State) GetIntervalKeys() (crypto.PublicKey, []byte) {
sta.intervalDataM.Lock()
defer sta.intervalDataM.Unlock()
return sta.intervalData.ephPub, sta.intervalData.intervalKey, sta.intervalData.seed
return sta.intervalData.ephPub, sta.intervalData.intervalKey
}
// semi-colon separated value. This is for Android plugin options

View File

@ -10,11 +10,16 @@ import (
"log"
)
const SESSION_TICKET_LEN = 192
const PUB_KEY_LEN = 32
const AUTH_TAG_LEN = 16
const USED_STAGNO_LEN = 16 + 16 + 1
func decryptSessionTicket(staticPv crypto.PrivateKey, ticket []byte) (UID []byte, proxyMethod string, encryptionMethod byte, tthKey []byte) {
// 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]
ephPub, _ := ecdh.Unmarshal(ticket[0:32])
// sessionTicket: [marshalled ephemeral pub key 32 bytes][encrypted UID 16 bytes, proxy method 16 bytes, encryption method 1 byte][reserved 111 bytes][padding 111 bytes]
ephPub, _ := ecdh.Unmarshal(ticket[0:PUB_KEY_LEN])
tthKey = ecdh.GenerateSharedSecret(staticPv, ephPub)
plain, err := util.AESGCMDecrypt(ticket[0:12], tthKey, ticket[32:81])
plain, err := util.AESGCMDecrypt(ticket[0:12], tthKey, ticket[PUB_KEY_LEN:])
if err != nil {
return
}
@ -51,7 +56,7 @@ func TouchStone(ch *ClientHello, sta *State) (isCK bool, UID []byte, sessionID u
ticket := ch.extensions[[2]byte{0x00, 0x23}]
if len(ticket) < 81 {
if len(ticket) < PUB_KEY_LEN+USED_STAGNO_LEN+AUTH_TAG_LEN {
return
}