From b9d73b83f7b9d68ff6a9d5f3b7d228e931b7d020 Mon Sep 17 00:00:00 2001
From: josh <josh@elixxir.io>
Date: Fri, 6 May 2022 16:19:38 -0700
Subject: [PATCH] Add logic for authenticated connections

---
 catalog/messageTypes.go                       |   5 +
 connections/authenticated/authenticated.go    | 180 ++++++++++++++++++
 connections/authenticated/authenticated.pb.go |  90 +++++++++
 connections/authenticated/authenticated.proto |  21 ++
 connections/authenticated/client.go           | 127 ++++++++++++
 connections/authenticated/generateProto.sh    |   3 +
 connections/authenticated/server.go           | 153 +++++++++++++++
 7 files changed, 579 insertions(+)
 create mode 100644 connections/authenticated/authenticated.go
 create mode 100644 connections/authenticated/authenticated.pb.go
 create mode 100644 connections/authenticated/authenticated.proto
 create mode 100644 connections/authenticated/client.go
 create mode 100755 connections/authenticated/generateProto.sh
 create mode 100644 connections/authenticated/server.go

diff --git a/catalog/messageTypes.go b/catalog/messageTypes.go
index 969e6c703..c6107e056 100644
--- a/catalog/messageTypes.go
+++ b/catalog/messageTypes.go
@@ -42,4 +42,9 @@ const (
 	// EndFileTransfer is sent once all file parts have been transmitted to
 	// inform the receiver that the file transfer has ended.
 	EndFileTransfer = 51
+
+	// IdentityAuthenticationRequest is sent by the recipient
+	// of an authenticated connection request
+	// (see the connections/ package)
+	ConnectionAuthenticationRequest = 60
 )
diff --git a/connections/authenticated/authenticated.go b/connections/authenticated/authenticated.go
new file mode 100644
index 000000000..ea4e9ef2e
--- /dev/null
+++ b/connections/authenticated/authenticated.go
@@ -0,0 +1,180 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                          //
+//                                                                           //
+// Use of this source code is governed by a license that can be found in the //
+// LICENSE file                                                              //
+///////////////////////////////////////////////////////////////////////////////
+
+package authenticated
+
+import (
+	"github.com/pkg/errors"
+	jww "github.com/spf13/jwalterweatherman"
+	"gitlab.com/elixxir/client/auth"
+	"gitlab.com/elixxir/client/catalog"
+	"gitlab.com/elixxir/client/cmix"
+	"gitlab.com/elixxir/client/connections/connect"
+	clientE2e "gitlab.com/elixxir/client/e2e"
+	"gitlab.com/elixxir/client/e2e/ratchet/partner"
+	"gitlab.com/elixxir/client/storage/versioned"
+	"gitlab.com/elixxir/crypto/contact"
+	"gitlab.com/elixxir/crypto/cyclic"
+	"gitlab.com/elixxir/crypto/fastRNG"
+	"gitlab.com/elixxir/ekv"
+	"gitlab.com/xx_network/crypto/signature/rsa"
+	"gitlab.com/xx_network/primitives/id"
+	"sync"
+	"time"
+)
+
+// Connection is a connect.Connection interface that
+// has the receiver authenticating their identity back to the
+// initiator.
+type Connection interface {
+	// Connection is the base connections API. This allows
+	// sending and listening to the partner
+	connect.Connection
+
+	// IsAuthenticated is a function which returns whether the
+	// authenticated connection has been completely established.
+	IsAuthenticated() bool
+}
+
+// ConnectionCallback is the callback format required to retrieve
+// new authenticated.Connection objects as they are established.
+type ConnectionCallback func(connection Connection)
+
+// ConnectWithAuthentication is called by the client, ie the initiator
+// of an authenticated.Connection.  This will send a request for an
+// authenticated connection to the server.
+func ConnectWithAuthentication(recipient contact.Contact,
+	myId *id.ID, dhPrivKey *cyclic.Int,
+	rng *fastRNG.StreamGenerator, grp *cyclic.Group, net cmix.Client,
+	p connect.Params) (Connection, error) {
+
+	// Build an ephemeral KV
+	kv := versioned.NewKV(ekv.MakeMemstore())
+
+	// Build E2e handler
+	err := clientE2e.Init(kv, myId, dhPrivKey, grp, p.Rekey)
+	if err != nil {
+		return nil, err
+	}
+	e2eHandler, err := clientE2e.Load(kv, net, myId, grp, rng, p.Event)
+	if err != nil {
+		return nil, err
+	}
+
+	// Build the callback for the connection being established
+	authConnChan := make(chan Connection, 1)
+	cb := func(authConn Connection) {
+		authConnChan <- authConn
+	}
+
+	// Build callback for E2E negotiation in the auth package
+	clientHandler := getClient(cb, e2eHandler, p)
+
+	// Register a listener for the server's response
+	e2eHandler.RegisterListener(recipient.ID,
+		catalog.ConnectionAuthenticationRequest,
+		clientHandler)
+
+	// Build auth object for E2E negotiation
+	authState, err := auth.NewState(kv, net, e2eHandler,
+		rng, p.Event, p.Auth, clientHandler, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	// Perform the auth request
+	_, err = authState.Request(recipient, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	// Block waiting for auth to confirm
+	jww.DEBUG.Printf("Connection waiting for authenticated "+
+		"connection with %s to be established...", recipient.ID.String())
+
+	timeout := time.NewTimer(p.Timeout)
+	select {
+	case newConnection := <-authConnChan:
+		if newConnection == nil {
+			return nil, errors.Errorf(
+				"Unable to complete authenticated connection with partner %s",
+				recipient.ID.String())
+		}
+
+		return newConnection, nil
+	case <-timeout.C:
+		return nil, errors.Errorf("Authenticated connection with "+
+			"partner %s timed out", recipient.ID.String())
+	}
+}
+
+// StartAuthenticatedConnectionServer is Called by the receiver of an
+// authenticated connection request. Calling this indicated that they will
+// recognize and respond to identity authentication requests by
+// a client.
+func StartAuthenticatedConnectionServer(cb ConnectionCallback,
+	myId *id.ID, salt []byte, rsaPrivkey *rsa.PrivateKey, privKey *cyclic.Int,
+	rng *fastRNG.StreamGenerator, grp *cyclic.Group, net cmix.Client,
+	p connect.Params) error {
+
+	// Build an ephemeral KV
+	kv := versioned.NewKV(ekv.MakeMemstore())
+
+	// Build E2e handler
+	err := clientE2e.Init(kv, myId, privKey, grp, p.Rekey)
+	if err != nil {
+		return err
+	}
+	e2eHandler, err := clientE2e.Load(kv, net, myId, grp, rng, p.Event)
+	if err != nil {
+		return err
+	}
+
+	// Build callback for E2E negotiation
+	callback := getServer(cb, e2eHandler, net, rsaPrivkey, rng, p)
+
+	// Build auth object for E2E negotiation
+	_, err = auth.NewState(kv, net, e2eHandler,
+		rng, p.Event, p.Auth, callback, nil)
+
+	return err
+}
+
+// handler provides an implementation for the authenticated.Connection
+// interface.
+type handler struct {
+	connect.Connection
+	isAuthenticated bool
+	authMux         sync.Mutex
+}
+
+// buildAuthenticatedConnection assembles an authenticated.Connection object.
+// This is called by the connection server once it has sent an
+// IdentityAuthentication to the client.
+// This is called by the client when they have received and confirmed the data within
+// a IdentityAuthentication message.
+func buildAuthenticatedConnection(partner partner.Manager,
+	e2eHandler clientE2e.Handler,
+	p connect.Params) *handler {
+
+	return &handler{
+		Connection: connect.BuildConnection(partner, e2eHandler, p),
+	}
+}
+
+// IsAuthenticated returns whether the Connection has completed the authentication
+// process.
+func (h *handler) IsAuthenticated() bool {
+	return h.isAuthenticated
+}
+
+// setAuthenticated is a helper function which sets the Connection as authenticated.
+func (h *handler) setAuthenticated() {
+	h.authMux.Lock()
+	defer h.authMux.Unlock()
+	h.isAuthenticated = true
+}
diff --git a/connections/authenticated/authenticated.pb.go b/connections/authenticated/authenticated.pb.go
new file mode 100644
index 000000000..86716c0e6
--- /dev/null
+++ b/connections/authenticated/authenticated.pb.go
@@ -0,0 +1,90 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: connections/authenticated/authenticated.proto
+
+package authenticated // import "gitlab.com/elixxir/client/connections/authenticated"
+
+import proto "github.com/golang/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+
+// Sent by the receiver of the authenticated connection request.
+type IdentityAuthentication struct {
+	Signature []byte `protobuf:"bytes,1,opt,name=Signature,proto3" json:"Signature,omitempty"`
+	// established between the two partners
+	RsaPubKey            []byte   `protobuf:"bytes,2,opt,name=RsaPubKey,proto3" json:"RsaPubKey,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *IdentityAuthentication) Reset()         { *m = IdentityAuthentication{} }
+func (m *IdentityAuthentication) String() string { return proto.CompactTextString(m) }
+func (*IdentityAuthentication) ProtoMessage()    {}
+func (*IdentityAuthentication) Descriptor() ([]byte, []int) {
+	return fileDescriptor_authenticated_3bb97abbbfff0896, []int{0}
+}
+func (m *IdentityAuthentication) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_IdentityAuthentication.Unmarshal(m, b)
+}
+func (m *IdentityAuthentication) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_IdentityAuthentication.Marshal(b, m, deterministic)
+}
+func (dst *IdentityAuthentication) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_IdentityAuthentication.Merge(dst, src)
+}
+func (m *IdentityAuthentication) XXX_Size() int {
+	return xxx_messageInfo_IdentityAuthentication.Size(m)
+}
+func (m *IdentityAuthentication) XXX_DiscardUnknown() {
+	xxx_messageInfo_IdentityAuthentication.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_IdentityAuthentication proto.InternalMessageInfo
+
+func (m *IdentityAuthentication) GetSignature() []byte {
+	if m != nil {
+		return m.Signature
+	}
+	return nil
+}
+
+func (m *IdentityAuthentication) GetRsaPubKey() []byte {
+	if m != nil {
+		return m.RsaPubKey
+	}
+	return nil
+}
+
+func init() {
+	proto.RegisterType((*IdentityAuthentication)(nil), "authenticatedConnectionMessages.IdentityAuthentication")
+}
+
+func init() {
+	proto.RegisterFile("connections/authenticated/authenticated.proto", fileDescriptor_authenticated_3bb97abbbfff0896)
+}
+
+var fileDescriptor_authenticated_3bb97abbbfff0896 = []byte{
+	// 166 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xd2, 0x4d, 0xce, 0xcf, 0xcb,
+	0x4b, 0x4d, 0x2e, 0xc9, 0xcc, 0xcf, 0x2b, 0xd6, 0x4f, 0x2c, 0x2d, 0xc9, 0x48, 0xcd, 0x2b, 0xc9,
+	0x4c, 0x4e, 0x2c, 0x49, 0x4d, 0x41, 0xe5, 0xe9, 0x15, 0x14, 0xe5, 0x97, 0xe4, 0x0b, 0xc9, 0xa3,
+	0x08, 0x3a, 0xc3, 0xf5, 0xfa, 0xa6, 0x16, 0x17, 0x27, 0xa6, 0xa7, 0x16, 0x2b, 0x85, 0x70, 0x89,
+	0x79, 0xa6, 0x80, 0x14, 0x94, 0x54, 0x3a, 0x22, 0x94, 0x66, 0xe6, 0xe7, 0x09, 0xc9, 0x70, 0x71,
+	0x06, 0x67, 0xa6, 0xe7, 0x25, 0x96, 0x94, 0x16, 0xa5, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0xf0, 0x04,
+	0x21, 0x04, 0x40, 0xb2, 0x41, 0xc5, 0x89, 0x01, 0xa5, 0x49, 0xde, 0xa9, 0x95, 0x12, 0x4c, 0x10,
+	0x59, 0xb8, 0x80, 0x93, 0x69, 0x94, 0x71, 0x7a, 0x66, 0x49, 0x4e, 0x62, 0x92, 0x5e, 0x72, 0x7e,
+	0xae, 0x7e, 0x6a, 0x4e, 0x66, 0x45, 0x45, 0x66, 0x91, 0x7e, 0x72, 0x4e, 0x66, 0x6a, 0x5e, 0x89,
+	0x3e, 0x4e, 0x1f, 0x24, 0xb1, 0x81, 0x1d, 0x6d, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x7c, 0xda,
+	0xe4, 0xd4, 0xe5, 0x00, 0x00, 0x00,
+}
diff --git a/connections/authenticated/authenticated.proto b/connections/authenticated/authenticated.proto
new file mode 100644
index 000000000..492bad5de
--- /dev/null
+++ b/connections/authenticated/authenticated.proto
@@ -0,0 +1,21 @@
+///////////////////////////////////////////////////////////////////////////////
+// Copyright © 2020 xx network SEZC                                          //
+//                                                                           //
+// Use of this source code is governed by a license that can be found in the //
+// LICENSE file                                                              //
+///////////////////////////////////////////////////////////////////////////////
+
+syntax = "proto3";
+package authenticatedConnectionMessages;
+option go_package = "gitlab.com/elixxir/client/connections/authenticated";
+
+// Sent by the receiver of the authenticated connection request.
+message IdentityAuthentication {
+  bytes Signature      = 1;  // Signature of the connection fingerprint
+        // established between the two partners
+  bytes RsaPubKey = 2; // The RSA public key of the sender of this message,
+        // PEM-encoded
+}
+
+
+
diff --git a/connections/authenticated/client.go b/connections/authenticated/client.go
new file mode 100644
index 000000000..e1d137f2e
--- /dev/null
+++ b/connections/authenticated/client.go
@@ -0,0 +1,127 @@
+package authenticated
+
+import (
+	"github.com/golang/protobuf/proto"
+	jww "github.com/spf13/jwalterweatherman"
+	"gitlab.com/elixxir/client/auth"
+	"gitlab.com/elixxir/client/cmix/identity/receptionID"
+	"gitlab.com/elixxir/client/cmix/rounds"
+	"gitlab.com/elixxir/client/connections/connect"
+	clientE2e "gitlab.com/elixxir/client/e2e"
+	"gitlab.com/elixxir/client/e2e/receive"
+	"gitlab.com/elixxir/crypto/contact"
+	"gitlab.com/xx_network/crypto/signature/rsa"
+)
+
+// clientListenerName is the name of the client's listener interface.
+const clientListenerName = "AuthenticatedClientListener"
+
+// client is an interface that collates the receive.Listener and
+// auth.Callbacks interfaces. This allows a singular object
+// to handle auth package endpoints as well as handling the
+// custom catalog.MessageType's.
+type client interface {
+	receive.Listener
+	auth.Callbacks
+}
+
+// clientHandler provides an implementation of the client interface.
+type clientHandler struct {
+	// connectionCallback allows an authenticated.Connection
+	// to be passed back upon establishment.
+	connectionCallback ConnectionCallback
+
+	// Used for building new Connection objects
+	connectionE2e    clientE2e.Handler
+	connectionParams connect.Params
+}
+
+// getClient returns a client interface to be used to handle
+// auth.Callbacks and receive.Listener operations.
+func getClient(cb ConnectionCallback,
+	e2e clientE2e.Handler, p connect.Params) client {
+	return clientHandler{
+		connectionCallback: cb,
+		connectionE2e:      e2e,
+		connectionParams:   p,
+	}
+}
+
+// Request will be called when an auth Request message is processed.
+func (a clientHandler) Request(requestor contact.Contact,
+	receptionID receptionID.EphemeralIdentity, round rounds.Round) {
+}
+
+// Confirm will be called when an auth Confirm message is processed.
+func (a clientHandler) Confirm(requestor contact.Contact,
+	receptionID receptionID.EphemeralIdentity, round rounds.Round) {
+}
+
+// Reset will be called when an auth Reset operation occurs.
+func (a clientHandler) Reset(requestor contact.Contact,
+	receptionID receptionID.EphemeralIdentity, round rounds.Round) {
+}
+
+// Hear handles the reception of an IdentityAuthentication by the
+// server.
+func (a clientHandler) Hear(item receive.Message) {
+	// Process the message data into a protobuf
+	iar := &IdentityAuthentication{}
+	err := proto.Unmarshal(item.Payload, iar)
+	if err != nil {
+		jww.ERROR.Printf("Unable to build connection with "+
+			"partner %s: %+v", item.Sender, err)
+		// Send a nil connection to avoid hold-ups down the line
+		a.connectionCallback(nil)
+		return
+	}
+
+	// Process the PEM encoded public key to an rsa.PublicKey object
+	partnerPubKey, err := rsa.LoadPublicKeyFromPem(iar.RsaPubKey)
+	if err != nil {
+		jww.ERROR.Printf("Unable to build connection with "+
+			"partner %s: %+v", item.Sender, err)
+		// Send a nil connection to avoid hold-ups down the line
+		a.connectionCallback(nil)
+		return
+	}
+
+	// Get the new partner
+	newPartner, err := a.connectionE2e.GetPartner(item.Sender)
+	if err != nil {
+		jww.ERROR.Printf("Unable to build connection with "+
+			"partner %s: %+v", item.Sender, err)
+		// Send a nil connection to avoid hold-ups down the line
+		a.connectionCallback(nil)
+		return
+	}
+
+	// The connection fingerprint (hashed) represents a shared nonce
+	// between these two partners
+	conneptionFp := newPartner.ConnectionFingerprint().Bytes()
+
+	// Hash the connection fingerprint
+	opts := rsa.NewDefaultOptions()
+	h := opts.Hash.New()
+	h.Write(conneptionFp)
+	nonce := h.Sum(nil)
+
+	// Verify the signature
+	err = rsa.Verify(partnerPubKey, opts.Hash, nonce, iar.Signature, opts)
+	if err != nil {
+		jww.ERROR.Printf("Unable to build connection with "+
+			"partner %s: %+v", item.Sender, err)
+		// Send a nil connection to avoid hold-ups down the line
+		a.connectionCallback(nil)
+	}
+
+	// If successful, pass along the established connection
+	jww.DEBUG.Printf("Connection auth request for %s confirmed",
+		item.Sender.String())
+	a.connectionCallback(buildAuthenticatedConnection(newPartner, a.connectionE2e,
+		a.connectionParams))
+}
+
+func (a clientHandler) Name() string {
+	return clientListenerName
+}
diff --git a/connections/authenticated/generateProto.sh b/connections/authenticated/generateProto.sh
new file mode 100755
index 000000000..43c03831f
--- /dev/null
+++ b/connections/authenticated/generateProto.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+protoc --go_out=paths=source_relative:. connections/authenticated/authenticated.proto
diff --git a/connections/authenticated/server.go b/connections/authenticated/server.go
new file mode 100644
index 000000000..65c3b5eb8
--- /dev/null
+++ b/connections/authenticated/server.go
@@ -0,0 +1,153 @@
+package authenticated
+
+import (
+	"github.com/golang/protobuf/proto"
+	jww "github.com/spf13/jwalterweatherman"
+	"gitlab.com/elixxir/client/catalog"
+	"gitlab.com/elixxir/client/cmix"
+	"gitlab.com/elixxir/client/cmix/identity/receptionID"
+	"gitlab.com/elixxir/client/cmix/rounds"
+	"gitlab.com/elixxir/client/connections/connect"
+	clientE2e "gitlab.com/elixxir/client/e2e"
+	"gitlab.com/elixxir/crypto/contact"
+	"gitlab.com/elixxir/crypto/fastRNG"
+	"gitlab.com/xx_network/crypto/signature/rsa"
+	"gitlab.com/xx_network/primitives/id"
+)
+
+// serverHandler handles the io operations of an
+// authenticated.Connection server.
+type serverHandler struct {
+	// connectionCallback allows an authenticated.Connection
+	// to be passed back upon establishment.
+	connectionCallback ConnectionCallback
+
+	// Used for building new Connection objects
+	connectionE2e    clientE2e.Handler
+	connectionParams connect.Params
+
+	// Used for tracking the round the identity confirmation message was sent.
+	// A successful round assumes the client received the confirmation and
+	// an authenticated.Connection has been established
+	services cmix.Client
+
+	// Used for signing the connection fingerprint used as a nonce
+	// to confirm the user's identity
+	privateKey *rsa.PrivateKey
+	rng        *fastRNG.StreamGenerator
+}
+
+// getServer returns a serverHandler object. This is used to pass
+// into the auth.State object to handle the auth.Callbacks.
+func getServer(cb ConnectionCallback,
+	e2e clientE2e.Handler, services cmix.Client,
+	privateKey *rsa.PrivateKey,
+	rng *fastRNG.StreamGenerator, p connect.Params) serverHandler {
+	return serverHandler{
+		connectionCallback: cb,
+		connectionE2e:      e2e,
+		connectionParams:   p,
+		privateKey:         privateKey,
+		rng:                rng,
+		services:           services,
+	}
+}
+
+// Request will be called when an auth Request message is processed.
+func (a serverHandler) Request(requestor contact.Contact,
+	receptionID receptionID.EphemeralIdentity, round rounds.Round) {
+
+	// After confirmation, get the new partner
+	newPartner, err := a.connectionE2e.GetPartner(requestor.ID)
+	if err != nil {
+		jww.ERROR.Printf("Unable to build connection with "+
+			"partner %s: %+v", requestor.ID, err)
+		// Send a nil connection to avoid hold-ups down the line
+		a.connectionCallback(nil)
+		return
+	}
+
+	authConn := buildAuthenticatedConnection(newPartner, a.connectionE2e,
+		a.connectionParams)
+
+	// The connection fingerprint (hashed) represents a shared nonce
+	// between these two partners
+	connectionFp := newPartner.ConnectionFingerprint().Bytes()
+
+	opts := rsa.NewDefaultOptions()
+	h := opts.Hash.New()
+	h.Write(connectionFp)
+	nonce := h.Sum(nil)
+
+	// Sign the connection fingerprint
+	stream := a.rng.GetStream()
+	defer stream.Close()
+	signature, err := rsa.Sign(stream, a.privateKey,
+		opts.Hash, nonce, opts)
+	if err != nil {
+		jww.ERROR.Printf("Unable to build connection with "+
+			"partner %s: %+v", requestor.ID, err)
+		// Send a nil connection to avoid hold-ups down the line
+		a.connectionCallback(nil)
+	}
+
+	// Construct message
+	pemEncodedRsaPubKey := rsa.CreatePublicKeyPem(a.privateKey.GetPublic())
+	iar := &IdentityAuthentication{
+		Signature: signature,
+		RsaPubKey: pemEncodedRsaPubKey,
+	}
+	payload, err := proto.Marshal(iar)
+	if err != nil {
+		jww.ERROR.Printf("Unable to build connection with "+
+			"partner %s: %+v", requestor.ID, err)
+		// Send a nil connection to avoid hold-ups down the line
+		a.connectionCallback(nil)
+	}
+
+	// Send message to user
+	rids, _, _, err := authConn.SendE2E(catalog.ConnectionAuthenticationRequest,
+		payload, clientE2e.GetDefaultParams())
+	if err != nil {
+		jww.ERROR.Printf("Unable to build connection with "+
+			"partner %s: %+v", requestor.ID, err)
+		// Send a nil connection to avoid hold-ups down the line
+		a.connectionCallback(nil)
+	}
+
+	// Determine that the message is properly sent by tracking the success
+	// of the round(s)
+	roundCb := cmix.RoundEventCallback(func(allRoundsSucceeded,
+		timedOut bool, rounds map[id.Round]cmix.RoundResult) {
+		if allRoundsSucceeded {
+			// If rounds succeeded, assume recipient has successfully
+			// confirmed the authentication
+			authConn.setAuthenticated()
+			a.connectionCallback(authConn)
+		} else {
+			jww.ERROR.Printf("Unable to build connection with "+
+				"partner %s: %+v", requestor.ID, err)
+			// Send a nil connection to avoid hold-ups down the line
+			a.connectionCallback(nil)
+		}
+	})
+	err = a.services.GetRoundResults(a.connectionParams.Timeout,
+		roundCb, rids...)
+	if err != nil {
+		jww.ERROR.Printf("Unable to build connection with "+
+			"partner %s: %+v", requestor.ID, err)
+		// Send a nil connection to avoid hold-ups down the line
+		a.connectionCallback(nil)
+	}
+
+}
+
+// Confirm will be called when an auth Confirm message is processed.
+func (a serverHandler) Confirm(requestor contact.Contact,
+	receptionID receptionID.EphemeralIdentity, round rounds.Round) {
+}
+
+// Reset will be called when an auth Reset operation occurs.
+func (a serverHandler) Reset(requestor contact.Contact,
+	receptionID receptionID.EphemeralIdentity, round rounds.Round) {
+}
-- 
GitLab