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..2d455d90cc2ea0d972dc82eee94f7e8a2773f18d 100644 --- a/api/client.go +++ b/api/client.go @@ -696,6 +696,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,6 +730,15 @@ func (c *Client) DeleteContact(partnerId *id.ID) error { "from %s on contact deletion: %+v", partnerId, 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) + } + if err = c.storage.Auth().Delete(partnerId); err != nil { return err } 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..4f5668fc90a6fcf53d61d6a440fe7fb35f35e7b5 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,19 +152,14 @@ 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 { + if roundReport.TimedOut { allRoundsSucceeded = false - numResults-- - } else if roundReport.TimedOut { - // Generate a message to track the timed out round - timeoutRequest := &pb.HistoricalRounds{ - Rounds: []uint64{roundReport.RoundInfo.ID}, - } - // 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) + anyRoundTimedOut = true } else { // If available, denote the result roundId := id.Round(roundReport.RoundInfo.ID) @@ -180,7 +169,6 @@ func (c *Client) getRoundResults(roundList []id.Round, timeout time.Duration, roundsResults[roundId] = Failed allRoundsSucceeded = false } - numResults-- } } } @@ -219,9 +207,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/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/version_vars.go b/api/version_vars.go index 8ca79de091b8427e4dd1a5f7d0afa55557668dfc..8a44f54952836daac733cc3e151032f5e6b042cb 100644 --- a/api/version_vars.go +++ b/api/version_vars.go @@ -1,15 +1,16 @@ // Code generated by go generate; DO NOT EDIT. // This file was generated by robots at -// 2021-12-27 11:52:01.388598 -0600 CST m=+0.029765559 +// 2022-01-04 12:45:01.875155 -0600 CST m=+0.041061278 package api -const GITVERSION = `6faba65a Merge branch 'XX-3650/dummyMessageSendStatus' into 'release'` -const SEMVER = "3.3.0" +const GITVERSION = `1144194c Merge branch 'dev' into 'release'` +const SEMVER = "4.0.0" const DEPENDENCIES = `module gitlab.com/elixxir/client go 1.13 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 @@ -25,17 +26,17 @@ require ( 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.20211222195106-4ec0e4f02f69 - gitlab.com/elixxir/crypto v0.0.7-0.20211222194952-736897f54f09 + 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.20211222194918-5c28e9620d4e - gitlab.com/xx_network/comms v0.0.4-0.20211222194906-4c28450f7144 - gitlab.com/xx_network/crypto v0.0.5-0.20211222194842-09692b01f03e + 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 - google.golang.org/grpc v1.38.0 + google.golang.org/grpc v1.42.0 google.golang.org/protobuf v1.27.1 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..471d415a8e4147e6718e926553619cd2908403e8 100644 --- a/auth/callback.go +++ b/auth/callback.go @@ -434,6 +434,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..492e758a38c55fb0cda1aed0feedcb8fcce66f73 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 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/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/root.go b/cmd/root.go index 28dd11f80212f4c8e1e7749020c54df2d042707b..a5e84631a369ae594a576c36e5a02c3f1924c9f8 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -599,7 +599,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 diff --git a/cmd/version.go b/cmd/version.go index 23aa30916e42b6883393e6165440b2f6d2f7dbf0..f6443702ed89544cfeda43c6671e75d740bb21fc 100644 --- a/cmd/version.go +++ b/cmd/version.go @@ -18,7 +18,7 @@ import ( ) // Change this value to set the version for this build -const currentVersion = "3.3.0" +const currentVersion = "4.0.0" func Version() string { out := fmt.Sprintf("Elixxir Client v%s -- %s\n\n", api.SEMVER, 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..daeca373c1cba813f000840f39f747049db6262d 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 @@ -270,7 +270,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 +364,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 +444,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 @@ -529,7 +514,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 +550,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/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 640dc632a534b03e130a0a34cd69655706fa4213..612f49f79f95504cc7ef282b6ca2418cb00e7370 100644 --- a/go.mod +++ b/go.mod @@ -19,8 +19,8 @@ require ( 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.20220107213343-9f1e103033c0 - gitlab.com/elixxir/crypto v0.0.7-0.20220107220614-ebd5833c6a97 + gitlab.com/elixxir/comms v0.0.4-0.20220108185838-d88d862a7a6a + gitlab.com/elixxir/crypto v0.0.7-0.20220108181613-7e3f9b9e7434 gitlab.com/elixxir/ekv v0.1.6 gitlab.com/elixxir/primitives v0.0.3-0.20220104173924-275cb9d7834f gitlab.com/xx_network/comms v0.0.4-0.20211227194445-c099754b3cda diff --git a/go.sum b/go.sum index cf19fd81937f634eb318444c3632d385e45fd953..6c1b5be8af49318b1add387e789a73f95cd13cdc 100644 --- a/go.sum +++ b/go.sum @@ -270,18 +270,12 @@ 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.20220107213343-9f1e103033c0 h1:txl/CpDfILEXnbXrKytuQ3DO7mEf0c3gffUOkF07aG8= -gitlab.com/elixxir/comms v0.0.4-0.20220107213343-9f1e103033c0/go.mod h1:hO02O34PPUz1ozBc0UR1UIEooniuoI9ssRXBUiSEU40= +gitlab.com/elixxir/comms v0.0.4-0.20220108185838-d88d862a7a6a h1:CV844Fkl7lFAG1DgPybFGYnmyYNZHSaedxsMqyfoDlI= +gitlab.com/elixxir/comms v0.0.4-0.20220108185838-d88d862a7a6a/go.mod h1:IOzur3kBTqxV24izawS2VmIyvM8qSmXRg+uY5ooVpt8= 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.20220107212555-7b80ecaff6d6 h1:/ZZzlUqeeN+VWnaU+MfOJ743VnkhrUrSWO9pdz9PJHU= -gitlab.com/elixxir/crypto v0.0.7-0.20220107212555-7b80ecaff6d6/go.mod h1:qmW0OGPB21GcaGg1Jvt527/qUw7ke6W8DKCiYBfsx48= -gitlab.com/elixxir/crypto v0.0.7-0.20220107220614-ebd5833c6a97 h1:SPfQBMMCKJHHG5ceKsrYzdixzCGQFf2sL0dO+FeLqks= -gitlab.com/elixxir/crypto v0.0.7-0.20220107220614-ebd5833c6a97/go.mod h1:qmW0OGPB21GcaGg1Jvt527/qUw7ke6W8DKCiYBfsx48= -gitlab.com/elixxir/crypto v0.0.7-0.20220107220814-3dc5e0f3df54 h1:mWxjZTVPV4nxdz7IAWWGUoARWGCKhEhagaq89uSkPBA= -gitlab.com/elixxir/crypto v0.0.7-0.20220107220814-3dc5e0f3df54/go.mod h1:qmW0OGPB21GcaGg1Jvt527/qUw7ke6W8DKCiYBfsx48= -gitlab.com/elixxir/crypto v0.0.7-0.20220107220916-b98ad66916dd h1:OKbDcBirU3zCG+1K/FGu1Z/chjxXEsUGzyQtEQHRm4Q= -gitlab.com/elixxir/crypto v0.0.7-0.20220107220916-b98ad66916dd/go.mod h1:qmW0OGPB21GcaGg1Jvt527/qUw7ke6W8DKCiYBfsx48= +gitlab.com/elixxir/crypto v0.0.7-0.20220108181613-7e3f9b9e7434 h1:4wXhEflExxO5Dzaxmz4WqUFmoRjr3ZNJ8pFBsyRXq+c= +gitlab.com/elixxir/crypto v0.0.7-0.20220108181613-7e3f9b9e7434/go.mod h1:qmW0OGPB21GcaGg1Jvt527/qUw7ke6W8DKCiYBfsx48= 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= diff --git a/groupChat/sendRequests.go b/groupChat/sendRequests.go index edcecc77f1b5516a9ed75d9481738210374b88e4..b48335911b5ff2d193a070dc62455b5378f769fb 100644 --- a/groupChat/sendRequests.go +++ b/groupChat/sendRequests.go @@ -117,7 +117,17 @@ 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() + + 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/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/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/sendCmix.go b/network/message/sendCmix.go index 173fb1f8c2bbb87eb56d48472a737aff950300f9..e0b127542041623ba3437e289d872632c36184a5 100644 --- a/network/message/sendCmix.go +++ b/network/message/sendCmix.go @@ -123,7 +123,7 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message, jww.WARN.Printf("Best round on send is nil") continue } - jww.DEBUG.Printf("[sendCMIX] bestRound: %v", bestRound) + jww.TRACE.Printf("[sendCMIX] bestRound: %v", bestRound) // add the round on to the list of attempted, so it is not tried again attempted.Insert(bestRound.GetRoundId()) @@ -149,7 +149,7 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message, continue } - jww.DEBUG.Printf("[sendCMIX] round %v processed, firstGW: %s", + jww.TRACE.Printf("[sendCMIX] round %v processed, firstGW: %s", bestRound, firstGateway) // Build the messages to send @@ -196,7 +196,7 @@ func sendCmixHelper(sender *gateway.Sender, msg format.Message, } return result, err } - jww.DEBUG.Printf("[sendCMIX] sendToPreferred %s", firstGateway) + jww.TRACE.Printf("[sendCMIX] sendToPreferred %s", firstGateway) result, err := sender.SendToPreferred( []*id.ID{firstGateway}, sendFunc, stop, cmixParams.SendTimeout) jww.DEBUG.Printf("[sendCMIX] sendToPreferred %s returned", 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/rounds/retrieve.go b/network/rounds/retrieve.go index 1db833b43ccf1b415efe44b1d0f10aefe3aa2bed..bd378ecf24728018dbdf824d97efc522bd227ed3 100644 --- a/network/rounds/retrieve.go +++ b/network/rounds/retrieve.go @@ -193,7 +193,7 @@ func (m *Manager) getMessagesFromGateway(roundID id.Round, err = m.Session.UncheckedRounds().Remove(roundID, identity.Source, identity.EphId) if err != nil { - jww.FATAL.Panicf("Failed to remove round %d: %+v", roundID, err) + 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/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/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