//////////////////////////////////////////////////////////////////////////////// // Copyright © 2022 xx foundation // // // // Use of this source code is governed by a license that can be found in the // // LICENSE file. // //////////////////////////////////////////////////////////////////////////////// package groupChat import ( "fmt" "github.com/cloudflare/circl/dh/sidh" "github.com/golang/protobuf/proto" sessionImport "gitlab.com/elixxir/client/e2e/ratchet/partner/session" util "gitlab.com/elixxir/client/storage/utility" "gitlab.com/elixxir/crypto/diffieHellman" "gitlab.com/xx_network/crypto/csprng" "gitlab.com/xx_network/primitives/id" "math/rand" "reflect" "sort" "strings" "testing" ) // Tests that manager.ResendRequest sends all expected requests successfully. func Test_manager_ResendRequest(t *testing.T) { prng := rand.New(rand.NewSource(42)) m, g := newTestManagerWithStore(prng, 10, 0, nil, t) expected := &Request{ Name: g.Name, IdPreimage: g.IdPreimage.Bytes(), KeyPreimage: g.KeyPreimage.Bytes(), Members: g.Members.Serialize(), Message: g.InitMessage, Created: g.Created.UnixNano(), } for i := range g.Members { grp := m.getE2eGroup() dhKey := grp.NewInt(int64(i + 42)) pubKey := diffieHellman.GeneratePublicKey(dhKey, grp) p := sessionImport.GetDefaultParams() rng := csprng.NewSystemRNG() _, mySidhPriv := util.GenerateSIDHKeyPair( sidh.KeyVariantSidhA, rng) theirSidhPub, _ := util.GenerateSIDHKeyPair( sidh.KeyVariantSidhB, rng) _, err := m.getE2eHandler().AddPartner(g.Members[i].ID, pubKey, dhKey, mySidhPriv, theirSidhPub, p, p) if err != nil { t.Errorf("Failed to add partner #%d %s: %+v", i, g.Members[i].ID, err) } } _, status, err := m.ResendRequest(g.ID) if err != nil { t.Errorf("ResendRequest() returned an error: %+v", err) } if status != AllSent { t.Errorf("ResendRequest() failed to return the expected status."+ "\nexpected: %s\nreceived: %s", AllSent, status) } if len(m.getE2eHandler().(*testE2eManager).e2eMessages) < len(g.Members)-1 { t.Errorf("ResendRequest() failed to send the correct number of requests."+ "\nexpected: %d\nreceived: %d", len(g.Members)-1, len(m.getE2eHandler().(*testE2eManager).e2eMessages)) } for i := 0; i < len(m.getE2eHandler().(*testE2eManager).e2eMessages); i++ { msg := m.getE2eHandler().(*testE2eManager).GetE2eMsg(i) // Check if the message recipient is a member in the group matchesMember := false for j, m := range g.Members { if msg.Recipient.Cmp(m.ID) { matchesMember = true g.Members = append(g.Members[:j], g.Members[j+1:]...) break } } if !matchesMember { t.Errorf("Message %d has recipient ID %s that is not in membership.", i, msg.Recipient) } testRequest := &Request{} err = proto.Unmarshal(msg.Payload, testRequest) if err != nil { t.Errorf("Failed to unmarshal proto message (%d): %+v", i, err) } if expected.String() != testRequest.String() { t.Errorf("Message %d has unexpected payload."+ "\nexpected: %s\nreceived: %s", i, expected, testRequest) } } } // Error path: an error is returned when no group with the corresponding group // ID exists. func Test_manager_ResendRequest_GetGroupError(t *testing.T) { prng := rand.New(rand.NewSource(42)) m, _ := newTestManagerWithStore(prng, 10, 0, nil, t) expectedErr := strings.SplitN(resendGroupIdErr, "%", 2)[0] _, status, err := m.ResendRequest(id.NewIdFromString("invalidID", id.Group, t)) if err == nil || !strings.Contains(err.Error(), expectedErr) { t.Errorf("ResendRequest() failed to return the expected error."+ "\nexpected: %s\nreceived: %+v", expectedErr, err) } if status != NotSent { t.Errorf("ResendRequest() failed to return the expected status."+ "\nexpected: %s\nreceived: %s", NotSent, status) } } // Tests that manager.sendRequests sends all expected requests successfully. func Test_manager_sendRequests(t *testing.T) { prng := rand.New(rand.NewSource(42)) m, g := newTestManagerWithStore(prng, 10, 0, nil, t) expected := &Request{ Name: g.Name, IdPreimage: g.IdPreimage.Bytes(), KeyPreimage: g.KeyPreimage.Bytes(), Members: g.Members.Serialize(), Message: g.InitMessage, Created: g.Created.UnixNano(), } for i := range g.Members { grp := m.getE2eGroup() dhKey := grp.NewInt(int64(i + 42)) pubKey := diffieHellman.GeneratePublicKey(dhKey, grp) p := sessionImport.GetDefaultParams() rng := csprng.NewSystemRNG() _, mySidhPriv := util.GenerateSIDHKeyPair( sidh.KeyVariantSidhA, rng) theirSidhPub, _ := util.GenerateSIDHKeyPair( sidh.KeyVariantSidhB, rng) _, err := m.getE2eHandler().AddPartner(g.Members[i].ID, pubKey, dhKey, mySidhPriv, theirSidhPub, p, p) if err != nil { t.Errorf("Failed to add partner #%d %s: %+v", i, g.Members[i].ID, err) } } _, status, err := m.sendRequests(g) if err != nil { t.Errorf("sendRequests() returned an error: %+v", err) } if status != AllSent { t.Errorf("sendRequests() failed to return the expected status."+ "\nexpected: %s\nreceived: %s", AllSent, status) } if len(m.getE2eHandler().(*testE2eManager).e2eMessages) < len(g.Members)-1 { t.Errorf("sendRequests() failed to send the correct number of requests."+ "\nexpected: %d\nreceived: %d", len(g.Members)-1, len(m.getE2eHandler().(*testE2eManager).e2eMessages)) } for i := 0; i < len(m.getE2eHandler().(*testE2eManager).e2eMessages); i++ { msg := m.getE2eHandler().(*testE2eManager).GetE2eMsg(i) // Check if the message recipient is a member in the group matchesMember := false for j, m := range g.Members { if msg.Recipient.Cmp(m.ID) { matchesMember = true g.Members = append(g.Members[:j], g.Members[j+1:]...) break } } if !matchesMember { t.Errorf("Message %d has recipient ID %s that is not in membership.", i, msg.Recipient) } testRequest := &Request{} err = proto.Unmarshal(msg.Payload, testRequest) if err != nil { t.Errorf("Failed to unmarshal proto message (%d): %+v", i, err) } if expected.String() != testRequest.String() { t.Errorf("Message %d has unexpected payload."+ "\nexpected: %s\nreceived: %s", i, expected, testRequest) } } } // Tests that manager.sendRequests returns the correct status when all sends // fail. func Test_manager_sendRequests_SendAllFail(t *testing.T) { prng := rand.New(rand.NewSource(42)) m, g := newTestManagerWithStore(prng, 10, 1, nil, t) expectedErr := fmt.Sprintf(sendRequestAllErr, len(g.Members)-1, "") rounds, status, err := m.sendRequests(g) if err == nil || !strings.Contains(err.Error(), expectedErr) { t.Errorf("sendRequests() failed to return the expected error."+ "\nexpected: %s\nreceived: %+v", expectedErr, err) } if status != AllFail { t.Errorf("sendRequests() failed to return the expected status."+ "\nexpected: %s\nreceived: %s", AllFail, status) } if rounds != nil { t.Errorf("sendRequests() returned rounds on failure."+ "\nexpected: %v\nreceived: %v", nil, rounds) } if len(m.getE2eHandler().(*testE2eManager).e2eMessages) != 0 { t.Errorf("sendRequests() sent %d messages when sending should have failed.", len(m.getE2eHandler().(*testE2eManager).e2eMessages)) } } // Tests that manager.sendRequests returns the correct status when some sends // fail. func Test_manager_sendRequests_SendPartialSent(t *testing.T) { prng := rand.New(rand.NewSource(42)) m, g := newTestManagerWithStore(prng, 10, 2, nil, t) expectedErr := fmt.Sprintf(sendRequestPartialErr, (len(g.Members)-1)/2, len(g.Members)-1, "") for i := range g.Members { grp := m.getE2eGroup() dhKey := grp.NewInt(int64(i + 42)) pubKey := diffieHellman.GeneratePublicKey(dhKey, grp) p := sessionImport.GetDefaultParams() rng := csprng.NewSystemRNG() _, mySidhPriv := util.GenerateSIDHKeyPair( sidh.KeyVariantSidhA, rng) theirSidhPub, _ := util.GenerateSIDHKeyPair( sidh.KeyVariantSidhB, rng) _, err := m.getE2eHandler().AddPartner(g.Members[i].ID, pubKey, dhKey, mySidhPriv, theirSidhPub, p, p) if err != nil { t.Errorf("Failed to add partner #%d %s: %+v", i, g.Members[i].ID, err) } } _, status, err := m.sendRequests(g) if err == nil || !strings.Contains(err.Error(), expectedErr) { t.Errorf("sendRequests() failed to return the expected error."+ "\nexpected: %s\nreceived: %+v", expectedErr, err) } if status != PartialSent { t.Errorf("sendRequests() failed to return the expected status."+ "\nexpected: %s\nreceived: %s", PartialSent, status) } if len(m.getE2eHandler().(*testE2eManager).e2eMessages) != (len(g.Members)-1)/2+1 { t.Errorf("sendRequests() sent %d out of %d expected messages.", len(m.getE2eHandler().(*testE2eManager).e2eMessages), (len(g.Members)-1)/2+1) } } // Unit test of manager.sendRequest. func Test_manager_sendRequest(t *testing.T) { prng := rand.New(rand.NewSource(42)) m, g := newTestManagerWithStore(prng, 10, 0, nil, t) for i := range g.Members { grp := m.getE2eGroup() dhKey := grp.NewInt(int64(i + 42)) pubKey := diffieHellman.GeneratePublicKey(dhKey, grp) p := sessionImport.GetDefaultParams() rng := csprng.NewSystemRNG() _, mySidhPriv := util.GenerateSIDHKeyPair( sidh.KeyVariantSidhA, rng) theirSidhPub, _ := util.GenerateSIDHKeyPair( sidh.KeyVariantSidhB, rng) _, err := m.getE2eHandler().AddPartner(g.Members[i].ID, pubKey, dhKey, mySidhPriv, theirSidhPub, p, p) if err != nil { t.Errorf("Failed to add partner #%d %s: %+v", i, g.Members[i].ID, err) } } _, err := m.sendRequest(g.Members[0].ID, []byte("request message")) if err != nil { t.Errorf("sendRequest() returned an error: %+v", err) } expected := testE2eMessage{ Recipient: g.Members[0].ID, Payload: []byte("request message"), } received := m.getE2eHandler().(*testE2eManager).GetE2eMsg(0) if !reflect.DeepEqual(expected, received) { t.Errorf("sendRequest() did not send the correct message."+ "\nexpected: %+v\nreceived: %+v", expected, received) } } // Error path: an error is returned when SendE2E fails func Test_manager_sendRequest_SendE2eError(t *testing.T) { prng := rand.New(rand.NewSource(42)) m, _ := newTestManagerWithStore(prng, 10, 1, nil, t) expectedErr := strings.SplitN(sendE2eErr, "%", 2)[0] recipientID := id.NewIdFromString("memberID", id.User, t) grp := m.getE2eGroup() dhKey := grp.NewInt(int64(42)) pubKey := diffieHellman.GeneratePublicKey(dhKey, grp) p := sessionImport.GetDefaultParams() rng := csprng.NewSystemRNG() _, mySidhPriv := util.GenerateSIDHKeyPair( sidh.KeyVariantSidhA, rng) theirSidhPub, _ := util.GenerateSIDHKeyPair( sidh.KeyVariantSidhB, rng) _, err := m.getE2eHandler().AddPartner(recipientID, pubKey, dhKey, mySidhPriv, theirSidhPub, p, p) if err != nil { t.Errorf("Failed to add partner %s: %+v", recipientID, err) } _, err = m.sendRequest(recipientID, nil) if err == nil || !strings.Contains(err.Error(), expectedErr) { t.Errorf("sendRequest() failed to return the expected error."+ "\nexpected: %s\nreceived: %+v", expectedErr, err) } } // Unit test of roundIdMap2List. func Test_roundIdMap2List(t *testing.T) { prng := rand.New(rand.NewSource(42)) // Construct map and expected list n := 100 expected := make([]id.Round, n) ridMap := make(map[id.Round]struct{}, n) for i := 0; i < n; i++ { expected[i] = id.Round(prng.Uint64()) ridMap[expected[i]] = struct{}{} } // Create list of IDs from map ridList := roundIdMap2List(ridMap) // Sort expected and received slices to see if they match sort.Slice(expected, func(i, j int) bool { return expected[i] < expected[j] }) sort.Slice(ridList, func(i, j int) bool { return ridList[i] < ridList[j] }) if !reflect.DeepEqual(expected, ridList) { t.Errorf("roundIdMap2List() failed to return the expected list."+ "\nexpected: %v\nreceived: %v", expected, ridList) } }