mirror of https://github.com/cbeuw/Cloak
drop aes encryption of headers
This commit is contained in:
parent
bd69784443
commit
3f7eef98e3
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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() {
|
||||||
|
|
|
||||||
|
|
@ -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{
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
func AESDecrypt(iv []byte, key []byte, ciphertext []byte) []byte {
|
ret[j] = h.Sum32()
|
||||||
ret := make([]byte, len(ciphertext))
|
}
|
||||||
copy(ret, ciphertext) // Because XORKeyStream is inplace, but we don't want the input to be changed
|
return ret[0], ret[1], ret[2]
|
||||||
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,
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue