drop aes encryption of headers

This commit is contained in:
Qian Wang 2018-10-20 21:41:01 +01:00
parent bd69784443
commit 3f7eef98e3
7 changed files with 66 additions and 46 deletions

View File

@ -20,7 +20,9 @@ import (
var version string var version string
func pipe(dst io.ReadWriteCloser, src io.ReadWriteCloser) { func pipe(dst io.ReadWriteCloser, src io.ReadWriteCloser) {
buf := make([]byte, 20000) // The maximum size of TLS message will be 16396+12. 12 because of the stream header
// 16408 is the max TLS message size on Firefox
buf := make([]byte, 16396)
for { for {
i, err := io.ReadAtLeast(src, buf, 1) i, err := io.ReadAtLeast(src, buf, 1)
if err != nil || i == 0 { if err != nil || i == 0 {
@ -42,6 +44,7 @@ func pipe(dst io.ReadWriteCloser, src io.ReadWriteCloser) {
// This establishes a connection with ckserver and performs a handshake // This establishes a connection with ckserver and performs a handshake
func makeRemoteConn(sta *client.State) (net.Conn, error) { func makeRemoteConn(sta *client.State) (net.Conn, error) {
// For android
d := net.Dialer{Control: protector} d := net.Dialer{Control: protector}
clientHello := TLS.ComposeInitHandshake(sta) clientHello := TLS.ComposeInitHandshake(sta)
@ -142,8 +145,8 @@ func main() {
log.Fatalf("Failed to establish connection to remote: %v\n", err) log.Fatalf("Failed to establish connection to remote: %v\n", err)
} }
obfs := util.MakeObfs(sta.SID[:16]) obfs := util.MakeObfs(sta.SID)
deobfs := util.MakeDeobfs(sta.SID[:16]) deobfs := util.MakeDeobfs(sta.SID)
// TODO: where to put obfs deobfs and rtd? // TODO: where to put obfs deobfs and rtd?
sesh := mux.MakeSession(0, initRemoteConn, obfs, deobfs, util.ReadTillDrain) sesh := mux.MakeSession(0, initRemoteConn, obfs, deobfs, util.ReadTillDrain)

View File

@ -21,7 +21,9 @@ import (
var version string var version string
func pipe(dst io.ReadWriteCloser, src io.ReadWriteCloser) { func pipe(dst io.ReadWriteCloser, src io.ReadWriteCloser) {
buf := make([]byte, 20000) // The maximum size of TLS message will be 16396+12. 12 because of the stream header
// 16408 is the max TLS message size on Firefox
buf := make([]byte, 16396)
for { for {
i, err := io.ReadAtLeast(src, buf, 1) i, err := io.ReadAtLeast(src, buf, 1)
if err != nil || i == 0 { if err != nil || i == 0 {
@ -105,7 +107,7 @@ func dispatchConnection(conn net.Conn, sta *server.State) {
if sesh = sta.GetSession(arrSID); sesh != nil { if sesh = sta.GetSession(arrSID); sesh != nil {
sesh.AddConnection(conn) sesh.AddConnection(conn)
} else { } else {
sesh = mux.MakeSession(0, conn, util.MakeObfs(SID[:16]), util.MakeDeobfs(SID[:16]), util.ReadTillDrain) sesh = mux.MakeSession(0, conn, util.MakeObfs(SID), util.MakeDeobfs(SID), util.ReadTillDrain)
sta.PutSession(arrSID, sesh) sta.PutSession(arrSID, sesh)
} }
go func() { go func() {

View File

@ -42,7 +42,6 @@ type Session struct {
closeQCh chan uint32 closeQCh chan uint32
} }
// TODO: put this in main maybe?
// 1 conn is needed to make a session // 1 conn is needed to make a session
func MakeSession(id int, conn net.Conn, obfs func(*Frame) []byte, deobfs func([]byte) *Frame, obfsedReader func(net.Conn, []byte) (int, error)) *Session { func MakeSession(id int, conn net.Conn, obfs func(*Frame) []byte, deobfs func([]byte) *Frame, obfsedReader func(net.Conn, []byte) (int, error)) *Session {
sesh := &Session{ sesh := &Session{

View File

@ -12,6 +12,7 @@ const (
newConnBacklog = 8 newConnBacklog = 8
) )
// switchboard is responsible for keeping the reference of TLS connections between client and server
type switchboard struct { type switchboard struct {
session *Session session *Session
@ -130,6 +131,9 @@ func (sb *switchboard) dispatch() {
} }
} }
// deplex function costantly reads from a TLS connection
// it is responsible to act in response to the deobfsed header
// i.e. should a new stream be added? which existing stream should be closed?
func (sb *switchboard) deplex(ce *connEnclave) { func (sb *switchboard) deplex(ce *connEnclave) {
buf := make([]byte, 20480) buf := make([]byte, 20480)
for { for {

View File

@ -45,7 +45,11 @@ func TouchStone(ch *ClientHello, sta *State) (bool, []byte) {
} }
sta.putUsedRandom(random) sta.putUsedRandom(random)
SID, err := decryptSessionTicket(sta.staticPv, ch.extensions[[2]byte{0x00, 0x23}]) ticket := ch.extensions[[2]byte{0x00, 0x23}]
if len(ticket) < 64 {
return false, nil
}
SID, err := decryptSessionTicket(sta.staticPv, ticket)
if err != nil { if err != nil {
log.Printf("ts: %v\n", err) log.Printf("ts: %v\n", err)
return false, nil return false, nil

View File

@ -1,54 +1,44 @@
package util package util
import ( import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/binary" "encoding/binary"
"io"
xxhash "github.com/OneOfOne/xxhash"
mux "github.com/cbeuw/Cloak/internal/multiplex" mux "github.com/cbeuw/Cloak/internal/multiplex"
) )
func AESEncrypt(iv []byte, key []byte, plaintext []byte) []byte { func genXorKeys(SID []byte, data []byte) (i uint32, ii uint32, iii uint32) {
block, _ := aes.NewCipher(key) h := xxhash.New32()
ciphertext := make([]byte, len(plaintext)) ret := make([]uint32, 3)
stream := cipher.NewCTR(block, iv) preHash := make([]byte, 16)
stream.XORKeyStream(ciphertext, plaintext) for j := 0; j < 3; j++ {
return ciphertext copy(preHash[0:10], SID[j*10:j*10+10])
copy(preHash[10:16], data[j*6:j*6+6])
h.Write(preHash)
ret[j] = h.Sum32()
} }
return ret[0], ret[1], ret[2]
func AESDecrypt(iv []byte, key []byte, ciphertext []byte) []byte {
ret := make([]byte, len(ciphertext))
copy(ret, ciphertext) // Because XORKeyStream is inplace, but we don't want the input to be changed
block, _ := aes.NewCipher(key)
stream := cipher.NewCTR(block, iv)
stream.XORKeyStream(ret, ret)
return ret
} }
func MakeObfs(key []byte) func(*mux.Frame) []byte { func MakeObfs(key []byte) func(*mux.Frame) []byte {
obfs := func(f *mux.Frame) []byte { obfs := func(f *mux.Frame) []byte {
header := make([]byte, 12) obfsedHeader := make([]byte, 12)
binary.BigEndian.PutUint32(header[0:4], f.StreamID)
binary.BigEndian.PutUint32(header[4:8], f.Seq)
binary.BigEndian.PutUint32(header[8:12], f.ClosingStreamID)
// header: [StreamID 4 bytes][Seq 4 bytes][ClosingStreamID 4 bytes] // header: [StreamID 4 bytes][Seq 4 bytes][ClosingStreamID 4 bytes]
iv := make([]byte, 16) i, ii, iii := genXorKeys(key, f.Payload[0:18])
io.ReadFull(rand.Reader, iv) binary.BigEndian.PutUint32(obfsedHeader[0:4], f.StreamID^i)
cipherheader := AESEncrypt(iv, key, header) binary.BigEndian.PutUint32(obfsedHeader[4:8], f.Seq^ii)
binary.BigEndian.PutUint32(obfsedHeader[8:12], f.ClosingStreamID^iii)
// Composing final obfsed message // Composing final obfsed message
// We don't use util.AddRecordLayer here to avoid unnecessary malloc // We don't use util.AddRecordLayer here to avoid unnecessary malloc
obfsed := make([]byte, 5+16+12+len(f.Payload)) obfsed := make([]byte, 5+12+len(f.Payload))
obfsed[0] = 0x17 obfsed[0] = 0x17
obfsed[1] = 0x03 obfsed[1] = 0x03
obfsed[2] = 0x03 obfsed[2] = 0x03
binary.BigEndian.PutUint16(obfsed[3:5], uint16(16+12+len(f.Payload))) binary.BigEndian.PutUint16(obfsed[3:5], uint16(12+len(f.Payload)))
copy(obfsed[5:21], iv) copy(obfsed[5:17], obfsedHeader)
copy(obfsed[21:33], cipherheader) copy(obfsed[17:], f.Payload)
copy(obfsed[33:], f.Payload) // obfsed: [record layer 5 bytes][cipherheader 12 bytes][payload]
// obfsed: [record layer 5 bytes][iv 16 bytes][cipherheader 12 bytes][payload]
return obfsed return obfsed
} }
return obfs return obfs
@ -57,13 +47,12 @@ func MakeObfs(key []byte) func(*mux.Frame) []byte {
func MakeDeobfs(key []byte) func([]byte) *mux.Frame { func MakeDeobfs(key []byte) func([]byte) *mux.Frame {
deobfs := func(in []byte) *mux.Frame { deobfs := func(in []byte) *mux.Frame {
peeled := in[5:] peeled := in[5:]
header := AESDecrypt(peeled[0:16], key, peeled[16:28]) i, ii, iii := genXorKeys(key, peeled[12:30])
streamID := binary.BigEndian.Uint32(header[0:4]) streamID := binary.BigEndian.Uint32(peeled[0:4]) ^ i
seq := binary.BigEndian.Uint32(header[4:8]) seq := binary.BigEndian.Uint32(peeled[4:8]) ^ ii
closingStreamID := binary.BigEndian.Uint32(header[8:12]) closingStreamID := binary.BigEndian.Uint32(peeled[8:12]) ^ iii
payload := make([]byte, len(peeled)-12-16) payload := make([]byte, len(peeled)-12)
//log.Printf("Payload: %x\n", payload) copy(payload, peeled[12:])
copy(payload, peeled[28:])
ret := &mux.Frame{ ret := &mux.Frame{
StreamID: streamID, StreamID: streamID,
Seq: seq, Seq: seq,

View File

@ -1,6 +1,8 @@
package util package util
import ( import (
"crypto/aes"
"crypto/cipher"
"encoding/binary" "encoding/binary"
"errors" "errors"
"io" "io"
@ -10,6 +12,23 @@ import (
"time" "time"
) )
func AESEncrypt(iv []byte, key []byte, plaintext []byte) []byte {
block, _ := aes.NewCipher(key)
ciphertext := make([]byte, len(plaintext))
stream := cipher.NewCTR(block, iv)
stream.XORKeyStream(ciphertext, plaintext)
return ciphertext
}
func AESDecrypt(iv []byte, key []byte, ciphertext []byte) []byte {
ret := make([]byte, len(ciphertext))
copy(ret, ciphertext) // Because XORKeyStream is inplace, but we don't want the input to be changed
block, _ := aes.NewCipher(key)
stream := cipher.NewCTR(block, iv)
stream.XORKeyStream(ret, ret)
return ret
}
// BtoInt converts a byte slice into int in Big Endian order // BtoInt converts a byte slice into int in Big Endian order
// Uint methods from binary package can be used, but they are messy // Uint methods from binary package can be used, but they are messy
func BtoInt(b []byte) int { func BtoInt(b []byte) int {