From 99fa093df7f9fc0926ecc4a8efbc0ab6fdbc5a85 Mon Sep 17 00:00:00 2001
From: Benjamin Wenger <ben@elixxir.ioo>
Date: Fri, 16 Oct 2020 16:17:23 -0700
Subject: [PATCH] finished ownership and callbacks

---
 auth/callback.go              | 263 +++++++++++++++++++++++++++++++++-
 auth/confirm.go               |  35 +++++
 auth/fmt.go                   |   8 +-
 auth/request.go               |   8 +-
 auth/verify.go                |  13 ++
 interfaces/message/type.go    |   5 +-
 network/message/handler.go    |   2 +-
 network/message/sendE2E.go    |   5 +-
 network/message/sendUnsafe.go |   5 +-
 storage/auth/sentRequest.go   |  51 ++++---
 storage/auth/store.go         |  18 ++-
 storage/e2e/manager.go        |   8 ++
 12 files changed, 381 insertions(+), 40 deletions(-)
 create mode 100644 auth/confirm.go
 create mode 100644 auth/verify.go

diff --git a/auth/callback.go b/auth/callback.go
index 50ab60557..ffb89bf72 100644
--- a/auth/callback.go
+++ b/auth/callback.go
@@ -1,37 +1,288 @@
 package auth
 
 import (
+	"github.com/pkg/errors"
 	"gitlab.com/elixxir/client/interfaces"
 	"gitlab.com/elixxir/client/interfaces/contact"
 	"gitlab.com/elixxir/client/interfaces/message"
 	"gitlab.com/elixxir/client/stoppable"
 	"gitlab.com/elixxir/client/storage"
+	"gitlab.com/elixxir/client/storage/auth"
+	"gitlab.com/elixxir/client/storage/e2e"
+	"gitlab.com/elixxir/crypto/cyclic"
 	"gitlab.com/elixxir/primitives/format"
 	"gitlab.com/xx_network/primitives/id"
-	"time"
+	cAuth "gitlab.com/elixxir/crypto/e2e/auth"
+	jww "github.com/spf13/jwalterweatherman"
+	"io"
+	"strings"
 )
 
 type RequestCallback func(requestor contact.Contact, message string)
 type ConfirmCallback func(partner contact.Contact)
 
 func RegisterCallbacks(rcb RequestCallback, ccb ConfirmCallback,
-	sw interfaces.Switchboard, storage *storage.Session) stoppable.Stoppable {
+	sw interfaces.Switchboard, storage *storage.Session,
+	net interfaces.NetworkManager, rng io.Reader) stoppable.Stoppable {
 
 	rawMessages := make(chan message.Receive, 1000)
 	sw.RegisterChannel("Auth", &id.ID{}, message.Raw, rawMessages)
 
 	stop := stoppable.NewSingle("Auth")
+	authStore := storage.Auth()
+	grp := storage.E2e().GetGroup()
 
 	go func() {
 		select {
 		case <-stop.Quit():
 			return
 		case msg := <-rawMessages:
-			//check the message is well formed
-			if msglen(msg.Payload) != 2*
-				cmixMsg := format.Unmarshal(msg.Payload)
-		}
+			//lookup the message, check if it is an auth request
+			cmixMsg := format.Unmarshal(msg.Payload)
+			fp := cmixMsg.GetKeyFP()
+			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
+			storage.GetGarbledMessages().Remove(cmixMsg)
 
+			switch fpType {
+			// if it is general, that means a new request has been received
+			case auth.General:
+				handleRequest(cmixMsg, myHistoricalPrivKey, grp, storage, rcb,
+					ccb, net)
+			// if it is specific, that means the original request was sent
+			// by this users and a confirmation has been received
+			case auth.Specific:
+				handleConfirm(cmixMsg, sr, ccb, storage, grp, net)
+			}
+		}
 	}()
+	return stop
+}
+
+func handleRequest(cmixMsg format.Message, myHistoricalPrivKey *cyclic.Int,
+	grp *cyclic.Group, storage *storage.Session, rcb RequestCallback,
+	ccb ConfirmCallback, net interfaces.NetworkManager) {
+	//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 := 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 := 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 := doConfirm(sr2, grp, partnerPubKey, ecrFmt.GetOwnership(),
+					storage, ccb, net); err != nil {
+					jww.WARN.Printf("Confirmation failed: %s", err)
+				}
+				//exit
+				return
+			}
+		}
+	}
+
+	//process the inner payload
+	facts, msg, err := contact.UnstringifyFacts(
+		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,
+	}
+
+	//create the auth storage
+	if err = storage.Auth().AddReceived(c); err != nil {
+		jww.WARN.Printf("failed to store contact Auth "+
+			"Request: %s", err)
+		return
+	}
+
+	//call the callback
+
+	go rcb(c, msg)
+	return
+}
+
+func handleConfirm(cmixMsg format.Message, sr *auth.SentRequest,
+	ccb ConfirmCallback, storage *storage.Session, grp *cyclic.Group,
+	net interfaces.NetworkManager) {
+	// check if relationship already exists
+	if m, err := storage.E2e().GetPartner(sr.GetPartner()); m != nil || err == nil {
+		jww.WARN.Printf("Cannot confirm auth for %s, channel already "+
+			"exists.", 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)
+		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")
+		return
+	}
+
+	ecrFmt, err := unmarshalEcrFormat(payload)
+	if err != nil {
+		jww.WARN.Printf("Failed to unmarshal auth confirmation's "+
+			"encrypted payload: %s", err)
+		return
+	}
+
+	// finalize the confirmation
+	if err := doConfirm(sr, grp, partnerPubKey, ecrFmt.GetOwnership(),
+		storage, ccb, net); err != nil {
+		jww.WARN.Printf("Confirmation failed: %s", err)
+		return
+	}
+}
+
+func doConfirm(sr *auth.SentRequest, grp *cyclic.Group,
+	partnerPubKey *cyclic.Int, ownershipProof []byte, storage *storage.Session,
+	ccb ConfirmCallback, net interfaces.NetworkManager) 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())
+	}
 
+	// create the relationship
+	p := e2e.GetDefaultSessionParams()
+	if err := storage.E2e().AddPartner(sr.GetPartner(),
+		partnerPubKey, p, p); err != nil {
+		return errors.Errorf("Failed to create channel with partner (%s) "+
+			"after confirmation: %+v",
+			sr.GetPartner(), err)
+	}
+	net.CheckGarbledMessages()
+
+	// delete the in progress negotiation
+	if err := storage.Auth().Delete(sr.GetPartner()); err != nil {
+		return errors.Errorf("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),
+	}
+
+	go ccb(c)
+
+	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
+}
\ No newline at end of file
diff --git a/auth/confirm.go b/auth/confirm.go
new file mode 100644
index 000000000..c8dcff3e0
--- /dev/null
+++ b/auth/confirm.go
@@ -0,0 +1,35 @@
+package auth
+
+import (
+	"github.com/pkg/errors"
+	"gitlab.com/elixxir/client/interfaces"
+	"gitlab.com/elixxir/client/interfaces/contact"
+	"gitlab.com/elixxir/client/storage"
+	"io"
+)
+
+func ConfirmRequestAuth(partner contact.Contact, rng io.Reader,
+	storage *storage.Session, net interfaces.NetworkManager) error {
+
+	// check that messages can be sent over the network
+	if !net.GetHealthTracker().IsHealthy() {
+		return errors.New("Cannot confirm authenticated message " +
+			"when the network is not healthy")
+	}
+
+	// check if the partner has an auth in progress
+	storedContact, err := storage.Auth().GetReceivedRequest(partner.ID)
+	if err == nil {
+		return errors.Errorf("failed to find a pending Auth Request: %s",
+			err)
+	}
+
+	// verify the passed contact matches what is stored
+	if storedContact.DhPubKey.Cmp(partner.DhPubKey) != 0 {
+		return errors.Errorf("Pending Auth Request has diferent pubkey than : %s",
+			err)
+	}
+
+	// chec
+
+}
diff --git a/auth/fmt.go b/auth/fmt.go
index 39f13a002..5ee0c4874 100644
--- a/auth/fmt.go
+++ b/auth/fmt.go
@@ -40,7 +40,7 @@ func buildBaseFormat(data []byte, pubkeySize int) baseFormat {
 	return f
 }
 
-func unmarshalFormat(b []byte, pubkeySize int) (baseFormat, error) {
+func unmarshalBaseFormat(b []byte, pubkeySize int) (baseFormat, error) {
 	if len(b) < pubkeySize+saltSize {
 		return baseFormat{}, errors.New("Received baseFormat too small")
 	}
@@ -167,9 +167,9 @@ type requestFormat struct {
 	msgPayload []byte
 }
 
-func newRequestFormat(ecrFmt ecrFormat) (requestFormat) {
+func newRequestFormat(ecrFmt ecrFormat) (requestFormat, error) {
 	if len(ecrFmt.payload) < id.ArrIDLen {
-		jww.FATAL.Panicf("Payload is not long enough")
+		return requestFormat{}, errors.New("Payload is not long enough")
 	}
 
 	rf := requestFormat{
@@ -179,7 +179,7 @@ func newRequestFormat(ecrFmt ecrFormat) (requestFormat) {
 	rf.id = rf.payload[:id.ArrIDLen]
 	rf.id = rf.payload[id.ArrIDLen:]
 
-	return rf
+	return rf, nil
 }
 
 func (rf requestFormat) GetID() (*id.ID, error) {
diff --git a/auth/request.go b/auth/request.go
index bafa9c5fb..102da56df 100644
--- a/auth/request.go
+++ b/auth/request.go
@@ -69,7 +69,10 @@ func RequestAuth(partner, me contact.Contact, message string, rng io.Reader,
 	cmixMsg := format.NewMessage(storage.Cmix().GetGroup().GetP().ByteLen())
 	baseFmt := newBaseFormat(cmixMsg.ContentsSize(), grp.GetP().ByteLen())
 	ecrFmt := newEcrFormat(baseFmt.GetEcrPayloadLen())
-	requestFmt := newRequestFormat(ecrFmt)
+	requestFmt, err := newRequestFormat(ecrFmt)
+	if err != nil {
+		return errors.Errorf("failed to make request format: %+v", err)
+	}
 
 	//check the payload fits
 	facts := me.StringifyFacts()
@@ -117,7 +120,8 @@ func RequestAuth(partner, me contact.Contact, message string, rng io.Reader,
 	/*store state*/
 	//fixme: channel is bricked if the first store succedes but the second fails
 	//store the in progress auth
-	err = storage.Auth().AddSent(partner.ID, newPrivKey, newPrivKey, fp)
+	err = storage.Auth().AddSent(partner.ID, partner.DhPubKey, newPrivKey,
+		newPrivKey, fp)
 	if err != nil {
 		return errors.Errorf("Failed to store auth request: %s", err)
 	}
diff --git a/auth/verify.go b/auth/verify.go
new file mode 100644
index 000000000..c456d738d
--- /dev/null
+++ b/auth/verify.go
@@ -0,0 +1,13 @@
+package auth
+
+import (
+	"gitlab.com/elixxir/client/interfaces/contact"
+	"gitlab.com/elixxir/client/storage"
+	cAuth "gitlab.com/elixxir/crypto/e2e/auth"
+)
+
+func VerifyOwnership(received, verified contact.Contact, storage *storage.Session) bool {
+	myHistoricalPrivKey := storage.E2e().GetDHPrivateKey()
+	return cAuth.VerifyOwnershipProof(myHistoricalPrivKey, verified.DhPubKey,
+		storage.E2e().GetGroup(), received.OwnershipProof)
+}
diff --git a/interfaces/message/type.go b/interfaces/message/type.go
index 19c71c879..e6b569b08 100644
--- a/interfaces/message/type.go
+++ b/interfaces/message/type.go
@@ -10,8 +10,9 @@ const (
 	NoType Type = 0
 
 	// A message with no message structure
-	// this is a reserved type, a message sent with this type will be
-	// ignored on reception. To send a raw message use SendCMIX.
+	// this is a reserved type, messages sent via SendCmix automatically gain
+	// this type. Sent messages with this type will be rejected and received
+	// non Cmix messages will be ignored
 	Raw Type = 1
 
 	//General text message, contains human readable text
diff --git a/network/message/handler.go b/network/message/handler.go
index f740f7756..8e3b34f30 100644
--- a/network/message/handler.go
+++ b/network/message/handler.go
@@ -68,7 +68,7 @@ func (m *Manager) handleMessage(ecrMsg format.Message) {
 		raw := message.Receive{
 			Payload:     msg.Marshal(),
 			MessageType: message.Raw,
-			Sender:      &id.ID{},
+			Sender:      msg.GetRecipientID(),
 			Timestamp:   time.Time{},
 			Encryption:  message.None,
 		}
diff --git a/network/message/sendE2E.go b/network/message/sendE2E.go
index e728cdf14..447bc3ce7 100644
--- a/network/message/sendE2E.go
+++ b/network/message/sendE2E.go
@@ -19,7 +19,10 @@ import (
 )
 
 func (m *Manager) SendE2E(msg message.Send, param params.E2E) ([]id.Round, e2e.MessageID, error) {
-
+	if msg.MessageType == message.Raw {
+		return nil, e2e.MessageID{}, errors.Errorf("Raw (%d) is a reserved "+
+			"message type", msg.MessageType)
+	}
 	//timestamp the message
 	ts := time.Now()
 
diff --git a/network/message/sendUnsafe.go b/network/message/sendUnsafe.go
index 6e00fb157..88a1ef2d0 100644
--- a/network/message/sendUnsafe.go
+++ b/network/message/sendUnsafe.go
@@ -21,7 +21,10 @@ import (
 // Sends using SendCMIX and returns a list of rounds the messages are in. Will
 // return an error if a single part of the message fails to send.
 func (m *Manager) SendUnsafe(msg message.Send, param params.Unsafe) ([]id.Round, error) {
-
+	if msg.MessageType == message.Raw {
+		return nil, errors.Errorf("Raw (%d) is a reserved message type",
+			msg.MessageType)
+	}
 	//timestamp the message
 	ts := time.Now()
 
diff --git a/storage/auth/sentRequest.go b/storage/auth/sentRequest.go
index c2c67d728..20f12c42f 100644
--- a/storage/auth/sentRequest.go
+++ b/storage/auth/sentRequest.go
@@ -16,17 +16,19 @@ const currentSentRequestVersion = 0
 type SentRequest struct {
 	kv *versioned.KV
 
-	partner     *id.ID
-	myPrivKey   *cyclic.Int
-	myPubKey    *cyclic.Int
-	fingerprint format.Fingerprint
-	sentMux     sync.Mutex
+	partner                 *id.ID
+	partnerhistoricalPubKey *cyclic.Int
+	myPrivKey               *cyclic.Int
+	myPubKey                *cyclic.Int
+	fingerprint             format.Fingerprint
+	sentMux                 sync.Mutex
 }
 
 type sentRequestDisk struct {
-	MyPrivKey   []byte
-	MyPubKey    []byte
-	Fingerprint []byte
+	PartnerhistoricalPubKey []byte
+	MyPrivKey               []byte
+	MyPubKey                []byte
+	Fingerprint             []byte
 }
 
 func loadSentRequest(kv *versioned.KV, partner *id.ID, grp *cyclic.Group) (*SentRequest, error) {
@@ -43,6 +45,12 @@ func loadSentRequest(kv *versioned.KV, partner *id.ID, grp *cyclic.Group) (*Sent
 			"SentRequest Auth with %s", partner)
 	}
 
+	historicalPrivKey := grp.NewInt(1)
+	if err = historicalPrivKey.GobDecode(srd.PartnerhistoricalPubKey); err != nil {
+		return nil, errors.WithMessagef(err, "Failed to decode historical"+
+			" private key with %s for SentRequest Auth", partner)
+	}
+
 	myPrivKey := grp.NewInt(1)
 	if err = myPrivKey.GobDecode(srd.MyPubKey); err != nil {
 		return nil, errors.WithMessagef(err, "Failed to decode private "+
@@ -59,11 +67,12 @@ func loadSentRequest(kv *versioned.KV, partner *id.ID, grp *cyclic.Group) (*Sent
 	copy(fp[:], srd.Fingerprint)
 
 	return &SentRequest{
-		kv:          kv,
-		partner:     partner,
-		myPrivKey:   myPrivKey,
-		myPubKey:    myPubKey,
-		fingerprint: fp,
+		kv:                      kv,
+		partner:                 partner,
+		partnerhistoricalPubKey: historicalPrivKey,
+		myPrivKey:               myPrivKey,
+		myPubKey:                myPubKey,
+		fingerprint:             fp,
 	}, nil
 }
 
@@ -79,10 +88,16 @@ func (sr *SentRequest) save() error {
 		return err
 	}
 
+	historicalPrivKey, err := sr.myPubKey.GobEncode()
+	if err != nil {
+		return err
+	}
+
 	ipd := sentRequestDisk{
-		MyPrivKey:   privKey,
-		MyPubKey:    pubKey,
-		Fingerprint: sr.fingerprint[:],
+		PartnerhistoricalPubKey: historicalPrivKey,
+		MyPrivKey:               privKey,
+		MyPubKey:                pubKey,
+		Fingerprint:             sr.fingerprint[:],
 	}
 
 	data, err := json.Marshal(&ipd)
@@ -107,6 +122,10 @@ func (sr *SentRequest) GetPartner() *id.ID {
 	return sr.partner
 }
 
+func (sr *SentRequest) GetPartnerHistoricalPubKey() *cyclic.Int {
+	return sr.partnerhistoricalPubKey
+}
+
 func (sr *SentRequest) GetMyPrivKey() *cyclic.Int {
 	return sr.myPrivKey
 }
diff --git a/storage/auth/store.go b/storage/auth/store.go
index e7a15c07e..940a61f59 100644
--- a/storage/auth/store.go
+++ b/storage/auth/store.go
@@ -151,8 +151,8 @@ func (s *Store) save() error {
 	return s.kv.Set(requestMapKey, &obj)
 }
 
-func (s *Store) AddSent(partner *id.ID, myPrivKey, myPubKey *cyclic.Int,
-	fp format.Fingerprint) error {
+func (s *Store) AddSent(partner *id.ID, partnerhistoricalPubKey, myPrivKey,
+	myPubKey *cyclic.Int, fp format.Fingerprint) error {
 	s.mux.Lock()
 	defer s.mux.Unlock()
 
@@ -162,11 +162,12 @@ func (s *Store) AddSent(partner *id.ID, myPrivKey, myPubKey *cyclic.Int,
 	}
 
 	sr := &SentRequest{
-		kv:          s.kv,
-		partner:     partner,
-		myPrivKey:   myPrivKey,
-		myPubKey:    myPubKey,
-		fingerprint: fp,
+		kv:                      s.kv,
+		partner:                 partner,
+		partnerhistoricalPubKey: partnerhistoricalPubKey,
+		myPrivKey:               myPrivKey,
+		myPubKey:                myPubKey,
+		fingerprint:             fp,
 	}
 
 	if err := sr.save(); err != nil {
@@ -253,6 +254,9 @@ func (s *Store) GetFingerprint(fp format.Fingerprint) (FingerprintType,
 		//return the request
 		return Specific, r.Request.sent, nil, nil
 	default:
+		jww.WARN.Printf("Auth request message ignored due to "+
+			"Unknown fingerprint type %d on lookup, should be "+
+			"impossible", r.Type)
 		return 0, nil, nil, errors.Errorf("Unknown fingerprint type")
 	}
 }
diff --git a/storage/e2e/manager.go b/storage/e2e/manager.go
index d395de5be..1ee144555 100644
--- a/storage/e2e/manager.go
+++ b/storage/e2e/manager.go
@@ -191,3 +191,11 @@ func (m *Manager) Confirm(sid SessionID) error {
 func (m *Manager) TriggerNegotiations() []*Session {
 	return m.send.TriggerNegotiation()
 }
+
+func (m *Manager) GetMyOriginPrivateKey() *cyclic.Int {
+	return m.originMyPrivKey.DeepCopy()
+}
+
+func (m *Manager) GetPartnerOriginPublicKey() *cyclic.Int {
+	return m.originPartnerPubKey.DeepCopy()
+}
-- 
GitLab