diff --git a/groupChat/send.go b/groupChat/send.go index 8407439f5abc884be387af876d8ce67f7d4ca665..b75f6e209fde626c64f25686928ebbf0c8f01418 100644 --- a/groupChat/send.go +++ b/groupChat/send.go @@ -13,9 +13,7 @@ import ( "gitlab.com/elixxir/client/cmix" "gitlab.com/elixxir/client/cmix/message" gs "gitlab.com/elixxir/client/groupChat/groupStore" - "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/group" - "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/primitives/id" "gitlab.com/xx_network/primitives/netTime" "io" @@ -63,19 +61,12 @@ func (m *manager) Send(groupID *id.ID, tag string, message []byte) ( timeNow := netTime.Now().Round(0) // Create a cMix message for each group member - groupMessages, err := m.newMessages(g, tag, message, timeNow) + groupMessages, msgId, err := m.newMessages(g, tag, message, timeNow) if err != nil { return 0, time.Time{}, group.MessageID{}, errors.Errorf(newCmixMsgErr, g.Name, g.ID, err) } - // Obtain message ID - msgId, err := getGroupMessageId( - m.getE2eGroup(), groupID, m.getReceptionIdentity().ID, timeNow, message) - if err != nil { - return 0, time.Time{}, group.MessageID{}, err - } - // Send all the groupMessages param := cmix.GetDefaultCMIXParams() param.DebugTag = "group.Message" @@ -92,14 +83,39 @@ func (m *manager) Send(groupID *id.ID, tag string, message []byte) ( // newMessages builds a list of messages, one for each group chat member. func (m *manager) newMessages(g gs.Group, tag string, msg []byte, - timestamp time.Time) ([]cmix.TargetedCmixMessage, error) { + timestamp time.Time) ([]cmix.TargetedCmixMessage, group.MessageID, error) { // Create list of cMix messages messages := make([]cmix.TargetedCmixMessage, 0, len(g.Members)) rng := m.getRng().GetStream() defer rng.Close() - // Create cMix messages in parallel + // Generate initial internal message + maxCmixMessageLength := m.getCMix().GetMaxMessageLength() + + // Generate public message to determine what length internal message can be + pubMsg, err := newPublicMsg(maxCmixMessageLength) + if err != nil { + return nil, group.MessageID{}, errors.Errorf(newPublicMsgErr, err) + } + + // Generate internal message + intlMsg, err := newInternalMsg(pubMsg.GetPayloadSize()) + if err != nil { + return nil, group.MessageID{}, errors.Errorf(newInternalMsgErr, err) + } + + // Return an error if the message is too large to fit in the payload + if intlMsg.GetPayloadMaxSize() < len(msg) { + return nil, group.MessageID{}, errors.Errorf( + messageLenErr, len(msg), intlMsg.GetPayloadMaxSize()) + } + + // Generate internal message + internalMessagePayload := setInternalPayload(intlMsg, timestamp, + m.getReceptionIdentity().ID, msg) + + // Create cMix messages for _, member := range g.Members { // Do not send to the sender if m.getReceptionIdentity().ID.Cmp(member.ID) { @@ -107,21 +123,22 @@ func (m *manager) newMessages(g gs.Group, tag string, msg []byte, } // Add cMix message to list - cMixMsg, err := newCmixMsg(g, tag, msg, timestamp, member, rng, - m.getReceptionIdentity().ID, m.getCMix().GetMaxMessageLength()) + cMixMsg, err := newCmixMsg(g, tag, timestamp, member, rng, maxCmixMessageLength, + internalMessagePayload) if err != nil { - return nil, err + return nil, group.MessageID{}, err } messages = append(messages, cMixMsg) } - return messages, nil + return messages, group.NewMessageID(g.ID, internalMessagePayload), nil } // newCmixMsg generates a new cmix.TargetedCmixMessage for the given group // member -func newCmixMsg(g gs.Group, tag string, msg []byte, timestamp time.Time, - mem group.Member, rng io.Reader, senderId *id.ID, maxCmixMessageSize int) ( +func newCmixMsg(g gs.Group, tag string, timestamp time.Time, + mem group.Member, rng io.Reader, maxCmixMessageSize int, + internalMessagePayload []byte) ( cmix.TargetedCmixMessage, error) { // Initialize targeted message @@ -134,18 +151,12 @@ func newCmixMsg(g gs.Group, tag string, msg []byte, timestamp time.Time, }, } - // Create three message layers - pubMsg, intlMsg, err := newMessageParts(maxCmixMessageSize) + // Generate public message + pubMsg, err := newPublicMsg(maxCmixMessageSize) if err != nil { return cmixMsg, err } - // Return an error if the message is too large to fit in the payload - if intlMsg.GetPayloadMaxSize() < len(msg) { - return cmixMsg, errors.Errorf( - messageLenErr, len(msg), intlMsg.GetPayloadMaxSize()) - } - // Generate 256-bit salt salt, err := newSalt(rng) if err != nil { @@ -161,11 +172,9 @@ func newCmixMsg(g gs.Group, tag string, msg []byte, timestamp time.Time, return cmixMsg, errors.WithMessage(err, newKeyErr) } - // Generate internal message - payload := setInternalPayload(intlMsg, timestamp, senderId, msg) - // Encrypt internal message - encryptedPayload := group.Encrypt(key, cmixMsg.Fingerprint, payload) + encryptedPayload := group.Encrypt(key, cmixMsg.Fingerprint, + internalMessagePayload) // Generate public message cmixMsg.Payload = setPublicPayload(pubMsg, salt, encryptedPayload) @@ -176,35 +185,6 @@ func newCmixMsg(g gs.Group, tag string, msg []byte, timestamp time.Time, return cmixMsg, nil } -// getGroupMessageId builds the group message ID. -func getGroupMessageId(grp *cyclic.Group, groupId, senderId *id.ID, - timestamp time.Time, msg []byte) (group.MessageID, error) { - cmixMsg := format.NewMessage(grp.GetP().ByteLen()) - _, intlMsg, err := newMessageParts(cmixMsg.ContentsSize()) - if err != nil { - return group.MessageID{}, errors.WithMessage(err, - "Failed to make message parts for message ID") - } - return group.NewMessageID(groupId, - setInternalPayload(intlMsg, timestamp, senderId, msg)), nil -} - -// newMessageParts generates a public payload message and the internal payload -// message. An error is returned if the messages cannot fit in the payloadSize. -func newMessageParts(payloadSize int) (publicMsg, internalMsg, error) { - pubMsg, err := newPublicMsg(payloadSize) - if err != nil { - return pubMsg, internalMsg{}, errors.Errorf(newPublicMsgErr, err) - } - - intlMsg, err := newInternalMsg(pubMsg.GetPayloadSize()) - if err != nil { - return pubMsg, intlMsg, errors.Errorf(newInternalMsgErr, err) - } - - return pubMsg, intlMsg, nil -} - // newSalt generates a new salt of the specified size. func newSalt(rng io.Reader) ([group.SaltLen]byte, error) { var salt [group.SaltLen]byte diff --git a/groupChat/send_test.go b/groupChat/send_test.go index bac5a18486d3e3a11a021b008f5251116c253850..2b19472859973171c40409527c48412ddda4f306 100644 --- a/groupChat/send_test.go +++ b/groupChat/send_test.go @@ -78,9 +78,9 @@ func TestGroup_newCmixMsg_SaltReaderError(t *testing.T) { m, _ := newTestManager(t) _, err := newCmixMsg( - gs.Group{ID: id.NewIdFromString("test", id.User, t)}, "", - []byte{}, time.Time{}, group.Member{}, strings.NewReader(""), - m.getReceptionIdentity().ID, m.getCMix().GetMaxMessageLength()) + gs.Group{ID: id.NewIdFromString("test", id.User, t)}, + "", time.Time{}, group.Member{}, strings.NewReader(""), + m.getCMix().GetMaxMessageLength(), []byte("internal Message")) if err == nil || !strings.Contains(err.Error(), expectedErr) { t.Errorf("newCmixMsg failed to return the expected error"+ "\nexpected: %s\nreceived: %+v", expectedErr, err) @@ -97,40 +97,16 @@ func TestGroup_newCmixMsg_InternalMsgSizeError(t *testing.T) { // Create test parameters testMsg := make([]byte, 1500) - mem := group.Member{ID: id.NewIdFromString("memberID", id.User, t)} // Create cMix message prng = rand.New(rand.NewSource(42)) - _, err := newCmixMsg(g, "", testMsg, netTime.Now(), mem, prng, - m.getReceptionIdentity().ID, m.getCMix().GetMaxMessageLength()) + _, _, err := m.newMessages(g, "", testMsg, netTime.Now()) if err == nil || !strings.Contains(err.Error(), expectedErr) { t.Errorf("newCmixMsg failed to return the expected error"+ "\nexpected: %s\nreceived: %+v", expectedErr, err) } } -// Error path: payload size too small to fit publicMsg. -func Test_newMessageParts_PublicMsgSizeErr(t *testing.T) { - expectedErr := strings.SplitN(newPublicMsgErr, "%", 2)[0] - - _, _, err := newMessageParts(publicMinLen - 1) - if err == nil || !strings.Contains(err.Error(), expectedErr) { - t.Errorf("newMessageParts did not return the expected error."+ - "\nexpected: %s\nreceived: %+v", expectedErr, err) - } -} - -// Error path: payload size too small to fit internalMsg. -func Test_newMessageParts_InternalMsgSizeErr(t *testing.T) { - expectedErr := strings.SplitN(newInternalMsgErr, "%", 2)[0] - - _, _, err := newMessageParts(publicMinLen) - if err == nil || !strings.Contains(err.Error(), expectedErr) { - t.Errorf("newMessageParts did not return the expected error."+ - "\nexpected: %s\nreceived: %+v", expectedErr, err) - } -} - // Tests the consistency of newSalt. func Test_newSalt_Consistency(t *testing.T) { prng := rand.New(rand.NewSource(42))