diff --git a/api/authenticatedChannel.go b/api/authenticatedChannel.go index 7e995a896a69e1d045186aa377b9ada99d5c4087..4b9ef33192279c0d5a6d36c520e17ddddf77c35d 100644 --- a/api/authenticatedChannel.go +++ b/api/authenticatedChannel.go @@ -91,7 +91,7 @@ func (c *Client) ConfirmAuthenticatedChannel(recipient contact.Contact) (id.Roun "creation when the network is not healthy") } - return c.auth.ConfirmRequestAuth(recipient) + return c.auth.Confirm(recipient) } // VerifyOwnership checks if the ownership proof on a passed contact matches the diff --git a/api/client.go b/api/client.go index a7019760a03751caacf9c64b9de92adf4f16b09b..c38663782d6ee45a19d719b90d987da2728cfaa7 100644 --- a/api/client.go +++ b/api/client.go @@ -63,7 +63,7 @@ type Client struct { //object used to register and communicate with permissioning permissioning *registration.Registration //object containing auth interactions - auth *auth.State + auth *auth.state //services system to track running threads followerServices *services diff --git a/auth/confirm.go b/auth/confirm.go index 92bdf65a5950a14c5d8257bdb2b7f188c0e29e79..8e26dcdeb04c6209dc18578e48d75d37028ae0cd 100644 --- a/auth/confirm.go +++ b/auth/confirm.go @@ -23,7 +23,17 @@ import ( "gitlab.com/xx_network/primitives/id" ) -func (s *State) ConfirmRequestAuth(partner contact.Contact) ( +// Confirm sends a confirmation for a received request. It can only be called +// once. This both sends keying material to the other party and creates a +// channel in the e2e handler, after which e2e messages can be sent to the +// partner using e2e.Handler.SendE2e. +// The round the request is initially sent on will be returned, but the request +// will be listed as a critical message, so the underlying cmix client will +// auto resend it in the event of failure. +// A confirm cannot be sent for a contact who has not sent a request or +// who is already a partner. This can only be called once for a specific contact. +// If the request must be resent, use ReplayConfirm +func (s *state) Confirm(partner contact.Contact) ( id.Round, error) { // check that messages can be sent over the network @@ -32,10 +42,10 @@ func (s *State) ConfirmRequestAuth(partner contact.Contact) ( "when the network is not healthy") } - return s.confirmRequestAuth(partner, s.params.ConfirmTag) + return s.confirm(partner, s.params.ConfirmTag) } -func (s *State) confirmRequestAuth(partner contact.Contact, serviceTag string) ( +func (s *state) confirm(partner contact.Contact, serviceTag string) ( id.Round, error) { // check that messages can be sent over the network @@ -170,3 +180,4 @@ func sendAuthConfirm(net cmix.Client, partner *id.ID, event.Report(1, "Auth", "SendConfirm", em) return sentRound, nil } + diff --git a/auth/interface.go b/auth/interface.go new file mode 100644 index 0000000000000000000000000000000000000000..7fdd7396879d40871b21315d498e78400eee3391 --- /dev/null +++ b/auth/interface.go @@ -0,0 +1,62 @@ +package auth + +import ( + "gitlab.com/elixxir/crypto/contact" + "gitlab.com/elixxir/primitives/fact" + "gitlab.com/xx_network/primitives/id" +) + +type State interface { + // Request sends a contact request from the user identity in the imported + // e2e structure to the passed contact, as well as the passed facts (will + // error if they are too long). + // The other party must accept the request by calling Confirm in order to be + // able to send messages using e2e.Handler.SendE2e. When the other party + // does so, the "confirm" callback will get called. + // The round the request is initially sent on will be returned, but the + // request will be listed as a critical message, so the underlying cmix + // client will auto resend it in the event of failure. + // A request cannot be sent for a contact who has already received a + // request or who is already a partner. + // The request sends as a critical message, if the round send on fails, it + // will be auto resent by the cmix client + Request(partner contact.Contact, myfacts fact.FactList) (id.Round, error) + + // Confirm sends a confirmation for a received request. It can only be + // called once. This both sends keying material to the other party and + // creates a channel in the e2e handler, after which e2e messages can be + // sent to the partner using e2e.Handler.SendE2e. + // The round the request is initially sent on will be returned, but the + // request will be listed as a critical message, so the underlying cmix + // client will auto resend it in the event of failure. + // A confirm cannot be sent for a contact who has not sent a request or + // who is already a partner. This can only be called once for a specific + // contact. + // The confirm sends as a critical message, if the round send on fails, it + // will be auto resent by the cmix client + // If the confirm must be resent, use ReplayConfirm + Confirm(partner contact.Contact) (id.Round, error) + + // Reset sends a contact reset request from the user identity in the + // imported e2e structure to the passed contact, as well as the passed + // facts (will error if they are too long). + // This delete all traces of the relationship with the partner from e2e and + // create a new relationship from scratch. + // The round the reset is initially sent on will be returned, but the request + // will be listed as a critical message, so the underlying cmix client will + // auto resend it in the event of failure. + // A request cannot be sent for a contact who has already received a + // request or who is already a partner. + Reset(partner contact.Contact) (id.Round, error) + + // ReplayConfirm Resends a confirm to the partner. + // will fail to send if the send relationship with the partner has already + // ratcheted + // The confirm sends as a critical message, if the round send on fails, it + // will be auto resent by the cmix client + ReplayConfirm(partner *id.ID) (id.Round, error) + + // ReplayRequests will iterate through all pending contact requests and replay + // them on the callbacks. + ReplayRequests() +} diff --git a/auth/params.go b/auth/params.go index 60ab3ac58fcf594b96fe131b599b9932a187468c..92cb6e57de8f1e39efafccfc8d8d93ce5f30b917 100644 --- a/auth/params.go +++ b/auth/params.go @@ -1,5 +1,7 @@ package auth +import "gitlab.com/elixxir/client/catalog" + type Param struct { ReplayRequests bool @@ -9,6 +11,25 @@ type Param struct { ResetConfirmTag string } +func GetDefaultParams() Param { + return Param{ + ReplayRequests: false, + RequestTag: catalog.Request, + ConfirmTag: catalog.Confirm, + ResetRequestTag: catalog.Reset, + ResetConfirmTag: catalog.ConfirmReset, + } +} + +func GetDefaultTemporaryParams() Param { + p := GetDefaultParams() + p.RequestTag = catalog.RequestEphemeral + p.ConfirmTag = catalog.ConfirmEphemeral + p.ResetRequestTag = catalog.ResetEphemeral + p.ResetConfirmTag = catalog.ConfirmResetEphemeral + return p +} + func (p Param) getConfirmTag(reset bool) string { if reset { return p.ResetConfirmTag diff --git a/auth/receivedConfirm.go b/auth/receivedConfirm.go index 7a1447426156065ed4ebdd187ec60659f781cc4e..70da22a76cee612449d5c9aad3224468725344bd 100644 --- a/auth/receivedConfirm.go +++ b/auth/receivedConfirm.go @@ -16,7 +16,7 @@ import ( ) type receivedConfirmService struct { - s *State + s *state *store.SentRequest notificationsService message.Service } diff --git a/auth/receivedRequest.go b/auth/receivedRequest.go index a50a059966be2099f626350bbcfbe79c34869306..586b048aa8f35d0ace9ff9fcb32ecb00fdcf2cfa 100644 --- a/auth/receivedRequest.go +++ b/auth/receivedRequest.go @@ -21,7 +21,7 @@ import ( const dummyerr = "dummy error so we dont delete the request" type receivedRequestService struct { - s *State + s *state reset bool } @@ -175,6 +175,8 @@ func (rrs *receivedRequestService) Process(message format.Message, // if a sent request already exists, that means we requested at the same // time they did. We need to auto-confirm if we are randomly selected + // (the one with the smaller id,when looked at as long unsigned integer, + // is selected) // (SIDH keys have polarity, so both sent keys cannot be used together) autoConfirm := false bail := false diff --git a/auth/replayConfirm.go b/auth/replayConfirm.go new file mode 100644 index 0000000000000000000000000000000000000000..70b4ba61922b35d047f8a68bd7488cc4c92c0094 --- /dev/null +++ b/auth/replayConfirm.go @@ -0,0 +1,8 @@ +package auth + +import "gitlab.com/xx_network/primitives/id" + +//todo implement replay confirm +func (s *state) ReplayConfirm(partner *id.ID) (id.Round, error) { + return 0, nil +} diff --git a/auth/request.go b/auth/request.go index 4cdef0e97e1da92a36004338ad450e49fa3d198c..3e6b8c874b8243c313182d39437aca0d1d412153 100644 --- a/auth/request.go +++ b/auth/request.go @@ -12,7 +12,6 @@ import ( "github.com/cloudflare/circl/dh/sidh" "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" - "gitlab.com/elixxir/client/catalog" "gitlab.com/elixxir/client/cmix" "gitlab.com/elixxir/client/cmix/message" "gitlab.com/elixxir/client/e2e/ratchet" @@ -30,7 +29,18 @@ import ( const terminator = ";" -func (s *State) RequestAuth(partner contact.Contact, myfacts fact.FactList) (id.Round, error) { +// Request sends a contact request from the user identity in the imported e2e +// structure to the passed contact, as well as the passed facts (will error if +// they are too long). +// The other party must accept the request by calling Confirm in order to be +// able to send messages using e2e.Handler.SendE2e. When the other party does so, +// the "confirm" callback will get called. +// The round the request is initially sent on will be returned, but the request +// will be listed as a critical message, so the underlying cmix client will +// auto resend it in the event of failure. +// A request cannot be sent for a contact who has already received a request or +// who is already a partner. +func (s *state) Request(partner contact.Contact, myfacts fact.FactList) (id.Round, error) { // check that an authenticated channel does not already exist if _, err := s.e2e.GetPartner(partner.ID); err == nil || !strings.Contains(err.Error(), ratchet.NoPartnerErrorStr) { @@ -38,11 +48,11 @@ func (s *State) RequestAuth(partner contact.Contact, myfacts fact.FactList) (id. "established with partner") } - return s.requestAuth(partner, myfacts, false) + return s.request(partner, myfacts, false) } -// requestAuth internal helper -func (s *State) requestAuth(partner contact.Contact, myfacts fact.FactList, reset bool) (id.Round, error) { +// request internal helper +func (s *state) request(partner contact.Contact, myfacts fact.FactList, reset bool) (id.Round, error) { //do key generation rng := s.rng.GetStream() @@ -103,12 +113,12 @@ func (s *State) requestAuth(partner contact.Contact, myfacts fact.FactList, rese //register service for notification on confirmation s.net.AddService(me, message.Service{ Identifier: confirmFp[:], - Tag: catalog.Confirm, + Tag: s.params.getConfirmTag(reset), Metadata: partner.ID[:], }, nil) - jww.TRACE.Printf("RequestAuth ECRPAYLOAD: %v", request.GetEcrPayload()) - jww.TRACE.Printf("RequestAuth MAC: %v", mac) + jww.TRACE.Printf("Request ECRPAYLOAD: %v", request.GetEcrPayload()) + jww.TRACE.Printf("Request MAC: %v", mac) jww.INFO.Printf("Requesting Auth with %s, msgDigest: %s", partner.ID, format.DigestContents(contents)) @@ -117,7 +127,7 @@ func (s *State) requestAuth(partner contact.Contact, myfacts fact.FactList, rese p.DebugTag = "auth.Request" svc := message.Service{ Identifier: partner.ID.Marshal(), - Tag: catalog.Request, + Tag: s.params.RequestTag, Metadata: nil, } round, _, err := s.net.Send(partner.ID, requestfp, svc, contents, mac, p) diff --git a/auth/reset.go b/auth/reset.go index 0f415129ca36f6d5acf67b240159eb9c34844e45..a688926b93c6cb441a88ff016ba817276b6b5321 100644 --- a/auth/reset.go +++ b/auth/reset.go @@ -7,7 +7,17 @@ import ( "gitlab.com/xx_network/primitives/id" ) -func (s *State) ResetSession(partner contact.Contact) (id.Round, error) { +// Reset sends a contact reset request from the user identity in the imported e2e +// structure to the passed contact, as well as the passed facts (will error if +// they are too long). +// This delete all traces of the relationship with the partner from e2e and +// create a new relationship from scratch. +// The round the reset is initially sent on will be returned, but the request +// will be listed as a critical message, so the underlying cmix client will +// auto resend it in the event of failure. +// A request cannot be sent for a contact who has already received a request or +// who is already a partner. +func (s *state) Reset(partner contact.Contact) (id.Round, error) { // Delete authenticated channel if it exists. if err := s.e2e.DeletePartner(partner.ID); err != nil { @@ -21,5 +31,5 @@ func (s *State) ResetSession(partner contact.Contact) (id.Round, error) { _ = s.store.DeleteReceivedRequest(partner.ID) // Try to initiate a clean session request - return s.requestAuth(partner, fact.FactList{}, true) + return s.request(partner, fact.FactList{}, true) } diff --git a/auth/sentRequestHandler.go b/auth/sentRequestHandler.go index cb9b0b0b21591f998b9ba0a95e910e9264050410..51d5b7dc982b7705db9a63ff756a0e1b2bd0e635 100644 --- a/auth/sentRequestHandler.go +++ b/auth/sentRequestHandler.go @@ -9,7 +9,7 @@ import ( // sentRequestHandler interface which allows the lower level to register // sent requests with cmix while blackboxing it type sentRequestHandler struct { - s *State + s *state } // Add Adds the service and fingerprints to cmix for the given sent request diff --git a/auth/state.go b/auth/state.go index f09e174a28f9343b2d142709e583e10d79573507..f75b7fbd508e94febe7791796fc5948489541adf 100644 --- a/auth/state.go +++ b/auth/state.go @@ -23,7 +23,7 @@ import ( "gitlab.com/xx_network/primitives/id" ) -type State struct { +type state struct { callbacks Callbacks net cmix.Client @@ -49,9 +49,20 @@ type Callbacks interface { // found. // Bases its reception identity and keys off of what is found in e2e. // Uses this ID to modify the kv prefix for a unique storage path +// Parameters: +// The params object passed in determines the services that will be used +// to pick up requests and signal notifications. These are unique to an +// identity, so multiple auth states with the same service tags with different +// identities can run simultaneously. +// Default parameters can be retrieved via GetDefaultParameters() +// Temporary: +// In some cases, for example client <-> server communications, connections +// are treated as ephemeral. To do so in auth, pass in an ephemeral e2e (made +// with a memory only versioned.KV) as well as a memory only versioned.KV for +// NewState and use GetDefaultTemporaryParams() for the parameters func NewState(kv *versioned.KV, net cmix.Client, e2e e2e.Handler, rng *fastRNG.StreamGenerator, event event.Manager, params Param, - callbacks Callbacks) (*State, error) { + callbacks Callbacks) (State, error) { kv = kv.Prefix(makeStorePrefix(e2e.GetReceptionID())) return NewStateLegacy(kv, net, e2e, rng, event, params, callbacks) } @@ -60,11 +71,12 @@ func NewState(kv *versioned.KV, net cmix.Client, e2e e2e.Handler, // found. // Bases its reception identity and keys off of what is found in e2e. // Does not modify the kv prefix for backwards compatibility +// Otherwise, acts the same as NewState func NewStateLegacy(kv *versioned.KV, net cmix.Client, e2e e2e.Handler, rng *fastRNG.StreamGenerator, event event.Manager, params Param, - callbacks Callbacks) (*State, error) { + callbacks Callbacks) (State, error) { - s := &State{ + s := &state{ callbacks: callbacks, net: net, @@ -100,9 +112,9 @@ func NewStateLegacy(kv *versioned.KV, net cmix.Client, e2e e2e.Handler, return s, nil } -// ReplayRequests will iterate through all pending contact requests and resend them -// to the desired contact. -func (s *State) ReplayRequests() { +// ReplayRequests will iterate through all pending contact requests and replay +// them on the callbacks. +func (s *state) ReplayRequests() { rrList := s.store.GetAllReceivedRequests() for i := range rrList { rr := rrList[i] diff --git a/auth/state_test.go b/auth/state_test.go index d023abfad0542ce7374ef57f796e0dd5d831d45b..c24877dd9a57466e9f6c79c3319f750ba4409c25 100644 --- a/auth/state_test.go +++ b/auth/state_test.go @@ -31,7 +31,7 @@ func TestManager_ReplayRequests(t *testing.T) { numReceived := 10 // Construct barebones manager - m := State{ + m := state{ requestCallbacks: newCallbackMap(), storage: s, replayRequests: true, diff --git a/catalog/services.go b/catalog/services.go index 9c32b32522508dbacc1ddaf7f14bc1708a1c1f68..cd28b663e04c925eadd329a49f82aa84aec824de 100644 --- a/catalog/services.go +++ b/catalog/services.go @@ -4,10 +4,22 @@ import "gitlab.com/elixxir/crypto/sih" const ( Default = sih.Default - Request = "request" - Confirm = "confirm" - Silent = "silent" - E2e = "e2e" + + //e2e + Silent = "silent" + E2e = "e2e" + + //auth + Request = "request" + Reset = "reset" + Confirm = "confirm" + ConfirmReset = "confirmReset" + + RequestEphemeral = "requestEph" + ResetEphemeral = "resetEph" + ConfirmEphemeral = "confirmEph" + ConfirmResetEphemeral = "confirmResetEph" + Group = "group" EndFT = "endFT" GroupRq = "groupRq"