mirror of https://github.com/cbeuw/Cloak
Remove psudorandom padding
This commit is contained in:
parent
bb16f92b61
commit
0fbb6011fc
|
|
@ -100,7 +100,7 @@ func makeSession(sta *client.State) *mux.Session {
|
||||||
|
|
||||||
sta.UpdateIntervalKeys()
|
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)
|
sesh := mux.MakeSession(sta.SessionID, mux.UNLIMITED_VALVE, mux.MakeObfs(tthKey, sta.Cipher), mux.MakeDeobfs(tthKey, sta.Cipher), util.ReadTLS)
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
|
|
|
||||||
|
|
@ -29,34 +29,28 @@ func MakeRandomField(sta *State) []byte {
|
||||||
return ret
|
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 {
|
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
|
// The first 12 bytes of the marshalled ephemeral public key is used as the nonce
|
||||||
// for encrypting the UID
|
// for encrypting the UID
|
||||||
|
|
||||||
ticket := make([]byte, 192)
|
ticket := make([]byte, SESSION_TICKET_LEN)
|
||||||
|
|
||||||
//TODO: error when the interval has expired
|
//TODO: error when the interval has expired
|
||||||
ephPub, intervalKey, seed := sta.GetIntervalKeys()
|
ephPub, intervalKey := sta.GetIntervalKeys()
|
||||||
copy(ticket[0:32], ecdh.Marshal(ephPub))
|
copy(ticket[0:PUB_KEY_LEN], ecdh.Marshal(ephPub))
|
||||||
|
|
||||||
plain := make([]byte, 33)
|
plain := make([]byte, STEGANO_LEN)
|
||||||
copy(plain, sta.UID)
|
copy(plain, sta.UID)
|
||||||
copy(plain[16:32], []byte(sta.ProxyMethod))
|
copy(plain[16:32], sta.ProxyMethod)
|
||||||
plain[32] = sta.EncryptionMethod
|
plain[32] = sta.EncryptionMethod
|
||||||
|
|
||||||
cipher, _ := util.AESGCMEncrypt(ticket[0:12], intervalKey, plain)
|
cipher, _ := util.AESGCMEncrypt(ticket[0:12], intervalKey, plain)
|
||||||
copy(ticket[32:81], cipher)
|
copy(ticket[PUB_KEY_LEN:], 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))
|
|
||||||
return ticket
|
return ticket
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/binary"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
@ -32,7 +31,6 @@ type tthIntervalKeys struct {
|
||||||
ephPv crypto.PrivateKey
|
ephPv crypto.PrivateKey
|
||||||
ephPub crypto.PublicKey
|
ephPub crypto.PublicKey
|
||||||
intervalKey []byte
|
intervalKey []byte
|
||||||
seed int64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// State stores global variables
|
// State stores global variables
|
||||||
|
|
@ -82,14 +80,13 @@ func (sta *State) UpdateIntervalKeys() {
|
||||||
sta.intervalData.interval = currentInterval
|
sta.intervalData.interval = currentInterval
|
||||||
ephPv, ephPub, _ := ecdh.GenerateKey(rand.Reader)
|
ephPv, ephPub, _ := ecdh.GenerateKey(rand.Reader)
|
||||||
intervalKey := ecdh.GenerateSharedSecret(ephPv, sta.staticPub)
|
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 = ephPv, ephPub, intervalKey
|
||||||
sta.intervalData.ephPv, sta.intervalData.ephPub, sta.intervalData.intervalKey, sta.intervalData.seed = ephPv, ephPub, intervalKey, seed
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sta *State) GetIntervalKeys() (crypto.PublicKey, []byte, int64) {
|
func (sta *State) GetIntervalKeys() (crypto.PublicKey, []byte) {
|
||||||
sta.intervalDataM.Lock()
|
sta.intervalDataM.Lock()
|
||||||
defer sta.intervalDataM.Unlock()
|
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
|
// semi-colon separated value. This is for Android plugin options
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,16 @@ import (
|
||||||
"log"
|
"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) {
|
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]
|
// 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:32])
|
ephPub, _ := ecdh.Unmarshal(ticket[0:PUB_KEY_LEN])
|
||||||
tthKey = ecdh.GenerateSharedSecret(staticPv, ephPub)
|
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 {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -51,7 +56,7 @@ func TouchStone(ch *ClientHello, sta *State) (isCK bool, UID []byte, sessionID u
|
||||||
|
|
||||||
ticket := ch.extensions[[2]byte{0x00, 0x23}]
|
ticket := ch.extensions[[2]byte{0x00, 0x23}]
|
||||||
|
|
||||||
if len(ticket) < 81 {
|
if len(ticket) < PUB_KEY_LEN+USED_STAGNO_LEN+AUTH_TAG_LEN {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue