From c05cfa114da88b6c7a885d5e79a33ad068d06d3a Mon Sep 17 00:00:00 2001
From: Konstantinos Sideris <sideris.konstantin@gmail.com>
Date: Tue, 3 Apr 2018 12:28:35 +0300
Subject: [PATCH] Implement /keys/query

---
 src/client.cpp |   9 +++++
 src/client.hpp |   5 +++
 tests/e2ee.cpp | 107 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 121 insertions(+)

diff --git a/src/client.cpp b/src/client.cpp
index 71ebb954c..b19f6fc7b 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -568,3 +568,12 @@ Client::upload_keys(
         post<mtx::requests::UploadKeys, mtx::responses::UploadKeys>(
           "/client/r0/keys/upload", req, callback);
 }
+
+void
+Client::query_keys(
+  const mtx::requests::QueryKeys &req,
+  std::function<void(const mtx::responses::QueryKeys &res, RequestErr err)> callback)
+{
+        post<mtx::requests::QueryKeys, mtx::responses::QueryKeys>(
+          "/client/r0/keys/query", req, callback);
+}
diff --git a/src/client.hpp b/src/client.hpp
index 94509748e..4ba7d1169 100644
--- a/src/client.hpp
+++ b/src/client.hpp
@@ -206,6 +206,11 @@ public:
           const mtx::requests::UploadKeys &req,
           std::function<void(const mtx::responses::UploadKeys &res, RequestErr err)> cb);
 
+        //! Returns the current devices and identity keys for the given users.
+        void query_keys(
+          const mtx::requests::QueryKeys &req,
+          std::function<void(const mtx::responses::QueryKeys &res, RequestErr err)> cb);
+
 private:
         template<class Request, class Response>
         void post(const std::string &endpoint,
diff --git a/tests/e2ee.cpp b/tests/e2ee.cpp
index 3eb8de83a..52c04dad7 100644
--- a/tests/e2ee.cpp
+++ b/tests/e2ee.cpp
@@ -162,3 +162,110 @@ TEST(Encryption, UploadKeys)
 
         alice->close();
 }
+
+TEST(Encryption, QueryKeys)
+{
+        auto alice     = std::make_shared<Client>("localhost");
+        auto alice_olm = mtx::client::crypto::olm_new_account();
+
+        auto bob     = std::make_shared<Client>("localhost");
+        auto bob_olm = mtx::client::crypto::olm_new_account();
+
+        alice->login(
+          "alice", "secret", [](const mtx::responses::Login &, ErrType err) { check_error(err); });
+
+        bob->login(
+          "bob", "secret", [](const mtx::responses::Login &, ErrType err) { check_error(err); });
+
+        while (alice->access_token().empty() || bob->access_token().empty())
+                std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+        // Create and upload keys for both users.
+        auto alice_idks = mtx::client::crypto::identity_keys(alice_olm);
+        auto bob_idks   = mtx::client::crypto::identity_keys(bob_olm);
+
+        mtx::client::crypto::generate_one_time_keys(alice_olm, 1);
+        mtx::client::crypto::generate_one_time_keys(bob_olm, 1);
+
+        auto alice_otks = mtx::client::crypto::one_time_keys(alice_olm);
+        auto bob_otks   = mtx::client::crypto::one_time_keys(bob_olm);
+
+        auto alice_req = mtx::client::crypto::create_upload_keys_request(
+          alice_olm, alice_idks, alice_otks, alice->user_id(), alice->device_id());
+
+        auto bob_req = mtx::client::crypto::create_upload_keys_request(
+          bob_olm, bob_idks, bob_otks, bob->user_id(), bob->device_id());
+
+        // Validates that both upload requests are finished.
+        atomic_int uploads(0);
+
+        alice->upload_keys(alice_req,
+                           [&uploads](const mtx::responses::UploadKeys &res, ErrType err) {
+                                   check_error(err);
+                                   EXPECT_EQ(res.one_time_key_counts.size(), 1);
+                                   EXPECT_EQ(res.one_time_key_counts.at("signed_curve25519"), 1);
+
+                                   uploads += 1;
+                           });
+
+        bob->upload_keys(bob_req, [&uploads](const mtx::responses::UploadKeys &res, ErrType err) {
+                check_error(err);
+                EXPECT_EQ(res.one_time_key_counts.size(), 1);
+                EXPECT_EQ(res.one_time_key_counts.at("signed_curve25519"), 1);
+
+                uploads += 1;
+        });
+
+        while (uploads != 2)
+                std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+        atomic_int responses(0);
+
+        // Each user is requests each other's keys.
+        mtx::requests::QueryKeys alice_rk;
+        alice_rk.device_keys[bob->user_id().to_string()] = {};
+        alice->query_keys(
+          alice_rk, [&responses, bob, bob_req](const mtx::responses::QueryKeys &res, ErrType err) {
+                  check_error(err);
+
+                  ASSERT_TRUE(res.failures.size() == 0);
+
+                  auto bob_devices = res.device_keys.at(bob->user_id().to_string());
+                  ASSERT_TRUE(bob_devices.size() > 0);
+
+                  auto dev_keys = bob_devices.at(bob->device_id());
+                  EXPECT_EQ(dev_keys.user_id, bob->user_id().to_string());
+                  EXPECT_EQ(dev_keys.device_id, bob->device_id());
+                  EXPECT_EQ(dev_keys.keys, bob_req.device_keys.keys);
+                  EXPECT_EQ(dev_keys.signatures, bob_req.device_keys.signatures);
+
+                  responses += 1;
+          });
+
+        mtx::requests::QueryKeys bob_rk;
+        bob_rk.device_keys[alice->user_id().to_string()] = {};
+        bob->query_keys(
+          bob_rk,
+          [&responses, alice, alice_req](const mtx::responses::QueryKeys &res, ErrType err) {
+                  check_error(err);
+
+                  ASSERT_TRUE(res.failures.size() == 0);
+
+                  auto bob_devices = res.device_keys.at(alice->user_id().to_string());
+                  ASSERT_TRUE(bob_devices.size() > 0);
+
+                  auto dev_keys = bob_devices.at(alice->device_id());
+                  EXPECT_EQ(dev_keys.user_id, alice->user_id().to_string());
+                  EXPECT_EQ(dev_keys.device_id, alice->device_id());
+                  EXPECT_EQ(dev_keys.keys, alice_req.device_keys.keys);
+                  EXPECT_EQ(dev_keys.signatures, alice_req.device_keys.signatures);
+
+                  responses += 1;
+          });
+
+        while (responses != 2)
+                std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+        alice->close();
+        bob->close();
+}
-- 
GitLab