diff --git a/internal/client/TLS_test.go b/internal/client/TLS_test.go new file mode 100644 index 0000000..9093e8a --- /dev/null +++ b/internal/client/TLS_test.go @@ -0,0 +1,43 @@ +package client + +import ( + "bytes" + "encoding/hex" + "testing" +) + +func htob(s string) []byte { + b, _ := hex.DecodeString(s) + return b +} + +func TestMakeServerName(t *testing.T) { + type testingPair struct { + serverName string + target []byte + } + + pairs := []testingPair{ + { + "www.google.com", + htob("001100000e7777772e676f6f676c652e636f6d"), + }, + { + "www.gstatic.com", + htob("001200000f7777772e677374617469632e636f6d"), + }, + { + "googleads.g.doubleclick.net", + htob("001e00001b676f6f676c656164732e672e646f75626c65636c69636b2e6e6574"), + }, + } + + for _, p := range pairs { + if !bytes.Equal(makeServerName(p.serverName), p.target) { + t.Error( + "for", p.serverName, + "expecting", p.target, + "got", makeServerName(p.serverName)) + } + } +} diff --git a/internal/client/auth_test.go b/internal/client/auth_test.go deleted file mode 100644 index d08b705..0000000 --- a/internal/client/auth_test.go +++ /dev/null @@ -1,90 +0,0 @@ -package client - -import ( - "bytes" - "encoding/gob" - //"crypto/aes" - //"crypto/cipher" - //"crypto/rand" - "crypto/sha256" - "encoding/binary" - "encoding/hex" - "fmt" - //prand "math/rand" - "testing" - "time" - //"github.com/cbeuw/Cloak/internal/ecdh" -) - -func TestMakeSessionTicket(t *testing.T) { - - stateGob, _ := hex.DecodeString("ffc5ff8103010105537461746501ff8200010c01094c6f63616c486f7374010c0001094c6f63616c506f7274010c00010a52656d6f7465486f7374010c00010a52656d6f7465506f7274010c00010953657373696f6e49440106000103554944010a00010b50726f78794d6574686f64010c000110456e6372797074696f6e4d6574686f64010600010e5469636b657454696d6548696e74010400010a5365727665724e616d65010c00010a42726f77736572536967010c0001074e756d436f6e6e010400000065ff8201093132372e302e302e3101043139383401093132372e302e302e31010334343301fc52fdfc0701104cd8cc15600d7eb68131fd8097673746010b736861646f77736f636b7302fe1c20010c7777772e62696e672e636f6d01066368726f6d65010800") - buf := bytes.NewBuffer(stateGob) - mockSta := &State{} - gob.NewDecoder(buf).Decode(mockSta) - mockSta.intervalData = &tthIntervalKeys{} - mockSta.intervalData.interval = 434487 - ephPub, _ := hex.DecodeString("7b7e0db16bb8c83355771a424234e36c02fd752b6a9310968d27787d7c117b10") - ephPv, _ := hex.DecodeString("68584fed8ede64e2b17619b9cc0effb2678feb2face92456a8414dafa629334b") - mockSta.intervalData.intervalKey, _ = hex.DecodeString("47e1c2413f1a6b397fbd61d6cf21397b20a2338cef48fae68643602881d93d4b") - mockSta.intervalData.seed = 7518847459617826018 - staticPub, _ := hex.DecodeString("218a14ce495efd3fe4ae213e51f766ec01d0b487869c159b8619536e60e95142") - - var a, b, c [32]byte - copy(a[:], ephPub) - copy(b[:], ephPv) - copy(c[:], staticPub) - mockSta.intervalData.ephPub = &a - mockSta.intervalData.ephPv = &b - mockSta.staticPub = &c - - target, _ := hex.DecodeString("7b7e0db16bb8c83355771a424234e36c02fd752b6a9310968d27787d7c117b103a2d246fd8b7d9e4243d6b83a7365858bd9cb583ba950287c4f4edc249cea935e235eda92c48569f455fca34ff6e4d37cf8f519b1d66e7cd51b31c1766ffb03134576e8d61ad5eae58a9ce4153ec33af73c7a8d04ab56d51155ac19fe731c792c17ee98a97fbfef8efc952964b7fd61dd2a35d7de4128abc730e20ba44e069e44ba8e29b66a8e4f114b9ab4cc2fba944a925086d7f09892a59147d990b58d393") - ticket := MakeSessionTicket(mockSta) - - if !bytes.Equal(target, ticket) { - t.Error( - "For", "sessionTicket generation", - "expecting", fmt.Sprintf("%x", target), - "got", fmt.Sprintf("%x", ticket), - ) - } - -} - -func TestMakeRandomField(t *testing.T) { - UID, _ := hex.DecodeString("4cd8cc15600d7eb68131fd8097673746") - mockSta := &State{ - Now: time.Now, - UID: UID, - SessionID: 1, - } - random := MakeRandomField(mockSta) - - // verification - tb := make([]byte, 8) - binary.BigEndian.PutUint64(tb, uint64(time.Now().Unix()/(12*60*60))) - front := random[0:16] - preHash := make([]byte, 56) - copy(preHash[0:32], UID) - copy(preHash[32:40], tb) - copy(preHash[40:56], front) - h := sha256.New() - h.Write(preHash) - exp := h.Sum(nil)[0:16] - if !bytes.Equal(exp, random[16:32]) { - t.Error( - "For", "Random generation", - "expecting", fmt.Sprintf("%x", exp), - "got", fmt.Sprintf("%x", random[16:32]), - ) - } - - random2 := MakeRandomField(mockSta) - if bytes.Equal(random, random2) { - t.Error( - "For", "Duplicate random generation", - "expecting", "two different randoms", - "got", fmt.Sprintf("the same: %x", random), - ) - } -} diff --git a/internal/client/firefox_test.go b/internal/client/firefox_test.go new file mode 100644 index 0000000..76ac80f --- /dev/null +++ b/internal/client/firefox_test.go @@ -0,0 +1,19 @@ +package client + +import ( + "bytes" + "encoding/hex" + "testing" +) + +func TestComposeExtensions(t *testing.T) { + target, _ := hex.DecodeString("000000170015000012636f6e73656e742e676f6f676c652e636f6d00170000ff01000100000a000e000c001d00170018001901000101000b00020100002300000010000e000c02683208687474702f312e310005000501000000000033006b0069001d00206075db0a43812b2e4e0f44157f04295b484ccfc6d70e577c1e6113aa18e088270017004104948052ae52043e654641660ebbadb527c8280262e61f64b0f6f1794f32e1000865a49e4cbe2027c78e7180861e4336300815fa0f1b0091c4d788b97f809a47d3002b0009080304030303020301000d0018001604030503060308040805080604010501060102030201002d00020101001c000240010015008c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000") + + serverName := "consent.google.com" + keyShare, _ := hex.DecodeString("6075db0a43812b2e4e0f44157f04295b484ccfc6d70e577c1e6113aa18e08827") + result := (&Firefox{}).composeExtensions(serverName, keyShare) + // skip random secp256r1 + if !bytes.Equal(result[:137], target[:137]) || !bytes.Equal(result[202:], target[202:]) { + t.Errorf("got %x", result) + } +} diff --git a/internal/multiplex/stream_test.go b/internal/multiplex/stream_test.go index 66ecdf8..014633b 100644 --- a/internal/multiplex/stream_test.go +++ b/internal/multiplex/stream_test.go @@ -2,19 +2,98 @@ package multiplex import ( "bufio" - "github.com/cbeuw/Cloak/internal/util" + "crypto/aes" + "crypto/cipher" + "encoding/binary" + "errors" + "golang.org/x/crypto/chacha20poly1305" + "io" "io/ioutil" "math/rand" "net" + "strconv" "testing" "time" ) +// ReadTLS reads TLS data according to its record layer +func ReadTLS(conn net.Conn, buffer []byte) (n int, err error) { + // TCP is a stream. Multiple TLS messages can arrive at the same time, + // a single message can also be segmented due to MTU of the IP layer. + // This function guareentees a single TLS message to be read and everything + // else is left in the buffer. + i, err := io.ReadFull(conn, buffer[:5]) + if err != nil { + return + } + + dataLength := int(binary.BigEndian.Uint16(buffer[3:5])) + if dataLength > len(buffer) { + err = errors.New("Reading TLS message: message size greater than buffer. message size: " + strconv.Itoa(dataLength)) + return + } + left := dataLength + readPtr := 5 + + for left != 0 { + // If left > buffer size (i.e. our message got segmented), the entire MTU is read + // if left = buffer size, the entire buffer is all there left to read + // if left < buffer size (i.e. multiple messages came together), + // only the message we want is read + i, err = io.ReadFull(conn, buffer[readPtr:readPtr+left]) + if err != nil { + return + } + left -= i + readPtr += i + } + + n = 5 + dataLength + return +} + +func GenerateObfs(encryptionMethod byte, sessionKey []byte) (obfuscator *Obfuscator, err error) { + var payloadCipher cipher.AEAD + switch encryptionMethod { + case 0x00: + payloadCipher = nil + case 0x01: + var c cipher.Block + c, err = aes.NewCipher(sessionKey) + if err != nil { + return + } + payloadCipher, err = cipher.NewGCM(c) + if err != nil { + return + } + case 0x02: + payloadCipher, err = chacha20poly1305.New(sessionKey) + if err != nil { + return + } + default: + return nil, errors.New("Unknown encryption method") + } + + headerCipher, err := aes.NewCipher(sessionKey) + if err != nil { + return + } + + obfuscator = &Obfuscator{ + MakeObfs(headerCipher, payloadCipher), + MakeDeobfs(headerCipher, payloadCipher), + sessionKey, + } + return +} + func setupSesh() *Session { sessionKey := make([]byte, 32) rand.Read(sessionKey) - obfuscator, _ := util.GenerateObfs(0x00, sessionKey) - return MakeSession(0, UNLIMITED_VALVE, obfuscator, util.ReadTLS) + obfuscator, _ := GenerateObfs(0x00, sessionKey) + return MakeSession(0, UNLIMITED_VALVE, obfuscator, ReadTLS) } type blackhole struct { diff --git a/internal/server/auth_test.go b/internal/server/auth_test.go deleted file mode 100644 index 3477ddd..0000000 --- a/internal/server/auth_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package server - -import ( - "bytes" - //"bytes" - "encoding/hex" - "fmt" - "github.com/cbeuw/Cloak/internal/ecdh" - "testing" - //"github.com/cbeuw/Cloak/internal/ecdh" -) - -func TestDecryptSessionTicket(t *testing.T) { - UID, _ := hex.DecodeString("4cd8cc15600d7eb68131fd8097673746") - pvb, _ := hex.DecodeString("10de5a3c4a4d04efafc3e06d1506363a72bd6d053baef123e6a9a79a0c04b547") - staticPv, _ := ecdh.Unmarshal(pvb) - proxyMethod := "shadowsocks" - encryptionMethod := byte(0) - tthKey, _ := hex.DecodeString("92389a9b2769e2b76514c4cb163217bed0c5500bceb4a5ade1ceae597616db23") - - sessionTicket, _ := hex.DecodeString("9ee339202508b6fbe9c19988575330c547efbc27b0d072ed93c0cc265b67d826825a49211b8f86b4364b436ed5db15925774c3bec4a1776f70a17db68ba541dc4c23871d2cc1a5074b081bbe0f8b86f1c7f7749964517dcfd8830532eddc8ac707544ec04b754a133b9595ebc2af988156dbe1e4f3b89c9dc289d441cb5a15d72cc59423981d43a498292d509e5fa5c8e8bf8ee85a2e4991ae126fcd6e4d2aa1119e918c80afa2dc38bec1ef621c9c3994af43b1983c241c68e04e8043c95d74") - - decryUID, decryProxyMethod, decryEncryptionMethod, decryTthKey := decryptSessionTicket(staticPv, sessionTicket) - - if !bytes.Equal(decryUID, UID) { - t.Error( - "For", "UID", - "expecting", fmt.Sprintf("%x", UID), - "got", fmt.Sprintf("%x", decryUID), - ) - } - if proxyMethod != decryProxyMethod { - t.Error( - "For", "proxyMethod", - "expecting", fmt.Sprintf("%x", proxyMethod), - "got", fmt.Sprintf("%x", decryProxyMethod), - ) - } - if encryptionMethod != decryEncryptionMethod { - t.Error( - "For", "encryptionMethod", - "expecting", fmt.Sprintf("%x", encryptionMethod), - "got", fmt.Sprintf("%x", decryEncryptionMethod), - ) - } - if !bytes.Equal(tthKey, decryTthKey) { - t.Error( - "For", "tthKey", - "expecting", fmt.Sprintf("%x", tthKey), - "got", fmt.Sprintf("%x", decryTthKey), - ) - } - -} - -func TestValidateRandom(t *testing.T) { - sessionID := uint32(2422026642) - random, _ := hex.DecodeString("905d319272711946f6400db4f5028d6893f7b22659c78371c1f72386191a8ab4") - UID, _ := hex.DecodeString("4cd8cc15600d7eb68131fd8097673746") - - right, decrySessionID := validateRandom(random, UID, 1564150721) - if !right { - t.Error( - "For", fmt.Sprintf("good random: %x at time %v", random, 1564150721), - "expecting", true, - "got", false, - ) - } - if sessionID != decrySessionID { - t.Error( - "For", fmt.Sprintf("good random: %x at time %v", random, 1564150721), - "expecting", sessionID, - "got", decrySessionID, - ) - } - - replay, _ := validateRandom(random, UID, 1764150721) - if replay { - t.Error( - "For", fmt.Sprintf("expired random: %x at time %v", random, 1764150721), - "expecting", false, - "got", true, - ) - } - - random[13] = 0x42 - bogus, _ := validateRandom(random, UID, 1564150721) - if bogus { - t.Error( - "For", fmt.Sprintf("bogus random: %x at time %v", random, 1564150721), - "expecting", false, - "got", true, - ) - } - -}