diff --git a/auth/callbacks.go b/auth/callbacks.go index 781164bb89e374f166b0ace4d4fb639e7c250d10..273ff6b0b7af5b36ff53d44e265ebaeaab0183cd 100644 --- a/auth/callbacks.go +++ b/auth/callbacks.go @@ -7,10 +7,6 @@ package auth import ( - jww "github.com/spf13/jwalterweatherman" - "gitlab.com/elixxir/client/cmix/identity/receptionID" - "gitlab.com/elixxir/client/cmix/rounds" - "gitlab.com/elixxir/crypto/contact" "gitlab.com/xx_network/primitives/id" "sync" ) @@ -42,27 +38,8 @@ func (p *partnerCallbacks) DeletePartnerCallback(partnerId *id.ID) { // getPartnerCallback returns the Callbacks for the given partnerId func (p *partnerCallbacks) getPartnerCallback(partnerId *id.ID) Callbacks { - return p.callbacks[*partnerId] -} - -// DefaultAuthCallbacks is a simple structure for providing a default Callbacks implementation -// It should generally not be used. -type DefaultAuthCallbacks struct{} - -// Confirm will be called when an auth Confirm message is processed. -func (a DefaultAuthCallbacks) Confirm(requestor contact.Contact, - receptionID receptionID.EphemeralIdentity, round rounds.Round) { - jww.ERROR.Printf("No valid auth callback assigned!") -} - -// Request will be called when an auth Request message is processed. -func (a DefaultAuthCallbacks) Request(requestor contact.Contact, - receptionID receptionID.EphemeralIdentity, round rounds.Round) { - jww.ERROR.Printf("No valid auth callback assigned!") -} + p.RLock() + defer p.RUnlock() -// Reset will be called when an auth Reset operation occurs. -func (a DefaultAuthCallbacks) Reset(requestor contact.Contact, - receptionID receptionID.EphemeralIdentity, round rounds.Round) { - jww.ERROR.Printf("No valid auth callback assigned!") + return p.callbacks[*partnerId] } diff --git a/auth/interface.go b/auth/interface.go index c6f5a5193a13b6cebb42a4e930a0f58d6c52e23d..ea1b108744ed2fceb201eb6268c2d54d684f8002 100644 --- a/auth/interface.go +++ b/auth/interface.go @@ -100,6 +100,10 @@ type State interface { // auth callback for the given partner ID. DeletePartnerCallback(partnerId *id.ID) + // DeletePartner deletes the request and/or confirmation for the given + // partner. + DeletePartner(partner *id.ID) error + // Closer stops listening to auth. io.Closer } diff --git a/auth/receivedConfirm.go b/auth/receivedConfirm.go index 538329e1b532a1a34d813f866dfb739319c09d3b..bf25d5661743e226f2d7cb2024cff457365ab20c 100644 --- a/auth/receivedConfirm.go +++ b/auth/receivedConfirm.go @@ -113,13 +113,11 @@ func (rcs *receivedConfirmService) Process(msg format.Message, Facts: make([]fact.Fact, 0), } - authState.partnerCallbacks.RLock() if cb := authState.partnerCallbacks.getPartnerCallback(c.ID); cb != nil { cb.Confirm(c, receptionID, round) } else { authState.callbacks.Confirm(c, receptionID, round) } - authState.partnerCallbacks.RUnlock() } func (rcs *receivedConfirmService) String() string { diff --git a/auth/receivedRequest.go b/auth/receivedRequest.go index b410b04a3e29cca95dedd8bc14d310507e8bce57..4e3325996a6ca5c558d3531a019c68c92f026bf3 100644 --- a/auth/receivedRequest.go +++ b/auth/receivedRequest.go @@ -130,21 +130,17 @@ func (rrs *receivedRequestService) Process(message format.Message, } else if authState.params.ReplayRequests { //if we did not already accept, auto replay the request if rrs.reset { - authState.partnerCallbacks.RLock() if cb := authState.partnerCallbacks.getPartnerCallback(c.ID); cb != nil { cb.Reset(c, receptionID, round) } else { authState.callbacks.Reset(c, receptionID, round) } - authState.partnerCallbacks.RUnlock() } else { - authState.partnerCallbacks.RLock() if cb := authState.partnerCallbacks.getPartnerCallback(c.ID); cb != nil { cb.Request(c, receptionID, round) } else { authState.callbacks.Request(c, receptionID, round) } - authState.partnerCallbacks.RUnlock() } } //if not confirm, and params.replay requests is true, we need to replay @@ -254,8 +250,6 @@ func (rrs *receivedRequestService) Process(message format.Message, } // auto-confirm if we should - authState.partnerCallbacks.RLock() - defer authState.partnerCallbacks.RUnlock() if autoConfirm || reset { _, _ = authState.confirm(c, authState.params.getConfirmTag(reset)) //handle callbacks diff --git a/auth/state.go b/auth/state.go index 43e1d9735fde5bd0e5c4bbc396ec3ff74727c4b5..3b82f15e46049abd5cc8be2367fb4abb45b68e71 100644 --- a/auth/state.go +++ b/auth/state.go @@ -115,13 +115,11 @@ func (s *state) CallAllReceivedRequests() { rr := rrList[i] eph := receptionID.BuildIdentityFromRound(rr.GetContact().ID, rr.GetRound()) - s.partnerCallbacks.RLock() if cb := s.partnerCallbacks.getPartnerCallback(rr.GetContact().ID); cb != nil { cb.Request(rr.GetContact(), eph, rr.GetRound()) } else { s.callbacks.Request(rr.GetContact(), eph, rr.GetRound()) } - s.partnerCallbacks.RUnlock() } } @@ -144,6 +142,22 @@ func (s *state) Close() error { return nil } +// DeletePartner deletes the request and/or confirmation for the given partner. +func (s *state) DeletePartner(partner *id.ID) error { + err := s.store.DeleteRequest(partner) + err2 := s.store.DeleteConfirmation(partner) + + // Only return an error if both failed to delete + if err != nil && err2 != nil { + return errors.Errorf("Failed to delete partner: no requests or "+ + "confirmations found: %s, %s", err, err2) + } + + s.DeletePartnerCallback(partner) + + return nil +} + // AddPartnerCallback that overrides the generic auth callback for the given partnerId func (s *state) AddPartnerCallback(partnerId *id.ID, cb Callbacks) { s.partnerCallbacks.AddPartnerCallback(partnerId, cb) diff --git a/auth/utils_test.go b/auth/utils_test.go index 4d6b50ade9db9ff930e3824af62515a098578e77..4c53ff91a95c40e4b138258592cb292b12d8e749 100644 --- a/auth/utils_test.go +++ b/auth/utils_test.go @@ -88,6 +88,8 @@ func (m mockE2eHandler) Unregister(listenerID receive.ListenerID) { return } +func (m mockE2eHandler) UnregisterUserListeners(*id.ID) {} + func (m mockE2eHandler) AddPartner(partnerID *id.ID, partnerPubKey, myPrivKey *cyclic.Int, partnerSIDHPubKey *sidh.PublicKey, mySIDHPrivKey *sidh.PrivateKey, diff --git a/bindings/connect.go b/bindings/connect.go index 81a482a327304a640417717dfe55bc409b17b05d..98f9795398040d1cb372e8c54fb4d273efd84dbc 100644 --- a/bindings/connect.go +++ b/bindings/connect.go @@ -6,6 +6,7 @@ import ( "gitlab.com/elixxir/client/connect" e2e2 "gitlab.com/elixxir/client/e2e" "gitlab.com/elixxir/crypto/contact" + "time" ) // connectionTrackerSingleton is used to track connections so they can be @@ -44,7 +45,9 @@ func (c *Cmix) Connect(e2eId int, recipientContact []byte) ( return nil, err } - connection, err := connect.Connect(cont, e2eClient.api, connect.GetDefaultParams()) + p := connect.GetDefaultParams() + p.Timeout = 45 * time.Second + connection, err := connect.Connect(cont, e2eClient.api, p) if err != nil { return nil, err } @@ -85,6 +88,7 @@ func (c *Connection) GetPartner() []byte { // RegisterListener is used for E2E reception // and allows for reading data sent from the partner.Manager // Returns marshalled ListenerID -func (c *Connection) RegisterListener(messageType int, newListener Listener) { - _ = c.connection.RegisterListener(catalog.MessageType(messageType), listener{l: newListener}) +func (c *Connection) RegisterListener(messageType int, newListener Listener) error { + _, err := c.connection.RegisterListener(catalog.MessageType(messageType), listener{l: newListener}) + return err } diff --git a/bindings/e2e.go b/bindings/e2e.go index 289de4218cad4a4fd485b3524826e0f702e05f3c..bd849f9ad1304b14b6eaa3a20e8177059c82c7d6 100644 --- a/bindings/e2e.go +++ b/bindings/e2e.go @@ -7,15 +7,10 @@ package bindings import ( - "encoding/json" - "gitlab.com/elixxir/client/auth" "gitlab.com/elixxir/client/cmix/identity/receptionID" "gitlab.com/elixxir/client/cmix/rounds" "gitlab.com/elixxir/client/xxdk" "gitlab.com/elixxir/crypto/contact" - "gitlab.com/elixxir/crypto/cyclic" - "gitlab.com/xx_network/crypto/signature/rsa" - "gitlab.com/xx_network/primitives/id" ) // e2eTrackerSingleton is used to track E2e objects so that @@ -46,14 +41,14 @@ func LoginE2e(cmixId int, callbacks AuthCallbacks, identity []byte) (*E2e, error return nil, err } - newIdentity, err := unmarshalIdentity(identity, cmix.api.GetStorage().GetE2EGroup()) + newIdentity, err := xxdk.UnmarshalReceptionIdentity(identity) if err != nil { return nil, err } - var authCallbacks auth.Callbacks + var authCallbacks xxdk.AuthCallbacks if callbacks == nil { - authCallbacks = auth.DefaultAuthCallbacks{} + authCallbacks = xxdk.DefaultAuthCallbacks{} } else { authCallbacks = &authCallback{bindingsCbs: callbacks} } @@ -62,6 +57,7 @@ func LoginE2e(cmixId int, callbacks AuthCallbacks, identity []byte) (*E2e, error if err != nil { return nil, err } + return e2eTrackerSingleton.make(newE2e), nil } @@ -74,14 +70,14 @@ func LoginE2eEphemeral(cmixId int, callbacks AuthCallbacks, identity []byte) (*E return nil, err } - newIdentity, err := unmarshalIdentity(identity, cmix.api.GetStorage().GetE2EGroup()) + newIdentity, err := xxdk.UnmarshalReceptionIdentity(identity) if err != nil { return nil, err } - var authCallbacks auth.Callbacks + var authCallbacks xxdk.AuthCallbacks if callbacks == nil { - authCallbacks = auth.DefaultAuthCallbacks{} + authCallbacks = xxdk.DefaultAuthCallbacks{} } else { authCallbacks = &authCallback{bindingsCbs: callbacks} } @@ -104,9 +100,9 @@ func LoginE2eLegacy(cmixId int, callbacks AuthCallbacks) (*E2e, error) { return nil, err } - var authCallbacks auth.Callbacks + var authCallbacks xxdk.AuthCallbacks if callbacks == nil { - authCallbacks = auth.DefaultAuthCallbacks{} + authCallbacks = xxdk.DefaultAuthCallbacks{} } else { authCallbacks = &authCallback{bindingsCbs: callbacks} } @@ -120,38 +116,7 @@ func LoginE2eLegacy(cmixId int, callbacks AuthCallbacks) (*E2e, error) { // GetContact returns a marshalled contact.Contact object for the E2e ReceptionIdentity func (e *E2e) GetContact() []byte { - return e.api.GetReceptionIdentity().GetContact(e.api.GetStorage().GetE2EGroup()).Marshal() -} - -// unmarshalIdentity is a helper function for taking in a marshalled xxdk.ReceptionIdentity and making it an object -func unmarshalIdentity(marshaled []byte, e2eGrp *cyclic.Group) (xxdk.ReceptionIdentity, error) { - newIdentity := xxdk.ReceptionIdentity{} - - // Unmarshal given identity into ReceptionIdentity object - givenIdentity := ReceptionIdentity{} - err := json.Unmarshal(marshaled, &givenIdentity) - if err != nil { - return xxdk.ReceptionIdentity{}, err - } - - newIdentity.ID, err = id.Unmarshal(givenIdentity.ID) - if err != nil { - return xxdk.ReceptionIdentity{}, err - } - - newIdentity.DHKeyPrivate = e2eGrp.NewInt(1) - err = newIdentity.DHKeyPrivate.UnmarshalJSON(givenIdentity.DHKeyPrivate) - if err != nil { - return xxdk.ReceptionIdentity{}, err - } - - newIdentity.RSAPrivatePem, err = rsa.LoadPrivateKeyFromPem(givenIdentity.RSAPrivatePem) - if err != nil { - return xxdk.ReceptionIdentity{}, err - } - - newIdentity.Salt = givenIdentity.Salt - return newIdentity, nil + return e.api.GetReceptionIdentity().GetContact().Marshal() } // AuthCallbacks is the bindings-specific interface for auth.Callbacks methods. @@ -180,19 +145,19 @@ func convertAuthCallbacks(requestor contact.Contact, } // Confirm will be called when an auth Confirm message is processed. -func (a *authCallback) Confirm(requestor contact.Contact, - receptionID receptionID.EphemeralIdentity, round rounds.Round) { - a.bindingsCbs.Confirm(convertAuthCallbacks(requestor, receptionID, round)) +func (a *authCallback) Confirm(partner contact.Contact, + receptionID receptionID.EphemeralIdentity, round rounds.Round, _ *xxdk.E2e) { + a.bindingsCbs.Confirm(convertAuthCallbacks(partner, receptionID, round)) } // Request will be called when an auth Request message is processed. -func (a *authCallback) Request(requestor contact.Contact, - receptionID receptionID.EphemeralIdentity, round rounds.Round) { - a.bindingsCbs.Request(convertAuthCallbacks(requestor, receptionID, round)) +func (a *authCallback) Request(partner contact.Contact, + receptionID receptionID.EphemeralIdentity, round rounds.Round, _ *xxdk.E2e) { + a.bindingsCbs.Request(convertAuthCallbacks(partner, receptionID, round)) } // Reset will be called when an auth Reset operation occurs. -func (a *authCallback) Reset(requestor contact.Contact, - receptionID receptionID.EphemeralIdentity, round rounds.Round) { - a.bindingsCbs.Reset(convertAuthCallbacks(requestor, receptionID, round)) +func (a *authCallback) Reset(partner contact.Contact, + receptionID receptionID.EphemeralIdentity, round rounds.Round, _ *xxdk.E2e) { + a.bindingsCbs.Reset(convertAuthCallbacks(partner, receptionID, round)) } diff --git a/bindings/e2eAuth.go b/bindings/e2eAuth.go index b483671d90d3deb616bc0bd2e13018882e859ec1..77083daa290bfcd1dbd27ffe714e2d456624475d 100644 --- a/bindings/e2eAuth.go +++ b/bindings/e2eAuth.go @@ -8,6 +8,7 @@ package bindings import ( + "gitlab.com/elixxir/client/xxdk" "gitlab.com/elixxir/crypto/contact" "gitlab.com/elixxir/primitives/fact" "gitlab.com/xx_network/primitives/id" @@ -215,7 +216,8 @@ func (e *E2e) AddPartnerCallback(partnerID []byte, cb AuthCallbacks) error { return err } - e.api.GetAuth().AddPartnerCallback(partnerId, &authCallback{bindingsCbs: cb}) + e.api.GetAuth().AddPartnerCallback(partnerId, + xxdk.MakeAuthCallbacksAdapter(&authCallback{bindingsCbs: cb}, e.api)) return nil } diff --git a/bindings/contact.go b/bindings/identity.go similarity index 83% rename from bindings/contact.go rename to bindings/identity.go index 251c810f40c6e31a6212b8e2eaee770c86724d00..2718672f50b22b8017d84e9c1799eac4667905d6 100644 --- a/bindings/contact.go +++ b/bindings/identity.go @@ -5,7 +5,6 @@ import ( "gitlab.com/elixxir/client/xxdk" "gitlab.com/elixxir/crypto/contact" "gitlab.com/elixxir/primitives/fact" - "gitlab.com/xx_network/crypto/signature/rsa" ) // ReceptionIdentity struct @@ -27,23 +26,12 @@ type ReceptionIdentity struct { // MakeIdentity generates a new cryptographic identity for receiving messages func (c *Cmix) MakeIdentity() ([]byte, error) { - s := c.api.GetRng().GetStream() - defer s.Close() - ident, err := xxdk.MakeReceptionIdentity(s, c.api.GetStorage().GetE2EGroup()) - - dhPrivJson, err := ident.DHKeyPrivate.MarshalJSON() + ident, err := xxdk.MakeReceptionIdentity(c.api) if err != nil { return nil, err } - //create the identity object - I := ReceptionIdentity{ - ID: ident.ID.Marshal(), - RSAPrivatePem: rsa.CreatePrivateKeyPem(ident.RSAPrivatePem), - Salt: ident.Salt, - DHKeyPrivate: dhPrivJson, - } - return json.Marshal(&I) + return ident.Marshal() } // GetIDFromContact accepts a marshalled contact.Contact object & returns a marshalled id.ID object @@ -122,3 +110,32 @@ func GetFactsFromContact(marshaled []byte) ([]byte, error) { } return factsListMarshaled, nil } + +// StoreReceptionIdentity stores the given identity in Cmix storage with the given key +// This is the ideal way to securely store identities, as the caller of this function +// is only required to store the given key separately rather than the keying material +func StoreReceptionIdentity(key string, identity []byte, cmixId int) error { + cmix, err := cmixTrackerSingleton.get(cmixId) + if err != nil { + return err + } + receptionIdentity, err := xxdk.UnmarshalReceptionIdentity(identity) + if err != nil { + return err + } + return xxdk.StoreReceptionIdentity(key, receptionIdentity, cmix.api) +} + +// LoadReceptionIdentity loads the given identity in Cmix storage with the given key +func LoadReceptionIdentity(key string, cmixId int) ([]byte, error) { + cmix, err := cmixTrackerSingleton.get(cmixId) + if err != nil { + return nil, err + } + storageObj, err := cmix.api.GetStorage().Get(key) + if err != nil { + return nil, err + } + + return storageObj.Data, nil +} diff --git a/bindings/contact_test.go b/bindings/identity_test.go similarity index 100% rename from bindings/contact_test.go rename to bindings/identity_test.go diff --git a/bindings/utilities.go b/bindings/utilities.go index 96c7b346e6a920109d5678fa9802fe5fdfa37435..7eb109730a21b1c4cc519ee7b1e8d154362f62a0 100644 --- a/bindings/utilities.go +++ b/bindings/utilities.go @@ -5,6 +5,7 @@ import ( "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" "google.golang.org/grpc/grpclog" + "log" ) // sets level of logging. All logs the set level and above will be displayed @@ -25,6 +26,7 @@ func LogLevel(level int) error { threshold := jww.Threshold(level) jww.SetLogThreshold(threshold) jww.SetStdoutThreshold(threshold) + jww.SetFlags(log.LstdFlags | log.Lmicroseconds) switch threshold { case jww.LevelTrace: diff --git a/cmd/broadcast.go b/cmd/broadcast.go index af7ea93d85cf1160fee8eaaecbbe75acfadb91b1..c3221e938a4ad6814f27a8d032aa5561da971356 100644 --- a/cmd/broadcast.go +++ b/cmd/broadcast.go @@ -24,12 +24,11 @@ var broadcastCmd = &cobra.Command{ Short: "Send broadcast messages", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - client := initClient() + client := initE2e() // Write user contact to file - user := client.GetUser() - jww.INFO.Printf("User: %s", user.ReceptionID) - jww.INFO.Printf("User Transmission: %s", user.TransmissionID) + user := client.GetReceptionIdentity() + jww.INFO.Printf("User: %s", user.ID) writeContact(user.GetContact()) err := client.StartNetworkFollower(5 * time.Second) @@ -40,8 +39,8 @@ var broadcastCmd = &cobra.Command{ // Wait until connected or crash on timeout connected := make(chan bool, 10) client.GetCmix().AddHealthCallback( - func(isconnected bool) { - connected <- isconnected + func(isConnected bool) { + connected <- isConnected }) waitUntilConnected(connected) /* Set up underlying crypto broadcast.Channel */ @@ -69,9 +68,10 @@ var broadcastCmd = &cobra.Command{ jww.FATAL.Panicf("description cannot be empty") } + var cryptChannel *crypto.Channel if viper.GetBool("new") { // Create a new broadcast channel - channel, pk, err = crypto.NewChannel(name, desc, client.GetRng().GetStream()) + cryptChannel, pk, err = crypto.NewChannel(name, desc, client.GetRng().GetStream()) if err != nil { jww.FATAL.Panicf("Failed to create new channel: %+v", err) } @@ -99,7 +99,7 @@ var broadcastCmd = &cobra.Command{ jww.FATAL.Panicf("Failed to generate channel ID: %+v", err) } - channel = &crypto.Channel{ + cryptChannel = &crypto.Channel{ ReceptionID: rid, Name: name, Description: desc, @@ -109,7 +109,7 @@ var broadcastCmd = &cobra.Command{ } // Save channel to disk - cBytes, err := channel.Marshal() + cBytes, err := cryptChannel.Marshal() if err != nil { jww.ERROR.Printf("Failed to marshal channel to bytes: %+v", err) } diff --git a/cmd/callbacks.go b/cmd/callbacks.go index 13900622395ed2e6b787df1b2d7d22c426cb8c61..d5d75a321f7e215062738184bbaab954812c29b6 100644 --- a/cmd/callbacks.go +++ b/cmd/callbacks.go @@ -26,20 +26,18 @@ import ( type authCallbacks struct { autoConfirm bool confCh chan *id.ID - client *xxdk.E2e } -func makeAuthCallbacks(client *xxdk.E2e, autoConfirm bool) *authCallbacks { +func makeAuthCallbacks(autoConfirm bool) *authCallbacks { return &authCallbacks{ autoConfirm: autoConfirm, confCh: make(chan *id.ID, 10), - client: client, } } func (a *authCallbacks) Request(requestor contact.Contact, receptionID receptionID.EphemeralIdentity, - round rounds.Round) { + round rounds.Round, client *xxdk.E2e) { msg := fmt.Sprintf("Authentication channel request from: %s\n", requestor.ID) jww.INFO.Printf(msg) @@ -48,9 +46,9 @@ func (a *authCallbacks) Request(requestor contact.Contact, jww.INFO.Printf("Channel Request: %s", requestor.ID) if viper.GetBool("verify-sends") { // Verify message sends were successful - acceptChannelVerified(a.client, requestor.ID) + acceptChannelVerified(client, requestor.ID) } else { - acceptChannel(a.client, requestor.ID) + acceptChannel(client, requestor.ID) } a.confCh <- requestor.ID @@ -60,14 +58,14 @@ func (a *authCallbacks) Request(requestor contact.Contact, func (a *authCallbacks) Confirm(requestor contact.Contact, receptionID receptionID.EphemeralIdentity, - round rounds.Round) { + round rounds.Round, client *xxdk.E2e) { jww.INFO.Printf("Channel Confirmed: %s", requestor.ID) a.confCh <- requestor.ID } func (a *authCallbacks) Reset(requestor contact.Contact, receptionID receptionID.EphemeralIdentity, - round rounds.Round) { + round rounds.Round, client *xxdk.E2e) { msg := fmt.Sprintf("Authentication channel reset from: %s\n", requestor.ID) jww.INFO.Printf(msg) diff --git a/cmd/fileTransfer.go b/cmd/fileTransfer.go index bdafb1edf63440c3f1109516f687a66ba2778b37..468bc50fe270ecc3a9114cc8aedf8627ddcd0f90 100644 --- a/cmd/fileTransfer.go +++ b/cmd/fileTransfer.go @@ -36,11 +36,11 @@ var ftCmd = &cobra.Command{ Run: func(cmd *cobra.Command, args []string) { // Initialise a new client - client := initClient() + client := initE2e() // Print user's reception ID and save contact file - user := client.GetUser() - jww.INFO.Printf("User: %s", user.ReceptionID) + user := client.GetReceptionIdentity() + jww.INFO.Printf("User: %s", user.ID) writeContact(user.GetContact()) // Start the network follower @@ -152,7 +152,7 @@ func initFileTransferManager(client *xxdk.E2e, maxThroughput int) ( // Create new manager manager, err := ft.NewManager(p, - client.GetUser().ReceptionID, + client.GetReceptionIdentity().ID, client.GetCmix(), client.GetStorage(), client.GetRng()) @@ -169,7 +169,7 @@ func initFileTransferManager(client *xxdk.E2e, maxThroughput int) ( e2eParams := ftE2e.DefaultParams() e2eFt, err := ftE2e.NewWrapper(receiveCB, e2eParams, manager, - client.GetUser().ReceptionID, client.GetE2E(), client.GetCmix()) + client.GetReceptionIdentity().ID, client.GetE2E(), client.GetCmix()) if err != nil { jww.FATAL.Panicf( "[FT] Failed to create new e2e file transfer wrapper: %+v", err) diff --git a/cmd/group.go b/cmd/group.go index 8ea7fd73870ec504605674be1f3c2817412abe75..1520d97c648ed47d952636c9f8aba703c04295cd 100644 --- a/cmd/group.go +++ b/cmd/group.go @@ -34,11 +34,11 @@ var groupCmd = &cobra.Command{ Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - client := initClient() + client := initE2e() // Print user's reception ID - user := client.GetUser() - jww.INFO.Printf("User: %s", user.ReceptionID) + user := client.GetReceptionIdentity() + jww.INFO.Printf("User: %s", user.ID) err := client.StartNetworkFollower(5 * time.Second) if err != nil { @@ -155,7 +155,7 @@ func createGroup(name, msg []byte, filePath string, gm groupChat.GroupChat) { userIdStrings := ReadLines(filePath) userIDs := make([]*id.ID, 0, len(userIdStrings)) for _, userIdStr := range userIdStrings { - userID, _ := parseRecipient(userIdStr) + userID := parseRecipient(userIdStr) userIDs = append(userIDs, userID) } @@ -174,7 +174,7 @@ func createGroup(name, msg []byte, filePath string, gm groupChat.GroupChat) { // resendRequests resends group requests for the group ID. func resendRequests(groupIdString string, gm groupChat.GroupChat) { - groupID, _ := parseRecipient(groupIdString) + groupID := parseRecipient(groupIdString) rids, status, err := gm.ResendRequest(groupID) if err != nil { jww.FATAL.Panicf("[GC] Failed to resend requests to group %s: %+v", @@ -212,7 +212,7 @@ func joinGroup(reqChan chan groupStore.Group, timeout time.Duration, // leaveGroup leaves the group. func leaveGroup(groupIdString string, gm groupChat.GroupChat) { - groupID, _ := parseRecipient(groupIdString) + groupID := parseRecipient(groupIdString) jww.INFO.Printf("[GC] Leaving group %s.", groupID) err := gm.LeaveGroup(groupID) @@ -226,7 +226,7 @@ func leaveGroup(groupIdString string, gm groupChat.GroupChat) { // sendGroup send the message to the group. func sendGroup(groupIdString string, msg []byte, gm groupChat.GroupChat) { - groupID, _ := parseRecipient(groupIdString) + groupID := parseRecipient(groupIdString) jww.INFO.Printf("[GC] Sending to group %s message %q", groupID, msg) @@ -272,7 +272,7 @@ func listGroups(gm groupChat.GroupChat) { // showGroup prints all the information of the group. func showGroup(groupIdString string, gm groupChat.GroupChat) { - groupID, _ := parseRecipient(groupIdString) + groupID := parseRecipient(groupIdString) grp, ok := gm.GetGroup(groupID) if !ok { diff --git a/cmd/init.go b/cmd/init.go index 6c42b9cfc6a887d5f1dd3aacec85eb36ab47fb9e..75ea00e6c75bee432de3164d6f721291028ef97b 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -9,9 +9,6 @@ package cmd import ( - "fmt" - "gitlab.com/elixxir/client/xxdk" - "github.com/spf13/cobra" jww "github.com/spf13/jwalterweatherman" "github.com/spf13/viper" @@ -23,17 +20,10 @@ var initCmd = &cobra.Command{ Short: "Initialize a user ID but do not connect to the network", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - client := createClient() - e2e, err := xxdk.LoadOrInitE2e(client) - if err != nil { - jww.FATAL.Panicf("%+v", err) - } - user := client.GetUser() - user.E2eDhPublicKey = e2e.GetHistoricalDHPubkey() + _, receptionIdentity := initCmix() - jww.INFO.Printf("User: %s", user.ReceptionID) - writeContact(user.GetContact()) - fmt.Printf("%s\n", user.ReceptionID) + jww.INFO.Printf("User: %s", receptionIdentity.ID) + writeContact(receptionIdentity.GetContact()) }, } diff --git a/cmd/root.go b/cmd/root.go index a5e2cf3a978e9989a6acd60bf2fd89b863bda640..ce2b9987acfd1fe75cff381072b4c9a0b459bfbf 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -167,6 +167,9 @@ EnretBzQkeKeBwoB2u6NTiOmUjk= testNetCert = `` ) +// Key used for storing xxdk.ReceptionIdentity objects +const identityStorageKey = "identityStorageKey" + var authCbs *authCallbacks // Execute adds all child commands to the root command and sets flags @@ -194,35 +197,33 @@ var rootCmd = &cobra.Command{ pprof.StartCPUProfile(f) } - client := initClient() + client := initE2e() jww.INFO.Printf("Client Initialized...") - user := client.GetUser() - jww.INFO.Printf("USERPUBKEY: %s", - user.E2eDhPublicKey.TextVerbose(16, 0)) - jww.INFO.Printf("User: %s", user.ReceptionID) - writeContact(user.GetContact()) - - // get Recipient and/or set it to myself - isPrecanPartner := false - recipientContact := readContact() - recipientID := recipientContact.ID - - // Try to get recipientID from destid - if recipientID == nil { - recipientID, isPrecanPartner = parseRecipient( - viper.GetString("destid")) - } - - // Set it to myself - if recipientID == nil { - jww.INFO.Printf("sending message to self") - recipientID = user.ReceptionID - recipientContact = user.GetContact() + receptionIdentity := client.GetReceptionIdentity() + jww.INFO.Printf("User: %s", receptionIdentity.ID) + writeContact(receptionIdentity.GetContact()) + + var recipientContact contact.Contact + var recipientID *id.ID + + destFile := viper.GetString("destfile") + destId := viper.GetString("destid") + sendId := viper.GetString("sendid") + if destFile != "" { + recipientContact = readContact(destFile) + recipientID = recipientContact.ID + } else if destId == "0" || sendId == destId { + jww.INFO.Printf("Sending message to self") + recipientID = receptionIdentity.ID + recipientContact = receptionIdentity.GetContact() + } else { + recipientID = parseRecipient(destId) } + isPrecanPartner := isPrecanID(recipientID) - jww.INFO.Printf("Client: %s, Partner: %s", user.ReceptionID, + jww.INFO.Printf("Client: %s, Partner: %s", receptionIdentity.ID, recipientID) client.GetE2E().EnableUnsafeReception() @@ -240,8 +241,8 @@ var rootCmd = &cobra.Command{ // Wait until connected or crash on timeout connected := make(chan bool, 10) client.GetCmix().AddHealthCallback( - func(isconnected bool) { - connected <- isconnected + func(isConnected bool) { + connected <- isConnected }) waitUntilConnected(connected) @@ -292,6 +293,7 @@ var rootCmd = &cobra.Command{ authConfirmed = true } + jww.INFO.Printf("Preexisting E2e partners: %+v", client.GetE2E().GetAllPartnerIDs()) if client.GetE2E().HasAuthenticatedChannel(recipientID) { jww.INFO.Printf("Authenticated channel already in "+ "place for %s", recipientID) @@ -317,19 +319,22 @@ var rootCmd = &cobra.Command{ authConfirmed = false } - go func() { - for { - authID := <-authCbs.confCh - if authID.Cmp(recipientID) { - authConfirmed = true + if !unsafe && !authConfirmed { + // Signal for authConfirm callback in a separate thread + go func() { + for { + authID := <-authCbs.confCh + if authID.Cmp(recipientID) { + authConfirmed = true + } } - } - }() + }() - if !unsafe && !authConfirmed { jww.INFO.Printf("Waiting for authentication channel"+ " confirmation with partner %s", recipientID) scnt := uint(0) + + // Wait until authConfirmed waitSecs := viper.GetUint("auth-timeout") for !authConfirmed && scnt < waitSecs { time.Sleep(1 * time.Second) @@ -343,6 +348,8 @@ var rootCmd = &cobra.Command{ } jww.INFO.Printf("Authentication channel confirmation"+ " took %d seconds", scnt) + jww.INFO.Printf("Authenticated partners saved: %v\n PartnersList: %+v", + !client.GetStorage().GetKV().IsMemStore(), client.GetE2E().GetAllPartnerIDs()) } // DeleteFingerprint this recipient @@ -533,7 +540,8 @@ var rootCmd = &cobra.Command{ }, } -func createClient() *xxdk.Cmix { +// initCmix returns a newly-initialized xxdk.Cmix object and its stored xxdk.ReceptionIdentity +func initCmix() (*xxdk.Cmix, xxdk.ReceptionIdentity) { logLevel := viper.GetUint("logLevel") initLog(logLevel, viper.GetString("log")) jww.INFO.Printf(Version()) @@ -547,6 +555,9 @@ func createClient() *xxdk.Cmix { backupPath := viper.GetString("backupIn") backupPass := []byte(viper.GetString("backupPass")) + // FIXME: All branches of the upcoming path + var knownReception xxdk.ReceptionIdentity + // create a new client if none exist if _, err := os.Stat(storeDir); errors.Is(err, fs.ErrNotExist) { // Load NDF @@ -556,7 +567,7 @@ func createClient() *xxdk.Cmix { } if precannedID != 0 { - err = xxdk.NewPrecannedClient(precannedID, + knownReception, err = xxdk.NewPrecannedClient(precannedID, string(ndfJSON), storeDir, pass) } else if protoUserPath != "" { protoUserJson, err := utils.ReadFile(protoUserPath) @@ -625,12 +636,33 @@ func createClient() *xxdk.Cmix { } params := initParams() - client, err := xxdk.OpenCmix(storeDir, pass, params) if err != nil { jww.FATAL.Panicf("%+v", err) } - return client + + // If there is a known xxdk.ReceptionIdentity, store it now + if knownReception.ID != nil { + err = xxdk.StoreReceptionIdentity(identityStorageKey, knownReception, client) + if err != nil { + jww.FATAL.Panicf("%+v", err) + } + } + + // Attempt to load extant xxdk.ReceptionIdentity + identity, err := xxdk.LoadReceptionIdentity(identityStorageKey, client) + if err != nil { + // If no extant xxdk.ReceptionIdentity, generate and store a new one + identity, err = xxdk.MakeReceptionIdentity(client) + if err != nil { + jww.FATAL.Panicf("%+v", err) + } + err = xxdk.StoreReceptionIdentity(identityStorageKey, identity, client) + if err != nil { + jww.FATAL.Panicf("%+v", err) + } + } + return client, identity } func initParams() xxdk.Params { @@ -654,8 +686,9 @@ func initParams() xxdk.Params { return p } -func initClient() *xxdk.E2e { - createClient() +// initE2e returns a fully-formed xxdk.E2e object +func initE2e() *xxdk.E2e { + _, receptionIdentity := initCmix() pass := parsePassword(viper.GetString("password")) storeDir := viper.GetString("session") @@ -664,22 +697,30 @@ func initClient() *xxdk.E2e { params := initParams() // load the client - baseclient, err := xxdk.LoadCmix(storeDir, pass, params) - + baseClient, err := xxdk.LoadCmix(storeDir, pass, params) if err != nil { jww.FATAL.Panicf("%+v", err) } - authCbs = makeAuthCallbacks(nil, + authCbs = makeAuthCallbacks( viper.GetBool("unsafe-channel-creation")) - client, err := xxdk.LoginLegacy(baseclient, authCbs) - if err != nil { - jww.FATAL.Panicf("%+v", err) + // Force LoginLegacy for precanned senderID + var client *xxdk.E2e + if isPrecanID(receptionIdentity.ID) { + jww.INFO.Printf("Using LoginLegacy for precan sender") + client, err = xxdk.LoginLegacy(baseClient, authCbs) + if err != nil { + jww.FATAL.Panicf("%+v", err) + } + } else { + jww.INFO.Printf("Using Login for non-precan sender") + client, err = xxdk.Login(baseClient, authCbs, receptionIdentity) + if err != nil { + jww.FATAL.Panicf("%+v", err) + } } - authCbs.client = client - if protoUser := viper.GetString("protoUserOut"); protoUser != "" { jsonBytes, err := client.ConstructProtoUserFile() @@ -785,7 +826,7 @@ func addAuthenticatedChannel(client *xxdk.E2e, recipientID *id.ID, recipientContact := recipient if recipientContact.ID != nil && recipientContact.DhPubKey != nil { - me := client.GetUser().GetContact() + me := client.GetReceptionIdentity().GetContact() jww.INFO.Printf("Requesting auth channel from: %s", recipientID) @@ -1011,20 +1052,18 @@ func parsePassword(pwStr string) []byte { } } -func parseRecipient(idStr string) (*id.ID, bool) { +func parseRecipient(idStr string) *id.ID { if idStr == "0" { - return nil, false + jww.FATAL.Panicf("No recipient specified") } - var recipientID *id.ID if strings.HasPrefix(idStr, "0x") { - recipientID = getUIDFromHexString(idStr[2:]) + return getUIDFromHexString(idStr[2:]) } else if strings.HasPrefix(idStr, "b64:") { - recipientID = getUIDFromb64String(idStr[4:]) + return getUIDFromb64String(idStr[4:]) } else { - recipientID = getUIDFromString(idStr) + return getUIDFromString(idStr) } - return recipientID, isPrecanID(recipientID) } func getUIDFromHexString(idStr string) *id.ID { @@ -1291,7 +1330,7 @@ func init() { "for confirmation") viper.BindPFlag("send-auth-request", rootCmd.Flags().Lookup("send-auth-request")) - rootCmd.Flags().UintP("auth-timeout", "", 120, + rootCmd.Flags().UintP("auth-timeout", "", 60, "The number of seconds to wait for an authentication channel"+ "to confirm") viper.BindPFlag("auth-timeout", diff --git a/cmd/single.go b/cmd/single.go index 2ce03e5c35162a909d3c502410ed35bede436c13..ed6939618864287668739abd0e6e258c02ae41f4 100644 --- a/cmd/single.go +++ b/cmd/single.go @@ -33,12 +33,11 @@ var singleCmd = &cobra.Command{ Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - client := initClient() + client := initE2e() // Write user contact to file - user := client.GetUser() - jww.INFO.Printf("User: %s", user.ReceptionID) - jww.INFO.Printf("User Transmission: %s", user.TransmissionID) + user := client.GetReceptionIdentity() + jww.INFO.Printf("User: %s", user.ID) writeContact(user.GetContact()) err := client.StartNetworkFollower(5 * time.Second) @@ -66,9 +65,14 @@ var singleCmd = &cobra.Command{ }), } - myID := client.GetUser().ReceptionID + dhKeyPriv, err := user.GetDHKeyPrivate() + if err != nil { + jww.FATAL.Panicf("%+v", err) + } + + myID := user.ID listener := single.Listen(tag, myID, - client.GetUser().E2eDhPrivateKey, + dhKeyPriv, client.GetCmix(), client.GetStorage().GetE2EGroup(), receiver) diff --git a/cmd/ud.go b/cmd/ud.go index 4666cba3fec19f620b0903e191c3955b2b42439a..44d159684a40756dcc724c88b1a17b94c7ed9b65 100644 --- a/cmd/ud.go +++ b/cmd/ud.go @@ -33,11 +33,11 @@ var udCmd = &cobra.Command{ Short: "Register for and search users using the xx network user discovery service.", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - client := initClient() + client := initE2e() // get user and save contact to file - user := client.GetUser() - jww.INFO.Printf("User: %s", user.ReceptionID) + user := client.GetReceptionIdentity() + jww.INFO.Printf("User: %s", user.ID) writeContact(user.GetContact()) // // Set up reception handler @@ -78,21 +78,21 @@ var udCmd = &cobra.Command{ waitUntilConnected(connected) // Make user discovery manager - stream := client.GetRng().GetStream() - defer stream.Close() + rng := client.GetRng() userToRegister := viper.GetString("register") userDiscoveryMgr, err := ud.NewManager(client.GetCmix(), client.GetE2E(), client.NetworkFollowerStatus, client.GetEventReporter(), client.GetComms(), client.GetStorage(), - stream, + rng, userToRegister, client.GetStorage().GetKV()) if err != nil { if strings.Contains(err.Error(), ud.IsRegisteredErr) { userDiscoveryMgr, err = ud.LoadManager(client.GetCmix(), client.GetE2E(), client.GetEventReporter(), client.GetComms(), - client.GetStorage(), client.GetStorage().GetKV()) + client.GetStorage(), client.GetRng(), + client.GetStorage().GetKV()) if err != nil { jww.FATAL.Panicf("Failed to load UD manager: %+v", err) } @@ -152,7 +152,7 @@ var udCmd = &cobra.Command{ // Note: Cryptographic verification occurs above the bindings layer lookupIDStr := viper.GetString("lookup") if lookupIDStr != "" { - lookupID, _ := parseRecipient(lookupIDStr) + lookupID := parseRecipient(lookupIDStr) //if !ok { // jww.FATAL.Panicf("Could not parse recipient: %s", lookupIDStr) //} @@ -163,12 +163,15 @@ var udCmd = &cobra.Command{ } printContact(newContact) } + + stream := rng.GetStream() _, _, err = ud.Lookup(client.GetCmix(), stream, client.GetE2E().GetGroup(), udContact, cb, lookupID, single.GetDefaultRequestParams()) if err != nil { jww.WARN.Printf("Failed UD lookup: %+v", err) } + stream.Close() time.Sleep(31 * time.Second) } @@ -256,6 +259,8 @@ var udCmd = &cobra.Command{ } } + stream := rng.GetStream() + defer stream.Close() _, _, err = ud.Search(client.GetCmix(), client.GetEventReporter(), stream, client.GetE2E().GetGroup(), diff --git a/cmd/utils.go b/cmd/utils.go index aca4fc016ca31eae6efee3087791510ddef0ea42..12c01738e45ecae13c9a4fef3fdb4140f3284a42 100644 --- a/cmd/utils.go +++ b/cmd/utils.go @@ -87,8 +87,7 @@ func writeContact(c contact.Contact) { } } -func readContact() contact.Contact { - inputFilePath := viper.GetString("destfile") +func readContact(inputFilePath string) contact.Contact { if inputFilePath == "" { return contact.Contact{} } diff --git a/cmix/client.go b/cmix/client.go index a06e21f4d3a1c94e492a81ddd7bed6679a1547f9..996cacdb46016a7675cde906cbcd9c66dc68c766 100644 --- a/cmix/client.go +++ b/cmix/client.go @@ -147,6 +147,11 @@ func (c *client) initialize(ndf *ndf.NetworkDefinition) error { // Disable KeepAlive packets poolParams.HostParams.KaClientOpts.Time = time.Duration(math.MaxInt64) + // Configure the proxy error exponential moving average tracker + poolParams.HostParams.ProxyErrorMetricParams.Cutoff = 0.30 + poolParams.HostParams.ProxyErrorMetricParams.InitialAverage = + 0.75 * poolParams.HostParams.ProxyErrorMetricParams.Cutoff + // Enable optimized HostPool initialization poolParams.MaxPings = 50 poolParams.ForceConnection = true diff --git a/cmix/gateway/hostPool.go b/cmix/gateway/hostPool.go index 33bf7e9837fd51a9fa059cd3ddef7ae11158ced1..ca9e0c7b606cb50bd27e932f8da625fcc39aaae9 100644 --- a/cmix/gateway/hostPool.go +++ b/cmix/gateway/hostPool.go @@ -47,6 +47,7 @@ var errorsList = []string{ ndf.NO_NDF, "Host is in cool down", grpc.ErrClientConnClosing.Error(), + connect.TooManyProxyError, } // HostManager Interface allowing storage and retrieval of Host objects diff --git a/connect/authCallbacks.go b/connect/authCallbacks.go new file mode 100644 index 0000000000000000000000000000000000000000..cdf8b849fdf3b4065d3e19b62ec5191b79fbf48a --- /dev/null +++ b/connect/authCallbacks.go @@ -0,0 +1,153 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2022 Privategrity Corporation / +// / +// All rights reserved. / +//////////////////////////////////////////////////////////////////////////////// + +package connect + +import ( + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/auth" + "gitlab.com/elixxir/client/cmix/identity/receptionID" + "gitlab.com/elixxir/client/cmix/rounds" + clientE2e "gitlab.com/elixxir/client/e2e" + "gitlab.com/elixxir/client/xxdk" + "gitlab.com/elixxir/crypto/contact" +) + +// clientAuthCallback provides callback functionality for interfacing between +// auth.State and Connection. This is used both for blocking creation of a +// Connection object until the auth Request is confirmed and for dynamically +// building new Connection objects when an auth Request is received. +type clientAuthCallback struct { + // Used for signaling confirmation of E2E partnership + confirmCallback Callback + requestCallback Callback + + // Used for building new Connection objects + connectionE2e clientE2e.Handler + connectionParams Params + authState auth.State +} + +// getClientAuthCallback returns a callback interface to be passed into the creation +// of an auth.State object. +// it will accept requests only if a request callback is passed in +func getClientAuthCallback(confirm, request Callback, e2e clientE2e.Handler, + auth auth.State, params Params) *clientAuthCallback { + return &clientAuthCallback{ + confirmCallback: confirm, + requestCallback: request, + connectionE2e: e2e, + connectionParams: params, + authState: auth, + } +} + +// Confirm will be called when an auth Confirm message is processed. +func (a clientAuthCallback) Confirm(requestor contact.Contact, + _ receptionID.EphemeralIdentity, _ rounds.Round) { + jww.DEBUG.Printf("Connection auth request for %s confirmed", + requestor.ID.String()) + defer a.authState.DeletePartnerCallback(requestor.ID) + + // 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 + if a.confirmCallback != nil { + a.confirmCallback(nil) + } + return + } + + // Return the new Connection object + if a.confirmCallback != nil { + a.confirmCallback(BuildConnection(newPartner, a.connectionE2e, + a.authState, a.connectionParams)) + } +} + +// Request will be called when an auth Request message is processed. +func (a clientAuthCallback) Request(contact.Contact, + receptionID.EphemeralIdentity, rounds.Round) { +} + +// Reset will be called when an auth Reset operation occurs. +func (a clientAuthCallback) Reset(contact.Contact, + receptionID.EphemeralIdentity, rounds.Round) { +} + +// serverAuthCallback provides callback functionality for interfacing between +// auth.State and Connection. This is used both for blocking creation of a +// Connection object until the auth Request is confirmed and for dynamically +// building new Connection objects when an auth Request is received. +type serverAuthCallback struct { + // Used for signaling confirmation of E2E partnership + confirmCallback Callback + requestCallback Callback + + // Used to track stale connections + cl *ConnectionList + + // Used for building new Connection objects + connectionParams Params +} + +// getServerAuthCallback returns a callback interface to be passed into the creation +// of a xxdk.E2e object. +// it will accept requests only if a request callback is passed in +func getServerAuthCallback(confirm, request Callback, cl *ConnectionList, + params Params) *serverAuthCallback { + return &serverAuthCallback{ + confirmCallback: confirm, + requestCallback: request, + cl: cl, + connectionParams: params, + } +} + +// Confirm will be called when an auth Confirm message is processed. +func (a serverAuthCallback) Confirm(contact.Contact, + receptionID.EphemeralIdentity, rounds.Round, *xxdk.E2e) { +} + +// Request will be called when an auth Request message is processed. +func (a serverAuthCallback) Request(requestor contact.Contact, + _ receptionID.EphemeralIdentity, _ rounds.Round, e2e *xxdk.E2e) { + if a.requestCallback == nil { + jww.ERROR.Printf("Received a request when requests are" + + "not enable, will not accept") + } + _, err := e2e.GetAuth().Confirm(requestor) + 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.requestCallback(nil) + } + // After confirmation, get the new partner + newPartner, err := e2e.GetE2E().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.requestCallback(nil) + + return + } + + // Return the new Connection object + c := BuildConnection( + newPartner, e2e.GetE2E(), e2e.GetAuth(), a.connectionParams) + a.cl.Add(c) + a.requestCallback(c) +} + +// Reset will be called when an auth Reset operation occurs. +func (a serverAuthCallback) Reset(contact.Contact, + receptionID.EphemeralIdentity, rounds.Round, *xxdk.E2e) { +} diff --git a/connect/authenticated.go b/connect/authenticated.go index 89311eff3ee33dd957b790c06a98e3de7bd745b9..926cd15435d61883896117c98a5c370860365c27 100644 --- a/connect/authenticated.go +++ b/connect/authenticated.go @@ -66,7 +66,11 @@ func ConnectWithAuthentication(recipient contact.Contact, e2eClient *xxdk.E2e, // Build the authenticated connection and return identity := e2eClient.GetReceptionIdentity() - return connectWithAuthentication(conn, timeStart, recipient, identity.Salt, identity.RSAPrivatePem, + privKey, err := identity.GetRSAPrivatePem() + if err != nil { + return nil, err + } + return connectWithAuthentication(conn, timeStart, recipient, identity.Salt, privKey, e2eClient.GetRng(), e2eClient.GetCmix(), p) } @@ -170,7 +174,7 @@ func connectWithAuthentication(conn Connection, timeStart time.Time, // authenticate themselves. An established AuthenticatedConnection will // be passed via the callback. func StartAuthenticatedServer(identity xxdk.ReceptionIdentity, - cb AuthenticatedCallback, net *xxdk.Cmix, p Params) (*xxdk.E2e, error) { + cb AuthenticatedCallback, net *xxdk.Cmix, p Params) (*ConnectionServer, error) { // Register the waiter for a connection establishment connCb := Callback(func(connection Connection) { @@ -178,8 +182,14 @@ func StartAuthenticatedServer(identity xxdk.ReceptionIdentity, // client's identity proof. If an identity authentication // message is received and validated, an authenticated connection will // be passed along via the AuthenticatedCallback - connection.RegisterListener(catalog.ConnectionAuthenticationRequest, + _, err := connection.RegisterListener( + catalog.ConnectionAuthenticationRequest, buildAuthConfirmationHandler(cb, connection)) + if err != nil { + jww.ERROR.Printf( + "Failed to register listener on connection with %s: %+v", + connection.GetPartner().PartnerId(), err) + } }) return StartServer(identity, connCb, net, p) } diff --git a/connect/authenticated_test.go b/connect/authenticated_test.go index 3668a742229ed15a27af3697c8a1ae05ad7deca2..5a8a3112fbe10e7672fae864d75f995dcb0c1c50 100644 --- a/connect/authenticated_test.go +++ b/connect/authenticated_test.go @@ -42,7 +42,7 @@ func TestConnectWithAuthentication(t *testing.T) { myRsaPrivKey, err := rsa.LoadPrivateKeyFromPem(getPrivKey()) if err != nil { - t.Fatalf("Faled to load private key: %v", err) + t.Fatalf("Failed to load private key: %v", err) } // Construct client ID the proper way as server will need to verify it diff --git a/connect/connect.go b/connect/connect.go index e7d4d14c5c8b1cdee3da162226ba8f7c59313e31..0a6b9a7fc8b1d95125bf24ac9d0f9cfff245205e 100644 --- a/connect/connect.go +++ b/connect/connect.go @@ -8,21 +8,21 @@ package connect import ( "encoding/json" + "gitlab.com/elixxir/client/e2e/rekey" + "gitlab.com/elixxir/client/event" "gitlab.com/elixxir/client/xxdk" + "gitlab.com/xx_network/primitives/netTime" "io" + "sync/atomic" "time" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/auth" "gitlab.com/elixxir/client/catalog" - "gitlab.com/elixxir/client/cmix/identity/receptionID" - "gitlab.com/elixxir/client/cmix/rounds" clientE2e "gitlab.com/elixxir/client/e2e" "gitlab.com/elixxir/client/e2e/ratchet/partner" "gitlab.com/elixxir/client/e2e/receive" - "gitlab.com/elixxir/client/e2e/rekey" - "gitlab.com/elixxir/client/event" "gitlab.com/elixxir/crypto/contact" "gitlab.com/elixxir/crypto/e2e" "gitlab.com/xx_network/primitives/id" @@ -34,6 +34,8 @@ const ( connectionTimeout = 15 * time.Second ) +var alreadyClosedErr = errors.New("connection is closed") + // Connection is a wrapper for the E2E and auth packages. // It can be used to automatically establish an E2E partnership // with a partner.Manager, or be built from an existing E2E partnership. @@ -54,7 +56,7 @@ type Connection interface { // RegisterListener is used for E2E reception // and allows for reading data sent from the partner.Manager RegisterListener(messageType catalog.MessageType, - newListener receive.Listener) receive.ListenerID + newListener receive.Listener) (receive.ListenerID, error) // Unregister listener for E2E reception Unregister(listenerID receive.ListenerID) @@ -73,6 +75,10 @@ type Connection interface { // PayloadSize Returns the max payload size for a partitionable E2E // message PayloadSize() uint + + // LastUse returns the timestamp of the last time the connection was + // utilised. + LastUse() time.Time } // Callback is the callback format required to retrieve @@ -84,6 +90,7 @@ type Params struct { Auth auth.Params Rekey rekey.Params Event event.Reporter `json:"-"` + List ConnectionListParams Timeout time.Duration } @@ -93,6 +100,7 @@ func GetDefaultParams() Params { Auth: auth.GetDefaultTemporaryParams(), Rekey: rekey.GetDefaultEphemeralParams(), Event: event.NewEventManager(), + List: DefaultConnectionListParams(), Timeout: connectionTimeout, } } @@ -116,13 +124,12 @@ func GetParameters(params string) (Params, error) { // partner.Manager is confirmed. func Connect(recipient contact.Contact, e2eClient *xxdk.E2e, p Params) (Connection, error) { - // Build callback for E2E negotiation signalChannel := make(chan Connection, 1) cb := func(connection Connection) { signalChannel <- connection } - callback := getAuthCallback(cb, nil, e2eClient.GetE2E(), e2eClient.GetAuth(), p) + callback := getClientAuthCallback(cb, nil, e2eClient.GetE2E(), e2eClient.GetAuth(), p) e2eClient.GetAuth().AddPartnerCallback(recipient.ID, callback) // Perform the auth request @@ -161,19 +168,31 @@ func Connect(recipient contact.Contact, e2eClient *xxdk.E2e, // This call does an xxDK.ephemeralLogin under the hood and the connection // server must be the only listener on auth. func StartServer(identity xxdk.ReceptionIdentity, cb Callback, net *xxdk.Cmix, - p Params) (*xxdk.E2e, error) { + p Params) (*ConnectionServer, error) { + + // Create connection list and start cleanup thread + cl := NewConnectionList(p.List) + err := net.AddService(cl.CleanupThread) + if err != nil { + return nil, err + } // Build callback for E2E negotiation - callback := getAuthCallback(nil, cb, nil, nil, p) + callback := getServerAuthCallback(nil, cb, cl, p) - client, err := xxdk.LoginEphemeral(net, callback, identity) + e2eClient, err := xxdk.LoginEphemeral(net, callback, identity) if err != nil { return nil, err } - callback.connectionE2e = client.GetE2E() - callback.authState = client.GetAuth() - return client, nil + // Return an ephemeral E2e object + return &ConnectionServer{e2eClient, cl}, nil +} + +// ConnectionServer contains +type ConnectionServer struct { + E2e *xxdk.E2e + Cl *ConnectionList } // handler provides an implementation for the Connection interface. @@ -181,7 +200,14 @@ type handler struct { auth auth.State partner partner.Manager e2e clientE2e.Handler - params Params + + // Timestamp of last time a message was sent or received (Unix nanoseconds) + lastUse *int64 + + // Indicates if the connection has been closed (0 = open, 1 = closed) + closed *uint32 + + params Params } // BuildConnection assembles a Connection object @@ -189,20 +215,42 @@ type handler struct { // partner.Manager. func BuildConnection(partner partner.Manager, e2eHandler clientE2e.Handler, auth auth.State, p Params) Connection { + lastUse := netTime.Now().UnixNano() + closed := uint32(0) return &handler{ auth: auth, partner: partner, params: p, e2e: e2eHandler, + lastUse: &lastUse, + closed: &closed, } } -// Close deletes this Connection's partner.Manager and releases resources. +// Close deletes this Connection's partner.Manager and releases resources. If +// the connection is already closed, then nil is returned. func (h *handler) Close() error { - if err := h.e2e.DeletePartner(h.partner.PartnerId()); err != nil { + if h.isClosed() { + return nil + } + + // Get partner ID once at the top because PartnerId makes a copy + partnerID := h.partner.PartnerId() + + // Unregister all listeners + h.e2e.UnregisterUserListeners(partnerID) + + // Delete partner from e2e and auth + if err := h.e2e.DeletePartner(partnerID); err != nil { + return err + } + if err := h.auth.DeletePartner(partnerID); err != nil { return err } - return h.auth.Close() + + atomic.StoreUint32(h.closed, 1) + + return nil } // GetPartner returns the partner.Manager for this Connection. @@ -213,17 +261,25 @@ func (h *handler) GetPartner() partner.Manager { // SendE2E is a wrapper for sending specifically to the Connection's // partner.Manager. func (h *handler) SendE2E(mt catalog.MessageType, payload []byte, - params clientE2e.Params) ( - []id.Round, e2e.MessageID, time.Time, error) { + params clientE2e.Params) ([]id.Round, e2e.MessageID, time.Time, error) { + if h.isClosed() { + return nil, e2e.MessageID{}, time.Time{}, alreadyClosedErr + } + + h.updateLastUse(netTime.Now()) + return h.e2e.SendE2E(mt, h.partner.PartnerId(), payload, params) } // RegisterListener is used for E2E reception // and allows for reading data sent from the partner.Manager. func (h *handler) RegisterListener(messageType catalog.MessageType, - newListener receive.Listener) receive.ListenerID { - return h.e2e.RegisterListener(h.partner.PartnerId(), - messageType, newListener) + newListener receive.Listener) (receive.ListenerID, error) { + if h.isClosed() { + return receive.ListenerID{}, alreadyClosedErr + } + lt := &listenerTracker{h, newListener} + return h.e2e.RegisterListener(h.partner.PartnerId(), messageType, lt), nil } // Unregister listener for E2E reception. @@ -231,96 +287,6 @@ func (h *handler) Unregister(listenerID receive.ListenerID) { h.e2e.Unregister(listenerID) } -// authCallback provides callback functionality for interfacing between -// auth.State and Connection. This is used both for blocking creation of a -// Connection object until the auth Request is confirmed and for dynamically -// building new Connection objects when an auth Request is received. -type authCallback struct { - // Used for signaling confirmation of E2E partnership - confirmCallback Callback - requestCallback Callback - - // Used for building new Connection objects - connectionE2e clientE2e.Handler - connectionParams Params - authState auth.State -} - -// getAuthCallback returns a callback interface to be passed into the creation -// of an auth.State object. -// it will accept requests only if a request callback is passed in -func getAuthCallback(confirm, request Callback, e2e clientE2e.Handler, - auth auth.State, params Params) *authCallback { - return &authCallback{ - confirmCallback: confirm, - requestCallback: request, - connectionE2e: e2e, - connectionParams: params, - authState: auth, - } -} - -// Confirm will be called when an auth Confirm message is processed. -func (a authCallback) Confirm(requestor contact.Contact, - receptionID receptionID.EphemeralIdentity, round rounds.Round) { - jww.DEBUG.Printf("Connection auth request for %s confirmed", - requestor.ID.String()) - defer a.authState.DeletePartnerCallback(requestor.ID) - - // 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 - if a.confirmCallback != nil { - a.confirmCallback(nil) - } - return - } - - // Return the new Connection object - if a.confirmCallback != nil { - a.confirmCallback(BuildConnection(newPartner, a.connectionE2e, - a.authState, a.connectionParams)) - } -} - -// Request will be called when an auth Request message is processed. -func (a authCallback) Request(requestor contact.Contact, - receptionID receptionID.EphemeralIdentity, round rounds.Round) { - if a.requestCallback == nil { - jww.ERROR.Printf("Received a request when requests are" + - "not enable, will not accept") - } - _, err := a.authState.Confirm(requestor) - 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.requestCallback(nil) - } - // 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.requestCallback(nil) - - return - } - - // Return the new Connection object - a.requestCallback(BuildConnection(newPartner, a.connectionE2e, - a.authState, a.connectionParams)) -} - -// Reset will be called when an auth Reset operation occurs. -func (a authCallback) Reset(requestor contact.Contact, - receptionID receptionID.EphemeralIdentity, round rounds.Round) { -} - // FirstPartitionSize returns the max partition payload size for the // first payload func (h *handler) FirstPartitionSize() uint { @@ -344,3 +310,18 @@ func (h *handler) PartitionSize(payloadIndex uint) uint { func (h *handler) PayloadSize() uint { return h.e2e.PayloadSize() } + +// LastUse returns the timestamp of the last time the connection was utilised. +func (h *handler) LastUse() time.Time { + return time.Unix(0, atomic.LoadInt64(h.lastUse)) +} + +// updateLastUse updates the last use time stamp to the given time. +func (h *handler) updateLastUse(t time.Time) { + atomic.StoreInt64(h.lastUse, t.UnixNano()) +} + +// isClosed returns true if the connection is closed. +func (h *handler) isClosed() bool { + return atomic.LoadUint32(h.closed) == 1 +} diff --git a/connect/connectionList.go b/connect/connectionList.go new file mode 100644 index 0000000000000000000000000000000000000000..fab3404126334ce4c3e0b29f46f9e307a751894e --- /dev/null +++ b/connect/connectionList.go @@ -0,0 +1,119 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +//////////////////////////////////////////////////////////////////////////////// + +package connect + +import ( + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/stoppable" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/netTime" + "sync" + "time" +) + +// ConnectionList is a list of all connections. +type ConnectionList struct { + list map[id.ID]Connection + p ConnectionListParams + mux sync.Mutex +} + +// NewConnectionList initialises an empty ConnectionList. +func NewConnectionList(p ConnectionListParams) *ConnectionList { + return &ConnectionList{ + list: make(map[id.ID]Connection), + p: p, + } +} + +// Add adds the connection to the list. +func (cl *ConnectionList) Add(c Connection) { + cl.mux.Lock() + defer cl.mux.Unlock() + + cl.list[*c.GetPartner().PartnerId()] = c +} + +// CleanupThread runs the loop that runs the cleanup processes periodically. +func (cl *ConnectionList) CleanupThread() (stoppable.Stoppable, error) { + stop := stoppable.NewSingle("StaleConnectionCleanup") + + go func() { + jww.INFO.Printf("Starting stale connection cleanup thread to delete "+ + "connections older than %s. Running every %s.", + cl.p.MaxAge, cl.p.CleanupPeriod) + ticker := time.NewTicker(cl.p.CleanupPeriod) + for { + select { + case <-stop.Quit(): + jww.INFO.Print( + "Stopping connection cleanup thread: stoppable triggered") + ticker.Stop() + stop.ToStopped() + case <-ticker.C: + jww.DEBUG.Print("Starting connection cleanup.") + cl.Cleanup() + } + } + }() + + return stop, nil +} + +// Cleanup disconnects all connections that have been stale for longer than the +// max allowed time. +func (cl *ConnectionList) Cleanup() { + cl.mux.Lock() + defer cl.mux.Unlock() + + for partnerID, c := range cl.list { + lastUse := c.LastUse() + timeSinceLastUse := netTime.Since(lastUse) + if timeSinceLastUse > cl.p.MaxAge { + err := c.Close() + if err != nil { + jww.ERROR.Printf( + "Could not close connection with partner %s: %+v", + partnerID, err) + } + delete(cl.list, partnerID) + + jww.INFO.Printf("Deleted stale connection for partner %s. "+ + "Last use was %s ago (%s)", + &partnerID, timeSinceLastUse, lastUse.Local()) + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Parameters // +//////////////////////////////////////////////////////////////////////////////// + +// Default values. +const ( + cleanupPeriodDefault = 5 * time.Minute + maxAgeDefault = 30 * time.Minute +) + +// ConnectionListParams are the parameters used for the ConnectionList. +type ConnectionListParams struct { + // CleanupPeriod is the duration between when cleanups occur. + CleanupPeriod time.Duration + + // MaxAge is the maximum age of an unused connection before it is deleted. + MaxAge time.Duration +} + +// DefaultConnectionListParams returns a ConnectionListParams filled with +// default values. +func DefaultConnectionListParams() ConnectionListParams { + return ConnectionListParams{ + CleanupPeriod: cleanupPeriodDefault, + MaxAge: maxAgeDefault, + } +} diff --git a/connect/connectionList_test.go b/connect/connectionList_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b76c6b35876ee6656b099a46b663a75820db08e5 --- /dev/null +++ b/connect/connectionList_test.go @@ -0,0 +1,125 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +//////////////////////////////////////////////////////////////////////////////// + +package connect + +import ( + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/netTime" + "reflect" + "testing" + "time" +) + +// Tests that NewConnectionList returned the expected new ConnectionList. +func TestNewConnectionList(t *testing.T) { + expected := &ConnectionList{ + list: make(map[id.ID]Connection), + p: DefaultConnectionListParams(), + } + + cl := NewConnectionList(expected.p) + + if !reflect.DeepEqual(expected, cl) { + t.Errorf("New ConnectionList did not match expected."+ + "\nexpected: %+v\nreceived: %+v", expected, cl) + } +} + +// Tests that ConnectionList.Add adds all the given connections to the list. +func TestConnectionList_Add(t *testing.T) { + cl := NewConnectionList(DefaultConnectionListParams()) + + expected := map[id.ID]Connection{ + *id.NewIdFromString("p1", id.User, t): &handler{ + partner: &mockPartner{partnerId: id.NewIdFromString("p1", id.User, t)}}, + *id.NewIdFromString("p2", id.User, t): &handler{ + partner: &mockPartner{partnerId: id.NewIdFromString("p2", id.User, t)}}, + *id.NewIdFromString("p3", id.User, t): &handler{ + partner: &mockPartner{partnerId: id.NewIdFromString("p3", id.User, t)}}, + *id.NewIdFromString("p4", id.User, t): &handler{ + partner: &mockPartner{partnerId: id.NewIdFromString("p4", id.User, t)}}, + *id.NewIdFromString("p5", id.User, t): &handler{ + partner: &mockPartner{partnerId: id.NewIdFromString("p5", id.User, t)}}, + } + + for _, c := range expected { + cl.Add(c) + } + + if !reflect.DeepEqual(expected, cl.list) { + t.Errorf("List does not have expected connections."+ + "\nexpected: %+v\nreceived: %+v", expected, cl.list) + } + +} + +// Tests that ConnectionList.Cleanup deletes only stale connections from the +// list and that they are closed. +func TestConnectionList_Cleanup(t *testing.T) { + cl := NewConnectionList(DefaultConnectionListParams()) + + list := []*mockConnection{ + { + partner: &mockPartner{partnerId: id.NewIdFromString("p0", id.User, t)}, + lastUse: netTime.Now().Add(-(cl.p.MaxAge * 2)), + }, { + partner: &mockPartner{partnerId: id.NewIdFromString("p1", id.User, t)}, + lastUse: netTime.Now().Add(-(cl.p.MaxAge / 2)), + }, { + partner: &mockPartner{partnerId: id.NewIdFromString("p2", id.User, t)}, + lastUse: netTime.Now().Add(-(cl.p.MaxAge + 10)), + }, { + partner: &mockPartner{partnerId: id.NewIdFromString("p3", id.User, t)}, + lastUse: netTime.Now().Add(-(cl.p.MaxAge - time.Second)), + }, + } + + for _, c := range list { + cl.Add(c) + } + + cl.Cleanup() + + for i, c := range list { + if i%2 == 0 { + if _, exists := cl.list[*c.GetPartner().PartnerId()]; exists { + t.Errorf("Connection #%d exists while being stale.", i) + } + if !c.closed { + t.Errorf("Connection #%d was not closed.", i) + } + } else { + if _, exists := cl.list[*c.GetPartner().PartnerId()]; !exists { + t.Errorf("Connection #%d was removed when it was not stale.", i) + } + if c.closed { + t.Errorf("Connection #%d was closed.", i) + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Parameters // +//////////////////////////////////////////////////////////////////////////////// + +// Tests that DefaultConnectionListParams returns a ConnectionListParams with +// the expected default values. +func TestDefaultConnectionListParams(t *testing.T) { + expected := ConnectionListParams{ + CleanupPeriod: cleanupPeriodDefault, + MaxAge: maxAgeDefault, + } + + p := DefaultConnectionListParams() + + if !reflect.DeepEqual(expected, p) { + t.Errorf("Default ConnectionListParams does not match expected."+ + "\nexpected: %+v\nreceived: %+v", expected, p) + } +} diff --git a/connect/listenerTracker.go b/connect/listenerTracker.go new file mode 100644 index 0000000000000000000000000000000000000000..875598dfe53d314cf0052c2ab592ceff425b032c --- /dev/null +++ b/connect/listenerTracker.go @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +//////////////////////////////////////////////////////////////////////////////// + +package connect + +import ( + "gitlab.com/elixxir/client/e2e/receive" + "gitlab.com/xx_network/primitives/netTime" +) + +// listenerTracker wraps a listener and updates the last use timestamp on every +// call to Hear. +type listenerTracker struct { + h *handler + l receive.Listener +} + +// Hear updates the last call timestamp and then calls Hear on the wrapped +// listener. +func (lt *listenerTracker) Hear(item receive.Message) { + lt.h.updateLastUse(netTime.Now()) + lt.l.Hear(item) +} + +// Name returns a name, used for debugging. +func (lt *listenerTracker) Name() string { return lt.l.Name() } diff --git a/connect/listenerTracker_test.go b/connect/listenerTracker_test.go new file mode 100644 index 0000000000000000000000000000000000000000..1cab220ad7955c6b25be5b716fc41197eefc055b --- /dev/null +++ b/connect/listenerTracker_test.go @@ -0,0 +1,74 @@ +//////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +//////////////////////////////////////////////////////////////////////////////// + +package connect + +import ( + "gitlab.com/elixxir/client/e2e/receive" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/netTime" + "reflect" + "testing" + "time" +) + +// Tests that listenerTracker.Hear correctly updates the last use timestamp and +// calls the wrapped Hear function. +func Test_listenerTracker_Hear(t *testing.T) { + lastUsed, closed := int64(0), uint32(0) + itemChan := make(chan receive.Message, 1) + lt := &listenerTracker{ + h: &handler{ + lastUse: &lastUsed, + closed: &closed, + }, + l: &mockListener{itemChan, "mockListener"}, + } + + expected := receive.Message{ + Payload: []byte("Message payload."), + Sender: id.NewIdFromString("senderID", id.User, t), + RecipientID: id.NewIdFromString("RecipientID", id.User, t), + } + + lt.Hear(expected) + + select { + case r := <-itemChan: + if !reflect.DeepEqual(expected, r) { + t.Errorf("Did not receive expected receive.Message."+ + "\nexpected: %+v\nreceived: %+v", expected, r) + } + if netTime.Since(lt.h.LastUse()) > 300*time.Millisecond { + t.Errorf("Last use has incorrect time: %s", lt.h.LastUse()) + } + + case <-time.After(30 * time.Millisecond): + t.Error("Timed out waiting for Hear to be called.") + } +} + +// Tests that listenerTracker.Name calls the wrapped listener Name function. +func Test_listenerTracker_Name(t *testing.T) { + expected := "mockListener" + lt := &listenerTracker{ + l: &mockListener{make(chan receive.Message, 1), "mockListener"}, + } + + if lt.Name() != expected { + t.Errorf("Did not get expected name.\nexected: %s\nreceived: %s", + expected, lt.Name()) + } +} + +type mockListener struct { + item chan receive.Message + name string +} + +func (m *mockListener) Hear(item receive.Message) { m.item <- item } +func (m *mockListener) Name() string { return m.name } diff --git a/connect/utils_test.go b/connect/utils_test.go index dec043d0433cfe2efe7d6016a45ae61a1c1d1488..6e4be5173d00cd8ba4b671d6abee3a8669e1c77c 100644 --- a/connect/utils_test.go +++ b/connect/utils_test.go @@ -22,15 +22,17 @@ import ( "gitlab.com/xx_network/crypto/large" "gitlab.com/xx_network/primitives/id" "gitlab.com/xx_network/primitives/id/ephemeral" - "gitlab.com/xx_network/primitives/ndf" "gitlab.com/xx_network/primitives/netTime" "time" ) //////////////////////////////////////////////////////////////////////////////// -// Mock Partner Interface // +// Mock Partner Interface // //////////////////////////////////////////////////////////////////////////////// +// Tests that mockPartner adheres to the partner.Manager interface. +var _ partner.Manager = (*mockPartner)(nil) + type mockPartner struct { partnerId *id.ID myID *id.ID @@ -48,126 +50,73 @@ func newMockPartner(partnerId, myId *id.ID, } } -func (m mockPartner) PartnerId() *id.ID { - return m.partnerId -} - -func (m mockPartner) MyId() *id.ID { - return m.myID -} - -func (m mockPartner) MyRootPrivateKey() *cyclic.Int { - return m.myDhPrivKey -} - -func (m mockPartner) PartnerRootPublicKey() *cyclic.Int { - return m.partnerDhPubKey -} - -func (m mockPartner) SendRelationshipFingerprint() []byte { - return nil -} - -func (m mockPartner) ReceiveRelationshipFingerprint() []byte { - return nil -} - -func (m mockPartner) ConnectionFingerprint() partner.ConnectionFp { - return partner.ConnectionFp{} -} - -func (m mockPartner) Contact() contact.Contact { +func (m *mockPartner) PartnerId() *id.ID { return m.partnerId } +func (m *mockPartner) MyId() *id.ID { return m.myID } +func (m *mockPartner) MyRootPrivateKey() *cyclic.Int { return m.myDhPrivKey } +func (m *mockPartner) PartnerRootPublicKey() *cyclic.Int { return m.partnerDhPubKey } +func (m *mockPartner) SendRelationshipFingerprint() []byte { return nil } +func (m *mockPartner) ReceiveRelationshipFingerprint() []byte { return nil } +func (m *mockPartner) ConnectionFingerprint() partner.ConnectionFp { return partner.ConnectionFp{} } +func (m *mockPartner) Contact() contact.Contact { return contact.Contact{ ID: m.partnerId, DhPubKey: m.partnerDhPubKey, } } - -func (m mockPartner) PopSendCypher() (session.Cypher, error) { - return nil, nil -} - -func (m mockPartner) PopRekeyCypher() (session.Cypher, error) { - return nil, nil -} - -func (m mockPartner) NewReceiveSession(partnerPubKey *cyclic.Int, partnerSIDHPubKey *sidh.PublicKey, e2eParams session.Params, source *session.Session) (*session.Session, bool) { +func (m *mockPartner) PopSendCypher() (session.Cypher, error) { return nil, nil } +func (m *mockPartner) PopRekeyCypher() (session.Cypher, error) { return nil, nil } +func (m *mockPartner) NewReceiveSession(*cyclic.Int, *sidh.PublicKey, session.Params, *session.Session) (*session.Session, bool) { return nil, false } - -func (m mockPartner) NewSendSession(myDHPrivKey *cyclic.Int, mySIDHPrivateKey *sidh.PrivateKey, e2eParams session.Params, source *session.Session) *session.Session { - return nil -} - -func (m mockPartner) GetSendSession(sid session.SessionID) *session.Session { - return nil -} - -func (m mockPartner) GetReceiveSession(sid session.SessionID) *session.Session { - return nil -} - -func (m mockPartner) Confirm(sid session.SessionID) error { - return nil -} - -func (m mockPartner) TriggerNegotiations() []*session.Session { - return nil -} - -func (m mockPartner) MakeService(tag string) message.Service { - return message.Service{} -} - -func (m mockPartner) Delete() error { +func (m *mockPartner) NewSendSession(*cyclic.Int, *sidh.PrivateKey, session.Params, *session.Session) *session.Session { return nil } +func (m *mockPartner) GetSendSession(session.SessionID) *session.Session { return nil } +func (m *mockPartner) GetReceiveSession(session.SessionID) *session.Session { return nil } +func (m *mockPartner) Confirm(session.SessionID) error { return nil } +func (m *mockPartner) TriggerNegotiations() []*session.Session { return nil } +func (m *mockPartner) MakeService(string) message.Service { return message.Service{} } +func (m *mockPartner) Delete() error { return nil } //////////////////////////////////////////////////////////////////////////////// -// Mock Connection Interface // +// Mock Connection Interface // //////////////////////////////////////////////////////////////////////////////// +// Tests that mockConnection adheres to the Connection interface. +var _ Connection = (*mockConnection)(nil) + type mockConnection struct { partner *mockPartner payloadChan chan []byte listener server + lastUse time.Time + closed bool } -func newMockConnection(partnerId, myId *id.ID, - myDhPrivKey, partnerDhPubKey *cyclic.Int) *mockConnection { +func newMockConnection(partnerId, myId *id.ID, myDhPrivKey, + partnerDhPubKey *cyclic.Int) *mockConnection { return &mockConnection{ - partner: newMockPartner(partnerId, myId, - myDhPrivKey, partnerDhPubKey), + partner: newMockPartner(partnerId, myId, myDhPrivKey, partnerDhPubKey), payloadChan: make(chan []byte, 1), } } -func (m mockConnection) FirstPartitionSize() uint { - return 0 -} - -func (m mockConnection) SecondPartitionSize() uint { - return 0 -} - -func (m mockConnection) PartitionSize(payloadIndex uint) uint { - return 0 -} - -func (m mockConnection) PayloadSize() uint { - return 0 -} +func (m *mockConnection) FirstPartitionSize() uint { return 0 } +func (m *mockConnection) SecondPartitionSize() uint { return 0 } +func (m *mockConnection) PartitionSize(uint) uint { return 0 } +func (m *mockConnection) PayloadSize() uint { return 0 } -func (m mockConnection) Close() error { +func (m *mockConnection) Close() error { + m.closed = true return nil } -func (m mockConnection) GetPartner() partner.Manager { - return m.partner -} +func (m *mockConnection) GetPartner() partner.Manager { return m.partner } -func (m mockConnection) SendE2E(mt catalog.MessageType, payload []byte, params e2e.Params) ([]id.Round, cryptoE2e.MessageID, time.Time, error) { +func (m *mockConnection) SendE2E( + mt catalog.MessageType, payload []byte, _ e2e.Params) ( + []id.Round, cryptoE2e.MessageID, time.Time, error) { m.payloadChan <- payload m.listener.Hear(receive.Message{ MessageType: mt, @@ -178,250 +127,86 @@ func (m mockConnection) SendE2E(mt catalog.MessageType, payload []byte, params e return nil, cryptoE2e.MessageID{}, time.Time{}, nil } -func (m mockConnection) RegisterListener(messageType catalog.MessageType, newListener receive.Listener) receive.ListenerID { - return receive.ListenerID{} -} - -func (m mockConnection) Unregister(listenerID receive.ListenerID) { - +func (m *mockConnection) RegisterListener( + catalog.MessageType, receive.Listener) (receive.ListenerID, error) { + return receive.ListenerID{}, nil } +func (m *mockConnection) Unregister(receive.ListenerID) {} +func (m *mockConnection) LastUse() time.Time { return m.lastUse } //////////////////////////////////////////////////////////////////////////////// -// Mock cMix // +// Mock cMix // //////////////////////////////////////////////////////////////////////////////// +// Tests that mockCmix adheres to the cmix.Client interface. +var _ cmix.Client = (*mockCmix)(nil) + type mockCmix struct { instance *network.Instance } func newMockCmix() *mockCmix { - return &mockCmix{} } -func (m *mockCmix) Follow(report cmix.ClientErrorReport) (stoppable.Stoppable, error) { - return nil, nil -} +func (m *mockCmix) Follow(cmix.ClientErrorReport) (stoppable.Stoppable, error) { return nil, nil } -func (m *mockCmix) GetMaxMessageLength() int { - return 4096 -} - -func (m *mockCmix) Send(recipient *id.ID, fingerprint format.Fingerprint, - service message.Service, payload, mac []byte, - cmixParams cmix.CMIXParams) (id.Round, ephemeral.Id, error) { +func (m *mockCmix) GetMaxMessageLength() int { return 4096 } +func (m *mockCmix) Send(*id.ID, format.Fingerprint, message.Service, []byte, + []byte, cmix.CMIXParams) (id.Round, ephemeral.Id, error) { return 0, ephemeral.Id{}, nil } - -func (m *mockCmix) SendMany(messages []cmix.TargetedCmixMessage, p cmix.CMIXParams) (id.Round, []ephemeral.Id, error) { +func (m *mockCmix) SendMany([]cmix.TargetedCmixMessage, cmix.CMIXParams) (id.Round, []ephemeral.Id, error) { return 0, []ephemeral.Id{}, nil } - -func (m *mockCmix) AddIdentity(id *id.ID, validUntil time.Time, persistent bool) { -} - -func (m *mockCmix) RemoveIdentity(id *id.ID) { -} - -func (m *mockCmix) GetIdentity(get *id.ID) (identity.TrackedID, error) { - return identity.TrackedID{ - Creation: netTime.Now().Add(-time.Minute), - }, nil -} - -func (m *mockCmix) AddFingerprint(identity *id.ID, fp format.Fingerprint, mp message.Processor) error { - return nil -} - -func (m *mockCmix) DeleteFingerprint(identity *id.ID, fingerprint format.Fingerprint) { - return -} - -func (m *mockCmix) DeleteClientFingerprints(identity *id.ID) { - return -} - -func (m *mockCmix) AddService(clientID *id.ID, newService message.Service, response message.Processor) { - return -} - -func (m *mockCmix) DeleteService(clientID *id.ID, toDelete message.Service, processor message.Processor) { - return -} - -func (m *mockCmix) DeleteClientService(clientID *id.ID) { -} - -func (m *mockCmix) TrackServices(tracker message.ServicesTracker) { - return -} - -func (m *mockCmix) CheckInProgressMessages() { - return -} - -func (m *mockCmix) IsHealthy() bool { - return true -} - -func (m *mockCmix) WasHealthy() bool { - return true -} - -func (m *mockCmix) AddHealthCallback(f func(bool)) uint64 { - return 0 -} - -func (m *mockCmix) RemoveHealthCallback(u uint64) { - return -} - -func (m *mockCmix) HasNode(nid *id.ID) bool { - return true -} - -func (m *mockCmix) NumRegisteredNodes() int { - return 24 -} - -func (m *mockCmix) TriggerNodeRegistration(nid *id.ID) { - return -} - -func (m *mockCmix) GetRoundResults(timeout time.Duration, roundCallback cmix.RoundEventCallback, roundList ...id.Round) error { +func (m *mockCmix) AddIdentity(*id.ID, time.Time, bool) {} +func (m *mockCmix) RemoveIdentity(*id.ID) {} + +func (m *mockCmix) GetIdentity(*id.ID) (identity.TrackedID, error) { + return identity.TrackedID{Creation: netTime.Now().Add(-time.Minute)}, nil +} + +func (m *mockCmix) AddFingerprint(*id.ID, format.Fingerprint, message.Processor) error { return nil } +func (m *mockCmix) DeleteFingerprint(*id.ID, format.Fingerprint) {} +func (m *mockCmix) DeleteClientFingerprints(*id.ID) {} +func (m *mockCmix) AddService(*id.ID, message.Service, message.Processor) {} +func (m *mockCmix) DeleteService(*id.ID, message.Service, message.Processor) {} +func (m *mockCmix) DeleteClientService(*id.ID) {} +func (m *mockCmix) TrackServices(message.ServicesTracker) {} +func (m *mockCmix) CheckInProgressMessages() {} +func (m *mockCmix) IsHealthy() bool { return true } +func (m *mockCmix) WasHealthy() bool { return true } +func (m *mockCmix) AddHealthCallback(func(bool)) uint64 { return 0 } +func (m *mockCmix) RemoveHealthCallback(uint64) {} +func (m *mockCmix) HasNode(*id.ID) bool { return true } +func (m *mockCmix) NumRegisteredNodes() int { return 24 } +func (m *mockCmix) TriggerNodeRegistration(*id.ID) {} + +func (m *mockCmix) GetRoundResults(_ time.Duration, roundCallback cmix.RoundEventCallback, _ ...id.Round) error { roundCallback(true, false, nil) return nil } -func (m *mockCmix) LookupHistoricalRound(rid id.Round, callback rounds.RoundResultCallback) error { - return nil -} - -func (m *mockCmix) SendToAny(sendFunc func(host *connect.Host) (interface{}, error), stop *stoppable.Single) (interface{}, error) { +func (m *mockCmix) LookupHistoricalRound(id.Round, rounds.RoundResultCallback) error { return nil } +func (m *mockCmix) SendToAny(func(host *connect.Host) (interface{}, error), *stoppable.Single) (interface{}, error) { return nil, nil } - -func (m *mockCmix) SendToPreferred(targets []*id.ID, sendFunc gateway.SendToPreferredFunc, stop *stoppable.Single, timeout time.Duration) (interface{}, error) { - return nil, nil -} - -func (m *mockCmix) SetGatewayFilter(f gateway.Filter) { - return -} - -func (m *mockCmix) GetHostParams() connect.HostParams { - return connect.GetDefaultHostParams() -} - -func (m *mockCmix) GetAddressSpace() uint8 { - return 32 -} - -func (m *mockCmix) RegisterAddressSpaceNotification(tag string) (chan uint8, error) { +func (m *mockCmix) SendToPreferred([]*id.ID, gateway.SendToPreferredFunc, *stoppable.Single, time.Duration) (interface{}, error) { return nil, nil } - -func (m *mockCmix) UnregisterAddressSpaceNotification(tag string) { - return -} - -func (m *mockCmix) GetInstance() *network.Instance { - return m.instance -} - -func (m *mockCmix) GetVerboseRounds() string { - return "" -} +func (m *mockCmix) SetGatewayFilter(gateway.Filter) {} +func (m *mockCmix) GetHostParams() connect.HostParams { return connect.GetDefaultHostParams() } +func (m *mockCmix) GetAddressSpace() uint8 { return 32 } +func (m *mockCmix) RegisterAddressSpaceNotification(string) (chan uint8, error) { return nil, nil } +func (m *mockCmix) UnregisterAddressSpaceNotification(string) {} +func (m *mockCmix) GetInstance() *network.Instance { return m.instance } +func (m *mockCmix) GetVerboseRounds() string { return "" } //////////////////////////////////////////////////////////////////////////////// // Misc set-up utils // //////////////////////////////////////////////////////////////////////////////// -var testCert = `-----BEGIN CERTIFICATE----- -MIIF4DCCA8igAwIBAgIUegUvihtQooWNIzsNqj6lucXn6g8wDQYJKoZIhvcNAQEL -BQAwgYwxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTESMBAGA1UEBwwJQ2xhcmVt -b250MRAwDgYDVQQKDAdFbGl4eGlyMRQwEgYDVQQLDAtEZXZlbG9wbWVudDETMBEG -A1UEAwwKZWxpeHhpci5pbzEfMB0GCSqGSIb3DQEJARYQYWRtaW5AZWxpeHhpci5p -bzAeFw0yMTExMzAxODMwMTdaFw0zMTExMjgxODMwMTdaMIGMMQswCQYDVQQGEwJV -UzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCUNsYXJlbW9udDEQMA4GA1UECgwHRWxp -eHhpcjEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxEzARBgNVBAMMCmVsaXh4aXIuaW8x -HzAdBgkqhkiG9w0BCQEWEGFkbWluQGVsaXh4aXIuaW8wggIiMA0GCSqGSIb3DQEB -AQUAA4ICDwAwggIKAoICAQCckGabzUitkySleveyD9Yrxrpj50FiGkOvwkmgN1jF -9r5StN3otiU5tebderkjD82mVqB781czRA9vPqAggbw1ZdAyQPTvDPTj7rmzkByq -QIkdZBMshV/zX1z8oXoNB9bzZlUFVF4HTY3dEytAJONJRkGGAw4FTa/wCkWsITiT -mKvkP3ciKgz7s8uMyZzZpj9ElBphK9Nbwt83v/IOgTqDmn5qDBnHtoLw4roKJkC8 -00GF4ZUhlVSQC3oFWOCu6tvSUVCBCTUzVKYJLmCnoilmiE/8nCOU0VOivtsx88f5 -9RSPfePUk8u5CRmgThwOpxb0CAO0gd+sY1YJrn+FaW+dSR8OkM3bFuTq7fz9CEkS -XFfUwbJL+HzT0ZuSA3FupTIExyDmM/5dF8lC0RB3j4FNQF+H+j5Kso86e83xnXPI -e+IKKIYa/LVdW24kYRuBDpoONN5KS/F+F/5PzOzH9Swdt07J9b7z1dzWcLnKGtkN -WVsZ7Ue6cuI2zOEWqF1OEr9FladgORcdVBoF/WlsA63C2c1J0tjXqqcl/27GmqGW -gvhaA8Jkm20qLCEhxQ2JzrBdk/X/lCZdP/7A5TxnLqSBq8xxMuLJlZZbUG8U/BT9 -sHF5mXZyiucMjTEU7qHMR2UGNFot8TQ7ZXntIApa2NlB/qX2qI5D13PoXI9Hnyxa -8wIDAQABozgwNjAVBgNVHREEDjAMggplbGl4eGlyLmlvMB0GA1UdDgQWBBQimFud -gCzDVFD3Xz68zOAebDN6YDANBgkqhkiG9w0BAQsFAAOCAgEAccsH9JIyFZdytGxC -/6qjSHPgV23ZGmW7alg+GyEATBIAN187Du4Lj6cLbox5nqLdZgYzizVop32JQAHv -N1QPKjViOOkLaJprSUuRULa5kJ5fe+XfMoyhISI4mtJXXbMwl/PbOaDSdeDjl0ZO -auQggWslyv8ZOkfcbC6goEtAxljNZ01zY1ofSKUj+fBw9Lmomql6GAt7NuubANs4 -9mSjXwD27EZf3Aqaaju7gX1APW2O03/q4hDqhrGW14sN0gFt751ddPuPr5COGzCS -c3Xg2HqMpXx//FU4qHrZYzwv8SuGSshlCxGJpWku9LVwci1Kxi4LyZgTm6/xY4kB -5fsZf6C2yAZnkIJ8bEYr0Up4KzG1lNskU69uMv+d7W2+4Ie3Evf3HdYad/WeUskG -tc6LKY6B2NX3RMVkQt0ftsDaWsktnR8VBXVZSBVYVEQu318rKvYRdOwZJn339obI -jyMZC/3D721e5Anj/EqHpc3I9Yn3jRKw1xc8kpNLg/JIAibub8JYyDvT1gO4xjBO -+6EWOBFgDAsf7bSP2xQn1pQFWcA/sY1MnRsWeENmKNrkLXffP+8l1tEcijN+KCSF -ek1mr+qBwSaNV9TA+RXVhvqd3DEKPPJ1WhfxP1K81RdUESvHOV/4kdwnSahDyao0 -EnretBzQkeKeBwoB2u6NTiOmUjk= ------END CERTIFICATE----- -` - -func getNDF() *ndf.NetworkDefinition { - return &ndf.NetworkDefinition{ - UDB: ndf.UDB{ - ID: id.DummyUser.Bytes(), - Cert: testCert, - Address: "address", - DhPubKey: []byte{123, 34, 86, 97, 108, 117, 101, 34, 58, 53, 48, 49, 53, 53, 53, 52, 54, 53, 49, 48, 54, 49, 56, 57, 53, 54, 51, 48, 54, 52, 49, 51, 53, 49, 57, 56, 55, 57, 52, 57, 50, 48, 56, 49, 52, 57, 52, 50, 57, 51, 57, 53, 49, 50, 51, 54, 52, 56, 49, 57, 55, 48, 50, 50, 49, 48, 55, 55, 50, 52, 52, 48, 49, 54, 57, 52, 55, 52, 57, 53, 53, 56, 55, 54, 50, 57, 53, 57, 53, 48, 54, 55, 57, 55, 48, 53, 48, 48, 54, 54, 56, 49, 57, 50, 56, 48, 52, 48, 53, 51, 50, 48, 57, 55, 54, 56, 56, 53, 57, 54, 57, 56, 57, 49, 48, 54, 56, 54, 50, 52, 50, 52, 50, 56, 49, 48, 51, 51, 51, 54, 55, 53, 55, 54, 52, 51, 54, 55, 54, 56, 53, 56, 48, 55, 56, 49, 52, 55, 49, 52, 53, 49, 52, 52, 52, 52, 53, 51, 57, 57, 51, 57, 57, 53, 50, 52, 52, 53, 51, 56, 48, 49, 48, 54, 54, 55, 48, 52, 50, 49, 55, 54, 57, 53, 57, 57, 57, 51, 52, 48, 54, 54, 54, 49, 50, 48, 54, 56, 57, 51, 54, 57, 48, 52, 55, 55, 54, 50, 49, 49, 56, 56, 53, 51, 50, 57, 57, 50, 54, 53, 48, 52, 57, 51, 54, 55, 54, 48, 57, 56, 56, 49, 55, 52, 52, 57, 53, 57, 54, 53, 50, 55, 53, 52, 52, 52, 49, 57, 55, 49, 54, 50, 52, 52, 56, 50, 55, 55, 50, 49, 48, 53, 56, 56, 57, 54, 51, 53, 54, 54, 53, 53, 53, 53, 49, 56, 50, 53, 49, 49, 50, 57, 50, 48, 49, 56, 48, 48, 54, 49, 56, 57, 48, 55, 48, 51, 53, 51, 51, 56, 57, 52, 49, 50, 57, 49, 55, 50, 56, 55, 57, 57, 52, 55, 53, 51, 49, 55, 55, 48, 53, 55, 55, 49, 50, 51, 57, 49, 51, 55, 54, 48, 50, 49, 55, 50, 54, 54, 52, 56, 52, 48, 48, 54, 48, 52, 48, 53, 56, 56, 53, 54, 52, 56, 56, 49, 52, 52, 51, 57, 56, 51, 51, 57, 54, 55, 48, 49, 53, 55, 52, 53, 50, 56, 51, 49, 51, 48, 53, 52, 49, 49, 49, 49, 49, 56, 51, 53, 52, 52, 52, 52, 48, 53, 54, 57, 48, 54, 52, 56, 57, 52, 54, 53, 50, 56, 51, 53, 50, 48, 48, 50, 48, 48, 49, 50, 51, 51, 48, 48, 53, 48, 49, 50, 52, 56, 57, 48, 49, 51, 54, 55, 52, 57, 55, 50, 49, 48, 55, 53, 54, 49, 50, 52, 52, 57, 55, 48, 50, 56, 55, 55, 51, 51, 50, 53, 50, 48, 57, 52, 56, 57, 49, 49, 56, 49, 54, 57, 50, 55, 50, 51, 57, 51, 57, 54, 50, 56, 48, 54, 54, 49, 57, 55, 48, 50, 48, 57, 49, 51, 54, 50, 49, 50, 53, 50, 54, 50, 53, 53, 55, 57, 54, 51, 56, 49, 57, 48, 51, 49, 54, 54, 53, 51, 56, 56, 49, 48, 56, 48, 51, 57, 53, 49, 53, 53, 55, 49, 53, 57, 48, 57, 57, 55, 49, 56, 53, 55, 54, 48, 50, 54, 48, 49, 55, 57, 52, 55, 53, 51, 57, 49, 51, 53, 52, 49, 48, 50, 49, 55, 52, 51, 57, 48, 50, 56, 48, 50, 51, 53, 51, 54, 56, 49, 56, 50, 49, 55, 50, 57, 52, 51, 49, 56, 48, 56, 56, 50, 51, 53, 52, 56, 55, 49, 52, 55, 53, 50, 56, 48, 57, 55, 49, 53, 48, 48, 51, 50, 48, 57, 50, 50, 53, 50, 56, 51, 57, 55, 57, 49, 57, 50, 53, 56, 51, 55, 48, 51, 57, 54, 48, 50, 55, 54, 48, 54, 57, 55, 52, 53, 54, 52, 51, 56, 52, 53, 54, 48, 51, 57, 55, 55, 55, 49, 53, 57, 57, 49, 57, 52, 57, 56, 56, 54, 56, 50, 49, 49, 54, 56, 55, 56, 55, 51, 51, 57, 52, 53, 49, 52, 52, 55, 57, 53, 57, 49, 57, 52, 48, 51, 53, 49, 49, 49, 51, 48, 53, 54, 54, 50, 49, 56, 57, 52, 55, 50, 49, 54, 53, 57, 53, 50, 57, 50, 48, 51, 51, 52, 48, 56, 55, 54, 50, 49, 49, 49, 56, 53, 54, 57, 51, 57, 50, 53, 48, 53, 56, 56, 55, 56, 53, 54, 55, 51, 56, 55, 50, 53, 57, 56, 52, 54, 53, 49, 51, 50, 54, 51, 50, 48, 56, 56, 57, 52, 53, 57, 53, 56, 57, 57, 54, 52, 55, 55, 50, 57, 51, 51, 52, 55, 51, 48, 52, 56, 56, 50, 51, 50, 52, 53, 48, 51, 50, 56, 56, 50, 49, 55, 51, 51, 53, 54, 55, 51, 50, 51, 52, 56, 53, 52, 55, 48, 51, 56, 50, 51, 49, 53, 55, 52, 53, 53, 48, 55, 55, 56, 55, 48, 50, 51, 52, 50, 53, 52, 51, 48, 57, 56, 56, 54, 56, 54, 49, 57, 54, 48, 55, 55, 52, 57, 55, 56, 51, 48, 51, 57, 49, 55, 52, 49, 51, 49, 54, 57, 54, 50, 49, 52, 50, 55, 57, 55, 56, 56, 51, 49, 55, 51, 50, 54, 56, 49, 56, 53, 57, 48, 49, 49, 53, 48, 52, 53, 51, 51, 56, 52, 57, 57, 55, 54, 51, 55, 55, 48, 55, 49, 52, 50, 49, 54, 48, 49, 54, 52, 49, 57, 53, 56, 49, 54, 50, 55, 49, 52, 49, 52, 56, 49, 51, 52, 50, 53, 56, 55, 53, 57, 55, 52, 49, 57, 49, 55, 51, 55, 49, 51, 57, 54, 51, 49, 51, 49, 56, 53, 50, 49, 53, 52, 49, 51, 44, 34, 70, 105, 110, 103, 101, 114, 112, 114, 105, 110, 116, 34, 58, 49, 54, 56, 48, 49, 53, 52, 49, 53, 49, 49, 50, 51, 51, 48, 57, 56, 51, 54, 51, 125}, - }, - E2E: ndf.Group{ - Prime: "E2EE983D031DC1DB6F1A7A67DF0E9A8E5561DB8E8D49413394C049B7A" + - "8ACCEDC298708F121951D9CF920EC5D146727AA4AE535B0922C688B55B3D" + - "D2AEDF6C01C94764DAB937935AA83BE36E67760713AB44A6337C20E78615" + - "75E745D31F8B9E9AD8412118C62A3E2E29DF46B0864D0C951C394A5CBBDC" + - "6ADC718DD2A3E041023DBB5AB23EBB4742DE9C1687B5B34FA48C3521632C" + - "4A530E8FFB1BC51DADDF453B0B2717C2BC6669ED76B4BDD5C9FF558E88F2" + - "6E5785302BEDBCA23EAC5ACE92096EE8A60642FB61E8F3D24990B8CB12EE" + - "448EEF78E184C7242DD161C7738F32BF29A841698978825B4111B4BC3E1E" + - "198455095958333D776D8B2BEEED3A1A1A221A6E37E664A64B83981C46FF" + - "DDC1A45E3D5211AAF8BFBC072768C4F50D7D7803D2D4F278DE8014A47323" + - "631D7E064DE81C0C6BFA43EF0E6998860F1390B5D3FEACAF1696015CB79C" + - "3F9C2D93D961120CD0E5F12CBB687EAB045241F96789C38E89D796138E63" + - "19BE62E35D87B1048CA28BE389B575E994DCA755471584A09EC723742DC3" + - "5873847AEF49F66E43873", - Generator: "2", - }, - CMIX: ndf.Group{ - Prime: "9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642" + - "F0B5C48C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757" + - "264E5A1A44FFE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F" + - "9716BFE6117C6B5B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091E" + - "B51743BF33050C38DE235567E1B34C3D6A5C0CEAA1A0F368213C3D19843D" + - "0B4B09DCB9FC72D39C8DE41F1BF14D4BB4563CA28371621CAD3324B6A2D3" + - "92145BEBFAC748805236F5CA2FE92B871CD8F9C36D3292B5509CA8CAA77A" + - "2ADFC7BFD77DDA6F71125A7456FEA153E433256A2261C6A06ED3693797E7" + - "995FAD5AABBCFBE3EDA2741E375404AE25B", - Generator: "5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E2480" + - "9670716C613D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D" + - "1AA58C4328A06C46A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A33" + - "8661D10461C0D135472085057F3494309FFA73C611F78B32ADBB5740C361" + - "C9F35BE90997DB2014E2EF5AA61782F52ABEB8BD6432C4DD097BC5423B28" + - "5DAFB60DC364E8161F4A2A35ACA3A10B1C4D203CC76A470A33AFDCBDD929" + - "59859ABD8B56E1725252D78EAC66E71BA9AE3F1DD2487199874393CD4D83" + - "2186800654760E1E34C09E4D155179F9EC0DC4473F996BDCE6EED1CABED8" + - "B6F116F7AD9CF505DF0F998E34AB27514B0FFE7", - }, - } -} - func getGroup() *cyclic.Group { return cyclic.NewGroup( large.NewIntFromString("E2EE983D031DC1DB6F1A7A67DF0E9A8E5561DB8E8D4941"+ diff --git a/e2e/interface.go b/e2e/interface.go index d9710650594926f528985ddf5f7aa1c41365d74c..c4c6b68af6ccacfc85876ce964120a5868e3e4a2 100644 --- a/e2e/interface.go +++ b/e2e/interface.go @@ -103,6 +103,10 @@ type Handler interface { // will no longer get called Unregister(listenerID receive.ListenerID) + // UnregisterUserListeners removes all the listeners registered with the + // specified user. + UnregisterUserListeners(userID *id.ID) + /* === Partners ===================================================== */ // AddPartner adds a partner. Automatically creates both send diff --git a/e2e/receive/byID.go b/e2e/receive/byID.go index 625aa05db6958642b25961e18daf5196d3dd8659..425cd036e6b7c0bccc307621a709b08098b5ba2e 100644 --- a/e2e/receive/byID.go +++ b/e2e/receive/byID.go @@ -44,13 +44,13 @@ func (bi *byId) Get(uid *id.ID) *set.Set { // adds a listener to a set for the given ID. Creates a new set to add it to if // the set does not exist -func (bi *byId) Add(uid *id.ID, l Listener) *set.Set { - s, ok := bi.list[*uid] +func (bi *byId) Add(lid ListenerID) *set.Set { + s, ok := bi.list[*lid.userID] if !ok { - s = set.New(l) - bi.list[*uid] = s + s = set.New(lid) + bi.list[*lid.userID] = s } else { - s.Insert(l) + s.Insert(lid) } return s @@ -58,13 +58,20 @@ func (bi *byId) Add(uid *id.ID, l Listener) *set.Set { // Removes the passed listener from the set for UserID and // deletes the set if it is empty if the ID is not a generic one -func (bi *byId) Remove(uid *id.ID, l Listener) { - s, ok := bi.list[*uid] +func (bi *byId) Remove(lid ListenerID) { + s, ok := bi.list[*lid.userID] if ok { - s.Remove(l) + s.Remove(lid) - if s.Len() == 0 && !uid.Cmp(AnyUser()) && !uid.Cmp(&id.ID{}) { - delete(bi.list, *uid) + if s.Len() == 0 && !lid.userID.Cmp(AnyUser()) && !lid.userID.Cmp(&id.ID{}) { + delete(bi.list, *lid.userID) } } } + +// RemoveId removes all listeners registered for the given user ID. +func (bi *byId) RemoveId(uid *id.ID) { + if !uid.Cmp(AnyUser()) && !uid.Cmp(&id.ID{}) { + delete(bi.list, *uid) + } +} diff --git a/e2e/receive/byID_test.go b/e2e/receive/byID_test.go index 750c1bc6c958d76b56315f0ad10f7bdd954e63bd..e8aebd2777242ff1e910876d0f2d964f493c3bc7 100644 --- a/e2e/receive/byID_test.go +++ b/e2e/receive/byID_test.go @@ -120,9 +120,9 @@ func TestById_Add_New(t *testing.T) { uid := id.NewIdFromUInt(42, id.User, t) - l := &funcListener{} + lid := ListenerID{uid, 1, &funcListener{}} - nbi.Add(uid, l) + nbi.Add(lid) s := nbi.list[*uid] @@ -130,7 +130,7 @@ func TestById_Add_New(t *testing.T) { t.Errorf("Should a set of the wrong size") } - if !s.Has(l) { + if !s.Has(lid) { t.Errorf("Wrong set returned") } } @@ -142,26 +142,26 @@ func TestById_Add_Old(t *testing.T) { uid := id.NewIdFromUInt(42, id.User, t) - l1 := &funcListener{} - l2 := &funcListener{} + lid1 := ListenerID{uid, 1, &funcListener{}} + lid2 := ListenerID{uid, 1, &funcListener{}} - set1 := set.New(l1) + set1 := set.New(lid1) nbi.list[*uid] = set1 - nbi.Add(uid, l2) + nbi.Add(lid2) s := nbi.list[*uid] if s.Len() != 2 { - t.Errorf("Should have returned a set") + t.Errorf("Incorrect set length.\nexpected: %d\nreceived: %d", 2, s.Len()) } - if !s.Has(l1) { + if !s.Has(lid1) { t.Errorf("Set does not include the initial listener") } - if !s.Has(l2) { + if !s.Has(lid2) { t.Errorf("Set does not include the new listener") } } @@ -171,11 +171,11 @@ func TestById_Add_Old(t *testing.T) { func TestById_Add_Generic(t *testing.T) { nbi := newById() - l1 := &funcListener{} - l2 := &funcListener{} + lid1 := ListenerID{&id.ID{}, 1, &funcListener{}} + lid2 := ListenerID{AnyUser(), 1, &funcListener{}} - nbi.Add(&id.ID{}, l1) - nbi.Add(AnyUser(), l2) + nbi.Add(lid1) + nbi.Add(lid2) s := nbi.generic @@ -183,11 +183,11 @@ func TestById_Add_Generic(t *testing.T) { t.Errorf("Should have returned a set of size 2") } - if !s.Has(l1) { + if !s.Has(lid1) { t.Errorf("Set does not include the ZeroUser listener") } - if !s.Has(l2) { + if !s.Has(lid2) { t.Errorf("Set does not include the empty user listener") } } @@ -199,14 +199,14 @@ func TestById_Remove_ManyInSet(t *testing.T) { uid := id.NewIdFromUInt(42, id.User, t) - l1 := &funcListener{} - l2 := &funcListener{} + lid1 := ListenerID{uid, 1, &funcListener{}} + lid2 := ListenerID{uid, 1, &funcListener{}} - set1 := set.New(l1, l2) + set1 := set.New(lid1, lid2) nbi.list[*uid] = set1 - nbi.Remove(uid, l1) + nbi.Remove(lid1) if _, ok := nbi.list[*uid]; !ok { t.Errorf("Set removed when it should not have been") @@ -217,11 +217,11 @@ func TestById_Remove_ManyInSet(t *testing.T) { set1.Len()) } - if set1.Has(l1) { + if set1.Has(lid1) { t.Errorf("Listener 1 still in set, it should not be") } - if !set1.Has(l2) { + if !set1.Has(lid2) { t.Errorf("Listener 2 not still in set, it should be") } @@ -234,13 +234,13 @@ func TestById_Remove_SingleInSet(t *testing.T) { uid := id.NewIdFromUInt(42, id.User, t) - l1 := &funcListener{} + lid1 := ListenerID{uid, 1, &funcListener{}} - set1 := set.New(l1) + set1 := set.New(lid1) nbi.list[*uid] = set1 - nbi.Remove(uid, l1) + nbi.Remove(lid1) if _, ok := nbi.list[*uid]; ok { t.Errorf("Set not removed when it should have been") @@ -251,7 +251,7 @@ func TestById_Remove_SingleInSet(t *testing.T) { set1.Len()) } - if set1.Has(l1) { + if set1.Has(lid1) { t.Errorf("Listener 1 still in set, it should not be") } } @@ -263,13 +263,13 @@ func TestById_Remove_SingleInSet_ZeroUser(t *testing.T) { uid := &id.ZeroUser - l1 := &funcListener{} + lid1 := ListenerID{uid, 1, &funcListener{}} - set1 := set.New(l1) + set1 := set.New(lid1) nbi.list[*uid] = set1 - nbi.Remove(uid, l1) + nbi.Remove(lid1) if _, ok := nbi.list[*uid]; !ok { t.Errorf("Set removed when it should not have been") @@ -280,7 +280,7 @@ func TestById_Remove_SingleInSet_ZeroUser(t *testing.T) { set1.Len()) } - if set1.Has(l1) { + if set1.Has(lid1) { t.Errorf("Listener 1 still in set, it should not be") } } @@ -292,13 +292,13 @@ func TestById_Remove_SingleInSet_EmptyUser(t *testing.T) { uid := &id.ID{} - l1 := &funcListener{} + lid1 := ListenerID{uid, 1, &funcListener{}} - set1 := set.New(l1) + set1 := set.New(lid1) nbi.list[*uid] = set1 - nbi.Remove(uid, l1) + nbi.Remove(lid1) if _, ok := nbi.list[*uid]; !ok { t.Errorf("Set removed when it should not have been") @@ -309,7 +309,7 @@ func TestById_Remove_SingleInSet_EmptyUser(t *testing.T) { set1.Len()) } - if set1.Has(l1) { + if set1.Has(lid1) { t.Errorf("Listener 1 still in set, it should not be") } } diff --git a/e2e/receive/byType.go b/e2e/receive/byType.go index 0ec3f6b4502a2f8ece67599ea95d0e65dabe8eb0..183e2da360b5c03fceba294bcc578b6c73f6fb86 100644 --- a/e2e/receive/byType.go +++ b/e2e/receive/byType.go @@ -45,13 +45,13 @@ func (bt *byType) Get(messageType catalog.MessageType) *set.Set { // adds a listener to a set for the given messageType. Creates a new set to add // it to if the set does not exist -func (bt *byType) Add(messageType catalog.MessageType, r Listener) *set.Set { - s, ok := bt.list[messageType] +func (bt *byType) Add(lid ListenerID) *set.Set { + s, ok := bt.list[lid.messageType] if !ok { - s = set.New(r) - bt.list[messageType] = s + s = set.New(lid) + bt.list[lid.messageType] = s } else { - s.Insert(r) + s.Insert(lid) } return s @@ -59,13 +59,13 @@ func (bt *byType) Add(messageType catalog.MessageType, r Listener) *set.Set { // Removes the passed listener from the set for messageType and // deletes the set if it is empty and the type is not AnyType -func (bt *byType) Remove(mt catalog.MessageType, l Listener) { - s, ok := bt.list[mt] +func (bt *byType) Remove(lid ListenerID) { + s, ok := bt.list[lid.messageType] if ok { - s.Remove(l) + s.Remove(lid) - if s.Len() == 0 && mt != AnyType { - delete(bt.list, mt) + if s.Len() == 0 && lid.messageType != AnyType { + delete(bt.list, lid.messageType) } } } diff --git a/e2e/receive/byType_test.go b/e2e/receive/byType_test.go index 30f3c45bd005d922f44190e20a3b3f48bd31188f..3ca6127d222f2b7b9038c7fbe7d2ac389c833969 100644 --- a/e2e/receive/byType_test.go +++ b/e2e/receive/byType_test.go @@ -10,6 +10,7 @@ package receive import ( "github.com/golang-collections/collections/set" "gitlab.com/elixxir/client/catalog" + "gitlab.com/xx_network/primitives/id" "testing" ) @@ -108,9 +109,9 @@ func TestByType_Add_New(t *testing.T) { m := catalog.MessageType(42) - l := &funcListener{} + l := ListenerID{&id.ZeroUser, m, &funcListener{}} - nbt.Add(m, l) + nbt.Add(l) s := nbt.list[m] @@ -130,14 +131,14 @@ func TestByType_Add_Old(t *testing.T) { m := catalog.MessageType(42) - l1 := &funcListener{} - l2 := &funcListener{} + lid1 := ListenerID{&id.ZeroUser, m, &funcListener{}} + lid2 := ListenerID{&id.ZeroUser, m, &funcListener{}} - set1 := set.New(l1) + set1 := set.New(lid1) nbt.list[m] = set1 - nbt.Add(m, l2) + nbt.Add(lid2) s := nbt.list[m] @@ -145,11 +146,11 @@ func TestByType_Add_Old(t *testing.T) { t.Errorf("Should have returned a set") } - if !s.Has(l1) { + if !s.Has(lid1) { t.Errorf("Set does not include the initial listener") } - if !s.Has(l2) { + if !s.Has(lid2) { t.Errorf("Set does not include the new listener") } } @@ -159,9 +160,9 @@ func TestByType_Add_Old(t *testing.T) { func TestByType_Add_Generic(t *testing.T) { nbt := newByType() - l1 := &funcListener{} + lid1 := ListenerID{&id.ZeroUser, AnyType, &funcListener{}} - nbt.Add(AnyType, l1) + nbt.Add(lid1) s := nbt.generic @@ -169,7 +170,7 @@ func TestByType_Add_Generic(t *testing.T) { t.Errorf("Should have returned a set of size 2") } - if !s.Has(l1) { + if !s.Has(lid1) { t.Errorf("Set does not include the ZeroUser listener") } } @@ -181,13 +182,13 @@ func TestByType_Remove_SingleInSet(t *testing.T) { m := catalog.MessageType(42) - l1 := &funcListener{} + lid1 := ListenerID{&id.ZeroUser, m, &funcListener{}} - set1 := set.New(l1) + set1 := set.New(lid1) nbt.list[m] = set1 - nbt.Remove(m, l1) + nbt.Remove(lid1) if _, ok := nbt.list[m]; ok { t.Errorf("Set not removed when it should have been") @@ -198,7 +199,7 @@ func TestByType_Remove_SingleInSet(t *testing.T) { set1.Len()) } - if set1.Has(l1) { + if set1.Has(lid1) { t.Errorf("Listener 1 still in set, it should not be") } } @@ -210,13 +211,13 @@ func TestByType_Remove_SingleInSet_AnyType(t *testing.T) { m := AnyType - l1 := &funcListener{} + lid1 := ListenerID{&id.ZeroUser, m, &funcListener{}} - set1 := set.New(l1) + set1 := set.New(lid1) nbt.list[m] = set1 - nbt.Remove(m, l1) + nbt.Remove(lid1) if _, ok := nbt.list[m]; !ok { t.Errorf("Set removed when it should not have been") @@ -227,7 +228,7 @@ func TestByType_Remove_SingleInSet_AnyType(t *testing.T) { set1.Len()) } - if set1.Has(l1) { + if set1.Has(lid1) { t.Errorf("Listener 1 still in set, it should not be") } } diff --git a/e2e/receive/listener.go b/e2e/receive/listener.go index 2bef0525c185a291ee861d9447dd9e18824a6109..5e1d34067c5b7709509c18de6000103d73107687 100644 --- a/e2e/receive/listener.go +++ b/e2e/receive/listener.go @@ -11,6 +11,8 @@ import ( jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/catalog" "gitlab.com/xx_network/primitives/id" + "strconv" + "strings" ) //Listener interface for a listener adhere to @@ -50,6 +52,19 @@ func (lid ListenerID) GetName() string { return lid.listener.Name() } +// String returns the values in the ListenerID in a human-readable format. This +// functions adheres to the fmt.Stringer interface. +func (lid ListenerID) String() string { + str := []string{ + "userID:" + lid.userID.String(), + "messageType:" + lid.messageType.String() + + "(" + strconv.FormatUint(uint64(lid.messageType), 10) + ")", + "listener:" + lid.listener.Name(), + } + + return "{" + strings.Join(str, " ") + "}" +} + /*internal listener implementations*/ //listener based off of a function diff --git a/e2e/receive/switchboard.go b/e2e/receive/switchboard.go index ad214f53ae524c36fa36ab62a598d76e6e732fa0..3bb5e23cfae466cad43a4a2ca665a079caa48413 100644 --- a/e2e/receive/switchboard.go +++ b/e2e/receive/switchboard.go @@ -55,20 +55,23 @@ func (sw *Switchboard) RegisterListener(user *id.ID, jww.FATAL.Panicf("cannot register nil listener") } + // Create new listener ID + lid := ListenerID{ + userID: user, + messageType: messageType, + listener: newListener, + } + //register the listener by both ID and messageType sw.mux.Lock() - sw.id.Add(user, newListener) - sw.messageType.Add(messageType, newListener) + sw.id.Add(lid) + sw.messageType.Add(lid) sw.mux.Unlock() //return a ListenerID so it can be unregistered in the future - return ListenerID{ - userID: user, - messageType: messageType, - listener: newListener, - } + return lid } // RegisterFunc Registers a new listener built around the passed function. @@ -141,8 +144,8 @@ func (sw *Switchboard) Speak(item Message) { //Execute hear on all matched listeners in a new goroutine matches.Do(func(i interface{}) { - r := i.(Listener) - go r.Hear(item) + lid := i.(ListenerID) + go lid.listener.Hear(item) }) // print to log if nothing was heard @@ -158,12 +161,32 @@ func (sw *Switchboard) Speak(item Message) { func (sw *Switchboard) Unregister(listenerID ListenerID) { sw.mux.Lock() - sw.id.Remove(listenerID.userID, listenerID.listener) - sw.messageType.Remove(listenerID.messageType, listenerID.listener) + sw.id.Remove(listenerID) + sw.messageType.Remove(listenerID) sw.mux.Unlock() } +// UnregisterUserListeners removes all the listeners registered with the +// specified user. +func (sw *Switchboard) UnregisterUserListeners(userID *id.ID) { + sw.mux.Lock() + defer sw.mux.Unlock() + + // Get list of all listeners for the specified user + idSet := sw.id.Get(userID) + + // Find each listener in the messageType list and delete it + idSet.Do(func(i interface{}) { + lid := i.(ListenerID) + mtSet := sw.messageType.list[lid.messageType] + mtSet.Remove(lid) + }) + + // Remove all listeners for the user from the ID list + sw.id.RemoveId(userID) +} + // finds all listeners who match the items sender or ID, or have those fields // as generic func (sw *Switchboard) matchListeners(item Message) *set.Set { diff --git a/e2e/receive/switchboard_test.go b/e2e/receive/switchboard_test.go index aaee91e2d9d5090175334b503a0ca3a54f929cfe..7d2f8c4ceff37b2be6a6abe09675d0e50a6822dc 100644 --- a/e2e/receive/switchboard_test.go +++ b/e2e/receive/switchboard_test.go @@ -85,13 +85,13 @@ func TestSwitchboard_RegisterListener(t *testing.T) { //check that the listener is registered in the appropriate location setID := sw.id.Get(uid) - if !setID.Has(l) { + if !setID.Has(lid) { t.Errorf("Listener is not registered by ID") } setType := sw.messageType.Get(mt) - if !setType.Has(l) { + if !setType.Has(lid) { t.Errorf("Listener is not registered by Message Tag") } @@ -152,13 +152,13 @@ func TestSwitchboard_RegisterFunc(t *testing.T) { //check that the listener is registered in the appropriate location setID := sw.id.Get(uid) - if !setID.Has(lid.listener) { + if !setID.Has(lid) { t.Errorf("Listener is not registered by ID") } setType := sw.messageType.Get(mt) - if !setType.Has(lid.listener) { + if !setType.Has(lid) { t.Errorf("Listener is not registered by Message Tag") } @@ -221,13 +221,13 @@ func TestSwitchboard_RegisterChan(t *testing.T) { //check that the listener is registered in the appropriate location setID := sw.id.Get(uid) - if !setID.Has(lid.listener) { + if !setID.Has(lid) { t.Errorf("Listener is not registered by ID") } setType := sw.messageType.Get(mt) - if !setType.Has(lid.listener) { + if !setType.Has(lid) { t.Errorf("Listener is not registered by Message Tag") } @@ -330,21 +330,67 @@ func TestSwitchboard_Unregister(t *testing.T) { setType := sw.messageType.Get(mt) //check that the removed listener is not registered - if setID.Has(lid1.listener) { + if setID.Has(lid1) { t.Errorf("Removed Listener is registered by ID, should not be") } - if setType.Has(lid1.listener) { + if setType.Has(lid1) { t.Errorf("Removed Listener not registered by Message Tag, " + "should not be") } //check that the not removed listener is still registered - if !setID.Has(lid2.listener) { + if !setID.Has(lid2) { t.Errorf("Remaining Listener is not registered by ID") } - if !setType.Has(lid2.listener) { + if !setType.Has(lid2) { t.Errorf("Remaining Listener is not registered by Message Tag") } } + +// Tests that three listeners with three different message types but the same +// user are deleted with Switchboard.UnregisterUserListeners and that three +// other listeners with the same message types but different users are not +// delete. +func TestSwitchboard_UnregisterUserListeners(t *testing.T) { + sw := New() + + uid1 := id.NewIdFromUInt(42, id.User, t) + uid2 := id.NewIdFromUInt(11, id.User, t) + + l := func(receive Message) {} + + lid1 := sw.RegisterFunc("a", uid1, catalog.NoType, l) + lid2 := sw.RegisterFunc("b", uid1, catalog.XxMessage, l) + lid3 := sw.RegisterFunc("c", uid1, catalog.KeyExchangeTrigger, l) + lid4 := sw.RegisterFunc("d", uid2, catalog.NoType, l) + lid5 := sw.RegisterFunc("e", uid2, catalog.XxMessage, l) + lid6 := sw.RegisterFunc("f", uid2, catalog.KeyExchangeTrigger, l) + + sw.UnregisterUserListeners(uid1) + + if s, exists := sw.id.list[*uid1]; exists { + t.Errorf("Set for ID %s still exists: %+v", uid1, s) + } + + if sw.messageType.Get(lid1.messageType).Has(lid1) { + t.Errorf("Listener %+v still exists", lid1) + } + if sw.messageType.Get(lid2.messageType).Has(lid2) { + t.Errorf("Listener %+v still exists", lid2) + } + if sw.messageType.Get(lid3.messageType).Has(lid3) { + t.Errorf("Listener %+v still exists", lid3) + } + + if !sw.messageType.Get(lid4.messageType).Has(lid4) { + t.Errorf("Listener %+v does not exist", lid4) + } + if !sw.messageType.Get(lid5.messageType).Has(lid5) { + t.Errorf("Listener %+v does not exist", lid5) + } + if !sw.messageType.Get(lid6.messageType).Has(lid6) { + t.Errorf("Listener %+v does not exist", lid6) + } +} diff --git a/fileTransfer/connect/utils_test.go b/fileTransfer/connect/utils_test.go index bd155a4825642ec6cf629241b10e3a4e1e561523..e5c5c6d902c9d22578d847b95d4dc866e315e935 100644 --- a/fileTransfer/connect/utils_test.go +++ b/fileTransfer/connect/utils_test.go @@ -32,7 +32,7 @@ import ( ) //////////////////////////////////////////////////////////////////////////////// -// Mock cMix // +// Mock cMix // //////////////////////////////////////////////////////////////////////////////// type mockCmixHandler struct { @@ -137,6 +137,7 @@ func (m *mockCmix) GetRoundResults(_ time.Duration, //////////////////////////////////////////////////////////////////////////////// // Mock Connection Handler // //////////////////////////////////////////////////////////////////////////////// + func newMockListener(hearChan chan receive.Message) *mockListener { return &mockListener{hearChan: hearChan} } @@ -162,6 +163,9 @@ func newMockConnectionHandler() *mockConnectionHandler { } } +// Tests that mockConnection adheres to the Connection interface. +var _ Connection = (*mockConnection)(nil) + type mockConnection struct { myID *id.ID handler *mockConnectionHandler @@ -193,12 +197,12 @@ func (m *mockConnection) SendE2E(mt catalog.MessageType, payload []byte, return []id.Round{42}, e2eCrypto.MessageID{}, netTime.Now(), nil } -func (m *mockConnection) RegisterListener( - mt catalog.MessageType, listener receive.Listener) receive.ListenerID { +func (m *mockConnection) RegisterListener(mt catalog.MessageType, + listener receive.Listener) (receive.ListenerID, error) { m.handler.Lock() defer m.handler.Unlock() m.handler.listeners[mt] = listener - return receive.ListenerID{} + return receive.ListenerID{}, nil } //////////////////////////////////////////////////////////////////////////////// diff --git a/fileTransfer/connect/wrapper.go b/fileTransfer/connect/wrapper.go index fa78ee94b75879898f0dcfcefc67b1038bd11991..0bfde770e1fbf236858485ce4d6b2ae6e267406e 100644 --- a/fileTransfer/connect/wrapper.go +++ b/fileTransfer/connect/wrapper.go @@ -41,7 +41,7 @@ type Connection interface { SendE2E(mt catalog.MessageType, payload []byte, params e2e.Params) ( []id.Round, e2eCrypto.MessageID, time.Time, error) RegisterListener(messageType catalog.MessageType, - newListener receive.Listener) receive.ListenerID + newListener receive.Listener) (receive.ListenerID, error) } // NewWrapper generates a new file transfer manager using Connection E2E. @@ -56,7 +56,10 @@ func NewWrapper(receiveCB ft.ReceiveCallback, p Params, ft ft.FileTransfer, } // Register listener to receive new file transfers - w.conn.RegisterListener(catalog.NewFileTransfer, &listener{w}) + _, err := w.conn.RegisterListener(catalog.NewFileTransfer, &listener{w}) + if err != nil { + return nil, err + } return w, nil } @@ -109,7 +112,7 @@ func (w *Wrapper) RegisterSentProgressCallback(tid *ftCrypto.TransferID, return w.ft.RegisterSentProgressCallback(tid, modifiedProgressCB, period) } -// addEndMessageToCallback adds the sending of an Connection E2E message when +// addEndMessageToCallback adds the sending of a Connection E2E message when // the transfer completed to the callback. If NotifyUponCompletion is not set, // then the message is not sent. func (w *Wrapper) addEndMessageToCallback(progressCB ft.SentProgressCallback) ft.SentProgressCallback { diff --git a/go.mod b/go.mod index 3e9394e23cb1ce9237a5562d3e59322c85d6c2e9..f1594495c9516fd0d6a4586c377e8810f976a8bb 100644 --- a/go.mod +++ b/go.mod @@ -16,9 +16,9 @@ require ( gitlab.com/elixxir/crypto v0.0.7-0.20220606201132-c370d5039cea gitlab.com/elixxir/ekv v0.1.7 gitlab.com/elixxir/primitives v0.0.3-0.20220606195757-40f7a589347f - gitlab.com/xx_network/comms v0.0.4-0.20220315161313-76acb14429ac + gitlab.com/xx_network/comms v0.0.4-0.20220630163702-f3d372ef6acd gitlab.com/xx_network/crypto v0.0.5-0.20220606200528-3f886fe49e81 - gitlab.com/xx_network/primitives v0.0.4-0.20220324193139-b292d1ae6e7e + gitlab.com/xx_network/primitives v0.0.4-0.20220630163313-7890038258c6 go.uber.org/ratelimit v0.2.0 golang.org/x/crypto v0.0.0-20220128200615-198e4374d7ed golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 diff --git a/go.sum b/go.sum index 88d420c80a70e484ea4ed24885df87c44823b307..8d1e3fc2d22efad52720f2a922b18426b5ced73f 100644 --- a/go.sum +++ b/go.sum @@ -292,8 +292,9 @@ gitlab.com/elixxir/primitives v0.0.3-0.20220323183834-b98f255361b8/go.mod h1:MtF gitlab.com/elixxir/primitives v0.0.3-0.20220606195757-40f7a589347f h1:CTf2+ewHWYrzp5Ar3RwNvHePfTHyFniJTVjFW4zqoaE= gitlab.com/elixxir/primitives v0.0.3-0.20220606195757-40f7a589347f/go.mod h1:9Bb2+u+CDSwsEU5Droo6saDAXuBDvLRjexpBhPAYxhA= gitlab.com/xx_network/comms v0.0.0-20200805174823-841427dd5023/go.mod h1:owEcxTRl7gsoM8c3RQ5KAm5GstxrJp5tn+6JfQ4z5Hw= -gitlab.com/xx_network/comms v0.0.4-0.20220315161313-76acb14429ac h1:+ykw0JqLH/qMprPEKazGHNH8gUoHGA78EIr4ienxnw4= gitlab.com/xx_network/comms v0.0.4-0.20220315161313-76acb14429ac/go.mod h1:isHnwem0v4rTcwwHP455FhVlFyPcHkHiVz+N3s/uCSI= +gitlab.com/xx_network/comms v0.0.4-0.20220630163702-f3d372ef6acd h1:qYR2V/8KliGyJ2clWpVYhpgwpnfS1MXGpAbrFoOkSWk= +gitlab.com/xx_network/comms v0.0.4-0.20220630163702-f3d372ef6acd/go.mod h1:TraR4sW+YxK/2CV+IQUNaAV61+ElrBV0kXd5HLEjM7M= gitlab.com/xx_network/crypto v0.0.3/go.mod h1:DF2HYvvCw9wkBybXcXAgQMzX+MiGbFPjwt3t17VRqRE= gitlab.com/xx_network/crypto v0.0.4/go.mod h1:+lcQEy+Th4eswFgQDwT0EXKp4AXrlubxalwQFH5O0Mk= gitlab.com/xx_network/crypto v0.0.5-0.20220222212031-750f7e8a01f4/go.mod h1:6apvsoHCQJDjO0J4E3uhR3yO9tTz/Mq5be5rjB3tQPU= @@ -305,8 +306,9 @@ gitlab.com/xx_network/primitives v0.0.0-20200804183002-f99f7a7284da/go.mod h1:OK gitlab.com/xx_network/primitives v0.0.2/go.mod h1:cs0QlFpdMDI6lAo61lDRH2JZz+3aVkHy+QogOB6F/qc= gitlab.com/xx_network/primitives v0.0.4-0.20220222211843-901fa4a2d72b/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE= gitlab.com/xx_network/primitives v0.0.4-0.20220317172007-4d2a53e6e669/go.mod h1:AXVVFt7dDAeIUpOGPiStCcUIKsBXLWbmV/BgZ4T+tOo= -gitlab.com/xx_network/primitives v0.0.4-0.20220324193139-b292d1ae6e7e h1:F651DdbU9n5qOLN8rdyAIluXWtm5Wl4jwH5D6ED3bSI= gitlab.com/xx_network/primitives v0.0.4-0.20220324193139-b292d1ae6e7e/go.mod h1:AXVVFt7dDAeIUpOGPiStCcUIKsBXLWbmV/BgZ4T+tOo= +gitlab.com/xx_network/primitives v0.0.4-0.20220630163313-7890038258c6 h1:3It6ILDHn/9J/Oi7MfMjkidKPe7vbFCy5JQtXx8EfYM= +gitlab.com/xx_network/primitives v0.0.4-0.20220630163313-7890038258c6/go.mod h1:AXVVFt7dDAeIUpOGPiStCcUIKsBXLWbmV/BgZ4T+tOo= gitlab.com/xx_network/ring v0.0.3-0.20220222211904-da613960ad93 h1:eJZrXqHsMmmejEPWw8gNAt0I8CGAMNO/7C339Zco3TM= gitlab.com/xx_network/ring v0.0.3-0.20220222211904-da613960ad93/go.mod h1:aLzpP2TiZTQut/PVHR40EJAomzugDdHXetbieRClXIM= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= diff --git a/groupChat/groupStore/store_test.go b/groupChat/groupStore/store_test.go index a41439b730f46285f39f9a960e6fe6bd94bbce77..e5549f06b1afadeceb44dd3a8bb3d2ac3c0ce7d1 100644 --- a/groupChat/groupStore/store_test.go +++ b/groupChat/groupStore/store_test.go @@ -558,7 +558,7 @@ func TestStore_GetUser(t *testing.T) { } if !user.Equal(store.GetUser()) { - t.Errorf("GetUser() failed to return the expected member."+ + t.Errorf("GetTransmissionIdentity() failed to return the expected member."+ "\nexpected: %#v\nreceived: %#v", user, store.GetUser()) } } diff --git a/groupChat/manager_test.go b/groupChat/manager_test.go index 374062ea83700854b222aaea55d70483304c8d30..de32491e49f2b7555783abc49d26d131c81a877e 100644 --- a/groupChat/manager_test.go +++ b/groupChat/manager_test.go @@ -170,26 +170,26 @@ func TestNewManager_LoadError(t *testing.T) { // m2, _ := newTestManagerWithStore(prng, 10, 0, requestFunc2, receiveFunc2, t) // m3, _ := newTestManagerWithStore(prng, 10, 0, requestFunc3, receiveFunc3, t) // -// membership, err := group.NewMembership(m1.store.GetUser().Contact(), -// m2.store.GetUser().Contact(), m3.store.GetUser().Contact()) +// membership, err := group.NewMembership(m1.store.GetTransmissionIdentity().Contact(), +// m2.store.GetTransmissionIdentity().Contact(), m3.store.GetTransmissionIdentity().Contact()) // if err != nil { // t.Errorf("Failed to generate new membership: %+v", err) // } // -// dhKeys := gs.GenerateDhKeyList(m1.gs.GetUser().ID, -// m1.store.GetUser().E2eDhPrivateKey, membership, m1.store.E2e().GetGroup()) +// dhKeys := gs.GenerateDhKeyList(m1.gs.GetTransmissionIdentity().ID, +// m1.store.GetTransmissionIdentity().E2eDhPrivateKey, membership, m1.store.E2e().GetGroup()) // -// grp1 := newTestGroup(m1.store.E2e().GetGroup(), m1.store.GetUser().E2eDhPrivateKey, prng, t) +// grp1 := newTestGroup(m1.store.E2e().GetGroup(), m1.store.GetTransmissionIdentity().E2eDhPrivateKey, prng, t) // grp1.Members = membership // grp1.DhKeys = dhKeys // grp1.ID = group.NewID(grp1.IdPreimage, grp1.Members) // grp1.Key = group.NewKey(grp1.KeyPreimage, grp1.Members) // grp2 := grp1.DeepCopy() -// grp2.DhKeys = gs.GenerateDhKeyList(m2.gs.GetUser().ID, -// m2.store.GetUser().E2eDhPrivateKey, membership, m2.store.E2e().GetGroup()) +// grp2.DhKeys = gs.GenerateDhKeyList(m2.gs.GetTransmissionIdentity().ID, +// m2.store.GetTransmissionIdentity().E2eDhPrivateKey, membership, m2.store.E2e().GetGroup()) // grp3 := grp1.DeepCopy() -// grp3.DhKeys = gs.GenerateDhKeyList(m3.gs.GetUser().ID, -// m3.store.GetUser().E2eDhPrivateKey, membership, m3.store.E2e().GetGroup()) +// grp3.DhKeys = gs.GenerateDhKeyList(m3.gs.GetTransmissionIdentity().ID, +// m3.store.GetTransmissionIdentity().E2eDhPrivateKey, membership, m3.store.E2e().GetGroup()) // // err = m1.gs.Add(grp1) // if err != nil { @@ -222,7 +222,7 @@ func TestNewManager_LoadError(t *testing.T) { // msg := message.Receive{ // Payload: requestMarshaled, // MessageType: message.GroupCreationRequest, -// Sender: m1.gs.GetUser().ID, +// Sender: m1.gs.GetTransmissionIdentity().ID, // } // // m2.swb.(*switchboard.Switchboard).Speak(msg) @@ -252,14 +252,14 @@ func TestNewManager_LoadError(t *testing.T) { // timestamp := netTime.Now() // // // Create cMix message and get public message -// cMixMsg, err := m1.newCmixMsg(grp1, contents, timestamp, m2.gs.GetUser(), prng) +// cMixMsg, err := m1.newCmixMsg(grp1, contents, timestamp, m2.gs.GetTransmissionIdentity(), prng) // if err != nil { // t.Errorf("Failed to create new cMix message: %+v", err) // } // // internalMsg, _ := newInternalMsg(cMixMsg.ContentsSize() - publicMinLen) // internalMsg.SetTimestamp(timestamp) -// internalMsg.SetSenderID(m1.gs.GetUser().ID) +// internalMsg.SetSenderID(m1.gs.GetTransmissionIdentity().ID) // internalMsg.SetPayload(contents) // expectedMsgID := group.NewMessageID(grp1.ID, internalMsg.Marshal()) // @@ -267,14 +267,14 @@ func TestNewManager_LoadError(t *testing.T) { // GroupID: grp1.ID, // ID: expectedMsgID, // Payload: contents, -// SenderID: m1.gs.GetUser().ID, +// SenderID: m1.gs.GetTransmissionIdentity().ID, // RoundTimestamp: timestamp.Local(), // } // // msg = message.Receive{ // Payload: cMixMsg.Marshal(), // MessageType: message.Raw, -// Sender: m1.gs.GetUser().ID, +// Sender: m1.gs.GetTransmissionIdentity().ID, // RoundTimestamp: timestamp.Local(), // } // m2.swb.(*switchboard.Switchboard).Speak(msg) diff --git a/storage/user/info.go b/storage/user/info.go index 62603c2225f0921f54684299f140fb33bfb6d182..3da77bb13935d863e382e26ad55aa529f1b63b6b 100644 --- a/storage/user/info.go +++ b/storage/user/info.go @@ -9,9 +9,7 @@ package user import ( "gitlab.com/elixxir/crypto/backup" - "gitlab.com/elixxir/crypto/contact" "gitlab.com/elixxir/crypto/cyclic" - "gitlab.com/elixxir/primitives/fact" "gitlab.com/xx_network/crypto/signature/rsa" "gitlab.com/xx_network/primitives/id" ) @@ -55,14 +53,6 @@ type Info struct { E2eDhPublicKey *cyclic.Int } -func (u Info) GetContact() contact.Contact { - return contact.Contact{ - ID: u.ReceptionID.DeepCopy(), - DhPubKey: u.E2eDhPublicKey, - Facts: make([]fact.Fact, 0), - } -} - func NewUserFromProto(proto *Proto) Info { return Info{ TransmissionID: proto.TransmissionID, diff --git a/ud/addFact.go b/ud/addFact.go index 6412e94b308871e9aa7266b2176e39cb8f169376..3c2b0af5428d71c57fd5f80cc3a0b5657bc9cccc 100644 --- a/ud/addFact.go +++ b/ud/addFact.go @@ -1,7 +1,6 @@ package ud import ( - "crypto/rand" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" pb "gitlab.com/elixxir/comms/mixmessages" @@ -47,7 +46,9 @@ func (m *Manager) addFact(inFact fact.Fact, myId *id.ID, // Sign our inFact for putting into the request privKey := m.user.PortableUserInfo().ReceptionRSA - fSig, err := rsa.Sign(rand.Reader, privKey, hash.CMixHash, fHash, nil) + stream := m.rng.GetStream() + defer stream.Close() + fSig, err := rsa.Sign(stream, privKey, hash.CMixHash, fHash, nil) if err != nil { return "", err } diff --git a/ud/manager.go b/ud/manager.go index 1ce22eac8d6c24812c381fdc60753e07ba5e5e66..77dbc01f44def0d37e0d84ff208fb8cffe39befe 100644 --- a/ud/manager.go +++ b/ud/manager.go @@ -2,6 +2,7 @@ package ud import ( "fmt" + "gitlab.com/elixxir/crypto/fastRNG" "sync" "time" @@ -14,7 +15,6 @@ import ( "gitlab.com/elixxir/crypto/contact" "gitlab.com/elixxir/primitives/fact" "gitlab.com/xx_network/comms/connect" - "gitlab.com/xx_network/crypto/csprng" "gitlab.com/xx_network/primitives/id" ) @@ -65,6 +65,10 @@ type Manager struct { // alternativeUd is an alternate User discovery service to circumvent // production. This is for testing with a separately deployed UD service. alternativeUd *alternateUd + + // rng is a fastRNG.StreamGenerator which is used to generate random + // data. This is used for signatures for adding/removing facts. + rng *fastRNG.StreamGenerator } // NewManager builds a new user discovery manager. @@ -73,7 +77,7 @@ type Manager struct { func NewManager(services CMix, e2e E2E, follower NetworkStatus, events event.Reporter, comms Comms, userStore UserInfo, - rng csprng.Source, username string, + rng *fastRNG.StreamGenerator, username string, kv *versioned.KV) (*Manager, error) { jww.INFO.Println("ud.NewManager()") @@ -90,6 +94,7 @@ func NewManager(services CMix, e2e E2E, comms: comms, user: userStore, kv: kv, + rng: rng, } if m.isRegistered() { @@ -111,7 +116,9 @@ func NewManager(services CMix, e2e E2E, } // Register with user discovery - err = m.register(username, rng, comms, udHost) + stream := rng.GetStream() + defer stream.Close() + err = m.register(username, stream, comms, udHost) if err != nil { return nil, errors.Errorf("Failed to register: %v", err) } @@ -132,6 +139,7 @@ func NewManager(services CMix, e2e E2E, func NewManagerFromBackup(services CMix, e2e E2E, follower NetworkStatus, events event.Reporter, comms Comms, userStore UserInfo, + rng *fastRNG.StreamGenerator, email, phone fact.Fact, kv *versioned.KV) (*Manager, error) { jww.INFO.Println("ud.NewManagerFromBackup()") if follower() != xxdk.Running { @@ -148,6 +156,7 @@ func NewManagerFromBackup(services CMix, comms: comms, user: userStore, kv: kv, + rng: rng, } // Initialize our store @@ -212,6 +221,7 @@ func InitStoreFromBackup(kv *versioned.KV, // instantiation of the manager by NewUserDiscovery. func LoadManager(services CMix, e2e E2E, events event.Reporter, comms Comms, userStore UserInfo, + rng *fastRNG.StreamGenerator, kv *versioned.KV) (*Manager, error) { m := &Manager{ @@ -220,8 +230,8 @@ func LoadManager(services CMix, e2e E2E, events: events, comms: comms, user: userStore, - - kv: kv, + rng: rng, + kv: kv, } if !m.isRegistered() { diff --git a/ud/remove.go b/ud/remove.go index 61d130ec240ad1bda693af3c39c9660f7858f597..b64a218e6d54f120de999478ef8b9fd5fb10dd0c 100644 --- a/ud/remove.go +++ b/ud/remove.go @@ -1,7 +1,6 @@ package ud import ( - "crypto/rand" "fmt" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" @@ -47,7 +46,9 @@ func (m *Manager) removeFact(f fact.Fact, // Sign our inFact for putting into the request privKey := m.user.PortableUserInfo().ReceptionRSA - fSig, err := rsa.Sign(rand.Reader, privKey, hash.CMixHash, fHash, nil) + stream := m.rng.GetStream() + defer stream.Close() + fSig, err := rsa.Sign(stream, privKey, hash.CMixHash, fHash, nil) if err != nil { return err } @@ -103,7 +104,9 @@ func (m *Manager) permanentDeleteAccount(f fact.Fact, myId *id.ID, privateKey *r fHash := factID.Fingerprint(f) // Sign our inFact for putting into the request - fsig, err := rsa.Sign(rand.Reader, privateKey, hash.CMixHash, fHash, nil) + stream := m.rng.GetStream() + defer stream.Close() + fsig, err := rsa.Sign(stream, privateKey, hash.CMixHash, fHash, nil) if err != nil { return err } diff --git a/ud/utils_test.go b/ud/utils_test.go index 640c62ec048b356c66319a15e3e08f3ae187ff61..5124db19cd73f3b153afde487c4649a2276780ff 100644 --- a/ud/utils_test.go +++ b/ud/utils_test.go @@ -24,6 +24,7 @@ import ( "gitlab.com/elixxir/comms/testkeys" "gitlab.com/elixxir/crypto/contact" "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/fastRNG" "gitlab.com/elixxir/ekv" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/comms/messages" @@ -69,6 +70,8 @@ func newTestManager(t *testing.T) (*Manager, *testNetworkManager) { t.Fatalf("Failed to initialize store %v", err) } + rngGen := fastRNG.NewStreamGenerator(1000, 10, csprng.NewSystemRNG) + // Create our Manager object m := &Manager{ e2e: mockE2e{grp: getGroup()}, @@ -76,6 +79,7 @@ func newTestManager(t *testing.T) (*Manager, *testNetworkManager) { user: mockUser{testing: t, key: key}, store: udStore, comms: &mockComms{}, + rng: rngGen, kv: kv, } tnm := newTestNetworkManager(t) diff --git a/xxdk/cmix.go b/xxdk/cmix.go index 059c9d061ffa8b83db87cf30db140f05d5311f7f..54aa051cbd8c22b0e7205e7e3c4d06822d5c5c39 100644 --- a/xxdk/cmix.go +++ b/xxdk/cmix.go @@ -392,7 +392,6 @@ func (c *Cmix) StopNetworkFollower() error { // NetworkFollowerStatus Gets the state of the network follower. Returns: // Stopped - 0 -// Starting - 1000 // Running - 2000 // Stopping - 3000 func (c *Cmix) NetworkFollowerStatus() Status { @@ -421,12 +420,11 @@ func (c *Cmix) AddService(sp Service) error { return c.followerServices.add(sp) } -// GetUser returns the current user Identity for this client. This -// can be serialized into a byte stream for out-of-band sharing. -func (c *Cmix) GetUser() user.Info { - jww.INFO.Printf("GetUser()") +// GetTransmissionIdentity returns the current TransmissionIdentity for this client +func (c *Cmix) GetTransmissionIdentity() TransmissionIdentity { + jww.INFO.Printf("GetTransmissionIdentity()") cMixUser := c.storage.PortableUserInfo() - return cMixUser + return buildTransmissionIdentity(cMixUser) } // GetComms returns the client comms object diff --git a/xxdk/e2e.go b/xxdk/e2e.go index ce139c2941c26eb86ccd917403864718b61c2946..8811994fd3e1b5032d6b5821c4d34e798cb41fdd 100644 --- a/xxdk/e2e.go +++ b/xxdk/e2e.go @@ -12,10 +12,13 @@ import ( "github.com/pkg/errors" 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/e2e" "gitlab.com/elixxir/client/e2e/rekey" "gitlab.com/elixxir/client/storage/user" "gitlab.com/elixxir/client/storage/versioned" + "gitlab.com/elixxir/crypto/contact" "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/diffieHellman" "gitlab.com/elixxir/ekv" @@ -34,16 +37,27 @@ type E2e struct { e2eIdentity ReceptionIdentity } +// AuthCallbacks is an adapter for the auth.Callbacks interface +// that allows for initializing an E2e object without an E2e-dependant auth.Callbacks +type AuthCallbacks interface { + Request(partner contact.Contact, receptionID receptionID.EphemeralIdentity, + round rounds.Round, e2e *E2e) + Confirm(partner contact.Contact, receptionID receptionID.EphemeralIdentity, + round rounds.Round, e2e *E2e) + Reset(partner contact.Contact, receptionID receptionID.EphemeralIdentity, + round rounds.Round, e2e *E2e) +} + // Login creates a new E2e backed by the xxdk.Cmix persistent versioned.KV -// If identity == nil, a new ReceptionIdentity will be generated automagically -func Login(client *Cmix, callbacks auth.Callbacks, +// It bundles a Cmix object with a ReceptionIdentity object +// and initializes the auth.State and e2e.Handler objects +func Login(client *Cmix, callbacks AuthCallbacks, identity ReceptionIdentity) (m *E2e, err error) { return login(client, callbacks, identity, client.GetStorage().GetKV()) } // LoginEphemeral creates a new E2e backed by a totally ephemeral versioned.KV -// If identity == nil, a new ReceptionIdentity will be generated automagically -func LoginEphemeral(client *Cmix, callbacks auth.Callbacks, +func LoginEphemeral(client *Cmix, callbacks AuthCallbacks, identity ReceptionIdentity) (m *E2e, err error) { return login(client, callbacks, identity, versioned.NewKV(ekv.MakeMemstore())) } @@ -52,17 +66,19 @@ func LoginEphemeral(client *Cmix, callbacks auth.Callbacks, // Uses the pre-generated transmission ID used by xxdk.Cmix. // This function is designed to maintain backwards compatibility with previous // xx messenger designs and should not be used for other purposes. -func LoginLegacy(client *Cmix, callbacks auth.Callbacks) (m *E2e, err error) { +func LoginLegacy(client *Cmix, callbacks AuthCallbacks) (m *E2e, err error) { m = &E2e{ Cmix: client, backup: &Container{}, } - m.e2e, err = LoadOrInitE2e(client) + m.e2e, err = loadOrInitE2eLegacy(client) if err != nil { return nil, err } - client.GetCmix().AddIdentity(client.GetUser().ReceptionID, time.Time{}, true) + + userInfo := client.GetStorage().PortableUserInfo() + client.GetCmix().AddIdentity(userInfo.ReceptionID, time.Time{}, true) err = client.AddService(m.e2e.StartProcesses) if err != nil { @@ -72,19 +88,12 @@ func LoginLegacy(client *Cmix, callbacks auth.Callbacks) (m *E2e, err error) { m.auth, err = auth.NewState(client.GetStorage().GetKV(), client.GetCmix(), m.e2e, client.GetRng(), client.GetEventReporter(), - auth.GetDefaultParams(), callbacks, m.backup.TriggerBackup) + auth.GetDefaultParams(), MakeAuthCallbacksAdapter(callbacks, m), m.backup.TriggerBackup) if err != nil { return nil, err } - u := m.Cmix.GetUser() - m.e2eIdentity = ReceptionIdentity{ - ID: u.TransmissionID, - RSAPrivatePem: u.TransmissionRSA, - Salt: u.TransmissionSalt, - DHKeyPrivate: u.E2eDhPrivateKey, - } - + m.e2eIdentity, err = buildReceptionIdentity(userInfo, m.e2e.GetGroup(), m.e2e.GetHistoricalDHPrivkey()) return m, err } @@ -131,7 +140,7 @@ func LoginWithNewBaseNDF_UNSAFE(storageDir string, password []byte, // JSON containing the cryptographic primitives. This is designed for // some specific deployment procedures and is generally unsafe. func LoginWithProtoClient(storageDir string, password []byte, - protoClientJSON []byte, newBaseNdf string, + protoClientJSON []byte, newBaseNdf string, callbacks AuthCallbacks, params Params) (*E2e, error) { jww.INFO.Printf("LoginWithProtoClient()") @@ -164,33 +173,26 @@ func LoginWithProtoClient(storageDir string, password []byte, return nil, err } - c.network.AddIdentity(c.GetUser().ReceptionID, time.Time{}, true) - - // FIXME: The callbacks need to be set, so I suppose we would need to - // either set them via a special type or add them - // to the login call? - if err != nil { - return nil, err - } err = c.registerFollower() if err != nil { return nil, err } - return Login(c, nil, ReceptionIdentity{ - ID: protoUser.ReceptionID, - RSAPrivatePem: protoUser.ReceptionRSA, - Salt: protoUser.ReceptionSalt, - DHKeyPrivate: protoUser.E2eDhPrivateKey, - }) + userInfo := c.GetStorage().PortableUserInfo() + receptionIdentity, err := buildReceptionIdentity(userInfo, c.GetStorage().GetE2EGroup(), protoUser.E2eDhPrivateKey) + return Login(c, callbacks, receptionIdentity) } // login creates a new xxdk.E2e backed by the given versioned.KV -func login(client *Cmix, callbacks auth.Callbacks, +func login(client *Cmix, callbacks AuthCallbacks, identity ReceptionIdentity, kv *versioned.KV) (m *E2e, err error) { // Verify the passed-in ReceptionIdentity matches its properties - generatedId, err := xx.NewID(identity.RSAPrivatePem.GetPublic(), identity.Salt, id.User) + privatePem, err := identity.GetRSAPrivatePem() + if err != nil { + return nil, err + } + generatedId, err := xx.NewID(privatePem.GetPublic(), identity.Salt, id.User) if err != nil { return nil, err } @@ -199,27 +201,38 @@ func login(client *Cmix, callbacks auth.Callbacks, identity.ID.String()) } - e2eGrp := client.GetStorage().GetE2EGroup() m = &E2e{ Cmix: client, backup: &Container{}, e2eIdentity: identity, } - - //initialize the e2e storage - err = e2e.Init(kv, identity.ID, identity.DHKeyPrivate, e2eGrp, - rekey.GetDefaultEphemeralParams()) + dhPrivKey, err := identity.GetDHKeyPrivate() if err != nil { return nil, err } - //load the new e2e storage + // load or init the new e2e storage + e2eGrp := client.GetStorage().GetE2EGroup() m.e2e, err = e2e.Load(kv, client.GetCmix(), identity.ID, e2eGrp, client.GetRng(), client.GetEventReporter()) if err != nil { - return nil, errors.WithMessage(err, "Failed to load a "+ - "newly created e2e store") + //initialize the e2e storage + jww.INFO.Printf("Initializing new e2e.Handler for %s", identity.ID.String()) + err = e2e.Init(kv, identity.ID, dhPrivKey, e2eGrp, + rekey.GetDefaultParams()) + if err != nil { + return nil, err + } + + //load the new e2e storage + m.e2e, err = e2e.Load(kv, + client.GetCmix(), identity.ID, e2eGrp, client.GetRng(), + client.GetEventReporter()) + if err != nil { + return nil, errors.WithMessage(err, "Failed to load a "+ + "newly created e2e store") + } } err = client.AddService(m.e2e.StartProcesses) @@ -230,19 +243,20 @@ func login(client *Cmix, callbacks auth.Callbacks, m.auth, err = auth.NewState(kv, client.GetCmix(), m.e2e, client.GetRng(), client.GetEventReporter(), - auth.GetDefaultTemporaryParams(), callbacks, m.backup.TriggerBackup) + auth.GetDefaultTemporaryParams(), MakeAuthCallbacksAdapter(callbacks, m), m.backup.TriggerBackup) if err != nil { return nil, err } + client.network.AddIdentity(identity.ID, time.Time{}, true) return m, err } -// LoadOrInitE2e loads the e2e handler or makes a new one, generating a new +// loadOrInitE2eLegacy loads the e2e handler or makes a new one, generating a new // e2e private key. It attempts to load via a legacy construction, then tries // to load the modern one, creating a new modern ID if neither can be found -func LoadOrInitE2e(client *Cmix) (e2e.Handler, error) { - usr := client.GetUser() +func loadOrInitE2eLegacy(client *Cmix) (e2e.Handler, error) { + usr := client.GetStorage().PortableUserInfo() e2eGrp := client.GetStorage().GetE2EGroup() kv := client.GetStorage().GetKV() @@ -306,15 +320,6 @@ func LoadOrInitE2e(client *Cmix) (e2e.Handler, error) { return e2eHandler, nil } -// GetUser replaces xxdk.Cmix's GetUser with one which includes the e2e dh -// private keys -func (m *E2e) GetUser() user.Info { - u := m.Cmix.GetUser() - u.E2eDhPrivateKey = m.e2e.GetHistoricalDHPrivkey() - u.E2eDhPublicKey = m.e2e.GetHistoricalDHPubkey() - return u -} - // GetReceptionIdentity returns a safe copy of the E2e ReceptionIdentity func (m *E2e) GetReceptionIdentity() ReceptionIdentity { return m.e2eIdentity.DeepCopy() @@ -331,15 +336,22 @@ func (m *E2e) ConstructProtoUserFile() ([]byte, error) { "permissioning") } + transIdentity := m.Cmix.GetTransmissionIdentity() + receptionIdentity := m.GetReceptionIdentity() + privatePem, err := receptionIdentity.GetRSAPrivatePem() + if err != nil { + return nil, err + } + Usr := user.Proto{ - TransmissionID: m.GetUser().TransmissionID, - TransmissionSalt: m.GetUser().TransmissionSalt, - TransmissionRSA: m.GetUser().TransmissionRSA, - ReceptionID: m.GetUser().ReceptionID, - ReceptionSalt: m.GetUser().ReceptionSalt, - ReceptionRSA: m.GetUser().ReceptionRSA, - Precanned: m.GetUser().Precanned, - RegistrationTimestamp: m.GetUser().RegistrationTimestamp, + TransmissionID: transIdentity.ID, + TransmissionSalt: transIdentity.Salt, + TransmissionRSA: transIdentity.RSAPrivatePem, + ReceptionID: receptionIdentity.ID, + ReceptionSalt: receptionIdentity.Salt, + ReceptionRSA: privatePem, + Precanned: m.GetStorage().IsPrecanned(), + RegistrationTimestamp: transIdentity.RegistrationTimestamp, RegCode: regCode, TransmissionRegValidationSig: m.GetStorage(). GetTransmissionRegistrationValidationSignature(), @@ -396,3 +408,55 @@ func (m *E2e) DeleteContact(partnerId *id.ID) error { return nil } + +// MakeAuthCallbacksAdapter creates an authCallbacksAdapter +func MakeAuthCallbacksAdapter(ac AuthCallbacks, e2e *E2e) *authCallbacksAdapter { + return &authCallbacksAdapter{ + ac: ac, + e2e: e2e, + } +} + +// authCallbacksAdapter is an adapter type to make the AuthCallbacks type +// compatible with the auth.Callbacks type +type authCallbacksAdapter struct { + ac AuthCallbacks + e2e *E2e +} + +func (aca *authCallbacksAdapter) Request(partner contact.Contact, + receptionID receptionID.EphemeralIdentity, round rounds.Round) { + aca.ac.Request(partner, receptionID, round, aca.e2e) +} + +func (aca *authCallbacksAdapter) Confirm(partner contact.Contact, + receptionID receptionID.EphemeralIdentity, round rounds.Round) { + aca.ac.Confirm(partner, receptionID, round, aca.e2e) +} + +func (aca *authCallbacksAdapter) Reset(partner contact.Contact, + receptionID receptionID.EphemeralIdentity, round rounds.Round) { + aca.ac.Reset(partner, receptionID, round, aca.e2e) +} + +// DefaultAuthCallbacks is a simple structure for providing a default Callbacks implementation +// It should generally not be used. +type DefaultAuthCallbacks struct{} + +// Confirm will be called when an auth Confirm message is processed. +func (a DefaultAuthCallbacks) Confirm(contact.Contact, + receptionID.EphemeralIdentity, rounds.Round, *E2e) { + jww.ERROR.Printf("No valid auth callback assigned!") +} + +// Request will be called when an auth Request message is processed. +func (a DefaultAuthCallbacks) Request(contact.Contact, + receptionID.EphemeralIdentity, rounds.Round, *E2e) { + jww.ERROR.Printf("No valid auth callback assigned!") +} + +// Reset will be called when an auth Reset operation occurs. +func (a DefaultAuthCallbacks) Reset(contact.Contact, + receptionID.EphemeralIdentity, rounds.Round, *E2e) { + jww.ERROR.Printf("No valid auth callback assigned!") +} diff --git a/xxdk/identity.go b/xxdk/identity.go index 923bf3d5c6704284360d319c2d7e2483df77acd2..46caef778129618a8a07a2ebc5ba036fc5500718 100644 --- a/xxdk/identity.go +++ b/xxdk/identity.go @@ -7,26 +7,87 @@ package xxdk import ( + "encoding/json" + "gitlab.com/elixxir/client/storage/user" + "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/crypto/contact" "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/crypto/diffieHellman" - "gitlab.com/xx_network/crypto/csprng" "gitlab.com/xx_network/crypto/signature/rsa" "gitlab.com/xx_network/crypto/xx" "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/netTime" ) +const idVersion = 0 + +// ReceptionIdentity is used by the E2e object for managing +// identities used for message pickup type ReceptionIdentity struct { ID *id.ID - RSAPrivatePem *rsa.PrivateKey + RSAPrivatePem []byte Salt []byte - DHKeyPrivate *cyclic.Int + DHKeyPrivate []byte + E2eGrp []byte +} + +// StoreReceptionIdentity stores the given identity in Cmix storage with the given key +// This is the ideal way to securely store identities, as the caller of this function +// is only required to store the given key separately rather than the keying material +func StoreReceptionIdentity(key string, identity ReceptionIdentity, client *Cmix) error { + marshalledIdentity, err := identity.Marshal() + if err != nil { + return err + } + + return client.GetStorage().Set(key, &versioned.Object{ + Version: idVersion, + Timestamp: netTime.Now(), + Data: marshalledIdentity, + }) +} + +// LoadReceptionIdentity loads the given identity in Cmix storage with the given key +func LoadReceptionIdentity(key string, client *Cmix) (ReceptionIdentity, error) { + storageObj, err := client.GetStorage().Get(key) + if err != nil { + return ReceptionIdentity{}, err + } + + return UnmarshalReceptionIdentity(storageObj.Data) +} + +// Marshal returns the JSON representation of a ReceptionIdentity +func (r ReceptionIdentity) Marshal() ([]byte, error) { + return json.Marshal(&r) +} + +// UnmarshalReceptionIdentity takes in a marshalled ReceptionIdentity +// and converts it to an object +func UnmarshalReceptionIdentity(marshaled []byte) (ReceptionIdentity, error) { + newIdentity := ReceptionIdentity{} + return newIdentity, json.Unmarshal(marshaled, &newIdentity) +} + +// GetDHKeyPrivate returns the DHKeyPrivate in go format +func (r ReceptionIdentity) GetDHKeyPrivate() (*cyclic.Int, error) { + dhKeyPriv := &cyclic.Int{} + err := dhKeyPriv.UnmarshalJSON(r.DHKeyPrivate) + return dhKeyPriv, err +} + +// GetRSAPrivatePem returns the RSAPrivatePem in go format +func (r ReceptionIdentity) GetRSAPrivatePem() (*rsa.PrivateKey, error) { + return rsa.LoadPrivateKeyFromPem(r.RSAPrivatePem) } // MakeReceptionIdentity generates a new cryptographic identity // for receiving messages. -func MakeReceptionIdentity(rng csprng.Source, - grp *cyclic.Group) (ReceptionIdentity, error) { +func MakeReceptionIdentity(client *Cmix) (ReceptionIdentity, error) { + rng := client.GetRng().GetStream() + defer rng.Close() + grp := client.GetStorage().GetE2EGroup() + //make RSA Key rsaKey, err := rsa.GenerateKey(rng, rsa.DefaultRSABitLen) @@ -50,12 +111,24 @@ func MakeReceptionIdentity(rng csprng.Source, return ReceptionIdentity{}, err } + privKeyBytes, err := privKey.MarshalJSON() + if err != nil { + return ReceptionIdentity{}, err + } + + grpBytes, err := grp.MarshalJSON() + if err != nil { + return ReceptionIdentity{}, err + } + //create the identity object + rsaPem := rsa.CreatePrivateKeyPem(rsaKey) I := ReceptionIdentity{ ID: newId, - RSAPrivatePem: rsaKey, + RSAPrivatePem: rsaPem, Salt: salt, - DHKeyPrivate: privKey, + DHKeyPrivate: privKeyBytes, + E2eGrp: grpBytes, } return I, nil @@ -65,18 +138,28 @@ func MakeReceptionIdentity(rng csprng.Source, func (r ReceptionIdentity) DeepCopy() ReceptionIdentity { saltCopy := make([]byte, len(r.Salt)) copy(saltCopy, r.Salt) + + dhKeyCopy := make([]byte, len(r.DHKeyPrivate)) + copy(dhKeyCopy, r.DHKeyPrivate) + + grpCopy := make([]byte, len(r.E2eGrp)) + copy(grpCopy, r.E2eGrp) return ReceptionIdentity{ ID: r.ID.DeepCopy(), RSAPrivatePem: r.RSAPrivatePem, Salt: saltCopy, - DHKeyPrivate: r.DHKeyPrivate.DeepCopy(), + DHKeyPrivate: dhKeyCopy, + E2eGrp: grpCopy, } } // GetContact accepts a xxdk.ReceptionIdentity object and returns a contact.Contact object -func (r ReceptionIdentity) GetContact(grp *cyclic.Group) contact.Contact { - dhPub := grp.ExpG(r.DHKeyPrivate, grp.NewInt(1)) +func (r ReceptionIdentity) GetContact() contact.Contact { + grp := &cyclic.Group{} + _ = grp.UnmarshalJSON(r.E2eGrp) + dhKeyPriv, _ := r.GetDHKeyPrivate() + dhPub := grp.ExpG(dhKeyPriv, grp.NewInt(1)) ct := contact.Contact{ ID: r.ID, DhPubKey: dhPub, @@ -85,3 +168,62 @@ func (r ReceptionIdentity) GetContact(grp *cyclic.Group) contact.Contact { } return ct } + +// buildReceptionIdentity creates a new ReceptionIdentity +// from the given user.Info +func buildReceptionIdentity(userInfo user.Info, e2eGrp *cyclic.Group, dHPrivkey *cyclic.Int) (ReceptionIdentity, error) { + saltCopy := make([]byte, len(userInfo.TransmissionSalt)) + copy(saltCopy, userInfo.TransmissionSalt) + + grp, err := e2eGrp.MarshalJSON() + if err != nil { + return ReceptionIdentity{}, err + } + privKey, err := dHPrivkey.MarshalJSON() + if err != nil { + return ReceptionIdentity{}, err + } + + return ReceptionIdentity{ + ID: userInfo.ReceptionID.DeepCopy(), + RSAPrivatePem: rsa.CreatePrivateKeyPem(userInfo.ReceptionRSA), + Salt: saltCopy, + DHKeyPrivate: privKey, + E2eGrp: grp, + }, nil +} + +// TransmissionIdentity represents the identity +// used to transmit over the network via a specific Cmix object +type TransmissionIdentity struct { + ID *id.ID + RSAPrivatePem *rsa.PrivateKey + Salt []byte + // Timestamp in which user has registered with the network + RegistrationTimestamp int64 +} + +// DeepCopy produces a safe copy of a TransmissionIdentity +func (t TransmissionIdentity) DeepCopy() TransmissionIdentity { + saltCopy := make([]byte, len(t.Salt)) + copy(saltCopy, t.Salt) + return TransmissionIdentity{ + ID: t.ID.DeepCopy(), + RSAPrivatePem: t.RSAPrivatePem, + Salt: saltCopy, + RegistrationTimestamp: t.RegistrationTimestamp, + } +} + +// buildTransmissionIdentity creates a new TransmissionIdentity +// from the given user.Info +func buildTransmissionIdentity(userInfo user.Info) TransmissionIdentity { + saltCopy := make([]byte, len(userInfo.TransmissionSalt)) + copy(saltCopy, userInfo.TransmissionSalt) + return TransmissionIdentity{ + ID: userInfo.TransmissionID.DeepCopy(), + RSAPrivatePem: userInfo.TransmissionRSA, + Salt: saltCopy, + RegistrationTimestamp: userInfo.RegistrationTimestamp, + } +} diff --git a/xxdk/permissioning.go b/xxdk/permissioning.go index 2e1d735e1236c410bfb473e7d02bad87b6f73536..e3327d8a875c33885c3194013434e0b8d7037252 100644 --- a/xxdk/permissioning.go +++ b/xxdk/permissioning.go @@ -63,15 +63,16 @@ func (c *Cmix) ConstructProtoUserFile() ([]byte, error) { "permissioning") } + userInfo := c.GetStorage().PortableUserInfo() Usr := user.Proto{ - TransmissionID: c.GetUser().TransmissionID, - TransmissionSalt: c.GetUser().TransmissionSalt, - TransmissionRSA: c.GetUser().TransmissionRSA, - ReceptionID: c.GetUser().ReceptionID, - ReceptionSalt: c.GetUser().ReceptionSalt, - ReceptionRSA: c.GetUser().ReceptionRSA, - Precanned: c.GetUser().Precanned, - RegistrationTimestamp: c.GetUser().RegistrationTimestamp, + TransmissionID: userInfo.TransmissionID, + TransmissionSalt: userInfo.TransmissionSalt, + TransmissionRSA: userInfo.TransmissionRSA, + ReceptionID: userInfo.ReceptionID, + ReceptionSalt: userInfo.ReceptionSalt, + ReceptionRSA: userInfo.ReceptionRSA, + Precanned: userInfo.Precanned, + RegistrationTimestamp: userInfo.RegistrationTimestamp, RegCode: regCode, TransmissionRegValidationSig: c.storage.GetTransmissionRegistrationValidationSignature(), ReceptionRegValidationSig: c.storage.GetReceptionRegistrationValidationSignature(), diff --git a/xxdk/precan.go b/xxdk/precan.go index b710954cc27f6901d5f4b0226a3b5aa920bb8f4a..51ae566f33d7983bf2009c3b5e3107551653f1ab 100644 --- a/xxdk/precan.go +++ b/xxdk/precan.go @@ -62,7 +62,7 @@ func CreatePrecannedUser(precannedID uint, rng csprng.Source) user.Info { // username/identity, but merely creates a new cryptographic identity // for adding such information at a later date. func NewPrecannedClient(precannedID uint, defJSON, storageDir string, - password []byte) error { + password []byte) (ReceptionIdentity, error) { jww.INFO.Printf("NewPrecannedClient()") rngStreamGen := fastRNG.NewStreamGenerator(12, 1024, csprng.NewSystemRNG) @@ -70,26 +70,32 @@ func NewPrecannedClient(precannedID uint, defJSON, storageDir string, def, err := ParseNDF(defJSON) if err != nil { - return err + return ReceptionIdentity{}, err } cmixGrp, e2eGrp := DecodeGroups(def) + dhPrivKey := generatePrecanDHKeypair(precannedID, e2eGrp) + protoUser := CreatePrecannedUser(precannedID, rngStream) + identity, err := buildReceptionIdentity(protoUser, e2eGrp, dhPrivKey) + if err != nil { + return ReceptionIdentity{}, err + } store, err := CheckVersionAndSetupStorage(def, storageDir, password, protoUser, cmixGrp, e2eGrp, "") if err != nil { - return err + return ReceptionIdentity{}, err } // Mark the precanned user as finished with permissioning and registered // with the network. err = store.ForwardRegistrationStatus(storage.PermissioningComplete) if err != nil { - return err + return ReceptionIdentity{}, err } - return nil + return identity, err } func generatePrecanDHKeypair(precannedID uint, e2eGrp *cyclic.Group) *cyclic.Int {