diff --git a/.gitignore b/.gitignore index 30f29f0e733db9a0ae86702d463517e7be394e29..9f03361f7129fca9258961dd53bc90023155fd01 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ localdev_* *.class *.aar *.jar +# Ignore test output +.ekv* +.*test* diff --git a/api/client.go b/api/client.go index 7901d1fef5d4d30f8a265d50e145dbad36fe0c65..b45e8cf77ebbb7ea072d4fb89019e7f2e9a21091 100644 --- a/api/client.go +++ b/api/client.go @@ -439,30 +439,28 @@ type SearchCallback interface { func (cl *Client) SearchForUser(emailAddress string, cb SearchCallback, timeout time.Duration) { //see if the user has been searched before, if it has, return it - uid, pk := cl.session.GetContactByValue(emailAddress) + contact, err := io.SessionV2.GetContact(emailAddress) - if uid != nil { - cb.Callback(uid.Bytes(), pk, nil) + // if we successfully got the contact, return it. + // errors can include the email address not existing, + // so errors from the GetContact call are ignored + if contact != nil && err == nil { + cb.Callback(contact.Id.Bytes(), contact.PublicKey, nil) + return } valueType := "EMAIL" go func() { - uid, pubKey, err := bots.Search(valueType, emailAddress, cl.opStatus, timeout) - if err == nil && uid != nil && pubKey != nil { + contact, err := bots.Search(valueType, emailAddress, cl.opStatus, timeout) + if err == nil && contact.Id != nil && contact.PublicKey != nil { cl.opStatus(globals.UDB_SEARCH_BUILD_CREDS) - err = cl.registerUserE2E(uid, pubKey) + err = cl.registerUserE2E(contact) if err != nil { - cb.Callback(uid[:], pubKey, err) + cb.Callback(contact.Id.Bytes(), contact.PublicKey, err) return } //store the user so future lookups can find it - cl.session.StoreContactByValue(emailAddress, uid, pubKey) - - err = cl.session.StoreSession() - if err != nil { - cb.Callback(uid[:], pubKey, err) - return - } + io.SessionV2.SetContact(emailAddress, contact) // If there is something in the channel then send it; otherwise, // skip over it @@ -471,7 +469,7 @@ func (cl *Client) SearchForUser(emailAddress string, default: } - cb.Callback(uid[:], pubKey, err) + cb.Callback(contact.Id.Bytes(), contact.PublicKey, err) } else { if err == nil { diff --git a/api/client_test.go b/api/client_test.go index 190efcefc6096cd7dbf4b7e3085418a0eb546a0a..2db50011e98f42ed1ab0774969e90a5cb4810761 100644 --- a/api/client_test.go +++ b/api/client_test.go @@ -13,6 +13,7 @@ import ( "gitlab.com/elixxir/client/io" "gitlab.com/elixxir/client/keyStore" "gitlab.com/elixxir/client/parse" + "gitlab.com/elixxir/client/storage" "gitlab.com/elixxir/client/user" "gitlab.com/elixxir/crypto/csprng" "gitlab.com/elixxir/crypto/cyclic" @@ -177,7 +178,13 @@ func TestRegisterUserE2E(t *testing.T) { testClient.session = session - testClient.registerUserE2E(partner, partnerPubKeyCyclic.Bytes()) + err = testClient.registerUserE2E(&storage.Contact{ + Id: partner, + PublicKey: partnerPubKeyCyclic.Bytes(), + }) + if err != nil { + t.Fatal(err) + } // Confirm we can get all types of keys km := session.GetKeyStore().GetSendManager(partner) @@ -243,7 +250,7 @@ func TestRegisterUserE2E(t *testing.T) { func TestRegisterUserE2E_CheckAllKeys(t *testing.T) { testClient, err := NewClient(&globals.RamStorage{}, ".ekv-testrege2e-allkeys", "", def) if err != nil { - t.Error(err) + t.Fatal(err) } cmixGrp, e2eGrp := getGroups() @@ -268,7 +275,13 @@ func TestRegisterUserE2E_CheckAllKeys(t *testing.T) { testClient.session = session - testClient.registerUserE2E(partner, partnerPubKeyCyclic.Bytes()) + err = testClient.registerUserE2E(&storage.Contact{ + Id: partner, + PublicKey: partnerPubKeyCyclic.Bytes(), + }) + if err != nil { + t.Fatal(err) + } // Generate all keys and confirm they all match keyParams := testClient.GetKeyParams() @@ -712,8 +725,8 @@ func TestClient_LogoutTimeout(t *testing.T) { // Test that if we logout we can logback in. func TestClient_LogoutAndLoginAgain(t *testing.T) { //Initialize a client - storage := &DummyStorage{LocationA: ".ekv-logoutlogin", StoreA: []byte{'a', 'b', 'c'}} - tc, err := NewClient(storage, ".ekv-logoutlogin", "", def) + dummyStorage := &DummyStorage{LocationA: ".ekv-logoutlogin", StoreA: []byte{'a', 'b', 'c'}} + tc, err := NewClient(dummyStorage, ".ekv-logoutlogin", "", def) if err != nil { t.Errorf("Failed to create new client: %+v", err) } @@ -750,7 +763,7 @@ func TestClient_LogoutAndLoginAgain(t *testing.T) { } //Redefine client with old session files and attempt to login. - tc, err = NewClient(storage, ".ekv-logoutlogin", "", def) + tc, err = NewClient(dummyStorage, ".ekv-logoutlogin", "", def) if err != nil { t.Errorf("Failed second client initialization: %+v", err) } diff --git a/api/private.go b/api/private.go index bc76f0f36d6f02080c16698ceab5c73a6f6b7caa..55253f75c7942307bafc776eb6c20cb29dc567ed 100644 --- a/api/private.go +++ b/api/private.go @@ -13,6 +13,7 @@ import ( "gitlab.com/elixxir/client/globals" "gitlab.com/elixxir/client/io" "gitlab.com/elixxir/client/keyStore" + "gitlab.com/elixxir/client/storage" "gitlab.com/elixxir/client/user" pb "gitlab.com/elixxir/comms/mixmessages" "gitlab.com/elixxir/crypto/csprng" @@ -208,16 +209,15 @@ func (cl *Client) confirmNonce(UID, nonce []byte, return nil } -func (cl *Client) registerUserE2E(partnerID *id.ID, - partnerPubKey []byte) error { +func (cl *Client) registerUserE2E(partner *storage.Contact) error { // Check that the returned user is valid - if partnerKeyStore := cl.session.GetKeyStore().GetSendManager(partnerID); partnerKeyStore != nil { + if partnerKeyStore := cl.session.GetKeyStore().GetSendManager(partner.Id); partnerKeyStore != nil { return errors.New(fmt.Sprintf("UDB searched failed for %v because user has "+ - "been searched for before", partnerID)) + "been searched for before", partner.Id)) } - if cl.session.GetCurrentUser().User.Cmp(partnerID) { + if cl.session.GetCurrentUser().User.Cmp(partner.Id) { return errors.New("cannot search for yourself on UDB") } @@ -228,11 +228,11 @@ func (cl *Client) registerUserE2E(partnerID *id.ID, // Create user private key and partner public key // in the group privKeyCyclic := cl.session.GetE2EDHPrivateKey() - partnerPubKeyCyclic := grp.NewIntFromBytes(partnerPubKey) + publicKeyCyclic := grp.NewIntFromBytes(partner.PublicKey) // Generate baseKey baseKey, _ := diffieHellman.CreateDHSessionKey( - partnerPubKeyCyclic, + publicKeyCyclic, privKeyCyclic, grp) @@ -243,7 +243,7 @@ func (cl *Client) registerUserE2E(partnerID *id.ID, // Create Send KeyManager km := keyStore.NewManager(baseKey, privKeyCyclic, - partnerPubKeyCyclic, partnerID, true, + publicKeyCyclic, partner.Id, true, numKeys, keysTTL, params.NumRekeys) // Generate Send Keys @@ -252,7 +252,7 @@ func (cl *Client) registerUserE2E(partnerID *id.ID, // Create Receive KeyManager km = keyStore.NewManager(baseKey, privKeyCyclic, - partnerPubKeyCyclic, partnerID, false, + publicKeyCyclic, partner.Id, false, numKeys, keysTTL, params.NumRekeys) // Generate Receive Keys @@ -265,10 +265,10 @@ func (cl *Client) registerUserE2E(partnerID *id.ID, keys := &keyStore.RekeyKeys{ CurrPrivKey: privKeyCyclic, - CurrPubKey: partnerPubKeyCyclic, + CurrPubKey: publicKeyCyclic, } - rkm.AddKeys(partnerID, keys) + rkm.AddKeys(partner.Id, keys) return nil } diff --git a/bots/bots_test.go b/bots/bots_test.go index 6f0c7edba4e04504b780cf8039028940a7e25986..9675c5506e34c9ef77a8ca41f7a54cf8b0a986de 100644 --- a/bots/bots_test.go +++ b/bots/bots_test.go @@ -126,11 +126,6 @@ func TestRegister(t *testing.T) { // TestSearch smoke tests the search function func TestSearch(t *testing.T) { publicKeyString := base64.StdEncoding.EncodeToString(pubKey) - //uid := id.NewIdFromUInt(26, id.User, t) - //serRetUid := base64.StdEncoding.EncodeToString(uid[:]) - //result, _ := base64.StdEncoding.DecodeString(serRetUid) - //t.Fatal(serRetUid) - //t.Fatal(len(result)) // Send response messages from fake UDB in advance searchResponseListener <- "blah@elixxir.io FOUND UR69db14ZyicpZVqJ1HFC5rk9UZ8817aV6+VHmrJpGc= AAAAAAAAABoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD 8oKh7TYG4KxQcBAymoXPBHSD/uga9pX3Mn/jKhvcD8M=" @@ -141,16 +136,19 @@ func TestSearch(t *testing.T) { return } - searchedUser, _, err := Search("EMAIL", "blah@elixxir.io", + searchedUser, err := Search("EMAIL", "blah@elixxir.io", dummySearchState, 30*time.Second) if err != nil { - t.Errorf("Error on Search: %s", err.Error()) + t.Fatalf("Error on Search: %s", err.Error()) } - if !searchedUser.Cmp(id.NewIdFromUInt(26, id.User, t)) { + if !searchedUser.Id.Cmp(id.NewIdFromUInt(26, id.User, t)) { t.Errorf("Search did not return user ID 26! returned %s", searchedUser) } //Test the timeout capabilities - searchedUser, _, err = Search("EMAIL", "blah@elixxir.io", dummySearchState, 1*time.Millisecond) + searchedUser, err = Search("EMAIL", "blah@elixxir.io", dummySearchState, 1*time.Millisecond) + if err == nil { + t.Fatal("udb search timeout should have caused error") + } if strings.Compare(err.Error(), "UDB search timeout exceeded on user lookup") != 0 { t.Errorf("error: %v", err) } diff --git a/bots/userDiscovery.go b/bots/userDiscovery.go index d84976c955d24e3814939419a7d3c8541dae07fc..3e51ebfb55f4e12dcd4090a0328588cbb7a4a4f4 100644 --- a/bots/userDiscovery.go +++ b/bots/userDiscovery.go @@ -16,6 +16,7 @@ import ( "gitlab.com/elixxir/client/cmixproto" "gitlab.com/elixxir/client/globals" "gitlab.com/elixxir/client/parse" + "gitlab.com/elixxir/client/storage" "gitlab.com/elixxir/crypto/hash" "gitlab.com/elixxir/primitives/id" "strings" @@ -38,7 +39,7 @@ func Register(valueType, value string, publicKey []byte, regStatus func(int), ti if valueType == "EMAIL" { value, err = hashAndEncode(strings.ToLower(value)) if err != nil { - return fmt.Errorf("Could not hash and encode email %s: %+v", value, err) + return fmt.Errorf("could not hash and encode email %s: %+v", value, err) } } @@ -114,7 +115,7 @@ func Register(valueType, value string, publicKey []byte, regStatus func(int), ti // Search returns a userID and public key based on the search criteria // it accepts a valueType of EMAIL and value of an e-mail address, and // returns a map of userid -> public key -func Search(valueType, value string, searchStatus func(int), timeout time.Duration) (*id.ID, []byte, error) { +func Search(valueType, value string, searchStatus func(int), timeout time.Duration) (*storage.Contact, error) { globals.Log.DEBUG.Printf("Running search for %v, %v", valueType, value) searchTimeout := time.NewTimer(timeout) @@ -123,7 +124,7 @@ func Search(valueType, value string, searchStatus func(int), timeout time.Durati if valueType == "EMAIL" { value, err = hashAndEncode(strings.ToLower(value)) if err != nil { - return nil, nil, fmt.Errorf("Could not hash and encode email %s: %+v", value, err) + return nil, fmt.Errorf("could not hash and encode email %s: %+v", value, err) } } @@ -135,7 +136,7 @@ func Search(valueType, value string, searchStatus func(int), timeout time.Durati }) err = sendCommand(&id.UDB, msgBody) if err != nil { - return nil, nil, err + return nil, err } var response string @@ -149,20 +150,20 @@ func Search(valueType, value string, searchStatus func(int), timeout time.Durati case response = <-searchResponseListener: empty := fmt.Sprintf("SEARCH %s NOTFOUND", value) if response == empty { - return nil, nil, nil + return nil, nil } if strings.Contains(response, value) { found = true } case <-searchTimeout.C: - return nil, nil, errors.New("UDB search timeout exceeded on user lookup") + return nil, errors.New("UDB search timeout exceeded on user lookup") } } // While search returns more than 1 result, we only process the first cMixUID, keyFP, err := parseSearch(response) if err != nil { - return nil, nil, err + return nil, err } searchStatus(globals.UDB_SEARCH_GETKEY) @@ -174,7 +175,7 @@ func Search(valueType, value string, searchStatus func(int), timeout time.Durati }) err = sendCommand(&id.UDB, msgBody) if err != nil { - return nil, nil, err + return nil, err } // wait for the response to searching for the key against the timeout. @@ -187,13 +188,16 @@ func Search(valueType, value string, searchStatus func(int), timeout time.Durati found = true } case <-searchTimeout.C: - return nil, nil, errors.New("UDB search timeout exceeded on key lookup") + return nil, errors.New("UDB search timeout exceeded on key lookup") } } publicKey := parseGetKey(response) - return cMixUID, publicKey, nil + return &storage.Contact{ + Id: cMixUID, + PublicKey: publicKey, + }, nil } func hashAndEncode(s string) (string, error) { diff --git a/storage/contact.go b/storage/contact.go index a28095d03234547daa0ac7c6763a59b2ec8e52fb..ce3deefdec85e5cba5dbcd2beabcb2002906eccf 100644 --- a/storage/contact.go +++ b/storage/contact.go @@ -3,16 +3,16 @@ package storage import ( "encoding/json" "gitlab.com/elixxir/client/globals" - "gitlab.com/elixxir/client/user" + "gitlab.com/elixxir/primitives/id" "time" ) const currentContactVersion = 0 -func (s *Session) GetContact(name string) (*user.SearchedUserRecord, error) { +func (s *Session) GetContact(name string) (*Contact, error) { // Make key // If upgrading version, may need to add logic to update version number in key prefix - key := MakeKeyPrefix("SearchedUserRecord", currentContactVersion) + name + key := MakeKeyPrefix("Contact", currentContactVersion) + name obj, err := s.Get(key) if err != nil { @@ -24,18 +24,18 @@ func (s *Session) GetContact(name string) (*user.SearchedUserRecord, error) { } // deserialize - var contact user.SearchedUserRecord + var contact Contact err = json.Unmarshal(obj.Data, &contact) return &contact, err } -func (s *Session) SetContact(name string, record *user.SearchedUserRecord) error { +func (s *Session) SetContact(name string, record *Contact) error { now, err := time.Now().MarshalText() if err != nil { return err } - key := MakeKeyPrefix("SearchedUserRecord", currentContactVersion) + name + key := MakeKeyPrefix("Contact", currentContactVersion) + name var data []byte data, err = json.Marshal(record) if err != nil { @@ -48,3 +48,8 @@ func (s *Session) SetContact(name string, record *user.SearchedUserRecord) error } return s.Set(key, &obj) } + +type Contact struct { + Id *id.ID + PublicKey []byte +} diff --git a/storage/contact_test.go b/storage/contact_test.go index 8fc667da5133a0a0b5621ea028ca2eedc4975634..2b8f77463fe32fc17bb647d825529530b0eb404d 100644 --- a/storage/contact_test.go +++ b/storage/contact_test.go @@ -1,7 +1,6 @@ package storage import ( - "gitlab.com/elixxir/client/user" "gitlab.com/elixxir/ekv" "gitlab.com/elixxir/primitives/id" "reflect" @@ -13,9 +12,9 @@ func TestSession_Contact(t *testing.T) { store := make(ekv.Memstore) session := &Session{NewVersionedKV(store)} - expectedRecord := &user.SearchedUserRecord{ - Id: *id.NewIdFromUInt(24601, id.User, t), - Pk: []byte("not a real public key"), + expectedRecord := &Contact{ + Id: id.NewIdFromUInt(24601, id.User, t), + PublicKey: []byte("not a real public key"), } name := "niamh@elixxir.io"