Skip to content
Snippets Groups Projects
Select Git revision
  • d6d8e637976ccec68e2b8469e0d22fa081b2abf1
  • release default protected
  • 11-22-implement-kv-interface-defined-in-collectiveversionedkvgo
  • hotfix/TestHostPool_UpdateNdf_AddFilter
  • XX-4719/announcementChannels
  • xx-4717/logLevel
  • jonah/noob-channel
  • master protected
  • XX-4707/tagDiskJson
  • xx-4698/notification-retry
  • hotfix/notifylockup
  • syncNodes
  • hotfix/localCB
  • XX-4677/NewChanManagerMobile
  • XX-4689/DmSync
  • duplicatePrefix
  • XX-4601/HavenInvites
  • finalizedUICallbacks
  • XX-4673/AdminKeySync
  • debugNotifID
  • anne/test
  • v4.7.5
  • v4.7.4
  • v4.7.3
  • v4.7.2
  • v4.7.1
  • v4.6.3
  • v4.6.1
  • v4.5.0
  • v4.4.4
  • v4.3.11
  • v4.3.8
  • v4.3.7
  • v4.3.6
  • v4.3.5
  • v4.2.0
  • v4.3.0
  • v4.3.4
  • v4.3.3
  • v4.3.2
  • v4.3.1
41 results

receive.go

Blame
  • receive.go 5.04 KiB
    ///////////////////////////////////////////////////////////////////////////////
    // Copyright © 2020 xx network SEZC                                          //
    //                                                                           //
    // Use of this source code is governed by a license that can be found in the //
    // LICENSE file                                                              //
    ///////////////////////////////////////////////////////////////////////////////
    
    package groupChat
    
    import (
    	"github.com/pkg/errors"
    	jww "github.com/spf13/jwalterweatherman"
    	gs "gitlab.com/elixxir/client/groupChat/groupStore"
    	"gitlab.com/elixxir/client/network/historical"
    	"gitlab.com/elixxir/client/network/identity/receptionID"
    	"gitlab.com/elixxir/crypto/group"
    	"gitlab.com/elixxir/primitives/format"
    	"gitlab.com/elixxir/primitives/states"
    	"time"
    )
    
    // Error messages.
    const (
    	newDecryptKeyErr        = "failed to generate key for decrypting group payload: %+v"
    	unmarshalInternalMsgErr = "failed to unmarshal group internal message: %+v"
    	unmarshalSenderIdErr    = "failed to unmarshal sender ID: %+v"
    	unmarshalPublicMsgErr   = "failed to unmarshal group cMix message contents: %+v"
    	findGroupKeyFpErr       = "no group with key fingerprint %s"
    	genCryptKeyMacErr       = "failed to generate encryption key for group " +
    		"cMix message because MAC verification failed (epoch %d could be off)"
    )
    
    // Adheres to network.Manager interface for reception processing
    type receptionProcessor struct {
    	m *Manager
    	g gs.Group
    }
    
    // Process incoming group chat messages
    func (p *receptionProcessor) Process(message format.Message, receptionID receptionID.EphemeralIdentity, round historical.Round) {
    	jww.TRACE.Print("Group message reception received cMix message.")
    
    	// Attempt to read the message
    	roundTimestamp := round.Timestamps[states.QUEUED]
    
    	// Unmarshal cMix message contents to get public message format
    	pubMsg, err := unmarshalPublicMsg(message.GetContents())
    	if err != nil {
    		jww.WARN.Printf("Failed to unmarshal: %+v", errors.Errorf(unmarshalPublicMsgErr, err))
    	}
    
    	// Obtain the cryptKey for the public message
    	key, err := getCryptKey(p.g.Key, pubMsg.GetSalt(), message.GetMac(),
    		pubMsg.GetPayload(), p.g.DhKeys, roundTimestamp)
    	if err != nil {
    		jww.WARN.Printf("Unable to getCryptKey: %+v", err)
    		return
    	}
    
    	// Decrypt the message payload using the cryptKey
    	result, err := decryptMessage(p.g, message.GetKeyFP(), key, pubMsg.GetPayload())
    	if err != nil {
    		jww.WARN.Printf("Group message reception failed to read "+
    			"cMix message: %+v", err)
    		return
    	}
    	// Populate remaining fields from the top level
    	result.GroupID = p.g.ID
    	result.RecipientID = receptionID.Source
    	result.EphemeralID = receptionID.EphId
    	result.RoundID = round.ID
    	result.RoundTimestamp = roundTimestamp
    
    	jww.DEBUG.Printf("Received group message with ID %s from sender "+
    		"%s in group %s with ID %s at %s.", result.ID, result.SenderID, p.g.Name,
    		p.g.ID, result.Timestamp)
    
    	// If the message was read correctly, send it to the callback
    	p.m.receiveFunc(result)
    }
    
    // decryptMessage decrypts the group message payload and returns its message ID,
    // timestamp, sender ID, and message contents.
    func decryptMessage(g gs.Group, fingerprint format.Fingerprint, key group.CryptKey, payload []byte) (
    	MessageReceive, error) {
    
    	// Decrypt internal message
    	decryptedPayload := group.Decrypt(key, fingerprint, payload)
    
    	// Unmarshal internal message
    	intlMsg, err := unmarshalInternalMsg(decryptedPayload)
    	if err != nil {
    		return MessageReceive{}, errors.Errorf(unmarshalInternalMsgErr, err)
    	}
    
    	// Unmarshal sender ID
    	senderID, err := intlMsg.GetSenderID()
    	if err != nil {
    		return MessageReceive{}, errors.Errorf(unmarshalSenderIdErr, err)
    	}
    
    	return MessageReceive{
    		ID:        group.NewMessageID(g.ID, intlMsg.Marshal()),
    		Payload:   intlMsg.GetPayload(),
    		SenderID:  senderID,
    		Timestamp: intlMsg.GetTimestamp(),
    	}, nil
    }
    
    // getCryptKey generates the decryption key for a group internal message. The
    // key is generated using the group key, an epoch, and a salt. The epoch is
    // based off the round timestamp. So, to avoid missing the correct epoch, the
    // current, past, and next epochs are checked until one of them produces a key
    // that matches the message's MAC. The DH key is also unknown, so each member's
    // DH key is tried until there is a match.
    func getCryptKey(key group.Key, salt [group.SaltLen]byte, mac, payload []byte,
    	dhKeys gs.DhKeyList, roundTimestamp time.Time) (group.CryptKey, error) {
    
    	// Compute the current epoch
    	epoch := group.ComputeEpoch(roundTimestamp)
    
    	for _, dhKey := range dhKeys {
    
    		// Create a key with the correct epoch
    		for _, epoch := range []uint32{epoch, epoch - 1, epoch + 1} {
    			// Generate key
    			cryptKey, err := group.NewKdfKey(key, epoch, salt)
    			if err != nil {
    				return group.CryptKey{}, errors.Errorf(newDecryptKeyErr, err)
    			}
    
    			// Return the key if the MAC matches
    			if group.CheckMAC(mac, cryptKey, payload, dhKey) {
    				return cryptKey, nil
    			}
    		}
    	}
    
    	// Return an error if none of the epochs worked
    	return group.CryptKey{}, errors.Errorf(genCryptKeyMacErr, epoch)
    }