diff --git a/api/client.go b/api/client.go index 2a07d1743783e8a22dff5bc4d7c384b5e092a772..c47a4eb5f7e495ca3a24c5c66264c80a1f74e86e 100644 --- a/api/client.go +++ b/api/client.go @@ -346,7 +346,7 @@ func (c *Client) GetHealth() interfaces.HealthTracker { return c.network.GetHealthTracker() } -// Returns the switchboard for Registration +// Returns the switchboard for Identity func (c *Client) GetSwitchboard() interfaces.Switchboard { jww.INFO.Printf("GetSwitchboard()") return c.switchboard diff --git a/api/send.go b/api/send.go index 84256b206d71db926818cc5c93b8e0269ce14eef..19d6a54b853f911e47af7d2d9bcafa46e2b7bd4d 100644 --- a/api/send.go +++ b/api/send.go @@ -15,6 +15,7 @@ import ( "gitlab.com/elixxir/crypto/e2e" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/id/ephemeral" ) //This holds all functions to send messages over the network @@ -45,23 +46,21 @@ func (c *Client) SendUnsafe(m message.Send, param params.Unsafe) ([]id.Round, // recipient. Note that both SendE2E and SendUnsafe call SendCMIX. // Returns the round ID of the round the payload was sent or an error // if it fails. -func (c *Client) SendCMIX(msg format.Message, param params.CMIX) (id.Round, - error) { +func (c *Client) SendCMIX(msg format.Message, recipientID *id.ID, + param params.CMIX) (id.Round, ephemeral.Id, error) { jww.INFO.Printf("SendCMIX(%s)", string(msg.GetContents())) - return c.network.SendCMIX(msg, param) + return c.network.SendCMIX(msg, recipientID, param) } // NewCMIXMessage Creates a new cMix message with the right properties // for the current cMix network. // FIXME: this is weird and shouldn't be necessary, but it is. -func (c *Client) NewCMIXMessage(recipient *id.ID, - contents []byte) (format.Message, error) { +func (c *Client) NewCMIXMessage(contents []byte) (format.Message, error) { primeSize := len(c.storage.Cmix().GetGroup().GetPBytes()) msg := format.NewMessage(primeSize) if len(contents) > msg.ContentsSize() { return format.Message{}, errors.New("Contents to long for cmix") } msg.SetContents(contents) - msg.SetRecipientID(recipient) return msg, nil } diff --git a/auth/confirm.go b/auth/confirm.go index a58306c2da8dce6017c96af1d10888652f67ea73..4a1d27cdea37d86bc8e59305daffca7c9cc78216 100644 --- a/auth/confirm.go +++ b/auth/confirm.go @@ -99,7 +99,6 @@ func ConfirmRequestAuth(partner contact.Contact, rng io.Reader, cmixMsg.SetKeyFP(fp) cmixMsg.SetMac(mac) cmixMsg.SetContents(baseFmt.Marshal()) - cmixMsg.SetRecipientID(partner.ID) // fixme: channel can get into a bricked state if the first save occurs and // the second does not or the two occur and the storage into critical @@ -127,7 +126,7 @@ func ConfirmRequestAuth(partner contact.Contact, rng io.Reader, storage.GetCriticalRawMessages().AddProcessing(cmixMsg) /*send message*/ - round, err := net.SendCMIX(cmixMsg, params.GetDefaultCMIX()) + round, _, err := net.SendCMIX(cmixMsg, partner.ID, params.GetDefaultCMIX()) if err != nil { // if the send fails just set it to failed, it will but automatically // retried diff --git a/auth/request.go b/auth/request.go index dd8ba152500b192591a58aab863df4c14a462c26..d0e20a44b2f09707a4d456c99c5ced2cacd65551 100644 --- a/auth/request.go +++ b/auth/request.go @@ -132,7 +132,6 @@ func RequestAuth(partner, me contact.Contact, message string, rng io.Reader, cmixMsg.SetKeyFP(requestfp) cmixMsg.SetMac(mac) cmixMsg.SetContents(baseFmt.Marshal()) - cmixMsg.SetRecipientID(partner.ID) jww.INFO.Printf("PARTNER ID: %s", partner.ID) /*store state*/ @@ -149,11 +148,11 @@ func RequestAuth(partner, me contact.Contact, message string, rng io.Reader, //jww.INFO.Printf("CMIX MESSAGE 1: %s, %v, %v, %v", cmixMsg.GetRecipientID(), // cmixMsg.GetKeyFP(), cmixMsg.GetMac(), cmixMsg.GetContents()) - jww.INFO.Printf("CMIX MESSAGE FP: %s, %v", cmixMsg.GetRecipientID(), + jww.INFO.Printf("CMIX MESSAGE FP: %s, %v", partner.ID, cmixMsg.GetKeyFP()) /*send message*/ - round, err := net.SendCMIX(cmixMsg, params.GetDefaultCMIX()) + round, _, err := net.SendCMIX(cmixMsg, partner.ID, params.GetDefaultCMIX()) if err != nil { // if the send fails just set it to failed, it will but automatically // retried diff --git a/bindings/send.go b/bindings/send.go index 605fddd26d2ceace3a7081e73a0a17b0d007d87a..a7e35873447758c22cb9ddfc8eddeaf86fe1bb73 100644 --- a/bindings/send.go +++ b/bindings/send.go @@ -43,13 +43,13 @@ func (c *Client) SendCmix(recipient, contents []byte, parameters string) (int, e err)) } - msg, err := c.api.NewCMIXMessage(u, contents) + msg, err := c.api.NewCMIXMessage(contents) if err != nil { return -1, errors.New(fmt.Sprintf("Failed to sendCmix: %+v", err)) } - rid, err := c.api.SendCMIX(msg, p) + rid, _, err := c.api.SendCMIX(msg, u, p) if err != nil { return -1, errors.New(fmt.Sprintf("Failed to sendCmix: %+v", err)) diff --git a/bindings/ud.go b/bindings/ud.go index bae84a46f6b040a7a366cd51396f0c433278da0a..643066a2ad2417ab444698c63b717101454ea53c 100644 --- a/bindings/ud.go +++ b/bindings/ud.go @@ -44,7 +44,7 @@ func NewUserDiscovery(client *Client)(*UserDiscovery, error){ // network signatures are malformed or if the username is taken. Usernames // cannot be changed after registration at this time. Will fail if the user is // already registered. -// Registration does not go over cmix, it occurs over normal communications +// Identity does not go over cmix, it occurs over normal communications func (ud *UserDiscovery)Register(username string)error{ return ud.ud.Register(username) } diff --git a/cmd/root.go b/cmd/root.go index 7fcc9a5b00cfb05a121441bff85f74349abbbc67..ca88cd9bf72b9054afc7023367c5d54f751772ed 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -565,7 +565,7 @@ func init() { viper.BindPFlag("log", rootCmd.PersistentFlags().Lookup("log")) rootCmd.Flags().StringP("regcode", "", "", - "Registration code (optional)") + "Identity code (optional)") viper.BindPFlag("regcode", rootCmd.Flags().Lookup("regcode")) rootCmd.Flags().StringP("message", "m", "", "Message to send") diff --git a/globals/statusEvents.go b/globals/statusEvents.go index 9695871da46a8252d42bd838996b1bb7a30330c2..fdf8d6ff99f6ac0c5cbc155eebe1a9c645c0ab8f 100644 --- a/globals/statusEvents.go +++ b/globals/statusEvents.go @@ -7,9 +7,9 @@ package globals -//Registration +//Identity const REG_KEYGEN = 1 //Generating Cryptographic Keys -const REG_PRECAN = 2 //Doing a Precanned Registration (Not Secure) +const REG_PRECAN = 2 //Doing a Precanned Identity (Not Secure) const REG_UID_GEN = 3 //Generating User ID const REG_PERM = 4 //Validating User Identity With Permissioning Server const REG_NODE = 5 //Registering with Nodes diff --git a/go.mod b/go.mod index 6176f5edda53c1f4f99c6289e28f15280496e803..cff0e768391d7579628b29049b954625e5b4b577 100644 --- a/go.mod +++ b/go.mod @@ -19,14 +19,14 @@ require ( github.com/spf13/viper v1.7.1 github.com/ugorji/go v1.1.4 // indirect github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 // indirect - gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228 - gitlab.com/elixxir/comms v0.0.4-0.20210114174157-1306832d440b - gitlab.com/elixxir/crypto v0.0.7-0.20210107184400-5c3e52a35758 + gitlab.com/elixxir/bloomfilter v0.0.0-20210120224144-ac046983a17a + gitlab.com/elixxir/comms v0.0.4-0.20210208181740-1ad1139f7170 + gitlab.com/elixxir/crypto v0.0.7-0.20210208181828-64b4b57e23d6 gitlab.com/elixxir/ekv v0.1.4 - gitlab.com/elixxir/primitives v0.0.3-0.20210107183456-9cf6fe2de1e5 - gitlab.com/xx_network/comms v0.0.4-0.20210112233928-eac8db03c397 - gitlab.com/xx_network/crypto v0.0.5-0.20210107183440-804e0f8b7d22 - gitlab.com/xx_network/primitives v0.0.4-0.20210114170718-1549f24d462c + gitlab.com/elixxir/primitives v0.0.3-0.20210127201240-6a42ad925e8a + gitlab.com/xx_network/comms v0.0.4-0.20210121204701-7a1eb0542424 + gitlab.com/xx_network/crypto v0.0.5-0.20210121204626-b251b926e4f7 + gitlab.com/xx_network/primitives v0.0.4-0.20210208183356-ee1e9ec13f8f golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect golang.org/x/sys v0.0.0-20210105210732-16f7687f5001 // indirect diff --git a/go.sum b/go.sum index f5f678113615e8cdda98594645a7827210c2d71a..7d36eea12e9b2f59707b7674b4a09b0c68330d9e 100644 --- a/go.sum +++ b/go.sum @@ -265,6 +265,9 @@ github.com/zeebo/pcg v1.0.0 h1:dt+dx+HvX8g7Un32rY9XWoYnd0NmKmrIzpHF7qiTDj0= github.com/zeebo/pcg v1.0.0/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228 h1:Gi6rj4mAlK0BJIk1HIzBVMjWNjIUfstrsXC2VqLYPcA= gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228/go.mod h1:H6jztdm0k+wEV2QGK/KYA+MY9nj9Zzatux/qIvDDv3k= +gitlab.com/elixxir/bloomfilter v0.0.0-20210120224144-ac046983a17a h1:0vLmGrqRDlaru89aKQPk3MyRPUInFujpqnVspBA6QTQ= +gitlab.com/elixxir/bloomfilter v0.0.0-20210120224144-ac046983a17a/go.mod h1:H6jztdm0k+wEV2QGK/KYA+MY9nj9Zzatux/qIvDDv3k= +gitlab.com/elixxir/comms v0.0.3/go.mod h1:5p7oz4yFrK037rPap6ooaWrloJrzuVZ4jnzOdvgyqnU= gitlab.com/elixxir/comms v0.0.4-0.20201217200754-6259dc49e6f1 h1:4iuAA/I8/aQ1Jn3gBguuR1u+LVy3YyShxpoNcqApaVg= gitlab.com/elixxir/comms v0.0.4-0.20201217200754-6259dc49e6f1/go.mod h1:2sNUHm725vQG4pG1RtvMd7kJ5CNqFb7Rl9cenuQCa2c= gitlab.com/elixxir/comms v0.0.4-0.20201229200853-c403d72e877c h1:YjTlUbZiNiJdL7Fy4TIUoWlHNK4dFOtuJ40YgsG7fac= @@ -279,10 +282,22 @@ gitlab.com/elixxir/comms v0.0.4-0.20210112234945-18c36b2d908f h1:EsCG5+sB1ZapIBY gitlab.com/elixxir/comms v0.0.4-0.20210112234945-18c36b2d908f/go.mod h1:R2Ank04m99uGRhKOssWzITqN47AT+EOyG2OiCHLGroE= gitlab.com/elixxir/comms v0.0.4-0.20210114174157-1306832d440b h1:LdlL28odDDoQbhpgF7jUR3x2TXG1P4TzGfVBg2Md6Ek= gitlab.com/elixxir/comms v0.0.4-0.20210114174157-1306832d440b/go.mod h1:R2Ank04m99uGRhKOssWzITqN47AT+EOyG2OiCHLGroE= +gitlab.com/elixxir/comms v0.0.4-0.20210115185903-5dba75967ad3 h1:QJLHqjl35tV/BmFqY+auK6wrmjTdRh+IO/fXQA56XIg= +gitlab.com/elixxir/comms v0.0.4-0.20210115185903-5dba75967ad3/go.mod h1:s+wIMGLQWeIiOY+LYmGciAkcM9whr+CWQNzf8qb+Oao= +gitlab.com/elixxir/comms v0.0.4-0.20210121204920-12c3111eff14 h1:/5w3IE06wUF35/h8x6SHM8zZUcrQw2VgBjhmAcJjDow= +gitlab.com/elixxir/comms v0.0.4-0.20210121204920-12c3111eff14/go.mod h1:oJPQZEobUQwmype16+Hl9s2l+k3luL4ViUKxj0S4Dp8= +gitlab.com/elixxir/comms v0.0.4-0.20210121222442-b714136536e9 h1:9jQb6bynSFVw9jDKX8t71V7EEwKkDtxzDxMaxw7+hYc= +gitlab.com/elixxir/comms v0.0.4-0.20210121222442-b714136536e9/go.mod h1:oJPQZEobUQwmype16+Hl9s2l+k3luL4ViUKxj0S4Dp8= +gitlab.com/elixxir/comms v0.0.4-0.20210126185553-8ccfd64bf81b h1:ENB2YHpF72bbVHA6GG8BrMXEb+si9JHhO39vx7vk7hA= +gitlab.com/elixxir/comms v0.0.4-0.20210126185553-8ccfd64bf81b/go.mod h1:5hxGwa/8BEpo4cZrbxhxyXPpXmwzMPunKE/H141+rPU= +gitlab.com/elixxir/comms v0.0.4-0.20210208181740-1ad1139f7170 h1:YD0QgSP5puQXn3bMOJuA7sT7DXLCVgHBW//PSESU45o= +gitlab.com/elixxir/comms v0.0.4-0.20210208181740-1ad1139f7170/go.mod h1:fWuPOszadMhHLOywy2+mMSMH00k9sh/zw/povSWurn4= gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4 h1:28ftZDeYEko7xptCZzeFWS1Iam95dj46TWFVVlKmw6A= gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4/go.mod h1:ucm9SFKJo+K0N2GwRRpaNr+tKXMIOVWzmyUD0SbOu2c= gitlab.com/elixxir/crypto v0.0.3 h1:znCt/x2bL4y8czTPaaFkwzdgSgW3BJc/1+dxyf1jqVw= gitlab.com/elixxir/crypto v0.0.3/go.mod h1:ZNgBOblhYToR4m8tj4cMvJ9UsJAUKq+p0gCp07WQmhA= +gitlab.com/elixxir/crypto v0.0.4/go.mod h1:ZNgBOblhYToR4m8tj4cMvJ9UsJAUKq+p0gCp07WQmhA= +gitlab.com/elixxir/crypto v0.0.6 h1:c94CGzBTV7LgInGHfmeJHrqq9nIc/WEOLUd9OeQBN74= gitlab.com/elixxir/crypto v0.0.7-0.20201217200352-0ba771a66932/go.mod h1:nqSNe486j6ua96nv1et6x2ESl/qXevkx7f31GowMRh4= gitlab.com/elixxir/crypto v0.0.7-0.20201222203132-9b4cc1ae3da6 h1:Vyf2wJ1/CoHLznATpI+z84OJQ+sgAbpVLT9P1CNjSVI= gitlab.com/elixxir/crypto v0.0.7-0.20201222203132-9b4cc1ae3da6/go.mod h1:NsIzH+TN592lMcmJGVzxUE64dObXuci7jo0qajyGahI= @@ -290,6 +305,18 @@ gitlab.com/elixxir/crypto v0.0.7-0.20210104223925-7dfd3ad55d5c h1:/iLLZvu5mbFXxc gitlab.com/elixxir/crypto v0.0.7-0.20210104223925-7dfd3ad55d5c/go.mod h1:cEn3ghYlk7Fhz1dOu3OEw9v9nIZNYH6hqnjHlTLxFiA= gitlab.com/elixxir/crypto v0.0.7-0.20210107184400-5c3e52a35758 h1:h2l8SZbdgcyMVwgcMqBgAoJSqI1CC23MChB5k7xqqRI= gitlab.com/elixxir/crypto v0.0.7-0.20210107184400-5c3e52a35758/go.mod h1:JDC7EFs5V97qourZyiSHBiiXHGUoKWX9XtJVhfEvpC4= +gitlab.com/elixxir/crypto v0.0.7-0.20210113224347-cc4926b30fba h1:GtjhegVf6L9MZ6gp2oeBo/oVYUVB/IO91xBycF/jcNo= +gitlab.com/elixxir/crypto v0.0.7-0.20210113224347-cc4926b30fba/go.mod h1:JDC7EFs5V97qourZyiSHBiiXHGUoKWX9XtJVhfEvpC4= +gitlab.com/elixxir/crypto v0.0.7-0.20210114172156-b576829ba7d8 h1:KhhsuPn+aPzZ66jdbbRF55SurogvQ/X9KuMWkXtmq+U= +gitlab.com/elixxir/crypto v0.0.7-0.20210114172156-b576829ba7d8/go.mod h1:4AumkGNppiEaMo6bIpFBrV6wZtaIjpbvkpA5+FBisA8= +gitlab.com/elixxir/crypto v0.0.7-0.20210115231025-5d9b0dbc6985 h1:Csf3dIhwA6D293aURIHKuvwZr4HgIRzJ4FpCklmXEmA= +gitlab.com/elixxir/crypto v0.0.7-0.20210115231025-5d9b0dbc6985/go.mod h1:JDC7EFs5V97qourZyiSHBiiXHGUoKWX9XtJVhfEvpC4= +gitlab.com/elixxir/crypto v0.0.7-0.20210121204803-2caab60ff687 h1:tBlYA/jUOquWpe7bGZBQtvngbfbHsyt11aKZ761++8k= +gitlab.com/elixxir/crypto v0.0.7-0.20210121204803-2caab60ff687/go.mod h1:7egP0+qiVyZnqcBlRr4rL0FhGNkqDCNEygungCAp7NM= +gitlab.com/elixxir/crypto v0.0.7-0.20210125231257-b52a556be893 h1:0NjQOwdGO/xBvgBMzRLSFXpSpnluzjT16obl5AcGfmI= +gitlab.com/elixxir/crypto v0.0.7-0.20210125231257-b52a556be893/go.mod h1:BoYrgHnTJPvvd4f/f4A+y22wwgZ7IxkYtGC8x9WiwxA= +gitlab.com/elixxir/crypto v0.0.7-0.20210208181828-64b4b57e23d6 h1:Q1Ldvj70eM60MLaHgSXE7C95uTkn5utCq+bMCY2pyrA= +gitlab.com/elixxir/crypto v0.0.7-0.20210208181828-64b4b57e23d6/go.mod h1:JDC7EFs5V97qourZyiSHBiiXHGUoKWX9XtJVhfEvpC4= gitlab.com/elixxir/ekv v0.1.4-0.20201217220111-9c7e8be21577 h1:qT3ZO9GbKv7PcmakATsXsUdzn/hlm817ETOT/csDIak= gitlab.com/elixxir/ekv v0.1.4-0.20201217220111-9c7e8be21577/go.mod h1:e6WPUt97taFZe5PFLPb1Dupk7tqmDCTQu1kkstqJvw4= gitlab.com/elixxir/ekv v0.1.4 h1:NLVMwsFEKArWcsDHu2DbXlm9374iSgn7oIA3rVSsvjc= @@ -299,6 +326,7 @@ gitlab.com/elixxir/primitives v0.0.0-20200804170709-a1896d262cd9/go.mod h1:p0Vel gitlab.com/elixxir/primitives v0.0.0-20200804182913-788f47bded40/go.mod h1:tzdFFvb1ESmuTCOl1z6+yf6oAICDxH2NPUemVgoNLxc= gitlab.com/elixxir/primitives v0.0.1 h1:q61anawANlNAExfkeQEE1NCsNih6vNV1FFLoUQX6txQ= gitlab.com/elixxir/primitives v0.0.1/go.mod h1:kNp47yPqja2lHSiS4DddTvFpB/4D9dB2YKnw5c+LJCE= +gitlab.com/elixxir/primitives v0.0.2/go.mod h1:3fxFHSlQhkV4vs+S0dZEz3Om3m+40WX8L806yvSnNFc= gitlab.com/elixxir/primitives v0.0.3-0.20201217193438-8ebfc882f1de/go.mod h1:H1OZ6ZXzTB3G4nOEdJzBJ7BySRnivpJTkTphxazFCl4= gitlab.com/elixxir/primitives v0.0.3-0.20201222171449-bdfe30389bb5 h1:BUV1ouY+9NA5+ZF+QfTpIBL1vO5zk31I68N7DX9bKj8= gitlab.com/elixxir/primitives v0.0.3-0.20201222171449-bdfe30389bb5/go.mod h1:5Z8fce94mks0aFhMvzftx2jJpDx4/pzDU20G2oTiU9w= @@ -310,7 +338,17 @@ gitlab.com/elixxir/primitives v0.0.3-0.20210106014507-bf3dfe228fa6 h1:sUqEla1uUI gitlab.com/elixxir/primitives v0.0.3-0.20210106014507-bf3dfe228fa6/go.mod h1:Ph6isHUDVjmRUd9DioyKpd8W9J52gKBiDeue4DCygXA= gitlab.com/elixxir/primitives v0.0.3-0.20210107183456-9cf6fe2de1e5 h1:50HbCJWirpX2Q+NNhIHcs0M9f45H1UJ/7LNMu81Bnn0= gitlab.com/elixxir/primitives v0.0.3-0.20210107183456-9cf6fe2de1e5/go.mod h1:Ph6isHUDVjmRUd9DioyKpd8W9J52gKBiDeue4DCygXA= +gitlab.com/elixxir/primitives v0.0.3-0.20210121204717-e15ada7f0a73 h1:9h+pCc1ceIWDR1CM5sdoV7KHvNYzKQRJjeIfc26fyDU= +gitlab.com/elixxir/primitives v0.0.3-0.20210121204717-e15ada7f0a73/go.mod h1:bGYfAGerVVZhNzh+PxOMZVG7iYAUfInYvtdAkAqgiY8= +gitlab.com/elixxir/primitives v0.0.3-0.20210122185056-ad244787d961 h1:BUN6bUBmTeebwYvTQ5yVi1iJlNAhjR7kEndcsV339dE= +gitlab.com/elixxir/primitives v0.0.3-0.20210122185056-ad244787d961/go.mod h1:bGYfAGerVVZhNzh+PxOMZVG7iYAUfInYvtdAkAqgiY8= +gitlab.com/elixxir/primitives v0.0.3-0.20210125225949-9469ce6b08fc/go.mod h1:bGYfAGerVVZhNzh+PxOMZVG7iYAUfInYvtdAkAqgiY8= +gitlab.com/elixxir/primitives v0.0.3-0.20210126180712-bfbfb725c75a h1:3jq90Nmn8Ew9vfUuizV4mvj6py5LOmX3wkuBc+ywJ0w= +gitlab.com/elixxir/primitives v0.0.3-0.20210126180712-bfbfb725c75a/go.mod h1:bGYfAGerVVZhNzh+PxOMZVG7iYAUfInYvtdAkAqgiY8= +gitlab.com/elixxir/primitives v0.0.3-0.20210127201240-6a42ad925e8a h1:ZQncDfITNE12EdJK+shh6UzHlALhNU4Zjvv4hid2krs= +gitlab.com/elixxir/primitives v0.0.3-0.20210127201240-6a42ad925e8a/go.mod h1:Ph6isHUDVjmRUd9DioyKpd8W9J52gKBiDeue4DCygXA= gitlab.com/xx_network/comms v0.0.0-20200805174823-841427dd5023/go.mod h1:owEcxTRl7gsoM8c3RQ5KAm5GstxrJp5tn+6JfQ4z5Hw= +gitlab.com/xx_network/comms v0.0.3/go.mod h1:YViGbRj7FjJYoaO4NpALGEd9dK/l8uUT000FEBbUTL8= gitlab.com/xx_network/comms v0.0.4-0.20201130190834-365ddae56e7b/go.mod h1:YViGbRj7FjJYoaO4NpALGEd9dK/l8uUT000FEBbUTL8= gitlab.com/xx_network/comms v0.0.4-0.20201217200138-87075d5b4ffd h1:4LjS3UuBNA/AaglIJ+k1IBoxYgCWt+FM1MPYxjAFfaQ= gitlab.com/xx_network/comms v0.0.4-0.20201217200138-87075d5b4ffd/go.mod h1:/vIk6tSrDqk/7HZOdrbSXZT+kEL43HIoz60AoZTzTXg= @@ -322,6 +360,10 @@ gitlab.com/xx_network/comms v0.0.4-0.20210107184305-7bb439c08ded h1:VHAQcap/+jcF gitlab.com/xx_network/comms v0.0.4-0.20210107184305-7bb439c08ded/go.mod h1:12OZe2Ol9lEk7JTm48GQ4zj1O01SH0ygfLqOb8EHrtU= gitlab.com/xx_network/comms v0.0.4-0.20210112233928-eac8db03c397 h1:E7p5lGX+nTAIKB6Wno3mRxBfC+SX5ZY2FKp8JEMzdSM= gitlab.com/xx_network/comms v0.0.4-0.20210112233928-eac8db03c397/go.mod h1:12OZe2Ol9lEk7JTm48GQ4zj1O01SH0ygfLqOb8EHrtU= +gitlab.com/xx_network/comms v0.0.4-0.20210115175102-ad5814bff11c h1:MRxHypL++mTDzVJq503ePBW3aGHE5FTG1ybac8rGK0A= +gitlab.com/xx_network/comms v0.0.4-0.20210115175102-ad5814bff11c/go.mod h1:12OZe2Ol9lEk7JTm48GQ4zj1O01SH0ygfLqOb8EHrtU= +gitlab.com/xx_network/comms v0.0.4-0.20210121204701-7a1eb0542424 h1:zhfkbutYEY7PQuC1kcugCt4am/oRAyla6u4xDc5PqWs= +gitlab.com/xx_network/comms v0.0.4-0.20210121204701-7a1eb0542424/go.mod h1:MNkja6iUsM2yjBQicwLUGYctZmAU/D4AMpDD04vfBg8= gitlab.com/xx_network/crypto v0.0.3/go.mod h1:DF2HYvvCw9wkBybXcXAgQMzX+MiGbFPjwt3t17VRqRE= gitlab.com/xx_network/crypto v0.0.4 h1:lpKOL5mTJ2awWMfgBy30oD/UvJVrWZzUimSHlOdZZxo= gitlab.com/xx_network/crypto v0.0.4/go.mod h1:+lcQEy+Th4eswFgQDwT0EXKp4AXrlubxalwQFH5O0Mk= @@ -334,6 +376,8 @@ gitlab.com/xx_network/crypto v0.0.5-0.20210106014410-0554a33a7124 h1:G2fNW7uPzJE gitlab.com/xx_network/crypto v0.0.5-0.20210106014410-0554a33a7124/go.mod h1:tTYlvAZNUFMtndcijM37iujkM4gP8kUwGiS9n7NA5Ck= gitlab.com/xx_network/crypto v0.0.5-0.20210107183440-804e0f8b7d22 h1:SQga1vOuAGzbYURnBjG0f37r7WmWKZRdiqmdopaNMb4= gitlab.com/xx_network/crypto v0.0.5-0.20210107183440-804e0f8b7d22/go.mod h1:tTYlvAZNUFMtndcijM37iujkM4gP8kUwGiS9n7NA5Ck= +gitlab.com/xx_network/crypto v0.0.5-0.20210121204626-b251b926e4f7 h1:eraxUmI0EFDtfqTV4Ojndoh8Bv8PBUbZaT27VLU1yhc= +gitlab.com/xx_network/crypto v0.0.5-0.20210121204626-b251b926e4f7/go.mod h1:61fEAGI2+JD6CbT5PMzxy1SBMvgiFxE4MyiQghzoOVg= gitlab.com/xx_network/primitives v0.0.0-20200803231956-9b192c57ea7c/go.mod h1:wtdCMr7DPePz9qwctNoAUzZtbOSHSedcK++3Df3psjA= gitlab.com/xx_network/primitives v0.0.0-20200804183002-f99f7a7284da h1:CCVslUwNC7Ul7NG5nu3ThGTSVUt1TxNRX+47f5TUwnk= gitlab.com/xx_network/primitives v0.0.0-20200804183002-f99f7a7284da/go.mod h1:OK9xevzWCaPO7b1wiluVJGk7R5ZsuC7pHY5hteZFQug= @@ -347,8 +391,25 @@ gitlab.com/xx_network/primitives v0.0.4-0.20201229212313-fe33d9809f27 h1:JGpYRKk gitlab.com/xx_network/primitives v0.0.4-0.20201229212313-fe33d9809f27/go.mod h1:cs0QlFpdMDI6lAo61lDRH2JZz+3aVkHy+QogOB6F/qc= gitlab.com/xx_network/primitives v0.0.4-0.20210106014326-691ebfca3b07 h1:ZxGp7Q0Vyx2mWM84GIPPT3PZK2TLfwycSIk7EgNMIws= gitlab.com/xx_network/primitives v0.0.4-0.20210106014326-691ebfca3b07/go.mod h1:cs0QlFpdMDI6lAo61lDRH2JZz+3aVkHy+QogOB6F/qc= -gitlab.com/xx_network/primitives v0.0.4-0.20210114170718-1549f24d462c h1:RjDklUt70MgcVqBoJvWdBoygkizoByv6Q6DsZSqcFSI= gitlab.com/xx_network/primitives v0.0.4-0.20210114170718-1549f24d462c/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE= +gitlab.com/xx_network/primitives v0.0.4-0.20210118193646-93176e2e6925 h1:n40/5N6aXnye+QmkqKCnEEEepYw4lN9uQ/mhAyCyA3o= +gitlab.com/xx_network/primitives v0.0.4-0.20210118193646-93176e2e6925/go.mod h1:cs0QlFpdMDI6lAo61lDRH2JZz+3aVkHy+QogOB6F/qc= +gitlab.com/xx_network/primitives v0.0.4-0.20210121203635-8a771fc14f8a h1:ZDv8RHUsl9pjIkgqd8u6y00P+6TXEASwBg9dpvHSsT0= +gitlab.com/xx_network/primitives v0.0.4-0.20210121203635-8a771fc14f8a/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE= +gitlab.com/xx_network/primitives v0.0.4-0.20210127181825-f9178f9d19b5 h1:dI/3SkJie/Twy4ZZnsNncyiLI6KHJboaRagSU5V96YY= +gitlab.com/xx_network/primitives v0.0.4-0.20210127181825-f9178f9d19b5/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE= +gitlab.com/xx_network/primitives v0.0.4-0.20210129181607-5b719add9171 h1:9nvaBYeOH/f8Ev/1b3IhoJdrxXaQZ5Lb/tFe1GzqH00= +gitlab.com/xx_network/primitives v0.0.4-0.20210129181607-5b719add9171/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE= +gitlab.com/xx_network/primitives v0.0.4-0.20210201174745-f294e495260d h1:tXEJOJq6rIizJyo/fgs6G17isq9UMahHx6BPtXgheLg= +gitlab.com/xx_network/primitives v0.0.4-0.20210201174745-f294e495260d/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE= +gitlab.com/xx_network/primitives v0.0.4-0.20210201180649-48711cee9127 h1:DYYHQeNyh/6Cj3swZYy8bAYFL4yqxedu2dFZMDt8D3M= +gitlab.com/xx_network/primitives v0.0.4-0.20210201180649-48711cee9127/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE= +gitlab.com/xx_network/primitives v0.0.4-0.20210201180820-639f0a366d52 h1:gZk8XTQOAFt3IUd2LUsWWYpQQz5ktQF5uD+gV4pu4e8= +gitlab.com/xx_network/primitives v0.0.4-0.20210201180820-639f0a366d52/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE= +gitlab.com/xx_network/primitives v0.0.4-0.20210202001929-8bf8bf35a234 h1:OXR+c++r+IsFu4OKEMY9pJrZFCTp/qt8irEdCgNj4Hw= +gitlab.com/xx_network/primitives v0.0.4-0.20210202001929-8bf8bf35a234/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE= +gitlab.com/xx_network/primitives v0.0.4-0.20210208183356-ee1e9ec13f8f h1:21mljvQ0ZPNZbOP0BG5JNGlDgiC/vg9NZ/AaseXSxBI= +gitlab.com/xx_network/primitives v0.0.4-0.20210208183356-ee1e9ec13f8f/go.mod h1:9imZHvYwNFobxueSvVtHneZLk9wTK7HQTzxPm+zhFhE= gitlab.com/xx_network/ring v0.0.2 h1:TlPjlbFdhtJrwvRgIg4ScdngMTaynx/ByHBRZiXCoL0= gitlab.com/xx_network/ring v0.0.2/go.mod h1:aLzpP2TiZTQut/PVHR40EJAomzugDdHXetbieRClXIM= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -524,6 +585,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= diff --git a/interfaces/bloom.go b/interfaces/bloom.go new file mode 100644 index 0000000000000000000000000000000000000000..abd84fcb8ebb590ead20b20ebcb6f62b9827d329 --- /dev/null +++ b/interfaces/bloom.go @@ -0,0 +1,4 @@ +package interfaces + +const BloomFilterSize = 904 // In Bits +const BloomFilterHashes = 10 diff --git a/interfaces/contact/contact_test.go b/interfaces/contact/contact_test.go index 97af5f2f40d6072f696b584c88ebd6710481e24c..b87f983ca8d055de6da893d23f9d8ab8382c01a8 100644 --- a/interfaces/contact/contact_test.go +++ b/interfaces/contact/contact_test.go @@ -11,6 +11,7 @@ import ( "crypto" "encoding/base64" "encoding/json" + "fmt" "gitlab.com/elixxir/crypto/cyclic" "gitlab.com/elixxir/primitives/fact" "gitlab.com/xx_network/crypto/csprng" @@ -37,6 +38,8 @@ func TestContact_Marshal_Unmarshal(t *testing.T) { buff := expectedContact.Marshal() + fmt.Println(string(buff)) + testContact, err := Unmarshal(buff) if err != nil { t.Errorf("Unmarshal() produced an error: %+v", err) diff --git a/interfaces/message/receiveMessage.go b/interfaces/message/receiveMessage.go index faa61d61f0a375eae5506c55b10dfb7b99b83a32..f5373e716513a10a85d8a168b690b8a8beefbbf9 100644 --- a/interfaces/message/receiveMessage.go +++ b/interfaces/message/receiveMessage.go @@ -10,6 +10,7 @@ package message import ( "gitlab.com/elixxir/crypto/e2e" "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/id/ephemeral" "time" ) @@ -18,6 +19,8 @@ type Receive struct { Payload []byte MessageType Type Sender *id.ID + RecipientID *id.ID + EphemeralID ephemeral.Id Timestamp time.Time Encryption EncryptionType } diff --git a/interfaces/networkManager.go b/interfaces/networkManager.go index 541dc5b147d64440b3507ec28f72f735086cd37b..b7ff3a886346a0d23934b6ef359db4e872101415 100644 --- a/interfaces/networkManager.go +++ b/interfaces/networkManager.go @@ -15,12 +15,13 @@ import ( "gitlab.com/elixxir/crypto/e2e" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/id/ephemeral" ) type NetworkManager interface { SendE2E(m message.Send, p params.E2E) ([]id.Round, e2e.MessageID, error) SendUnsafe(m message.Send, p params.Unsafe) ([]id.Round, error) - SendCMIX(message format.Message, p params.CMIX) (id.Round, error) + SendCMIX(message format.Message, recipient *id.ID, p params.CMIX) (id.Round, ephemeral.Id, error) GetInstance() *network.Instance GetHealthTracker() HealthTracker Follow() (stoppable.Stoppable, error) diff --git a/keyExchange/utils_test.go b/keyExchange/utils_test.go index 4fc72a604e50891d56c748a9c84272b7e7c1b2aa..c11728b1afd66ace0983fce8dcf2134978f35150 100644 --- a/keyExchange/utils_test.go +++ b/keyExchange/utils_test.go @@ -26,6 +26,7 @@ import ( "gitlab.com/xx_network/comms/connect" "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" "testing" "time" @@ -76,9 +77,9 @@ func (t *testNetworkManagerGeneric) SendUnsafe(m message.Send, p params.Unsafe) return nil, nil } -func (t *testNetworkManagerGeneric) SendCMIX(message format.Message, p params.CMIX) (id.Round, error) { +func (t *testNetworkManagerGeneric) SendCMIX(message format.Message, rid *id.ID, p params.CMIX) (id.Round, ephemeral.Id, error) { - return id.Round(0), nil + return id.Round(0), ephemeral.Id{}, nil } @@ -183,9 +184,9 @@ func (t *testNetworkManagerFullExchange) SendUnsafe(m message.Send, p params.Uns return nil, nil } -func (t *testNetworkManagerFullExchange) SendCMIX(message format.Message, p params.CMIX) (id.Round, error) { +func (t *testNetworkManagerFullExchange) SendCMIX(message format.Message, eid *id.ID, p params.CMIX) (id.Round, ephemeral.Id, error) { - return id.Round(0), nil + return id.Round(0), ephemeral.Id{}, nil } diff --git a/network/ephemeral/testutil.go b/network/ephemeral/testutil.go new file mode 100644 index 0000000000000000000000000000000000000000..1691ae8bd450fd70cd03e0f8f8c00733c16ec828 --- /dev/null +++ b/network/ephemeral/testutil.go @@ -0,0 +1,145 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package ephemeral + +import ( + "gitlab.com/elixxir/client/globals" + "gitlab.com/elixxir/client/interfaces" + "gitlab.com/elixxir/client/interfaces/message" + "gitlab.com/elixxir/client/interfaces/params" + "gitlab.com/elixxir/client/stoppable" + "gitlab.com/elixxir/comms/network" + "gitlab.com/elixxir/comms/testkeys" + "gitlab.com/elixxir/crypto/e2e" + "gitlab.com/elixxir/primitives/format" + "gitlab.com/xx_network/comms/connect" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/id/ephemeral" + "gitlab.com/xx_network/primitives/ndf" + "gitlab.com/xx_network/primitives/utils" + "testing" +) + +// testNetworkManager is a test implementation of NetworkManager interface. +type testNetworkManager struct { + instance *network.Instance + msg message.Send +} + +func (t *testNetworkManager) SendE2E(m message.Send, _ params.E2E) ([]id.Round, + e2e.MessageID, error) { + rounds := []id.Round{ + id.Round(0), + id.Round(1), + id.Round(2), + } + + t.msg = m + + return rounds, e2e.MessageID{}, nil +} + +func (t *testNetworkManager) SendUnsafe(m message.Send, _ params.Unsafe) ([]id.Round, error) { + rounds := []id.Round{ + id.Round(0), + id.Round(1), + id.Round(2), + } + + t.msg = m + + return rounds, nil +} + +func (t *testNetworkManager) SendCMIX(format.Message, *id.ID, params.CMIX) (id.Round, ephemeral.Id, error) { + return 0, ephemeral.Id{}, nil +} + +func (t *testNetworkManager) GetInstance() *network.Instance { + return t.instance +} + +func (t *testNetworkManager) GetHealthTracker() interfaces.HealthTracker { + return nil +} + +func (t *testNetworkManager) Follow() (stoppable.Stoppable, error) { + return nil, nil +} + +func (t *testNetworkManager) CheckGarbledMessages() {} + +func NewTestNetworkManager(i interface{}) interfaces.NetworkManager { + switch i.(type) { + case *testing.T, *testing.M, *testing.B: + break + default: + globals.Log.FATAL.Panicf("initTesting is restricted to testing only."+ + "Got %T", i) + } + + commsManager := connect.NewManagerTesting(i) + + cert, err := utils.ReadFile(testkeys.GetNodeCertPath()) + if err != nil { + globals.Log.FATAL.Panicf("Failed to create new test Instance: %v", err) + } + + commsManager.AddHost(&id.Permissioning, "", cert, connect.GetDefaultHostParams()) + instanceComms := &connect.ProtoComms{ + Manager: commsManager, + } + + thisInstance, err := network.NewInstanceTesting(instanceComms, getNDF(), getNDF(), nil, nil, i) + if err != nil { + globals.Log.FATAL.Panicf("Failed to create new test Instance: %v", err) + } + + thisManager := &testNetworkManager{instance: thisInstance} + + return thisManager +} + +func getNDF() *ndf.NetworkDefinition { + return &ndf.NetworkDefinition{ + E2E: ndf.Group{ + Prime: "E2EE983D031DC1DB6F1A7A67DF0E9A8E5561DB8E8D49413394C049B" + + "7A8ACCEDC298708F121951D9CF920EC5D146727AA4AE535B0922C688B55B3DD2AE" + + "DF6C01C94764DAB937935AA83BE36E67760713AB44A6337C20E7861575E745D31F" + + "8B9E9AD8412118C62A3E2E29DF46B0864D0C951C394A5CBBDC6ADC718DD2A3E041" + + "023DBB5AB23EBB4742DE9C1687B5B34FA48C3521632C4A530E8FFB1BC51DADDF45" + + "3B0B2717C2BC6669ED76B4BDD5C9FF558E88F26E5785302BEDBCA23EAC5ACE9209" + + "6EE8A60642FB61E8F3D24990B8CB12EE448EEF78E184C7242DD161C7738F32BF29" + + "A841698978825B4111B4BC3E1E198455095958333D776D8B2BEEED3A1A1A221A6E" + + "37E664A64B83981C46FFDDC1A45E3D5211AAF8BFBC072768C4F50D7D7803D2D4F2" + + "78DE8014A47323631D7E064DE81C0C6BFA43EF0E6998860F1390B5D3FEACAF1696" + + "015CB79C3F9C2D93D961120CD0E5F12CBB687EAB045241F96789C38E89D796138E" + + "6319BE62E35D87B1048CA28BE389B575E994DCA755471584A09EC723742DC35873" + + "847AEF49F66E43873", + Generator: "2", + }, + CMIX: ndf.Group{ + Prime: "9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642F0B5C48" + + "C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757264E5A1A44F" + + "FE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F9716BFE6117C6B5" + + "B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091EB51743BF33050C38DE2" + + "35567E1B34C3D6A5C0CEAA1A0F368213C3D19843D0B4B09DCB9FC72D39C8DE41" + + "F1BF14D4BB4563CA28371621CAD3324B6A2D392145BEBFAC748805236F5CA2FE" + + "92B871CD8F9C36D3292B5509CA8CAA77A2ADFC7BFD77DDA6F71125A7456FEA15" + + "3E433256A2261C6A06ED3693797E7995FAD5AABBCFBE3EDA2741E375404AE25B", + Generator: "5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E24809670716C613" + + "D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D1AA58C4328A06C4" + + "6A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A338661D10461C0D135472" + + "085057F3494309FFA73C611F78B32ADBB5740C361C9F35BE90997DB2014E2EF5" + + "AA61782F52ABEB8BD6432C4DD097BC5423B285DAFB60DC364E8161F4A2A35ACA" + + "3A10B1C4D203CC76A470A33AFDCBDD92959859ABD8B56E1725252D78EAC66E71" + + "BA9AE3F1DD2487199874393CD4D832186800654760E1E34C09E4D155179F9EC0" + + "DC4473F996BDCE6EED1CABED8B6F116F7AD9CF505DF0F998E34AB27514B0FFE7", + }, + } +} diff --git a/network/ephemeral/tracker.go b/network/ephemeral/tracker.go new file mode 100644 index 0000000000000000000000000000000000000000..d9b4d029f7b2da3fba18b078e97404c11a66afe9 --- /dev/null +++ b/network/ephemeral/tracker.go @@ -0,0 +1,188 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package ephemeral + +import ( + "github.com/pkg/errors" + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/globals" + "gitlab.com/elixxir/client/stoppable" + "gitlab.com/elixxir/client/storage" + "gitlab.com/elixxir/client/storage/reception" + "gitlab.com/elixxir/client/storage/versioned" + "gitlab.com/elixxir/comms/network" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/id/ephemeral" + "time" +) + +const validityGracePeriod = 5 * time.Minute +const TimestampKey = "IDTrackingTimestamp" +const ephemeralStoppable = "EphemeralCheck" + +// Track runs a thread which checks for past and present ephemeral ids +func Track(session *storage.Session, instance *network.Instance, ourId *id.ID) stoppable.Stoppable { + stop := stoppable.NewSingle(ephemeralStoppable) + + go track(session, instance, ourId, stop) + + return stop +} + +// track is a thread which continuously processes ephemeral ids. +// If any error occurs, the thread crashes +func track(session *storage.Session, instance *network.Instance, ourId *id.ID, stop *stoppable.Single) { + identityStore := session.Reception() + + // Check that there is a timestamp in store at all + err := checkTimestampStore(session) + if err != nil { + jww.FATAL.Panicf("Could not store timestamp "+ + "for ephemeral ID tracking: %v", err) + } + + // Get the latest timestamp from store + lastTimestampObj, err := session.Get(TimestampKey) + if err != nil { + globals.Log.FATAL.Panicf("Could not get timestamp: %v", err) + } + + lastCheck, err := unmarshalTimestamp(lastTimestampObj) + if err != nil { + globals.Log.FATAL.Panicf("Could not parse stored timestamp: %v", err) + } + + for true { + now := time.Now() + + // Generates the IDs since the last track + protoIds, err := ephemeral.GetIdsByRange(ourId, session.Reception().GetIDSize(), + now.UnixNano(), now.Sub(lastCheck)) + + if err != nil { + globals.Log.FATAL.Panicf("Could not generate "+ + "upcoming IDs: %v", err) + } + + // Generate identities off of that list + identities := generateIdentities(protoIds, ourId) + + // Add identities to storage if unique + for _, identity := range identities { + // Track if identity has been generated already + if identity.StartValid.After(lastCheck) { + // If not not, insert identity into store + if err = identityStore.AddIdentity(identity); err != nil { + globals.Log.FATAL.Panicf("Could not insert "+ + "identity: %v", err) + } + } + + } + + // Generate the time stamp for storage + vo, err := marshalTimestamp(now) + if err != nil { + globals.Log.FATAL.Panicf("Could not marshal "+ + "timestamp for storage: %v", err) + + } + + // Store the timestamp + if err = session.Set(TimestampKey, vo); err != nil { + globals.Log.FATAL.Panicf("Could not store timestamp: %v", err) + } + + // Sleep until the last Id has expired + timeToSleep := calculateTickerTime(protoIds) + t := time.NewTimer(timeToSleep) + select { + case <-t.C: + case <-stop.Quit(): + return + } + } +} + +// generateIdentities is a constructor which generates a list of +// identities off of the list of protoIdentities passed in +func generateIdentities(protoIds []ephemeral.ProtoIdentity, + ourId *id.ID) []reception.Identity { + + identities := make([]reception.Identity, 0) + + // Add identities for every ephemeral id + for _, eid := range protoIds { + // Expand the grace period for both start and end + eid.End.Add(validityGracePeriod) + eid.Start.Add(-validityGracePeriod) + identities = append(identities, reception.Identity{ + EphId: eid.Id, + Source: ourId, + End: eid.End, + StartValid: eid.Start, + EndValid: eid.End, + Ephemeral: false, + }) + + } + + return identities +} + +// Sanitation check of timestamp store. If a value has not been stored yet +// then the current time is stored +func checkTimestampStore(session *storage.Session) error { + if _, err := session.Get(TimestampKey); err != nil { + now, err := marshalTimestamp(time.Now()) + if err != nil { + return errors.Errorf("Could not marshal new timestamp for storage: %v", err) + } + return session.Set(TimestampKey, now) + } + + return nil +} + +// Takes the stored timestamp and unmarshal into a time object +func unmarshalTimestamp(lastTimestampObj *versioned.Object) (time.Time, error) { + if lastTimestampObj == nil || lastTimestampObj.Data == nil { + return time.Now(), nil + } + + lastTimestamp := time.Time{} + err := lastTimestamp.UnmarshalBinary(lastTimestampObj.Data) + return lastTimestamp, err +} + +// Marshals the timestamp for ekv storage. Generates a storable object +func marshalTimestamp(timeToStore time.Time) (*versioned.Object, error) { + data, err := timeToStore.MarshalBinary() + + return &versioned.Object{ + Version: 0, + Timestamp: time.Now(), + Data: data, + }, err +} + +// Helper function which calculates the time for the ticker based +// off of the last ephemeral ID to expire +func calculateTickerTime(baseIDs []ephemeral.ProtoIdentity) time.Duration { + // Get the last identity in the list + index := 0 + if len(baseIDs)-1 > 0 { + index = len(baseIDs) - 1 + } + lastIdentity := baseIDs[index] + + // Factor out the grace period previously expanded upon. + // Calculate and return that duration + gracePeriod := lastIdentity.End.Add(-validityGracePeriod) + return lastIdentity.End.Sub(gracePeriod) +} diff --git a/network/ephemeral/tracker_test.go b/network/ephemeral/tracker_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b61d78841afb804fd62be71c0cb786ed382518b2 --- /dev/null +++ b/network/ephemeral/tracker_test.go @@ -0,0 +1,149 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright © 2020 xx network SEZC // +// // +// Use of this source code is governed by a license that can be found in the // +// LICENSE file // +/////////////////////////////////////////////////////////////////////////////// + +package ephemeral + +import ( + "github.com/pkg/errors" + "gitlab.com/elixxir/client/interfaces" + "gitlab.com/elixxir/client/stoppable" + "gitlab.com/elixxir/client/storage" + "gitlab.com/elixxir/comms/mixmessages" + "gitlab.com/elixxir/comms/testkeys" + "gitlab.com/elixxir/crypto/fastRNG" + "gitlab.com/xx_network/comms/signature" + "gitlab.com/xx_network/crypto/csprng" + "gitlab.com/xx_network/crypto/signature/rsa" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/id/ephemeral" + "gitlab.com/xx_network/primitives/utils" + "testing" + "time" +) + +// Smoke test for Track function +func TestCheck(t *testing.T) { + session := storage.InitTestingSession(t) + instance := NewTestNetworkManager(t) + if err := setupInstance(instance); err != nil { + t.Errorf("Could not set up instance: %v", err) + } + + /// Store a mock initial timestamp the store + now := time.Now() + twoDaysAgo := now.Add(-2 * 24 * time.Hour) + twoDaysTimestamp, err := marshalTimestamp(twoDaysAgo) + if err != nil { + t.Errorf("Could not marshal timestamp for test setup: %v", err) + } + err = session.Set(TimestampKey, twoDaysTimestamp) + if err != nil { + t.Errorf("Could not set mock timestamp for test setup: %v", err) + } + + ourId := id.NewIdFromBytes([]byte("Sauron"), t) + stop := Track(session, instance.GetInstance(), ourId) + + err = stop.Close(3 * time.Second) + if err != nil { + t.Errorf("Could not close thread: %v", err) + } + +} + +// Unit test for track +func TestCheck_Thread(t *testing.T) { + + session := storage.InitTestingSession(t) + instance := NewTestNetworkManager(t) + if err := setupInstance(instance); err != nil { + t.Errorf("Could not set up instance: %v", err) + } + ourId := id.NewIdFromBytes([]byte("Sauron"), t) + stop := stoppable.NewSingle(ephemeralStoppable) + + /// Store a mock initial timestamp the store + now := time.Now() + yesterday := now.Add(-24 * time.Hour) + yesterdayTimestamp, err := marshalTimestamp(yesterday) + if err != nil { + t.Errorf("Could not marshal timestamp for test setup: %v", err) + } + err = session.Set(TimestampKey, yesterdayTimestamp) + if err != nil { + t.Errorf("Could not set mock timestamp for test setup: %v", err) + } + + // Run the tracker + go func() { + track(session, instance.GetInstance(), ourId, stop) + }() + + time.Sleep(1 * time.Second) + + // Manually generate identities + + eids, err := ephemeral.GetIdsByRange(ourId, 64, now.UnixNano(), now.Sub(yesterday)) + if err != nil { + t.Errorf("Could not generate upcoming ids: %v", err) + } + + identities := generateIdentities(eids, ourId) + + rngStreamGen := fastRNG.NewStreamGenerator(12, 3, csprng.NewSystemRNG) + + retrieved, err := session.Reception().GetIdentity(rngStreamGen.GetStream()) + if err != nil { + t.Errorf("Could not retrieve identity: %v", err) + } + + // Check if store has been updated for new identities + if identities[0].String() != retrieved.String() { + t.Errorf("Store was not updated for newly generated identies") + } + + err = stop.Close(3 * time.Second) + if err != nil { + t.Errorf("Could not close thread: %v", err) + } + +} + +func setupInstance(instance interfaces.NetworkManager) error { + cert, err := utils.ReadFile(testkeys.GetNodeKeyPath()) + if err != nil { + return errors.Errorf("Failed to read cert from from file: %v", err) + } + ri := &mixmessages.RoundInfo{ + ID: 1, + AddressSpaceSize: 64, + } + + testCert, err := rsa.LoadPrivateKeyFromPem(cert) + if err != nil { + return errors.Errorf("Failed to load cert from from file: %v", err) + } + if err = signature.Sign(ri, testCert); err != nil { + return errors.Errorf("Failed to sign round info: %v", err) + } + if err = instance.GetInstance().RoundUpdate(ri); err != nil { + return errors.Errorf("Failed to RoundUpdate from from file: %v", err) + } + + ri = &mixmessages.RoundInfo{ + ID: 2, + AddressSpaceSize: 64, + } + if err = signature.Sign(ri, testCert); err != nil { + return errors.Errorf("Failed to sign round info: %v", err) + } + if err = instance.GetInstance().RoundUpdate(ri); err != nil { + return errors.Errorf("Failed to RoundUpdate from from file: %v", err) + } + + return nil +} diff --git a/network/follow.go b/network/follow.go index 2c0d7bc615b1fb8351db09f53d3a5067ff9032af..bad43fe6bff4a923272c0ea6f74e15b55b59de21 100644 --- a/network/follow.go +++ b/network/follow.go @@ -25,8 +25,8 @@ package network import ( "bytes" jww "github.com/spf13/jwalterweatherman" - bloom "gitlab.com/elixxir/bloomfilter" "gitlab.com/elixxir/client/network/gateway" + "gitlab.com/elixxir/client/network/rounds" pb "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/elixxir/primitives/knownRounds" "gitlab.com/elixxir/primitives/states" @@ -36,9 +36,6 @@ import ( "time" ) -const bloomFilterSize = 71888 // In Bits -const bloomFilterHashes = 8 - //comms interface makes testing easier type followNetworkComms interface { GetHost(hostId *id.ID) (*connect.Host, bool) @@ -71,6 +68,13 @@ func (m *manager) follow(rng csprng.Source, comms followNetworkComms) { jww.TRACE.Printf("follow: %d", followCnt) followCnt++ + //get the identity we will poll for + identity, err := m.Session.Reception().GetIdentity(rng) + if err != nil { + jww.FATAL.Panicf("Failed to get an ideneity, this should be "+ + "impossible: %+v", err) + } + //randomly select a gateway to poll //TODO: make this more intelligent gwHost, err := gateway.Get(m.Instance.GetPartialNdf().Get(), comms, rng) @@ -84,8 +88,10 @@ func (m *manager) follow(rng csprng.Source, comms followNetworkComms) { Partial: &pb.NDFHash{ Hash: m.Instance.GetPartialNdf().GetHash(), }, - LastUpdate: uint64(m.Instance.GetLastUpdateID()), - ClientID: m.Uid.Bytes(), + LastUpdate: uint64(m.Instance.GetLastUpdateID()), + ReceptionID: identity.EphId[:], + StartTimestamp: identity.StartRequest.UnixNano(), + EndTimestamp: identity.EndRequest.UnixNano(), } jww.TRACE.Printf("Polling %s for NDF...", gwHost) pollResp, err := comms.SendPoll(gwHost, &pollReq) @@ -94,33 +100,13 @@ func (m *manager) follow(rng csprng.Source, comms followNetworkComms) { return } - // ---- Process Update Data ---- - lastTrackedRound := id.Round(pollResp.LastTrackedRound) + // ---- Process Network State Update Data ---- gwRoundsState := &knownRounds.KnownRounds{} err = gwRoundsState.Unmarshal(pollResp.KnownRounds) if err != nil { jww.ERROR.Printf("Failed to unmarshal: %+v", err) return } - var filterList []*bloom.Ring - for _, f := range pollResp.BloomFilters { - jww.DEBUG.Printf("Bloom Filter size: %d, hashes: %d", - bloomFilterSize, bloomFilterHashes) - filter, err := bloom.InitByParameters(bloomFilterSize, - bloomFilterHashes) - if err != nil { - jww.INFO.Printf("Bloom Filter Data: %v", f) - jww.FATAL.Panicf("Unable to create a bloom filter: %+v", - err) - } - if err := filter.UnmarshalBinary(f); err != nil { - jww.WARN.Printf("Failed to unmarshal filter: %+v", err) - jww.INFO.Printf("Bloom Filter Unmarshal Data: %v", f) - continue - } - filterList = append(filterList, filter) - } - jww.INFO.Printf("Bloom filters found in response: %d", len(filterList)) // ---- Node Events ---- // NOTE: this updates the structure, AND sends events over the node @@ -139,6 +125,9 @@ func (m *manager) follow(rng csprng.Source, comms followNetworkComms) { } } + //check that the stored address space is correct + m.Session.Reception().UpdateIdSize(uint(m.Instance.GetPartialNdf().Get().AddressSpaceSize)) + // NOTE: this updates rounds and updates the tracking of the health of the // network if pollResp.Updates != nil { @@ -187,20 +176,37 @@ func (m *manager) follow(rng csprng.Source, comms followNetworkComms) { } } - // ---- Round Processing ----- + // ---- Identity Specific Round Processing ----- + if identity.Fake { + return + } + + //get the range fo filters which are valid for the identity + filtersStart, filtersEnd := rounds.ValidFilterRange(identity, pollResp.Filters) + + //check if there are any valid filters returned + if !(filtersEnd > filtersStart) { + return + } + + //prepare the filter objects for processing + filterList := make([]*rounds.RemoteFilter, filtersEnd-filtersStart) + for i := filtersStart; i < filtersEnd; i++ { + filterList[i-filtersStart] = rounds.NewRemoteFilter(pollResp.Filters.Filters[i]) + } + + jww.INFO.Printf("Bloom filters found in response: %d, num filters used: %d", + len(pollResp.Filters.Filters), len(filterList)) + // check rounds using the round checker function which determines if there // are messages waiting in rounds and then sends signals to the appropriate // handling threads roundChecker := func(rid id.Round) bool { - return m.round.Checker(rid, filterList) + return m.round.Checker(rid, filterList, identity) } // get the bit vector of rounds that have been checked checkedRounds := m.Session.GetCheckedRounds() - // cleave off old state in the bit vector which is deprecated from the - // network - jww.DEBUG.Printf("lastCheckedRound: %v", lastTrackedRound) - checkedRounds.Forward(lastTrackedRound) jww.TRACE.Printf("gwRoundState: %+v", gwRoundsState) jww.TRACE.Printf("pollResp.KnownRounds: %s", string(pollResp.KnownRounds)) @@ -208,6 +214,7 @@ func (m *manager) follow(rng csprng.Source, comms followNetworkComms) { // loop through all rounds the client does not know about and the gateway // does, checking the bloom filter for the user to see if there are // messages for the user (bloom not implemented yet) - checkedRounds.RangeUncheckedMasked(gwRoundsState, roundChecker, + checkedRounds.RangeUncheckedMaskedRange(gwRoundsState, roundChecker, + filterList[0].FirstRound(), filterList[len(filterList)-1].LastRound(), int(m.param.MaxCheckedRounds)) } diff --git a/network/manager.go b/network/manager.go index 39718e6d36185e72d5969ff7887ec6d8ff958ff2..592ee108b11fae5408c9f802c11c6874726e719f 100644 --- a/network/manager.go +++ b/network/manager.go @@ -7,13 +7,14 @@ package network -// manager.go controls access to network resources. Interprocess communications +// tracker.go controls access to network resources. Interprocess communications // and intraclient state are accessible through the context object. import ( "github.com/pkg/errors" "gitlab.com/elixxir/client/interfaces" "gitlab.com/elixxir/client/interfaces/params" + "gitlab.com/elixxir/client/network/ephemeral" "gitlab.com/elixxir/client/network/health" "gitlab.com/elixxir/client/network/internal" "gitlab.com/elixxir/client/network/message" @@ -44,7 +45,6 @@ type manager struct { //sub-managers round *rounds.Manager message *message.Manager - //atomic denotes if the network is running running *uint32 } @@ -96,6 +96,7 @@ func NewManager(session *storage.Session, switchboard *switchboard.Switchboard, // - Health Tracker (/network/health) // - Garbled Messages (/network/message/garbled.go) // - Critical Messages (/network/message/critical.go) +// - Ephemeral ID tracking (network/ephemeral/tracker.go) func (m *manager) Follow() (stoppable.Stoppable, error) { if !atomic.CompareAndSwapUint32(m.running, 0, 1) { return nil, errors.Errorf("network routines are already running") @@ -127,6 +128,8 @@ func (m *manager) Follow() (stoppable.Stoppable, error) { // Round processing multi.Add(m.round.StartProcessors()) + multi.Add(ephemeral.Track(m.Session, m.Instance, m.Comms.Id)) + //set the running status back to 0 so it can be started again closer := stoppable.NewCleanup(multi, func(time.Duration) error { if !atomic.CompareAndSwapUint32(m.running, 1, 0) { diff --git a/network/message/bundle.go b/network/message/bundle.go index 84962ded31793e5600eefa1bb425a04cc3a5e881..56f1618d643641da6e9c2550e998a37a1269344c 100644 --- a/network/message/bundle.go +++ b/network/message/bundle.go @@ -8,6 +8,7 @@ package message import ( + "gitlab.com/elixxir/client/storage/reception" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/primitives/id" ) @@ -16,4 +17,5 @@ type Bundle struct { Round id.Round Messages []format.Message Finish func() + Identity reception.IdentityUse } diff --git a/network/message/critical.go b/network/message/critical.go index e43cee7aa56684fcada1218136fb3549abd7e8ec..cb29ae3f3b19b18c75815f86f3c3c9df326b69d6 100644 --- a/network/message/critical.go +++ b/network/message/critical.go @@ -85,7 +85,7 @@ func (m *Manager) criticalMessages() { for msg, has := critRawMsgs.Next(); has; msg, has = critRawMsgs.Next() { go func(msg format.Message) { //send the message - round, err := m.SendCMIX(msg, param) + round, _, err := m.SendCMIX(msg, m.Uid, param) //if the message fail to send, notify the buffer so it can be handled //in the future and exit if err != nil { diff --git a/network/message/handler.go b/network/message/handler.go index 6a3d5bb2ae73e3806316e14f21b2db28a1fad2d2..6ff08e5d78254113fd379f36c2564fa04148ac52 100644 --- a/network/message/handler.go +++ b/network/message/handler.go @@ -10,7 +10,9 @@ package message import ( jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/interfaces/message" + "gitlab.com/elixxir/client/storage/reception" "gitlab.com/elixxir/crypto/e2e" + fingerprint2 "gitlab.com/elixxir/crypto/fingerprint" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/primitives/id" "time" @@ -24,7 +26,7 @@ func (m *Manager) handleMessages(quitCh <-chan struct{}) { done = true case bundle := <-m.messageReception: for _, msg := range bundle.Messages { - m.handleMessage(msg) + m.handleMessage(msg, bundle.Identity) } bundle.Finish() } @@ -32,7 +34,7 @@ func (m *Manager) handleMessages(quitCh <-chan struct{}) { } -func (m *Manager) handleMessage(ecrMsg format.Message) { +func (m *Manager) handleMessage(ecrMsg format.Message, identity reception.IdentityUse) { // We've done all the networking, now process the message fingerprint := ecrMsg.GetKeyFP() @@ -44,6 +46,15 @@ func (m *Manager) handleMessage(ecrMsg format.Message) { var err error var relationshipFingerprint []byte + //check if the identity fingerprint matches + forMe, err := fingerprint2.CheckIdentityFP(ecrMsg.GetIdentityFP(), ecrMsg.GetContents(), identity.Source) + if err != nil { + jww.FATAL.Panicf("Could not check IdentityFIngerprint: %+v", err) + } + if !forMe { + return + } + // try to get the key fingerprint, process as e2e encryption if // the fingerprint is found if key, isE2E := e2eKv.PopKey(fingerprint); isE2E { @@ -72,12 +83,18 @@ func (m *Manager) handleMessage(ecrMsg format.Message) { // if it doesnt match any form of encrypted, hear it as a raw message // and add it to garbled messages to be handled later msg = ecrMsg + if err != nil { + jww.DEBUG.Printf("Failed to unmarshal ephemeral ID "+ + "on unknown message: %+v", err) + } raw := message.Receive{ Payload: msg.Marshal(), MessageType: message.Raw, - Sender: msg.GetRecipientID(), + Sender: nil, + EphemeralID: identity.EphId, Timestamp: time.Time{}, Encryption: message.None, + RecipientID: identity.Source, } jww.INFO.Printf("Garbled/RAW Message: %v", msg.GetKeyFP()) m.Session.GetGarbledMessages().Add(msg) @@ -89,6 +106,11 @@ func (m *Manager) handleMessage(ecrMsg format.Message) { // we get a full message xxMsg, ok := m.partitioner.HandlePartition(sender, encTy, msg.GetContents(), relationshipFingerprint) + + //Set the identities + xxMsg.RecipientID = identity.Source + xxMsg.EphemeralID = identity.EphId + // If the reception completed a message, hear it on the switchboard if ok { if xxMsg.MessageType == message.Raw { diff --git a/network/message/sendCmix.go b/network/message/sendCmix.go index 4addcb7e8998f00301d0c8d9800e6e92b6c37fd8..0dae64e7e44a34da225414f2e5737f053ab20819 100644 --- a/network/message/sendCmix.go +++ b/network/message/sendCmix.go @@ -12,19 +12,34 @@ import ( "github.com/pkg/errors" jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/interfaces/params" - "gitlab.com/elixxir/comms/mixmessages" + "gitlab.com/elixxir/client/storage" + pb "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/elixxir/comms/network" + "gitlab.com/elixxir/crypto/fastRNG" + "gitlab.com/elixxir/crypto/fingerprint" "gitlab.com/elixxir/primitives/format" "gitlab.com/elixxir/primitives/states" "gitlab.com/xx_network/comms/connect" "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/id/ephemeral" "strings" "time" ) +// interface for sendcmix comms; allows mocking this in testing +type sendCmixCommsInterface interface { + GetHost(hostId *id.ID) (*connect.Host, bool) + SendPutMessage(host *connect.Host, message *pb.GatewaySlot) (*pb.GatewaySlotResponse, error) +} + const sendTimeBuffer = uint64(100 * time.Millisecond) // WARNING: Potentially Unsafe +// Public manager function to send a message over CMIX +func (m *Manager) SendCMIX(msg format.Message, recipient *id.ID, param params.CMIX) (id.Round, ephemeral.Id, error) { + return sendCmixHelper(msg, recipient, param, m.Instance, m.Session, m.nodeRegistration, m.Rng, m.Uid, m.Comms) +} + // Payloads send are not End to End encrypted, MetaData is NOT protected with // this call, see SendE2E for End to End encryption and full privacy protection // Internal SendCmix which bypasses the network check, will attempt to send to @@ -33,21 +48,24 @@ const sendTimeBuffer = uint64(100 * time.Millisecond) // If the message is successfully sent, the id of the round sent it is returned, // which can be registered with the network instance to get a callback on // its status -func (m *Manager) SendCMIX(msg format.Message, param params.CMIX) (id.Round, error) { +func sendCmixHelper(msg format.Message, recipient *id.ID, param params.CMIX, instance *network.Instance, + session *storage.Session, nodeRegistration chan network.NodeGateway, rng *fastRNG.StreamGenerator, senderId *id.ID, + comms sendCmixCommsInterface) (id.Round, ephemeral.Id, error) { timeStart := time.Now() attempted := set.New() for numRoundTries := uint(0); numRoundTries < param.RoundTries; numRoundTries++ { elapsed := time.Now().Sub(timeStart) + jww.DEBUG.Printf("SendCMIX Send Attempt %d", numRoundTries+1) if elapsed > param.Timeout { - return 0, errors.New("Sending cmix message timed out") + return 0, ephemeral.Id{}, errors.New("Sending cmix message timed out") } remainingTime := param.Timeout - elapsed jww.TRACE.Printf("SendCMIX GetUpcommingRealtime") //find the best round to send to, excluding attempted rounds - bestRound, _ := m.Instance.GetWaitingRounds().GetUpcomingRealtime(remainingTime, attempted) + bestRound, _ := instance.GetWaitingRounds().GetUpcomingRealtime(remainingTime, attempted) if bestRound == nil { continue } @@ -59,6 +77,32 @@ func (m *Manager) SendCMIX(msg format.Message, param params.CMIX) (id.Round, err continue } + //set the ephemeral ID + ephID, _, _, err := ephemeral.GetId(recipient, + uint(bestRound.AddressSpaceSize), + int64(bestRound.Timestamps[states.REALTIME])) + if err != nil { + jww.FATAL.Panicf("Failed to generate ephemeral ID: %+v", err) + } + + stream := rng.GetStream() + ephID, err = ephID.Fill(uint(bestRound.AddressSpaceSize), stream) + if err != nil { + jww.FATAL.Panicf("Failed to obviscate the ephemeralID: %+v", err) + } + stream.Close() + + msg.SetEphemeralRID(ephID[:]) + + //set the identity fingerprint + ifp, err := fingerprint.IdentityFP(msg.GetContents(), recipient) + if err != nil { + jww.FATAL.Panicf("failed to generate the Identity "+ + "fingerprint due to unrecoverable error: %+v", err) + } + + msg.SetIdentityFP(ifp) + //build the topology idList, err := id.NewIDListFromBytes(bestRound.Topology) if err != nil { @@ -69,9 +113,9 @@ func (m *Manager) SendCMIX(msg format.Message, param params.CMIX) (id.Round, err jww.TRACE.Printf("SendCMIX GetRoundKeys") //get they keys for the round, reject if any nodes do not have //keying relationships - roundKeys, missingKeys := m.Session.Cmix().GetRoundKeys(topology) + roundKeys, missingKeys := session.Cmix().GetRoundKeys(topology) if len(missingKeys) > 0 { - go handleMissingNodeKeys(m.Instance, m.nodeRegistration, missingKeys) + go handleMissingNodeKeys(instance, nodeRegistration, missingKeys) time.Sleep(param.RetryDelay) continue } @@ -80,7 +124,7 @@ func (m *Manager) SendCMIX(msg format.Message, param params.CMIX) (id.Round, err firstGateway := topology.GetNodeAtIndex(0).DeepCopy() firstGateway.SetType(id.Gateway) - transmitGateway, ok := m.Comms.GetHost(firstGateway) + transmitGateway, ok := comms.GetHost(firstGateway) if !ok { jww.ERROR.Printf("Failed to get host for gateway %s", transmitGateway) time.Sleep(param.RetryDelay) @@ -88,21 +132,21 @@ func (m *Manager) SendCMIX(msg format.Message, param params.CMIX) (id.Round, err } //encrypt the message + stream = rng.GetStream() salt := make([]byte, 32) - stream := m.Rng.GetStream() _, err = stream.Read(salt) stream.Close() if err != nil { - return 0, errors.WithMessage(err, "Failed to generate "+ - "salt, this should never happen") + return 0, ephemeral.Id{}, errors.WithMessage(err, + "Failed to generate salt, this should never happen") } - jww.INFO.Printf("RECIPIENTIDPRE_ENCRYPT: %s", msg.GetRecipientID()) + encMsg, kmacs := roundKeys.Encrypt(msg, salt) //build the message payload - msgPacket := &mixmessages.Slot{ - SenderID: m.Uid.Bytes(), + msgPacket := &pb.Slot{ + SenderID: senderId.Bytes(), PayloadA: encMsg.GetPayloadA(), PayloadB: encMsg.GetPayloadB(), Salt: salt, @@ -110,18 +154,20 @@ func (m *Manager) SendCMIX(msg format.Message, param params.CMIX) (id.Round, err } //create the wrapper to the gateway - msg := &mixmessages.GatewaySlot{ + wrappedMsg := &pb.GatewaySlot{ Message: msgPacket, RoundID: bestRound.ID, } //Add the mac proving ownership - msg.MAC = roundKeys.MakeClientGatewayKey(salt, network.GenerateSlotDigest(msg)) + wrappedMsg.MAC = roundKeys.MakeClientGatewayKey(salt, + network.GenerateSlotDigest(wrappedMsg)) //add the round on to the list of attempted so it is not tried again attempted.Insert(bestRound) + jww.DEBUG.Printf("SendCMIX SendPutMessage") //Send the payload - gwSlotResp, err := m.Comms.SendPutMessage(transmitGateway, msg) + gwSlotResp, err := comms.SendPutMessage(transmitGateway, wrappedMsg) //if the comm errors or the message fails to send, continue retrying. //return if it sends properly if err != nil { @@ -134,15 +180,14 @@ func (m *Manager) SendCMIX(msg format.Message, param params.CMIX) (id.Round, err jww.ERROR.Printf("Failed to send message to %s: %s", transmitGateway, err) } else if gwSlotResp.Accepted { - return id.Round(bestRound.ID), nil + return id.Round(bestRound.ID), ephID, nil } } - - return 0, errors.New("failed to send the message") + return 0, ephemeral.Id{}, errors.New("failed to send the message") } // Signals to the node registration thread to register a node if keys are -// missing. Registration is triggered automatically when the node is first seen, +// missing. Identity is triggered automatically when the node is first seen, // so this should on trigger on rare events. func handleMissingNodeKeys(instance *network.Instance, newNodeChan chan network.NodeGateway, nodes []*id.ID) { diff --git a/network/message/sendCmix_test.go b/network/message/sendCmix_test.go new file mode 100644 index 0000000000000000000000000000000000000000..a623c77fa03842a62b3dddfb20a18b236bcceb22 --- /dev/null +++ b/network/message/sendCmix_test.go @@ -0,0 +1,150 @@ +package message + +import ( + "gitlab.com/elixxir/client/interfaces/message" + "gitlab.com/elixxir/client/interfaces/params" + "gitlab.com/elixxir/client/network/internal" + "gitlab.com/elixxir/client/storage" + "gitlab.com/elixxir/client/switchboard" + "gitlab.com/elixxir/comms/client" + "gitlab.com/elixxir/comms/mixmessages" + "gitlab.com/elixxir/comms/network" + "gitlab.com/elixxir/crypto/cyclic" + "gitlab.com/elixxir/crypto/e2e" + "gitlab.com/elixxir/crypto/fastRNG" + "gitlab.com/elixxir/primitives/format" + "gitlab.com/elixxir/primitives/states" + "gitlab.com/xx_network/comms/connect" + "gitlab.com/xx_network/crypto/csprng" + "gitlab.com/xx_network/crypto/large" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/ndf" + "testing" + "time" +) + +type MockSendCMIXComms struct { + t *testing.T +} + +func (mc *MockSendCMIXComms) GetHost(hostId *id.ID) (*connect.Host, bool) { + nid1 := id.NewIdFromString("zezima", id.Node, mc.t) + gwid := nid1.DeepCopy() + gwid.SetType(id.Gateway) + h, _ := connect.NewHost(gwid, "0.0.0.0", []byte(""), connect.HostParams{ + MaxRetries: 0, + AuthEnabled: false, + }) + return h, true +} +func (mc *MockSendCMIXComms) SendPutMessage(host *connect.Host, message *mixmessages.GatewaySlot) (*mixmessages.GatewaySlotResponse, error) { + return &mixmessages.GatewaySlotResponse{ + Accepted: true, + RoundID: 3, + }, nil +} + +func Test_attemptSendCmix(t *testing.T) { + sess1 := storage.InitTestingSession(t) + + sess2 := storage.InitTestingSession(t) + + sw := switchboard.New() + l := TestListener{ + ch: make(chan bool), + } + sw.RegisterListener(sess2.GetUser().TransmissionID, message.Raw, l) + comms, err := client.NewClientComms(sess1.GetUser().TransmissionID, nil, nil, nil) + if err != nil { + t.Errorf("Failed to start client comms: %+v", err) + } + inst, err := network.NewInstanceTesting(comms.ProtoComms, getNDF(), nil, nil, nil, t) + if err != nil { + t.Errorf("Failed to start instance: %+v", err) + } + now := uint64(time.Now().UnixNano()) + nid1 := id.NewIdFromString("zezima", id.Node, t) + nid2 := id.NewIdFromString("jakexx360", id.Node, t) + nid3 := id.NewIdFromString("westparkhome", id.Node, t) + grp := cyclic.NewGroup(large.NewInt(7), large.NewInt(13)) + sess1.Cmix().Add(nid1, grp.NewInt(1)) + sess1.Cmix().Add(nid2, grp.NewInt(2)) + sess1.Cmix().Add(nid3, grp.NewInt(3)) + inst.GetWaitingRounds().Insert(&mixmessages.RoundInfo{ + ID: 3, + UpdateID: 0, + State: uint32(states.QUEUED), + BatchSize: 0, + Topology: [][]byte{nid1.Marshal(), nid2.Marshal(), nid3.Marshal()}, + Timestamps: []uint64{now - 30, now - 15, now, now + 15, 0}, + Errors: nil, + ClientErrors: nil, + ResourceQueueTimeoutMillis: 0, + Signature: nil, + AddressSpaceSize: 0, + }) + i := internal.Internal{ + Session: sess1, + Switchboard: sw, + Rng: fastRNG.NewStreamGenerator(1, 1, csprng.NewSystemRNG), + Comms: comms, + Health: nil, + Uid: sess1.GetUser().TransmissionID, + Instance: inst, + NodeRegistration: nil, + } + m := NewManager(i, params.Messages{ + MessageReceptionBuffLen: 20, + MessageReceptionWorkerPoolSize: 20, + MaxChecksGarbledMessage: 20, + GarbledMessageWait: time.Hour, + }, nil) + msgCmix := format.NewMessage(m.Session.Cmix().GetGroup().GetP().ByteLen()) + msgCmix.SetContents([]byte("test")) + e2e.SetUnencrypted(msgCmix, m.Session.User().GetCryptographicIdentity().GetTransmissionID()) + _, _, err = sendCmixHelper(msgCmix, sess2.GetUser().ReceptionID, params.GetDefaultCMIX(), + m.Instance, m.Session, m.nodeRegistration, m.Rng, + m.Uid, &MockSendCMIXComms{t: t}) + if err != nil { + t.Errorf("Failed to sendcmix: %+v", err) + } +} + +func getNDF() *ndf.NetworkDefinition { + return &ndf.NetworkDefinition{ + E2E: ndf.Group{ + Prime: "E2EE983D031DC1DB6F1A7A67DF0E9A8E5561DB8E8D49413394C049B" + + "7A8ACCEDC298708F121951D9CF920EC5D146727AA4AE535B0922C688B55B3DD2AE" + + "DF6C01C94764DAB937935AA83BE36E67760713AB44A6337C20E7861575E745D31F" + + "8B9E9AD8412118C62A3E2E29DF46B0864D0C951C394A5CBBDC6ADC718DD2A3E041" + + "023DBB5AB23EBB4742DE9C1687B5B34FA48C3521632C4A530E8FFB1BC51DADDF45" + + "3B0B2717C2BC6669ED76B4BDD5C9FF558E88F26E5785302BEDBCA23EAC5ACE9209" + + "6EE8A60642FB61E8F3D24990B8CB12EE448EEF78E184C7242DD161C7738F32BF29" + + "A841698978825B4111B4BC3E1E198455095958333D776D8B2BEEED3A1A1A221A6E" + + "37E664A64B83981C46FFDDC1A45E3D5211AAF8BFBC072768C4F50D7D7803D2D4F2" + + "78DE8014A47323631D7E064DE81C0C6BFA43EF0E6998860F1390B5D3FEACAF1696" + + "015CB79C3F9C2D93D961120CD0E5F12CBB687EAB045241F96789C38E89D796138E" + + "6319BE62E35D87B1048CA28BE389B575E994DCA755471584A09EC723742DC35873" + + "847AEF49F66E43873", + Generator: "2", + }, + CMIX: ndf.Group{ + Prime: "9DB6FB5951B66BB6FE1E140F1D2CE5502374161FD6538DF1648218642F0B5C48" + + "C8F7A41AADFA187324B87674FA1822B00F1ECF8136943D7C55757264E5A1A44F" + + "FE012E9936E00C1D3E9310B01C7D179805D3058B2A9F4BB6F9716BFE6117C6B5" + + "B3CC4D9BE341104AD4A80AD6C94E005F4B993E14F091EB51743BF33050C38DE2" + + "35567E1B34C3D6A5C0CEAA1A0F368213C3D19843D0B4B09DCB9FC72D39C8DE41" + + "F1BF14D4BB4563CA28371621CAD3324B6A2D392145BEBFAC748805236F5CA2FE" + + "92B871CD8F9C36D3292B5509CA8CAA77A2ADFC7BFD77DDA6F71125A7456FEA15" + + "3E433256A2261C6A06ED3693797E7995FAD5AABBCFBE3EDA2741E375404AE25B", + Generator: "5C7FF6B06F8F143FE8288433493E4769C4D988ACE5BE25A0E24809670716C613" + + "D7B0CEE6932F8FAA7C44D2CB24523DA53FBE4F6EC3595892D1AA58C4328A06C4" + + "6A15662E7EAA703A1DECF8BBB2D05DBE2EB956C142A338661D10461C0D135472" + + "085057F3494309FFA73C611F78B32ADBB5740C361C9F35BE90997DB2014E2EF5" + + "AA61782F52ABEB8BD6432C4DD097BC5423B285DAFB60DC364E8161F4A2A35ACA" + + "3A10B1C4D203CC76A470A33AFDCBDD92959859ABD8B56E1725252D78EAC66E71" + + "BA9AE3F1DD2487199874393CD4D832186800654760E1E34C09E4D155179F9EC0" + + "DC4473F996BDCE6EED1CABED8B6F116F7AD9CF505DF0F998E34AB27514B0FFE7", + }, + } +} diff --git a/network/message/sendE2E.go b/network/message/sendE2E.go index 04e24e8e435d7871c6512b0a90abb329c2efa101..fbb2e910f399a249060eac085d5a8c0f393cad3e 100644 --- a/network/message/sendE2E.go +++ b/network/message/sendE2E.go @@ -51,7 +51,6 @@ func (m *Manager) SendE2E(msg message.Send, param params.E2E) ([]id.Round, e2e.M //create the cmix message msgCmix := format.NewMessage(m.Session.Cmix().GetGroup().GetP().ByteLen()) msgCmix.SetContents(p) - msgCmix.SetRecipientID(msg.Recipient) //get a key to end to end encrypt key, err := partner.GetKeyForSending(param.Type) @@ -67,7 +66,7 @@ func (m *Manager) SendE2E(msg message.Send, param params.E2E) ([]id.Round, e2e.M wg.Add(1) go func(i int) { var err error - roundIds[i], err = m.SendCMIX(msgEnc, param.CMIX) + roundIds[i], _, err = m.SendCMIX(msgEnc, msg.Recipient, param.CMIX) if err != nil { errCh <- err } diff --git a/network/message/sendUnsafe.go b/network/message/sendUnsafe.go index 8975309dc25b884b35b599c56f88ce53e9fadcea..956a50f544c5524fe6820f9d3429c1af93b48639 100644 --- a/network/message/sendUnsafe.go +++ b/network/message/sendUnsafe.go @@ -51,12 +51,11 @@ func (m *Manager) SendUnsafe(msg message.Send, param params.Unsafe) ([]id.Round, for i, p := range partitions { msgCmix := format.NewMessage(m.Session.Cmix().GetGroup().GetP().ByteLen()) msgCmix.SetContents(p) - msgCmix.SetRecipientID(msg.Recipient) e2e.SetUnencrypted(msgCmix, m.Session.User().GetCryptographicIdentity().GetTransmissionID()) wg.Add(1) go func(i int) { var err error - roundIds[i], err = m.SendCMIX(msgCmix, param.CMIX) + roundIds[i], _, err = m.SendCMIX(msgCmix, msg.Recipient, param.CMIX) if err != nil { errCh <- err } diff --git a/network/rounds/check.go b/network/rounds/check.go index 72d31bed5d1840f6d2b5f7b00f8b24607a7963a8..b84676f5bf91ce8cbc24211310225ddf8dd730d0 100644 --- a/network/rounds/check.go +++ b/network/rounds/check.go @@ -11,6 +11,7 @@ import ( "encoding/binary" jww "github.com/spf13/jwalterweatherman" bloom "gitlab.com/elixxir/bloomfilter" + "gitlab.com/elixxir/client/storage/reception" "gitlab.com/xx_network/primitives/id" ) @@ -25,7 +26,7 @@ import ( // if the information about that round is already present, if it is the data is // sent to Message Retrieval Workers, otherwise it is sent to Historical Round // Retrieval -func (m *Manager) Checker(roundID id.Round, filters []*bloom.Ring) bool { +func (m *Manager) Checker(roundID id.Round, filters []*RemoteFilter, identity reception.IdentityUse) bool { jww.DEBUG.Printf("Checker(roundID: %d)", roundID) // Set round to processing, if we can processing, count := m.p.Process(roundID) @@ -43,14 +44,24 @@ func (m *Manager) Checker(roundID id.Round, filters []*bloom.Ring) bool { return true } - //check if the round is in the bloom filters - hasRound := false - serialRid := serializeRound(roundID) + //find filters that could have the round + var potentialFilters []*bloom.Bloom for _, filter := range filters { - hasRound = filter.Test(serialRid) - if hasRound { - break + if filter.FirstRound() <= roundID && filter.LastRound() >= roundID { + potentialFilters = append(potentialFilters, filter.GetFilter()) + } + } + + hasRound := false + //check if the round is in any of the potential filters + if len(potentialFilters) > 0 { + serialRid := serializeRound(roundID) + for _, f := range potentialFilters { + if f.Test(serialRid) { + hasRound = true + break + } } } @@ -66,11 +77,17 @@ func (m *Manager) Checker(roundID id.Round, filters []*bloom.Ring) bool { if err != nil { jww.DEBUG.Printf("HistoricalRound <- %d", roundID) // If we didn't find it, send to Historical Rounds Retrieval - m.historicalRounds <- roundID + m.historicalRounds <- historicalRoundRequest{ + rid: roundID, + identity: identity, + } } else { jww.DEBUG.Printf("lookupRoundMessages <- %d", roundID) // IF found, send to Message Retrieval Workers - m.lookupRoundMessages <- ri + m.lookupRoundMessages <- roundLookup{ + roundInfo: ri, + identity: identity, + } } return false diff --git a/network/rounds/historical.go b/network/rounds/historical.go index 6b8fd8a53b2101b47b817bc3ec0341a1986ca9ef..5d5868028477e2a3c8f5dc61fb12193a51a474f1 100644 --- a/network/rounds/historical.go +++ b/network/rounds/historical.go @@ -10,6 +10,7 @@ package rounds import ( jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/network/gateway" + "gitlab.com/elixxir/client/storage/reception" pb "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/xx_network/comms/connect" "gitlab.com/xx_network/primitives/id" @@ -31,6 +32,12 @@ type historicalRoundsComms interface { message *pb.HistoricalRounds) (*pb.HistoricalRoundsResponse, error) } +//structure which contains a historical round lookup +type historicalRoundRequest struct { + rid id.Round + identity reception.IdentityUse +} + // Long running thread which process historical rounds // Can be killed by sending a signal to the quit channel // takes a comms interface to aid in testing @@ -39,7 +46,7 @@ func (m *Manager) processHistoricalRounds(comm historicalRoundsComms, quitCh <-c timerCh := make(<-chan time.Time) rng := m.Rng.GetStream() - var rounds []uint64 + var roundRequests []historicalRoundRequest done := false for !done { @@ -48,28 +55,28 @@ func (m *Manager) processHistoricalRounds(comm historicalRoundsComms, quitCh <-c select { case <-quitCh: rng.Close() - // return all rounds in the queue to the input channel so they can + // return all roundRequests in the queue to the input channel so they can // be checked in the future. If the queue is full, disable them as // processing so they are picked up from the beginning - for _, rid := range rounds { + for _, r := range roundRequests { select { - case m.historicalRounds <- id.Round(rid): + case m.historicalRounds <- r: default: - m.p.NotProcessing(id.Round(rid)) + m.p.NotProcessing(r.rid) } } done = true - // if the timer elapses process rounds to ensure the delay isn't too long + // if the timer elapses process roundRequests to ensure the delay isn't too long case <-timerCh: - if len(rounds) > 0 { + if len(roundRequests) > 0 { shouldProcess = true } // get new round to lookup and force a lookup if - case rid := <-m.historicalRounds: - rounds = append(rounds, uint64(rid)) - if len(rounds) > int(m.params.MaxHistoricalRounds) { + case r := <-m.historicalRounds: + roundRequests = append(roundRequests, r) + if len(roundRequests) > int(m.params.MaxHistoricalRounds) { shouldProcess = true - } else if len(rounds) == 1 { + } else if len(roundRequests) == 1 { //if this is the first round, start the timeout timerCh = time.NewTimer(m.params.HistoricalRoundsPeriod).C } @@ -78,21 +85,26 @@ func (m *Manager) processHistoricalRounds(comm historicalRoundsComms, quitCh <-c continue } - //find a gateway to request about the rounds + //find a gateway to request about the roundRequests gwHost, err := gateway.Get(m.Instance.GetPartialNdf().Get(), comm, rng) if err != nil { jww.FATAL.Panicf("Failed to track network, NDF has corrupt "+ "data: %s", err) } - //send the historical rounds request + rounds := make([]uint64, len(roundRequests)) + for i, rr := range roundRequests { + rounds[i] = uint64(rr.rid) + } + + //send the historical roundRequests request hr := &pb.HistoricalRounds{ Rounds: rounds, } response, err := comm.RequestHistoricalRounds(gwHost, hr) if err != nil { - jww.ERROR.Printf("Failed to request historical rounds "+ + jww.ERROR.Printf("Failed to request historical roundRequests "+ "data: %s", response) // if the check fails to resolve, break the loop and so they will be // checked again @@ -100,20 +112,24 @@ func (m *Manager) processHistoricalRounds(comm historicalRoundsComms, quitCh <-c continue } - // process the returned historical rounds. + // process the returned historical roundRequests. for i, roundInfo := range response.Rounds { - // The interface has missing returns returned as nil, such rounds + // The interface has missing returns returned as nil, such roundRequests // need be be removes as processing so the network follower will // pick them up in the future. if roundInfo == nil { jww.ERROR.Printf("could not retreive "+ - "historical round %d", rounds[i]) - m.p.Fail(id.Round(rounds[i])) + "historical round %d", roundRequests[i].rid) + m.p.Fail(roundRequests[i].rid) continue } - // Successfully retrieved rounds are sent to the Message + // Successfully retrieved roundRequests are sent to the Message // Retrieval Workers - m.lookupRoundMessages <- roundInfo + rl := roundLookup{ + roundInfo: roundInfo, + identity: roundRequests[i].identity, + } + m.lookupRoundMessages <- rl } } } diff --git a/network/rounds/manager.go b/network/rounds/manager.go index 3c3221474d3124b9de4ab1e688c7eeda118e8a52..9fe5ce57a34f342aaf1fb2b1055442e08f802d0a 100644 --- a/network/rounds/manager.go +++ b/network/rounds/manager.go @@ -13,8 +13,6 @@ import ( "gitlab.com/elixxir/client/network/internal" "gitlab.com/elixxir/client/network/message" "gitlab.com/elixxir/client/stoppable" - "gitlab.com/elixxir/comms/mixmessages" - "gitlab.com/xx_network/primitives/id" ) type Manager struct { @@ -24,8 +22,8 @@ type Manager struct { internal.Internal - historicalRounds chan id.Round - lookupRoundMessages chan *mixmessages.RoundInfo + historicalRounds chan historicalRoundRequest + lookupRoundMessages chan roundLookup messageBundles chan<- message.Bundle } @@ -35,8 +33,8 @@ func NewManager(internal internal.Internal, params params.Rounds, params: params, p: newProcessingRounds(), - historicalRounds: make(chan id.Round, params.HistoricalRoundsBufferLen), - lookupRoundMessages: make(chan *mixmessages.RoundInfo, params.LookupRoundsBufferLen), + historicalRounds: make(chan historicalRoundRequest, params.HistoricalRoundsBufferLen), + lookupRoundMessages: make(chan roundLookup, params.LookupRoundsBufferLen), messageBundles: bundles, } diff --git a/network/rounds/remoteFilters.go b/network/rounds/remoteFilters.go new file mode 100644 index 0000000000000000000000000000000000000000..3209a8261008dde0004319198d80e43e823b73c4 --- /dev/null +++ b/network/rounds/remoteFilters.go @@ -0,0 +1,55 @@ +package rounds + +import ( + bloom "gitlab.com/elixxir/bloomfilter" + "gitlab.com/elixxir/client/interfaces" + "gitlab.com/elixxir/client/storage/reception" + "gitlab.com/elixxir/comms/mixmessages" + "gitlab.com/xx_network/primitives/id" + "time" +) + +func NewRemoteFilter(data *mixmessages.ClientBloom) *RemoteFilter { + return &RemoteFilter{ + data: data, + } +} + +type RemoteFilter struct { + data *mixmessages.ClientBloom + filter *bloom.Bloom +} + +func (rf *RemoteFilter) GetFilter() *bloom.Bloom { + + if rf.filter == nil { + var err error + rf.filter, err = bloom.InitByParameters(interfaces.BloomFilterSize, + interfaces.BloomFilterHashes) + if err != nil { + return nil + } + } + return rf.filter +} + +func (rf *RemoteFilter) FirstRound() id.Round { + return id.Round(rf.data.FirstRound) +} + +func (rf *RemoteFilter) LastRound() id.Round { + return id.Round(rf.data.FirstRound + uint64(rf.data.RoundRange)) +} + +// ValidFilterRange calculates which of the returned filters are valid for the identity +func ValidFilterRange(identity reception.IdentityUse, filters *mixmessages.ClientBlooms) (start int, end int) { + firstFilterStart := time.Unix(0, filters.FirstTimestamp) + filterDelta := time.Duration(filters.Period) + + deltaFromStart := int(identity.StartValid.Sub(firstFilterStart) / filterDelta) + deltaFromEnd := int((identity.EndValid.Sub(firstFilterStart) + filterDelta - 1) / filterDelta) + if deltaFromEnd > (len(filters.Filters) - 1) { + deltaFromEnd = len(filters.Filters) + } + return deltaFromStart, deltaFromEnd + 1 +} diff --git a/network/rounds/retrieve.go b/network/rounds/retrieve.go index 0762c3a197061273993e5a57ff056f6c041d51f0..05a03821118b95e7c34f984b24b4601ca9bb697c 100644 --- a/network/rounds/retrieve.go +++ b/network/rounds/retrieve.go @@ -12,6 +12,7 @@ import ( jww "github.com/spf13/jwalterweatherman" "gitlab.com/elixxir/client/network/gateway" "gitlab.com/elixxir/client/network/message" + "gitlab.com/elixxir/client/storage/reception" pb "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/comms/connect" @@ -24,6 +25,11 @@ type messageRetrievalComms interface { message *pb.GetMessages) (*pb.GetMessagesResponse, error) } +type roundLookup struct { + roundInfo *pb.RoundInfo + identity reception.IdentityUse +} + func (m *Manager) processMessageRetrieval(comms messageRetrievalComms, quitCh <-chan struct{}) { @@ -32,13 +38,15 @@ func (m *Manager) processMessageRetrieval(comms messageRetrievalComms, select { case <-quitCh: done = true - case ri := <-m.lookupRoundMessages: + case rl := <-m.lookupRoundMessages: + ri := rl.roundInfo bundle, err := m.getMessagesFromGateway(ri, comms) if err != nil { jww.WARN.Printf("Failed to get messages for round %v: %s", ri.ID, err) break } + bundle.Identity = rl.identity if len(bundle.Messages) != 0 { m.messageBundles <- bundle } diff --git a/network/send.go b/network/send.go index cd3d13da43cd97ab8a610dd05e12e7776645dd49..0e3e9020aa187716e3a7dd7da9f538bff47ba3e9 100644 --- a/network/send.go +++ b/network/send.go @@ -15,19 +15,20 @@ import ( "gitlab.com/elixxir/crypto/e2e" "gitlab.com/elixxir/primitives/format" "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/id/ephemeral" ) // SendCMIX sends a "raw" CMIX message payload to the provided // recipient. Note that both SendE2E and SendUnsafe call SendCMIX. // Returns the round ID of the round the payload was sent or an error // if it fails. -func (m *manager) SendCMIX(msg format.Message, param params.CMIX) (id.Round, error) { +func (m *manager) SendCMIX(msg format.Message, recipient *id.ID, param params.CMIX) (id.Round, ephemeral.Id, error) { if !m.Health.IsHealthy() { - return 0, errors.New("Cannot send cmix message when the " + + return 0, ephemeral.Id{}, errors.New("Cannot send cmix message when the " + "network is not healthy") } - return m.message.SendCMIX(msg, param) + return m.message.SendCMIX(msg, recipient, param) } // SendUnsafe sends an unencrypted payload to the provided recipient diff --git a/permissioning/register.go b/permissioning/register.go index f013fbd94f4e4f5af599689b07a410c02453d3dc..1a64e54798560b270ab0d62267f7b2704a2c8ae1 100644 --- a/permissioning/register.go +++ b/permissioning/register.go @@ -36,7 +36,7 @@ func register(comms registrationMessageSender, host *connect.Host, ClientReceptionRSAPubKey: string(rsa.CreatePublicKeyPem(receptionPublicKey)), }) if err != nil { - err = errors.Wrap(err, "sendRegistrationMessage: Unable to contact Registration Server!") + err = errors.Wrap(err, "sendRegistrationMessage: Unable to contact Identity Server!") return nil, nil, err } if response.Error != "" { diff --git a/storage/e2e/session.go b/storage/e2e/session.go index bcea1ff782e45d742dd1a7429e7df4f9a90983f6..1fd729e013a093eb706ec7cc8de220255cb9167c 100644 --- a/storage/e2e/session.go +++ b/storage/e2e/session.go @@ -16,9 +16,10 @@ import ( "gitlab.com/elixxir/client/storage/versioned" "gitlab.com/elixxir/crypto/cyclic" dh "gitlab.com/elixxir/crypto/diffieHellman" - "gitlab.com/elixxir/crypto/e2e" "gitlab.com/elixxir/crypto/hash" + "gitlab.com/xx_network/crypto/randomness" "gitlab.com/xx_network/primitives/id" + "math/big" "sync" "testing" "time" @@ -547,15 +548,12 @@ func (s *Session) generate(kv *versioned.KV) *versioned.KV { kv = kv.Prefix(makeSessionPrefix(s.GetID())) //generate ttl and keying info - keysTTL, numKeys := e2e.GenerateKeyTTL(s.baseKey.GetLargeInt(), - s.params.MinKeys, s.params.MaxKeys, s.params.TTLParams) + h, _ := hash.NewCMixHash() - //ensure that enough keys are remaining to rekey - if numKeys-uint32(keysTTL) < uint32(s.params.NumRekeys) { - numKeys = uint32(keysTTL + s.params.NumRekeys) - } + numKeys := uint32(randomness.RandInInterval(big.NewInt(int64(s.params.MaxKeys-s.params.MinKeys)), + s.baseKey.Bytes(), h).Int64() + int64(s.params.MinKeys)) - s.ttl = uint32(keysTTL) + s.ttl = uint32(s.params.NumRekeys) //create the new state vectors. This will cause disk operations storing them diff --git a/storage/reception/IdentityUse.go b/storage/reception/IdentityUse.go new file mode 100644 index 0000000000000000000000000000000000000000..4bbbc4ee4b7f9819b75aaf3efa093ddfb8f2a032 --- /dev/null +++ b/storage/reception/IdentityUse.go @@ -0,0 +1,61 @@ +package reception + +import ( + "github.com/pkg/errors" + "gitlab.com/elixxir/crypto/hash" + "gitlab.com/elixxir/primitives/knownRounds" + "gitlab.com/xx_network/crypto/randomness" + "gitlab.com/xx_network/primitives/id" + "io" + "math/big" + "time" +) + +type IdentityUse struct { + Identity + + // Randomly generated time to poll between + StartRequest time.Time // Timestamp to request the start of bloom filters + EndRequest time.Time // Timestamp to request the End of bloom filters + + // Denotes if the identity is fake, in which case we do not process messages + Fake bool + + // rounds data + KR KnownRounds +} + +// setSamplingPeriod add the Request mask as a random buffer around the sampling +// time to obfuscate it. +func (iu IdentityUse) setSamplingPeriod(rng io.Reader) (IdentityUse, error) { + + // Generate the seed + seed := make([]byte, 32) + if _, err := rng.Read(seed); err != nil { + return IdentityUse{}, errors.WithMessage(err, "Failed to choose ID "+ + "due to rng failure") + } + + h, err := hash.NewCMixHash() + if err != nil { + return IdentityUse{}, err + } + + // Calculate the period offset + periodOffset := randomness.RandInInterval( + big.NewInt(iu.RequestMask.Nanoseconds()), seed, h).Int64() + iu.StartRequest = iu.StartValid.Add(-time.Duration(periodOffset)) + iu.EndRequest = iu.EndValid.Add(iu.RequestMask - time.Duration(periodOffset)) + return iu, nil +} + +type KnownRounds interface { + Checked(rid id.Round) bool + Check(rid id.Round) + Forward(rid id.Round) + RangeUnchecked(newestRid id.Round, roundCheck func(id id.Round) bool) + RangeUncheckedMasked(mask *knownRounds.KnownRounds, + roundCheck knownRounds.RoundCheckFunc, maxChecked int) + RangeUncheckedMaskedRange(mask *knownRounds.KnownRounds, + roundCheck knownRounds.RoundCheckFunc, start, end id.Round, maxChecked int) +} diff --git a/storage/reception/fake.go b/storage/reception/fake.go new file mode 100644 index 0000000000000000000000000000000000000000..38cb63a241cc25122b98ff8b5a1762da0f3994a5 --- /dev/null +++ b/storage/reception/fake.go @@ -0,0 +1,45 @@ +package reception + +import ( + "github.com/pkg/errors" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/id/ephemeral" + "io" + "time" +) + +// generateFakeIdentity generates a fake identity of the given size with the +// given random number generator +func generateFakeIdentity(rng io.Reader, idSize uint, now time.Time) (IdentityUse, error) { + // Randomly generate an identity + randIdBytes := make([]byte, id.ArrIDLen-1) + if _, err := rng.Read(randIdBytes); err != nil { + return IdentityUse{}, errors.WithMessage(err, "failed to "+ + "generate a random identity when none is available") + } + + randID := &id.ID{} + copy(randID[:id.ArrIDLen-1], randIdBytes) + randID.SetType(id.User) + + // Generate the current ephemeral ID from the random identity + ephID, start, end, err := ephemeral.GetId(randID, idSize, now.UnixNano()) + if err != nil { + return IdentityUse{}, errors.WithMessage(err, "failed to generate an "+ + "ephemeral ID for random identity when none is available") + } + + return IdentityUse{ + Identity: Identity{ + EphId: ephID, + Source: randID, + End: end, + ExtraChecks: 0, + StartValid: start, + EndValid: end, + RequestMask: 24 * time.Hour, + Ephemeral: true, + }, + Fake: true, + }, nil +} diff --git a/storage/reception/fake_test.go b/storage/reception/fake_test.go new file mode 100644 index 0000000000000000000000000000000000000000..babf46ec2361207bf1872a21ca326e7b556dd209 --- /dev/null +++ b/storage/reception/fake_test.go @@ -0,0 +1,66 @@ +package reception + +import ( + "encoding/json" + "math" + "math/rand" + "strings" + "testing" + "time" +) + +// Tests Generate Fake identity is consistent and returns a correct result. +func Test_generateFakeIdentity(t *testing.T) { + rng := rand.New(rand.NewSource(42)) + + end, _ := json.Marshal(time.Unix(0, 1258494203759765625)) + startValid, _ := json.Marshal(time.Unix(0, 1258407803759765625)) + endValid, _ := json.Marshal(time.Unix(0, 1258494203759765625)) + expected := "{\"EphId\":[0,0,0,0,0,0,46,197]," + + "\"Source\":[83,140,127,150,177,100,191,27,151,187,159,75,180,114," + + "232,159,91,20,132,242,82,9,201,217,52,62,146,186,9,221,157,82,3]," + + "\"End\":" + string(end) + ",\"ExtraChecks\":0," + + "\"StartValid\":" + string(startValid) + "," + + "\"EndValid\":" + string(endValid) + "," + + "\"RequestMask\":86400000000000,\"Ephemeral\":true," + + "\"StartRequest\":\"0001-01-01T00:00:00Z\"," + + "\"EndRequest\":\"0001-01-01T00:00:00Z\",\"Fake\":true,\"KR\":null}" + + timestamp := time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC) + + received, err := generateFakeIdentity(rng, 15, timestamp) + if err != nil { + t.Errorf("generateFakeIdentity() returned an error: %+v", err) + } + + receivedJson, _ := json.Marshal(received) + + if expected != string(receivedJson) { + t.Errorf("The fake identity was generated incorrectly.\n "+ + "expected: %s\nreceived: %s", expected, receivedJson) + } +} + +// Error path: fails to generate random bytes. +func Test_generateFakeIdentity_RngError(t *testing.T) { + rng := strings.NewReader("") + timestamp := time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC) + + _, err := generateFakeIdentity(rng, 15, timestamp) + if err == nil || !strings.Contains(err.Error(), "failed to generate a random identity") { + t.Errorf("generateFakeIdentity() did not return the correct error on "+ + "failure to generate random bytes: %+v", err) + } +} + +// Error path: fails to get the ephemeral ID. +func Test_generateFakeIdentity_GetEphemeralIdError(t *testing.T) { + rng := rand.New(rand.NewSource(42)) + timestamp := time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC) + + _, err := generateFakeIdentity(rng, math.MaxUint64, timestamp) + if err == nil || !strings.Contains(err.Error(), "ephemeral ID") { + t.Errorf("generateFakeIdentity() did not return the correct error on "+ + "failure to generate ephemeral ID: %+v", err) + } +} diff --git a/storage/reception/identity.go b/storage/reception/identity.go new file mode 100644 index 0000000000000000000000000000000000000000..e64c0dd33c172a5bc4a854c8f6379670d08316f1 --- /dev/null +++ b/storage/reception/identity.go @@ -0,0 +1,94 @@ +package reception + +import ( + "encoding/json" + "github.com/pkg/errors" + "gitlab.com/elixxir/client/storage/versioned" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/id/ephemeral" + "strconv" + "time" +) + +const identityStorageKey = "IdentityStorage" +const identityStorageVersion = 0 + +type Identity struct { + // Identity + EphId ephemeral.Id + Source *id.ID + + // Usage variables + End time.Time // Timestamp when active polling will stop + ExtraChecks uint // Number of extra checks executed as active after the + // ID exits active + + // Polling parameters + StartValid time.Time // Timestamp when the ephID begins being valid + EndValid time.Time // Timestamp when the ephID stops being valid + RequestMask time.Duration // Amount of extra time requested for the poll in + // order to mask the exact valid time for the ID + + // Makes the identity not store on disk + Ephemeral bool +} + +func loadIdentity(kv *versioned.KV) (Identity, error) { + obj, err := kv.Get(identityStorageKey) + if err != nil { + return Identity{}, errors.WithMessage(err, "Failed to load Identity") + } + + r := Identity{} + err = json.Unmarshal(obj.Data, &r) + if err != nil { + return Identity{}, errors.WithMessage(err, "Failed to unmarshal Identity") + } + return r, nil +} + +func (i Identity) store(kv *versioned.KV) error { + // Marshal the registration + regStr, err := json.Marshal(&i) + if err != nil { + return errors.WithMessage(err, "Failed to marshal Identity") + } + + // Create versioned object with data + obj := &versioned.Object{ + Version: identityStorageVersion, + Timestamp: time.Now(), + Data: regStr, + } + + // Store the data + err = kv.Set(identityStorageKey, obj) + if err != nil { + return errors.WithMessage(err, "Failed to store Identity") + } + + return nil +} + +func (i Identity) delete(kv *versioned.KV) error { + return kv.Delete(identityStorageKey) +} + +func (i Identity) calculateKrSize() int { + return int(i.EndValid.Sub(i.StartValid).Seconds()+1) * maxRoundsPerSecond +} + +func (i *Identity) String() string { + return strconv.FormatInt(i.EphId.Int64(), 16) + " " + i.Source.String() +} + +func (i Identity) Equal(b Identity) bool { + return i.EphId == b.EphId && + i.Source.Cmp(b.Source) && + i.End.Equal(b.End) && + i.ExtraChecks == b.ExtraChecks && + i.StartValid.Equal(b.StartValid) && + i.EndValid.Equal(b.EndValid) && + i.RequestMask == b.RequestMask && + i.Ephemeral == b.Ephemeral +} diff --git a/storage/reception/identityUse_test.go b/storage/reception/identityUse_test.go new file mode 100644 index 0000000000000000000000000000000000000000..e54d73e723a4daa8365f34e446c60916a43ca3dd --- /dev/null +++ b/storage/reception/identityUse_test.go @@ -0,0 +1,67 @@ +package reception + +import ( + "math/rand" + "testing" + "time" +) + +func TestIdentityUse_SetSamplingPeriod(t *testing.T) { + rng := rand.New(rand.NewSource(42)) + + const numTests = 1000 + + for i := 0; i < numTests; i++ { + // Generate an identity use + start := randate() + end := start.Add(time.Duration(rand.Uint64() % uint64(92*time.Hour))) + mask := time.Duration(rand.Uint64() % uint64(92*time.Hour)) + iu := IdentityUse{ + Identity: Identity{ + StartValid: start, + EndValid: end, + RequestMask: mask, + }, + } + + // Generate the sampling period + var err error + iu, err = iu.setSamplingPeriod(rng) + if err != nil { + t.Errorf("Errored in generatign sampling "+ + "period on interation %v: %+v", i, err) + } + + // Test that the range between the periods is correct + resultRange := iu.EndRequest.Sub(iu.StartRequest) + expectedRange := iu.EndValid.Sub(iu.StartValid) + iu.RequestMask + + if resultRange != expectedRange { + t.Errorf("The generated sampling period is of the wrong "+ + "size: Expecterd: %s, Received: %s", expectedRange, resultRange) + } + + // Test the sampling range does not exceed a reasonable lower bound + lowerBound := iu.StartValid.Add(-iu.RequestMask) + if !iu.StartRequest.After(lowerBound) { + t.Errorf("Start request exceeds the reasonable lower "+ + "bound: \n\t Bound: %s\n\t Start: %s", lowerBound, iu.StartValid) + } + + // Test the sampling range does not exceed a reasonable upper bound + upperBound := iu.EndValid.Add(iu.RequestMask - time.Millisecond) + if iu.EndRequest.After(upperBound) { + t.Errorf("End request exceeds the reasonable upper bound") + } + } + +} + +func randate() time.Time { + min := time.Date(1970, 1, 0, 0, 0, 0, 0, time.UTC).Unix() + max := time.Date(2070, 1, 0, 0, 0, 0, 0, time.UTC).Unix() + delta := max - min + + sec := rand.Int63n(delta) + min + return time.Unix(sec, 0) +} diff --git a/storage/reception/identity_test.go b/storage/reception/identity_test.go new file mode 100644 index 0000000000000000000000000000000000000000..4657330ebb1cf3359eb3fb34cedba96c74e2e2f9 --- /dev/null +++ b/storage/reception/identity_test.go @@ -0,0 +1,118 @@ +package reception + +import ( + "gitlab.com/elixxir/client/storage/versioned" + "gitlab.com/elixxir/ekv" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/id/ephemeral" + "math/rand" + "testing" + "time" +) + +func TestIdentity_EncodeDecode(t *testing.T) { + kv := versioned.NewKV(make(ekv.Memstore)) + r := Identity{ + EphId: ephemeral.Id{}, + Source: &id.Permissioning, + End: time.Now().Round(0), + ExtraChecks: 12, + StartValid: time.Now().Round(0), + EndValid: time.Now().Round(0), + RequestMask: 2 * time.Hour, + Ephemeral: false, + } + + err := r.store(kv) + if err != nil { + t.Errorf("Failed to store: %+v", err) + } + + rLoad, err := loadIdentity(kv) + if err != nil { + t.Errorf("Failed to load: %+v", err) + } + + if !r.Equal(rLoad) { + t.Errorf("Registrations are not the same\nsaved: %+v\nloaded: %+v", + r, rLoad) + } +} + +func TestIdentity_Delete(t *testing.T) { + kv := versioned.NewKV(make(ekv.Memstore)) + r := Identity{ + EphId: ephemeral.Id{}, + Source: &id.Permissioning, + End: time.Now().Round(0), + ExtraChecks: 12, + StartValid: time.Now().Round(0), + EndValid: time.Now().Round(0), + RequestMask: 2 * time.Hour, + Ephemeral: false, + } + + err := r.store(kv) + if err != nil { + t.Errorf("Failed to store: %s", err) + } + + err = r.delete(kv) + if err != nil { + t.Errorf("Failed to delete: %s", err) + } + + _, err = loadIdentity(kv) + if err == nil { + t.Error("Load after delete succeeded.") + } +} + +func TestIdentity_String(t *testing.T) { + rng := rand.New(rand.NewSource(42)) + timestamp := time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC) + received, _ := generateFakeIdentity(rng, 15, timestamp) + expected := "-1763 U4x/lrFkvxuXu59LtHLon1sUhPJSCcnZND6SugndnVID" + + s := received.String() + if s != expected { + t.Errorf("String did not return the correct value."+ + "\nexpected: %s\nreceived: %s", expected, s) + } +} + +func TestIdentity_CalculateKrSize(t *testing.T) { + deltas := []time.Duration{0, 2 * time.Second, 2 * time.Hour, 36 * time.Hour, + time.Duration(rand.Uint32()) * time.Millisecond} + for _, d := range deltas { + expected := int(d.Seconds()+1) * maxRoundsPerSecond + now := time.Now() + i := Identity{ + StartValid: now, + EndValid: now.Add(d), + } + + krSize := i.calculateKrSize() + if krSize != expected { + t.Errorf("krSize is not correct.\nexpected: %d\nrecieved: %d", + expected, krSize) + } + } +} + +func TestIdentity_Equal(t *testing.T) { + timestamp := time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC) + a, _ := generateFakeIdentity(rand.New(rand.NewSource(42)), 15, timestamp) + b, _ := generateFakeIdentity(rand.New(rand.NewSource(42)), 15, timestamp) + c, _ := generateFakeIdentity(rand.New(rand.NewSource(42)), 15, time.Now()) + + if !a.Identity.Equal(b.Identity) { + t.Errorf("Equal() found two equal identities as unequal."+ + "\na: %s\nb: %s", a.String(), b.String()) + } + + if a.Identity.Equal(c.Identity) { + t.Errorf("Equal() found two unequal identities as equal."+ + "\na: %s\nc: %s", a.String(), c.String()) + } +} diff --git a/storage/reception/registration.go b/storage/reception/registration.go new file mode 100644 index 0000000000000000000000000000000000000000..0df67b81c8be0be71b58158a5f76f1f53ce16ee0 --- /dev/null +++ b/storage/reception/registration.go @@ -0,0 +1,118 @@ +package reception + +import ( + "github.com/pkg/errors" + "gitlab.com/elixxir/client/storage/utility" + "gitlab.com/elixxir/client/storage/versioned" + "gitlab.com/elixxir/primitives/knownRounds" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/id/ephemeral" + "strconv" + "time" +) + +const maxRoundsPerSecond = 100 +const knownRoundsStorageKey = "krStorage" + +type registration struct { + Identity + knownRounds *knownRounds.KnownRounds + knownRoundsStorage *utility.KnownRounds + kv *versioned.KV +} + +func newRegistration(reg Identity, kv *versioned.KV) (*registration, error) { + // Round the times to remove the monotonic clocks for future saving + reg.StartValid = reg.StartValid.Round(0) + reg.EndValid = reg.EndValid.Round(0) + reg.End = reg.End.Round(0) + + now := time.Now() + + // Do edge checks to determine if the identity is valid + if now.After(reg.End) && reg.ExtraChecks < 1 { + return nil, errors.New("Cannot create a registration for an " + + "identity which has expired") + } + + // Set the prefix + kv = kv.Prefix(regPrefix(reg.EphId, reg.Source, reg.StartValid)) + + r := ®istration{ + Identity: reg, + knownRounds: knownRounds.NewKnownRound(reg.calculateKrSize()), + kv: kv, + } + + // If this is not ephemeral, then store everything + if !reg.Ephemeral { + // Store known rounds + var err error + r.knownRoundsStorage, err = utility.NewKnownRounds(kv, knownRoundsStorageKey, r.knownRounds) + if err != nil { + return nil, errors.WithMessage(err, "failed to store known rounds") + } + + // Store the registration + if err = reg.store(kv); err != nil { + return nil, errors.WithMessage(err, "failed to store registration") + } + } + + return r, nil +} + +func loadRegistration(EphId ephemeral.Id, Source *id.ID, startValid time.Time, + kv *versioned.KV) (*registration, error) { + + kv = kv.Prefix(regPrefix(EphId, Source, startValid)) + + reg, err := loadIdentity(kv) + if err != nil { + return nil, errors.WithMessagef(err, "Failed to load identity "+ + "for %s", regPrefix(EphId, Source, startValid)) + } + + kr, err := utility.LoadKnownRounds(kv, knownRoundsStorageKey, reg.calculateKrSize()) + if err != nil { + return nil, errors.WithMessagef(err, "Failed to load known "+ + "rounds for %s", regPrefix(EphId, Source, startValid)) + } + + r := ®istration{ + Identity: reg, + knownRoundsStorage: kr, + kv: kv, + } + + return r, nil +} + +func (r *registration) Delete() error { + if !r.Ephemeral { + if err := r.knownRoundsStorage.Delete(); err != nil { + return errors.WithMessagef(err, "Failed to delete registration "+ + "known rounds %s", r) + } + if err := r.delete(r.kv); err != nil { + return errors.WithMessagef(err, "Failed to delete registration "+ + "public data %s", r) + } + } + + return nil +} + +func (r registration) getKR() KnownRounds { + if r.Ephemeral { + return r.knownRounds + } else { + return r.knownRoundsStorage + } +} + +func regPrefix(EphId ephemeral.Id, Source *id.ID, startTime time.Time) string { + return "receptionRegistration_" + + strconv.FormatInt(EphId.Int64(), 16) + Source.String() + + strconv.FormatInt(startTime.Round(0).UnixNano(), 10) +} diff --git a/storage/reception/registration_test.go b/storage/reception/registration_test.go new file mode 100644 index 0000000000000000000000000000000000000000..ffb155fe3b0e08949fd66b73975ac98cb4733692 --- /dev/null +++ b/storage/reception/registration_test.go @@ -0,0 +1,158 @@ +package reception + +import ( + "gitlab.com/elixxir/client/storage/utility" + "gitlab.com/elixxir/client/storage/versioned" + "gitlab.com/elixxir/ekv" + "math/rand" + "strings" + "testing" + "time" +) + +func TestNewRegistration_Failed(t *testing.T) { + // Generate an identity for use + rng := rand.New(rand.NewSource(42)) + timestamp := time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC) + idu, _ := generateFakeIdentity(rng, 15, timestamp) + id := idu.Identity + kv := versioned.NewKV(make(ekv.Memstore)) + + id.End = time.Time{} + id.ExtraChecks = 0 + + _, err := newRegistration(id, kv) + if err == nil || !strings.Contains(err.Error(), "Cannot create a registration for an identity which has expired") { + t.Error("Registration creation succeeded with expired identity.") + } +} + +func TestNewRegistration_Ephemeral(t *testing.T) { + // Generate an identity for use + rng := rand.New(rand.NewSource(42)) + timestamp := time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC) + idu, _ := generateFakeIdentity(rng, 15, timestamp) + id := idu.Identity + kv := versioned.NewKV(make(ekv.Memstore)) + + id.End = time.Now().Add(1 * time.Hour) + id.ExtraChecks = 2 + id.Ephemeral = true + + reg, err := newRegistration(id, kv) + if err != nil { + t.Fatalf("Registration creation failed when it should have "+ + "succeeded: %+v", err) + } + + if reg.knownRounds == nil { + t.Error("Ephemeral identity does not have a known rounds.") + } + + if reg.knownRoundsStorage != nil { + t.Error("Ephemeral identity has a known rounds storage.") + } + + // Check if the known rounds is stored, it should not be + if _, err = utility.LoadKnownRounds(reg.kv, knownRoundsStorageKey, id.calculateKrSize()); err == nil { + t.Error("Ephemeral identity stored the known rounds when it should not have.") + } + + if _, err = reg.kv.Get(identityStorageKey); err == nil { + t.Error("Ephemeral identity stored the identity when it should not have.") + } +} + +func TestNewRegistration_Persistent(t *testing.T) { + // Generate an identity for use + rng := rand.New(rand.NewSource(42)) + timestamp := time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC) + idu, _ := generateFakeIdentity(rng, 15, timestamp) + id := idu.Identity + kv := versioned.NewKV(make(ekv.Memstore)) + + id.End = time.Now().Add(1 * time.Hour) + id.ExtraChecks = 2 + id.Ephemeral = false + + reg, err := newRegistration(id, kv) + if err != nil { + t.Fatalf("Registration creation failed when it should have "+ + "succeeded: %+v", err) + } + + if reg.knownRounds == nil { + t.Error("Persistent identity does not have a known rounds.") + } + + if reg.knownRoundsStorage == nil { + t.Error("Persistent identity does not have a known rounds storage.") + } + + // Check if the known rounds is stored, it should not be + if _, err = utility.LoadKnownRounds(reg.kv, knownRoundsStorageKey, id.calculateKrSize()); err != nil { + t.Errorf("Persistent identity did not store known rounds when "+ + "it should: %+v", err) + } + + if _, err = reg.kv.Get(identityStorageKey); err != nil { + t.Error("Persistent identity did not store the identity when it should.") + } +} + +func TestLoadRegistration(t *testing.T) { + // Generate an identity for use + rng := rand.New(rand.NewSource(42)) + timestamp := time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC) + idu, _ := generateFakeIdentity(rng, 15, timestamp) + id := idu.Identity + kv := versioned.NewKV(make(ekv.Memstore)) + + id.End = time.Now().Add(1 * time.Hour) + id.ExtraChecks = 2 + id.Ephemeral = false + + _, err := newRegistration(id, kv) + if err != nil { + t.Fatalf("Registration creation failed when it should have "+ + "succeeded: %+v", err) + } + + reg, err := loadRegistration(idu.EphId, idu.Source, idu.StartValid, kv) + if err != nil { + t.Fatalf("Registration loading failed: %+v", err) + } + + if reg.knownRounds != nil { + t.Error("Loading has a separated known rounds, it should not have.") + } + + if reg.knownRoundsStorage == nil { + t.Error("Loading identity does not have a known rounds storage.") + } +} + +// TODO: finish +func Test_registration_Delete(t *testing.T) { + // Generate an identity for use + rng := rand.New(rand.NewSource(42)) + timestamp := time.Date(2009, 11, 17, 20, 34, 58, 651387237, time.UTC) + idu, _ := generateFakeIdentity(rng, 15, timestamp) + id := idu.Identity + kv := versioned.NewKV(make(ekv.Memstore)) + + id.End = time.Now().Add(1 * time.Hour) + id.ExtraChecks = 2 + id.Ephemeral = false + + r, err := newRegistration(id, kv) + if err != nil { + t.Fatalf("Registration creation failed when it should have "+ + "succeeded: %+v", err) + } + + err = r.Delete() + if err != nil { + t.Errorf("Delete() returned an error: %+v", err) + } +} diff --git a/storage/reception/store.go b/storage/reception/store.go new file mode 100644 index 0000000000000000000000000000000000000000..ec987af518ce38a03c222ac6d960eebf1b9b69ab --- /dev/null +++ b/storage/reception/store.go @@ -0,0 +1,314 @@ +package reception + +import ( + "bytes" + "encoding/json" + "github.com/pkg/errors" + jww "github.com/spf13/jwalterweatherman" + "gitlab.com/elixxir/client/storage/versioned" + "gitlab.com/elixxir/crypto/hash" + "gitlab.com/xx_network/crypto/randomness" + "gitlab.com/xx_network/primitives/id" + "gitlab.com/xx_network/primitives/id/ephemeral" + "io" + "math/big" + "strconv" + "sync" + "time" +) + +const receptionPrefix = "reception" +const receptionStoreStorageKey = "receptionStoreKey" +const receptionStoreStorageVersion = 0 +const receptionIDSizeStorageKey = "receptionIDSizeKey" +const receptionIDSizeStorageVersion = 0 +const defaultIDSize = 12 + +type Store struct { + // Identities which are being actively checked + active []*registration + idSize int + + kv *versioned.KV + + mux sync.Mutex +} + +type storedReference struct { + Eph ephemeral.Id + Source *id.ID + StartValid time.Time +} + +// NewStore creates a new reception store that starts empty. +func NewStore(kv *versioned.KV) *Store { + kv = kv.Prefix(receptionPrefix) + s := &Store{ + active: make([]*registration, 0), + idSize: defaultIDSize * 2, + kv: kv, + } + + // Store the empty list + if err := s.save(); err != nil { + jww.FATAL.Panicf("Failed to save new reception store: %+v", err) + } + + // Update the size so queries can be made + s.UpdateIdSize(defaultIDSize) + + return s +} + +func LoadStore(kv *versioned.KV) *Store { + kv = kv.Prefix(receptionPrefix) + s := &Store{ + kv: kv, + } + + // Load the versioned object for the reception list + vo, err := kv.Get(receptionStoreStorageKey) + if err != nil { + jww.FATAL.Panicf("Failed to get the reception storage list: %+v", err) + } + + identities := make([]storedReference, len(s.active)) + err = json.Unmarshal(vo.Data, &identities) + if err != nil { + jww.FATAL.Panicf("Failed to unmarshal the reception storage list: %+v", err) + } + + s.active = make([]*registration, len(identities)) + for i, sr := range identities { + s.active[i], err = loadRegistration(sr.Eph, sr.Source, sr.StartValid, s.kv) + if err != nil { + jww.FATAL.Panicf("Failed to load registration for %s: %+v", + regPrefix(sr.Eph, sr.Source, sr.StartValid), err) + } + } + + // Load the ephemeral ID length + vo, err = kv.Get(receptionIDSizeStorageKey) + if err != nil { + jww.FATAL.Panicf("Failed to get the reception ID size: %+v", err) + } + + if s.idSize, err = strconv.Atoi(string(vo.Data)); err != nil { + jww.FATAL.Panicf("Failed to unmarshal the reception ID size: %+v", + err) + } + + return s +} + +func (s *Store) save() error { + identities := s.makeStoredReferences() + + data, err := json.Marshal(&identities) + if err != nil { + return errors.WithMessage(err, "failed to store reception store") + } + + // Create versioned object with data + obj := &versioned.Object{ + Version: receptionStoreStorageVersion, + Timestamp: time.Now(), + Data: data, + } + + err = s.kv.Set(receptionStoreStorageKey, obj) + if err != nil { + return errors.WithMessage(err, "Failed to store reception store") + } + + return nil +} + +// makeStoredReferences generates a reference of any non-ephemeral identities +// for storage. +func (s *Store) makeStoredReferences() []storedReference { + identities := make([]storedReference, len(s.active)) + + i := 0 + for _, reg := range s.active { + if !reg.Ephemeral { + identities[i] = storedReference{ + Eph: reg.EphId, + Source: reg.Source, + StartValid: reg.StartValid.Round(0), + } + i++ + } + } + + return identities[:i] +} + +func (s *Store) GetIdentity(rng io.Reader) (IdentityUse, error) { + s.mux.Lock() + defer s.mux.Unlock() + + now := time.Now() + + // Remove any now expired identities + s.prune(now) + + var identity IdentityUse + var err error + + // If the list is empty, then we return a randomly generated identity to + // poll with so we can continue tracking the network and to further + // obfuscate network identities. + if len(s.active) == 0 { + identity, err = generateFakeIdentity(rng, uint(s.idSize), now) + if err != nil { + jww.FATAL.Panicf("Failed to generate a new ID when none "+ + "available: %+v", err) + } + } else { + identity, err = s.selectIdentity(rng, now) + if err != nil { + jww.FATAL.Panicf("Failed to select an ID: %+v", err) + } + } + + // Calculate the sampling period + identity, err = identity.setSamplingPeriod(rng) + if err != nil { + jww.FATAL.Panicf("Failed to calculate the sampling period: "+ + "%+v", err) + } + + return identity, nil +} + +func (s *Store) AddIdentity(identity Identity) error { + s.mux.Lock() + defer s.mux.Unlock() + + reg, err := newRegistration(identity, s.kv) + if err != nil { + return errors.WithMessage(err, "failed to add new identity to "+ + "reception store") + } + + s.active = append(s.active, reg) + if !identity.Ephemeral { + if err := s.save(); err != nil { + jww.FATAL.Panicf("Failed to save reception store after identity " + + "addition") + } + } + + return nil +} + +func (s *Store) RemoveIdentity(ephID ephemeral.Id) { + s.mux.Lock() + defer s.mux.Unlock() + + for i := 0; i < len(s.active); i++ { + inQuestion := s.active[i] + if bytes.Equal(inQuestion.EphId[:], ephID[:]) { + s.active = append(s.active[:i], s.active[i+1:]...) + err := inQuestion.Delete() + if err != nil { + jww.FATAL.Panicf("Failed to delete identity: %+v", err) + } + if !inQuestion.Ephemeral { + if err := s.save(); err != nil { + jww.FATAL.Panicf("Failed to save reception store after " + + "identity removal") + } + } + return + } + } +} + +func (s *Store) UpdateIdSize(idSize uint) { + s.mux.Lock() + defer s.mux.Unlock() + if s.idSize == int(idSize) { + return + } + + s.idSize = int(idSize) + + // Store the ID size + obj := &versioned.Object{ + Version: receptionIDSizeStorageVersion, + Timestamp: time.Now(), + Data: []byte(strconv.Itoa(s.idSize)), + } + + err := s.kv.Set(receptionIDSizeStorageKey, obj) + if err != nil { + jww.FATAL.Panicf("Failed to store reception ID size: %+v", err) + } +} + +func (s *Store) GetIDSize() uint { + s.mux.Lock() + defer s.mux.Unlock() + return uint(s.idSize) +} + +func (s *Store) prune(now time.Time) { + lengthBefore := len(s.active) + + // Prune the list + for i := 0; i < len(s.active); i++ { + inQuestion := s.active[i] + if now.After(inQuestion.End) && inQuestion.ExtraChecks == 0 { + if err := inQuestion.Delete(); err != nil { + jww.ERROR.Printf("Failed to delete Identity for %s: "+ + "%+v", inQuestion, err) + } + + s.active = append(s.active[:i], s.active[i+1:]...) + + i-- + } + } + + // Save the list if it changed + if lengthBefore != len(s.active) { + if err := s.save(); err != nil { + jww.FATAL.Panicf("Failed to store reception storage") + } + } +} + +func (s *Store) selectIdentity(rng io.Reader, now time.Time) (IdentityUse, error) { + // Choose a member from the list + var selected *registration + + if len(s.active) == 1 { + selected = s.active[0] + } else { + seed := make([]byte, 32) + if _, err := rng.Read(seed); err != nil { + return IdentityUse{}, errors.WithMessage(err, "Failed to "+ + "choose ID due to rng failure") + } + h, err := hash.NewCMixHash() + if err != nil { + return IdentityUse{}, err + } + + selectedNum := randomness.RandInInterval( + big.NewInt(int64(len(s.active)-1)), seed, h) + selected = s.active[selectedNum.Uint64()] + } + + if now.After(selected.End) { + selected.ExtraChecks-- + } + + return IdentityUse{ + Identity: selected.Identity, + Fake: false, + KR: selected.getKR(), + }, nil +} diff --git a/storage/reception/store_test.go b/storage/reception/store_test.go new file mode 100644 index 0000000000000000000000000000000000000000..bb6ad2b6fc395cd9324136383bb3c6065044621f --- /dev/null +++ b/storage/reception/store_test.go @@ -0,0 +1,291 @@ +package reception + +import ( + "bytes" + "encoding/json" + "gitlab.com/elixxir/client/storage/versioned" + "gitlab.com/elixxir/ekv" + "math/rand" + "reflect" + "testing" + "time" +) + +func TestNewStore(t *testing.T) { + kv := versioned.NewKV(make(ekv.Memstore)) + expected := &Store{ + active: make([]*registration, 0), + idSize: defaultIDSize, + kv: kv, + } + + s := NewStore(kv) + + if !reflect.DeepEqual([]*registration{}, s.active) || s.idSize != defaultIDSize { + t.Errorf("NewStore() failed to return the expected Store."+ + "\nexpected: %+v\nreceived: %+v", expected, s) + } + + obj, err := s.kv.Get(receptionStoreStorageKey) + if err != nil { + t.Fatalf("Failed to load store from KV: %+v", err) + } + + var testStoredReference []storedReference + err = json.Unmarshal(obj.Data, &testStoredReference) + if err != nil { + t.Errorf("Failed to unmarshal []storedReference: %+v", err) + } + if !reflect.DeepEqual([]storedReference{}, testStoredReference) { + t.Errorf("Failed to retreive expected storedReference from KV."+ + "\nexpected: %#v\nreceived: %#v", []storedReference{}, testStoredReference) + } +} + +func TestLoadStore(t *testing.T) { + kv := versioned.NewKV(make(ekv.Memstore)) + s := NewStore(kv) + prng := rand.New(rand.NewSource(42)) + + // Fill active registration with fake identities + for i := 0; i < 10; i++ { + testID, err := generateFakeIdentity(prng, 15, time.Now()) + if err != nil { + t.Fatalf("Failed to generate fake ID: %+v", err) + } + testID.Ephemeral = false + if s.AddIdentity(testID.Identity) != nil { + t.Fatalf("Failed to AddIdentity: %+v", err) + } + } + + err := s.save() + if err != nil { + t.Errorf("save() produced an error: %+v", err) + } + + testStore := LoadStore(kv) + for i, active := range testStore.active { + s.active[i].knownRounds = nil + if !s.active[i].Equal(active.Identity) { + t.Errorf("Failed to generate expected Store."+ + "\nexpected: %#v\nreceived: %#v", s.active[i], active) + } + } +} + +func TestStore_save(t *testing.T) { + kv := versioned.NewKV(make(ekv.Memstore)) + s := NewStore(kv) + prng := rand.New(rand.NewSource(42)) + + // Fill active registration with fake identities + for i := 0; i < 10; i++ { + testID, err := generateFakeIdentity(prng, 15, time.Now()) + if err != nil { + t.Fatalf("Failed to generate fake ID: %+v", err) + } + testID.Ephemeral = false + s.active = append(s.active, ®istration{Identity: testID.Identity}) + } + + expected := s.makeStoredReferences() + + err := s.save() + if err != nil { + t.Errorf("save() produced an error: %+v", err) + } + + obj, err := kv.Prefix(receptionPrefix).Get(receptionStoreStorageKey) + if err != nil { + t.Errorf("Get() produced an error: %+v", err) + } + + expectedData, err := json.Marshal(expected) + if obj.Version != receptionStoreStorageVersion { + t.Errorf("Rectrieved version incorrect.\nexpected: %d\nreceived: %d", + receptionStoreStorageVersion, obj.Version) + } + + if !bytes.Equal(expectedData, obj.Data) { + t.Errorf("Rectrieved data incorrect.\nexpected: %s\nreceived: %s", + expectedData, obj.Data) + } +} + +func TestStore_makeStoredReferences(t *testing.T) { + s := NewStore(versioned.NewKV(make(ekv.Memstore))) + prng := rand.New(rand.NewSource(42)) + expected := make([]storedReference, 0) + + // Fill active registration with fake identities + for i := 0; i < 10; i++ { + testID, err := generateFakeIdentity(prng, 15, time.Now()) + if err != nil { + t.Fatalf("Failed to generate fake ID: %+v", err) + } + if i%2 == 0 { + testID.Ephemeral = false + expected = append(expected, storedReference{ + Eph: testID.EphId, + Source: testID.Source, + StartValid: testID.StartValid.Round(0), + }) + } + s.active = append(s.active, ®istration{Identity: testID.Identity}) + } + + sr := s.makeStoredReferences() + if !reflect.DeepEqual(expected, sr) { + t.Errorf("Failed to generate expected list of identities."+ + "\nexpected: %+v\nreceived: %+v", expected, sr) + } +} + +func TestStore_GetIdentity(t *testing.T) { + kv := versioned.NewKV(make(ekv.Memstore)) + s := NewStore(kv) + prng := rand.New(rand.NewSource(42)) + testID, err := generateFakeIdentity(prng, 15, time.Now()) + if err != nil { + t.Fatalf("Failed to generate fake ID: %+v", err) + } + if s.AddIdentity(testID.Identity) != nil { + t.Errorf("AddIdentity() produced an error: %+v", err) + } + + idu, err := s.GetIdentity(prng) + if err != nil { + t.Errorf("GetIdentity() produced an error: %+v", err) + } + + if !testID.Equal(idu.Identity) { + t.Errorf("GetIdentity() did not return the expected Identity."+ + "\nexpected: %s\nreceived: %s", testID.String(), idu.String()) + } +} + +func TestStore_AddIdentity(t *testing.T) { + kv := versioned.NewKV(make(ekv.Memstore)) + s := NewStore(kv) + prng := rand.New(rand.NewSource(42)) + testID, err := generateFakeIdentity(prng, 15, time.Now()) + if err != nil { + t.Fatalf("Failed to generate fake ID: %+v", err) + } + + err = s.AddIdentity(testID.Identity) + if err != nil { + t.Errorf("AddIdentity() produced an error: %+v", err) + } + + if !s.active[0].Identity.Equal(testID.Identity) { + t.Errorf("Failed to get expected Identity.\nexpected: %s\nreceived: %s", + testID.Identity.String(), s.active[0]) + } +} + +func TestStore_RemoveIdentity(t *testing.T) { + kv := versioned.NewKV(make(ekv.Memstore)) + s := NewStore(kv) + prng := rand.New(rand.NewSource(42)) + testID, err := generateFakeIdentity(prng, 15, time.Now()) + if err != nil { + t.Fatalf("Failed to generate fake ID: %+v", err) + } + err = s.AddIdentity(testID.Identity) + if err != nil { + t.Fatalf("AddIdentity() produced an error: %+v", err) + } + s.RemoveIdentity(testID.EphId) + + if len(s.active) != 0 { + t.Errorf("RemoveIdentity() failed to remove: %+v", s.active) + } +} + +func TestStore_UpdateIdSize(t *testing.T) { + kv := versioned.NewKV(make(ekv.Memstore)) + s := NewStore(kv) + newSize := s.idSize * 2 + + s.UpdateIdSize(uint(newSize)) + + if s.idSize != newSize { + t.Errorf("UpdateIdSize() failed to update the size."+ + "\nexpected: %d\nrecieved: %d", newSize, s.idSize) + } +} + +func TestStore_prune(t *testing.T) { + kv := versioned.NewKV(make(ekv.Memstore)) + s := NewStore(kv) + prng := rand.New(rand.NewSource(42)) + runs := 10 + expected := make([]*registration, runs/2) + + for i := 0; i < runs; i++ { + timestamp := time.Now() + if i%2 == 0 { + timestamp = timestamp.Add(24 * time.Hour) + } + testID, err := generateFakeIdentity(prng, 15, timestamp) + if err != nil { + t.Fatalf("Failed to generate fake ID: %+v", err) + } + err = s.AddIdentity(testID.Identity) + if err != nil { + t.Fatalf("AddIdentity() produced an error: %+v", err) + } + if i%2 == 0 { + expected[i/2] = s.active[i] + } + } + + s.prune(time.Now().Add(24 * time.Hour)) + + for i, reg := range s.active { + if !reg.Equal(expected[i].Identity) { + t.Errorf("Unexpected identity (%d).\nexpected: %+v\nreceived: %+v", + i, expected[i], reg) + } + } +} + +func TestStore_selectIdentity(t *testing.T) { + kv := versioned.NewKV(make(ekv.Memstore)) + s := NewStore(kv) + prng := rand.New(rand.NewSource(42)) + runs := 10 + expectedReg := make([]*registration, runs) + + for i := 0; i < runs; i++ { + testID, err := generateFakeIdentity(prng, 15, time.Now()) + if err != nil { + t.Fatalf("Failed to generate fake ID: %+v", err) + } + err = s.AddIdentity(testID.Identity) + if err != nil { + t.Fatalf("AddIdentity() produced an error: %+v", err) + } + expectedReg[i] = s.active[i] + } + + for i := 0; i < runs; i++ { + idu, err := s.selectIdentity(prng, time.Now()) + if err != nil { + t.Errorf("selectIdentity() produced an error: %+v", err) + } + + var found bool + for _, expected := range expectedReg { + if idu.Equal(expected.Identity) { + found = true + break + } + } + if !found { + t.Errorf("Unexpected Identity returned.\nreceived: %+v", idu) + } + } +} diff --git a/storage/regStatus.go b/storage/regStatus.go index 52f74dd10bf98631892237b1820ba31b105c8ecd..ec18a0cfcb92aaa9c154f23528a40a96f202273f 100644 --- a/storage/regStatus.go +++ b/storage/regStatus.go @@ -27,7 +27,7 @@ const ( UDBComplete RegistrationStatus = 30000 // Set upon completion of RegisterWithUdb ) -// stringer for Registration Status +// stringer for Identity Status func (rs RegistrationStatus) String() string { switch rs { case NotStarted: @@ -35,9 +35,9 @@ func (rs RegistrationStatus) String() string { case KeyGenComplete: return "Key Generation Complete" case PermissioningComplete: - return "Permissioning Registration Complete" + return "Permissioning Identity Complete" case UDBComplete: - return "User Discovery Registration Complete" + return "User Discovery Identity Complete" default: return fmt.Sprintf("Unknown registration state %v", uint32(rs)) } diff --git a/storage/session.go b/storage/session.go index 44278489c94d868f1b76556c2375c8fe8981d298..ce575952455fd0d84cb24d9497ce7acb80fe6f8c 100644 --- a/storage/session.go +++ b/storage/session.go @@ -18,6 +18,7 @@ import ( "gitlab.com/elixxir/client/storage/conversation" "gitlab.com/elixxir/client/storage/e2e" "gitlab.com/elixxir/client/storage/partition" + "gitlab.com/elixxir/client/storage/reception" "gitlab.com/elixxir/client/storage/user" "gitlab.com/elixxir/client/storage/utility" "gitlab.com/elixxir/client/storage/versioned" @@ -56,6 +57,7 @@ type Session struct { criticalRawMessages *utility.CmixMessageBuffer garbledMessages *utility.MeteredCmixMessageBuffer checkedRounds *utility.KnownRounds + reception *reception.Store } // Initialize a new Session object @@ -116,13 +118,6 @@ func New(baseDir, password string, u userInterface.User, cmixGrp, return nil, errors.WithMessage(err, "Failed to create garbledMessages buffer") } - s.checkedRounds, err = utility.NewKnownRounds(s.kv, checkedRoundsKey, CheckRoundsMaxSize) - if err != nil { - return nil, errors.WithMessage(err, "Failed to create checkedRounds") - } - // There is no round id 0 - s.checkedRounds.Check(0) - s.criticalMessages, err = utility.NewE2eMessageBuffer(s.kv, criticalMessagesKey) if err != nil { return nil, errors.WithMessage(err, "Failed to create e2e critical message buffer") @@ -136,6 +131,8 @@ func New(baseDir, password string, u userInterface.User, cmixGrp, s.conversations = conversation.NewStore(s.kv) s.partition = partition.New(s.kv) + s.reception = reception.NewStore(s.kv) + return s, nil } @@ -197,6 +194,8 @@ func Load(baseDir, password string, rng *fastRNG.StreamGenerator) (*Session, err s.conversations = conversation.NewStore(s.kv) s.partition = partition.New(s.kv) + s.reception = reception.LoadStore(s.kv) + return s, nil } @@ -224,6 +223,12 @@ func (s *Session) Auth() *auth.Store { return s.auth } +func (s *Session) Reception() *reception.Store { + s.mux.RLock() + defer s.mux.RUnlock() + return s.reception +} + func (s *Session) GetCriticalMessages() *utility.E2eMessageBuffer { s.mux.RLock() defer s.mux.RUnlock() @@ -344,5 +349,6 @@ func InitTestingSession(i interface{}) *Session { s.conversations = conversation.NewStore(s.kv) s.partition = partition.New(s.kv) + s.reception = reception.NewStore(s.kv) return s } diff --git a/storage/user/regValidationSig.go b/storage/user/regValidationSig.go index 215526936b02440a3ac6a8c84d2ee9b6d96a135b..a64b33d4d713acefe6dad14eb8d1c3a056188d66 100644 --- a/storage/user/regValidationSig.go +++ b/storage/user/regValidationSig.go @@ -17,7 +17,7 @@ const currentRegValidationSigVersion = 0 const transmissionRegValidationSigKey = "transmissionRegistrationValidationSignature" const receptionRegValidationSigKey = "receptionRegistrationValidationSignature" -// Returns the transmission Registration Validation Signature stored in RAM. May return +// Returns the transmission Identity Validation Signature stored in RAM. May return // nil of no signature is stored func (u *User) GetTransmissionRegistrationValidationSignature() []byte { u.rvsMux.RLock() @@ -25,7 +25,7 @@ func (u *User) GetTransmissionRegistrationValidationSignature() []byte { return u.transmissionRegValidationSig } -// Returns the reception Registration Validation Signature stored in RAM. May return +// Returns the reception Identity Validation Signature stored in RAM. May return // nil of no signature is stored func (u *User) GetReceptionRegistrationValidationSignature() []byte { u.rvsMux.RLock() @@ -33,7 +33,7 @@ func (u *User) GetReceptionRegistrationValidationSignature() []byte { return u.receptionRegValidationSig } -// Loads the transmission Registration Validation Signature if it exists in the ekv +// Loads the transmission Identity Validation Signature if it exists in the ekv func (u *User) loadTransmissionRegistrationValidationSignature() { u.rvsMux.Lock() obj, err := u.kv.Get(transmissionRegValidationSigKey) @@ -43,7 +43,7 @@ func (u *User) loadTransmissionRegistrationValidationSignature() { u.rvsMux.Unlock() } -// Loads the reception Registration Validation Signature if it exists in the ekv +// Loads the reception Identity Validation Signature if it exists in the ekv func (u *User) loadReceptionRegistrationValidationSignature() { u.rvsMux.Lock() obj, err := u.kv.Get(receptionRegValidationSigKey) @@ -53,7 +53,7 @@ func (u *User) loadReceptionRegistrationValidationSignature() { u.rvsMux.Unlock() } -// Sets the Registration Validation Signature if it is not set and stores it in +// Sets the Identity Validation Signature if it is not set and stores it in // the ekv func (u *User) SetTransmissionRegistrationValidationSignature(b []byte) { u.rvsMux.Lock() @@ -61,7 +61,7 @@ func (u *User) SetTransmissionRegistrationValidationSignature(b []byte) { //check if the signature already exists if u.transmissionRegValidationSig != nil { - jww.FATAL.Panicf("cannot overwrite existing transmission Registration Validation Signature") + jww.FATAL.Panicf("cannot overwrite existing transmission Identity Validation Signature") } obj := &versioned.Object{ @@ -72,14 +72,14 @@ func (u *User) SetTransmissionRegistrationValidationSignature(b []byte) { err := u.kv.Set(transmissionRegValidationSigKey, obj) if err != nil { - jww.FATAL.Panicf("Failed to store the transmission Registration Validation "+ + jww.FATAL.Panicf("Failed to store the transmission Identity Validation "+ "Signature: %s", err) } u.transmissionRegValidationSig = b } -// Sets the Registration Validation Signature if it is not set and stores it in +// Sets the Identity Validation Signature if it is not set and stores it in // the ekv func (u *User) SetReceptionRegistrationValidationSignature(b []byte) { u.rvsMux.Lock() @@ -87,7 +87,7 @@ func (u *User) SetReceptionRegistrationValidationSignature(b []byte) { //check if the signature already exists if u.receptionRegValidationSig != nil { - jww.FATAL.Panicf("cannot overwrite existing reception Registration Validation Signature") + jww.FATAL.Panicf("cannot overwrite existing reception Identity Validation Signature") } obj := &versioned.Object{ @@ -98,7 +98,7 @@ func (u *User) SetReceptionRegistrationValidationSignature(b []byte) { err := u.kv.Set(receptionRegValidationSigKey, obj) if err != nil { - jww.FATAL.Panicf("Failed to store the reception Registration Validation "+ + jww.FATAL.Panicf("Failed to store the reception Identity Validation "+ "Signature: %s", err) } diff --git a/storage/utility/knownRounds.go b/storage/utility/knownRounds.go index 767ad917c4e57f7e8040deb58713e069fd2b6596..72f4833d7158483fb88c881434a7ed8f4009852b 100644 --- a/storage/utility/knownRounds.go +++ b/storage/utility/knownRounds.go @@ -35,10 +35,10 @@ type KnownRounds struct { // NewKnownRounds creates a new empty KnownRounds and saves it to the passed // in key value store at the specified key. An error is returned on an // unsuccessful save. -func NewKnownRounds(kv *versioned.KV, key string, size int) (*KnownRounds, error) { +func NewKnownRounds(kv *versioned.KV, key string, known *knownRounds.KnownRounds) (*KnownRounds, error) { // Create new empty struct kr := &KnownRounds{ - rounds: knownRounds.NewKnownRound(size), + rounds: known, kv: kv.Prefix(knownRoundsPrefix), key: key, } @@ -107,6 +107,15 @@ func (kr *KnownRounds) load() error { return nil } +// Deletes a known rounds object from disk and memory +func (kr *KnownRounds) Delete() error { + err := kr.kv.Delete(kr.key) + if err != nil { + return err + } + return nil +} + // Checked determines if the round has been checked. func (kr *KnownRounds) Checked(rid id.Round) bool { kr.mux.RLock() @@ -158,7 +167,7 @@ func (kr *KnownRounds) RangeUnchecked(newestRid id.Round, // RangeUncheckedMasked checks rounds based off the provided mask. func (kr *KnownRounds) RangeUncheckedMasked(mask *knownRounds.KnownRounds, - roundCheck func(id id.Round) bool, maxChecked int) { + roundCheck knownRounds.RoundCheckFunc, maxChecked int) { kr.mux.Lock() defer kr.mux.Unlock() @@ -169,3 +178,17 @@ func (kr *KnownRounds) RangeUncheckedMasked(mask *knownRounds.KnownRounds, jww.FATAL.Panicf("Error saving list of checked rounds: %v", err) } } + +// RangeUncheckedMasked checks rounds based off the provided mask. +func (kr *KnownRounds) RangeUncheckedMaskedRange(mask *knownRounds.KnownRounds, + roundCheck knownRounds.RoundCheckFunc, start, end id.Round, maxChecked int) { + kr.mux.Lock() + defer kr.mux.Unlock() + + kr.rounds.RangeUncheckedMaskedRange(mask, roundCheck, start, end, maxChecked) + + err := kr.save() + if err != nil { + jww.FATAL.Panicf("Error saving list of checked rounds: %v", err) + } +} diff --git a/storage/utility/knownRounds_test.go b/storage/utility/knownRounds_test.go index 73f0cb5a38d2ef39b33d57ea1ae2892d335d015c..653025dbb46282006abdafdbf6733e591ade0705 100644 --- a/storage/utility/knownRounds_test.go +++ b/storage/utility/knownRounds_test.go @@ -28,7 +28,8 @@ func TestNewKnownRounds(t *testing.T) { } // Create new KnownRounds - kr, err := NewKnownRounds(rootKv, expectedKR.key, size) + k := knownRounds.NewKnownRound(size) + kr, err := NewKnownRounds(rootKv, expectedKR.key, k) if err != nil { t.Errorf("NewKnownRounds() returned an error."+ "\n\texpected: %v\n\treceived: %v", nil, err) @@ -154,7 +155,8 @@ func TestKnownRounds_save(t *testing.T) { // } func TestKnownRounds_Smoke(t *testing.T) { - kr, err := NewKnownRounds(versioned.NewKV(make(ekv.Memstore)), "testKey", 10) + k := knownRounds.NewKnownRound(10) + kr, err := NewKnownRounds(versioned.NewKV(make(ekv.Memstore)), "testKey", k) if err != nil { t.Fatalf("Failed to create new KnownRounds: %v", err) } diff --git a/ud/lookup_test.go b/ud/lookup_test.go index d732c7004f8f0bdf9f35291006cf08b93ae10b91..6bd9b7357855ef99f55d0fcdced916cfcba0d787 100644 --- a/ud/lookup_test.go +++ b/ud/lookup_test.go @@ -18,6 +18,7 @@ import ( "gitlab.com/xx_network/crypto/csprng" "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" "math/rand" "reflect" @@ -352,8 +353,8 @@ func (t *testNetworkManager) SendUnsafe(m message.Send, _ params.Unsafe) ([]id.R return rounds, nil } -func (t *testNetworkManager) SendCMIX(format.Message, params.CMIX) (id.Round, error) { - return 0, nil +func (t *testNetworkManager) SendCMIX(format.Message, *id.ID, params.CMIX) (id.Round, ephemeral.Id, error) { + return 0, ephemeral.Id{}, nil } func (t *testNetworkManager) GetInstance() *network.Instance { diff --git a/ud/register.go b/ud/register.go index c280ce58ded85c05c859849b1a170e39d0dbdd73..764b8a2b96ee03e75b3eba91e44a2f31f4590f3b 100644 --- a/ud/register.go +++ b/ud/register.go @@ -20,7 +20,7 @@ type registerUserComms interface { // network signatures are malformed or if the username is taken. Usernames cannot // be changed after registration at this time. Will fail if the user is already // registered. -// Registration does not go over cmix, it occurs over normal communications +// Identity does not go over cmix, it occurs over normal communications func (m *Manager) Register(username string) error { jww.INFO.Printf("ud.Register(%s)", username) return m.register(username, m.comms)