Skip to content
Snippets Groups Projects
Commit bd39e4fc authored by Josh Brooks's avatar Josh Brooks
Browse files

Restructure authenticated connections

parent cc3b404b
No related branches found
No related tags found
3 merge requests!510Release,!216Xx 3895/authenticated connection,!207WIP: Client Restructure
...@@ -13,11 +13,13 @@ import ( ...@@ -13,11 +13,13 @@ import (
"gitlab.com/elixxir/client/catalog" "gitlab.com/elixxir/client/catalog"
"gitlab.com/elixxir/client/cmix" "gitlab.com/elixxir/client/cmix"
"gitlab.com/elixxir/client/connections/connect" "gitlab.com/elixxir/client/connections/connect"
clientE2e "gitlab.com/elixxir/client/e2e"
"gitlab.com/elixxir/crypto/contact" "gitlab.com/elixxir/crypto/contact"
"gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/cyclic"
"gitlab.com/elixxir/crypto/fastRNG" "gitlab.com/elixxir/crypto/fastRNG"
"gitlab.com/xx_network/crypto/signature/rsa" "gitlab.com/xx_network/crypto/signature/rsa"
"gitlab.com/xx_network/primitives/id" "gitlab.com/xx_network/primitives/id"
"gitlab.com/xx_network/primitives/netTime"
"sync" "sync"
"time" "time"
) )
...@@ -39,27 +41,74 @@ type Connection interface { ...@@ -39,27 +41,74 @@ type Connection interface {
// new authenticated.Connection objects as they are established. // new authenticated.Connection objects as they are established.
type ConnectionCallback func(connection Connection) type ConnectionCallback func(connection Connection)
// ConnectWithAuthentication is called by the client, ie the initiator // ConnectWithAuthentication is called by the client, ie the one establishing
// of establishing a connection. This will establish a connect.Connection with // connection with the server. Once a connect.Connection has been established
// the server and then authenticate their identity to the server. // with the server and then authenticate their identity to the server.
func ConnectWithAuthentication(recipient contact.Contact, myId *id.ID, func ConnectWithAuthentication(recipient contact.Contact, myId *id.ID,
salt []byte, rsaPrivkey *rsa.PrivateKey, dhPrivKey *cyclic.Int, salt []byte, myRsaPrivKey *rsa.PrivateKey, myDhPrivKey *cyclic.Int,
rng *fastRNG.StreamGenerator, grp *cyclic.Group, net cmix.Client, rng *fastRNG.StreamGenerator, grp *cyclic.Group, net cmix.Client,
p connect.Params) (Connection, error) { p connect.Params) (Connection, error) {
conn, err := connect.Connect(recipient, myId, dhPrivKey, rng, grp, net, p) conn, err := connect.Connect(recipient, myId, myDhPrivKey, rng, grp, net, p)
if err != nil { if err != nil {
return nil, errors.WithMessagef(err, "Failed to establish connection "+ return nil, errors.WithMessagef(err, "Failed to establish connection "+
"with recipient %s", recipient.ID) "with recipient %s", recipient.ID)
} }
// Build the callback for the connection being established
// Construct message
payload, err := makeClientAuthRequest(conn.GetPartner(), rng, myRsaPrivKey, salt)
if err != nil {
errClose := conn.Close()
if errClose != nil {
return nil, errors.Errorf(
"failed to close connection with %s after error %v: %+v",
recipient.ID, err, errClose)
}
return nil, errors.WithMessagef(err, "failed to construct client "+
"authentication message")
}
// Send message to user
e2eParams := clientE2e.GetDefaultParams()
rids, _, _, err := conn.SendE2E(catalog.ConnectionAuthenticationRequest,
payload, e2eParams)
if err != nil {
errClose := conn.Close()
if errClose != nil {
return nil, errors.Errorf(
"failed to close connection with %s after error %v: %+v",
recipient.ID, err, errClose)
}
return nil, errors.WithMessagef(err, "failed to construct client "+
"authentication message")
}
// Record since we first successfully sen the message
timeStart := netTime.Now()
authConnChan := make(chan Connection, 1) authConnChan := make(chan Connection, 1)
cb := ConnectionCallback(func(authConn Connection) {
authConnChan <- authConn
})
go initiateClientAuthentication(cb, conn, net, rng, rsaPrivkey, salt, p) // 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. Pass the connection
// along via the callback
authConn := buildAuthenticatedConnection(conn)
authConn.setAuthenticated()
authConnChan <- authConn
}
})
remainingTime := e2eParams.Timeout - netTime.Since(timeStart)
err = net.GetRoundResults(remainingTime,
roundCb, rids...)
if err != nil {
return nil, errors.Errorf("could not track rounds for successful " +
"identity confirmation message delivery")
}
// Block waiting for auth to confirm it timeouts // Block waiting for auth to confirm it timeouts
jww.DEBUG.Printf("Connection waiting for authenticated "+ jww.DEBUG.Printf("Connection waiting for authenticated "+
"connection with %s to be established...", recipient.ID.String()) "connection with %s to be established...", recipient.ID.String())
...@@ -80,41 +129,27 @@ func ConnectWithAuthentication(recipient contact.Contact, myId *id.ID, ...@@ -80,41 +129,27 @@ func ConnectWithAuthentication(recipient contact.Contact, myId *id.ID,
} }
// StartAuthenticatedConnectionServer is called by the receiver of an // StartServer is called by the receiver of an
// authenticated connection request. Calling this will indicate that they // authenticated connection request. Calling this will indicate that they
// will handle authenticated requests and verify the client's attempt to // will handle authenticated requests and verify the client's attempt to
// authenticate themselves. An established authenticated.Connection will // authenticate themselves. An established authenticated.Connection will
// be passed via the callback. // be passed via the callback.
func StartAuthenticatedConnectionServer(cb ConnectionCallback, func StartServer(cb ConnectionCallback,
myId *id.ID, privKey *cyclic.Int, myId *id.ID, privKey *cyclic.Int,
rng *fastRNG.StreamGenerator, grp *cyclic.Group, net cmix.Client, rng *fastRNG.StreamGenerator, grp *cyclic.Group, net cmix.Client,
p connect.Params) error { p connect.Params) error {
// Register the waiter for a connection establishment // Register the waiter for a connection establishment
connChan := make(chan connect.Connection, 1)
connCb := connect.Callback(func(connection connect.Connection) { connCb := connect.Callback(func(connection connect.Connection) {
connChan <- connection
})
err := connect.RegisterConnectionCallback(connCb, myId, privKey, rng, grp, net, p)
if err != nil {
return err
}
// Wait for a connection to be established
timer := time.NewTimer(p.Timeout)
defer timer.Stop()
select {
case conn := <-connChan:
// Upon establishing a connection, register a listener for the // Upon establishing a connection, register a listener for the
// client's identity proof. If a identity authentication // client's identity proof. If a identity authentication
// message is received and validated, an authenticated connection will be // message is received and validated, an authenticated connection will
// passed along via the callback // be passed along via the callback
conn.RegisterListener(catalog.ConnectionAuthenticationRequest, connection.RegisterListener(catalog.ConnectionAuthenticationRequest,
getServer(cb, conn)) handleAuthConfirmation(cb, connection))
return nil })
case <-timer.C: return connect.StartServer(connCb, myId, privKey, rng, grp,
return errors.New("Timed out trying to establish a connection") net, p)
}
} }
// handler provides an implementation for the authenticated.Connection // handler provides an implementation for the authenticated.Connection
......
...@@ -2,34 +2,21 @@ package authenticated ...@@ -2,34 +2,21 @@ package authenticated
import ( import (
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
jww "github.com/spf13/jwalterweatherman" "github.com/pkg/errors"
"gitlab.com/elixxir/client/catalog" "gitlab.com/elixxir/client/e2e/ratchet/partner"
"gitlab.com/elixxir/client/cmix"
"gitlab.com/elixxir/client/connections/connect"
clientE2e "gitlab.com/elixxir/client/e2e"
"gitlab.com/elixxir/crypto/fastRNG" "gitlab.com/elixxir/crypto/fastRNG"
"gitlab.com/xx_network/crypto/signature/rsa" "gitlab.com/xx_network/crypto/signature/rsa"
"gitlab.com/xx_network/primitives/id"
) )
// initiateClientAuthentication is a helper function which will handle the // makeClientAuthRequest is a helper function which constructs a marshalled
// establishment of an authenticated.Connection. This will have the // IdentityAuthentication message.
// client send an identity authentication message to the server. Upon func makeClientAuthRequest(newPartner partner.Manager,
// successful sending of the message (determined by the result of the round(s)) rng *fastRNG.StreamGenerator, rsaPrivKey *rsa.PrivateKey,
// an authenticated.Connection is assumed to be established and passed along salt []byte) ([]byte, error) {
// via the callback.
func initiateClientAuthentication(authCb ConnectionCallback,
conn connect.Connection, net cmix.Client,
rng *fastRNG.StreamGenerator, rsaPrivKey *rsa.PrivateKey, salt []byte,
connParams connect.Params) {
// After confirmation, get the new partner
newPartner := conn.GetPartner()
// The connection fingerprint (hashed) represents a shared nonce // The connection fingerprint (hashed) represents a shared nonce
// between these two partners // between these two partners
connectionFp := newPartner.ConnectionFingerprint().Bytes() connectionFp := newPartner.ConnectionFingerprint().Bytes()
opts := rsa.NewDefaultOptions() opts := rsa.NewDefaultOptions()
h := opts.Hash.New() h := opts.Hash.New()
h.Write(connectionFp) h.Write(connectionFp)
...@@ -41,10 +28,7 @@ func initiateClientAuthentication(authCb ConnectionCallback, ...@@ -41,10 +28,7 @@ func initiateClientAuthentication(authCb ConnectionCallback,
signature, err := rsa.Sign(stream, rsaPrivKey, signature, err := rsa.Sign(stream, rsaPrivKey,
opts.Hash, nonce, opts) opts.Hash, nonce, opts)
if err != nil { if err != nil {
jww.ERROR.Printf("Unable to build connection with "+ return nil, errors.Errorf("failed to sign nonce: %+v", err)
"partner %s: %+v", newPartner.PartnerId(), err)
// Send a nil connection to avoid hold-ups down the line
authCb(nil)
} }
// Construct message // Construct message
...@@ -56,48 +40,10 @@ func initiateClientAuthentication(authCb ConnectionCallback, ...@@ -56,48 +40,10 @@ func initiateClientAuthentication(authCb ConnectionCallback,
} }
payload, err := proto.Marshal(iar) payload, err := proto.Marshal(iar)
if err != nil { if err != nil {
jww.ERROR.Printf("Unable to build connection with "+
"partner %s: %+v", newPartner.PartnerId(), err)
// Send a nil connection to avoid hold-ups down the line
authCb(nil)
}
// Send message to user
rids, _, _, err := conn.SendE2E(catalog.ConnectionAuthenticationRequest,
payload, clientE2e.GetDefaultParams())
if err != nil {
jww.ERROR.Printf("Unable to build connection with "+
"partner %s: %+v", newPartner.PartnerId(), err)
// Send a nil connection to avoid hold-ups down the line
authCb(nil)
}
// Determine that the message is properly sent by tracking the success return nil, errors.Errorf("failed to marshal identity request "+
// of the round(s) "message: %+v", err)
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. Pass the connection
// along via the callback
authConn := buildAuthenticatedConnection(conn)
authConn.setAuthenticated()
authCb(authConn)
} else {
jww.ERROR.Printf("Unable to build connection with "+
"partner %s: %+v", newPartner.PartnerId(), err)
// Send a nil connection to avoid hold-ups down the line
authCb(nil)
}
})
err = net.GetRoundResults(connParams.Timeout,
roundCb, rids...)
if err != nil {
jww.ERROR.Printf("Unable to build connection with "+
"partner %s: %+v", newPartner.PartnerId(), err)
// Send a nil connection to avoid hold-ups down the line
authCb(nil)
} }
return payload, nil
} }
///////////////////////////////////////////////////////////////////////////////
// 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 package authenticated
import ( import (
...@@ -30,8 +37,9 @@ type serverListener struct { ...@@ -30,8 +37,9 @@ type serverListener struct {
conn connect.Connection conn connect.Connection
} }
// getServer returns a serverListener object. // handleAuthConfirmation returns a serverListener object.
func getServer(cb ConnectionCallback, connection connect.Connection) server { func handleAuthConfirmation(cb ConnectionCallback,
connection connect.Connection) server {
return serverListener{ return serverListener{
connectionCallback: cb, connectionCallback: cb,
conn: connection, conn: connection,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment