diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 196fafd674d0b6763552739bfd87de96b23ccf3e..0e232306e01c4b38cea29bb638bcdd9f96243e7e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -76,12 +76,15 @@ tag: bindings-ios: stage: build + dependencies: [] except: - tags tags: - ios script: - - go get -u golang.org/x/mobile/cmd/gomobile + - export PATH=$PATH:$HOME/go/bin + - go install golang.org/x/mobile/cmd/gomobile@latest + - go get golang.org/x/mobile/cmd/gobind - gomobile init - gomobile bind -target ios gitlab.com/elixxir/client/bindings - ls @@ -92,14 +95,18 @@ bindings-ios: bindings-android: stage: build + dependencies: [] except: - tags tags: - android script: + - export PATH=$PATH:/usr/local/go/bin:$HOME/go/bin:/android-sdk/platform-tools/ - export ANDROID_HOME=/android-sdk - - export PATH=$PATH:/android-sdk/platform-tools/:/usr/local/go/bin - - go get -u golang.org/x/mobile/cmd/gomobile + + # Build the bindings + - go install golang.org/x/mobile/cmd/gomobile@latest + - go get golang.org/x/mobile/cmd/gobind - gomobile init - gomobile bind -target android -androidapi 21 gitlab.com/elixxir/client/bindings artifacts: diff --git a/README.md b/README.md index e494e57d83614256744b08ea2adf0af371103f4d..0d0938ebbe404dc1cefd87833031017d3d9c6564 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,9 @@ The client is open source software released under the simplified BSD License. The command line tool is intended for testing xx network functionality and not for regular user use. -Compilation (assuming golang 1.13 or newer): +These instructions assume that you have [Go 1.17.X installed](https://go.dev/doc/install), and GCC installed for Cgo (such as `build-essential` on Debian or Ubuntu). + +Compilation steps: ``` git clone https://gitlab.com/elixxir/client.git client @@ -160,7 +162,10 @@ Available Commands: Flags: --accept-channel Accept the channel request for the corresponding recipient ID --auth-timeout uint The number of seconds to wait for an authentication channelto confirm (default 120) + --delete-all-requests Delete the all contact requests, both sent and received. --delete-channel Delete the channel information for the corresponding recipient ID + --delete-receive-requests Delete the all received contact requests. + --delete-sent-requests Delete the all sent contact requests. --destfile string Read this contact file for the destination id -d, --destid string ID to send message to (if below 40, will be precanned. Use '0x' or 'b64:' for hex and base64 representations) (default "0") --e2eMaxKeys uint Max keys used before blocking until a rekey completes (default 800) diff --git a/api/authenticatedChannel.go b/api/authenticatedChannel.go index 84fd751178f710482c49c24df38449ef731f0779..93446db2b9bee15a5755f49d62ce42bcedf15794 100644 --- a/api/authenticatedChannel.go +++ b/api/authenticatedChannel.go @@ -158,6 +158,14 @@ func (c *Client) MakePrecannedAuthenticatedChannel(precannedID uint) (contact.Co Source: precan.ID[:], }, me) + // group request + c.storage.GetEdge().Add(edge.Preimage{ + Data: sessionPartner.GetGroupRequestPreimage(), + Type: preimage.GroupRq, + Source: precan.ID[:], + }, me) + + return precan, err } diff --git a/api/client.go b/api/client.go index c705187068226c528ac1ac24f1dfd28c49f1eb8f..a289fea1e030118346df4011ef5601e49438fe6d 100644 --- a/api/client.go +++ b/api/client.go @@ -304,7 +304,7 @@ func Login(storageDir string, password []byte, parameters params.Network) (*Clie } // initialize the auth tracker - c.auth = auth.NewManager(c.switchboard, c.storage, c.network) + c.auth = auth.NewManager(c.switchboard, c.storage, c.network, parameters.ReplayRequests) // Add all processes to the followerServices err = c.registerFollower() @@ -363,7 +363,7 @@ func LoginWithNewBaseNDF_UNSAFE(storageDir string, password []byte, } // initialize the auth tracker - c.auth = auth.NewManager(c.switchboard, c.storage, c.network) + c.auth = auth.NewManager(c.switchboard, c.storage, c.network, parameters.ReplayRequests) err = c.registerFollower() if err != nil { @@ -420,7 +420,7 @@ func LoginWithProtoClient(storageDir string, password []byte, protoClientJSON [] } // initialize the auth tracker - c.auth = auth.NewManager(c.switchboard, c.storage, c.network) + c.auth = auth.NewManager(c.switchboard, c.storage, c.network, parameters.ReplayRequests) err = c.registerFollower() if err != nil { @@ -684,6 +684,24 @@ func (c *Client) GetNodeRegistrationStatus() (int, int, error) { return numRegistered, len(nodes) - numStale, nil } +// DeleteAllRequests clears all requests from client's auth storage. +func (c *Client) DeleteAllRequests() error { + jww.DEBUG.Printf("Deleting all requests") + return c.GetStorage().Auth().DeleteAllRequests() +} + +// DeleteSentRequests clears sent requests from client's auth storage. +func (c *Client) DeleteSentRequests() error { + jww.DEBUG.Printf("Deleting all sent requests") + return c.GetStorage().Auth().DeleteSentRequests() +} + +// DeleteReceiveRequests clears receive requests from client's auth storage. +func (c *Client) DeleteReceiveRequests() error { + jww.DEBUG.Printf("Deleting all received requests") + return c.GetStorage().Auth().DeleteReceiveRequests() +} + // DeleteContact is a function which removes a partner from Client's storage func (c *Client) DeleteContact(partnerId *id.ID) error { jww.DEBUG.Printf("Deleting contact with ID %s", partnerId) @@ -696,6 +714,7 @@ func (c *Client) DeleteContact(partnerId *id.ID) error { e2ePreimage := partner.GetE2EPreimage() rekeyPreimage := partner.GetSilentPreimage() fileTransferPreimage := partner.GetFileTransferPreimage() + groupRequestPreimage := partner.GetGroupRequestPreimage() //delete the partner if err = c.storage.E2e().DeletePartner(partnerId); err != nil { @@ -729,13 +748,23 @@ func (c *Client) DeleteContact(partnerId *id.ID) error { "from %s on contact deletion: %+v", partnerId, err) } - if err = c.storage.Auth().Delete(partnerId); err != nil { - return err + if err = c.storage.GetEdge().Remove(edge.Preimage{ + Data: groupRequestPreimage, + Type: preimage.GroupRq, + Source: partnerId[:], + }, c.storage.GetUser().ReceptionID); err != nil { + jww.WARN.Printf("Failed delete the preimage for group request "+ + "from %s on contact deletion: %+v", partnerId, err) } //delete conversations c.storage.Conversations().Delete(partnerId) + // call delete requests to make sure nothing is lingering. + // this is for saftey to ensure the contact can be readded + // in the future + _ = c.storage.Auth().Delete(partnerId) + return nil } diff --git a/api/event.go b/api/event.go index 86e75b14ef212033ca906858e265c713022ee709..c5fcf03a90842095990568893051afea5a7e6cee 100644 --- a/api/event.go +++ b/api/event.go @@ -94,7 +94,7 @@ func (e *eventManager) reportEventsHandler(stop *stoppable.Single) { stop.ToStopped() return case evt := <-e.eventCh: - jww.DEBUG.Printf("Received event: %s", evt) + jww.TRACE.Printf("Received event: %s", evt) // NOTE: We could call each in a routine but decided // against it. It's the users responsibility not to let // the event queue explode. The API will report errors diff --git a/api/results.go b/api/results.go index 4a31b66b525e60037479ac4042017b21cfece313..04e91e15e7c6e3407ecfab759dd9eb1f598d9885 100644 --- a/api/results.go +++ b/api/results.go @@ -89,17 +89,11 @@ func (c *Client) getRoundResults(roundList []id.Round, timeout time.Duration, roundEvents := c.GetRoundEvents() roundsResults := make(map[id.Round]RoundResult) allRoundsSucceeded := true + anyRoundTimedOut := false numResults := 0 oldestRound := networkInstance.GetOldestRoundID() - // Set a lower timeout so there is room for retries, - // while ensuring it does not go too low and cause too many timeouts - roundEventTimeout := 5 * time.Second - if timeout < roundEventTimeout { - roundEventTimeout = timeout - } - // Parse and adjudicate every round for _, rnd := range roundList { // Every round is timed out by default, until proven to have finished @@ -116,7 +110,7 @@ func (c *Client) getRoundResults(roundList []id.Round, timeout time.Duration, } else { // If in progress, add a channel monitoring its state roundEvents.AddRoundEventChan(rnd, sendResults, - roundEventTimeout, states.COMPLETED, states.FAILED) + timeout-time.Millisecond, states.COMPLETED, states.FAILED) numResults++ } } else { @@ -129,7 +123,7 @@ func (c *Client) getRoundResults(roundList []id.Round, timeout time.Duration, } else { // Otherwise, monitor its progress roundEvents.AddRoundEventChan(rnd, sendResults, - roundEventTimeout, states.COMPLETED, states.FAILED) + timeout-time.Millisecond, states.COMPLETED, states.FAILED) numResults++ } } @@ -148,7 +142,7 @@ func (c *Client) getRoundResults(roundList []id.Round, timeout time.Duration, // If we know about all rounds, return if numResults == 0 { - roundCallback(allRoundsSucceeded, false, roundsResults) + roundCallback(allRoundsSucceeded, anyRoundTimedOut, roundsResults) return } @@ -158,29 +152,34 @@ func (c *Client) getRoundResults(roundList []id.Round, timeout time.Duration, roundCallback(false, true, roundsResults) return case roundReport := <-sendResults: + numResults-- + // Skip if the round is nil (unknown from historical rounds) // they default to timed out, so correct behavior is preserved - if roundReport.RoundInfo == nil { - allRoundsSucceeded = false - numResults-- - } else if roundReport.TimedOut { - // Generate a message to track the timed out round - timeoutRequest := &pb.HistoricalRounds{ - Rounds: []uint64{roundReport.RoundInfo.ID}, + roundId := roundReport.RoundInfo.GetRoundId() + if roundReport.TimedOut { + roundInfo, err := networkInstance.GetRound(roundId) + // If we have the round in the buffer + if err == nil { + // Check if the round is done (completed or failed) or in progress + if states.Round(roundInfo.State) == states.COMPLETED { + roundsResults[roundId] = Succeeded + } else if states.Round(roundInfo.State) == states.FAILED { + roundsResults[roundId] = Failed + allRoundsSucceeded = false + } + return } - // Request that round's information, feeding back into sendResults - jww.DEBUG.Printf("Sending HistoricalRounds retry for Round %d", roundReport.RoundInfo.ID) - go c.getHistoricalRounds(timeoutRequest, sendResults, commsInterface) + allRoundsSucceeded = false + anyRoundTimedOut = true } else { // If available, denote the result - roundId := id.Round(roundReport.RoundInfo.ID) if states.Round(roundReport.RoundInfo.State) == states.COMPLETED { roundsResults[roundId] = Succeeded } else { roundsResults[roundId] = Failed allRoundsSucceeded = false } - numResults-- } } } @@ -219,9 +218,17 @@ func (c *Client) getHistoricalRounds(msg *pb.HistoricalRounds, } // Service historical rounds, sending back to the caller thread - for _, ri := range resp.Rounds { - sendResults <- ds.EventReturn{ - RoundInfo: ri, + for i, ri := range resp.Rounds { + if ri == nil { + // Handle unknown by historical rounds + sendResults <- ds.EventReturn{ + RoundInfo: &pb.RoundInfo{ID: msg.Rounds[i]}, + TimedOut: true, + } + } else { + sendResults <- ds.EventReturn{ + RoundInfo: ri, + } } } } diff --git a/api/results_test.go b/api/results_test.go index 68e21e9aca1a317bada3ea1497c90166b1b4289c..c5245dd9a193d2f6c8bdb84e3f3862f1f884b1c2 100644 --- a/api/results_test.go +++ b/api/results_test.go @@ -40,7 +40,7 @@ func TestClient_GetRoundResults(t *testing.T) { // Create a new copy of the test client for this test client, err := newTestingClient(t) if err != nil { - t.Fatalf("Failed in setup: %v", err) + t.Fatalf("Failed in setup: %+v", err) } // Construct the round call back function signature @@ -171,7 +171,7 @@ func TestClient_GetRoundResults_HistoricalRounds(t *testing.T) { t.Errorf("Failed to sign round in set up: %v", err) } - err = client.network.GetInstance().RoundUpdate(ri) + _, err = client.network.GetInstance().RoundUpdate(ri) if err != nil { t.Errorf("Failed to upsert round in set up: %v", err) } diff --git a/api/utils.go b/api/utils.go index 96eb44f8834cb5b07cf020556aeb637caf2dd00a..2486d4d66143bb7fe6b660a9d23da26e70828652 100644 --- a/api/utils.go +++ b/api/utils.go @@ -20,7 +20,9 @@ const ( // Maximum input image size (in bytes) maxSize int64 = 12000000 // Desired number of pixels in output image - desiredSize = 307200 + desiredSize = 640*480 + // Desired number of pixels in output image for preview + desiredPreviewSize = 32*24 ) // CompressJpeg takes a JPEG image in byte format @@ -73,3 +75,55 @@ func CompressJpeg(imgBytes []byte) ([]byte, error) { // Return the compressed image in byte form return newImgBuf.Bytes(), nil } + + +// CompressJpeg takes a JPEG image in byte format +// and compresses it based on desired output size +func CompressJpegForPreview(imgBytes []byte) ([]byte, error) { + // Convert bytes to a reader + imgBuf := bytes.NewReader(imgBytes) + + // Ensure the size of the image is under the limit + if imgSize := imgBuf.Size(); imgSize > maxSize { + return nil, errors.Errorf("Image is too large: %d/%d", imgSize, maxSize) + } + + // Decode the image information + imgInfo, err := jpeg.DecodeConfig(imgBuf) + if err != nil { + return nil, errors.Errorf("Unable to decode image config: %+v", err) + } + + // If the dimensions of the image are below desiredSize, no compression is required + if imgInfo.Width*imgInfo.Height < desiredSize { + return imgBytes, nil + } + + // Reset the buffer to the beginning to begin decoding the image + _, err = imgBuf.Seek(0, 0) + if err != nil { + return nil, errors.Errorf("Unable to reset image buffer: %+v", err) + } + + // Decode image into image.Image object + img, err := jpeg.Decode(imgBuf) + if err != nil { + return nil, errors.Errorf("Unable to decode image: %+v", err) + } + + // Determine the new width of the image based on desiredSize + newWidth := uint(math.Sqrt(float64(desiredSize) * (float64(imgInfo.Width) / float64(imgInfo.Height)))) + + // Resize the image based on newWidth while preserving aspect ratio + newImg := resize.Resize(newWidth, 0, img, resize.Bicubic) + + // Encode the new image to a buffer + newImgBuf := new(bytes.Buffer) + err = jpeg.Encode(newImgBuf, newImg, nil) + if err != nil { + return nil, errors.Errorf("Unable to encode image: %+v", err) + } + + // Return the compressed image in byte form + return newImgBuf.Bytes(), nil +} diff --git a/api/utils_test.go b/api/utils_test.go index 973f15747b12741f0f83724a196a30b89800be1d..f1fea1358c4bd46a33c3a8f66d7cf56568485d67 100644 --- a/api/utils_test.go +++ b/api/utils_test.go @@ -68,7 +68,10 @@ func newTestingClient(face interface{}) (*Client, error) { p := gateway.DefaultPoolParams() p.MaxPoolSize = 1 - sender, _ := gateway.NewSender(p, c.rng, def, commsManager, c.storage, nil) + sender, err := gateway.NewSender(p, c.rng, def, commsManager, c.storage, nil) + if err != nil { + return nil, err + } c.network = &testNetworkManagerGeneric{instance: thisInstance, sender: sender} return c, nil @@ -85,6 +88,8 @@ func getNDF(face interface{}) *ndf.NetworkDefinition { cert, _ := utils.ReadFile(testkeys.GetNodeCertPath()) nodeID := id.NewIdFromBytes([]byte("gateway"), face) + gwId := nodeID.DeepCopy() + gwId.SetType(id.Gateway) return &ndf.NetworkDefinition{ Registration: ndf.Registration{ TlsCertificate: string(cert), @@ -100,7 +105,7 @@ func getNDF(face interface{}) *ndf.NetworkDefinition { }, Gateways: []ndf.Gateway{ { - ID: nodeID.Bytes(), + ID: gwId.Bytes(), Address: "", TlsCertificate: string(cert), }, diff --git a/api/version_vars.go b/api/version_vars.go index 8a44f54952836daac733cc3e151032f5e6b042cb..9ef72f196cc77927fd45ecad2a70d4a28d5ce7ae 100644 --- a/api/version_vars.go +++ b/api/version_vars.go @@ -1,43 +1,62 @@ // Code generated by go generate; DO NOT EDIT. // This file was generated by robots at -// 2022-01-04 12:45:01.875155 -0600 CST m=+0.041061278 +// 2022-02-15 12:19:19.667352 -0600 CST m=+0.033427370 package api -const GITVERSION = `1144194c Merge branch 'dev' into 'release'` +const GITVERSION = `d8832766 made splitSends default to false` const SEMVER = "4.0.0" const DEPENDENCIES = `module gitlab.com/elixxir/client -go 1.13 +go 1.17 require ( github.com/cloudflare/circl v1.0.1-0.20211008185751-59b49bc148ce github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 github.com/golang/protobuf v1.5.2 - github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect - github.com/magiconair/properties v1.8.4 // indirect - github.com/mitchellh/mapstructure v1.4.0 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 - github.com/pelletier/go-toml v1.8.1 // indirect github.com/pkg/errors v0.9.1 - github.com/smartystreets/assertions v1.0.1 // indirect - github.com/spf13/afero v1.5.1 // indirect - github.com/spf13/cast v1.3.1 // indirect github.com/spf13/cobra v1.1.1 github.com/spf13/jwalterweatherman v1.1.0 github.com/spf13/viper v1.7.1 gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228 - gitlab.com/elixxir/comms v0.0.4-0.20220104174855-044783c5c1e6 - gitlab.com/elixxir/crypto v0.0.7-0.20220104174238-dbd761b30553 - gitlab.com/elixxir/ekv v0.1.5 - gitlab.com/elixxir/primitives v0.0.3-0.20220104173924-275cb9d7834f - gitlab.com/xx_network/comms v0.0.4-0.20211227194445-c099754b3cda - gitlab.com/xx_network/crypto v0.0.5-0.20211227194420-f311e8920467 - gitlab.com/xx_network/primitives v0.0.4-0.20211222205802-03e9d7d835b0 - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 - golang.org/x/net v0.0.0-20210525063256-abc453219eb5 - google.golang.org/genproto v0.0.0-20210105202744-fe13368bc0e1 // indirect + gitlab.com/elixxir/comms v0.0.4-0.20220214214811-4a1bd320aa45 + gitlab.com/elixxir/crypto v0.0.7-0.20220211185439-4a6d9f41f8ab + gitlab.com/elixxir/ekv v0.1.6 + gitlab.com/elixxir/primitives v0.0.3-0.20220211185255-f9bc3df21e1d + gitlab.com/xx_network/comms v0.0.4-0.20220211184526-00dc9cfe8e2e + gitlab.com/xx_network/crypto v0.0.5-0.20220211184244-5803ecaafd59 + gitlab.com/xx_network/primitives v0.0.4-0.20220211183913-d6f5fd114a2a + golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed + golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 google.golang.org/grpc v1.42.0 google.golang.org/protobuf v1.27.1 +) + +require ( + github.com/badoux/checkmail v1.2.1 // indirect + github.com/elliotchance/orderedmap v1.4.0 // indirect + github.com/fsnotify/fsnotify v1.4.9 // indirect + github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/magiconair/properties v1.8.4 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.4.0 // indirect + github.com/pelletier/go-toml v1.8.1 // indirect + github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect + github.com/smartystreets/assertions v1.0.1 // indirect + github.com/spf13/afero v1.5.1 // indirect + github.com/spf13/cast v1.3.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.2.0 // indirect + github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 // indirect + github.com/ttacon/libphonenumber v1.2.1 // indirect + github.com/tyler-smith/go-bip39 v1.1.0 // indirect + github.com/zeebo/blake3 v0.1.1 // indirect + gitlab.com/xx_network/ring v0.0.3-0.20210527191221-ce3f170aabd5 // indirect + golang.org/x/sys v0.0.0-20210902050250-f475640dd07b // indirect + golang.org/x/text v0.3.6 // indirect + google.golang.org/genproto v0.0.0-20210105202744-fe13368bc0e1 // indirect gopkg.in/ini.v1 v1.62.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/auth/callback.go b/auth/callback.go index 3151fdb43de00b988d3356ea1833c9979c83cfba..7ae57d144bfb2a3c6e0b7b8cd02cb610d4694553 100644 --- a/auth/callback.go +++ b/auth/callback.go @@ -9,6 +9,8 @@ package auth import ( "fmt" + "strings" + "github.com/cloudflare/circl/dh/sidh" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" @@ -26,7 +28,6 @@ import ( "gitlab.com/elixxir/primitives/fact" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/crypto/csprng" - "strings" ) func (m *Manager) StartProcesses() (stoppable.Stoppable, error) { @@ -165,7 +166,7 @@ func (m *Manager) handleRequest(cmixMsg format.Message, return } else { //check if the relationship already exists, - rType, sr2, _, err := m.storage.Auth().GetRequest(partnerID) + rType, _, c, err := m.storage.Auth().GetRequest(partnerID) if err != nil && !strings.Contains(err.Error(), auth.NoRequest) { // if another error is received, print it and exit em := fmt.Sprintf("Received new Auth request for %s, "+ @@ -183,6 +184,15 @@ func (m *Manager) handleRequest(cmixMsg format.Message, "is a duplicate", partnerID) jww.WARN.Print(em) events.Report(5, "Auth", "DuplicateRequest", em) + // if the caller of the API wants requests replayed, + // replay the duplicate request + if m.replayRequests { + cbList := m.requestCallbacks.Get(c.ID) + for _, cb := range cbList { + rcb := cb.(interfaces.RequestCallback) + go rcb(c) + } + } return // if we sent a request, then automatically confirm // then exit, nothing else needed @@ -201,13 +211,9 @@ func (m *Manager) handleRequest(cmixMsg format.Message, } // Check if I need to resend by comparing the - // SIDH Keys - mySIDH := sr2.GetMySIDHPubKey() - theirSIDH := partnerSIDHPubKey - myBytes := make([]byte, mySIDH.Size()) - theirBytes := make([]byte, theirSIDH.Size()) - mySIDH.Export(myBytes) - theirSIDH.Export(theirBytes) + // IDs + myBytes := m.storage.GetUser().ReceptionID.Bytes() + theirBytes := partnerID.Bytes() for i := 0; i < len(myBytes); i++ { if myBytes[i] > theirBytes[i] { // OK, this side is dropping @@ -224,9 +230,29 @@ func (m *Manager) handleRequest(cmixMsg format.Message, } // If I do, delete my request on disk - _, _, partnerContact, _ := m.storage.Auth().GetRequest(partnerID) m.storage.Auth().Delete(partnerID) + //process the inner payload + facts, _, err := fact.UnstringifyFactList( + string(requestFmt.msgPayload)) + if err != nil { + em := fmt.Sprintf("failed to parse facts and message "+ + "from Auth Request: %s", err) + jww.WARN.Print(em) + events.Report(10, "Auth", "RequestError", em) + return + } + + // create the contact, note that we use the data + // sent in the request and not any data we had + // already + partnerContact := contact.Contact{ + ID: partnerID, + DhPubKey: partnerPubKey, + OwnershipProof: copySlice(ownership), + Facts: facts, + } + // add a confirmation to disk if err = m.storage.Auth().AddReceived(partnerContact, partnerSIDHPubKey); err != nil { @@ -250,6 +276,12 @@ func (m *Manager) handleRequest(cmixMsg format.Message, jww.INFO.Printf("ConfirmRequestAuth to %s on round %d", partnerID, rndNum) + c := partnerContact + cbList := m.confirmCallbacks.Get(c.ID) + for _, cb := range cbList { + ccb := cb.(interfaces.ConfirmCallback) + go ccb(c) + } return } } @@ -290,7 +322,7 @@ func (m *Manager) handleRequest(cmixMsg format.Message, cbList := m.requestCallbacks.Get(c.ID) for _, cb := range cbList { rcb := cb.(interfaces.RequestCallback) - go rcb(c, "") + go rcb(c) } return } @@ -434,6 +466,13 @@ func (m *Manager) doConfirm(sr *auth.SentRequest, grp *cyclic.Group, Source: sr.GetPartner()[:], }, me) + //group Request + m.storage.GetEdge().Add(edge.Preimage{ + Data: sessionPartner.GetGroupRequestPreimage(), + Type: preimage.GroupRq, + Source: sr.GetPartner()[:], + }, me) + // delete the in progress negotiation // this undoes the request lock if err := m.storage.Auth().Delete(sr.GetPartner()); err != nil { diff --git a/auth/confirm.go b/auth/confirm.go index 2a8169d21eb5be5f80ccbe5519aeb6a5a1e0d783..f0f7ba84975ae9f8e261c68014fabe8f347bc2d7 100644 --- a/auth/confirm.go +++ b/auth/confirm.go @@ -147,6 +147,13 @@ func ConfirmRequestAuth(partner contact.Contact, rng io.Reader, Source: partner.ID[:], }, me) + //group Request + storage.GetEdge().Add(edge.Preimage{ + Data: sessionPartner.GetGroupRequestPreimage(), + Type: preimage.GroupRq, + Source: partner.ID[:], + }, me) + // delete the in progress negotiation // this unlocks the request lock //fixme - do these deletes at a later date @@ -161,6 +168,7 @@ func ConfirmRequestAuth(partner contact.Contact, rng io.Reader, param := params.GetDefaultCMIX() param.IdentityPreimage = preimg + param.DebugTag = "auth.Confirm" /*send message*/ round, _, err := net.SendCMIX(cmixMsg, partner.ID, param) if err != nil { diff --git a/auth/manager.go b/auth/manager.go index b5803d69feb7ae67073e351b1c7fd24c3ac5b597..20b341c3891212edf308ec9c7afcf43a5f1588f5 100644 --- a/auth/manager.go +++ b/auth/manager.go @@ -23,16 +23,19 @@ type Manager struct { storage *storage.Session net interfaces.NetworkManager + + replayRequests bool } func NewManager(sw interfaces.Switchboard, storage *storage.Session, - net interfaces.NetworkManager) *Manager { + net interfaces.NetworkManager, replayRequests bool) *Manager { m := &Manager{ requestCallbacks: newCallbackMap(), confirmCallbacks: newCallbackMap(), rawMessages: make(chan message.Receive, 1000), storage: storage, net: net, + replayRequests: replayRequests, } sw.RegisterChannel("Auth", switchboard.AnyUser(), message.Raw, m.rawMessages) @@ -89,3 +92,17 @@ func (m *Manager) AddSpecificConfirmCallback(id *id.ID, cb interfaces.ConfirmCal func (m *Manager) RemoveSpecificConfirmCallback(id *id.ID) { m.confirmCallbacks.RemoveSpecific(id) } + +// ReplayRequests will iterate through all pending contact requests and resend them +// to the desired contact. +func (m *Manager) ReplayRequests() { + cList := m.storage.Auth().GetAllReceived() + for i := range cList { + c := cList[i] + cbList := m.requestCallbacks.Get(c.ID) + for _, cb := range cbList { + rcb := cb.(interfaces.RequestCallback) + go rcb(c) + } + } +} diff --git a/auth/manager_test.go b/auth/manager_test.go new file mode 100644 index 0000000000000000000000000000000000000000..7332a2db6d45ba7e2c0d9ddbfb18bc02d06ab286 --- /dev/null +++ b/auth/manager_test.go @@ -0,0 +1,105 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package auth + +import ( + "github.com/cloudflare/circl/dh/sidh" + "gitlab.com/elixxir/client/interfaces" + "gitlab.com/elixxir/client/storage" + "gitlab.com/elixxir/client/storage/auth" + util "gitlab.com/elixxir/client/storage/utility" + "gitlab.com/elixxir/client/storage/versioned" + "gitlab.com/elixxir/crypto/contact" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/ekv" + "gitlab.com/xx_network/crypto/csprng" + "gitlab.com/xx_network/crypto/large" + "gitlab.com/xx_network/primitives/id" + "io" + "math/rand" + "testing" + "time" +) + +func TestManager_ReplayRequests(t *testing.T) { + s := storage.InitTestingSession(t) + numReceived := 10 + + // Construct barebones manager + m := Manager{ + requestCallbacks: newCallbackMap(), + storage: s, + replayRequests: true, + } + + ch := make(chan struct{}, numReceived) + + // Add multiple received contact requests + for i := 0; i < numReceived; i++ { + c := contact.Contact{ID: id.NewIdFromUInt(rand.Uint64(), id.User, t)} + rng := csprng.NewSystemRNG() + _, sidhPubKey := genSidhAKeys(rng) + + if err := m.storage.Auth().AddReceived(c, sidhPubKey); err != nil { + t.Fatalf("AddReceived() returned an error: %+v", err) + } + + m.requestCallbacks.AddSpecific(c.ID, interfaces.RequestCallback(func(c contact.Contact) { + ch <- struct{}{} + })) + + } + + m.ReplayRequests() + + timeout := time.NewTimer(1 * time.Second) + numChannelReceived := 0 +loop: + for { + select { + case <-ch: + numChannelReceived++ + case <-timeout.C: + break loop + } + } + + if numReceived != numChannelReceived { + t.Errorf("Unexpected number of callbacks called"+ + "\nExpected: %d"+ + "\nReceived: %d", numChannelReceived, numReceived) + } +} + +func makeTestStore(t *testing.T) (*auth.Store, *versioned.KV, []*cyclic.Int) { + kv := versioned.NewKV(make(ekv.Memstore)) + grp := cyclic.NewGroup(large.NewInt(173), large.NewInt(0)) + privKeys := make([]*cyclic.Int, 10) + for i := range privKeys { + privKeys[i] = grp.NewInt(rand.Int63n(170) + 1) + } + + store, err := auth.NewStore(kv, grp, privKeys) + if err != nil { + t.Fatalf("Failed to create new Store: %+v", err) + } + + return store, kv, privKeys +} + +func genSidhAKeys(rng io.Reader) (*sidh.PrivateKey, *sidh.PublicKey) { + sidHPrivKeyA := util.NewSIDHPrivateKey(sidh.KeyVariantSidhA) + sidHPubKeyA := util.NewSIDHPublicKey(sidh.KeyVariantSidhA) + + if err := sidHPrivKeyA.Generate(rng); err != nil { + panic("failure to generate SidH A private key") + } + sidHPrivKeyA.GeneratePublicKey(sidHPubKeyA) + + return sidHPrivKeyA, sidHPubKeyA +} diff --git a/auth/request.go b/auth/request.go index 1f63d7e157caa9cb360d66f0fd3ec82c51ab7d69..7148cf5a4b0dc1d171da6eb62178cd08c60620b7 100644 --- a/auth/request.go +++ b/auth/request.go @@ -173,6 +173,7 @@ func RequestAuth(partner, me contact.Contact, rng io.Reader, /*send message*/ p := params.GetDefaultCMIX() p.IdentityPreimage = preimage.GenerateRequest(partner.ID) + p.DebugTag = "auth.Request" round, _, err := net.SendCMIX(cmixMsg, partner.ID, p) if err != nil { // if the send fails just set it to failed, it will diff --git a/bindings/authenticatedChannels.go b/bindings/authenticatedChannels.go index 67d2a885098b34671d75014189b682ed75a6a32c..36888a604177ae36b37d80f97ad61b01700cb51a 100644 --- a/bindings/authenticatedChannels.go +++ b/bindings/authenticatedChannels.go @@ -63,7 +63,7 @@ func (c *Client) RequestAuthenticatedChannel(recipientMarshaled, func (c *Client) RegisterAuthCallbacks(request AuthRequestCallback, confirm AuthConfirmCallback) { - requestFunc := func(requestor contact.Contact, message string) { + requestFunc := func(requestor contact.Contact) { requestorBind := &Contact{c: &requestor} request.Callback(requestorBind) } @@ -136,3 +136,8 @@ func (c *Client) GetRelationshipFingerprint(partnerID []byte) (string, error) { return c.api.GetRelationshipFingerprint(partner) } + +// ReplayRequests Resends all pending requests over the normal callbacks +func (c *Client) ReplayRequests() () { + c.api.GetAuthRegistrar().ReplayRequests() +} \ No newline at end of file diff --git a/bindings/client.go b/bindings/client.go index 47e4b34a34943c09a740eaec61ef9c6f26965538..1996507cbc0a005ae20006763f6596fe1ca25834 100644 --- a/bindings/client.go +++ b/bindings/client.go @@ -265,6 +265,7 @@ func (c *Client) WaitForNetwork(timeoutMS int) bool { func (c *Client) NetworkFollowerStatus() int { return int(c.api.NetworkFollowerStatus()) } + // HasRunningProcessies checks if any background threads are running. // returns true if none are running. This is meant to be // used when NetworkFollowerStatus() returns Stopping. @@ -449,6 +450,21 @@ func (c *Client) GetNodeRegistrationStatus() (*NodeRegistrationsStatus, error) { return &NodeRegistrationsStatus{registered, total}, err } +// DeleteAllRequests clears all requests from Client's auth storage. +func (c *Client) DeleteAllRequests() error { + return c.api.DeleteAllRequests() +} + +// DeleteSentRequests clears sent requests from Client's auth storage. +func (c *Client) DeleteSentRequests() error { + return c.api.DeleteSentRequests() +} + +// DeleteReceiveRequests clears receive requests from Client's auth storage. +func (c *Client) DeleteReceiveRequests() error { + return c.api.DeleteReceiveRequests() +} + // DeleteContact is a function which removes a contact from Client's storage func (c *Client) DeleteContact(b []byte) error { contactObj, err := UnmarshalContact(b) diff --git a/bindings/group.go b/bindings/group.go index a1f32b34e9c613bbb4357f6c2705ee930a7d3c01..aee0b0f7cc400bf55ed41673fc7c30773ea791fd 100644 --- a/bindings/group.go +++ b/bindings/group.go @@ -124,8 +124,8 @@ func (g *GroupChat) Send(groupIdBytes, message []byte) (*GroupSendReport, error) return nil, errors.Errorf("Failed to unmarshal group ID: %+v", err) } - round, timestamp, err := g.m.Send(groupID, message) - return &GroupSendReport{round, timestamp}, err + round, timestamp, msgID, err := g.m.Send(groupID, message) + return &GroupSendReport{round, timestamp, msgID}, err } // GetGroups returns an IdList containing a list of group IDs that the user is a @@ -239,6 +239,7 @@ func (ngr *NewGroupReport) Unmarshal(b []byte) error { type GroupSendReport struct { roundID id.Round timestamp time.Time + messageID group.MessageID } // GetRoundID returns the ID of the round that the send occurred on. @@ -257,6 +258,16 @@ func (gsr *GroupSendReport) GetTimestampMS() int64 { return int64(ts) } +// GetMessageID returns the ID of the round that the send occurred on. +func (gsr *GroupSendReport) GetMessageID() []byte { + return gsr.messageID[:] +} + +// GetRoundURL returns the URL of the round that the send occurred on. +func (gsr *GroupSendReport) GetRoundURL() string { + return getRoundURL(gsr.roundID) +} + //// // Group Structure //// @@ -407,6 +418,11 @@ func (gmr *GroupMessageReceive) GetRoundID() int64 { return int64(gmr.RoundID) } +// GetRoundURL returns the ID of the round the message was sent on. +func (gmr *GroupMessageReceive) GetRoundURL() string { + return getRoundURL(gmr.RoundID) +} + // GetRoundTimestampNano returns the timestamp, in nanoseconds, of the round the // message was sent on. func (gmr *GroupMessageReceive) GetRoundTimestampNano() int64 { diff --git a/bindings/message.go b/bindings/message.go index 680d9caf778d8d8ceb700b0a3d3ad2b8b6b20d4b..3eee3b343627338fb907ba81b93fe2e73f8f4731 100644 --- a/bindings/message.go +++ b/bindings/message.go @@ -65,3 +65,8 @@ func (m *Message) GetRoundTimestampNano() int64 { func (m *Message) GetRoundId() int64 { return int64(m.r.RoundId) } + +// GetRoundURL returns the message's round URL +func (m *Message) GetRoundURL() string { + return getRoundURL(m.r.RoundId) +} diff --git a/bindings/notifications.go b/bindings/notifications.go index 71db73728f59e1fcd4d382fe79de310eddf860d4..7f393cf7271c253cf5e07771f9f570d89b69a3de 100644 --- a/bindings/notifications.go +++ b/bindings/notifications.go @@ -59,6 +59,7 @@ func (mnfmr *ManyNotificationForMeReport) Len() int { // "e2e" sender user ID reception of an E2E message // "group" group ID reception of a group chat message // "endFT" sender user ID Last message sent confirming end of file transfer +// "groupRQ" sender user ID Request from sender to join a group chat func NotificationsForMe(notifCSV, preimages string) (*ManyNotificationForMeReport, error) { //handle deserialization of preimages var preimageList []edge.Preimage diff --git a/bindings/send.go b/bindings/send.go index be9366974b339fdd06539394cda02e34d94b785b..3b85a34b0f05fe6f119a7d45ffe4f195604fb8e3 100644 --- a/bindings/send.go +++ b/bindings/send.go @@ -199,6 +199,13 @@ func (sr *SendReport) GetMessageID() []byte { return sr.mid[:] } +func (sr *SendReport) GetRoundURL() string { + if sr.rl != nil && sr.rl.Len() > 0 { + return getRoundURL(sr.rl.list[0]) + } + return dashboardBaseURL +} + // GetTimestampMS returns the message's timestamp in milliseconds func (sr *SendReport) GetTimestampMS() int64 { ts := sr.ts.UnixNano() diff --git a/bindings/ud.go b/bindings/ud.go index 727f4118333c2813c8acd227000deffab78ad6e3..f9a12eb39972f94743a833e9ec4e081139b5ac40 100644 --- a/bindings/ud.go +++ b/bindings/ud.go @@ -17,14 +17,13 @@ import ( "time" ) -//This package wraps the user discovery system +// This package wraps the user discovery system -// User Discovery object type UserDiscovery struct { ud *ud.Manager } -// Returns a new user discovery object. Only call this once. It must be called +// NewUserDiscovery returns a new user discovery object. Only call this once. It must be called // after StartNetworkFollower is called and will fail if the network has never // been contacted. // This function technically has a memory leak because it causes both sides of @@ -55,7 +54,7 @@ func (ud *UserDiscovery) Register(username string) error { return ud.ud.Register(username) } -// Adds a fact for the user to user discovery. Will only succeed if the +// AddFact adds a fact for the user to user discovery. Will only succeed if the // user is already registered and the system does not have the fact currently // registered for any user. // Will fail if the fact string is not well formed. @@ -73,14 +72,14 @@ func (ud *UserDiscovery) AddFact(fStr string) (string, error) { return ud.ud.SendRegisterFact(f) } -// Confirms a fact first registered via AddFact. The confirmation ID comes from +// ConfirmFact confirms a fact first registered via AddFact. The confirmation ID comes from // AddFact while the code will come over the associated communications system func (ud *UserDiscovery) ConfirmFact(confirmationID, code string) error { return ud.ud.SendConfirmFact(confirmationID, code) } -// Removes a previously confirmed fact. Will fail if the passed fact string is -// not well formed or if the fact is not associated with this client. +// RemoveFact removes a previously confirmed fact. Will fail if the passed fact string is +// not well-formed or if the fact is not associated with this client. // Users cannot remove username facts and must instead remove the user. func (ud *UserDiscovery) RemoveFact(fStr string) error { f, err := fact.UnstringifyFact(fStr) @@ -108,7 +107,7 @@ type SearchCallback interface { Callback(contacts *ContactList, error string) } -// Searches for the passed Facts. The factList is the stringification of a +// Search for the passed Facts. The factList is the stringification of a // fact list object, look at /bindings/list.go for more on that object. // This will reject if that object is malformed. The SearchCallback will return // a list of contacts, each having the facts it hit against. @@ -141,7 +140,7 @@ type SingleSearchCallback interface { Callback(contact *Contact, error string) } -// Searches for the passed Facts. The fact is the stringification of a +// SearchSingle searches for the passed Facts. The fact is the stringification of a // fact object, look at /bindings/contact.go for more on that object. // This will reject if that object is malformed. The SearchCallback will return // a list of contacts, each having the facts it hit against. @@ -173,7 +172,7 @@ type LookupCallback interface { Callback(contact *Contact, error string) } -// Looks for the contact object associated with the given userID. The +// Lookup the contact object associated with the given userID. The // id is the byte representation of an id. // This will reject if that id is malformed. The LookupCallback will return // the associated contact if it exists. @@ -202,7 +201,7 @@ func (ud UserDiscovery) Lookup(idBytes []byte, callback LookupCallback, } -// MultiLookupCallback returns the result of many paralel lookups +// MultiLookupCallback returns the result of many parallel lookups type MultiLookupCallback interface { Callback(Succeeded *ContactList, failed *IdList, errors string) } @@ -293,3 +292,18 @@ func (ud UserDiscovery) MultiLookup(ids *IdList, callback MultiLookupCallback, return nil } + +// SetAlternativeUserDiscovery sets the alternativeUd object within manager. +// Once set, any user discovery operation will go through the alternative +// user discovery service. +// To undo this operation, use UnsetAlternativeUserDiscovery. +// The contact file is the already read in bytes, not the file path for the contact file. +func (ud *UserDiscovery) SetAlternativeUserDiscovery(address, cert, contactFile []byte) error { + return ud.ud.SetAlternativeUserDiscovery(cert, address, contactFile) +} + +// UnsetAlternativeUserDiscovery clears out the information from +// the Manager object. +func (ud *UserDiscovery) UnsetAlternativeUserDiscovery() error { + return ud.ud.UnsetAlternativeUserDiscovery() +} diff --git a/bindings/url.go b/bindings/url.go new file mode 100644 index 0000000000000000000000000000000000000000..0322b6c3dd05318bbce6a1d9dee8ed9caeebf7da --- /dev/null +++ b/bindings/url.go @@ -0,0 +1,20 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package bindings + + +import ( + "gitlab.com/xx_network/primitives/id" + "fmt" +) + +const dashboardBaseURL = "https://dashboard.xx.network" + +func getRoundURL(round id.Round) string { + return fmt.Sprintf("%s/rounds/%d?xxmessenger=true", dashboardBaseURL, round) +} diff --git a/bindings/utils.go b/bindings/utils.go index b7249f6b910005962f27cbeb34679698e8c69c63..93afd4aab7bbf66970f9041551c3c32f6bdf19a7 100644 --- a/bindings/utils.go +++ b/bindings/utils.go @@ -15,3 +15,9 @@ import "gitlab.com/elixxir/client/api" func CompressJpeg(imgBytes []byte) ([]byte, error) { return api.CompressJpeg(imgBytes) } + +// CompressJpegForPreview takes a JPEG image in byte format +// and compresses it based on desired output size +func CompressJpegForPreview(imgBytes []byte) ([]byte, error) { + return api.CompressJpegForPreview(imgBytes) +} diff --git a/cmd/group.go b/cmd/group.go index b973fb5c7f2b7addcb12a26c2a59b7b4c1524c22..1c7365921187bc6fda2754b2950498709c5f46f9 100644 --- a/cmd/group.go +++ b/cmd/group.go @@ -218,7 +218,7 @@ func sendGroup(groupIdString string, msg []byte, gm *groupChat.Manager) { jww.INFO.Printf("Sending to group %s message %q", groupID, msg) - rid, timestamp, err := gm.Send(groupID, msg) + rid, timestamp, _, err := gm.Send(groupID, msg) if err != nil { jww.FATAL.Panicf("Sending message to group %s: %+v", groupID, err) } diff --git a/cmd/root.go b/cmd/root.go index 28dd11f80212f4c8e1e7749020c54df2d042707b..c9f094a1883304107ea6cbdf5cd7d0c51fc6a7a4 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -304,10 +304,22 @@ var rootCmd = &cobra.Command{ deleteChannel(client, recipientID) } + if viper.GetBool("delete-receive-requests") { + client.DeleteReceiveRequests() + } + + if viper.GetBool("delete-sent-requests") { + client.DeleteSentRequests() + } + + if viper.GetBool("delete-all-requests") { + client.DeleteAllRequests() + } + msg := message.Send{ Recipient: recipientID, Payload: []byte(msgBody), - MessageType: message.Text, + MessageType: message.XxMessage, } paramsE2E := params.GetDefaultE2E() paramsUnsafe := params.GetDefaultUnsafe() @@ -328,10 +340,12 @@ var rootCmd = &cobra.Command{ var roundIDs []id.Round var roundTimeout time.Duration if unsafe { + paramsE2E.DebugTag = "cmd.Unsafe" roundIDs, err = client.SendUnsafe(msg, paramsUnsafe) roundTimeout = paramsUnsafe.Timeout } else { + paramsE2E.DebugTag = "cmd.E2E" roundIDs, _, _, err = client.SendE2E(msg, paramsE2E) roundTimeout = paramsE2E.Timeout @@ -453,7 +467,7 @@ func initClientCallbacks(client *api.Client) (chan *id.ID, swboard := client.GetSwitchboard() recvCh := make(chan message.Receive, 10000) listenerID := swboard.RegisterChannel("DefaultCLIReceiver", - switchboard.AnyUser(), message.Text, recvCh) + switchboard.AnyUser(), message.XxMessage, recvCh) jww.INFO.Printf("Message ListenerID: %v", listenerID) // Set up auth request handler, which simply prints the @@ -471,7 +485,7 @@ func initClientCallbacks(client *api.Client) (chan *id.ID, }) if viper.GetBool("unsafe-channel-creation") { authMgr.AddGeneralRequestCallback(func( - requestor contact.Contact, message string) { + requestor contact.Contact) { jww.INFO.Printf("Channel Request: %s", requestor.ID) _, err := client.ConfirmAuthenticatedChannel( @@ -599,7 +613,7 @@ func initClient() *api.Client { viper.GetUint("e2eNumReKeys")) netParams.E2EParams.RekeyThreshold = viper.GetFloat64("e2eRekeyThreshold") netParams.ForceHistoricalRounds = viper.GetBool("forceHistoricalRounds") - netParams.FastPolling = viper.GetBool(" slowPolling") + netParams.FastPolling = !viper.GetBool("slowPolling") netParams.ForceMessagePickupRetry = viper.GetBool("forceMessagePickupRetry") if netParams.ForceMessagePickupRetry { period := 3 * time.Second @@ -679,13 +693,11 @@ func deleteChannel(client *api.Client, partnerId *id.ID) { } } -func printChanRequest(requestor contact.Contact, message string) { +func printChanRequest(requestor contact.Contact) { msg := fmt.Sprintf("Authentication channel request from: %s\n", requestor.ID) jww.INFO.Printf(msg) fmt.Printf(msg) - msg = fmt.Sprintf("Authentication channel request message: %s\n", message) - jww.INFO.Printf(msg) // fmt.Printf(msg) } @@ -995,7 +1007,7 @@ func init() { "", 500, "The delay between sending the messages in ms") viper.BindPFlag("sendDelay", rootCmd.Flags().Lookup("sendDelay")) rootCmd.Flags().BoolP("splitSends", - "", true, "Force sends to go over multiple rounds if possible") + "", false, "Force sends to go over multiple rounds if possible") viper.BindPFlag("splitSends", rootCmd.Flags().Lookup("splitSends")) rootCmd.Flags().BoolP("verify-sends", "", false, @@ -1030,6 +1042,21 @@ func init() { viper.BindPFlag("delete-channel", rootCmd.PersistentFlags().Lookup("delete-channel")) + rootCmd.PersistentFlags().Bool("delete-receive-requests", false, + "Delete the all received contact requests.") + viper.BindPFlag("delete-receive-requests", + rootCmd.PersistentFlags().Lookup("delete-receive-requests")) + + rootCmd.PersistentFlags().Bool("delete-sent-requests", false, + "Delete the all sent contact requests.") + viper.BindPFlag("delete-sent-requests", + rootCmd.PersistentFlags().Lookup("delete-sent-requests")) + + rootCmd.PersistentFlags().Bool("delete-all-requests", false, + "Delete the all contact requests, both sent and received.") + viper.BindPFlag("delete-all-requests", + rootCmd.PersistentFlags().Lookup("delete-all-requests")) + rootCmd.Flags().BoolP("send-auth-request", "", false, "Send an auth request to the specified destination and wait"+ "for confirmation") diff --git a/cmd/single.go b/cmd/single.go index 27300ef1e6adb9965cc79682890df1ad6f0f7914..cd6cc0a8815caa9969f02123274a0023512988e9 100644 --- a/cmd/single.go +++ b/cmd/single.go @@ -42,7 +42,7 @@ var singleCmd = &cobra.Command{ swBoard := client.GetSwitchboard() recvCh := make(chan message.Receive, 10000) listenerID := swBoard.RegisterChannel("DefaultCLIReceiver", - switchboard.AnyUser(), message.Text, recvCh) + switchboard.AnyUser(), message.XxMessage, recvCh) jww.INFO.Printf("Message ListenerID: %v", listenerID) // Set up auth request handler, which simply prints the user ID of the @@ -53,7 +53,7 @@ var singleCmd = &cobra.Command{ // If unsafe channels, then add auto-acceptor if viper.GetBool("unsafe-channel-creation") { authMgr.AddGeneralRequestCallback(func( - requester contact.Contact, message string) { + requester contact.Contact) { jww.INFO.Printf("Got request: %s", requester.ID) _, err := client.ConfirmAuthenticatedChannel(requester) if err != nil { diff --git a/cmd/ud.go b/cmd/ud.go index 4dd6463cebea7a0f880c94977cda44ad058498e3..cebbe7371079cbc9619aa7c74c4fc68ab419b0c7 100644 --- a/cmd/ud.go +++ b/cmd/ud.go @@ -42,7 +42,7 @@ var udCmd = &cobra.Command{ swBoard := client.GetSwitchboard() recvCh := make(chan message.Receive, 10000) listenerID := swBoard.RegisterChannel("DefaultCLIReceiver", - switchboard.AnyUser(), message.Text, recvCh) + switchboard.AnyUser(), message.XxMessage, recvCh) jww.INFO.Printf("Message ListenerID: %v", listenerID) // Set up auth request handler, which simply prints the user ID of the @@ -53,7 +53,7 @@ var udCmd = &cobra.Command{ // If unsafe channels, add auto-acceptor if viper.GetBool("unsafe-channel-creation") { authMgr.AddGeneralRequestCallback(func( - requester contact.Contact, message string) { + requester contact.Contact) { jww.INFO.Printf("Got Request: %s", requester.ID) _, err := client.ConfirmAuthenticatedChannel(requester) if err != nil { @@ -127,7 +127,6 @@ var udCmd = &cobra.Command{ confirmID := viper.GetString("confirm") if confirmID != "" { - // TODO: Lookup code err = userDiscoveryMgr.SendConfirmFact(confirmID, confirmID) if err != nil { fmt.Printf("Couldn't confirm fact: %s\n", @@ -136,25 +135,27 @@ var udCmd = &cobra.Command{ } } + // Handle lookup (verification) process + // Note: Cryptographic verification occurs above the bindings layer lookupIDStr := viper.GetString("lookup") if lookupIDStr != "" { - lookupID, ok := parseRecipient(lookupIDStr) - if !ok { - jww.FATAL.Panicf("Could not parse recipient: %s", lookupIDStr) - } + lookupID, _ := parseRecipient(lookupIDStr) + //if !ok { + // jww.FATAL.Panicf("Could not parse recipient: %s", lookupIDStr) + //} err = userDiscoveryMgr.Lookup(lookupID, func(newContact contact.Contact, err error) { if err != nil { - jww.FATAL.Panicf("%+v", err) + jww.FATAL.Panicf("UserDiscovery Lookup error: %+v", err) } printContact(newContact) - }, 90*time.Second) + }, 30*time.Second) if err != nil { jww.WARN.Printf("Failed UD lookup: %+v", err) } - time.Sleep(91 * time.Second) + time.Sleep(31 * time.Second) } usernameSearchStr := viper.GetString("searchusername") diff --git a/dummy/send.go b/dummy/send.go index 2ae9d92888b5960bee25687f1b9a4f4078a2cd2b..a01f6ba69cc1940fda4735a9278cf460fbd3b7a9 100644 --- a/dummy/send.go +++ b/dummy/send.go @@ -102,7 +102,7 @@ func (m *Manager) sendMessages(msgs map[id.ID]format.Message) error { "preimage in e2e send: %+v", err) } rng.Close() - + p.DebugTag = "dummy" _, _, err := m.net.SendCMIX(msg, &recipient, p) if err != nil { jww.WARN.Printf("Failed to send dummy message %d/%d via "+ diff --git a/fileTransfer/fileMessage.go b/fileTransfer/fileMessage.go deleted file mode 100644 index b73b948c90cc4d99bad2d459be6a8f98df61f2a5..0000000000000000000000000000000000000000 --- a/fileTransfer/fileMessage.go +++ /dev/null @@ -1,130 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// Copyright © 2020 xx network SEZC // -// // -// Use of this source code is governed by a license that can be found in the // -// LICENSE file // -/////////////////////////////////////////////////////////////////////////////// - -package fileTransfer - -import ( - "encoding/binary" - "github.com/pkg/errors" - ftCrypto "gitlab.com/elixxir/crypto/fileTransfer" -) - -// Size constants. -const ( - paddingLen = ftCrypto.NonceSize // The length of the padding in bytes - partNumLen = 2 // The length of the part number in bytes - fmMinSize = partNumLen + paddingLen // Minimum size for the partMessage -) - -// Error messages. -const ( - newFmSizeErr = "size of external payload (%d) must be greater than %d" - unmarshalFmSizeErr = "size of passed in bytes (%d) must be greater than %d" - setFileFmErr = "length of part bytes (%d) must be smaller than maximum payload size %d" -) - -/* -+-----------------------------------------+ -| CMIX Message Contents | -+---------+-------------+-----------------+ -| Padding | Part Number | File Data | -| 8 bytes | 2 bytes | remaining space | -+---------+-------------+-----------------+ -*/ - -// partMessage contains part of the data being transferred and 256-bit padding -// that is used as a nonce. -type partMessage struct { - data []byte // Serial of all contents - padding []byte // Random padding bytes - partNum []byte // The part number of the file - part []byte // File part data -} - -// newPartMessage generates a new part message that fits into the specified -// external payload size. An error is returned if the external payload size is -// too small to fit the part message. -func newPartMessage(externalPayloadSize int) (partMessage, error) { - if externalPayloadSize < fmMinSize { - return partMessage{}, - errors.Errorf(newFmSizeErr, externalPayloadSize, fmMinSize) - } - - return mapPartMessage(make([]byte, externalPayloadSize)), nil -} - -// mapPartMessage maps the data to the components of a partMessage. It is mapped -// by reference; a copy is not made. -func mapPartMessage(data []byte) partMessage { - return partMessage{ - data: data, - padding: data[:paddingLen], - partNum: data[paddingLen : paddingLen+partNumLen], - part: data[paddingLen+partNumLen:], - } -} - -// unmarshalPartMessage converts the bytes into a partMessage. An error is -// returned if the size of the data is too small for a partMessage. -func unmarshalPartMessage(b []byte) (partMessage, error) { - if len(b) < fmMinSize { - return partMessage{}, - errors.Errorf(unmarshalFmSizeErr, len(b), fmMinSize) - } - - return mapPartMessage(b), nil -} - -// marshal returns the byte representation of the partMessage. -func (m partMessage) marshal() []byte { - return m.data -} - -// getPadding returns the padding in the message. -func (m partMessage) getPadding() []byte { - return m.padding -} - -// setPadding sets the partMessage padding to the given bytes. Note that this -// padding should be random bytes generated via the appropriate crypto function. -func (m partMessage) setPadding(b []byte) { - copy(m.padding, b) -} - -// getPartNum returns the file part number. -func (m partMessage) getPartNum() uint16 { - return binary.LittleEndian.Uint16(m.partNum) -} - -// setPartNum sets the file part number. -func (m partMessage) setPartNum(num uint16) { - b := make([]byte, partNumLen) - binary.LittleEndian.PutUint16(b, num) - copy(m.partNum, b) -} - -// getPart returns the file part data from the message. -func (m partMessage) getPart() []byte { - return m.part -} - -// setPart sets the partMessage part to the given bytes. An error is returned if -// the size of the provided part data is too large to store. -func (m partMessage) setPart(b []byte) error { - if len(b) > len(m.part) { - return errors.Errorf(setFileFmErr, len(b), len(m.part)) - } - - copy(m.part, b) - - return nil -} - -// getPartSize returns the number of bytes available to store part data. -func (m partMessage) getPartSize() int { - return len(m.part) -} diff --git a/fileTransfer/fileMessage_test.go b/fileTransfer/fileMessage_test.go deleted file mode 100644 index 77b72eac9fa78a04d6c4c36aceda9a0effbd8a3e..0000000000000000000000000000000000000000 --- a/fileTransfer/fileMessage_test.go +++ /dev/null @@ -1,283 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// Copyright © 2020 xx network SEZC // -// // -// Use of this source code is governed by a license that can be found in the // -// LICENSE file // -/////////////////////////////////////////////////////////////////////////////// - -package fileTransfer - -import ( - "bytes" - "encoding/binary" - "fmt" - "math/rand" - "testing" -) - -// Tests that newPartMessage returns a partMessage of the expected size. -func Test_newPartMessage(t *testing.T) { - externalPayloadSize := 256 - - fm, err := newPartMessage(externalPayloadSize) - if err != nil { - t.Errorf("newPartMessage returned an error: %+v", err) - } - - if len(fm.data) != externalPayloadSize { - t.Errorf("Size of partMessage data does not match payload size."+ - "\nexpected: %d\nreceived: %d", externalPayloadSize, len(fm.data)) - } -} - -// Error path: tests that newPartMessage returns the expected error when the -// external payload size is too small. -func Test_newPartMessage_SmallPayloadSizeError(t *testing.T) { - externalPayloadSize := fmMinSize - 1 - expectedErr := fmt.Sprintf(newFmSizeErr, externalPayloadSize, fmMinSize) - - _, err := newPartMessage(externalPayloadSize) - if err == nil || err.Error() != expectedErr { - t.Errorf("newPartMessage did not return the expected error when the "+ - "given external payload size is too small."+ - "\nexpected: %s\nreceived: %+v", expectedErr, err) - } -} - -// Tests that mapPartMessage maps the data to the correct parts of the -// partMessage. -func Test_mapPartMessage(t *testing.T) { - // Generate expected values - _, expectedData, expectedPadding, expectedPartNum, expectedFile := - newRandomFileMessage() - - fm := mapPartMessage(expectedData) - - if !bytes.Equal(expectedData, fm.data) { - t.Errorf("Incorrect data.\nexpected: %q\nreceived: %q", - expectedData, fm.data) - } - - if !bytes.Equal(expectedPadding, fm.padding) { - t.Errorf("Incorrect padding data.\nexpected: %q\nreceived: %q", - expectedPadding, fm.padding) - } - - if !bytes.Equal(expectedPartNum, fm.partNum) { - t.Errorf("Incorrect part number.\nexpected: %q\nreceived: %q", - expectedPartNum, fm.partNum) - } - - if !bytes.Equal(expectedFile, fm.part) { - t.Errorf("Incorrect part data.\nexpected: %q\nreceived: %q", - expectedFile, fm.part) - } - -} - -// Tests that unmarshalPartMessage returns a partMessage with the expected -// values. -func Test_unmarshalPartMessage(t *testing.T) { - // Generate expected values - _, expectedData, expectedPadding, expectedPartNumb, expectedFile := - newRandomFileMessage() - - fm, err := unmarshalPartMessage(expectedData) - if err != nil { - t.Errorf("unmarshalPartMessage return an error: %+v", err) - } - - if !bytes.Equal(expectedData, fm.data) { - t.Errorf("Incorrect data.\nexpected: %q\nreceived: %q", - expectedData, fm.data) - } - - if !bytes.Equal(expectedPadding, fm.padding) { - t.Errorf("Incorrect padding data.\nexpected: %q\nreceived: %q", - expectedPadding, fm.padding) - } - - if !bytes.Equal(expectedPartNumb, fm.partNum) { - t.Errorf("Incorrect part number.\nexpected: %q\nreceived: %q", - expectedPartNumb, fm.partNum) - } - - if !bytes.Equal(expectedFile, fm.part) { - t.Errorf("Incorrect part data.\nexpected: %q\nreceived: %q", - expectedFile, fm.part) - } -} - -// Error path: tests that unmarshalPartMessage returns the expected error when -// the provided data is too small to be unmarshalled into a partMessage. -func Test_unmarshalPartMessage_SizeError(t *testing.T) { - data := make([]byte, fmMinSize-1) - expectedErr := fmt.Sprintf(unmarshalFmSizeErr, len(data), fmMinSize) - - _, err := unmarshalPartMessage(data) - if err == nil || err.Error() != expectedErr { - t.Errorf("unmarshalPartMessage did not return the expected error when "+ - "the given bytes are too small to be a partMessage."+ - "\nexpected: %s\nreceived: %+v", expectedErr, err) - } -} - -// Tests that partMessage.marshal returns the correct data. -func Test_fileMessage_marshal(t *testing.T) { - fm, expectedData, _, _, _ := newRandomFileMessage() - - data := fm.marshal() - - if !bytes.Equal(expectedData, data) { - t.Errorf("Marshalled data does not match expected."+ - "\nexpected: %q\nreceived: %q", expectedData, data) - } -} - -// Tests that partMessage.getPadding returns the correct padding data. -func Test_fileMessage_getPadding(t *testing.T) { - fm, _, expectedPadding, _, _ := newRandomFileMessage() - - padding := fm.getPadding() - - if !bytes.Equal(expectedPadding, padding) { - t.Errorf("Padding data does not match expected."+ - "\nexpected: %q\nreceived: %q", expectedPadding, padding) - } -} - -// Tests that partMessage.setPadding sets the correct data. -func Test_fileMessage_setPadding(t *testing.T) { - fm, err := newPartMessage(256) - if err != nil { - t.Errorf("Failed to create new partMessage: %+v", err) - } - - expectedPadding := make([]byte, paddingLen) - rand.New(rand.NewSource(42)).Read(expectedPadding) - - fm.setPadding(expectedPadding) - - if !bytes.Equal(expectedPadding, fm.getPadding()) { - t.Errorf("Failed to set correct padding.\nexpected: %q\nreceived: %q", - expectedPadding, fm.getPadding()) - } -} - -// Tests that partMessage.getPartNum returns the correct part number. -func Test_fileMessage_getPartNum(t *testing.T) { - fm, _, _, expectedPartNum, _ := newRandomFileMessage() - - partNum := fm.getPartNum() - expected := binary.LittleEndian.Uint16(expectedPartNum) - - if expected != partNum { - t.Errorf("Part number does not match expected."+ - "\nexpected: %d\nreceived: %d", expected, partNum) - } -} - -// Tests that partMessage.setPartNum sets the correct part number. -func Test_fileMessage_setPartNum(t *testing.T) { - fm, err := newPartMessage(256) - if err != nil { - t.Errorf("Failed to create new partMessage: %+v", err) - } - - expectedPartNum := make([]byte, partNumLen) - rand.New(rand.NewSource(42)).Read(expectedPartNum) - expected := binary.LittleEndian.Uint16(expectedPartNum) - - fm.setPartNum(expected) - - if expected != fm.getPartNum() { - t.Errorf("Failed to set correct part number.\nexpected: %d\nreceived: %d", - expected, fm.getPartNum()) - } -} - -// Tests that partMessage.getPart returns the correct part data. -func Test_fileMessage_getFile(t *testing.T) { - fm, _, _, _, expectedFile := newRandomFileMessage() - - file := fm.getPart() - - if !bytes.Equal(expectedFile, file) { - t.Errorf("File data does not match expected."+ - "\nexpected: %q\nreceived: %q", expectedFile, file) - } -} - -// Tests that partMessage.setPart sets the correct part data. -func Test_fileMessage_setFile(t *testing.T) { - fm, err := newPartMessage(256) - if err != nil { - t.Errorf("Failed to create new partMessage: %+v", err) - } - - fileData := make([]byte, 64) - rand.New(rand.NewSource(42)).Read(fileData) - expectedFile := make([]byte, fm.getPartSize()) - copy(expectedFile, fileData) - - err = fm.setPart(expectedFile) - if err != nil { - t.Errorf("setPart returned an error: %+v", err) - } - - if !bytes.Equal(expectedFile, fm.getPart()) { - t.Errorf("Failed to set correct part data.\nexpected: %q\nreceived: %q", - expectedFile, fm.getPart()) - } -} - -// Error path: tests that partMessage.setPart returns the expected error when -// the provided part data is too large for the message. -func Test_fileMessage_setFile_FileTooLargeError(t *testing.T) { - fm, err := newPartMessage(fmMinSize + 1) - if err != nil { - t.Errorf("Failed to create new partMessage: %+v", err) - } - - expectedErr := fmt.Sprintf(setFileFmErr, fm.getPartSize()+1, fm.getPartSize()) - - err = fm.setPart(make([]byte, fm.getPartSize()+1)) - if err == nil || err.Error() != expectedErr { - t.Errorf("setPart did not return the expected error when the given "+ - "part data is too large to fit in the partMessage."+ - "\nexpected: %s\nreceived: %+v", expectedErr, err) - } -} - -// Tests that partMessage.getPartSize returns the expected available space for -// the part data. -func Test_fileMessage_getFileSize(t *testing.T) { - expectedSize := 256 - - fm, err := newPartMessage(fmMinSize + expectedSize) - if err != nil { - t.Errorf("Failed to create new partMessage: %+v", err) - } - - if expectedSize != fm.getPartSize() { - t.Errorf("File size incorrect.\nexpected: %d\nreceived: %d", - expectedSize, fm.getPartSize()) - } -} - -// newRandomFileMessage generates a new partMessage filled with random data and -// return the partMessage and its individual parts. -func newRandomFileMessage() (partMessage, []byte, []byte, []byte, []byte) { - prng := rand.New(rand.NewSource(42)) - padding := make([]byte, paddingLen) - prng.Read(padding) - partNum := make([]byte, partNumLen) - prng.Read(partNum) - part := make([]byte, 64) - prng.Read(part) - data := append(append(padding, partNum...), part...) - - fm := mapPartMessage(data) - - return fm, data, padding, partNum, part -} diff --git a/fileTransfer/manager.go b/fileTransfer/manager.go index e91303742ebc27ccb95810e004cd69b2d8449d62..b188bcc203fe5e286507a5f7e7de4cb34485a836 100644 --- a/fileTransfer/manager.go +++ b/fileTransfer/manager.go @@ -101,8 +101,8 @@ type Manager struct { sendQueue chan queuedPart // Indicates if old transfers saved to storage have been recovered after - // file transfer is closed and reopened - oldTransfersRecovered bool + // file transfer is closed and reopened; this is an atomic + oldTransfersRecovered *uint32 // File transfer parameters p Params @@ -159,14 +159,16 @@ func newManager(client *api.Client, store *storage.Session, } jww.DEBUG.Printf(""+ - "[FT] Created mew file transfer manager with params: %+v", p) + "[FT] Created new file transfer manager with params: %+v", p) + + oldTransfersRecovered := uint32(0) return &Manager{ receiveCB: receiveCB, sent: sent, received: received, sendQueue: make(chan queuedPart, sendQueueBuffLen), - oldTransfersRecovered: false, + oldTransfersRecovered: &oldTransfersRecovered, p: p, client: client, store: store, diff --git a/fileTransfer/manager_test.go b/fileTransfer/manager_test.go index c1133aa32a8f0f0e2fa09139678cdfd224e44fa2..63952f154487962fc6e504c56d58f6d0d00cda02 100644 --- a/fileTransfer/manager_test.go +++ b/fileTransfer/manager_test.go @@ -388,14 +388,13 @@ func TestManager_Resend_NoFingerprints(t *testing.T) { func TestManager_CloseSend_NoFingerprints(t *testing.T) { m, sti, _ := newTestManagerWithTransfers( []uint16{16}, false, false, nil, nil, nil, t) - prng := NewPrng(42) partSize, _ := m.getPartSize() // Use up all the fingerprints in the transfer transfer, _ := m.sent.GetTransfer(sti[0].tid) for fpNum := uint16(0); fpNum < sti[0].numFps; fpNum++ { partNum := fpNum % sti[0].numParts - _, _, _, _, err := transfer.GetEncryptedPart(partNum, partSize, prng) + _, _, _, err := transfer.GetEncryptedPart(partNum, partSize+2) if err != nil { t.Errorf("Failed to encrypt part %d (%d): %+v", partNum, fpNum, err) } diff --git a/fileTransfer/oldTransferRecovery.go b/fileTransfer/oldTransferRecovery.go index 2957e1b156835be260c470910b8767d0653a8b4b..7fc4d41c61915d070717a3001c4154237cf0f81c 100644 --- a/fileTransfer/oldTransferRecovery.go +++ b/fileTransfer/oldTransferRecovery.go @@ -12,6 +12,7 @@ import ( jww "github.com/spf13/jwalterweatherman" ftCrypto "gitlab.com/elixxir/crypto/fileTransfer" "gitlab.com/xx_network/primitives/id" + "sync/atomic" ) // Error messages. @@ -29,7 +30,9 @@ const roundResultsMaxAttempts = 5 func (m Manager) oldTransferRecovery(healthyChan chan bool, chanID uint64) { // Exit if old transfers have already been recovered - if m.oldTransfersRecovered { + // TODO: move GetUnsentPartsAndSentRounds to manager creation and remove the + // atomic + if !atomic.CompareAndSwapUint32(m.oldTransfersRecovered, 0, 1) { jww.DEBUG.Printf("[FT] Old file transfer recovery thread not " + "starting: none to recover (app was not closed)") return @@ -38,6 +41,9 @@ func (m Manager) oldTransferRecovery(healthyChan chan bool, chanID uint64) { // Get list of unsent parts and rounds that parts were sent on unsentParts, sentRounds, err := m.sent.GetUnsentPartsAndSentRounds() + jww.DEBUG.Printf("Adding unsent parts from %d recovered transfers: %v", + len(unsentParts), unsentParts) + // Add all unsent parts to the queue for tid, partNums := range unsentParts { m.queueParts(tid, partNums) @@ -106,7 +112,9 @@ func (m Manager) updateSentRounds(healthyChan chan bool, roundList, err) } else { jww.INFO.Printf( - "[FT] Successfully recovered old file transfers.") + "[FT] Successfully recovered old file transfers: %v", + sentRounds) + return nil } getRoundResultsAttempts++ diff --git a/fileTransfer/receive.go b/fileTransfer/receive.go index 9a70f5853212bae431653c096d9168d567008b2a..3552fdf5635a819f66cbecfb9945ca01ab529846 100644 --- a/fileTransfer/receive.go +++ b/fileTransfer/receive.go @@ -8,7 +8,6 @@ package fileTransfer import ( - "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/interfaces/message" "gitlab.com/elixxir/client/stoppable" @@ -64,16 +63,8 @@ func (m *Manager) readMessage(msg message.Receive) (format.Message, error) { return cMixMsg, err } - // Unmarshal cMix message contents into a file part message - partMsg, err := unmarshalPartMessage(cMixMsg.GetContents()) - if err != nil { - return cMixMsg, errors.Errorf(unmarshalPartMessageErr, err) - } - // Add part to received transfer - rt, tid, completed, err := m.received.AddPart(partMsg.getPart(), - partMsg.getPadding(), cMixMsg.GetMac(), partMsg.getPartNum(), - cMixMsg.GetKeyFP()) + rt, tid, completed, err := m.received.AddPart(cMixMsg) if err != nil { return cMixMsg, err } diff --git a/fileTransfer/receiveNew_test.go b/fileTransfer/receiveNew_test.go index 872012beee4ed019a035212c85f45a16ea7a1926..4aea311faa6e0fedadf6f627e8f4b43eb544753e 100644 --- a/fileTransfer/receiveNew_test.go +++ b/fileTransfer/receiveNew_test.go @@ -51,7 +51,7 @@ func TestManager_receiveNewFileTransfer(t *testing.T) { } marshalledMsg, err := proto.Marshal(protoMsg) if err != nil { - t.Errorf("Failed to marshal proto message: %+v", err) + t.Errorf("Failed to Marshal proto message: %+v", err) } receiveMsg := message.Receive{ Payload: marshalledMsg, @@ -120,7 +120,7 @@ func TestManager_receiveNewFileTransfer_Stop(t *testing.T) { } marshalledMsg, err := proto.Marshal(protoMsg) if err != nil { - t.Errorf("Failed to marshal proto message: %+v", err) + t.Errorf("Failed to Marshal proto message: %+v", err) } receiveMsg := message.Receive{ Payload: marshalledMsg, diff --git a/fileTransfer/send.go b/fileTransfer/send.go index dbab0994774046b8ce5fa62ab8030fbde4335fd8..0ae1bdfae8e58456395d4cc5cbc300fd13f7040a 100644 --- a/fileTransfer/send.go +++ b/fileTransfer/send.go @@ -33,7 +33,7 @@ import ( const ( // Manager.sendParts sendManyCmixWarn = "[FT] Failed to send %d file parts %v via SendManyCMIX: %+v" - setInProgressErr = "[FT] Failed to set parts %v to in-progress for transfer %s" + setInProgressErr = "[FT] Failed to set parts %v to in-progress for transfer %s: %+v" getRoundResultsErr = "[FT] Failed to get round results for round %d for file transfers %v: %+v" // Manager.buildMessages @@ -47,7 +47,7 @@ const ( finishedEndE2eMsfErr = "[FT] Failed to send E2E message to %s on completion of file transfer %s: %+v" roundFailureWarn = "[FT] Failed to send file parts for file transfers %v on round %d: round %s" finishFailNoTransferErr = "[FT] Failed to requeue in-progress parts on failure of round %d for transfer %s: %+v" - unsetInProgressErr = "[FT] Failed to remove parts from in-progress list for transfer %s: round %s" + unsetInProgressErr = "[FT] Failed to remove parts from in-progress list for transfer %s: round %s: %+v" // Manager.sendEndE2eMessage endE2eGetPartnerErr = "failed to get file transfer partner %s: %+v" @@ -211,7 +211,7 @@ func (m *Manager) handleSend(partList *[]queuedPart, lastSend *time.Time, // Send all the messages err := m.sendParts(*partList, sentRounds) if err != nil { - jww.FATAL.Panic(err) + jww.ERROR.Print(err) } // Update the timestamp of the send @@ -247,6 +247,7 @@ func (m *Manager) sendParts(partList []queuedPart, p := params.GetDefaultCMIX() p.SendTimeout = m.p.SendTimeout p.ExcludedRounds = sentRounds + p.DebugTag = "ft.Part" // Send parts rid, _, err := m.net.SendManyCMIX(messages, p) @@ -270,7 +271,7 @@ func (m *Manager) sendParts(partList []queuedPart, for tid, transfer := range transfers { exists, err := transfer.SetInProgress(rid, groupedParts[tid]...) if err != nil { - return errors.Errorf(setInProgressErr, groupedParts[tid], tid) + return errors.Errorf(setInProgressErr, groupedParts[tid], tid, err) } transfer.CallProgressCB(nil) @@ -364,30 +365,14 @@ func (m *Manager) newCmixMessage(transfer *ftStorage.SentTransfer, // Create new empty cMix message cmixMsg := format.NewMessage(m.store.Cmix().GetGroup().GetP().ByteLen()) - // Create new empty file part message of size equal to the available payload - // size in the cMix message - partMsg, err := newPartMessage(cmixMsg.ContentsSize()) - if err != nil { - return cmixMsg, err - } - - // Get encrypted file part, file part MAC, padding (nonce), and fingerprint - encPart, mac, padding, fp, err := transfer.GetEncryptedPart( - partNum, partMsg.getPartSize(), rng) - if err != nil { - return cmixMsg, err - } - - // Construct file part message from padding ( - partMsg.setPadding(padding) - partMsg.setPartNum(partNum) - err = partMsg.setPart(encPart) + // Get encrypted file part, file part MAC, nonce (nonce), and fingerprint + encPart, mac, fp, err := transfer.GetEncryptedPart(partNum, cmixMsg.ContentsSize()) if err != nil { - return cmixMsg, err + return format.Message{}, err } // Construct cMix message - cmixMsg.SetContents(partMsg.marshal()) + cmixMsg.SetContents(encPart) cmixMsg.SetKeyFP(fp) cmixMsg.SetMac(mac) @@ -460,7 +445,8 @@ func (m *Manager) makeRoundEventCallback( // Remove parts from in-progress list partsToResend, err := st.UnsetInProgress(rid) if err != nil { - jww.ERROR.Printf(unsetInProgressErr, tid, roundResult) + jww.ERROR.Printf( + unsetInProgressErr, tid, roundResult, err) } // Call progress callback after change in progress @@ -492,6 +478,7 @@ func (m *Manager) sendEndE2eMessage(recipient *id.ID) error { // Send the message under file transfer preimage e2eParams := params.GetDefaultE2E() e2eParams.IdentityPreimage = partner.GetFileTransferPreimage() + e2eParams.DebugTag = "ft.End" // Store the message in the critical messages buffer first to ensure it is // present if the send fails @@ -529,7 +516,7 @@ func (m *Manager) sendEndE2eMessage(recipient *id.ID) error { // the session and the log m.store.GetCriticalMessages().Succeeded(sendMsg, e2eParams) jww.INFO.Printf("[FT] Sending of message %s informing %s that a transfer "+ - "ended successful.", e2eMsgID, recipient) + "completed successfully.", e2eMsgID, recipient) return nil } @@ -565,12 +552,12 @@ func (m *Manager) getPartSize() (int, error) { // Create new empty file part message of size equal to the available payload // size in the cMix message - partMsg, err := newPartMessage(cmixMsg.ContentsSize()) + partMsg, err := ftStorage.NewPartMessage(cmixMsg.ContentsSize()) if err != nil { return 0, err } - return partMsg.getPartSize(), nil + return partMsg.GetPartSize(), nil } // partitionFile splits the file into parts of the specified part size. diff --git a/fileTransfer/sendNew.go b/fileTransfer/sendNew.go index 99a935b737a6d0cfff495cc4e458ab1314fd3414..b28fe263958c3ff5b95455faee9f3623f44267b1 100644 --- a/fileTransfer/sendNew.go +++ b/fileTransfer/sendNew.go @@ -43,6 +43,7 @@ func (m *Manager) sendNewFileTransfer(recipient *id.ID, fileName, // Sends as a silent message to avoid a notification p := params.GetDefaultE2E() p.CMIX.IdentityPreimage = relationship.GetSilentPreimage() + p.DebugTag = "ft.New" // Send E2E message rounds, _, _, err := m.net.SendE2E(sendMsg, p, nil) diff --git a/fileTransfer/send_test.go b/fileTransfer/send_test.go index ee10658b1fe6c190cbaa9a04879c72829a726787..2e3d3d16d8325be32ba975decb14b3af41f57636 100644 --- a/fileTransfer/send_test.go +++ b/fileTransfer/send_test.go @@ -662,20 +662,20 @@ func TestManager_newCmixMessage(t *testing.T) { "\nexpected: %s\nrecieved: %s", fp, cmixMsg.GetKeyFP()) } - partMsg, err := unmarshalPartMessage(cmixMsg.GetContents()) + decrPart, err := ftCrypto.DecryptPart(key, cmixMsg.GetContents(), + cmixMsg.GetMac(), 0,cmixMsg.GetKeyFP()) if err != nil { - t.Errorf("Failed to unmarshal part message: %+v", err) + t.Errorf("Failed to decrypt file part: %+v", err) } - decrPart, err := ftCrypto.DecryptPart(key, partMsg.getPart(), - partMsg.getPadding(), cmixMsg.GetMac(), partMsg.getPartNum()) + partMsg, err := ftStorage.UnmarshalPartMessage(decrPart) if err != nil { - t.Errorf("Failed to decrypt file part: %+v", err) + t.Errorf("Failed to unmarshal part message: %+v", err) } - if !bytes.Equal(decrPart, parts[0]) { + if !bytes.Equal(partMsg.GetPart(), parts[0]) { t.Errorf("Decrypted part does not match expected."+ - "\nexpected: %q\nreceived: %q", parts[0], decrPart) + "\nexpected: %q\nreceived: %q", parts[0], partMsg.GetPart()) } } @@ -998,7 +998,7 @@ func Test_makeListOfPartNums(t *testing.T) { } } -// Tests that the part size returned by Manager.getPartSize matches the manually +// Tests that the part size returned by Manager.GetPartSize matches the manually // calculated part size. func TestManager_getPartSize(t *testing.T) { m := newTestManager(false, nil, nil, nil, nil, t) @@ -1006,13 +1006,13 @@ func TestManager_getPartSize(t *testing.T) { // Calculate the expected part size primeByteLen := m.store.Cmix().GetGroup().GetP().ByteLen() cmixMsgUsedLen := format.AssociatedDataSize - filePartMsgUsedLen := fmMinSize + filePartMsgUsedLen := ftStorage.FmMinSize expected := 2*primeByteLen - cmixMsgUsedLen - filePartMsgUsedLen-1 // Get the part size partSize, err := m.getPartSize() if err != nil { - t.Errorf("getPartSize returned an error: %+v", err) + t.Errorf("GetPartSize returned an error: %+v", err) } if expected != partSize { diff --git a/fileTransfer/utils_test.go b/fileTransfer/utils_test.go index 7738b4529052317bf77a952da2d0e5dea311249d..a982d3ba226565db9e4886132f164998560826a6 100644 --- a/fileTransfer/utils_test.go +++ b/fileTransfer/utils_test.go @@ -191,17 +191,20 @@ func newTestManager(sendErr bool, sendChan, sendE2eChan chan message.Receive, avgSendSize := avgNumMessages * (8192 / 8) p.MaxThroughput = int(time.Second) * avgSendSize + oldTransfersRecovered := uint32(0) + m := &Manager{ - receiveCB: receiveCB, - sent: sent, - received: received, - sendQueue: make(chan queuedPart, sendQueueBuffLen), - p: p, - store: storage.InitTestingSession(t), - swb: switchboard.New(), - net: net, - rng: fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG), - getRoundResults: rr, + receiveCB: receiveCB, + sent: sent, + received: received, + sendQueue: make(chan queuedPart, sendQueueBuffLen), + oldTransfersRecovered: &oldTransfersRecovered, + p: p, + store: storage.InitTestingSession(t), + swb: switchboard.New(), + net: net, + rng: fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG), + getRoundResults: rr, } return m @@ -495,6 +498,7 @@ func (tnm *testNetworkManager) SendManyCMIX(messages []message.TargetedCmixMessa for _, msg := range messages { tnm.sendChan <- message.Receive{ Payload: msg.Message.Marshal(), + Sender: &id.ID{0}, RoundId: tnm.rid, } } diff --git a/go.mod b/go.mod index e4762cd35b89dc5ca2a563a8bcaac06f38f08d55..4a0f6367b73509140ecc2885b5e5b7bd87396bd4 100644 --- a/go.mod +++ b/go.mod @@ -1,36 +1,39 @@ module gitlab.com/elixxir/client -go 1.13 +go 1.17 require ( github.com/cloudflare/circl v1.0.1-0.20211008185751-59b49bc148ce github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 github.com/golang/protobuf v1.5.2 - github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect - github.com/magiconair/properties v1.8.4 // indirect - github.com/mitchellh/mapstructure v1.4.0 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 - github.com/pelletier/go-toml v1.8.1 // indirect github.com/pkg/errors v0.9.1 - github.com/smartystreets/assertions v1.0.1 // indirect - github.com/spf13/afero v1.5.1 // indirect - github.com/spf13/cast v1.3.1 // indirect github.com/spf13/cobra v1.1.1 github.com/spf13/jwalterweatherman v1.1.0 github.com/spf13/viper v1.7.1 gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228 - gitlab.com/elixxir/comms v0.0.4-0.20220104174855-044783c5c1e6 - gitlab.com/elixxir/crypto v0.0.7-0.20220104174238-dbd761b30553 - gitlab.com/elixxir/ekv v0.1.5 - gitlab.com/elixxir/primitives v0.0.3-0.20220104173924-275cb9d7834f - gitlab.com/xx_network/comms v0.0.4-0.20211227194445-c099754b3cda - gitlab.com/xx_network/crypto v0.0.5-0.20211227194420-f311e8920467 - gitlab.com/xx_network/primitives v0.0.4-0.20211222205802-03e9d7d835b0 - golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 - golang.org/x/net v0.0.0-20210525063256-abc453219eb5 - google.golang.org/genproto v0.0.0-20210105202744-fe13368bc0e1 // indirect + gitlab.com/elixxir/comms v0.0.4-0.20220222212253-41a1a0067369 + gitlab.com/elixxir/crypto v0.0.7-0.20220222212142-d3303373ee78 + gitlab.com/elixxir/ekv v0.1.6 + gitlab.com/elixxir/primitives v0.0.3-0.20220222212109-d412a6e46623 + gitlab.com/xx_network/comms v0.0.4-0.20220222212058-5a37737af57e + gitlab.com/xx_network/crypto v0.0.5-0.20220222212031-750f7e8a01f4 + gitlab.com/xx_network/primitives v0.0.4-0.20220222211843-901fa4a2d72b + golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed + golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 google.golang.org/grpc v1.42.0 google.golang.org/protobuf v1.27.1 +) + +require ( + github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect + github.com/magiconair/properties v1.8.4 // indirect + github.com/mitchellh/mapstructure v1.4.0 // indirect + github.com/pelletier/go-toml v1.8.1 // indirect + github.com/smartystreets/assertions v1.0.1 // indirect + github.com/spf13/afero v1.5.1 // indirect + github.com/spf13/cast v1.3.1 // indirect + google.golang.org/genproto v0.0.0-20210105202744-fe13368bc0e1 // indirect gopkg.in/ini.v1 v1.62.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index f331b2abbba23eafdf7369c14a046d90de1dada3..5cecc075ac7209f3664ffc22c85e679008a5edfb 100644 --- a/go.sum +++ b/go.sum @@ -51,6 +51,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/elliotchance/orderedmap v1.4.0 h1:wZtfeEONCbx6in1CZyE6bELEt/vFayMvsxqI5SgsR+A= +github.com/elliotchance/orderedmap v1.4.0/go.mod h1:wsDwEaX5jEoyhbs7x93zk2H/qv0zwuhg4inXhDkYqys= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -270,36 +272,34 @@ github.com/zeebo/pcg v1.0.0 h1:dt+dx+HvX8g7Un32rY9XWoYnd0NmKmrIzpHF7qiTDj0= github.com/zeebo/pcg v1.0.0/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228 h1:Gi6rj4mAlK0BJIk1HIzBVMjWNjIUfstrsXC2VqLYPcA= gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228/go.mod h1:H6jztdm0k+wEV2QGK/KYA+MY9nj9Zzatux/qIvDDv3k= -gitlab.com/elixxir/comms v0.0.4-0.20220104174855-044783c5c1e6 h1:MaiS3Mdhjwc3aNKonKJf9FSgnShwXQ6FnAI1QENDi7o= -gitlab.com/elixxir/comms v0.0.4-0.20220104174855-044783c5c1e6/go.mod h1:r4xZgd+DPDujHTJZ6Y8+JXM2Ae2rZHa6rFggOPxs/Gc= +gitlab.com/elixxir/comms v0.0.4-0.20220222212253-41a1a0067369 h1:Bk5T3unbs3cjEqzxCirb1IlcTOMAEVQLltgsrVs1/cw= +gitlab.com/elixxir/comms v0.0.4-0.20220222212253-41a1a0067369/go.mod h1:AligJKSltFDPe/rqE2EZBfWCMSrae0zUo7scsXoyMPE= gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4/go.mod h1:ucm9SFKJo+K0N2GwRRpaNr+tKXMIOVWzmyUD0SbOu2c= gitlab.com/elixxir/crypto v0.0.3/go.mod h1:ZNgBOblhYToR4m8tj4cMvJ9UsJAUKq+p0gCp07WQmhA= -gitlab.com/elixxir/crypto v0.0.7-0.20211230230452-bca020488964/go.mod h1:fexaw14nwGMlT6vL9eIJ1ixgiomyAp88hSHl0Yx0/xU= -gitlab.com/elixxir/crypto v0.0.7-0.20220104174238-dbd761b30553 h1:BPwepGZspxgiY4QMUwVFMgVIEs8ziuMcT/uXmPQQ9Gs= -gitlab.com/elixxir/crypto v0.0.7-0.20220104174238-dbd761b30553/go.mod h1:fexaw14nwGMlT6vL9eIJ1ixgiomyAp88hSHl0Yx0/xU= -gitlab.com/elixxir/ekv v0.1.5 h1:R8M1PA5zRU1HVnTyrtwybdABh7gUJSCvt1JZwUSeTzk= -gitlab.com/elixxir/ekv v0.1.5/go.mod h1:e6WPUt97taFZe5PFLPb1Dupk7tqmDCTQu1kkstqJvw4= +gitlab.com/elixxir/crypto v0.0.7-0.20220222212142-d3303373ee78 h1:MvZ0UwyhCSuNUcmHT905oadu7XYT2WSz+QD3Rjcgg00= +gitlab.com/elixxir/crypto v0.0.7-0.20220222212142-d3303373ee78/go.mod h1:bPD4FmnnaDFLxn+d4YDWZhVnevWXArKwOMMza4MU5uQ= +gitlab.com/elixxir/ekv v0.1.6 h1:M2hUSNhH/ChxDd+s8xBqSEKgoPtmE6hOEBqQ73KbN6A= +gitlab.com/elixxir/ekv v0.1.6/go.mod h1:e6WPUt97taFZe5PFLPb1Dupk7tqmDCTQu1kkstqJvw4= gitlab.com/elixxir/primitives v0.0.0-20200731184040-494269b53b4d/go.mod h1:OQgUZq7SjnE0b+8+iIAT2eqQF+2IFHn73tOo+aV11mg= gitlab.com/elixxir/primitives v0.0.0-20200804170709-a1896d262cd9/go.mod h1:p0VelQda72OzoUckr1O+vPW0AiFe0nyKQ6gYcmFSuF8= gitlab.com/elixxir/primitives v0.0.0-20200804182913-788f47bded40/go.mod h1:tzdFFvb1ESmuTCOl1z6+yf6oAICDxH2NPUemVgoNLxc= gitlab.com/elixxir/primitives v0.0.1/go.mod h1:kNp47yPqja2lHSiS4DddTvFpB/4D9dB2YKnw5c+LJCE= -gitlab.com/elixxir/primitives v0.0.3-0.20211230224340-fc0905d8776e/go.mod h1:zA+1Lp9fGPo6pl1QxtMoNPLeZJ1O5m4kcH7HNxePQnQ= -gitlab.com/elixxir/primitives v0.0.3-0.20220104173924-275cb9d7834f h1:zg3oYk+a6Wmq9tGRwka3GjJR1XRZIVCsOMpBGxtF2yc= -gitlab.com/elixxir/primitives v0.0.3-0.20220104173924-275cb9d7834f/go.mod h1:zA+1Lp9fGPo6pl1QxtMoNPLeZJ1O5m4kcH7HNxePQnQ= +gitlab.com/elixxir/primitives v0.0.3-0.20220222212109-d412a6e46623 h1:NzJ06KdJd3fVJee0QvGhNr3CO+Ki8Ea1PeakZsm+rZM= +gitlab.com/elixxir/primitives v0.0.3-0.20220222212109-d412a6e46623/go.mod h1:MtFIyJUQn9P7djzVlBpEYkPNnnWFTjZvw89swoXY+QM= gitlab.com/xx_network/comms v0.0.0-20200805174823-841427dd5023/go.mod h1:owEcxTRl7gsoM8c3RQ5KAm5GstxrJp5tn+6JfQ4z5Hw= -gitlab.com/xx_network/comms v0.0.4-0.20211227194445-c099754b3cda h1:oWl8TuAgdx/6J9lwdH8wYGSb4W6D5TG1AZkzbA8EzbE= -gitlab.com/xx_network/comms v0.0.4-0.20211227194445-c099754b3cda/go.mod h1:5arueRMa2MNa6dALnfJwyZOhqhV53Gqc+tlHRz+Ycjw= +gitlab.com/xx_network/comms v0.0.4-0.20220222212058-5a37737af57e h1:PrQoTQoA6be4J+Lr/AclebS5Gz0Zm/TYC5b44qWa1PU= +gitlab.com/xx_network/comms v0.0.4-0.20220222212058-5a37737af57e/go.mod h1:isHnwem0v4rTcwwHP455FhVlFyPcHkHiVz+N3s/uCSI= gitlab.com/xx_network/crypto v0.0.3/go.mod h1:DF2HYvvCw9wkBybXcXAgQMzX+MiGbFPjwt3t17VRqRE= gitlab.com/xx_network/crypto v0.0.4/go.mod h1:+lcQEy+Th4eswFgQDwT0EXKp4AXrlubxalwQFH5O0Mk= -gitlab.com/xx_network/crypto v0.0.5-0.20211227194420-f311e8920467 h1:LkZtWBYrM2e7QRf5aaBAcy7s7CpYGhAqgXRFVCdBRy4= -gitlab.com/xx_network/crypto v0.0.5-0.20211227194420-f311e8920467/go.mod h1:c+x0w3Xk6QZe5w2Redn5SiaBpqAhgNSfwBr0JGa/yyo= +gitlab.com/xx_network/crypto v0.0.5-0.20220222212031-750f7e8a01f4 h1:95dZDMn/hpLNwsgZO9eyQgGKaSDyh6F6+WygqZIciww= +gitlab.com/xx_network/crypto v0.0.5-0.20220222212031-750f7e8a01f4/go.mod h1:6apvsoHCQJDjO0J4E3uhR3yO9tTz/Mq5be5rjB3tQPU= gitlab.com/xx_network/primitives v0.0.0-20200803231956-9b192c57ea7c/go.mod h1:wtdCMr7DPePz9qwctNoAUzZtbOSHSedcK++3Df3psjA= gitlab.com/xx_network/primitives v0.0.0-20200804183002-f99f7a7284da/go.mod h1:OK9xevzWCaPO7b1wiluVJGk7R5ZsuC7pHY5hteZFQug= gitlab.com/xx_network/primitives v0.0.2/go.mod h1:cs0QlFpdMDI6lAo61lDRH2JZz+3aVkHy+QogOB6F/qc= -gitlab.com/xx_network/primitives v0.0.4-0.20211222205802-03e9d7d835b0 h1:IHHb59DJEKk02HgfxddqK2ilvkRMAUdPIBFn8rmjjIg= -gitlab.com/xx_network/primitives v0.0.4-0.20211222205802-03e9d7d835b0/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE= -gitlab.com/xx_network/ring v0.0.3-0.20210527191221-ce3f170aabd5 h1:FY+4Rh1Q2rgLyv10aKJjhWApuKRCR/054XhreudfAvw= -gitlab.com/xx_network/ring v0.0.3-0.20210527191221-ce3f170aabd5/go.mod h1:aLzpP2TiZTQut/PVHR40EJAomzugDdHXetbieRClXIM= +gitlab.com/xx_network/primitives v0.0.4-0.20220222211843-901fa4a2d72b h1:shZZ3xZNNKYVpEp4Mqu/G9+ZR+J8QA8mmPk/4Cit8+Y= +gitlab.com/xx_network/primitives v0.0.4-0.20220222211843-901fa4a2d72b/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE= +gitlab.com/xx_network/ring v0.0.3-0.20220222211904-da613960ad93 h1:eJZrXqHsMmmejEPWw8gNAt0I8CGAMNO/7C339Zco3TM= +gitlab.com/xx_network/ring v0.0.3-0.20220222211904-da613960ad93/go.mod h1:aLzpP2TiZTQut/PVHR40EJAomzugDdHXetbieRClXIM= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= @@ -320,8 +320,8 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed h1:YoWVYYAfvQ4ddHv3OKmIvX7NCAhFGTj62VP2l2kfBbA= +golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -357,8 +357,9 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5 h1:wjuX4b5yYQnEQHzd+CBcrcC6OVR2J1CN6mUy0oSxIPo= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= diff --git a/groupChat/send.go b/groupChat/send.go index 868d420134b6ebb79c0687f58648311af6445559..03c513ccc032ab28778dfb94bb44d05c1362355e 100644 --- a/groupChat/send.go +++ b/groupChat/send.go @@ -37,44 +37,55 @@ const ( // Send sends a message to all group members using Client.SendManyCMIX. The // send fails if the message is too long. -func (m *Manager) Send(groupID *id.ID, message []byte) (id.Round, time.Time, +func (m *Manager) Send(groupID *id.ID, message []byte) (id.Round, time.Time, group.MessageID, error) { // Get the current time stripped of the monotonic clock timeNow := netTime.Now().Round(0) // Create a cMix message for each group member - messages, err := m.createMessages(groupID, message, timeNow) + messages, msgID, err := m.createMessages(groupID, message, timeNow) if err != nil { - return 0, time.Time{}, errors.Errorf(newCmixMsgErr, err) + return 0, time.Time{}, group.MessageID{}, errors.Errorf(newCmixMsgErr, err) } param := params.GetDefaultCMIX() param.IdentityPreimage = groupID[:] + param.DebugTag = "group.Message" rid, _, err := m.net.SendManyCMIX(messages, param) if err != nil { - return 0, time.Time{}, + return 0, time.Time{}, group.MessageID{}, errors.Errorf(sendManyCmixErr, m.gs.GetUser().ID, groupID, err) } jww.DEBUG.Printf("Sent message to %d members in group %s at %s.", len(messages), groupID, timeNow) - return rid, timeNow, nil + return rid, timeNow, msgID, nil } // createMessages generates a list of cMix messages and a list of corresponding // recipient IDs. func (m *Manager) createMessages(groupID *id.ID, msg []byte, timestamp time.Time) ( - []message.TargetedCmixMessage, error) { + []message.TargetedCmixMessage, group.MessageID, error) { + + //make the message ID + cmixMsg := format.NewMessage(m.store.Cmix().GetGroup().GetP().ByteLen()) + _, intlMsg, err := newMessageParts(cmixMsg.ContentsSize()) + if err != nil { + return nil, group.MessageID{},errors.WithMessage(err,"Failed to make message parts for message ID") + } + messageID := group.NewMessageID(groupID, setInternalPayload(intlMsg, timestamp, m.gs.GetUser().ID, msg)) g, exists := m.gs.Get(groupID) if !exists { - return []message.TargetedCmixMessage{}, + return []message.TargetedCmixMessage{}, group.MessageID{}, errors.Errorf(newNoGroupErr, groupID) } - return m.newMessages(g, msg, timestamp) + NewMessages, err := m.newMessages(g, msg, timestamp) + + return NewMessages, messageID, err } // newMessages is a private function that allows the passing in of a timestamp diff --git a/groupChat/sendRequests.go b/groupChat/sendRequests.go index edcecc77f1b5516a9ed75d9481738210374b88e4..555fd2c5e0cfc891ba8cf37877e6dc72353aebf3 100644 --- a/groupChat/sendRequests.go +++ b/groupChat/sendRequests.go @@ -117,7 +117,18 @@ func (m Manager) sendRequest(memberID *id.ID, request []byte) ([]id.Round, error MessageType: message.GroupCreationRequest, } - rounds, _, _, err := m.net.SendE2E(sendMsg, params.GetDefaultE2E(), nil) + + recipent, err := m.store.E2e().GetPartner(memberID) + if err!=nil{ + return nil, errors.WithMessagef(err,"Failed to send request to %s " + + "because e2e relationship could not be found", memberID) + } + + p := params.GetDefaultE2E() + p.IdentityPreimage = recipent.GetGroupRequestPreimage() + p.DebugTag = "group.Request" + + rounds, _, _, err := m.net.SendE2E(sendMsg, p, nil) if err != nil { return nil, errors.Errorf(sendE2eErr, memberID, err) } diff --git a/groupChat/sendRequests_test.go b/groupChat/sendRequests_test.go index 9c22c13fed92f6b986a6f71db63d6f9aea3a230a..9f2b9c19f2eb2c65dd51d3f66e3dc512cfe991b3 100644 --- a/groupChat/sendRequests_test.go +++ b/groupChat/sendRequests_test.go @@ -9,8 +9,13 @@ package groupChat import ( "fmt" + "github.com/cloudflare/circl/dh/sidh" "github.com/golang/protobuf/proto" "gitlab.com/elixxir/client/interfaces/message" + "gitlab.com/elixxir/client/interfaces/params" + 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" @@ -33,11 +38,29 @@ func TestManager_ResendRequest(t *testing.T) { Created: g.Created.UnixNano(), } + for i := range g.Members{ + grp := m.store.E2e().GetGroup() + dhKey := grp.NewInt(int64(i + 42)) + pubKey := diffieHellman.GeneratePublicKey(dhKey, grp) + p := params.GetDefaultE2ESessionParams() + rng := csprng.NewSystemRNG() + _, mySidhPriv := util.GenerateSIDHKeyPair( + sidh.KeyVariantSidhA, rng) + theirSidhPub, _ := util.GenerateSIDHKeyPair( + sidh.KeyVariantSidhB, rng) + err := m.store.E2e().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) @@ -112,6 +135,23 @@ func TestManager_sendRequests(t *testing.T) { Created: g.Created.UnixNano(), } + for i := range g.Members{ + grp := m.store.E2e().GetGroup() + dhKey := grp.NewInt(int64(i + 42)) + pubKey := diffieHellman.GeneratePublicKey(dhKey, grp) + p := params.GetDefaultE2ESessionParams() + rng := csprng.NewSystemRNG() + _, mySidhPriv := util.GenerateSIDHKeyPair( + sidh.KeyVariantSidhA, rng) + theirSidhPub, _ := util.GenerateSIDHKeyPair( + sidh.KeyVariantSidhB, rng) + err := m.store.E2e().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) @@ -195,6 +235,23 @@ func TestManager_sendRequests_SendPartialSent(t *testing.T) { expectedErr := fmt.Sprintf(sendRequestPartialErr, (len(g.Members)-1)/2, len(g.Members)-1, "") + for i := range g.Members{ + grp := m.store.E2e().GetGroup() + dhKey := grp.NewInt(int64(i + 42)) + pubKey := diffieHellman.GeneratePublicKey(dhKey, grp) + p := params.GetDefaultE2ESessionParams() + rng := csprng.NewSystemRNG() + _, mySidhPriv := util.GenerateSIDHKeyPair( + sidh.KeyVariantSidhA, rng) + theirSidhPub, _ := util.GenerateSIDHKeyPair( + sidh.KeyVariantSidhB, rng) + err := m.store.E2e().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."+ @@ -217,6 +274,23 @@ func TestManager_sendRequest(t *testing.T) { prng := rand.New(rand.NewSource(42)) m, g := newTestManagerWithStore(prng, 10, 0, nil, nil, t) + for i := range g.Members{ + grp := m.store.E2e().GetGroup() + dhKey := grp.NewInt(int64(i + 42)) + pubKey := diffieHellman.GeneratePublicKey(dhKey, grp) + p := params.GetDefaultE2ESessionParams() + rng := csprng.NewSystemRNG() + _, mySidhPriv := util.GenerateSIDHKeyPair( + sidh.KeyVariantSidhA, rng) + theirSidhPub, _ := util.GenerateSIDHKeyPair( + sidh.KeyVariantSidhB, rng) + err := m.store.E2e().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) + } + } + expected := message.Send{ Recipient: g.Members[0].ID, Payload: []byte("request message"), @@ -241,7 +315,25 @@ func TestManager_sendRequest_SendE2eError(t *testing.T) { m, _ := newTestManagerWithStore(prng, 10, 1, nil, nil, t) expectedErr := strings.SplitN(sendE2eErr, "%", 2)[0] - _, err := m.sendRequest(id.NewIdFromString("memberID", id.User, t), nil) + recipientID := id.NewIdFromString("memberID", id.User, t) + + grp := m.store.E2e().GetGroup() + dhKey := grp.NewInt(int64(42)) + pubKey := diffieHellman.GeneratePublicKey(dhKey, grp) + p := params.GetDefaultE2ESessionParams() + rng := csprng.NewSystemRNG() + _, mySidhPriv := util.GenerateSIDHKeyPair( + sidh.KeyVariantSidhA, rng) + theirSidhPub, _ := util.GenerateSIDHKeyPair( + sidh.KeyVariantSidhB, rng) + err := m.store.E2e().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) diff --git a/groupChat/send_test.go b/groupChat/send_test.go index 8fd29ba8c60719ef6f0ca2eff8c1d39049ef1c42..2d6940c3a69dbe3b894481e738d6fbb1d7974e45 100644 --- a/groupChat/send_test.go +++ b/groupChat/send_test.go @@ -30,7 +30,7 @@ func TestManager_Send(t *testing.T) { messageBytes := []byte("Group chat message.") sender := m.gs.GetUser().DeepCopy() - _, _, err := m.Send(g.ID, messageBytes) + _, _, _, err := m.Send(g.ID, messageBytes) if err != nil { t.Errorf("Send() returned an error: %+v", err) } @@ -110,7 +110,7 @@ func TestManager_Send_CmixMessageError(t *testing.T) { expectedErr := strings.SplitN(newCmixMsgErr, "%", 2)[0] // Send message - _, _, err := m.Send(g.ID, make([]byte, 400)) + _, _, _, err := m.Send(g.ID, make([]byte, 400)) if err == nil || !strings.Contains(err.Error(), expectedErr) { t.Errorf("Send() failed to return the expected error."+ "\nexpected: %s\nreceived: %+v", expectedErr, err) @@ -125,7 +125,7 @@ func TestManager_Send_SendManyCMIXError(t *testing.T) { expectedErr := strings.SplitN(sendManyCmixErr, "%", 2)[0] // Send message - _, _, err := m.Send(g.ID, []byte("message")) + _, _, _, err := m.Send(g.ID, []byte("message")) if err == nil || !strings.Contains(err.Error(), expectedErr) { t.Errorf("Send() failed to return the expected error."+ "\nexpected: %s\nreceived: %+v", expectedErr, err) @@ -145,7 +145,7 @@ func TestManager_createMessages(t *testing.T) { testMsg := []byte("Test group message.") sender := m.gs.GetUser() - messages, err := m.createMessages(g.ID, testMsg, netTime.Now()) + messages, _, err := m.createMessages(g.ID, testMsg, netTime.Now()) if err != nil { t.Errorf("createMessages() returned an error: %+v", err) } @@ -205,7 +205,7 @@ func TestManager_createMessages_InvalidGroupIdError(t *testing.T) { m, _ := newTestManagerWithStore(prng, 10, 0, nil, nil, t) // Read message and make sure the error is expected - _, err := m.createMessages( + _, _, err := m.createMessages( id.NewIdFromString("invalidID", id.Group, t), nil, time.Time{}) if err == nil || !strings.Contains(err.Error(), expectedErr) { t.Errorf("createMessages() did not return the expected error."+ diff --git a/interfaces/auth.go b/interfaces/auth.go index bb3cae74d0a37f0fda2c34e8ce0623af6f8a789f..4ce22fba789b45a5616a640f0409a054f6a05ecb 100644 --- a/interfaces/auth.go +++ b/interfaces/auth.go @@ -12,7 +12,7 @@ import ( "gitlab.com/xx_network/primitives/id" ) -type RequestCallback func(requestor contact.Contact, message string) +type RequestCallback func(requestor contact.Contact) type ConfirmCallback func(partner contact.Contact) type Auth interface { @@ -42,4 +42,6 @@ type Auth interface { AddSpecificConfirmCallback(id *id.ID, cb ConfirmCallback) // Removes a specific callback to be used on auth confirm. RemoveSpecificConfirmCallback(id *id.ID) + //Replays all pending received requests over tha callbacks + ReplayRequests() } diff --git a/interfaces/message/type.go b/interfaces/message/type.go index 4a5115a162e562e04c6e2577d7a80d64589d8b4f..4c444628f46b06a80d12d43485f3c8bf2243ed45 100644 --- a/interfaces/message/type.go +++ b/interfaces/message/type.go @@ -23,8 +23,8 @@ const ( // non Cmix messages will be ignored Raw Type = 1 - //General text message, contains human readable text - Text Type = 2 + //Type of message sent by the xx messenger + XxMessage Type = 2 /*User Discovery message types*/ //Message structures defined in the UD package diff --git a/interfaces/params/CMIX.go b/interfaces/params/CMIX.go index 5e772d0360a94350ff6b99b6db33d22934e5410d..db2bfa655906d3b07d98ca54ea1f1539cd2fb9ed 100644 --- a/interfaces/params/CMIX.go +++ b/interfaces/params/CMIX.go @@ -27,6 +27,10 @@ type CMIX struct { // an alternate identity preimage to use on send. If not set, the default // for the sending identity will be used IdentityPreimage []byte + + // Tag which prints with sending logs to help localize the source + // All internal sends are tagged, so the default tag is "External" + DebugTag string } func GetDefaultCMIX() CMIX { @@ -35,6 +39,7 @@ func GetDefaultCMIX() CMIX { Timeout: 25 * time.Second, RetryDelay: 1 * time.Second, SendTimeout: 3 * time.Second, + DebugTag: "External", } } diff --git a/interfaces/params/message.go b/interfaces/params/message.go index fbf9779829b939145cf7bc1277fa79b5617b826a..27a8ebd7d626445cc4c2ace03772d5dbbb6105ed 100644 --- a/interfaces/params/message.go +++ b/interfaces/params/message.go @@ -16,6 +16,7 @@ type Messages struct { MessageReceptionWorkerPoolSize uint MaxChecksGarbledMessage uint GarbledMessageWait time.Duration + RealtimeOnly bool } func GetDefaultMessage() Messages { @@ -24,5 +25,6 @@ func GetDefaultMessage() Messages { MessageReceptionWorkerPoolSize: 4, MaxChecksGarbledMessage: 10, GarbledMessageWait: 15 * time.Minute, + RealtimeOnly: false, } } diff --git a/interfaces/params/network.go b/interfaces/params/network.go index d6f9ef1b01d6f493e8060febdb5e968ddb7f0a5c..7e3b6f4ab3f72f664af8e5b7e9cc5d226950a37f 100644 --- a/interfaces/params/network.go +++ b/interfaces/params/network.go @@ -34,6 +34,10 @@ type Network struct { // Determines if the state of every round processed is tracked in ram. // This is very memory intensive and is primarily used for debugging VerboseRoundTracking bool + //disables all attempts to pick up dropped or missed messages + RealtimeOnly bool + // Resends auth requests up the stack if received multiple times + ReplayRequests bool Rounds Messages @@ -46,7 +50,7 @@ func GetDefaultNetwork() Network { n := Network{ TrackNetworkPeriod: 100 * time.Millisecond, MaxCheckedRounds: 500, - RegNodesBufferLen: 500, + RegNodesBufferLen: 1000, NetworkHealthTimeout: 30 * time.Second, E2EParams: GetDefaultE2ESessionParams(), ParallelNodeRegistrations: 20, @@ -54,6 +58,8 @@ func GetDefaultNetwork() Network { FastPolling: true, BlacklistedNodes: make([]string, 0), VerboseRoundTracking: false, + RealtimeOnly: false, + ReplayRequests: true, } n.Rounds = GetDefaultRounds() n.Messages = GetDefaultMessage() @@ -65,6 +71,13 @@ func (n Network) Marshal() ([]byte, error) { return json.Marshal(n) } +func (n Network) SetRealtimeOnlyAll()Network { + n.RealtimeOnly = true + n.Rounds.RealtimeOnly = true + n.Messages.RealtimeOnly = true + return n +} + // Obtain default Network parameters, or override with given parameters if set func GetNetworkParameters(params string) (Network, error) { p := GetDefaultNetwork() diff --git a/interfaces/params/rounds.go b/interfaces/params/rounds.go index 40bd41bfbd80a6cef2ecf30d8a1c645782f7b305..75e4270987ab2b5d8ea1c4edb6e49efb9f32a863 100644 --- a/interfaces/params/rounds.go +++ b/interfaces/params/rounds.go @@ -43,6 +43,9 @@ type Rounds struct { // Duration to wait before sending on a round times out and a new round is // tried SendTimeout time.Duration + + //disables all attempts to pick up dropped or missed messages + RealtimeOnly bool } func GetDefaultRounds() Rounds { @@ -58,5 +61,6 @@ func GetDefaultRounds() Rounds { UncheckRoundPeriod: 20 * time.Second, ForceMessagePickupRetry: false, SendTimeout: 1 * time.Second, + RealtimeOnly: false, } } diff --git a/interfaces/preimage/types.go b/interfaces/preimage/types.go index 0b87ef34c47bc1574a94813ebb77ebb0155a67d8..bd6226e623594561ef0338210af8824c1b789f27 100644 --- a/interfaces/preimage/types.go +++ b/interfaces/preimage/types.go @@ -8,4 +8,5 @@ const ( E2e = "e2e" Group = "group" EndFT = "endFT" + GroupRq = "groupRq" ) diff --git a/keyExchange/confirm.go b/keyExchange/confirm.go index d754acc1da040efed8e55227d623c0cc5c414342..4c21f77db86638f24a50fcf65b65001e71e081c9 100644 --- a/keyExchange/confirm.go +++ b/keyExchange/confirm.go @@ -61,7 +61,7 @@ func handleConfirm(sess *storage.Session, confirmation message.Receive) { confirmedSession := partner.GetSendSession(confimedSessionID) if confirmedSession == nil { jww.ERROR.Printf("[REKEY] Failed to find confirmed session %s from "+ - "partner %s: %s", confimedSessionID, confirmation.Sender, err) + "partner %s", confimedSessionID, confirmation.Sender) return } diff --git a/keyExchange/rekey.go b/keyExchange/rekey.go index 773a8544c5a1d614f8b5ab1bbaab14035d3488f3..04bb31413af965f621884a01ab0f557c632ec504 100644 --- a/keyExchange/rekey.go +++ b/keyExchange/rekey.go @@ -8,6 +8,7 @@ package keyExchange import ( + "fmt" "github.com/golang/protobuf/proto" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" @@ -24,7 +25,6 @@ import ( "gitlab.com/elixxir/crypto/diffieHellman" "gitlab.com/elixxir/primitives/states" "time" - "fmt" ) func CheckKeyExchanges(instance *network.Instance, sendE2E interfaces.SendE2E, @@ -121,6 +121,7 @@ func negotiate(instance *network.Instance, sendE2E interfaces.SendE2E, e2eParams := params.GetDefaultE2E() e2eParams.Type = params.KeyExchange e2eParams.IdentityPreimage = rekeyPreimage + e2eParams.DebugTag = "kx.Request" rounds, msgID, _, err := sendE2E(m, e2eParams, stop) // If the send fails, returns the error so it can be handled. The caller diff --git a/keyExchange/trigger.go b/keyExchange/trigger.go index 9e3bf66a3a7b596478f35be48cb3e88415c184c6..fcdb71aa93fc535ac879696ebe36a86d6045de87 100644 --- a/keyExchange/trigger.go +++ b/keyExchange/trigger.go @@ -125,6 +125,7 @@ func handleTrigger(sess *storage.Session, net interfaces.NetworkManager, //send the message under the key exchange e2eParams := params.GetDefaultE2E() e2eParams.IdentityPreimage = partner.GetSilentPreimage() + e2eParams.DebugTag = "kx.Confirm" // store in critical messages buffer first to ensure it is resent if the // send fails diff --git a/network/ephemeral/tracker.go b/network/ephemeral/tracker.go index 5c2a23b4f750b372bd237895c7d7b664707ccd2e..e848c23c473b90170b740fc31cd5734bf746e7ad 100644 --- a/network/ephemeral/tracker.go +++ b/network/ephemeral/tracker.go @@ -78,7 +78,7 @@ func track(session *storage.Session, addrSpace *AddressSpace, ourId *id.ID, stop // Generates the IDs since the last track protoIds, err := ephemeral.GetIdsByRange( - ourId, uint(addressSize), lastCheck, now.Sub(lastCheck)) + ourId, uint(addressSize), lastCheck, now.Add(validityGracePeriod).Sub(lastCheck)) jww.DEBUG.Printf("Now: %s, LastCheck: %s, Different: %s", now, lastCheck, now.Sub(lastCheck)) diff --git a/network/ephemeral/tracker_test.go b/network/ephemeral/tracker_test.go index 47b8f034766c45c003f9f4997a599cf1ad22cfbd..4a946a3fc66c31071cf6b8ba31bc8e913ef73bfd 100644 --- a/network/ephemeral/tracker_test.go +++ b/network/ephemeral/tracker_test.go @@ -107,7 +107,7 @@ func setupInstance(instance interfaces.NetworkManager) error { if err = signature.SignRsa(ri, testCert); err != nil { return errors.Errorf("Failed to sign round info: %+v", err) } - if err = instance.GetInstance().RoundUpdate(ri); err != nil { + if _, err = instance.GetInstance().RoundUpdate(ri); err != nil { return errors.Errorf("Failed to RoundUpdate from from file: %+v", err) } @@ -117,7 +117,7 @@ func setupInstance(instance interfaces.NetworkManager) error { if err = signature.SignRsa(ri, testCert); err != nil { return errors.Errorf("Failed to sign round info: %+v", err) } - if err = instance.GetInstance().RoundUpdate(ri); err != nil { + if _, err = instance.GetInstance().RoundUpdate(ri); err != nil { return errors.Errorf("Failed to RoundUpdate from from file: %v", err) } diff --git a/network/follow.go b/network/follow.go index e25f5ff6cd43857aa7e5fd4cd5856f2a2eaa5172..6f98a33b50127d78f10e703b1c75081d02357d0f 100644 --- a/network/follow.go +++ b/network/follow.go @@ -41,7 +41,14 @@ import ( "time" ) -const debugTrackPeriod = 1 * time.Minute +const ( + debugTrackPeriod = 1 * time.Minute + + // Estimate the number of rounds per second in the network. Will need updated someday + // in order to correctly determine how far back to search rounds for messages + // as the network continues to grow, otherwise message drops occur. + estimatedRoundsPerSecond = 5 +) //comms interface makes testing easier type followNetworkComms interface { @@ -143,7 +150,7 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, result, err := m.GetSender().SendToAny(func(host *connect.Host) (interface{}, error) { jww.DEBUG.Printf("Executing poll for %v(%s) range: %s-%s(%s) from %s", identity.EphId.Int64(), identity.Source, identity.StartValid, - identity.EndValid, identity.StartValid.Sub(identity.EndValid), host.GetId()) + identity.EndValid, identity.EndValid.Sub(identity.StartValid), host.GetId()) return comms.SendPoll(host, &pollReq) }, stop) @@ -217,23 +224,12 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, } // Update the address space size - // todo: this is a fix for incompatibility with the live network - // remove once the live network has been pushed to if len(m.Instance.GetPartialNdf().Get().AddressSpace) != 0 { m.addrSpace.Update(m.Instance.GetPartialNdf().Get().AddressSpace[0].Size) - } else { - m.addrSpace.Update(18) } - // NOTE: this updates rounds and updates the tracking of the health of the - // network + // NOTE: this updates rounds and updates the tracking of the health of the network if pollResp.Updates != nil { - err = m.Instance.RoundUpdates(pollResp.Updates) - if err != nil { - jww.ERROR.Printf("%+v", err) - return - } - // TODO: ClientErr needs to know the source of the error and it doesn't yet // Iterate over ClientErrors for each RoundUpdate for _, update := range pollResp.Updates { @@ -259,19 +255,11 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, return } + // Mutate the update to indicate failure due to a ClientError // FIXME: Should be able to trigger proper type of round event // FIXME: without mutating the RoundInfo. Signature also needs verified // FIXME: before keys are deleted update.State = uint32(states.FAILED) - rnd, err := m.Instance.GetWrappedRound(id.Round(update.ID)) - if err != nil { - jww.ERROR.Printf("Failed to report client error: "+ - "Could not get round for event triggering: "+ - "Unable to get round %d from instance: %+v", - id.Round(update.ID), err) - break - } - m.Instance.GetRoundEvents().TriggerRoundEvent(rnd) // delete all existing keys and trigger a re-registration with the relevant Node m.Session.Cmix().Remove(nid) @@ -280,6 +268,13 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, } } + // Trigger RoundEvents for all polled updates, including modified rounds with ClientErrors + err = m.Instance.RoundUpdates(pollResp.Updates) + if err != nil { + jww.ERROR.Printf("%+v", err) + return + } + newestTS := uint64(0) for i := 0; i < len(pollResp.Updates[len(pollResp.Updates)-1].Timestamps); i++ { if pollResp.Updates[len(pollResp.Updates)-1].Timestamps[i] != 0 { @@ -308,18 +303,9 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, return } - //get the range fo filters which are valid for the identity - filtersStart, filtersEnd, outOfBounds := rounds.ValidFilterRange(identity, pollResp.Filters) - - //check if there are any valid filters returned - if outOfBounds { - jww.WARN.Printf("No filters processed, none in valid range") - return - } - //prepare the filter objects for processing - filterList := make([]*rounds.RemoteFilter, 0, filtersEnd-filtersStart) - for i := filtersStart; i < filtersEnd; i++ { + filterList := make([]*rounds.RemoteFilter, 0, len(pollResp.Filters.Filters)) + for i := range pollResp.Filters.Filters { if len(pollResp.Filters.Filters[i].Filter) != 0 { filterList = append(filterList, rounds.NewRemoteFilter(pollResp.Filters.Filters[i])) } @@ -340,25 +326,42 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, // tracked round if it is behind earliestTrackedRound := id.Round(pollResp.EarliestRound) m.SetFakeEarliestRound(earliestTrackedRound) - updated, old, _ := identity.ER.Set(earliestTrackedRound) + updatedEarliestRound, old, _ := identity.ER.Set(earliestTrackedRound) + + // If there was no registered rounds for the identity if old == 0 { - if gwRoundsState.GetLastChecked() > id.Round(m.param.KnownRoundsThreshold) { - updated = gwRoundsState.GetLastChecked() - id.Round(m.param.KnownRoundsThreshold) + lastCheckedRound := gwRoundsState.GetLastChecked() + // Approximate the earliest possible round that messages could be received on this ID + // by using an estimate of how many rounds the network runs per second + roundsDelta := uint(time.Now().Sub(identity.StartValid) / time.Second * estimatedRoundsPerSecond) + if roundsDelta < m.param.KnownRoundsThreshold { + roundsDelta = m.param.KnownRoundsThreshold + } + if id.Round(roundsDelta) > lastCheckedRound { + // Handles edge case for new networks to prevent starting at negative rounds + updatedEarliestRound = 1 } else { - updated = 1 + updatedEarliestRound = lastCheckedRound - id.Round(roundsDelta) + earliestFilterRound := filterList[0].FirstRound() // Length of filterList always > 0 + // If the network appears to be moving faster than our estimate, causing + // earliestFilterRound to be lower, we will instead use the earliestFilterRound + // which will ensure messages are not dropped as long as contacted gateway has all data + if updatedEarliestRound > earliestFilterRound { + updatedEarliestRound = earliestFilterRound + } } - identity.ER.Set(updated) + identity.ER.Set(updatedEarliestRound) } // loop through all rounds the client does not know about and the gateway // does, checking the bloom filter for the user to see if there are // messages for the user (bloom not implemented yet) //threshold is the earliest round that will not be excluded from earliest remaining - earliestRemaining, roundsWithMessages, roundsUnknown := gwRoundsState.RangeUnchecked(updated, + earliestRemaining, roundsWithMessages, roundsUnknown := gwRoundsState.RangeUnchecked(updatedEarliestRound, m.param.KnownRoundsThreshold, roundChecker) jww.DEBUG.Printf("Processed RangeUnchecked, Oldest: %d, firstUnchecked: %d, "+ "last Checked: %d, threshold: %d, NewEarliestRemaning: %d, NumWithMessages: %d, "+ - "NumUnknown: %d", updated, gwRoundsState.GetFirstUnchecked(), gwRoundsState.GetLastChecked(), + "NumUnknown: %d", updatedEarliestRound, gwRoundsState.GetFirstUnchecked(), gwRoundsState.GetLastChecked(), m.param.KnownRoundsThreshold, earliestRemaining, len(roundsWithMessages), len(roundsUnknown)) _, _, changed := identity.ER.Set(earliestRemaining) @@ -367,12 +370,17 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, jww.DEBUG.Printf("New Earliest Remaining: %d, Gateways last checked: %d", earliestRemaining, gwRoundsState.GetLastChecked()) } - roundsWithMessages2 := identity.UR.Iterate(func(rid id.Round) bool { - if gwRoundsState.Checked(rid) { - return rounds.Checker(rid, filterList, identity.CR) - } - return false - }, roundsUnknown, abandon) + var roundsWithMessages2 []id.Round + + if !m.param.RealtimeOnly{ + roundsWithMessages2 = identity.UR.Iterate(func(rid id.Round) bool { + if gwRoundsState.Checked(rid) { + return rounds.Checker(rid, filterList, identity.CR) + } + return false + }, roundsUnknown, abandon) + } + for _, rid := range roundsWithMessages { //denote that the round has been looked at in the tracking store @@ -393,8 +401,8 @@ func (m *manager) follow(report interfaces.ClientErrorReport, rng csprng.Source, } if m.verboseRounds != nil { - trackingStart := updated - if uint(earliestRemaining-updated) > m.param.KnownRoundsThreshold { + trackingStart := updatedEarliestRound + if uint(earliestRemaining-updatedEarliestRound) > m.param.KnownRoundsThreshold { trackingStart = earliestRemaining - id.Round(m.param.KnownRoundsThreshold) } jww.DEBUG.Printf("Rounds tracked: %v to %v", trackingStart, earliestRemaining) diff --git a/network/gateway/hostPool.go b/network/gateway/hostPool.go index 5bb0b82044058f8c51f055482cdd00a1b6bf37d9..43ae1481b9dcb01f732b12e6d6234fbc29acf5ff 100644 --- a/network/gateway/hostPool.go +++ b/network/gateway/hostPool.go @@ -578,7 +578,7 @@ func (h *HostPool) updateConns() error { return errors.Errorf("Unable to convert new NDF to set: %+v", err) } - // Filter out gateway IDs + // Filter out unwanted gateway IDs newMap = h.getFilter()(newMap, h.ndf) // Keep track of the old NDF set @@ -588,7 +588,7 @@ func (h *HostPool) updateConns() error { // Handle adding Gateways for gwId, ndfIdx := range newMap { - if _, ok := oldMap[gwId]; !ok { + if _, ok := oldMap[gwId]; !ok && gwId.GetType() == id.Gateway { // If GwId in newMap is not in ndfMap, add the Gateway h.addGateway(gwId.DeepCopy(), ndfIdx) } @@ -596,7 +596,7 @@ func (h *HostPool) updateConns() error { // Handle removing Gateways for gwId := range oldMap { - if _, ok := newMap[gwId]; !ok { + if _, ok := newMap[gwId]; !ok && gwId.GetType() == id.Gateway { // If GwId in ndfMap is not in newMap, remove the Gateway h.removeGateway(gwId.DeepCopy()) } diff --git a/network/gateway/sender.go b/network/gateway/sender.go index d09decce432779a13271667851159580dbc6f824..b3b029f369bd91ada4f023f5fd77bb336e682177 100644 --- a/network/gateway/sender.go +++ b/network/gateway/sender.go @@ -153,7 +153,7 @@ func (s *Sender) SendToPreferred(targets []*id.ID, sendFunc sendToPreferredFunc, for targetIdx := range proxies { // Return an error if the timeout duration is reached if netTime.Since(startTime) > timeout { - return nil, errors.Errorf("iterating over target's procies "+ + return nil, errors.Errorf("iterating over target's proxies "+ "timed out after %s", timeout) } diff --git a/network/manager.go b/network/manager.go index c968102e2f5b04269c123c5d1b624ae7a865678a..bba264c6b63263a196d4bfc135f1db42649948ec 100644 --- a/network/manager.go +++ b/network/manager.go @@ -105,6 +105,7 @@ func NewManager(session *storage.Session, switchboard *switchboard.Switchboard, events: events, earliestRound: &earliest, } + m.addrSpace.Update(18) if params.VerboseRoundTracking { m.verboseRounds = NewRoundTracker() diff --git a/network/message/garbled.go b/network/message/garbled.go index 9388f4dcfb2c42f3f9c106cc949c893207451ced..b2449ca9f0d79c8d1b5cdd7a8e9cd9db1ae2863f 100644 --- a/network/message/garbled.go +++ b/network/message/garbled.go @@ -45,6 +45,7 @@ func (m *Manager) processGarbledMessages(stop *stoppable.Single) { stop.ToStopped() return case <-m.triggerGarbled: + jww.INFO.Printf("[GARBLE] Checking Garbled messages") m.handleGarbledMessages() } } @@ -57,65 +58,72 @@ func (m *Manager) handleGarbledMessages() { var failedMsgs []format.Message //try to decrypt every garbled message, excising those who's counts are too high for grbldMsg, count, timestamp, has := garbledMsgs.Next(); has; grbldMsg, count, timestamp, has = garbledMsgs.Next() { - fingerprint := grbldMsg.GetKeyFP() - // Check if the key is there, process it if it is - if key, isE2E := e2eKv.PopKey(fingerprint); isE2E { - jww.INFO.Printf("[GARBLE] Check E2E for %s, KEYFP: %s", - grbldMsg.Digest(), grbldMsg.GetKeyFP()) - // Decrypt encrypted message - msg, err := key.Decrypt(grbldMsg) - if err == nil { - // get the sender - sender := key.GetSession().GetPartner() - //remove from the buffer if decryption is successful - garbledMsgs.Remove(grbldMsg) + //if it exists, check against all in the list + grbldContents := grbldMsg.GetContents() + identity := m.Session.GetUser().ReceptionID + _, forMe, _ := m.Session.GetEdge().Check(identity, grbldMsg.GetIdentityFP(), grbldContents) + if forMe { + fingerprint := grbldMsg.GetKeyFP() + // Check if the key is there, process it if it is + if key, isE2E := e2eKv.PopKey(fingerprint); isE2E { + jww.INFO.Printf("[GARBLE] Check E2E for %s, KEYFP: %s", + grbldMsg.Digest(), grbldMsg.GetKeyFP()) + // Decrypt encrypted message + msg, err := key.Decrypt(grbldMsg) + if err == nil { + // get the sender + sender := key.GetSession().GetPartner() + //remove from the buffer if decryption is successful + garbledMsgs.Remove(grbldMsg) - jww.INFO.Printf("[GARBLE] message decoded as E2E from "+ - "%s, msgDigest: %s", sender, grbldMsg.Digest()) + jww.INFO.Printf("[GARBLE] message decoded as E2E from "+ + "%s, msgDigest: %s", sender, grbldMsg.Digest()) - //handle the successfully decrypted message - xxMsg, ok := m.partitioner.HandlePartition(sender, message.E2E, - msg.GetContents(), - key.GetSession().GetRelationshipFingerprint()) - if ok { - m.Switchboard.Speak(xxMsg) - continue - } - } - } else { - // todo: figure out how to get the ephermal reception id in here. - // we have the raw data, but do not know what address space was - // used int he round - // todo: figure out how to get the round id, the recipient id, and the round timestamp - /* - ephid, err := ephemeral.Marshal(garbledMsg.GetEphemeralRID()) - if err!=nil{ - jww.WARN.Printf("failed to get the ephemeral id for a garbled " + - "message, clearing the message: %+v", err) - garbledMsgs.Remove(garbledMsg) - continue + //handle the successfully decrypted message + xxMsg, ok := m.partitioner.HandlePartition(sender, message.E2E, + msg.GetContents(), + key.GetSession().GetRelationshipFingerprint()) + if ok { + m.Switchboard.Speak(xxMsg) + continue + } } + } else { + // todo: figure out how to get the ephermal reception id in here. + // we have the raw data, but do not know what address space was + // used int he round + // todo: figure out how to get the round id, the recipient id, and the round timestamp + /* + ephid, err := ephemeral.Marshal(garbledMsg.GetEphemeralRID()) + if err!=nil{ + jww.WARN.Printf("failed to get the ephemeral id for a garbled " + + "message, clearing the message: %+v", err) + garbledMsgs.Remove(garbledMsg) + continue + } - ephid.Clear(m.)*/ + ephid.Clear(m.)*/ - raw := message.Receive{ - Payload: grbldMsg.Marshal(), - MessageType: message.Raw, - Sender: &id.ID{}, - EphemeralID: ephemeral.Id{}, - Timestamp: time.Time{}, - Encryption: message.None, - RecipientID: &id.ID{}, - RoundId: 0, - RoundTimestamp: time.Time{}, + raw := message.Receive{ + Payload: grbldMsg.Marshal(), + MessageType: message.Raw, + Sender: &id.ID{}, + EphemeralID: ephemeral.Id{}, + Timestamp: time.Time{}, + Encryption: message.None, + RecipientID: &id.ID{}, + RoundId: 0, + RoundTimestamp: time.Time{}, + } + im := fmt.Sprintf("[GARBLE] RAW Message reprocessed: keyFP: %v, "+ + "msgDigest: %s", grbldMsg.GetKeyFP(), grbldMsg.Digest()) + jww.INFO.Print(im) + m.Internal.Events.Report(1, "MessageReception", "Garbled", im) + m.Session.GetGarbledMessages().Add(grbldMsg) + m.Switchboard.Speak(raw) } - im := fmt.Sprintf("[GARBLE] RAW Message reprecessed: keyFP: %v, "+ - "msgDigest: %s", grbldMsg.GetKeyFP(), grbldMsg.Digest()) - jww.INFO.Print(im) - m.Internal.Events.Report(1, "MessageReception", "Garbled", im) - m.Session.GetGarbledMessages().Add(grbldMsg) - m.Switchboard.Speak(raw) } + // fail the message if any part of the decryption fails, // unless it is the last attempts and has been in the buffer long // enough, in which case remove it diff --git a/network/message/garbled_test.go b/network/message/garbled_test.go index 8656eda570f80f0a7f9333cbaa6f82b6ea80594e..4a0f7eda9cb50fe7e7ed61aee72040d3756edb10 100644 --- a/network/message/garbled_test.go +++ b/network/message/garbled_test.go @@ -11,10 +11,12 @@ import ( "gitlab.com/elixxir/client/network/message/parse" "gitlab.com/elixxir/client/stoppable" "gitlab.com/elixxir/client/storage" + "gitlab.com/elixxir/client/storage/edge" util "gitlab.com/elixxir/client/storage/utility" "gitlab.com/elixxir/client/switchboard" "gitlab.com/elixxir/comms/client" "gitlab.com/elixxir/crypto/fastRNG" + "gitlab.com/elixxir/crypto/fingerprint" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/comms/connect" "gitlab.com/xx_network/crypto/csprng" @@ -104,6 +106,13 @@ func TestManager_CheckGarbledMessages(t *testing.T) { t.FailNow() } + preimage := edge.Preimage{ + Data: []byte{0}, + Type: "test", + Source: nil, + } + m.Session.GetEdge().Add(preimage, sess2.GetUser().ReceptionID) + err = sess2.E2e().AddPartner(sess1.GetUser().TransmissionID, sess1.E2e().GetDHPublicKey(), sess2.E2e().GetDHPrivateKey(), mySIDHPubKey, partnerSIDHPrivKey, @@ -143,6 +152,7 @@ func TestManager_CheckGarbledMessages(t *testing.T) { copy(fmp.Timestamp, ts) msg.SetContents(fmp.Bytes()) encryptedMsg := key.Encrypt(msg) + msg.SetIdentityFP(fingerprint.IdentityFP( msg.GetContents(), preimage.Data)) i.Session.GetGarbledMessages().Add(encryptedMsg) stop := stoppable.NewSingle("stop") diff --git a/network/message/handler.go b/network/message/handler.go index b7574f4cf8636b67aaa37ef7330058dd037de2f6..7aa63e6ad85293b57d716d9bdf894736a3fd4bed 100644 --- a/network/message/handler.go +++ b/network/message/handler.go @@ -54,23 +54,27 @@ func (m *Manager) handleMessage(ecrMsg format.Message, bundle Bundle, edge *edge var relationshipFingerprint []byte //if it exists, check against all in the list - has, forMe, _ := m.Session.GetEdge().Check(identity.Source, ecrMsg.GetIdentityFP(), ecrMsg.GetContents()) + ecrMsgContents := ecrMsg.GetContents() + has, forMe, _ := m.Session.GetEdge().Check(identity.Source, ecrMsg.GetIdentityFP(), ecrMsgContents) if !has { jww.INFO.Printf("checking backup %v", preimage.MakeDefault(identity.Source)) //if it doesnt exist, check against the default fingerprint for the identity forMe = fingerprint2.CheckIdentityFP(ecrMsg.GetIdentityFP(), - ecrMsg.GetContents(), preimage.MakeDefault(identity.Source)) + ecrMsgContents, preimage.MakeDefault(identity.Source)) } if !forMe { if jww.GetLogThreshold() == jww.LevelTrace { - expectedFP := fingerprint2.IdentityFP(ecrMsg.GetContents(), + expectedFP := fingerprint2.IdentityFP(ecrMsgContents, preimage.MakeDefault(identity.Source)) jww.TRACE.Printf("Message for %d (%s) failed identity "+ "check: %v (expected-default) vs %v (received)", identity.EphId, identity.Source, expectedFP, ecrMsg.GetIdentityFP()) } - + im := fmt.Sprintf("Garbled/RAW Message: keyFP: %v, round: %d"+ + "msgDigest: %s, not determined to be for client", ecrMsg.GetKeyFP(), bundle.Round, ecrMsg.Digest()) + m.Internal.Events.Report(1, "MessageReception", "Garbled", im) + m.Session.GetGarbledMessages().Add(ecrMsg) return } @@ -103,7 +107,7 @@ func (m *Manager) handleMessage(ecrMsg format.Message, bundle Bundle, edge *edge msg = ecrMsg encTy = message.None } else { - // if it doesnt match any form of encrypted, hear it as a raw message + // if it doesn't match any form of encrypted, hear it as a raw message // and add it to garbled messages to be handled later msg = ecrMsg raw := message.Receive{ @@ -117,7 +121,7 @@ func (m *Manager) handleMessage(ecrMsg format.Message, bundle Bundle, edge *edge RoundId: id.Round(bundle.RoundInfo.ID), RoundTimestamp: time.Unix(0, int64(bundle.RoundInfo.Timestamps[states.QUEUED])), } - im := fmt.Sprintf("Garbled/RAW Message: keyFP: %v, round: %d"+ + im := fmt.Sprintf("Received message of type Garbled/RAW: keyFP: %v, round: %d, "+ "msgDigest: %s", msg.GetKeyFP(), bundle.Round, msg.Digest()) jww.INFO.Print(im) m.Internal.Events.Report(1, "MessageReception", "Garbled", im) @@ -126,14 +130,12 @@ func (m *Manager) handleMessage(ecrMsg format.Message, bundle Bundle, edge *edge return } - - // Process the decrypted/unencrypted message partition, to see if // we get a full message xxMsg, ok := m.partitioner.HandlePartition(sender, encTy, msg.GetContents(), relationshipFingerprint) - im := fmt.Sprintf("Received message of ecr type %s and msg type " + + im := fmt.Sprintf("Received message of ecr type %s and msg type "+ "%d from %s in round %d,msgDigest: %s, keyFP: %v", encTy, xxMsg.MessageType, sender, bundle.Round, msgDigest, msg.GetKeyFP()) jww.INFO.Print(im) diff --git a/network/message/parse/firstMessagePart_test.go b/network/message/parse/firstMessagePart_test.go index 2411dd4a6896453f08d2fd02ae2dd15946f0f76c..9902790552e45add0d651b5db38e3e277173b39e 100644 --- a/network/message/parse/firstMessagePart_test.go +++ b/network/message/parse/firstMessagePart_test.go @@ -36,7 +36,7 @@ var efmp = firstMessagePart{ // Test that newFirstMessagePart returns a correctly made firstMessagePart func TestNewFirstMessagePart(t *testing.T) { fmp := newFirstMessagePart( - message.Text, + message.XxMessage, 1077, 2, time.Unix(1609786229, 0).UTC(), @@ -67,8 +67,8 @@ func TestFirstMessagePartFromBytes(t *testing.T) { // Test that GetType returns the correct type for a firstMessagePart func TestFirstMessagePart_GetType(t *testing.T) { - if efmp.GetType() != message.Text { - t.Errorf("Got %v, expected %v", efmp.GetType(), message.Text) + if efmp.GetType() != message.XxMessage { + t.Errorf("Got %v, expected %v", efmp.GetType(), message.XxMessage) } } diff --git a/network/message/parse/partition_test.go b/network/message/parse/partition_test.go index 9351a1e77b7e80dfc74938fefe01203dd4f0b705..7c063f2d222d9171416a7d0366325dd464da7475 100644 --- a/network/message/parse/partition_test.go +++ b/network/message/parse/partition_test.go @@ -66,7 +66,7 @@ func TestPartitioner_Partition(t *testing.T) { storeSession := storage.InitTestingSession(t) p := NewPartitioner(len(ipsumTestStr), storeSession) - _, _, err := p.Partition(&id.DummyUser, message.Text, + _, _, err := p.Partition(&id.DummyUser, message.XxMessage, netTime.Now(), []byte(ipsumTestStr)) if err != nil { t.Error(err) @@ -94,7 +94,7 @@ func TestPartitioner_HandleFirstPartition(t *testing.T) { storeSession := storage.InitTestingSession(t) p := NewPartitioner(len(ipsumTestStr), storeSession) - m := newFirstMessagePart(message.Text, 1107, 1, netTime.Now(), []byte(ipsumTestStr)) + m := newFirstMessagePart(message.XxMessage, 1107, 1, netTime.Now(), []byte(ipsumTestStr)) _, _ = p.HandlePartition( &id.DummyUser, diff --git a/network/message/sendCmix.go b/network/message/sendCmix.go index 173fb1f8c2bbb87eb56d48472a737aff950300f9..a7db6d51f80e38bad648ccff338dc68409d6cb05 100644 --- a/network/message/sendCmix.go +++ b/network/message/sendCmix.go @@ -27,6 +27,8 @@ import ( "gitlab.com/xx_network/primitives/id" "gitlab.com/xx_network/primitives/id/ephemeral" "gitlab.com/xx_network/primitives/netTime" + "gitlab.com/xx_network/primitives/rateLimiting" + "strings" "time" ) @@ -85,8 +87,8 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message, attempted = excludedRounds.NewSet() } - jww.INFO.Printf("Looking for round to send cMix message to %s "+ - "(msgDigest: %s)", recipient, msg.Digest()) + jww.INFO.Printf("[SendCMIX-%s]Looking for round to send cMix message to %s "+ + "(msgDigest: %s)", cmixParams.DebugTag, recipient, msg.Digest()) stream := rng.GetStream() defer stream.Close() @@ -98,18 +100,18 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message, for numRoundTries := uint(0); numRoundTries < cmixParams.RoundTries; numRoundTries++ { elapsed := netTime.Since(timeStart) - jww.TRACE.Printf("[SendCMIX] try %d, elapsed: %s", - numRoundTries, elapsed) + jww.TRACE.Printf("[SendCMIX-%s] try %d, elapsed: %s", + cmixParams.DebugTag, numRoundTries, elapsed) if elapsed > cmixParams.Timeout { - jww.INFO.Printf("No rounds to send to %s (msgDigest: %s) "+ - "were found before timeout %s", recipient, msg.Digest(), + jww.INFO.Printf("[SendCMIX-%s] No rounds to send to %s (msgDigest: %s) "+ + "were found before timeout %s", cmixParams.DebugTag, recipient, msg.Digest(), cmixParams.Timeout) return 0, ephemeral.Id{}, errors.New("Sending cmix message timed out") } if numRoundTries > 0 { - jww.INFO.Printf("Attempt %d to find round to send message "+ - "to %s (msgDigest: %s)", numRoundTries+1, recipient, + jww.INFO.Printf("[SendCMIX-%s] Attempt %d to find round to send message "+ + "to %s (msgDigest: %s)", cmixParams.DebugTag, numRoundTries+1, recipient, msg.Digest()) } @@ -117,13 +119,14 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message, remainingTime := cmixParams.Timeout - elapsed bestRound, err := instance.GetWaitingRounds().GetUpcomingRealtime(remainingTime, attempted, sendTimeBuffer) if err != nil { - jww.WARN.Printf("Failed to GetUpcomingRealtime (msgDigest: %s): %+v", msg.Digest(), err) + jww.WARN.Printf("[SendCMIX-%s] Failed to GetUpcomingRealtime "+ + "(msgDigest: %s): %+v", cmixParams.DebugTag, msg.Digest(), err) } if bestRound == nil { - jww.WARN.Printf("Best round on send is nil") + jww.WARN.Printf("[SendCMIX-%s], Best round on send is nil", cmixParams.DebugTag) continue } - jww.DEBUG.Printf("[sendCMIX] bestRound: %v", bestRound) + jww.TRACE.Printf("[SendCMIX-%s] bestRound: %v", cmixParams.DebugTag, bestRound) // add the round on to the list of attempted, so it is not tried again attempted.Insert(bestRound.GetRoundId()) @@ -138,19 +141,21 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message, } } if containsBlacklisted { - jww.WARN.Printf("Round %d contains blacklisted node, skipping...", bestRound.ID) + jww.WARN.Printf("[SendCMIX-%s]Round %d contains blacklisted node, "+ + "skipping...", cmixParams.DebugTag, bestRound.ID) continue } // Retrieve host and key information from round firstGateway, roundKeys, err := processRound(instance, session, nodeRegistration, bestRound, recipient.String(), msg.Digest()) if err != nil { - jww.WARN.Printf("SendCmix failed to process round (will retry): %v", err) + jww.WARN.Printf("[SendCMIX-%s]SendCmix failed to process round"+ + " (will retry): %v", cmixParams.DebugTag, err) continue } - jww.DEBUG.Printf("[sendCMIX] round %v processed, firstGW: %s", - bestRound, firstGateway) + jww.TRACE.Printf("[SendCMIX-%s]round %v processed, firstGW: %s", + cmixParams.DebugTag, bestRound, firstGateway) // Build the messages to send @@ -160,9 +165,9 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message, return 0, ephemeral.Id{}, err } - jww.INFO.Printf("[sendCMIX] Sending to EphID %d (%s), "+ + jww.INFO.Printf("[SendCMIX-%s] Sending to EphID %d (%s), "+ "on round %d (msgDigest: %s, ecrMsgDigest: %s) "+ - "via gateway %s", + "via gateway %s", cmixParams.DebugTag, ephID.Int64(), recipient, bestRound.ID, msg.Digest(), encMsg.Digest(), firstGateway.String()) @@ -171,7 +176,7 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message, timeout time.Duration) (interface{}, error) { wrappedMsg.Target = target.Marshal() - jww.TRACE.Printf("[sendCMIX] sendFunc %s", host) + jww.TRACE.Printf("[SendCMIX-%s]sendFunc %s", cmixParams.DebugTag, host) timeout = calculateSendTimeout(bestRound, maxTimeout) // Use the smaller of the two timeout durations calculatedTimeout := calculateSendTimeout(bestRound, maxTimeout) @@ -182,25 +187,26 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message, //send the message result, err := comms.SendPutMessage(host, wrappedMsg, timeout) - jww.TRACE.Printf("[sendCMIX] sendFunc %s putmsg", host) + jww.TRACE.Printf("[SendCMIX-%s]sendFunc %s putmsg", cmixParams.DebugTag, host) if err != nil { // fixme: should we provide as a slice the whole topology? err := handlePutMessageError(firstGateway, instance, session, nodeRegistration, recipient.String(), bestRound, err) - jww.TRACE.Printf("[sendCMIX] sendFunc %s err %+v", - host, err) + jww.TRACE.Printf("[SendCMIX-%s] sendFunc %s err %+v", + cmixParams.DebugTag, host, err) return result, errors.WithMessagef(err, "SendCmix %s", unrecoverableError) } return result, err } - jww.DEBUG.Printf("[sendCMIX] sendToPreferred %s", firstGateway) + jww.TRACE.Printf("[SendCMIX-%s] sendToPreferred %s", + cmixParams.DebugTag, firstGateway) result, err := sender.SendToPreferred( []*id.ID{firstGateway}, sendFunc, stop, cmixParams.SendTimeout) - jww.DEBUG.Printf("[sendCMIX] sendToPreferred %s returned", - firstGateway) + jww.DEBUG.Printf("[SendCMIX-%s] sendToPreferred %s returned", + cmixParams.DebugTag, firstGateway) // Exit if the thread has been stopped if stoppable.CheckErr(err) { @@ -209,8 +215,15 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message, // if the comm errors or the message fails to send, continue retrying. if err != nil { - jww.ERROR.Printf("SendCmix failed to send to EphID %d (%s) on "+ - "round %d, trying a new round: %+v", ephID.Int64(), recipient, + if strings.Contains(err.Error(), rateLimiting.ClientRateLimitErr) { + jww.ERROR.Printf("[SendCMIX-%s] SendCmix failed to send to EphID %d (%s) on "+ + "round %d: %+v", cmixParams.DebugTag, ephID.Int64(), recipient, + bestRound.ID, err) + return 0, ephemeral.Id{}, err + } + + jww.ERROR.Printf("[SendCMIX-%s] SendCmix failed to send to EphID %d (%s) on "+ + "round %d, trying a new round: %+v", cmixParams.DebugTag, ephID.Int64(), recipient, bestRound.ID, err) continue } @@ -218,9 +231,9 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message, // Return if it sends properly gwSlotResp := result.(*pb.GatewaySlotResponse) if gwSlotResp.Accepted { - m := fmt.Sprintf("Successfully sent to EphID %v "+ + m := fmt.Sprintf("[SendCMIX-%s] Successfully sent to EphID %v "+ "(source: %s) in round %d (msgDigest: %s), "+ - "elapsed: %s numRoundTries: %d", ephID.Int64(), + "elapsed: %s numRoundTries: %d", cmixParams.DebugTag, ephID.Int64(), recipient, bestRound.ID, msg.Digest(), elapsed, numRoundTries) jww.INFO.Print(m) @@ -228,9 +241,9 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message, onSend(1, session) return id.Round(bestRound.ID), ephID, nil } else { - jww.FATAL.Panicf("Gateway %s returned no error, but failed "+ + jww.FATAL.Panicf("[SendCMIX-%s] Gateway %s returned no error, but failed "+ "to accept message when sending to EphID %d (%s) on round %d", - firstGateway, ephID.Int64(), recipient, bestRound.ID) + cmixParams.DebugTag, firstGateway, ephID.Int64(), recipient, bestRound.ID) } } diff --git a/network/message/sendCmixUtils.go b/network/message/sendCmixUtils.go index 15674cd2f0de004d306e44ebc9e1280845fedfa5..ae2344415338685af084a81744e941691854415c 100644 --- a/network/message/sendCmixUtils.go +++ b/network/message/sendCmixUtils.go @@ -154,12 +154,11 @@ func buildSlotMessage(msg format.Message, recipient *id.ID, target *id.ID, } // Set the identity fingerprint + ifp := fingerprint.IdentityFP(msg.GetContents(), preimage) msg.SetIdentityFP(ifp) - jww.INFO.Printf(" Sending to %s with preimage %v, ifp: %v, contents: %v", recipient, preimage, ifp, msg.GetContents()) - // Encrypt the message salt := make([]byte, 32) _, err = stream.Read(salt) diff --git a/network/message/sendCmix_test.go b/network/message/sendCmix_test.go index 984e5cd2d01d4f4e687b72f6e8217c9440c980cd..8ef85711c6d333d909b5f633994f47a4cc7caf46 100644 --- a/network/message/sendCmix_test.go +++ b/network/message/sendCmix_test.go @@ -90,7 +90,7 @@ func Test_attemptSendCmix(t *testing.T) { t.Errorf("Failed to load a key for testing: %v", err) } rnd := ds.NewRound(ri, pubKey, nil) - inst.GetWaitingRounds().Insert(rnd) + inst.GetWaitingRounds().Insert([]*ds.Round{rnd}, nil) i := internal.Internal{ Session: sess1, Switchboard: sw, diff --git a/network/message/sendManyCmix.go b/network/message/sendManyCmix.go index 58edff8076be9e365f0c4873281c78141136f68c..29e766edd90738a3e0f89afa8832c48f109b2f68 100644 --- a/network/message/sendManyCmix.go +++ b/network/message/sendManyCmix.go @@ -75,22 +75,22 @@ func sendManyCmixHelper(sender *gateway.Sender, recipientString, msgDigests := messageListToStrings(msgs) - jww.INFO.Printf("Looking for round to send cMix messages to [%s] "+ - "(msgDigest: %s)", recipientString, msgDigests) + jww.INFO.Printf("[SendManyCMIX-%s]Looking for round to send cMix messages to [%s] "+ + "(msgDigest: %s)", param.DebugTag, recipientString, msgDigests) for numRoundTries := uint(0); numRoundTries < param.RoundTries; numRoundTries++ { elapsed := netTime.Since(timeStart) if elapsed > param.Timeout { - jww.INFO.Printf("No rounds to send to %s (msgDigest: %s) were found "+ - "before timeout %s", recipientString, msgDigests, param.Timeout) + jww.INFO.Printf("[SendManyCMIX-%s]No rounds to send to %s (msgDigest: %s) were found "+ + "before timeout %s", param.DebugTag, recipientString, msgDigests, param.Timeout) return 0, []ephemeral.Id{}, errors.New("sending cMix message timed out") } if numRoundTries > 0 { - jww.INFO.Printf("Attempt %d to find round to send message to %s "+ - "(msgDigest: %s)", numRoundTries+1, recipientString, msgDigests) + jww.INFO.Printf("[SendManyCMIX-%s]Attempt %d to find round to send message to %s "+ + "(msgDigest: %s)", param.DebugTag, numRoundTries+1, recipientString, msgDigests) } remainingTime := param.Timeout - elapsed @@ -116,8 +116,8 @@ func sendManyCmixHelper(sender *gateway.Sender, } } if containsBlacklisted { - jww.WARN.Printf("Round %d contains blacklisted node, skipping...", - bestRound.ID) + jww.WARN.Printf("[SendManyCMIX-%s]Round %d contains blacklisted node, skipping...", + param.DebugTag, bestRound.ID) continue } @@ -125,9 +125,9 @@ func sendManyCmixHelper(sender *gateway.Sender, firstGateway, roundKeys, err := processRound(instance, session, nodeRegistration, bestRound, recipientString, msgDigests) if err != nil { - jww.INFO.Printf("error processing round: %v", err) - jww.WARN.Printf("SendManyCMIX failed to process round %d "+ - "(will retry): %+v", bestRound.ID, err) + jww.INFO.Printf("[SendManyCMIX-%s]error processing round: %v", param.DebugTag, err) + jww.WARN.Printf("[SendManyCMIX-%s]SendManyCMIX failed to process round %d "+ + "(will retry): %+v", param.DebugTag, bestRound.ID, err) continue } @@ -142,7 +142,7 @@ func sendManyCmixHelper(sender *gateway.Sender, bestRound, roundKeys, param) if err != nil { stream.Close() - jww.INFO.Printf("error building slot received: %v", err) + jww.INFO.Printf("[SendManyCMIX-%s]error building slot received: %v", param.DebugTag, err) return 0, []ephemeral.Id{}, errors.Errorf("failed to build "+ "slot message for %s: %+v", msg.Recipient, err) } @@ -154,8 +154,8 @@ func sendManyCmixHelper(sender *gateway.Sender, ephemeralIDsString := ephemeralIdListToString(ephemeralIDs) encMsgsDigest := messagesToDigestString(encMsgs) - jww.INFO.Printf("Sending to EphIDs [%s] (%s) on round %d, "+ - "(msgDigest: %s, ecrMsgDigest: %s) via gateway %s", + jww.INFO.Printf("[SendManyCMIX-%s]Sending to EphIDs [%s] (%s) on round %d, "+ + "(msgDigest: %s, ecrMsgDigest: %s) via gateway %s", param.DebugTag, ephemeralIDsString, recipientString, bestRound.ID, msgDigests, encMsgsDigest, firstGateway) @@ -198,10 +198,10 @@ func sendManyCmixHelper(sender *gateway.Sender, // If the comm errors or the message fails to send, continue retrying if err != nil { if !strings.Contains(err.Error(), unrecoverableError) { - jww.ERROR.Printf("SendManyCMIX failed to send to EphIDs [%s] "+ + jww.ERROR.Printf("[SendManyCMIX-%s]SendManyCMIX failed to send to EphIDs [%s] "+ "(sources: %s) on round %d, trying a new round %+v", - ephemeralIDsString, recipientString, bestRound.ID, err) - jww.INFO.Printf("error received, continuing: %v", err) + param.DebugTag, ephemeralIDsString, recipientString, bestRound.ID, err) + jww.INFO.Printf("[SendManyCMIX-%s]error received, continuing: %v", param.DebugTag, err) continue } jww.INFO.Printf("error received: %v", err) @@ -211,8 +211,8 @@ func sendManyCmixHelper(sender *gateway.Sender, // Return if it sends properly gwSlotResp := result.(*pb.GatewaySlotResponse) if gwSlotResp.Accepted { - m := fmt.Sprintf("Successfully sent to EphIDs %s (sources: [%s]) "+ - "in round %d", ephemeralIDsString, recipientString, bestRound.ID) + m := fmt.Sprintf("[SendManyCMIX-%s]Successfully sent to EphIDs %s (sources: [%s]) "+ + "in round %d", param.DebugTag, ephemeralIDsString, recipientString, bestRound.ID) jww.INFO.Print(m) events.Report(1, "MessageSendMany", "Metric", m) onSend(uint32(len(msgs)), session) diff --git a/network/message/sendManyCmix_test.go b/network/message/sendManyCmix_test.go index 476004edd18f481692d70794b3c22d3d8fa79b17..7ac3b99c3e0e121d8478a6162333741cfb411a29 100644 --- a/network/message/sendManyCmix_test.go +++ b/network/message/sendManyCmix_test.go @@ -90,7 +90,7 @@ func Test_attemptSendManyCmix(t *testing.T) { t.Errorf("Failed to load a key for testing: %v", err) } rnd := ds.NewRound(ri, pubKey, nil) - inst.GetWaitingRounds().Insert(rnd) + inst.GetWaitingRounds().Insert([]*ds.Round{rnd}, nil) i := internal.Internal{ Session: sess1, Switchboard: sw, diff --git a/network/node/register.go b/network/node/register.go index 3e19eb6750aa7bd040e12625ef1cf3bb0852ed0a..4336575dee390d0b948b914595d9bb15cfafd5bd 100644 --- a/network/node/register.go +++ b/network/node/register.go @@ -272,8 +272,8 @@ func requestKey(sender *gateway.Sender, comms RegisterNodeCommsInterface, // Verify the HMAC h.Reset() if !registration.VerifyClientHMAC(sessionKey.Bytes(), keyResponse.EncryptedClientKey, - h, keyResponse.EncryptedClientKeyHMAC) { - return nil, nil, 0, errors.WithMessagef(err, "Failed to verify client HMAC") + opts.Hash.New, keyResponse.EncryptedClientKeyHMAC) { + return nil, nil, 0, errors.New("Failed to verify client HMAC") } // Decrypt the client key diff --git a/network/rounds/check.go b/network/rounds/check.go index 3b9eb2d2849400d2fea2227373839cbc0def44e6..295a63f0abdb607660c0e6bc6865e7d026d608f6 100644 --- a/network/rounds/check.go +++ b/network/rounds/check.go @@ -56,6 +56,11 @@ func serializeRound(roundId id.Round) []byte { func (m *Manager) GetMessagesFromRound(roundID id.Round, identity reception.IdentityUse) { ri, err := m.Instance.GetRound(roundID) if err != nil || m.params.ForceHistoricalRounds { + if m.params.RealtimeOnly { + jww.WARN.Printf("Skipping round %d because it is not in ram and we are realtime only mode", + roundID) + return + } if m.params.ForceHistoricalRounds { jww.WARN.Printf("Forcing use of historical rounds for round ID %d.", roundID) @@ -80,11 +85,14 @@ func (m *Manager) GetMessagesFromRound(roundID id.Round, identity reception.Iden "up messages via in ram lookup", roundID, identity.EphId.Int64(), identity.Source) //store the round as an unretreived round - err = m.Session.UncheckedRounds().AddRound(roundID, ri, - identity.Source, identity.EphId) - if err != nil { - jww.FATAL.Panicf("Failed to denote Unchecked Round for round %d", roundID) + if !m.params.RealtimeOnly { + err = m.Session.UncheckedRounds().AddRound(roundID, ri, + identity.Source, identity.EphId) + if err != nil { + jww.FATAL.Panicf("Failed to denote Unchecked Round for round %d", roundID) + } } + // If found, send to Message Retrieval Workers m.lookupRoundMessages <- roundLookup{ roundInfo: ri, diff --git a/network/rounds/manager.go b/network/rounds/manager.go index c695cd7a8c78be622769a4388a669eda5757b304..f220583de8de98ab706b48db3a2b24d6a6168078 100644 --- a/network/rounds/manager.go +++ b/network/rounds/manager.go @@ -58,9 +58,12 @@ func (m *Manager) StartProcessors() stoppable.Stoppable { } // Start the periodic unchecked round worker - stopper := stoppable.NewSingle("UncheckRound") - go m.processUncheckedRounds(m.params.UncheckRoundPeriod, backOffTable, stopper) - multi.Add(stopper) + if !m.params.RealtimeOnly{ + stopper := stoppable.NewSingle("UncheckRound") + go m.processUncheckedRounds(m.params.UncheckRoundPeriod, backOffTable, stopper) + multi.Add(stopper) + } + return multi } diff --git a/network/rounds/remoteFilters.go b/network/rounds/remoteFilters.go index 7a434be073e51118d22adae682c27d2e3ad83b50..6753c4110716e83d08b55eb2f585b4e82ada1180 100644 --- a/network/rounds/remoteFilters.go +++ b/network/rounds/remoteFilters.go @@ -11,7 +11,6 @@ import ( jww "github.com/spf13/jwalterweatherman" bloom "gitlab.com/elixxir/bloomfilter" "gitlab.com/elixxir/client/interfaces" - "gitlab.com/elixxir/client/storage/reception" "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/xx_network/primitives/id" ) @@ -48,37 +47,3 @@ func (rf *RemoteFilter) FirstRound() id.Round { func (rf *RemoteFilter) LastRound() id.Round { return id.Round(rf.data.FirstRound + uint64(rf.data.RoundRange)) } - -// ValidFilterRange calculates which of the returned filters are valid for the identity -func ValidFilterRange(identity reception.IdentityUse, filters *mixmessages.ClientBlooms) (startIdx int, endIdx int, outOfBounds bool) { - outOfBounds = false - - firstElementTS := filters.FirstTimestamp - - identityStart := identity.StartValid.UnixNano() - identityEnd := identity.EndValid.UnixNano() - - startIdx = int((identityStart - firstElementTS) / filters.Period) - if startIdx < 0 { - startIdx = 0 - } - - if startIdx > len(filters.Filters)-1 { - outOfBounds = true - return startIdx, endIdx, outOfBounds - } - - endIdx = int((identityEnd - firstElementTS) / filters.Period) - if endIdx < 0 { - outOfBounds = true - return startIdx, endIdx, outOfBounds - } - - if endIdx > len(filters.Filters)-1 { - endIdx = len(filters.Filters) - 1 - } - - // Add 1 to the end index so that it follows Go's convention; the last index - // is exclusive to the range - return startIdx, endIdx + 1, outOfBounds -} diff --git a/network/rounds/remoteFilters_test.go b/network/rounds/remoteFilters_test.go index a04d7f51c7f580402744fef7a5b1f128d5f23332..51d26973d9f024142ed89b04e57d8f7117992268 100644 --- a/network/rounds/remoteFilters_test.go +++ b/network/rounds/remoteFilters_test.go @@ -11,15 +11,12 @@ import ( jww "github.com/spf13/jwalterweatherman" bloom "gitlab.com/elixxir/bloomfilter" "gitlab.com/elixxir/client/interfaces" - "gitlab.com/elixxir/client/storage/reception" "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/xx_network/comms/connect" "gitlab.com/xx_network/primitives/id" - "gitlab.com/xx_network/primitives/id/ephemeral" "os" "reflect" "testing" - "time" ) func TestMain(m *testing.M) { @@ -101,113 +98,4 @@ func TestRemoteFilter_FirstLastRound(t *testing.T) { "\n\tExpected: %v\n\tReceived: %v", receivedLastRound, firstRound+uint64(roundRange)) } -} - -// In bounds test -func TestValidFilterRange(t *testing.T) { - firstRound := uint64(25) - roundRange := uint32(75) - testFilter, err := bloom.InitByParameters(interfaces.BloomFilterSize, - interfaces.BloomFilterHashes) - if err != nil { - t.Fatalf("GetFilter error: "+ - "Cannot initialize bloom filter for setup: %v", err) - } - - data, err := testFilter.MarshalBinary() - if err != nil { - t.Fatalf("GetFilter error: "+ - "Cannot marshal filter for setup: %v", err) - } - - // Construct an in bounds value - expectedEphID := ephemeral.Id{1, 2, 3, 4, 5, 6, 7, 8} - requestGateway := id.NewIdFromString(ReturningGateway, id.Gateway, t) - iu := reception.IdentityUse{ - Identity: reception.Identity{ - EphId: expectedEphID, - Source: requestGateway, - StartValid: time.Now().Add(-12 * time.Hour), - EndValid: time.Now().Add(24 * time.Hour), - }, - } - - bloomFilter := &mixmessages.ClientBloom{ - Filter: data, - FirstRound: firstRound, - RoundRange: roundRange, - } - - // Fix for test on Windows machines: provides extra buffer between - // time.Now() for the reception.Identity and the mixmessages.ClientBlooms - time.Sleep(time.Millisecond) - - msg := &mixmessages.ClientBlooms{ - Period: int64(12 * time.Hour), - FirstTimestamp: time.Now().UnixNano(), - Filters: []*mixmessages.ClientBloom{bloomFilter}, - } - - start, end, outOfBounds := ValidFilterRange(iu, msg) - if outOfBounds { - t.Errorf("ValidFilterRange error: " + - "Range should not be out of bounds") - } - - if start != 0 && end != 1 { - t.Errorf("ValidFilterRange error: "+ - "Unexpected indices returned. "+ - "\n\tExpected start: %v\n\tReceived start: %v"+ - "\n\tExpected end: %v\n\tReceived end: %v", 0, start, 1, end) - } - -} - -// out of bounds test -func TestValidFilterRange_OutBounds(t *testing.T) { - firstRound := uint64(25) - roundRange := uint32(75) - testFilter, err := bloom.InitByParameters(interfaces.BloomFilterSize, - interfaces.BloomFilterHashes) - if err != nil { - t.Fatalf("GetFilter error: "+ - "Cannot initialize bloom filter for setup: %v", err) - } - - data, err := testFilter.MarshalBinary() - if err != nil { - t.Fatalf("GetFilter error: "+ - "Cannot marshal filter for setup: %v", err) - } - - // Construct an in bounds value - expectedEphID := ephemeral.Id{1, 2, 3, 4, 5, 6, 7, 8} - requestGateway := id.NewIdFromString(ReturningGateway, id.Gateway, t) - iu := reception.IdentityUse{ - Identity: reception.Identity{ - EphId: expectedEphID, - Source: requestGateway, - StartValid: time.Now().Add(-24 * time.Hour), - EndValid: time.Now().Add(-36 * time.Hour), - }, - } - - bloomFilter := &mixmessages.ClientBloom{ - Filter: data, - FirstRound: firstRound, - RoundRange: roundRange, - } - - msg := &mixmessages.ClientBlooms{ - Period: int64(12 * time.Hour), - FirstTimestamp: time.Now().UnixNano(), - Filters: []*mixmessages.ClientBloom{bloomFilter}, - } - - _, _, outOfBounds := ValidFilterRange(iu, msg) - if !outOfBounds { - t.Errorf("ValidFilterRange error: " + - "Range should be out of bounds") - } - -} +} \ No newline at end of file diff --git a/network/rounds/retrieve.go b/network/rounds/retrieve.go index 1db833b43ccf1b415efe44b1d0f10aefe3aa2bed..00f6675375243ef4025ad86f87751b578f185c1c 100644 --- a/network/rounds/retrieve.go +++ b/network/rounds/retrieve.go @@ -49,12 +49,15 @@ func (m *Manager) processMessageRetrieval(comms messageRetrievalComms, case rl := <-m.lookupRoundMessages: ri := rl.roundInfo jww.DEBUG.Printf("Checking for messages in round %d", ri.ID) - err := m.Session.UncheckedRounds().AddRound(id.Round(ri.ID), ri, - rl.identity.Source, rl.identity.EphId) - if err != nil { - jww.FATAL.Panicf("Failed to denote Unchecked Round for round %d", id.Round(ri.ID)) + if !m.params.RealtimeOnly{ + err := m.Session.UncheckedRounds().AddRound(id.Round(ri.ID), ri, + rl.identity.Source, rl.identity.EphId) + if err != nil { + jww.FATAL.Panicf("Failed to denote Unchecked Round for round %d", id.Round(ri.ID)) + } } + // Convert gateways in round to proper ID format gwIds := make([]*id.ID, len(ri.Topology)) for i, idBytes := range ri.Topology { @@ -73,7 +76,7 @@ func (m *Manager) processMessageRetrieval(comms messageRetrievalComms, // messages first, randomize other members of the team var rndBytes [32]byte stream := m.Rng.GetStream() - _, err = stream.Read(rndBytes[:]) + _, err := stream.Read(rndBytes[:]) stream.Close() if err != nil { jww.FATAL.Panicf("Failed to randomize shuffle in round %d "+ @@ -129,12 +132,15 @@ func (m *Manager) processMessageRetrieval(comms messageRetrievalComms, m.messageBundles <- bundle jww.DEBUG.Printf("Removing round %d from unchecked store", ri.ID) - err = m.Session.UncheckedRounds().Remove(id.Round(ri.ID), rl.identity.Source, rl.identity.EphId) - if err != nil { - jww.ERROR.Printf("Could not remove round %d "+ - "from unchecked rounds store: %v", ri.ID, err) + if !m.params.RealtimeOnly{ + err = m.Session.UncheckedRounds().Remove(id.Round(ri.ID), rl.identity.Source, rl.identity.EphId) + if err != nil { + jww.ERROR.Printf("Could not remove round %d "+ + "from unchecked rounds store: %v", ri.ID, err) + } } + } } @@ -190,12 +196,14 @@ func (m *Manager) getMessagesFromGateway(roundID id.Round, " in round %d. This happening every once in a while is normal,"+ " but can be indicative of a problem if it is consistent", m.TransmissionID, roundID) - - err = m.Session.UncheckedRounds().Remove(roundID, identity.Source, identity.EphId) - if err != nil { - jww.FATAL.Panicf("Failed to remove round %d: %+v", roundID, err) + if m.params.RealtimeOnly{ + err = m.Session.UncheckedRounds().Remove(roundID, identity.Source, identity.EphId) + if err != nil { + jww.ERROR.Printf("Failed to remove round %d: %+v", roundID, err) + } } + return message.Bundle{}, nil } diff --git a/network/rounds/unchecked.go b/network/rounds/unchecked.go index 0618a95c75d07faa2bdc030348bb0c27b68fca84..a73bf6eb24eef97f0fa47a693d045fcd18a38bae 100644 --- a/network/rounds/unchecked.go +++ b/network/rounds/unchecked.go @@ -115,8 +115,8 @@ func (m *Manager) processUncheckedRounds(checkInterval time.Duration, backoffTab func isRoundCheckDue(tries uint64, ts time.Time, backoffTable [cappedTries]time.Duration) bool { now := netTime.Now() - if tries > cappedTries { - tries = cappedTries + if tries >= uint64(len(backoffTable)) { + tries = uint64(len(backoffTable)) - 1 } roundCheckTime := ts.Add(backoffTable[tries]) diff --git a/single/reception.go b/single/reception.go index e1aa8e08d3341e6bf3bdb4d5354d6e4cf90dfda6..b4aa68f8341da6dba735639883239fcc1d0a97da 100644 --- a/single/reception.go +++ b/single/reception.go @@ -33,47 +33,48 @@ func (m *Manager) receiveTransmissionHandler(rawMessages chan message.Receive, case msg := <-rawMessages: jww.TRACE.Printf("Received CMIX message; checking if it is a " + "single-use transmission.") - - // Check if message is a single-use transmit message - cmixMsg, err := format.Unmarshal(msg.Payload) - if err != nil { - jww.ERROR.Printf("Could not unmarshal msg: %s", - err.Error()) - continue - } - if fp != cmixMsg.GetKeyFP() { - // If the verification fails, then ignore the message as it is - // likely garbled or for a different protocol - jww.TRACE.Print("Failed to read single-use CMIX message: " + - "fingerprint verification failed.") - continue - } - - // Denote that the message is not garbled - jww.DEBUG.Printf("Received single-use transmission message.") - m.store.GetGarbledMessages().Remove(cmixMsg) - - // Handle message - payload, c, err := m.processTransmission(cmixMsg, fp) - if err != nil { - jww.WARN.Printf("Failed to read single-use CMIX message: %+v", - err) - continue - } - jww.DEBUG.Printf("Successfully processed single-use transmission message.") - - // Lookup the registered callback for the message's tag fingerprint - callback, err := m.callbackMap.getCallback(c.tagFP) - if err != nil { - jww.WARN.Printf("Failed to find module to pass single-use "+ - "payload: %+v", err) - continue - } - - jww.DEBUG.Printf("Calling single-use callback with tag "+ - "fingerprint %s.", c.tagFP) - - go callback(payload, c) + go func(m *Manager, msg message.Receive) { + // Check if message is a single-use transmit message + cmixMsg, err := format.Unmarshal(msg.Payload) + if err != nil { + jww.ERROR.Printf("Could not unmarshal msg: %s", + err.Error()) + return + } + if fp != cmixMsg.GetKeyFP() { + // If the verification fails, then ignore the message as it is + // likely garbled or for a different protocol + jww.TRACE.Print("Failed to read single-use CMIX message: " + + "fingerprint verification failed.") + return + } + + // Denote that the message is not garbled + jww.DEBUG.Printf("Received single-use transmission message.") + m.store.GetGarbledMessages().Remove(cmixMsg) + + // Handle message + payload, c, err := m.processTransmission(cmixMsg, fp) + if err != nil { + jww.WARN.Printf("Failed to read single-use CMIX message: %+v", + err) + return + } + jww.DEBUG.Printf("Successfully processed single-use transmission message.") + + // Lookup the registered callback for the message's tag fingerprint + callback, err := m.callbackMap.getCallback(c.tagFP) + if err != nil { + jww.WARN.Printf("Failed to find module to pass single-use "+ + "payload: %+v", err) + return + } + + jww.DEBUG.Printf("Calling single-use callback with tag "+ + "fingerprint %s.", c.tagFP) + + callback(payload, c) + }(m, msg) } } } @@ -113,11 +114,9 @@ func (m *Manager) processTransmission(msg format.Message, c := NewContact(payload.GetRID(transmitMsg.GetPubKey(grp)), transmitMsg.GetPubKey(grp), dhKey, payload.GetTagFP(), payload.GetMaxParts()) - jww.INFO.Printf("generated by singe use receiver reception id for single use: %s, "+ - "ephId: %v, pubkey: %x, msg: %s", - c.partner, "unknown:", - transmitMsg.GetPubKey(grp).Bytes(), - msg) + jww.INFO.Printf("Generated by singe use receiver reception id for single use: %s, "+ + "ephId: %v, pubkey: %x", + c.partner, "unknown:", transmitMsg.GetPubKey(grp).Bytes()) return payload.GetContents(), c, nil } diff --git a/single/response.go b/single/response.go index a5ab3cdb1baee704b214ff694b783c10182471ac..4486690e78e29185443d9bfd1642be78b2064b39 100644 --- a/single/response.go +++ b/single/response.go @@ -74,7 +74,9 @@ func (m *Manager) respondSingleUse(partner Contact, payload []byte, go func() { defer wg.Done() // Send Message - round, ephID, err := m.net.SendCMIX(cmixMsgFunc, partner.partner, params.GetDefaultCMIX()) + p := params.GetDefaultCMIX() + p.DebugTag = "single.Response" + round, ephID, err := m.net.SendCMIX(cmixMsgFunc, partner.partner, p) if err != nil { jww.ERROR.Printf("Failed to send single-use response CMIX "+ "message part %d: %+v", j, err) diff --git a/single/transmission.go b/single/transmission.go index 30f2f6c90a3cc5e5e8f6ece6cb459fc15ec1aa57..436420a7f3711d7bbb4a5271c207779f64bd8704 100644 --- a/single/transmission.go +++ b/single/transmission.go @@ -13,7 +13,6 @@ import ( jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/interfaces" "gitlab.com/elixxir/client/interfaces/params" - "gitlab.com/elixxir/client/interfaces/utility" "gitlab.com/elixxir/client/storage/reception" ds "gitlab.com/elixxir/comms/network/dataStructures" contact2 "gitlab.com/elixxir/crypto/contact" @@ -51,8 +50,7 @@ func (m *Manager) TransmitSingleUse(partner contact2.Contact, payload []byte, rngReader := m.rng.GetStream() defer rngReader.Close() - return m.transmitSingleUse(partner, payload, tag, maxMsgs, rngReader, - callback, timeout, m.net.GetInstance().GetRoundEvents()) + return m.transmitSingleUse(partner, payload, tag, maxMsgs, rngReader, callback, timeout) } // roundEvents interface allows custom round events to be passed in for testing. @@ -63,23 +61,24 @@ type roundEvents interface { // transmitSingleUse has the fields passed in for easier testing. func (m *Manager) transmitSingleUse(partner contact2.Contact, payload []byte, - tag string, MaxMsgs uint8, rng io.Reader, callback ReplyComm, timeout time.Duration, roundEvents roundEvents) error { + tag string, MaxMsgs uint8, rng io.Reader, callback ReplyComm, timeout time.Duration) error { // Get ephemeral ID address space size; this blocks until the address space // size is set for the first time addressSize := m.net.GetAddressSize() + timeStart := netTime.Now() // Create new CMIX message containing the transmission payload cmixMsg, dhKey, rid, ephID, err := m.makeTransmitCmixMessage(partner, - payload, tag, MaxMsgs, addressSize, timeout, netTime.Now(), rng) + payload, tag, MaxMsgs, addressSize, timeout, timeStart, rng) if err != nil { return errors.Errorf("failed to create new CMIX message: %+v", err) } + startValid := timeStart.Add(-2 * timeout) + endValid := timeStart.Add(2 * timeout) jww.DEBUG.Printf("Created single-use transmission CMIX message with new ID "+ - "%s and ephemeral ID %d", rid, ephID.Int64()) - - timeStart := netTime.Now() + "%s and EphID %d (Valid %s - %s)", rid, ephID.Int64(), startValid.String(), endValid.String()) // Add message state to map quitChan, quit, err := m.p.addState(rid, dhKey, MaxMsgs, callback, timeout) @@ -92,10 +91,10 @@ func (m *Manager) transmitSingleUse(partner contact2.Contact, payload []byte, EphId: ephID, Source: rid, AddressSize: addressSize, - End: timeStart.Add(2 * timeout), + End: endValid, ExtraChecks: interfaces.DefaultExtraChecks, - StartValid: timeStart.Add(-2 * timeout), - EndValid: timeStart.Add(2 * timeout), + StartValid: startValid, + EndValid: endValid, Ephemeral: true, }) if err != nil { @@ -116,11 +115,13 @@ func (m *Manager) transmitSingleUse(partner contact2.Contact, payload []byte, // Send Message jww.DEBUG.Printf("Sending single-use transmission CMIX "+ "message to %s.", partner.ID) - round, _, err := m.net.SendCMIX(cmixMsg, partner.ID, params.GetDefaultCMIX()) + p := params.GetDefaultCMIX() + p.DebugTag = "single.Transmit" + round, _, err := m.net.SendCMIX(cmixMsg, partner.ID, p) if err != nil { errorString := fmt.Sprintf("failed to send single-use transmission "+ "CMIX message: %+v", err) - jww.ERROR.Print(errorString) + jww.ERROR.Printf(errorString) // Exit the state timeout handler, delete the state from map, and // return an error on the callback @@ -137,40 +138,9 @@ func (m *Manager) transmitSingleUse(partner contact2.Contact, payload []byte, "message because the timeout handler quit.") return } - - // Update the timeout for the elapsed time - roundEventTimeout := timeout - netTime.Since(timeStart) - time.Millisecond - - // Check message delivery - sendResults := make(chan ds.EventReturn, 1) - roundEvents.AddRoundEventChan(round, sendResults, roundEventTimeout, - states.COMPLETED, states.FAILED) - - im := fmt.Sprintf("Sent single-use transmission CMIX "+ + jww.DEBUG.Printf("Sent single-use transmission CMIX "+ "message to %s and ephemeral ID %d on round %d.", partner.ID, ephID.Int64(), round) - jww.DEBUG.Print(im) - if m.client != nil { - m.client.ReportEvent(1, "SingleUse", "MessageSend", im) - } - - // Wait until the result tracking responds - success, numRoundFail, numTimeOut := utility.TrackResults(sendResults, 1) - if !success { - errorString := fmt.Sprintf("failed to send single-use transmission "+ - "message: %d round failures, %d round event time outs.", - numRoundFail, numTimeOut) - jww.ERROR.Print(errorString) - - // Exit the state timeout handler, delete the state from map, and - // return an error on the callback - quitChan <- struct{}{} - m.p.Lock() - delete(m.p.singleUse, *rid) - m.p.Unlock() - go callback(nil, errors.New(errorString)) - } - jww.DEBUG.Print("Tracked single-use transmission message round.") }() return nil diff --git a/single/transmission_test.go b/single/transmission_test.go index 548cefa7c0be402eda3671278a4234e6fb610672..15c557e60292021edab2252bd05ec10e5553d6a7 100644 --- a/single/transmission_test.go +++ b/single/transmission_test.go @@ -26,7 +26,7 @@ func TestManager_GetMaxTransmissionPayloadSize(t *testing.T) { m := newTestManager(0, false, t) cmixPrimeSize := m.store.Cmix().GetGroup().GetP().ByteLen() e2ePrimeSize := m.store.E2e().GetGroup().GetP().ByteLen() - expectedSize := 2*cmixPrimeSize - e2ePrimeSize - format.KeyFPLen - format.MacLen - format.RecipientIDLen - transmitPlMinSize - transmitMessageVersionSize-1 + expectedSize := 2*cmixPrimeSize - e2ePrimeSize - format.KeyFPLen - format.MacLen - format.RecipientIDLen - transmitPlMinSize - transmitMessageVersionSize - 1 testSize := m.GetMaxTransmissionPayloadSize() if expectedSize != testSize { @@ -50,8 +50,7 @@ func TestManager_transmitSingleUse(t *testing.T) { callback, callbackChan := createReplyComm() timeout := 15 * time.Millisecond - err := m.transmitSingleUse(partner, payload, tag, maxMsgs, prng, - callback, timeout, newTestRoundEvents(false)) + err := m.transmitSingleUse(partner, payload, tag, maxMsgs, prng, callback, timeout) if err != nil { t.Errorf("transmitSingleUse() returned an error: %+v", err) } @@ -93,7 +92,7 @@ func TestManager_transmitSingleUse_QuitChanError(t *testing.T) { timeout := 15 * time.Millisecond err := m.transmitSingleUse(partner, []byte{}, "testTag", 9, - rand.New(rand.NewSource(42)), callback, timeout, newTestRoundEvents(false)) + rand.New(rand.NewSource(42)), callback, timeout) if err != nil { t.Errorf("transmitSingleUse() returned an error: %+v", err) } @@ -125,7 +124,7 @@ func TestManager_transmitSingleUse_AddIdentityError(t *testing.T) { callback, callbackChan := createReplyComm() err := m.transmitSingleUse(partner, []byte{}, "testTag", 9, - rand.New(rand.NewSource(42)), callback, timeout, newTestRoundEvents(false)) + rand.New(rand.NewSource(42)), callback, timeout) if err != nil { t.Errorf("transmitSingleUse() returned an error: %+v", err) } @@ -158,7 +157,7 @@ func TestManager_transmitSingleUse_SendCMIXError(t *testing.T) { timeout := 15 * time.Millisecond err := m.transmitSingleUse(partner, []byte{}, "testTag", 9, - rand.New(rand.NewSource(42)), callback, timeout, newTestRoundEvents(false)) + rand.New(rand.NewSource(42)), callback, timeout) if err != nil { t.Errorf("transmitSingleUse() returned an error: %+v", err) } @@ -182,7 +181,7 @@ func TestManager_transmitSingleUse_MakeTransmitCmixMessageError(t *testing.T) { prng := rand.New(rand.NewSource(42)) payload := make([]byte, m.store.Cmix().GetGroup().GetP().ByteLen()) - err := m.transmitSingleUse(contact2.Contact{}, payload, "", 0, prng, nil, 0, nil) + err := m.transmitSingleUse(contact2.Contact{}, payload, "", 0, prng, nil, 0) if err == nil { t.Error("transmitSingleUse() did not return an error when the payload " + "is too large.") @@ -212,7 +211,7 @@ func TestManager_transmitSingleUse_AddStateError(t *testing.T) { m.p.singleUse[*rid] = newState(dhKey, maxMsgs, nil) err = m.transmitSingleUse(partner, payload, tag, maxMsgs, - rand.New(rand.NewSource(42)), callback, timeout, nil) + rand.New(rand.NewSource(42)), callback, timeout) if !check(err, "failed to add pending state") { t.Errorf("transmitSingleUse() failed to error when on adding state "+ "when the state already exists: %+v", err) @@ -232,8 +231,7 @@ func TestManager_transmitSingleUse_RoundTimeoutError(t *testing.T) { callback, callbackChan := createReplyComm() timeout := 15 * time.Millisecond - err := m.transmitSingleUse(partner, payload, "testTag", 8, prng, callback, - timeout, newTestRoundEvents(true)) + err := m.transmitSingleUse(partner, payload, "testTag", 8, prng, callback, timeout) if err != nil { t.Errorf("transmitSingleUse() returned an error: %+v", err) } @@ -242,7 +240,7 @@ func TestManager_transmitSingleUse_RoundTimeoutError(t *testing.T) { select { case results := <-callbackChan: - if results.payload != nil || !check(results.err, "round failures") { + if results.payload != nil || !check(results.err, "timed out") { t.Errorf("Callback did not return the correct error when it "+ "should have timed out.\npayload: %+v\nerror: %+v", results.payload, results.err) diff --git a/storage/auth/store.go b/storage/auth/store.go index addd3d138499ae09cfc5e9dc5d221d79d95d060a..fb9aa2b04be8875bae32de4c74752db3dd80ddee 100644 --- a/storage/auth/store.go +++ b/storage/auth/store.go @@ -261,6 +261,20 @@ func (s *Store) AddReceived(c contact.Contact, key *sidh.PublicKey) error { return nil } +// GetAllReceived returns all pending received contact requests from storage. +func (s *Store) GetAllReceived() []contact.Contact { + s.mux.RLock() + defer s.mux.RUnlock() + cList := make([]contact.Contact, 0, len(s.requests)) + for key := range s.requests { + r := s.requests[key] + if r.rt == Receive { + cList = append(cList, *r.receive) + } + } + return cList +} + // GetFingerprint can return either a private key or a sentRequest if the // fingerprint is found. If it returns a sentRequest, then it takes the lock to // ensure there is only one operator at a time. The user of the API must release @@ -389,7 +403,7 @@ func (s *Store) Done(partner *id.ID) { r.mux.Unlock() } -// delete is one of two calls after using a request. This one is to be used when +// Delete is one of two calls after using a request. This one is to be used when // the use is unsuccessful. It deletes all references to the request associated // with the passed partner, if it exists. It will allow any thread waiting on // access to continue. They should fail due to the deletion of the structure. @@ -404,16 +418,9 @@ func (s *Store) Delete(partner *id.ID) error { switch r.rt { case Sent: - delete(s.fingerprints, r.sent.fingerprint) - if err := r.sent.delete(); err != nil { - jww.FATAL.Panicf("Failed to delete sent request: %+v", err) - } - + s.deleteSentRequest(r) case Receive: - if err := util.DeleteContact(s.kv, r.receive.ID); err != nil { - jww.FATAL.Panicf("Failed to delete recieved request "+ - "contact: %+v", err) - } + s.deleteReceiveRequest(r) } delete(s.requests, *partner) @@ -424,3 +431,91 @@ func (s *Store) Delete(partner *id.ID) error { return nil } + +// DeleteAllRequests clears the request map and all associated storage objects +// containing request data. +func (s *Store) DeleteAllRequests() error { + s.mux.Lock() + defer s.mux.Unlock() + + for partnerId, req := range s.requests { + switch req.rt { + case Sent: + s.deleteSentRequest(req) + delete(s.requests, partnerId) + case Receive: + s.deleteReceiveRequest(req) + delete(s.requests, partnerId) + } + + } + + if err := s.save(); err != nil { + jww.FATAL.Panicf("Failed to store updated request map after "+ + "deleting all requests: %+v", err) + } + + return nil +} + +// DeleteSentRequests deletes all Sent requests from Store. +func (s *Store) DeleteSentRequests() error { + s.mux.Lock() + defer s.mux.Unlock() + + for partnerId, req := range s.requests { + switch req.rt { + case Sent: + s.deleteSentRequest(req) + delete(s.requests, partnerId) + case Receive: + continue + } + } + + if err := s.save(); err != nil { + jww.FATAL.Panicf("Failed to store updated request map after "+ + "deleting all sent requests: %+v", err) + } + + return nil +} + +// DeleteReceiveRequests deletes all Receive requests from Store. +func (s *Store) DeleteReceiveRequests() error { + s.mux.Lock() + defer s.mux.Unlock() + + for partnerId, req := range s.requests { + switch req.rt { + case Sent: + continue + case Receive: + s.deleteReceiveRequest(req) + delete(s.requests, partnerId) + } + } + + if err := s.save(); err != nil { + jww.FATAL.Panicf("Failed to store updated request map after "+ + "deleting all receive requests: %+v", err) + } + + return nil +} + +// deleteSentRequest is a helper function which deletes a Sent request from storage. +func (s *Store) deleteSentRequest(r *request) { + delete(s.fingerprints, r.sent.fingerprint) + if err := r.sent.delete(); err != nil { + jww.FATAL.Panicf("Failed to delete sent request: %+v", err) + } +} + +// deleteReceiveRequest is a helper function which deletes a Receive request from storage. +func (s *Store) deleteReceiveRequest(r *request) { + if err := util.DeleteContact(s.kv, r.receive.ID); err != nil { + jww.FATAL.Panicf("Failed to delete recieved request "+ + "contact: %+v", err) + } +} diff --git a/storage/auth/store_test.go b/storage/auth/store_test.go index b1c4bb92b95c98eadc23db6e66d1ab5e353efc51..f1f10414836eb6c37596593322d112dbcfcc9394 100644 --- a/storage/auth/store_test.go +++ b/storage/auth/store_test.go @@ -8,6 +8,7 @@ package auth import ( + "bytes" "github.com/cloudflare/circl/dh/sidh" sidhinterface "gitlab.com/elixxir/client/interfaces/sidh" util "gitlab.com/elixxir/client/storage/utility" @@ -23,6 +24,7 @@ import ( "io" "math/rand" "reflect" + "sort" "sync" "testing" ) @@ -764,6 +766,309 @@ func TestStore_Delete_RequestNotInMap(t *testing.T) { } } +// Unit test of Store.GetAllReceived. +func TestStore_GetAllReceived(t *testing.T) { + s, _, _ := makeTestStore(t) + numReceived := 10 + + expectContactList := make([]contact.Contact, 0, numReceived) + // Add multiple received contact requests + for i := 0; i < numReceived; i++ { + c := contact.Contact{ID: id.NewIdFromUInt(rand.Uint64(), id.User, t)} + rng := csprng.NewSystemRNG() + _, sidhPubKey := genSidhAKeys(rng) + + if err := s.AddReceived(c, sidhPubKey); err != nil { + t.Fatalf("AddReceived() returned an error: %+v", err) + } + + expectContactList = append(expectContactList, c) + } + + // Check that GetAllReceived returns all contacts + receivedContactList := s.GetAllReceived() + if len(receivedContactList) != numReceived { + t.Errorf("GetAllReceived did not return expected amount of contacts."+ + "\nExpected: %d"+ + "\nReceived: %d", numReceived, len(receivedContactList)) + } + + // Sort expected and received lists so that they are in the same order + // since extraction from a map does not maintain order + sort.Slice(expectContactList, func(i, j int) bool { + return bytes.Compare(expectContactList[i].ID.Bytes(), expectContactList[j].ID.Bytes()) == -1 + }) + sort.Slice(receivedContactList, func(i, j int) bool { + return bytes.Compare(receivedContactList[i].ID.Bytes(), receivedContactList[j].ID.Bytes()) == -1 + }) + + // Check validity of contacts + if !reflect.DeepEqual(expectContactList, receivedContactList) { + t.Errorf("GetAllReceived did not return expected contact list."+ + "\nExpected: %+v"+ + "\nReceived: %+v", expectContactList, receivedContactList) + } + +} + +// Tests that Store.GetAllReceived returns an empty list when there are no +// received requests. +func TestStore_GetAllReceived_EmptyList(t *testing.T) { + s, _, _ := makeTestStore(t) + + // Check that GetAllReceived returns all contacts + receivedContactList := s.GetAllReceived() + if len(receivedContactList) != 0 { + t.Errorf("GetAllReceived did not return expected amount of contacts."+ + "\nExpected: %d"+ + "\nReceived: %d", 0, len(receivedContactList)) + } + + // Add Sent and Receive requests + for i := 0; i < 10; i++ { + partnerID := id.NewIdFromUInt(rand.Uint64(), id.User, t) + rng := csprng.NewSystemRNG() + sidhPrivKey, sidhPubKey := genSidhAKeys(rng) + sr := &SentRequest{ + kv: s.kv, + partner: partnerID, + partnerHistoricalPubKey: s.grp.NewInt(1), + myPrivKey: s.grp.NewInt(2), + myPubKey: s.grp.NewInt(3), + mySidHPrivKeyA: sidhPrivKey, + mySidHPubKeyA: sidhPubKey, + fingerprint: format.Fingerprint{5}, + } + if err := s.AddSent(sr.partner, sr.partnerHistoricalPubKey, + sr.myPrivKey, sr.myPubKey, sr.mySidHPrivKeyA, + sr.mySidHPubKeyA, sr.fingerprint); err != nil { + t.Fatalf("AddSent() returned an error: %+v", err) + } + } + + // Check that GetAllReceived returns all contacts + receivedContactList = s.GetAllReceived() + if len(receivedContactList) != 0 { + t.Errorf("GetAllReceived did not return expected amount of contacts. "+ + "It may be pulling from Sent Requests."+ + "\nExpected: %d"+ + "\nReceived: %d", 0, len(receivedContactList)) + } + +} + +// Tests that Store.GetAllReceived returns only Sent requests when there +// are both Sent and Receive requests in Store. +func TestStore_GetAllReceived_MixSentReceived(t *testing.T) { + s, _, _ := makeTestStore(t) + numReceived := 10 + + // Add multiple received contact requests + for i := 0; i < numReceived; i++ { + // Add received request + c := contact.Contact{ID: id.NewIdFromUInt(rand.Uint64(), id.User, t)} + rng := csprng.NewSystemRNG() + _, sidhPubKey := genSidhAKeys(rng) + + if err := s.AddReceived(c, sidhPubKey); err != nil { + t.Fatalf("AddReceived() returned an error: %+v", err) + } + + // Add sent request + partnerID := id.NewIdFromUInt(rand.Uint64(), id.User, t) + sidhPrivKey, sidhPubKey := genSidhAKeys(rng) + sr := &SentRequest{ + kv: s.kv, + partner: partnerID, + partnerHistoricalPubKey: s.grp.NewInt(1), + myPrivKey: s.grp.NewInt(2), + myPubKey: s.grp.NewInt(3), + mySidHPrivKeyA: sidhPrivKey, + mySidHPubKeyA: sidhPubKey, + fingerprint: format.Fingerprint{5}, + } + if err := s.AddSent(sr.partner, sr.partnerHistoricalPubKey, + sr.myPrivKey, sr.myPubKey, sr.mySidHPrivKeyA, + sr.mySidHPubKeyA, sr.fingerprint); err != nil { + t.Fatalf("AddSent() returned an error: %+v", err) + } + } + + // Check that GetAllReceived returns all contacts + receivedContactList := s.GetAllReceived() + if len(receivedContactList) != numReceived { + t.Errorf("GetAllReceived did not return expected amount of contacts. "+ + "It may be pulling from Sent Requests."+ + "\nExpected: %d"+ + "\nReceived: %d", numReceived, len(receivedContactList)) + } + +} + +// Unit test. +func TestStore_DeleteReceiveRequests(t *testing.T) { + s, _, _ := makeTestStore(t) + c := contact.Contact{ID: id.NewIdFromUInt(rand.Uint64(), id.User, t)} + rng := csprng.NewSystemRNG() + _, sidhPubKey := genSidhAKeys(rng) + if err := s.AddReceived(c, sidhPubKey); err != nil { + t.Fatalf("AddReceived() returned an error: %+v", err) + } + if _, _, err := s.GetReceivedRequest(c.ID); err != nil { + t.Fatalf("GetReceivedRequest() returned an error: %+v", err) + } + + err := s.DeleteReceiveRequests() + if err != nil { + t.Fatalf("DeleteReceiveRequests returned an error: %+v", err) + } + + if s.requests[*c.ID] != nil { + t.Errorf("delete() failed to delete request for user %s.", c.ID) + } +} + +// Unit test. +func TestStore_DeleteSentRequests(t *testing.T) { + s, _, _ := makeTestStore(t) + partnerID := id.NewIdFromUInt(rand.Uint64(), id.User, t) + rng := csprng.NewSystemRNG() + sidhPrivKey, sidhPubKey := genSidhAKeys(rng) + sr := &SentRequest{ + kv: s.kv, + partner: partnerID, + partnerHistoricalPubKey: s.grp.NewInt(1), + myPrivKey: s.grp.NewInt(2), + myPubKey: s.grp.NewInt(3), + mySidHPrivKeyA: sidhPrivKey, + mySidHPubKeyA: sidhPubKey, + fingerprint: format.Fingerprint{5}, + } + if err := s.AddSent(sr.partner, sr.partnerHistoricalPubKey, + sr.myPrivKey, sr.myPubKey, sr.mySidHPrivKeyA, + sr.mySidHPubKeyA, sr.fingerprint); err != nil { + t.Fatalf("AddSent() returned an error: %+v", err) + } + + err := s.DeleteSentRequests() + if err != nil { + t.Fatalf("DeleteSentRequests returned an error: %+v", err) + } + + if s.requests[*sr.partner] != nil { + t.Errorf("delete() failed to delete request for user %s.", + sr.partner) + } + + if _, exists := s.fingerprints[sr.fingerprint]; exists { + t.Errorf("delete() failed to delete fingerprint for fp %v.", + sr.fingerprint) + } +} + +// Tests that DeleteSentRequests does not affect receive requests in map +func TestStore_DeleteSentRequests_ReceiveInMap(t *testing.T) { + s, _, _ := makeTestStore(t) + c := contact.Contact{ID: id.NewIdFromUInt(rand.Uint64(), id.User, t)} + rng := csprng.NewSystemRNG() + _, sidhPubKey := genSidhAKeys(rng) + if err := s.AddReceived(c, sidhPubKey); err != nil { + t.Fatalf("AddReceived() returned an error: %+v", err) + } + + err := s.DeleteSentRequests() + if err != nil { + t.Fatalf("DeleteSentRequests returned an error: %+v", err) + } + + if s.requests[*c.ID] == nil { + t.Fatalf("DeleteSentRequests removes receive requests!") + } + +} + +// Tests that DeleteReceiveRequests does not affect sent requests in map +func TestStore_DeleteReceiveRequests_SentInMap(t *testing.T) { + s, _, _ := makeTestStore(t) + partnerID := id.NewIdFromUInt(rand.Uint64(), id.User, t) + rng := csprng.NewSystemRNG() + sidhPrivKey, sidhPubKey := genSidhAKeys(rng) + sr := &SentRequest{ + kv: s.kv, + partner: partnerID, + partnerHistoricalPubKey: s.grp.NewInt(1), + myPrivKey: s.grp.NewInt(2), + myPubKey: s.grp.NewInt(3), + mySidHPrivKeyA: sidhPrivKey, + mySidHPubKeyA: sidhPubKey, + fingerprint: format.Fingerprint{5}, + } + if err := s.AddSent(sr.partner, sr.partnerHistoricalPubKey, + sr.myPrivKey, sr.myPubKey, sr.mySidHPrivKeyA, + sr.mySidHPubKeyA, sr.fingerprint); err != nil { + t.Fatalf("AddSent() returned an error: %+v", err) + } + + err := s.DeleteReceiveRequests() + if err != nil { + t.Fatalf("DeleteSentRequests returned an error: %+v", err) + } + + if s.requests[*partnerID] == nil { + t.Fatalf("DeleteReceiveRequests removes sent requests!") + } + +} + +// Unit test. +func TestStore_DeleteAllRequests(t *testing.T) { + s, _, _ := makeTestStore(t) + partnerID := id.NewIdFromUInt(rand.Uint64(), id.User, t) + rng := csprng.NewSystemRNG() + sidhPrivKey, sidhPubKey := genSidhAKeys(rng) + sr := &SentRequest{ + kv: s.kv, + partner: partnerID, + partnerHistoricalPubKey: s.grp.NewInt(1), + myPrivKey: s.grp.NewInt(2), + myPubKey: s.grp.NewInt(3), + mySidHPrivKeyA: sidhPrivKey, + mySidHPubKeyA: sidhPubKey, + fingerprint: format.Fingerprint{5}, + } + if err := s.AddSent(sr.partner, sr.partnerHistoricalPubKey, + sr.myPrivKey, sr.myPubKey, sr.mySidHPrivKeyA, + sr.mySidHPubKeyA, sr.fingerprint); err != nil { + t.Fatalf("AddSent() returned an error: %+v", err) + } + + c := contact.Contact{ID: id.NewIdFromUInt(rand.Uint64(), id.User, t)} + _, sidhPubKey = genSidhAKeys(rng) + if err := s.AddReceived(c, sidhPubKey); err != nil { + t.Fatalf("AddReceived() returned an error: %+v", err) + } + + err := s.DeleteAllRequests() + if err != nil { + t.Fatalf("DeleteAllRequests returned an error: %+v", err) + } + + if s.requests[*sr.partner] != nil { + t.Errorf("delete() failed to delete request for user %s.", + sr.partner) + } + + if _, exists := s.fingerprints[sr.fingerprint]; exists { + t.Errorf("delete() failed to delete fingerprint for fp %v.", + sr.fingerprint) + } + + if s.requests[*c.ID] != nil { + t.Errorf("delete() failed to delete request for user %s.", c.ID) + } + +} + func makeTestStore(t *testing.T) (*Store, *versioned.KV, []*cyclic.Int) { kv := versioned.NewKV(make(ekv.Memstore)) grp := cyclic.NewGroup(large.NewInt(173), large.NewInt(0)) diff --git a/storage/e2e/manager.go b/storage/e2e/manager.go index d8ec60e65209a217e45bcc28d2387ce6eadd1d9d..f6c000f78ff002e3a3c82db143b8e783ca8a497e 100644 --- a/storage/e2e/manager.go +++ b/storage/e2e/manager.go @@ -292,3 +292,9 @@ func (m *Manager) GetSilentPreimage() []byte { func (m *Manager) GetFileTransferPreimage() []byte { return preimage.Generate(m.GetRelationshipFingerprintBytes(), preimage.EndFT) } + +// GetGroupRequestPreimage returns a hash of the unique +// fingerprint for group requests received from this user. +func (m *Manager) GetGroupRequestPreimage() []byte { + return preimage.Generate(m.GetRelationshipFingerprintBytes(), preimage.GroupRq) +} \ No newline at end of file diff --git a/storage/fileTransfer/fileMessage.go b/storage/fileTransfer/fileMessage.go new file mode 100644 index 0000000000000000000000000000000000000000..03269326bbeab891e4678c888994fc35ea1026a6 --- /dev/null +++ b/storage/fileTransfer/fileMessage.go @@ -0,0 +1,119 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package fileTransfer + +import ( + "encoding/binary" + "github.com/pkg/errors" +) + +// Size constants. +const ( + partNumLen = 2 // The length of the part number in bytes + FmMinSize = partNumLen // Minimum size for the PartMessage +) + +// Error messages. +const ( + newFmSizeErr = "size of external payload (%d) must be greater than %d" + unmarshalFmSizeErr = "size of passed in bytes (%d) must be greater than %d" + setFileFmErr = "length of part bytes (%d) must be smaller than maximum payload size %d" +) + +/* ++-----------------------------------------+ +| CMIX Message Contents | ++---------+-------------+-----------------+ +| Padding | Part Number | File Data | +| 8 bytes | 2 bytes | remaining space | ++---------+-------------+-----------------+ +*/ + +// PartMessage contains part of the data being transferred and 256-bit nonce +// that is used as a nonce. +type PartMessage struct { + data []byte // Serial of all contents + partNum []byte // The part number of the file + part []byte // File part data +} + +// NewPartMessage generates a new part message that fits into the specified +// external payload size. An error is returned if the external payload size is +// too small to fit the part message. +func NewPartMessage(externalPayloadSize int) (PartMessage, error) { + if externalPayloadSize < FmMinSize { + return PartMessage{}, + errors.Errorf(newFmSizeErr, externalPayloadSize, FmMinSize) + } + + return MapPartMessage(make([]byte, externalPayloadSize)), nil +} + +// MapPartMessage maps the data to the components of a PartMessage. It is mapped +// by reference; a copy is not made. +func MapPartMessage(data []byte) PartMessage { + return PartMessage{ + data: data, + partNum: data[:partNumLen], + part: data[partNumLen:], + } +} + +// UnmarshalPartMessage converts the bytes into a PartMessage. An error is +// returned if the size of the data is too small for a PartMessage. +func UnmarshalPartMessage(b []byte) (PartMessage, error) { + if len(b) < FmMinSize { + return PartMessage{}, + errors.Errorf(unmarshalFmSizeErr, len(b), FmMinSize) + } + + return MapPartMessage(b), nil +} + +// Marshal returns the byte representation of the PartMessage. +func (m PartMessage) Marshal() []byte { + b := make([]byte, len(m.data)) + copy(b,m.data) + return b +} + +// GetPartNum returns the file part number. +func (m PartMessage) GetPartNum() uint16 { + return binary.LittleEndian.Uint16(m.partNum) +} + +// SetPartNum sets the file part number. +func (m PartMessage) SetPartNum(num uint16) { + b := make([]byte, partNumLen) + binary.LittleEndian.PutUint16(b, num) + copy(m.partNum, b) +} + +// GetPart returns the file part data from the message. +func (m PartMessage) GetPart() []byte { + b := make([]byte, len(m.part)) + copy(b,m.part) + return b +} + +// SetPart sets the PartMessage part to the given bytes. An error is returned if +// the size of the provided part data is too large to store. +func (m PartMessage) SetPart(b []byte) error { + if len(b) > len(m.part) { + return errors.Errorf(setFileFmErr, len(b), len(m.part)) + } + + copy(m.part, b) + + return nil +} + +// GetPartSize returns the number of bytes available to store part data. +func (m PartMessage) GetPartSize() int { + return len(m.part) +} diff --git a/storage/fileTransfer/fileMessage_test.go b/storage/fileTransfer/fileMessage_test.go new file mode 100644 index 0000000000000000000000000000000000000000..ad565931e6d44d60f887552e9cb4c1a954fe5cf0 --- /dev/null +++ b/storage/fileTransfer/fileMessage_test.go @@ -0,0 +1,241 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package fileTransfer + +import ( + "bytes" + "encoding/binary" + "fmt" + "math/rand" + "testing" +) + +// Tests that NewPartMessage returns a PartMessage of the expected size. +func Test_newPartMessage(t *testing.T) { + externalPayloadSize := 256 + + fm, err := NewPartMessage(externalPayloadSize) + if err != nil { + t.Errorf("NewPartMessage returned an error: %+v", err) + } + + if len(fm.data) != externalPayloadSize { + t.Errorf("Size of PartMessage data does not match payload size."+ + "\nexpected: %d\nreceived: %d", externalPayloadSize, len(fm.data)) + } +} + +// Error path: tests that NewPartMessage returns the expected error when the +// external payload size is too small. +func Test_newPartMessage_SmallPayloadSizeError(t *testing.T) { + externalPayloadSize := FmMinSize - 1 + expectedErr := fmt.Sprintf(newFmSizeErr, externalPayloadSize, FmMinSize) + + _, err := NewPartMessage(externalPayloadSize) + if err == nil || err.Error() != expectedErr { + t.Errorf("NewPartMessage did not return the expected error when the "+ + "given external payload size is too small."+ + "\nexpected: %s\nreceived: %+v", expectedErr, err) + } +} + +// Tests that MapPartMessage maps the data to the correct parts of the +// PartMessage. +func Test_mapPartMessage(t *testing.T) { + // Generate expected values + _, expectedData, expectedPartNum, expectedFile := + newRandomFileMessage() + + fm := MapPartMessage(expectedData) + + if !bytes.Equal(expectedData, fm.data) { + t.Errorf("Incorrect data.\nexpected: %q\nreceived: %q", + expectedData, fm.data) + } + + if !bytes.Equal(expectedPartNum, fm.partNum) { + t.Errorf("Incorrect part number.\nexpected: %q\nreceived: %q", + expectedPartNum, fm.partNum) + } + + if !bytes.Equal(expectedFile, fm.part) { + t.Errorf("Incorrect part data.\nexpected: %q\nreceived: %q", + expectedFile, fm.part) + } + +} + +// Tests that UnmarshalPartMessage returns a PartMessage with the expected +// values. +func Test_unmarshalPartMessage(t *testing.T) { + // Generate expected values + _, expectedData, expectedPartNumb, expectedFile := + newRandomFileMessage() + + fm, err := UnmarshalPartMessage(expectedData) + if err != nil { + t.Errorf("UnmarshalPartMessage return an error: %+v", err) + } + + if !bytes.Equal(expectedData, fm.data) { + t.Errorf("Incorrect data.\nexpected: %q\nreceived: %q", + expectedData, fm.data) + } + + if !bytes.Equal(expectedPartNumb, fm.partNum) { + t.Errorf("Incorrect part number.\nexpected: %q\nreceived: %q", + expectedPartNumb, fm.partNum) + } + + if !bytes.Equal(expectedFile, fm.part) { + t.Errorf("Incorrect part data.\nexpected: %q\nreceived: %q", + expectedFile, fm.part) + } +} + +// Error path: tests that UnmarshalPartMessage returns the expected error when +// the provided data is too small to be unmarshalled into a PartMessage. +func Test_unmarshalPartMessage_SizeError(t *testing.T) { + data := make([]byte, FmMinSize-1) + expectedErr := fmt.Sprintf(unmarshalFmSizeErr, len(data), FmMinSize) + + _, err := UnmarshalPartMessage(data) + if err == nil || err.Error() != expectedErr { + t.Errorf("UnmarshalPartMessage did not return the expected error when "+ + "the given bytes are too small to be a PartMessage."+ + "\nexpected: %s\nreceived: %+v", expectedErr, err) + } +} + +// Tests that PartMessage.Marshal returns the correct data. +func Test_fileMessage_marshal(t *testing.T) { + fm, expectedData, _, _ := newRandomFileMessage() + + data := fm.Marshal() + + if !bytes.Equal(expectedData, data) { + t.Errorf("Marshalled data does not match expected."+ + "\nexpected: %q\nreceived: %q", expectedData, data) + } +} + +// Tests that PartMessage.GetPartNum returns the correct part number. +func Test_fileMessage_getPartNum(t *testing.T) { + fm, _, expectedPartNum, _ := newRandomFileMessage() + + partNum := fm.GetPartNum() + expected := binary.LittleEndian.Uint16(expectedPartNum) + + if expected != partNum { + t.Errorf("Part number does not match expected."+ + "\nexpected: %d\nreceived: %d", expected, partNum) + } +} + +// Tests that PartMessage.SetPartNum sets the correct part number. +func Test_fileMessage_setPartNum(t *testing.T) { + fm, err := NewPartMessage(256) + if err != nil { + t.Errorf("Failed to create new PartMessage: %+v", err) + } + + expectedPartNum := make([]byte, partNumLen) + rand.New(rand.NewSource(42)).Read(expectedPartNum) + expected := binary.LittleEndian.Uint16(expectedPartNum) + + fm.SetPartNum(expected) + + if expected != fm.GetPartNum() { + t.Errorf("Failed to set correct part number.\nexpected: %d\nreceived: %d", + expected, fm.GetPartNum()) + } +} + +// Tests that PartMessage.GetPart returns the correct part data. +func Test_fileMessage_getFile(t *testing.T) { + fm, _, _, expectedFile := newRandomFileMessage() + + file := fm.GetPart() + + if !bytes.Equal(expectedFile, file) { + t.Errorf("File data does not match expected."+ + "\nexpected: %q\nreceived: %q", expectedFile, file) + } +} + +// Tests that PartMessage.SetPart sets the correct part data. +func Test_fileMessage_setFile(t *testing.T) { + fm, err := NewPartMessage(256) + if err != nil { + t.Errorf("Failed to create new PartMessage: %+v", err) + } + + fileData := make([]byte, 64) + rand.New(rand.NewSource(42)).Read(fileData) + expectedFile := make([]byte, fm.GetPartSize()) + copy(expectedFile, fileData) + + err = fm.SetPart(expectedFile) + if err != nil { + t.Errorf("SetPart returned an error: %+v", err) + } + + if !bytes.Equal(expectedFile, fm.GetPart()) { + t.Errorf("Failed to set correct part data.\nexpected: %q\nreceived: %q", + expectedFile, fm.GetPart()) + } +} + +// Error path: tests that PartMessage.SetPart returns the expected error when +// the provided part data is too large for the message. +func Test_fileMessage_setFile_FileTooLargeError(t *testing.T) { + fm, err := NewPartMessage(FmMinSize + 1) + if err != nil { + t.Errorf("Failed to create new PartMessage: %+v", err) + } + + expectedErr := fmt.Sprintf(setFileFmErr, fm.GetPartSize()+1, fm.GetPartSize()) + + err = fm.SetPart(make([]byte, fm.GetPartSize()+1)) + if err == nil || err.Error() != expectedErr { + t.Errorf("SetPart did not return the expected error when the given "+ + "part data is too large to fit in the PartMessage."+ + "\nexpected: %s\nreceived: %+v", expectedErr, err) + } +} + +// Tests that PartMessage.GetPartSize returns the expected available space for +// the part data. +func Test_fileMessage_getFileSize(t *testing.T) { + expectedSize := 256 + + fm, err := NewPartMessage(FmMinSize + expectedSize) + if err != nil { + t.Errorf("Failed to create new PartMessage: %+v", err) + } + + if expectedSize != fm.GetPartSize() { + t.Errorf("File size incorrect.\nexpected: %d\nreceived: %d", + expectedSize, fm.GetPartSize()) + } +} + +// newRandomFileMessage generates a new PartMessage filled with random data and +// return the PartMessage and its individual parts. +func newRandomFileMessage() (PartMessage, []byte, []byte, []byte) { + prng := rand.New(rand.NewSource(42)) + partNum := make([]byte, partNumLen) + prng.Read(partNum) + part := make([]byte, 64) + prng.Read(part) + data := append(partNum, part...) + + fm := MapPartMessage(data) + + return fm, data, partNum, part +} diff --git a/storage/fileTransfer/partStore_test.go b/storage/fileTransfer/partStore_test.go index 5e3eabae208b88c8bfed5c2131727939c7c448e7..597fea4b20a6871cf44310e97cbd3c4f06c8aa59 100644 --- a/storage/fileTransfer/partStore_test.go +++ b/storage/fileTransfer/partStore_test.go @@ -12,6 +12,7 @@ import ( "encoding/binary" "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/ekv" + "gitlab.com/elixxir/primitives/format" "io" "math/rand" "reflect" @@ -521,7 +522,10 @@ func Test_makePartsKey_consistency(t *testing.T) { func newRandomPartStore(numParts uint16, kv *versioned.KV, prng io.Reader, t *testing.T) (*partStore, []byte) { - partSize := 64 + cmixMsg := format.NewMessage(format.MinimumPrimeSize) + + partData, _ := NewPartMessage(cmixMsg.ContentsSize()) + partSize := partData.GetPartSize() ps, err := newPartStore(kv, numParts) if err != nil { diff --git a/storage/fileTransfer/receiveFileTransfers.go b/storage/fileTransfer/receiveFileTransfers.go index ac9f262e6b2f6920fad1cf17a0453e3f89ace8c2..0c7bffdc6881e96fd79026deb58c98195b05dfbf 100644 --- a/storage/fileTransfer/receiveFileTransfers.go +++ b/storage/fileTransfer/receiveFileTransfers.go @@ -35,7 +35,7 @@ const ( getReceivedTransferErr = "received transfer with ID %s not found" addTransferNewIdErr = "could not generate new transfer ID: %+v" noFingerprintErr = "no part found with fingerprint %s" - addPartErr = "failed to add part number %d/%d to transfer %s: %+v" + addPartErr = "failed to add part to transfer %s: %+v" deleteReceivedTransferErr = "failed to delete received transfer with ID %s from store: %+v" ) @@ -176,17 +176,18 @@ func (rft *ReceivedFileTransfersStore) DeleteTransfer(tid ftCrypto.TransferID) e // transfer that the part was added to so that a progress callback can be // called. Returns the transfer ID so that it can be used for logging. Also // returns of the transfer is complete after adding the part. -func (rft *ReceivedFileTransfersStore) AddPart(encryptedPart, padding, - mac []byte, partNum uint16, fp format.Fingerprint) (*ReceivedTransfer, +func (rft *ReceivedFileTransfersStore) AddPart(cmixMsg format.Message) (*ReceivedTransfer, ftCrypto.TransferID, bool, error) { rft.mux.Lock() defer rft.mux.Unlock() + keyfp := cmixMsg.GetKeyFP() + // Lookup the part info for the given fingerprint - info, exists := rft.info[fp] + info, exists := rft.info[cmixMsg.GetKeyFP()] if !exists { return nil, ftCrypto.TransferID{}, false, - errors.Errorf(noFingerprintErr, fp) + errors.Errorf(noFingerprintErr, keyfp) } // Lookup the transfer with the ID in the part info @@ -197,15 +198,14 @@ func (rft *ReceivedFileTransfersStore) AddPart(encryptedPart, padding, } // Add the part to the transfer - completed, err := transfer.AddPart( - encryptedPart, padding, mac, partNum, info.fpNum) + completed, err := transfer.AddPart(cmixMsg, info.fpNum) if err != nil { return transfer, info.id, false, errors.Errorf( - addPartErr, partNum, transfer.numParts, info.id, err) + addPartErr, info.id, err) } // Remove the part info from the map - delete(rft.info, fp) + delete(rft.info, keyfp) return transfer, info.id, completed, nil } diff --git a/storage/fileTransfer/receiveFileTransfers_test.go b/storage/fileTransfer/receiveFileTransfers_test.go index 0c048246023069538f20870b89a8a2dbb60d1d47..4fd245ec4d902d99f8389a8a4a1a6393c95d1432 100644 --- a/storage/fileTransfer/receiveFileTransfers_test.go +++ b/storage/fileTransfer/receiveFileTransfers_test.go @@ -345,14 +345,26 @@ func TestReceivedFileTransfersStore_AddPart(t *testing.T) { } // Create encrypted part + + cmixMsg := format.NewMessage(format.MinimumPrimeSize) + expectedData := []byte("test") + partNum, fpNum := uint16(1), uint16(1) - encryptedPart, mac, padding := newEncryptedPartData( - key, expectedData, fpNum, t) + + partData, _ := NewPartMessage(cmixMsg.ContentsSize()) + partData.SetPartNum(partNum) + _ = partData.SetPart(expectedData) + fp := ftCrypto.GenerateFingerprint(key, fpNum) + encryptedPart, mac, err := ftCrypto.EncryptPart(key, partData.Marshal(), fpNum, fp) + + cmixMsg.SetKeyFP(fp) + cmixMsg.SetContents(encryptedPart) + cmixMsg.SetMac(mac) // Add encrypted part - rt, _, _, err := rft.AddPart(encryptedPart, padding, mac, partNum, fp) + rt, _, _, err := rft.AddPart(cmixMsg) if err != nil { t.Errorf("AddPart returned an error: %+v", err) } @@ -373,9 +385,9 @@ func TestReceivedFileTransfersStore_AddPart(t *testing.T) { // Check that the correct part was stored receivedPart := expectedRT.receivedParts.parts[partNum] - if !bytes.Equal(receivedPart, expectedData) { + if !bytes.Equal(receivedPart[:len(expectedData)], expectedData) { t.Errorf("Part in memory is not expected."+ - "\nexpected: %q\nreceived: %q", expectedData, receivedPart) + "\nexpected: %q\nreceived: %q", expectedData, receivedPart[:len(expectedData)]) } } @@ -391,9 +403,12 @@ func TestReceivedFileTransfersStore_AddPart_NoFingerprintError(t *testing.T) { // Create encrypted part fp := format.NewFingerprint([]byte("invalidTransferKey")) + msg := format.NewMessage(1000) + msg.SetKeyFP(fp) + // Add encrypted part expectedErr := fmt.Sprintf(noFingerprintErr, fp) - _, _, _, err = rft.AddPart([]byte{}, []byte{}, []byte{}, 0, fp) + _, _, _, err = rft.AddPart(msg) if err == nil || err.Error() != expectedErr { t.Errorf("AddPart did not return the expected error when no part for "+ "the fingerprint exists.\nexpected: %s\nreceived: %+v", @@ -425,9 +440,12 @@ func TestReceivedFileTransfersStore_AddPart_NoTransferError(t *testing.T) { invalidTid, _ := ftCrypto.NewTransferID(prng) rft.info[fp].id = invalidTid + msg := format.NewMessage(1000) + msg.SetKeyFP(fp) + // Add encrypted part expectedErr := fmt.Sprintf(getReceivedTransferErr, invalidTid) - _, _, _, err = rft.AddPart([]byte{}, []byte{}, []byte{}, 0, fp) + _, _, _, err = rft.AddPart(msg) if err == nil || err.Error() != expectedErr { t.Errorf("AddPart did not return the expected error when no transfer "+ "for the ID exists.\nexpected: %s\nreceived: %+v", expectedErr, err) @@ -455,14 +473,24 @@ func TestReceivedFileTransfersStore_AddPart_AddPartError(t *testing.T) { // Create encrypted part partNum, fpNum := uint16(1), uint16(1) - encryptedPart := []byte("invalidPart") - mac = []byte("invalidMAC") - padding := make([]byte, 24) + part := []byte("invalidPart") + mac = make([]byte, format.MacLen) fp := ftCrypto.GenerateFingerprint(key, fpNum) // Add encrypted part - expectedErr := fmt.Sprintf(addPartErr, partNum, numParts, tid, "") - _, _, _, err = rft.AddPart(encryptedPart, padding, mac, partNum, fp) + expectedErr := fmt.Sprintf(addPartErr, tid, "") + + cmixMsg := format.NewMessage(format.MinimumPrimeSize) + + partData, _ := NewPartMessage(cmixMsg.ContentsSize()) + partData.SetPartNum(partNum) + _ = partData.SetPart(part) + + cmixMsg.SetKeyFP(fp) + cmixMsg.SetContents(partData.Marshal()) + cmixMsg.SetMac(mac) + + _, _, _, err = rft.AddPart(cmixMsg) if err == nil || !strings.Contains(err.Error(), expectedErr) { t.Errorf("AddPart did not return the expected error when the "+ "encrypted part, padding, and MAC are invalid."+ diff --git a/storage/fileTransfer/receiveTransfer.go b/storage/fileTransfer/receiveTransfer.go index df4bad66163245c9593bfbbc949d73ccf04b5bc3..b3e387a750d4cb6ba398c28195add01ec5b5763e 100644 --- a/storage/fileTransfer/receiveTransfer.go +++ b/storage/fileTransfer/receiveTransfer.go @@ -16,6 +16,7 @@ import ( "gitlab.com/elixxir/client/storage/utility" "gitlab.com/elixxir/client/storage/versioned" ftCrypto "gitlab.com/elixxir/crypto/fileTransfer" + "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/primitives/netTime" "sync" "time" @@ -276,20 +277,25 @@ func (rt *ReceivedTransfer) AddProgressCB( // AddPart decrypts an encrypted file part, adds it to the list of received // parts and marks its fingerprint as used. Returns true if the part added was // the last in the transfer. -func (rt *ReceivedTransfer) AddPart(encryptedPart, padding, mac []byte, partNum, +func (rt *ReceivedTransfer) AddPart(cmixMsg format.Message, fpNum uint16) (bool, error) { rt.mux.Lock() defer rt.mux.Unlock() // Decrypt the encrypted file part - decryptedPart, err := ftCrypto.DecryptPart( - rt.key, encryptedPart, padding, mac, fpNum) + decryptedPart, err := ftCrypto.DecryptPart(rt.key, + cmixMsg.GetContents(), cmixMsg.GetMac(), fpNum, cmixMsg.GetKeyFP()) + if err != nil { + return false, err + } + + part, err := UnmarshalPartMessage(decryptedPart) if err != nil { return false, err } // Add the part to the list of parts - err = rt.receivedParts.addPart(decryptedPart, partNum) + err = rt.receivedParts.addPart(part.GetPart(), part.GetPartNum()) if err != nil { return false, err } @@ -298,7 +304,7 @@ func (rt *ReceivedTransfer) AddPart(encryptedPart, padding, mac []byte, partNum, rt.fpVector.Use(uint32(fpNum)) // Mark part as received - rt.receivedStatus.Use(uint32(partNum)) + rt.receivedStatus.Use(uint32(part.GetPartNum())) if rt.receivedStatus.GetNumUsed() >= uint32(rt.numParts) { return true, nil diff --git a/storage/fileTransfer/receiveTransfer_test.go b/storage/fileTransfer/receiveTransfer_test.go index 7c0382ec2f038899e9f1d047b087e958e0795abc..281bb373822ee0642a6c02c07c814d402cd3d174 100644 --- a/storage/fileTransfer/receiveTransfer_test.go +++ b/storage/fileTransfer/receiveTransfer_test.go @@ -16,6 +16,7 @@ import ( "gitlab.com/elixxir/client/storage/versioned" ftCrypto "gitlab.com/elixxir/crypto/fileTransfer" "gitlab.com/elixxir/ekv" + "gitlab.com/elixxir/primitives/format" "reflect" "strings" "sync" @@ -538,13 +539,25 @@ func TestReceivedTransfer_AddPart(t *testing.T) { _, rt, _ := newEmptyReceivedTransfer(16, 20, kv, t) // Create encrypted part + cmixMsg := format.NewMessage(format.MinimumPrimeSize) + expectedData := []byte("test") + partNum, fpNum := uint16(1), uint16(1) - encryptedPart, mac, padding := newEncryptedPartData( - rt.key, expectedData, fpNum, t) + + partData, _ := NewPartMessage(cmixMsg.ContentsSize()) + partData.SetPartNum(partNum) + _ = partData.SetPart(expectedData) + + fp := ftCrypto.GenerateFingerprint(rt.key, fpNum) + encryptedPart, mac, err := ftCrypto.EncryptPart(rt.key, partData.Marshal(), fpNum, fp) + + cmixMsg.SetKeyFP(fp) + cmixMsg.SetContents(encryptedPart) + cmixMsg.SetMac(mac) // Add encrypted part - complete, err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum) + complete, err := rt.AddPart(cmixMsg,fpNum) if err != nil { t.Errorf("AddPart returned an error: %+v", err) } @@ -556,9 +569,9 @@ func TestReceivedTransfer_AddPart(t *testing.T) { receivedData, exists := rt.receivedParts.parts[partNum] if !exists { t.Errorf("Part #%d not found in part map.", partNum) - } else if !bytes.Equal(expectedData, receivedData) { + } else if !bytes.Equal(expectedData, receivedData[:len(expectedData)]) { t.Fatalf("Part data in list does not match expected."+ - "\nexpected: %+v\nreceived: %+v", expectedData, receivedData) + "\nexpected: %+v\nreceived: %+v", expectedData, receivedData[:len(expectedData)]) } // Check that the fingerprint vector has correct values @@ -594,14 +607,27 @@ func TestReceivedTransfer_AddPart_DecryptPartError(t *testing.T) { _, rt, _ := newEmptyReceivedTransfer(16, 20, kv, t) // Create encrypted part - Data := []byte("test") + cmixMsg := format.NewMessage(format.MinimumPrimeSize) + + expectedData := []byte("test") + partNum, fpNum := uint16(1), uint16(1) - encryptedPart, _, padding := newEncryptedPartData(rt.key, Data, fpNum, t) - mac := []byte("invalidMAC") + + partData, _ := NewPartMessage(cmixMsg.ContentsSize()) + partData.SetPartNum(partNum) + _ = partData.SetPart(expectedData) + + fp := ftCrypto.GenerateFingerprint(rt.key, fpNum) + encryptedPart, _, err := ftCrypto.EncryptPart(rt.key, partData.Marshal(), fpNum, fp) + badMac := make([]byte, format.MacLen) + + cmixMsg.SetKeyFP(fp) + cmixMsg.SetContents(encryptedPart) + cmixMsg.SetMac(badMac) // Add encrypted part expectedErr := "reconstructed MAC from decrypting does not match MAC from sender" - _, err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum) + _, err = rt.AddPart(cmixMsg, fpNum) if err == nil || err.Error() != expectedErr { t.Errorf("AddPart did not return the expected error when the MAC is "+ "invalid.\nexpected: %s\nreceived: %+v", expectedErr, err) @@ -670,11 +696,22 @@ func Test_loadReceivedTransfer(t *testing.T) { // Create encrypted part expectedData := []byte("test") partNum, fpNum := uint16(1), uint16(1) - encryptedPart, mac, padding := newEncryptedPartData( - expectedRT.key, expectedData, fpNum, t) + + cmixMsg := format.NewMessage(format.MinimumPrimeSize) + + partData, _ := NewPartMessage(cmixMsg.ContentsSize()) + partData.SetPartNum(partNum) + _ = partData.SetPart(expectedData) + + fp := ftCrypto.GenerateFingerprint(expectedRT.key, fpNum) + encryptedPart, mac, err := ftCrypto.EncryptPart(expectedRT.key, partData.Marshal(), fpNum, fp) + + cmixMsg.SetKeyFP(fp) + cmixMsg.SetContents(encryptedPart) + cmixMsg.SetMac(mac) // Add encrypted part - _, err := expectedRT.AddPart(encryptedPart, padding, mac, partNum, fpNum) + _, err = expectedRT.AddPart(cmixMsg, fpNum) if err != nil { t.Errorf("Failed to add test part: %+v", err) } @@ -712,12 +749,25 @@ func Test_loadReceivedTransfer_LoadFpVectorError(t *testing.T) { tid, rt, _ := newRandomReceivedTransfer(16, 20, kv, t) // Create encrypted part + data := []byte("test") partNum, fpNum := uint16(1), uint16(1) - encryptedPart, mac, padding := newEncryptedPartData(rt.key, data, fpNum, t) + + cmixMsg := format.NewMessage(format.MinimumPrimeSize) + + partData, _ := NewPartMessage(cmixMsg.ContentsSize()) + partData.SetPartNum(partNum) + _ = partData.SetPart(data) + + fp := ftCrypto.GenerateFingerprint(rt.key, fpNum) + encryptedPart, mac, err := ftCrypto.EncryptPart(rt.key, partData.Marshal(), fpNum, fp) + + cmixMsg.SetKeyFP(fp) + cmixMsg.SetContents(encryptedPart) + cmixMsg.SetMac(mac) // Add encrypted part - _, err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum) + _, err = rt.AddPart(cmixMsg, fpNum) if err != nil { t.Errorf("Failed to add test part: %+v", err) } @@ -746,10 +796,22 @@ func Test_loadReceivedTransfer_LoadPartStoreError(t *testing.T) { // Create encrypted part data := []byte("test") partNum, fpNum := uint16(1), uint16(1) - encryptedPart, mac, padding := newEncryptedPartData(rt.key, data, fpNum, t) + + cmixMsg := format.NewMessage(format.MinimumPrimeSize) + + partData, _ := NewPartMessage(cmixMsg.ContentsSize()) + partData.SetPartNum(partNum) + _ = partData.SetPart(data) + + fp := ftCrypto.GenerateFingerprint(rt.key, fpNum) + encryptedPart, mac, err := ftCrypto.EncryptPart(rt.key, partData.Marshal(), fpNum, fp) + + cmixMsg.SetKeyFP(fp) + cmixMsg.SetContents(encryptedPart) + cmixMsg.SetMac(mac) // Add encrypted part - _, err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum) + _, err = rt.AddPart(cmixMsg, fpNum) if err != nil { t.Errorf("Failed to add test part: %+v", err) } @@ -778,10 +840,22 @@ func Test_loadReceivedTransfer_LoadReceivedVectorError(t *testing.T) { // Create encrypted part data := []byte("test") partNum, fpNum := uint16(1), uint16(1) - encryptedPart, mac, padding := newEncryptedPartData(rt.key, data, fpNum, t) + + cmixMsg := format.NewMessage(format.MinimumPrimeSize) + + partData, _ := NewPartMessage(cmixMsg.ContentsSize()) + partData.SetPartNum(partNum) + _ = partData.SetPart(data) + + fp := ftCrypto.GenerateFingerprint(rt.key, fpNum) + encryptedPart, mac, err := ftCrypto.EncryptPart(rt.key, partData.Marshal(), fpNum, fp) + + cmixMsg.SetKeyFP(fp) + cmixMsg.SetContents(encryptedPart) + cmixMsg.SetMac(mac) // Add encrypted part - _, err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum) + _, err = rt.AddPart(cmixMsg, fpNum) if err != nil { t.Errorf("Failed to add test part: %+v", err) } @@ -871,11 +945,22 @@ func TestReceivedTransfer_delete(t *testing.T) { // Create encrypted part expectedData := []byte("test") partNum, fpNum := uint16(1), uint16(1) - encryptedPart, mac, padding := newEncryptedPartData( - rt.key, expectedData, fpNum, t) + + cmixMsg := format.NewMessage(format.MinimumPrimeSize) + + partData, _ := NewPartMessage(cmixMsg.ContentsSize()) + partData.SetPartNum(partNum) + _ = partData.SetPart(expectedData) + + fp := ftCrypto.GenerateFingerprint(rt.key, fpNum) + encryptedPart, mac, err := ftCrypto.EncryptPart(rt.key, partData.Marshal(), fpNum, fp) + + cmixMsg.SetKeyFP(fp) + cmixMsg.SetContents(encryptedPart) + cmixMsg.SetMac(mac) // Add encrypted part - _, err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum) + _, err = rt.AddPart(cmixMsg, fpNum) if err != nil { t.Fatalf("Failed to add test part: %+v", err) } @@ -1030,8 +1115,20 @@ func newRandomReceivedTransfer(numParts, numFps uint16, kv *versioned.KV, } for partNum, part := range parts.parts { - encryptedPart, mac, padding := newEncryptedPartData(key, part, partNum, t) - _, err := rt.AddPart(encryptedPart, padding, mac, partNum, partNum) + cmixMsg := format.NewMessage(format.MinimumPrimeSize) + + partData, _ := NewPartMessage(cmixMsg.ContentsSize()) + partData.SetPartNum(partNum) + _ = partData.SetPart(part) + + fp := ftCrypto.GenerateFingerprint(rt.key, partNum) + encryptedPart, mac, err := ftCrypto.EncryptPart(rt.key, partData.Marshal(), partNum, fp) + + cmixMsg.SetKeyFP(fp) + cmixMsg.SetContents(encryptedPart) + cmixMsg.SetMac(mac) + + _, err = rt.AddPart(cmixMsg, partNum) if err != nil { t.Errorf("Failed to add part #%d: %+v", partNum, err) } @@ -1059,18 +1156,4 @@ func newEmptyReceivedTransfer(numParts, numFps uint16, kv *versioned.KV, } return tid, rt, fileData -} - -// newEncryptedPartData encrypts the part data and returns the encrypted part -// its MAC, and its padding. -func newEncryptedPartData(key ftCrypto.TransferKey, part []byte, fpNum uint16, - t *testing.T) ([]byte, []byte, []byte) { - // Create encrypted part - prng := NewPrng(42) - encPart, mac, padding, err := ftCrypto.EncryptPart(key, part, fpNum, prng) - if err != nil { - t.Fatalf("Failed to encrypt data: %+v", err) - } - - return encPart, mac, padding -} +} \ No newline at end of file diff --git a/storage/fileTransfer/sentFileTransfers.go b/storage/fileTransfer/sentFileTransfers.go index 485eb67d062eca8ccb75f9b53bda262d64996eb3..e672ce184fa4674a47b94c89bb47abdc2b420eb8 100644 --- a/storage/fileTransfer/sentFileTransfers.go +++ b/storage/fileTransfer/sentFileTransfers.go @@ -193,11 +193,6 @@ func (sft *SentFileTransfersStore) GetUnsentPartsAndSentRounds() ( sentRounds := map[id.Round][]ftCrypto.TransferID{} for tid, st := range sft.transfers { - // Get list of round IDs that transfers have in-progress rounds on - for _, rid := range st.GetSentRounds() { - sentRounds[rid] = append(sentRounds[rid], tid) - } - // Get list of unsent part numbers for each transfer stUnsentParts, err := st.GetUnsentPartNums() if err != nil { @@ -206,6 +201,11 @@ func (sft *SentFileTransfersStore) GetUnsentPartsAndSentRounds() ( if len(stUnsentParts) > 0 { unsentParts[tid] = stUnsentParts } + + // Get list of round IDs that transfers have in-progress rounds on + for _, rid := range st.GetSentRounds() { + sentRounds[rid] = append(sentRounds[rid], tid) + } } return unsentParts, sentRounds, nil diff --git a/storage/fileTransfer/sentPartTracker_test.go b/storage/fileTransfer/sentPartTracker_test.go index 8d5a476ed542563b7524e9f3da9d668680f9ff22..af50953b889415c3c11bf4969199ee6d318408fb 100644 --- a/storage/fileTransfer/sentPartTracker_test.go +++ b/storage/fileTransfer/sentPartTracker_test.go @@ -56,21 +56,20 @@ func Test_sentPartTracker_GetPartStatus(t *testing.T) { switch partStatuses[partNum] { case interfaces.FpSent: - err := st.partStats.Set(partNum, uint8(interfaces.FpSent)) + err := st.partStats.Set(partNum, inProgress) if err != nil { - t.Errorf("Failed to set part %d to %s: %+v", - partNum, interfaces.FpSent, err) + t.Errorf( + "Failed to set part %d to in-progress: %+v", partNum, err) } case interfaces.FpArrived: - err := st.partStats.Set(partNum, uint8(interfaces.FpSent)) + err := st.partStats.Set(partNum, inProgress) if err != nil { - t.Errorf("Failed to set part %d to %s: %+v", - partNum, interfaces.FpSent, err) + t.Errorf( + "Failed to set part %d to in-progress: %+v", partNum, err) } - err = st.partStats.Set(partNum, uint8(interfaces.FpArrived)) + err = st.partStats.Set(partNum, finished) if err != nil { - t.Errorf("Failed to set part %d to %s: %+v", - partNum, interfaces.FpArrived, err) + t.Errorf("Failed to set part %d to finished: %+v", partNum, err) } } } diff --git a/storage/fileTransfer/sentTransfer.go b/storage/fileTransfer/sentTransfer.go index a91a59847fee21466f0bff6a80a49ad01b67c82d..cdffaaeca6807c4025b9bf90925130e6be9cbcb5 100644 --- a/storage/fileTransfer/sentTransfer.go +++ b/storage/fileTransfer/sentTransfer.go @@ -17,7 +17,6 @@ import ( "gitlab.com/elixxir/client/storage/versioned" ftCrypto "gitlab.com/elixxir/crypto/fileTransfer" "gitlab.com/elixxir/primitives/format" - "gitlab.com/xx_network/crypto/csprng" "gitlab.com/xx_network/primitives/id" "gitlab.com/xx_network/primitives/netTime" "sync" @@ -91,6 +90,14 @@ const ( // been used. var MaxRetriesErr = errors.New(maxRetriesErr) +// States for parts in the partStats MultiStateVector. +const ( + unsent = iota + inProgress + finished + numStates // The number of part states (for initialisation of the vector) +) + // sentTransferStateMap prevents illegal state changes for part statuses. var sentTransferStateMap = [][]bool{ {false, true, false}, @@ -188,8 +195,8 @@ func NewSentTransfer(recipient *id.ID, tid ftCrypto.TransferID, } // Create new MultiStateVector for storing part statuses - st.partStats, err = utility.NewMultiStateVector( - st.numParts, 3, sentTransferStateMap, sentPartStatsVectorKey, st.kv) + st.partStats, err = utility.NewMultiStateVector(st.numParts, numStates, + sentTransferStateMap, sentPartStatsVectorKey, st.kv) if err != nil { return nil, errors.Errorf(newSentPartStatusVectorErr, err) } @@ -236,8 +243,8 @@ func (st *SentTransfer) ReInit(numFps uint16, } // Overwrite new part status MultiStateVector - st.partStats, err = utility.NewMultiStateVector( - st.numParts, 3, sentTransferStateMap, sentPartStatsVectorKey, st.kv) + st.partStats, err = utility.NewMultiStateVector(st.numParts, numStates, + sentTransferStateMap, sentPartStatsVectorKey, st.kv) if err != nil { return errors.Errorf(reInitSentPartStatusVectorErr, err) } @@ -314,7 +321,7 @@ func (st *SentTransfer) IsPartInProgress(partNum uint16) (bool, error) { if err != nil { return false, errors.Errorf(getStatusErr, partNum, err) } - return status == 1, nil + return status == inProgress, nil } // IsPartFinished returns true if the part has successfully arrived. Returns @@ -325,7 +332,7 @@ func (st *SentTransfer) IsPartFinished(partNum uint16) (bool, error) { if err != nil { return false, errors.Errorf(getStatusErr, partNum, err) } - return status == 2, nil + return status == finished, nil } // GetProgress returns the current progress of the transfer. Completed is true @@ -345,8 +352,8 @@ func (st *SentTransfer) GetProgress() (completed bool, sent, arrived, // getProgress is the thread-unsafe helper function for GetProgress. func (st *SentTransfer) getProgress() (completed bool, sent, arrived, total uint16, t interfaces.FilePartTracker) { - arrived, _ = st.partStats.GetCount(2) - sent, _ = st.partStats.GetCount(1) + arrived, _ = st.partStats.GetCount(finished) + sent, _ = st.partStats.GetCount(inProgress) total = st.numParts if sent == 0 && arrived == total { @@ -419,31 +426,44 @@ func (st *SentTransfer) AddProgressCB(cb interfaces.SentProgressCallback, // GetEncryptedPart gets the specified part, encrypts it, and returns the // encrypted part along with its MAC, padding, and fingerprint. -func (st *SentTransfer) GetEncryptedPart(partNum uint16, partSize int, - rng csprng.Source) (encPart, mac, padding []byte, fp format.Fingerprint, - err error) { +func (st *SentTransfer) GetEncryptedPart(partNum uint16, contentsSize int) (encPart, mac []byte, + fp format.Fingerprint, err error) { st.mux.Lock() defer st.mux.Unlock() + // Create new empty file part message of size equal to the available payload + // size in the cMix message + partMsg, err := NewPartMessage(contentsSize) + if err != nil { + return nil, nil, format.Fingerprint{}, err + } + + partMsg.SetPartNum(partNum) + // Lookup part part, exists := st.sentParts.getPart(partNum) if !exists { - return nil, nil, nil, format.Fingerprint{}, + return nil, nil, format.Fingerprint{}, errors.Errorf(noPartNumErr, partNum) } + if err = partMsg.SetPart(part); err != nil{ + return nil, nil, format.Fingerprint{}, + err + } + // If all fingerprints have been used but parts still remain, then change // the status to stopping and return an error specifying that all the // retries have been used if st.fpVector.GetNumAvailable() < 1 { st.status = Stopping - return nil, nil, nil, format.Fingerprint{}, MaxRetriesErr + return nil, nil, format.Fingerprint{}, MaxRetriesErr } // Get next unused fingerprint number and mark it as used nextKey, err := st.fpVector.Next() if err != nil { - return nil, nil, nil, format.Fingerprint{}, + return nil, nil, format.Fingerprint{}, errors.Errorf(fingerprintErr, err) } fpNum := uint16(nextKey) @@ -452,16 +472,13 @@ func (st *SentTransfer) GetEncryptedPart(partNum uint16, partSize int, fp = ftCrypto.GenerateFingerprint(st.key, fpNum) // Encrypt the file part and generate the file part MAC and padding (nonce) - maxLengthPart := make([]byte, partSize) - copy(maxLengthPart, part) - encPart, mac, padding, err = ftCrypto.EncryptPart( - st.key, maxLengthPart, fpNum, rng) + encPart, mac, err = ftCrypto.EncryptPart(st.key, partMsg.Marshal(), fpNum, fp) if err != nil { - return nil, nil, nil, format.Fingerprint{}, + return nil, nil, format.Fingerprint{}, errors.Errorf(encryptPartErr, partNum, err) } - return encPart, mac, padding, fp, err + return encPart, mac, fp, err } // SetInProgress adds the specified file part numbers to the in-progress @@ -476,7 +493,7 @@ func (st *SentTransfer) SetInProgress(rid id.Round, partNums ...uint16) (bool, _, exists := st.inProgressTransfers.getPartNums(rid) // Set parts as in-progress in part status vector - err := st.partStats.SetMany(partNums, 1) + err := st.partStats.SetMany(partNums, inProgress) if err != nil { return false, err } @@ -503,8 +520,16 @@ func (st *SentTransfer) UnsetInProgress(rid id.Round) ([]uint16, error) { // Get the list of part numbers to be removed from list partNums, _ := st.inProgressTransfers.getPartNums(rid) + // The part status is set in partStats before the parts and round ID so that + // in the event of recovery after a crash, the parts will be resent on a new + // round and the parts in the inProgressTransfers will be left until deleted + // with the rest of the storage on transfer completion. The side effect is + // that on recovery, the status of the round will be looked up again and the + // progress callback will be called for an event that has already been + // called on the callback. + // Set parts as unsent in part status vector - err := st.partStats.SetMany(partNums, 0) + err := st.partStats.SetMany(partNums, unsent) if err != nil { return nil, err } @@ -539,7 +564,7 @@ func (st *SentTransfer) FinishTransfer(rid id.Round) (bool, error) { } // Set parts as finished in part status vector - err = st.partStats.SetMany(partNums, 2) + err = st.partStats.SetMany(partNums, finished) if err != nil { return false, err } @@ -561,7 +586,7 @@ func (st *SentTransfer) GetUnsentPartNums() ([]uint16, error) { defer st.mux.RUnlock() // Get list of parts with a status of unsent - unsentPartNums, err := st.partStats.GetKeys(0) + unsentPartNums, err := st.partStats.GetKeys(unsent) if err != nil { return nil, errors.Errorf(getUnsentPartsErr, err) } diff --git a/storage/fileTransfer/sentTransfer_test.go b/storage/fileTransfer/sentTransfer_test.go index 79415befe0632d48092d75f078969f28f86f5693..033992bf84b42aa4bf54431ac50e3c3c0b132998 100644 --- a/storage/fileTransfer/sentTransfer_test.go +++ b/storage/fileTransfer/sentTransfer_test.go @@ -803,7 +803,6 @@ func TestSentTransfer_AddProgressCB(t *testing.T) { func TestSentTransfer_GetEncryptedPart(t *testing.T) { kv := versioned.NewKV(make(ekv.Memstore)) _, st := newRandomSentTransfer(16, 24, kv, t) - prng := NewPrng(42) // Create and fill fingerprint map used to check fingerprint validity // The first item in the uint16 slice is the fingerprint number and the @@ -816,7 +815,7 @@ func TestSentTransfer_GetEncryptedPart(t *testing.T) { for i := uint16(0); i < st.numFps; i++ { partNum := i % st.numParts - encPart, mac, padding, fp, err := st.GetEncryptedPart(partNum, 16, prng) + encPart, mac, fp, err := st.GetEncryptedPart(partNum, 18) if err != nil { t.Fatalf("GetEncryptedPart returned an error for part number "+ "%d (%d): %+v", partNum, i, err) @@ -836,17 +835,24 @@ func TestSentTransfer_GetEncryptedPart(t *testing.T) { } // Attempt to decrypt the part - part, err := ftCrypto.DecryptPart(st.key, encPart, padding, mac, fpNum[0]) + partMarshaled, err := ftCrypto.DecryptPart(st.key, encPart, mac, fpNum[0], fp) if err != nil { t.Errorf("Failed to decrypt file part number %d (%d): %+v", partNum, i, err) } + partMsg, _ := UnmarshalPartMessage(partMarshaled) + // Make sure the decrypted part matches the original expectedPart, _ := st.sentParts.getPart(i % st.numParts) - if !bytes.Equal(expectedPart, part) { + if !bytes.Equal(expectedPart, partMsg.GetPart()) { t.Errorf("Decyrpted part number %d does not match expected (%d)."+ - "\nexpected: %+v\nreceived: %+v", partNum, i, expectedPart, part) + "\nexpected: %+v\nreceived: %+v", partNum, i, expectedPart, partMsg.GetPart()) + } + + if partMsg.GetPartNum()!=i % st.numParts{ + t.Errorf("Number of part did not match, expected: %d, " + + "received: %d", i % st.numParts, partMsg.GetPartNum()) } } } @@ -856,12 +862,11 @@ func TestSentTransfer_GetEncryptedPart(t *testing.T) { func TestSentTransfer_GetEncryptedPart_NoPartError(t *testing.T) { kv := versioned.NewKV(make(ekv.Memstore)) _, st := newRandomSentTransfer(16, 24, kv, t) - prng := NewPrng(42) partNum := st.numParts + 1 expectedErr := fmt.Sprintf(noPartNumErr, partNum) - _, _, _, _, err := st.GetEncryptedPart(partNum, 16, prng) + _, _, _, err := st.GetEncryptedPart(partNum, 16) if err == nil || err.Error() != expectedErr { t.Errorf("GetEncryptedPart did not return the expected error for a "+ "nonexistent part number %d.\nexpected: %s\nreceived: %+v", @@ -874,12 +879,11 @@ func TestSentTransfer_GetEncryptedPart_NoPartError(t *testing.T) { func TestSentTransfer_GetEncryptedPart_NoFingerprintsError(t *testing.T) { kv := versioned.NewKV(make(ekv.Memstore)) _, st := newRandomSentTransfer(16, 24, kv, t) - prng := NewPrng(42) // Use up all the fingerprints for i := uint16(0); i < st.numFps; i++ { partNum := i % st.numParts - _, _, _, _, err := st.GetEncryptedPart(partNum, 16, prng) + _, _, _, err := st.GetEncryptedPart(partNum, 18) if err != nil { t.Errorf("Error when encyrpting part number %d (%d): %+v", partNum, i, err) @@ -887,7 +891,7 @@ func TestSentTransfer_GetEncryptedPart_NoFingerprintsError(t *testing.T) { } // Try to encrypt without any fingerprints - _, _, _, _, err := st.GetEncryptedPart(5, 16, prng) + _, _, _, err := st.GetEncryptedPart(5, 18) if err != MaxRetriesErr { t.Errorf("GetEncryptedPart did not return MaxRetriesErr when all "+ "fingerprints have been used.\nexpected: %s\nreceived: %+v", @@ -895,32 +899,6 @@ func TestSentTransfer_GetEncryptedPart_NoFingerprintsError(t *testing.T) { } } -// Error path: tests that SentTransfer.GetEncryptedPart returns the expected -// error when encrypting the part fails due to a PRNG error. -func TestSentTransfer_GetEncryptedPart_EncryptPartError(t *testing.T) { - kv := versioned.NewKV(make(ekv.Memstore)) - _, st := newRandomSentTransfer(16, 24, kv, t) - prng := NewPrngErr() - - // Create and fill fingerprint map used to check fingerprint validity - // The first item in the uint16 slice is the fingerprint number and the - // second item is the number of times it has been used - fpMap := make(map[format.Fingerprint][]uint16, st.numFps) - for num, fp := range ftCrypto.GenerateFingerprints(st.key, st.numFps) { - fpMap[fp] = []uint16{uint16(num), 0} - } - - partNum := uint16(0) - expectedErr := fmt.Sprintf(encryptPartErr, partNum, "") - - _, _, _, _, err := st.GetEncryptedPart(partNum, 16, prng) - if err == nil || !strings.Contains(err.Error(), expectedErr) { - t.Errorf("GetEncryptedPart did not return the expected error when "+ - "the PRNG should have errored.\nexpected: %s\nreceived: %+v", - expectedErr, err) - } -} - // Tests that SentTransfer.SetInProgress correctly adds the part numbers for the // given round ID to the in-progress map and sets the correct parts as // in-progress in the state vector. @@ -962,15 +940,15 @@ func TestSentTransfer_SetInProgress(t *testing.T) { // Check that the part numbers were set on the in-progress status vector for i, partNum := range expectedPartNums { - if status, _ := st.partStats.Get(partNum); status != 1 { - t.Errorf("Part number %d not marked as used in status vector (%d).", - partNum, i) + if status, _ := st.partStats.Get(partNum); status != inProgress { + t.Errorf("Part number %d not marked as in-progress in status "+ + "vector (%d).", partNum, i) } } // Check that the correct number of parts were marked as in-progress in the // status vector - count, _ := st.partStats.GetCount(1) + count, _ := st.partStats.GetCount(inProgress) if int(count) != len(expectedPartNums) { t.Errorf("Incorrect number of parts marked as in-progress."+ "\nexpected: %d\nreceived: %d", len(expectedPartNums), count) @@ -989,7 +967,7 @@ func TestSentTransfer_SetInProgress(t *testing.T) { } // Check that the number of parts were marked as in-progress is unchanged - count, _ = st.partStats.GetCount(1) + count, _ = st.partStats.GetCount(inProgress) if int(count) != len(expectedPartNums2)+len(expectedPartNums) { t.Errorf("Incorrect number of parts marked as in-progress."+ "\nexpected: %d\nreceived: %d", @@ -1075,10 +1053,10 @@ func TestSentTransfer_UnsetInProgress(t *testing.T) { } // Check that there are no set parts in the in-progress status vector - status, _ := st.partStats.Get(1) - if status != 0 { + status, _ := st.partStats.Get(inProgress) + if status != unsent { t.Errorf("Failed to unset all parts in the in-progress vector."+ - "\nexpected: %d\nreceived: %d", 0, status) + "\nexpected: %d\nreceived: %d", unsent, status) } } @@ -1134,7 +1112,7 @@ func TestSentTransfer_FinishTransfer(t *testing.T) { } // Check that there are no set parts in the in-progress status vector - count, _ := st.partStats.GetCount(1) + count, _ := st.partStats.GetCount(inProgress) if count != 0 { t.Errorf("Failed to unset all parts in the in-progress vector."+ "\nexpected: %d\nreceived: %d", 0, count) @@ -1142,17 +1120,16 @@ func TestSentTransfer_FinishTransfer(t *testing.T) { // Check that the part numbers were set on the finished status vector for i, partNum := range expectedPartNums { - - status, _ := st.partStats.Get(1) - if status != 2 { - t.Errorf("Part number %d not marked as used in status vector (%d).", - partNum, i) + status, _ := st.partStats.Get(inProgress) + if status != finished { + t.Errorf("Part number %d not marked as finished in status vector "+ + "(%d).", partNum, i) } } // Check that the correct number of parts were marked as finished in the // status vector - count, _ = st.partStats.GetCount(2) + count, _ = st.partStats.GetCount(finished) if int(count) != len(expectedPartNums) { t.Errorf("Incorrect number of parts marked as finished."+ "\nexpected: %d\nreceived: %d", len(expectedPartNums), count) diff --git a/storage/partition/multiPartMessage_test.go b/storage/partition/multiPartMessage_test.go index 752d53272bb121f566e2917d746087020f68d14b..0fa14f3294eef6efa0ff1362e0eca7d1f97864fa 100644 --- a/storage/partition/multiPartMessage_test.go +++ b/storage/partition/multiPartMessage_test.go @@ -203,7 +203,7 @@ func TestMultiPartMessage_IsComplete(t *testing.T) { t.Error("IsComplete() returned true when NumParts == 0.") } - mpm.AddFirst(message.Text, partNums[0], 75, netTime.Now(), netTime.Now(), parts[0]) + mpm.AddFirst(message.XxMessage, partNums[0], 75, netTime.Now(), netTime.Now(), parts[0]) for i := range partNums { if i > 0 { mpm.Add(partNums[i], parts[i]) diff --git a/storage/partition/store_test.go b/storage/partition/store_test.go index 4e3d221a159be0f1fd73664c1d6d6a9e7b5097aa..0b05232ca20380f5c066dfa31405dea0833a778f 100644 --- a/storage/partition/store_test.go +++ b/storage/partition/store_test.go @@ -41,7 +41,7 @@ func TestStore_AddFirst(t *testing.T) { s := New(versioned.NewKV(ekv.Memstore{})) msg, complete := s.AddFirst(id.NewIdFromString("User", id.User, t), - message.Text, 5, 0, 1, netTime.Now(), netTime.Now(), part, + message.XxMessage, 5, 0, 1, netTime.Now(), netTime.Now(), part, []byte{0}) if !complete { @@ -61,7 +61,7 @@ func TestStore_Add(t *testing.T) { s := New(versioned.NewKV(ekv.Memstore{})) msg, complete := s.AddFirst(id.NewIdFromString("User", id.User, t), - message.Text, 5, 0, 2, netTime.Now(), netTime.Now(), part1, + message.XxMessage, 5, 0, 2, netTime.Now(), netTime.Now(), part1, []byte{0}) if complete { @@ -92,7 +92,7 @@ func TestStore_ClearMessages(t *testing.T) { messageId1 := uint64(5) oldTimestamp := netTime.Now().Add(-2 * clearPartitionThreshold) s.AddFirst(partner1, - message.Text, messageId1, 0, 2, netTime.Now(), + message.XxMessage, messageId1, 0, 2, netTime.Now(), oldTimestamp, part1, []byte{0}) s.Add(partner1, messageId1, 1, part2, []byte{0}) @@ -100,7 +100,7 @@ func TestStore_ClearMessages(t *testing.T) { partner2 := id.NewIdFromString("User1", id.User, t) messageId2 := uint64(6) newTimestamp := netTime.Now() - s.AddFirst(partner2, message.Text, messageId2, 0, 2, netTime.Now(), + s.AddFirst(partner2, message.XxMessage, messageId2, 0, 2, netTime.Now(), newTimestamp, part1, []byte{0}) diff --git a/storage/session.go b/storage/session.go index e54ab01fbc4a2002bac5ffa445d906ffd8710186..d11e11fc6697edd4e52d3550c6de2d6677a37bd1 100644 --- a/storage/session.go +++ b/storage/session.go @@ -486,6 +486,18 @@ func InitTestingSession(i interface{}) *Session { s.hostList = hostList.NewStore(s.kv) + privKeys := make([]*cyclic.Int, 10) + pubKeys := make([]*cyclic.Int, 10) + for i := range privKeys { + privKeys[i] = cmixGrp.NewInt(5) + pubKeys[i] = cmixGrp.ExpG(privKeys[i], cmixGrp.NewInt(1)) + } + + s.auth, err = auth.NewStore(s.kv, cmixGrp, privKeys) + if err != nil { + jww.FATAL.Panicf("Failed to create auth store: %v", err) + } + s.edgeCheck, err = edge.NewStore(s.kv, uid) if err != nil { jww.FATAL.Panicf("Failed to create new edge Store: %+v", err) diff --git a/storage/utility/meteredCmixMessageBuffer.go b/storage/utility/meteredCmixMessageBuffer.go index ed1920ff42f2cf0f122549584ef11d480d43ebbb..9060dbb7ea2424f87879032371ee25944ff64851 100644 --- a/storage/utility/meteredCmixMessageBuffer.go +++ b/storage/utility/meteredCmixMessageBuffer.go @@ -114,6 +114,10 @@ func LoadMeteredCmixMessageBuffer(kv *versioned.KV, key string) (*MeteredCmixMes } func (mcmb *MeteredCmixMessageBuffer) Add(m format.Message) { + if m.GetPrimeByteLen()==0{ + jww.FATAL.Panicf("Cannot handle a metered " + + "cmix message with a length of 0") + } msg := meteredCmixMessage{ M: m.Marshal(), Count: 0, diff --git a/storage/utility/multiStateVector.go b/storage/utility/multiStateVector.go index 0a882ada564d7044ae1a5b42e914b1b311692146..77cf24ad1435c6c83ce431a13080a4d9d27cae68 100644 --- a/storage/utility/multiStateVector.go +++ b/storage/utility/multiStateVector.go @@ -43,7 +43,7 @@ const ( saveSetStateErr = "failed to save MultiStateVector after setting key %d state to %d: %+v" // MultiStateVector.SetMany - setManyStateErr = "failed to set state of key %d (%d/%d): %+v" + setManyStateErr = "failed to set state of key %d (%d of %d): %+v" saveManySetStateErr = "failed to save MultiStateVector after setting keys %d state to %d: %+v" // MultiStateVector.set diff --git a/ud/manager.go b/ud/manager.go index 1b9de113a558afc451e2acabfb5dabde096a4f88..b271c0d90a635e3927728a9337b2902bb8a58be4 100644 --- a/ud/manager.go +++ b/ud/manager.go @@ -42,9 +42,20 @@ type Manager struct { single SingleInterface myID *id.ID + // alternate User discovery service to circumvent production + alternativeUd *alternateUd + registered *uint32 } +// alternateUd is an alternative user discovery service. +// This is used for testing, so client can avoid using +// the production server. +type alternateUd struct { + host *connect.Host + dhPubKey []byte +} + // NewManager builds a new user discovery manager. It requires that an updated // NDF is available and will error if one is not. func NewManager(client *api.Client, single *single.Manager) (*Manager, error) { @@ -94,11 +105,60 @@ func NewManager(client *api.Client, single *single.Manager) (*Manager, error) { return m, nil } +// SetAlternativeUserDiscovery sets the alternativeUd object within manager. +// Once set, any user discovery operation will go through the alternative +// user discovery service. +// To undo this operation, use UnsetAlternativeUserDiscovery. +func (m *Manager) SetAlternativeUserDiscovery(altCert, altAddress, contactFile []byte) error { + params := connect.GetDefaultHostParams() + params.AuthEnabled = false + + udIdBytes, dhPubKey, err := contact.ReadContactFromFile(contactFile) + if err != nil { + return err + } + + udID, err := id.Unmarshal(udIdBytes) + if err != nil { + return err + } + + // Add a new host and return it if it does not already exist + host, err := m.comms.AddHost(udID, string(altAddress), + altCert, params) + if err != nil { + return errors.WithMessage(err, "User Discovery host object could "+ + "not be constructed.") + } + + m.alternativeUd = &alternateUd{ + host: host, + dhPubKey: dhPubKey, + } + + return nil +} + +// UnsetAlternativeUserDiscovery clears out the information from +// the Manager object. +func (m *Manager) UnsetAlternativeUserDiscovery() error { + if m.alternativeUd == nil { + return errors.New("Alternative User Discovery is already unset.") + } + + m.alternativeUd = nil + return nil +} + // getHost returns the current UD host for the UD ID found in the NDF. If the // host does not exist, then it is added and returned func (m *Manager) getHost() (*connect.Host, error) { - netDef := m.net.GetInstance().GetPartialNdf().Get() + // Return alternative User discovery service if it has been set + if m.alternativeUd != nil { + return m.alternativeUd.host, nil + } + netDef := m.net.GetInstance().GetPartialNdf().Get() // Unmarshal UD ID from the NDF udID, err := id.Unmarshal(netDef.UDB.ID) if err != nil { @@ -113,6 +173,7 @@ func (m *Manager) getHost() (*connect.Host, error) { params := connect.GetDefaultHostParams() params.AuthEnabled = false + params.SendTimeout = 20 * time.Second // Add a new host and return it if it does not already exist host, err = m.comms.AddHost(udID, netDef.UDB.Address, @@ -127,6 +188,23 @@ func (m *Manager) getHost() (*connect.Host, error) { // getContact returns the contact for UD as retrieved from the NDF. func (m *Manager) getContact() (contact.Contact, error) { + // Return alternative User discovery contact if set + if m.alternativeUd != nil { + // Unmarshal UD DH public key + alternativeDhPubKey := m.storage.E2e().GetGroup().NewInt(1) + if err := alternativeDhPubKey.UnmarshalJSON(m.alternativeUd.dhPubKey); err != nil { + return contact.Contact{}, + errors.WithMessage(err, "Failed to unmarshal UD DH public key.") + } + + return contact.Contact{ + ID: m.alternativeUd.host.GetId(), + DhPubKey: alternativeDhPubKey, + OwnershipProof: nil, + Facts: nil, + }, nil + } + netDef := m.net.GetInstance().GetPartialNdf().Get() // Unmarshal UD ID from the NDF diff --git a/ud/manager_test.go b/ud/manager_test.go new file mode 100644 index 0000000000000000000000000000000000000000..1dcb6c273df1c6ec7ed92980b15fc8990cfab01a --- /dev/null +++ b/ud/manager_test.go @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package ud + +import ( + "gitlab.com/elixxir/comms/client" + "gitlab.com/xx_network/crypto/csprng" + "gitlab.com/xx_network/crypto/signature/rsa" + "testing" +) + +var testCert = `-----BEGIN CERTIFICATE----- +MIIF4DCCA8igAwIBAgIUegUvihtQooWNIzsNqj6lucXn6g8wDQYJKoZIhvcNAQEL +BQAwgYwxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJQ2xhcmVt +b250MRAwDgYDVQQKDAdFbGl4eGlyMRQwEgYDVQQLDAtEZXZlbG9wbWVudDETMBEG +A1UEAwwKZWxpeHhpci5pbzEfMB0GCSqGSIb3DQEJARYQYWRtaW5AZWxpeHhpci5p +bzAeFw0yMTExMzAxODMwMTdaFw0zMTExMjgxODMwMTdaMIGMMQswCQYDVQQGEwJV +UzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCUNsYXJlbW9udDEQMA4GA1UECgwHRWxp +eHhpcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxEzARBgNVBAMMCmVsaXh4aXIuaW8x +HzAdBgkqhkiG9w0BCQEWEGFkbWluQGVsaXh4aXIuaW8wggIiMA0GCSqGSIb3DQEB +AQUAA4ICDwAwggIKAoICAQCckGabzUitkySleveyD9Yrxrpj50FiGkOvwkmgN1jF +9r5StN3otiU5tebderkjD82mVqB781czRA9vPqAggbw1ZdAyQPTvDPTj7rmzkByq +QIkdZBMshV/zX1z8oXoNB9bzZlUFVF4HTY3dEytAJONJRkGGAw4FTa/wCkWsITiT +mKvkP3ciKgz7s8uMyZzZpj9ElBphK9Nbwt83v/IOgTqDmn5qDBnHtoLw4roKJkC8 +00GF4ZUhlVSQC3oFWOCu6tvSUVCBCTUzVKYJLmCnoilmiE/8nCOU0VOivtsx88f5 +9RSPfePUk8u5CRmgThwOpxb0CAO0gd+sY1YJrn+FaW+dSR8OkM3bFuTq7fz9CEkS +XFfUwbJL+HzT0ZuSA3FupTIExyDmM/5dF8lC0RB3j4FNQF+H+j5Kso86e83xnXPI +e+IKKIYa/LVdW24kYRuBDpoONN5KS/F+F/5PzOzH9Swdt07J9b7z1dzWcLnKGtkN +WVsZ7Ue6cuI2zOEWqF1OEr9FladgORcdVBoF/WlsA63C2c1J0tjXqqcl/27GmqGW +gvhaA8Jkm20qLCEhxQ2JzrBdk/X/lCZdP/7A5TxnLqSBq8xxMuLJlZZbUG8U/BT9 +sHF5mXZyiucMjTEU7qHMR2UGNFot8TQ7ZXntIApa2NlB/qX2qI5D13PoXI9Hnyxa +8wIDAQABozgwNjAVBgNVHREEDjAMggplbGl4eGlyLmlvMB0GA1UdDgQWBBQimFud +gCzDVFD3Xz68zOAebDN6YDANBgkqhkiG9w0BAQsFAAOCAgEAccsH9JIyFZdytGxC +/6qjSHPgV23ZGmW7alg+GyEATBIAN187Du4Lj6cLbox5nqLdZgYzizVop32JQAHv +N1QPKjViOOkLaJprSUuRULa5kJ5fe+XfMoyhISI4mtJXXbMwl/PbOaDSdeDjl0ZO +auQggWslyv8ZOkfcbC6goEtAxljNZ01zY1ofSKUj+fBw9Lmomql6GAt7NuubANs4 +9mSjXwD27EZf3Aqaaju7gX1APW2O03/q4hDqhrGW14sN0gFt751ddPuPr5COGzCS +c3Xg2HqMpXx//FU4qHrZYzwv8SuGSshlCxGJpWku9LVwci1Kxi4LyZgTm6/xY4kB +5fsZf6C2yAZnkIJ8bEYr0Up4KzG1lNskU69uMv+d7W2+4Ie3Evf3HdYad/WeUskG +tc6LKY6B2NX3RMVkQt0ftsDaWsktnR8VBXVZSBVYVEQu318rKvYRdOwZJn339obI +jyMZC/3D721e5Anj/EqHpc3I9Yn3jRKw1xc8kpNLg/JIAibub8JYyDvT1gO4xjBO ++6EWOBFgDAsf7bSP2xQn1pQFWcA/sY1MnRsWeENmKNrkLXffP+8l1tEcijN+KCSF +ek1mr+qBwSaNV9TA+RXVhvqd3DEKPPJ1WhfxP1K81RdUESvHOV/4kdwnSahDyao0 +EnretBzQkeKeBwoB2u6NTiOmUjk= +-----END CERTIFICATE----- +` + +var testContact = `<xxc(2)LF2ccT+sdqh0AIKlFFeDOJdnxzbQQYhGStgxhOXmijIDkAZiB9kZo+Dl3bRSbBi5pXZ82rOu2IQXz9+5sspChvoccZqgC/dXGhlesmiNy/EbKxWtptTF4tcNyQxtnmCXg1p/HwKey4G2XDekTw86lq6Lpmj72jozvRWlQisqvWz/5deiPaeFGKDKC0OrrDFnIib7WnKqdYt4XyTKdmObnmbvdCbliZq0zBl7J40qKy5FypYXGlZjStIm0R1qtD4XHMZMsrMJEGxdM55zJdSzknXbR8MNahUrGMyUOTivXLHzojYLht0gFQifKMVWhrDjUoVQV43KOLPmdBwY/2Kc5KvVloDeuDXYY0i7tD63gNIp9JA3gJQUJymDdwqbS13riT1DMHHkdTzKEyGdHS+v2l7AVSlJBiTKuyM00FBNuXhhIcFR7ONFCf8cRPOPPBx3Q6iHNsvsca3KPNhwOJBgaQvHSkjIMsudiR954QbwG9rbi2vxVobIgWYMl5j6vlBS/9rfbE/uLdTEQZfNsLKDCIVCCI4I1bYZxZrDLPrfXTrN6W0sCLE7a/kRBQAAAgA7+LwJqiv9O1ogLnS4TYkSEg==xxc>` + +func TestManager_SetAlternativeUserDiscovery(t *testing.T) { + isReg := uint32(1) + + // Create a new Private Key to use for signing the Fact + rng := csprng.NewSystemRNG() + cpk, err := rsa.GenerateKey(rng, 2048) + if err != nil { + t.Fatal(err) + } + + comms, err := client.NewClientComms(nil, nil, nil, nil) + if err != nil { + t.Errorf("Failed to start client comms: %+v", err) + } + + // Create our Manager object + m := Manager{ + comms: comms, + net: newTestNetworkManager(t), + privKey: cpk, + registered: &isReg, + } + + altAddr := "0.0.0.0:11420" + err = m.SetAlternativeUserDiscovery([]byte(testCert), []byte(altAddr), []byte(testContact)) + if err != nil { + t.Fatalf("Unexpected error in SetAlternativeUserDiscovery: %v", err) + } +}