drop aes encryption of headers

pull/2/head
Qian Wang 6 years ago
parent bd69784443
commit 3f7eef98e3

@ -20,7 +20,9 @@ import (
var version string
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 {
i, err := io.ReadAtLeast(src, buf, 1)
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
func makeRemoteConn(sta *client.State) (net.Conn, error) {
// For android
d := net.Dialer{Control: protector}
clientHello := TLS.ComposeInitHandshake(sta)
@ -142,8 +145,8 @@ func main() {
log.Fatalf("Failed to establish connection to remote: %v\n", err)
}
obfs := util.MakeObfs(sta.SID[:16])
deobfs := util.MakeDeobfs(sta.SID[:16])
obfs := util.MakeObfs(sta.SID)
deobfs := util.MakeDeobfs(sta.SID)
// TODO: where to put obfs deobfs and rtd?
sesh := mux.MakeSession(0, initRemoteConn, obfs, deobfs, util.ReadTillDrain)

@ -21,7 +21,9 @@ import (
var version string
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 {
i, err := io.ReadAtLeast(src, buf, 1)
if err != nil || i == 0 {
@ -105,7 +107,7 @@ func dispatchConnection(conn net.Conn, sta *server.State) {
if sesh = sta.GetSession(arrSID); sesh != nil {
sesh.AddConnection(conn)
} 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)
}
go func() {

@ -42,7 +42,6 @@ type Session struct {
closeQCh chan uint32
}
// TODO: put this in main maybe?
// 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 {
sesh := &Session{

@ -12,6 +12,7 @@ const (
newConnBacklog = 8
)
// switchboard is responsible for keeping the reference of TLS connections between client and server
type switchboard struct {
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) {
buf := make([]byte, 20480)
for {

@ -45,7 +45,11 @@ func TouchStone(ch *ClientHello, sta *State) (bool, []byte) {
}
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 {
log.Printf("ts: %v\n", err)
return false, nil

@ -1,54 +1,44 @@
package util
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/binary"
"io"
xxhash "github.com/OneOfOne/xxhash"
mux "github.com/cbeuw/Cloak/internal/multiplex"
)
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
func genXorKeys(SID []byte, data []byte) (i uint32, ii uint32, iii uint32) {
h := xxhash.New32()
ret := make([]uint32, 3)
preHash := make([]byte, 16)
for j := 0; j < 3; j++ {
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 MakeObfs(key []byte) func(*mux.Frame) []byte {
obfs := func(f *mux.Frame) []byte {
header := 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)
obfsedHeader := make([]byte, 12)
// header: [StreamID 4 bytes][Seq 4 bytes][ClosingStreamID 4 bytes]
iv := make([]byte, 16)
io.ReadFull(rand.Reader, iv)
cipherheader := AESEncrypt(iv, key, header)
i, ii, iii := genXorKeys(key, f.Payload[0:18])
binary.BigEndian.PutUint32(obfsedHeader[0:4], f.StreamID^i)
binary.BigEndian.PutUint32(obfsedHeader[4:8], f.Seq^ii)
binary.BigEndian.PutUint32(obfsedHeader[8:12], f.ClosingStreamID^iii)
// Composing final obfsed message
// 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[1] = 0x03
obfsed[2] = 0x03
binary.BigEndian.PutUint16(obfsed[3:5], uint16(16+12+len(f.Payload)))
copy(obfsed[5:21], iv)
copy(obfsed[21:33], cipherheader)
copy(obfsed[33:], f.Payload)
// obfsed: [record layer 5 bytes][iv 16 bytes][cipherheader 12 bytes][payload]
binary.BigEndian.PutUint16(obfsed[3:5], uint16(12+len(f.Payload)))
copy(obfsed[5:17], obfsedHeader)
copy(obfsed[17:], f.Payload)
// obfsed: [record layer 5 bytes][cipherheader 12 bytes][payload]
return obfsed
}
return obfs
@ -57,13 +47,12 @@ func MakeObfs(key []byte) func(*mux.Frame) []byte {
func MakeDeobfs(key []byte) func([]byte) *mux.Frame {
deobfs := func(in []byte) *mux.Frame {
peeled := in[5:]
header := AESDecrypt(peeled[0:16], key, peeled[16:28])
streamID := binary.BigEndian.Uint32(header[0:4])
seq := binary.BigEndian.Uint32(header[4:8])
closingStreamID := binary.BigEndian.Uint32(header[8:12])
payload := make([]byte, len(peeled)-12-16)
//log.Printf("Payload: %x\n", payload)
copy(payload, peeled[28:])
i, ii, iii := genXorKeys(key, peeled[12:30])
streamID := binary.BigEndian.Uint32(peeled[0:4]) ^ i
seq := binary.BigEndian.Uint32(peeled[4:8]) ^ ii
closingStreamID := binary.BigEndian.Uint32(peeled[8:12]) ^ iii
payload := make([]byte, len(peeled)-12)
copy(payload, peeled[12:])
ret := &mux.Frame{
StreamID: streamID,
Seq: seq,

@ -1,6 +1,8 @@
package util
import (
"crypto/aes"
"crypto/cipher"
"encoding/binary"
"errors"
"io"
@ -10,6 +12,23 @@ import (
"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
// Uint methods from binary package can be used, but they are messy
func BtoInt(b []byte) int {

Loading…
Cancel
Save