diff --git a/internal/client/auth_test.go b/internal/client/auth_test.go new file mode 100644 index 0000000..897351c --- /dev/null +++ b/internal/client/auth_test.go @@ -0,0 +1,102 @@ +package client + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "crypto/sha256" + "encoding/binary" + "encoding/hex" + "fmt" + ecdh "github.com/cbeuw/go-ecdh" + prand "math/rand" + "testing" + "time" +) + +func TestMakeSessionTicket(t *testing.T) { + UID, _ := hex.DecodeString("26a8e88bcd7c64a69ca051740851d22a6818de2fddafc00882331f1c5a8b866c") + ec := ecdh.NewCurve25519ECDH() + staticPv, staticPub, _ := ec.GenerateKey(rand.Reader) + mockSta := &State{ + Now: time.Now, + sessionID: 42, + UID: UID, + staticPub: staticPub, + keyPairs: make(map[int64]*keyPair), + TicketTimeHint: 3600, + } + + ticket := MakeSessionTicket(mockSta) + + // verification + ephPub, _ := ec.Unmarshal(ticket[0:32]) + key, _ := ec.GenerateSharedSecret(staticPv, ephPub) + + // aes decrypt + UIDsID := make([]byte, len(ticket[32:68])) + copy(UIDsID, ticket[32:68]) // Because XORKeyStream is inplace, but we don't want the input to be changed + block, _ := aes.NewCipher(key) + stream := cipher.NewCTR(block, ticket[0:16]) + stream.XORKeyStream(UIDsID, UIDsID) + + decryUID := UIDsID[0:32] + decrySessionID := binary.BigEndian.Uint32(UIDsID[32:36]) + + // check padding + tthInterval := mockSta.Now().Unix() / int64(mockSta.TicketTimeHint) + r := prand.New(prand.NewSource(tthInterval + int64(mockSta.sessionID))) + pad := make([]byte, 124) + r.Read(pad) + + if !bytes.Equal(mockSta.UID, decryUID) { + t.Error( + "For", "UID", + "expecting", fmt.Sprintf("%x", mockSta.UID), + "got", fmt.Sprintf("%x", decryUID), + ) + } + if mockSta.sessionID != decrySessionID { + t.Error( + "For", "sessionID", + "expecting", mockSta.sessionID, + "got", decrySessionID, + ) + } + if !bytes.Equal(pad, ticket[68:]) { + t.Error( + "For", "Padding", + "expecting", fmt.Sprintf("%x", pad), + "got", fmt.Sprintf("%x", ticket[68:]), + ) + } +} + +func TestMakeRandomField(t *testing.T) { + UID, _ := hex.DecodeString("26a8e88bcd7c64a69ca051740851d22a6818de2fddafc00882331f1c5a8b866c") + mockSta := &State{ + Now: time.Now, + UID: UID, + } + random := MakeRandomField(mockSta) + + // verification + tb := make([]byte, 8) + binary.BigEndian.PutUint64(tb, uint64(time.Now().Unix()/(12*60*60))) + rdm := random[0:16] + preHash := make([]byte, 56) + copy(preHash[0:32], UID) + copy(preHash[32:40], tb) + copy(preHash[40:56], rdm) + h := sha256.New() + h.Write(preHash) + exp := h.Sum(nil)[0:16] + if !bytes.Equal(exp, random[16:32]) { + t.Error( + "For", "Random", + "expecting", fmt.Sprintf("%x", exp), + "got", fmt.Sprintf("%x", random[16:32]), + ) + } +} diff --git a/internal/client/state_test.go b/internal/client/state_test.go new file mode 100644 index 0000000..85f32e5 --- /dev/null +++ b/internal/client/state_test.go @@ -0,0 +1,20 @@ +package client + +import ( + "bytes" + "testing" +) + +func TestSSVtoJson(t *testing.T) { + ssv := "UID=iGAO85zysIyR4c09CyZSLdNhtP/ckcYu7nIPI082AHA=;PublicKey=IYoUzkle/T/kriE+Ufdm7AHQtIeGnBWbhhlTbmDpUUI=;ServerName=www.bing.com;TicketTimeHint=3600;NumConn=4;MaskBrowser=chrome;" + json := ssvToJson(ssv) + expected := []byte(`{"UID":"iGAO85zysIyR4c09CyZSLdNhtP/ckcYu7nIPI082AHA=","PublicKey":"IYoUzkle/T/kriE+Ufdm7AHQtIeGnBWbhhlTbmDpUUI=","ServerName":"www.bing.com","TicketTimeHint":3600,"NumConn":4,"MaskBrowser":"chrome"}`) + if !bytes.Equal(expected, json) { + t.Error( + "For", "ssvToJson", + "expecting", string(expected), + "got", string(json), + ) + } + +} diff --git a/internal/multiplex/frameSorter_test.go b/internal/multiplex/frameSorter_test.go new file mode 100644 index 0000000..4a030f3 --- /dev/null +++ b/internal/multiplex/frameSorter_test.go @@ -0,0 +1,54 @@ +package multiplex + +import ( + "encoding/binary" + //"log" + "sort" + "testing" +) + +func TestRecvNewFrame(t *testing.T) { + inOrder := []uint64{5, 6, 7, 8, 9, 10, 11} + outOfOrder0 := []uint64{5, 7, 8, 6, 11, 10, 9} + outOfOrder1 := []uint64{1, 96, 47, 2, 29, 18, 60, 8, 74, 22, 82, 58, 44, 51, 57, 71, 90, 94, 68, 83, 61, 91, 39, 97, 85, 63, 46, 73, 54, 84, 76, 98, 93, 79, 75, 50, 67, 37, 92, 99, 42, 77, 17, 16, 38, 3, 100, 24, 31, 7, 36, 40, 86, 64, 34, 45, 12, 5, 9, 27, 21, 26, 35, 6, 65, 69, 53, 4, 48, 28, 30, 56, 32, 11, 80, 66, 25, 41, 78, 13, 88, 62, 15, 70, 49, 43, 72, 23, 10, 55, 52, 95, 14, 59, 87, 33, 19, 20, 81, 89} + outOfOrderWrap0 := []uint64{1<<32 - 5, 1<<32 + 3, 1 << 32, 1<<32 - 3, 1<<32 - 4, 1<<32 + 2, 1<<32 - 2, 1<<32 - 1, 1<<32 + 1} + sets := [][]uint64{inOrder, outOfOrder0, outOfOrder1, outOfOrderWrap0} + for _, set := range sets { + stream := makeStream(1, &Session{}) + stream.nextRecvSeq = uint32(set[0]) + for _, n := range set { + bu64 := make([]byte, 8) + binary.BigEndian.PutUint64(bu64, n) + frame := &Frame{ + Seq: uint32(n), + Payload: bu64, + } + stream.writeNewFrame(frame) + } + + var testSorted []uint32 + for x := 0; x < len(set); x++ { + p := <-stream.sortedBufCh + //log.Print(p) + testSorted = append(testSorted, uint32(binary.BigEndian.Uint64(p))) + } + sorted64 := make([]uint64, len(set)) + copy(sorted64, set) + sort.Slice(sorted64, func(i, j int) bool { return sorted64[i] < sorted64[j] }) + sorted32 := make([]uint32, len(set)) + for i, _ := range sorted64 { + sorted32[i] = uint32(sorted64[i]) + } + + for i, _ := range sorted32 { + if sorted32[i] != testSorted[i] { + t.Error( + "For", set, + "expecting", sorted32, + "got", testSorted, + ) + } + } + close(stream.die) + } +} diff --git a/internal/server/auth_test.go b/internal/server/auth_test.go new file mode 100644 index 0000000..a3cab68 --- /dev/null +++ b/internal/server/auth_test.go @@ -0,0 +1,69 @@ +package server + +import ( + "bytes" + "encoding/hex" + "fmt" + ecdh "github.com/cbeuw/go-ecdh" + "testing" +) + +var ec = ecdh.NewCurve25519ECDH() + +func TestDecryptSessionTicket(t *testing.T) { + UID, _ := hex.DecodeString("26a8e88bcd7c64a69ca051740851d22a6818de2fddafc00882331f1c5a8b866c") + sessionID := uint32(42) + pvb, _ := hex.DecodeString("083794692e77b28fa2152dfee53142185fd58ea8172d3545fdeeaea97b3c597c") + staticPv, _ := ec.Unmarshal(pvb) + sessionTicket, _ := hex.DecodeString("f586223b50cada583d61dc9bf3d01cc3a45aab4b062ed6a31ead0badb87f7761aab4f9f737a1d8ff2a2aa4d50ceb808844588ee3c8fdf36c33a35ef5003e287337659c8164a7949e9e63623090763fc24d0386c8904e47bdd740e09dd9b395c72de669629c2a865ed581452d23306adf26de0c8a46ee05e3dac876f2bcd9a2de946d319498f579383d06b3e66b3aca05f533fdc5f017eeba45b42080aabd4f71151fa0dfc1b0e23be4ed3abdb47adc0d5740ca7b7689ad34426309fb6984a086") + + decryUID, decrySessionID := decryptSessionTicket(staticPv, sessionTicket) + if !bytes.Equal(decryUID, UID) { + t.Error( + "For", "UID", + "expecting", fmt.Sprintf("%x", UID), + "got", fmt.Sprintf("%x", decryUID), + ) + } + if decrySessionID != sessionID { + t.Error( + "For", "sessionID", + "expecting", fmt.Sprintf("%x", sessionID), + "got", fmt.Sprintf("%x", decrySessionID), + ) + } + +} + +func TestValidateRandom(t *testing.T) { + UID, _ := hex.DecodeString("26a8e88bcd7c64a69ca051740851d22a6818de2fddafc00882331f1c5a8b866c") + random, _ := hex.DecodeString("6274de9992a6f96a86fc35cf6644a5e7844951889a802e9531add440eabb939b") + right := validateRandom(random, UID, 1547912444) + if !right { + t.Error( + "For", fmt.Sprintf("good random: %x at time %v", random, 1547912444), + "expecting", true, + "got", false, + ) + } + + replay := validateRandom(random, UID, 1547955645) + if replay { + t.Error( + "For", fmt.Sprintf("expired random: %x at time %v", random, 1547955645), + "expecting", false, + "got", true, + ) + } + + random[13] = 0x42 + bogus := validateRandom(random, UID, 1547912444) + if bogus { + t.Error( + "For", fmt.Sprintf("bogus random: %x at time %v", random, 1547912444), + "expecting", false, + "got", true, + ) + } + +} diff --git a/internal/server/state_test.go b/internal/server/state_test.go new file mode 100644 index 0000000..7f76cd6 --- /dev/null +++ b/internal/server/state_test.go @@ -0,0 +1,20 @@ +package server + +import ( + "bytes" + "testing" +) + +func TestSSVtoJson(t *testing.T) { + ssv := "WebServerAddr=204.79.197.200:443;PrivateKey=EN5aPEpNBO+vw+BtFQY2OnK9bQU7rvEj5qmnmgwEtUc=;AdminUID=ugDmcEmxWf0pKxfkZ/8EoP35Ht+wQnqf3L0xYgyQFlQ=;DatabasePath=userinfo.db;BackupDirPath=;" + json := ssvToJson(ssv) + expected := []byte(`{"WebServerAddr":"204.79.197.200:443","PrivateKey":"EN5aPEpNBO+vw+BtFQY2OnK9bQU7rvEj5qmnmgwEtUc=","AdminUID":"ugDmcEmxWf0pKxfkZ/8EoP35Ht+wQnqf3L0xYgyQFlQ=","DatabasePath":"userinfo.db","BackupDirPath":""}`) + if !bytes.Equal(expected, json) { + t.Error( + "For", "ssvToJson", + "expecting", string(expected), + "got", string(json), + ) + } + +}