diff --git a/server/server.go b/server/server.go index 70ac609..b0063ba 100644 --- a/server/server.go +++ b/server/server.go @@ -9,6 +9,8 @@ import ( "github.com/s-rah/go-ricochet/channels" "github.com/s-rah/go-ricochet/utils" "log" + "os" + "io/ioutil" ) // Server encapsulates a complete, compliant Cwtch server. @@ -21,6 +23,16 @@ type Server struct { func (s *Server) Run(privateKeyFile string) { cwtchserver := new(application.RicochetApplication) + if _, err := os.Stat(privateKeyFile); os.IsNotExist(err) { + log.Printf("no private key found!") + log.Printf("generating new private key...") + pk, pk_err := utils.GeneratePrivateKey() + if pk_err != nil { + log.Fatalf("error generating new private key: %v", err) + } + ioutil.WriteFile(privateKeyFile, []byte(utils.PrivateKeyToString(pk)), 0400) + } + pk, err := utils.LoadPrivateKeyFromFile(privateKeyFile) if err != nil { diff --git a/testing/cwtch_peer_server_intergration_test.go b/testing/cwtch_peer_server_intergration_test.go index 697150d..9faae1d 100644 --- a/testing/cwtch_peer_server_intergration_test.go +++ b/testing/cwtch_peer_server_intergration_test.go @@ -1,22 +1,167 @@ package testing import ( + cwtchserver "git.mascherari.press/cwtch/server" + "github.com/s-rah/go-ricochet/utils" "git.mascherari.press/cwtch/peer" "testing" "time" + "log" + "git.mascherari.press/cwtch/protocol" + "github.com/golang/protobuf/proto" + "io/ioutil" + "fmt" + "git.mascherari.press/cwtch/model" + "os" + "github.com/s-rah/go-ricochet" ) -func TestCwtchPeerIntegration(t *testing.T) { - alice := peer.NewCwtchPeer("Alice") - id, _ := alice.Profile.StartGroup("ylhbhtypevo4ympq") - alice.Profile.AddContact(alice.Profile.Onion, alice.Profile.PublicProfile) - alice.JoinServer("ylhbhtypevo4ympq") - // time.Sleep(time.Second *5) - alice.SendMessageToGroup(id, "Hello") - alice.SendMessageToGroup(id, "My") - alice.SendMessageToGroup(id, "Name Is") - alice.SendMessageToGroup(id, "ALICE!!!") - time.Sleep(time.Second * 5) - group := alice.Profile.Groups[id] - t.Logf("%v", group.Timeline) +const ( + keyfile = "./private_key" +) + +var ( + aliceLines = []string {"Hello", "My name is Alice", "bye"} + bobLines = []string {"Hi", "My name is Bob.", "toodles", "hello?"} + +) + +func checkAndGenPrivateKey(privateKeyFile string) (generated bool){ + if _, err := os.Stat(privateKeyFile); os.IsNotExist(err) { + fmt.Println("generating new private key...") + pk, pk_err := utils.GeneratePrivateKey() + if pk_err != nil { + log.Fatalf("error generating new private key: %v\n", err) + } + err := ioutil.WriteFile(privateKeyFile, []byte(utils.PrivateKeyToString(pk)), 0400) + if err != nil { + log.Fatalf("error writing new private key to file %s: %v\n", privateKeyFile, err) + } + return true + } + return false +} + +func printAndVerifyTimeline(t *testing.T, timeline model.Timeline) error { + for _, message := range timeline.Messages { + fmt.Printf("%v %v> %s [%t]\n", message.Timestamp, message.PeerID, message.Message, message.Verified) + if !message.Verified { + t.Errorf("Message '%s' from '%s' not verified!", message.Message, message.PeerID) + } + } + return nil +} + +func serverCheck(serverAddr string) bool { + rc, err := goricochet.Open(serverAddr) + if err == nil { + rc.Conn.Close() + return true + } + return false +} + +func TestCwtchPeerIntegration(t *testing.T) { + // Hide logging "noise" + log.SetOutput(ioutil.Discard) + + generatedKey := checkAndGenPrivateKey(keyfile) + + serverKey, err := utils.LoadPrivateKeyFromFile(keyfile) + if err != nil { + t.Error("Could not load server's key from %v", keyfile) + } + serverAddr, _ := utils.GetOnionAddress(serverKey) + + serverOnline := false + if ! generatedKey { + fmt.Printf("Checking if test server %v is online...\n", serverAddr) + serverOnline = serverCheck(serverAddr) + } + + if ! serverOnline { + // launch app + server := new(cwtchserver.Server) + fmt.Printf("No server found\nStarting cwtch server...\n") + go server.Run(keyfile) + + // let tor get established + fmt.Printf("Establishing Tor hidden service: %v...\n", serverAddr) + time.Sleep(time.Second * 60) + } else { + fmt.Printf("Found existing cwtch server %v, using for tests...\n", serverAddr) + } + + // launch alice + alice := peer.NewCwtchPeer("Alice") + groupId, invite := alice.Profile.StartGroup(serverAddr) + gci := &protocol.CwtchPeerPacket{} + proto.Unmarshal(invite, gci) + alice.Profile.AddContact(alice.Profile.Onion, &alice.Profile.PublicProfile) + fmt.Println("Alice joining group...") + alice.JoinServer(serverAddr) + + // launch bob + bob := peer.NewCwtchPeer("Bob") + bob.Profile.ProcessInvite(gci.GetGroupChatInvite(), serverAddr) + bob.Profile.AddContact(bob.Profile.Onion, &bob.Profile.PublicProfile) + fmt.Println("Bob joining group...") + bob.JoinServer(serverAddr) + + // Associate peer alice & bob as peers + alice.Profile.AddContact(bob.Profile.Onion, &bob.Profile.PublicProfile) + bob.Profile.AddContact(alice.Profile.Onion, &alice.Profile.PublicProfile) + + // Conversation + fmt.Println("Alice> ", aliceLines[0]) + alice.SendMessageToGroup(groupId, aliceLines[0]) + time.Sleep(time.Second * 2) + + fmt.Println("Bob> ", bobLines[0]) + bob.SendMessageToGroup(groupId, bobLines[0]) + time.Sleep(time.Second * 2) + + fmt.Println("Alice> ", aliceLines[1]) + alice.SendMessageToGroup(groupId, aliceLines[1]) + fmt.Println("Bob> ", bobLines[1]) + bob.SendMessageToGroup(groupId, bobLines[1]) + time.Sleep(time.Second * 2) + + fmt.Println("Alice> ", aliceLines[2]) + alice.SendMessageToGroup(groupId, aliceLines[2]) + // Todo: Alice disconnects + time.Sleep(time.Second * 2) + + fmt.Println("Bob> ", bobLines[2]) + bob.SendMessageToGroup(groupId, bobLines[2]) + time.Sleep(time.Second * 2) + + + // Todo: Alice reconects, gets missed messages (from bob) + + // Verify + + // final syncing time... + time.Sleep(time.Second * 2) + + alicesGroup := alice.Profile.Groups[groupId] + fmt.Printf("Alice TimeLine:\n") + printAndVerifyTimeline(t, alicesGroup.Timeline) + + bobsGroup := bob.Profile.Groups[groupId] + fmt.Printf("Bob TimeLine:\n") + printAndVerifyTimeline(t, bobsGroup.Timeline) + + if len(alicesGroup.Timeline.Messages) != 6 { + t.Errorf("Alice's timeline does not have all messages") + return + } + + // check message 0,1 and 4,5 for content (2,3 could be out of order) + if alicesGroup.Timeline.Messages[0].Message != aliceLines[0] || alicesGroup.Timeline.Messages[1].Message != bobLines[0] || + alicesGroup.Timeline.Messages[4].Message != aliceLines[2] || alicesGroup.Timeline.Messages[5].Message != bobLines[2] { + t.Errorf("Some of the messages did not have the expected content!") + } + + // Todo: shutdown users and server } diff --git a/testing/private_key b/testing/private_key deleted file mode 100644 index 40b9757..0000000 --- a/testing/private_key +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXgIBAAKBgQC3xEJBH4oVFaotPJw6dezx67Gv4Xukw8CZRGqNFO8yF7Rejtcj -/0RTqqZwj6H6FjxY60dgYnN6IphW0juemNZhxOXeM/5Gb5xO+kWGi5Qt87aSDxnA -MDLgqw79ihuD3m1C1TBz0olmjXPU1VtadZuZcVBST7SLs2/k55GNNr7BoQIDAQAB -AoGBAK3ybVCdnSQWLM7DJ5LC23Wnx7sXceVlkiLCOyWuYjiFbatwBD/DupaD2yaD -HyzN7XOxyg93QZ2jr5XHTL30KEAn/3akNBsX3sjHZnjVfTwD5+oZKd7HYMMxekWf -87TIx2IHvGEo2NaFMLkEZ5TX3Gre8CYOofjFcpj4661ZfYp9AkEA9I0EmQX26ibs -CRGkwPuEj5q5N/PmIHgMWr1pepOlmzJjnxy6SI3NUwmzKrqM6YUM8loSywqfVMrJ -RVzA5jp76wJBAMBeu2hS8KcUTIu66j0pXMhI5wDA3yLiO53TEMwufCPXcaWUMH+e -5AIPL7aZ8ouf895OH0TZKxPNMnbrJ+5F0aMCQDoi/CDUxipMLnjJdP1bzdvF0Jp4 -pRC6+VTpCpZVW11V0VEWJ0LwUwuWlr1ls/If60ACIc2bLN2fh9Gxhzo0VRkCQQCS -nKCAVhYLgLEGHaLAknGgQ8+rB1QIphuBoYc/1n3OYzi+VT7RRSvJVgGrTZFJUNLw -LuIt+sWWBeHcOETqmFO5AkEAwwfcxs8QZtX6hCj2MTPi8Q28LIoA/M6eAqYc2I0B -eXxf2J2Qco7sMmBLr1Jp3jZNd5W2fMtlhUZAomOj4piVOA== ------END RSA PRIVATE KEY-----