Skip to content
Snippets Groups Projects
Select Git revision
  • 7072565b3cb2ed122144ef2d039cff038a9e2acb
  • 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

callback.go

Blame
  • callback.go 8.47 KiB
    package auth
    
    import (
    	"github.com/pkg/errors"
    	jww "github.com/spf13/jwalterweatherman"
    	"gitlab.com/elixxir/client/interfaces/contact"
    	"gitlab.com/elixxir/client/stoppable"
    	"gitlab.com/elixxir/client/storage/auth"
    	"gitlab.com/elixxir/client/storage/e2e"
    	"gitlab.com/elixxir/crypto/cyclic"
    	cAuth "gitlab.com/elixxir/crypto/e2e/auth"
    	"gitlab.com/elixxir/primitives/format"
    	"strings"
    )
    
    type RequestCallback func(requestor contact.Contact, message string)
    type ConfirmCallback func(partner contact.Contact)
    
    func (m *Manager) StartProcessies() stoppable.Stoppable {
    
    	stop := stoppable.NewSingle("Auth")
    	authStore := m.storage.Auth()
    	grp := m.storage.E2e().GetGroup()
    
    	go func() {
    		select {
    		case <-stop.Quit():
    			return
    		case msg := <-m.rawMessages:
    			//lookup the message, check if it is an auth request
    			cmixMsg := format.Unmarshal(msg.Payload)
    			fp := cmixMsg.GetKeyFP()
    			// this takes the request lock if it is a specific fp,
    			// all exits after this need to call fail or Delete if it is
    			// specific
    			fpType, sr, myHistoricalPrivKey, err := authStore.GetFingerprint(fp)
    			if err != nil {
    				// if the lookup fails, ignore the message. It is likely
    				// garbled or for a different protocol
    				break
    			}
    
    			//denote that the message is not garbled
    			m.storage.GetGarbledMessages().Remove(cmixMsg)
    
    			switch fpType {
    			// if it is general, that means a new request has been received
    			case auth.General:
    				m.handleRequest(cmixMsg, myHistoricalPrivKey, grp)
    			// if it is specific, that means the original request was sent
    			// by this users and a confirmation has been received
    			case auth.Specific:
    				m.handleConfirm(cmixMsg, sr, grp)
    			}
    		}
    	}()
    	return stop
    }
    
    func (m *Manager) handleRequest(cmixMsg format.Message,
    	myHistoricalPrivKey *cyclic.Int, grp *cyclic.Group) {
    	//decode the outer format
    	baseFmt, partnerPubKey, err := handleBaseFormat(cmixMsg, grp)
    	if err != nil {
    		jww.WARN.Printf("Failed to handle auth request: %s", err)
    		return
    	}
    
    	//decrypt the message
    	success, payload := cAuth.Decrypt(myHistoricalPrivKey,
    		partnerPubKey, baseFmt.GetSalt(), baseFmt.GetEcrPayload(),
    		cmixMsg.GetMac(), grp)
    
    	if !success {
    		jww.WARN.Printf("Recieved auth request failed " +
    			"its mac check")
    		return
    	}
    
    	//decode the ecr format
    	ecrFmt, err := unmarshalEcrFormat(payload)
    	if err != nil {
    		jww.WARN.Printf("Failed to unmarshal auth "+
    			"request's encrypted payload: %s", err)
    		return
    	}
    
    	//decode the request format
    	requestFmt, err := newRequestFormat(ecrFmt)
    	if err != nil {
    		jww.WARN.Printf("Failed to unmarshal auth "+
    			"request's internal payload: %s", err)
    		return
    	}
    
    	partnerID, err := requestFmt.GetID()
    	if err != nil {
    		jww.WARN.Printf("Failed to unmarshal auth "+
    			"request's sender ID: %s", err)
    		return
    	}
    
    	/*do state edge checks*/
    	// check if a relationship already exists.
    	// if it does and the keys used are the same as we have, send a
    	// confirmation in case there are state issues.
    	// do not store
    	if _, err := m.storage.E2e().GetPartner(partnerID); err == nil {
    		jww.WARN.Printf("Recieved Auth request for %s, "+
    			"channel already exists. Ignoring", partnerID)
    		//exit
    		return
    	} else {
    		//check if the relationship already exists,
    		rType, sr2, _, err := m.storage.Auth().GetRequest(partnerID)
    		if err != nil && !strings.Contains(err.Error(), auth.NoRequest) {
    			// if another error is recieved, print it and exist
    			jww.WARN.Printf("Recieved new Auth request for %s, "+
    				"internal lookup produced bad result: %+v",
    				partnerID, err)
    			return
    		} else {
    			//handle the events where the relationship already exists
    			switch rType {
    			// if this is a duplicate, ignore the message
    			case auth.Receive:
    				jww.WARN.Printf("Recieved new Auth request for %s, "+
    					"is a duplicate", partnerID)
    				return
    			// if we sent a request, then automatically confirm
    			// then exit, nothing else needed
    			case auth.Sent:
    				// do the confirmation
    				if err := m.doConfirm(sr2, grp, partnerPubKey,
    					ecrFmt.GetOwnership()); err != nil {
    					jww.WARN.Printf("Confirmation failed: %s", err)
    				}
    				//exit
    				return
    			}
    		}
    	}
    
    	//process the inner payload
    	facts, msg, err := contact.UnstringifyFactList(
    		string(requestFmt.msgPayload))
    	if err != nil {
    		jww.WARN.Printf("failed to parse facts and message "+
    			"from Auth Request: %s", err)
    		return
    	}
    
    	//create the contact
    	c := contact.Contact{
    		ID:             partnerID,
    		DhPubKey:       partnerPubKey,
    		OwnershipProof: copySlice(ecrFmt.ownership),
    		Facts:          facts,
    	}
    
    	// fixme: the client will never be notified of the channel creation if a
    	// crash occurs after the store but before the conclusion of the callback
    	//create the auth storage
    	if err = m.storage.Auth().AddReceived(c); err != nil {
    		jww.WARN.Printf("failed to store contact Auth "+
    			"Request: %s", err)
    		return
    	}
    
    	//  fixme: if a crash occurs before or during the calls, the notification
    	//  will never be sent.
    	cbList := m.requestCallbacks.Get(c.ID)
    	for _, cb := range cbList {
    		rcb := cb.(RequestCallback)
    		go rcb(c, msg)
    	}
    	return
    }
    
    func (m *Manager) handleConfirm(cmixMsg format.Message, sr *auth.SentRequest,
    	grp *cyclic.Group) {
    	// check if relationship already exists
    	if mgr, err := m.storage.E2e().GetPartner(sr.GetPartner()); mgr != nil || err == nil {
    		jww.WARN.Printf("Cannot confirm auth for %s, channel already "+
    			"exists.", sr.GetPartner())
    		m.storage.Auth().Fail(sr.GetPartner())
    		return
    	}
    
    	// extract the message
    	baseFmt, partnerPubKey, err := handleBaseFormat(cmixMsg, grp)
    	if err != nil {
    		jww.WARN.Printf("Failed to handle auth confirm: %s", err)
    		m.storage.Auth().Fail(sr.GetPartner())
    		return
    	}
    
    	// decrypt the payload
    	success, payload := cAuth.Decrypt(sr.GetMyPrivKey(),
    		partnerPubKey, baseFmt.GetSalt(), baseFmt.GetEcrPayload(),
    		cmixMsg.GetMac(), grp)
    
    	if !success {
    		jww.WARN.Printf("Recieved auth confirmation failed its mac " +
    			"check")
    		m.storage.Auth().Fail(sr.GetPartner())
    		return
    	}
    
    	ecrFmt, err := unmarshalEcrFormat(payload)
    	if err != nil {
    		jww.WARN.Printf("Failed to unmarshal auth confirmation's "+
    			"encrypted payload: %s", err)
    		m.storage.Auth().Fail(sr.GetPartner())
    		return
    	}
    
    	// finalize the confirmation
    	if err := m.doConfirm(sr, grp, partnerPubKey, ecrFmt.GetOwnership()); err != nil {
    		jww.WARN.Printf("Confirmation failed: %s", err)
    		m.storage.Auth().Fail(sr.GetPartner())
    		return
    	}
    }
    
    func (m *Manager) doConfirm(sr *auth.SentRequest, grp *cyclic.Group,
    	partnerPubKey *cyclic.Int, ownershipProof []byte) error {
    	// verify the message came from the intended recipient
    	if !cAuth.VerifyOwnershipProof(sr.GetMyPrivKey(),
    		sr.GetPartnerHistoricalPubKey(), grp, ownershipProof) {
    		return errors.Errorf("Failed authenticate identity for auth "+
    			"confirmation of %s", sr.GetPartner())
    	}
    
    	// fixme: channel can get into a bricked state if the first save occurs and
    	// the second does not
    	p := e2e.GetDefaultSessionParams()
    	if err := m.storage.E2e().AddPartner(sr.GetPartner(),
    		partnerPubKey, sr.GetMyPrivKey(), p, p); err != nil {
    		return errors.Errorf("Failed to create channel with partner (%s) "+
    			"after confirmation: %+v",
    			sr.GetPartner(), err)
    	}
    
    	// delete the in progress negotiation
    	// this undoes the request lock
    	if err := m.storage.Auth().Delete(sr.GetPartner()); err != nil {
    		return errors.Errorf("UNRECOVERABLE! Failed to delete in "+
    			"progress negotiation with partner (%s) after confirmation: %+v",
    			sr.GetPartner(), err)
    	}
    
    	//notify the end point
    	c := contact.Contact{
    		ID:             sr.GetPartner().DeepCopy(),
    		DhPubKey:       partnerPubKey.DeepCopy(),
    		OwnershipProof: copySlice(ownershipProof),
    		Facts:          make([]contact.Fact, 0),
    	}
    
    	//  fixme: if a crash occurs before or during the calls, the notification
    	//  will never be sent.
    	cbList := m.confirmCallbacks.Get(c.ID)
    	for _, cb := range cbList {
    		ccb := cb.(ConfirmCallback)
    		go ccb(c)
    	}
    
    	m.net.CheckGarbledMessages()
    
    	return nil
    }
    
    func copySlice(s []byte) []byte {
    	c := make([]byte, len(s))
    	copy(c, s)
    	return c
    }
    
    func handleBaseFormat(cmixMsg format.Message, grp *cyclic.Group) (baseFormat,
    	*cyclic.Int, error) {
    
    	baseFmt, err := unmarshalBaseFormat(cmixMsg.GetContents(),
    		grp.GetP().ByteLen())
    	if err != nil {
    		return baseFormat{}, nil, errors.WithMessage(err, "Failed to"+
    			" unmarshal auth")
    	}
    
    	if !grp.BytesInside(baseFmt.pubkey) {
    		return baseFormat{}, nil, errors.WithMessage(err, "Received "+
    			"auth confirmation public key is not in the e2e cyclic group")
    	}
    	partnerPubKey := grp.NewIntFromBytes(baseFmt.pubkey)
    	return baseFmt, partnerPubKey, nil
    }