//////////////////////////////////////////////////////////////////////////////// // Copyright © 2022 xx foundation // // // // Use of this source code is governed by a license that can be found in the // // LICENSE file. // //////////////////////////////////////////////////////////////////////////////// package connect import ( "github.com/golang/protobuf/proto" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/e2e/receive" "gitlab.com/xx_network/crypto/signature/rsa" "gitlab.com/xx_network/primitives/id" ) // authenticatedServerListenerName is the name of the client's //listener interface. const authenticatedServerListenerName = "AuthenticatedServerListener" // server is an interface that wraps receive.Listener. This handles // the server listening for the client's proof of identity message. type server interface { receive.Listener } // serverListener provides an implementation of the server interface. // This will handle the identity message sent by the client. type serverListener struct { // connectionCallback allows an AuthenticatedConnection // to be passed back upon establishment. connectionCallback AuthenticatedCallback // conn used to retrieve the connection context with the partner. conn Connection } // buildAuthConfirmationHandler returns a serverListener object. // This will handle incoming identity authentication confirmations // via the serverListener.Hear method. A successful AuthenticatedConnection // will be passed along via the serverListener.connectionCallback. func buildAuthConfirmationHandler(cb AuthenticatedCallback, connection Connection) server { return &serverListener{ connectionCallback: cb, conn: connection, } } // Hear handles the reception of an IdentityAuthentication by the // server. It will attempt to verify the identity confirmation of // the given client. func (a serverListener) Hear(item receive.Message) { // Process the message data into a protobuf iar := &IdentityAuthentication{} err := proto.Unmarshal(item.Payload, iar) if err != nil { a.handleAuthConfirmationErr(err, item.Sender) return } // Get the new partner's connection fingerprint newPartner := a.conn.GetPartner() connectionFp := newPartner.ConnectionFingerprint().Bytes() // Process the PEM encoded public key to an rsa.PublicKey object partnerPubKey, err := rsa.LoadPublicKeyFromPem(iar.RsaPubKey) if err != nil { a.handleAuthConfirmationErr(err, item.Sender) } // Verify the signature within the message err = verify(newPartner.PartnerId(), partnerPubKey, iar.Signature, connectionFp, iar.Salt) if err != nil { a.handleAuthConfirmationErr(err, item.Sender) return } // If successful, pass along the established authenticated connection // via the callback jww.DEBUG.Printf("AuthenticatedConnection auth request for %s confirmed", item.Sender.String()) authConn := buildAuthenticatedConnection(a.conn) authConn.setAuthenticated() go a.connectionCallback(authConn) } // handleAuthConfirmationErr is a helper function which will close the connection // between the server and the client. It will also print out the passed in error. func (a serverListener) handleAuthConfirmationErr(err error, sender *id.ID) { jww.ERROR.Printf("Unable to build connection with "+ "partner %s: %+v", sender, err) // Send a nil connection to avoid hold-ups down the line a.connectionCallback(nil) err = a.conn.Close() if err != nil { jww.ERROR.Printf("Failed to close connection with partner %s: %v", sender, err) } } // Name returns the name of this listener. This is typically for // printing/debugging purposes. func (a serverListener) Name() string { return authenticatedServerListenerName }