Skip to content
Snippets Groups Projects
Select Git revision
  • fbb8653bdc85f40c88c605c471d01148bbe3cd2b
  • release default
  • master protected
  • hotfix/gtnNoToken
  • XX-4441
  • Jakub/rootless-CI
  • jonah/refactorProviders
  • Ace/Huawei
  • AceVentura/AccountBackup
  • hotfix/delete-error
  • waitingRoundsRewrite
  • dev
  • quantumSecure
  • hotfix/ratelimit
  • fullRateLimit
  • XX-3564/TlsCipherSuite
  • hotfix/notifications-db
  • hotfix/groupNotification
  • Project/LastMile
  • notls
  • url-repo-rename
  • v2.3.0
  • v2.2.0
  • v2.1.0
  • v2.0.0
  • v1.0.0
26 results

Makefile

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)
    }