From c0a16c609ff8e639a9477ee08693c3478933e7fe Mon Sep 17 00:00:00 2001
From: Konstantinos Sideris <sideris.konstantin@gmail.com>
Date: Tue, 22 May 2018 21:32:26 +0300
Subject: [PATCH] Add tests for megolm sessions locally

---
 examples/crypto_bot.cpp             |  2 +
 include/mtxclient/crypto/client.hpp |  3 ++
 lib/crypto/client.cpp               |  6 +++
 tests/e2ee.cpp                      | 80 +++++++++++++++++++++++++++++
 4 files changed, 91 insertions(+)

diff --git a/examples/crypto_bot.cpp b/examples/crypto_bot.cpp
index ea57c7c46..8ba2573e6 100644
--- a/examples/crypto_bot.cpp
+++ b/examples/crypto_bot.cpp
@@ -475,6 +475,7 @@ keys_uploaded_cb(const mtx::responses::UploadKeys &, RequestErr err)
                 return;
         }
 
+        olm_client->mark_keys_as_published();
         console->info("keys uploaded");
 }
 
@@ -852,6 +853,7 @@ login_cb(const mtx::responses::Login &, RequestErr err)
                                             return;
                                     }
 
+                                    olm_client->mark_keys_as_published();
                                     console->info("keys uploaded");
                                     console->debug("starting initial sync");
 
diff --git a/include/mtxclient/crypto/client.hpp b/include/mtxclient/crypto/client.hpp
index b9dbd91a5..ad97eeb1f 100644
--- a/include/mtxclient/crypto/client.hpp
+++ b/include/mtxclient/crypto/client.hpp
@@ -180,6 +180,9 @@ public:
         //! Generate the json structure for the signed one time key.
         json signed_one_time_key_json(const std::string &key, const std::string &signature);
 
+        //! Marks the current set of one time keys as being published.
+        void mark_keys_as_published();
+
         //! Prepare request for the /keys/upload endpoint by signing identity & one time keys.
         mtx::requests::UploadKeys create_upload_keys_request(const OneTimeKeys &keys);
         mtx::requests::UploadKeys create_upload_keys_request();
diff --git a/lib/crypto/client.cpp b/lib/crypto/client.cpp
index ec7c2b372..d63e542dc 100644
--- a/lib/crypto/client.cpp
+++ b/lib/crypto/client.cpp
@@ -142,6 +142,12 @@ OlmClient::signed_one_time_key_json(const std::string &key, const std::string &s
                     {"signatures", {{user_id_, {{"ed25519:" + device_id_, signature}}}}}};
 }
 
+void
+OlmClient::mark_keys_as_published()
+{
+        olm_account_mark_keys_as_published(account_.get());
+}
+
 mtx::requests::UploadKeys
 OlmClient::create_upload_keys_request()
 {
diff --git a/tests/e2ee.cpp b/tests/e2ee.cpp
index 24b608468..1ff78d88c 100644
--- a/tests/e2ee.cpp
+++ b/tests/e2ee.cpp
@@ -559,3 +559,83 @@ TEST(Encryption, OlmSessions)
         auto body_str = std::string((char *)decrypted.data(), decrypted.size());
         ASSERT_EQ(body_str, plaintext);
 }
+
+TEST(Encryption, MegolmSessions)
+{
+        auto alice = std::make_shared<OlmClient>();
+        alice->create_new_account();
+        alice->generate_one_time_keys(1);
+
+        auto bob = std::make_shared<OlmClient>();
+        bob->create_new_account();
+        bob->generate_one_time_keys(1);
+
+        // Alice wants to send an encrypted megolm message to Bob.
+        const std::string secret_message = "Hey, Bob!";
+
+        // Alice creates an outbound megolm session that will be used by both parties.
+        auto outbound_megolm_session = alice->init_outbound_group_session();
+        auto msg_index = olm_outbound_group_session_message_index(outbound_megolm_session.get());
+        ASSERT_EQ(msg_index, 0);
+
+        // Alice extracts the session id & session key so she can share them with Bob.
+        const auto session_id  = mtx::crypto::session_id(outbound_megolm_session.get());
+        const auto session_key = mtx::crypto::session_key(outbound_megolm_session.get());
+
+        // Encrypt the message using megolm.
+        auto encrypted_secret_message =
+          alice->encrypt_group_message(outbound_megolm_session.get(), secret_message);
+
+        msg_index = olm_outbound_group_session_message_index(outbound_megolm_session.get());
+        ASSERT_EQ(msg_index, 1);
+
+        // First she will create an outbound olm session so she can share the session data.
+        // Alice will need Bob's curve25519 key and one claimed one time key.
+        auto outbound_olm_session = alice->create_outbound_session(
+          bob->identity_keys().curve25519, bob->one_time_keys().curve25519.begin()->second);
+        const auto msg_type = olm_encrypt_message_type(outbound_olm_session.get());
+
+        // Plaintext version of the session data to be shared.
+        const auto session_data = json{{"session_id", session_id}, {"session_key", session_key}};
+
+        // Alice encrypts the session data using olm.
+        const auto encrypted_session_data =
+          alice->encrypt_message(outbound_olm_session.get(), session_data.dump());
+        const auto encrypted_session_data_str =
+          std::string((char *)encrypted_session_data.data(), encrypted_session_data.size());
+
+        //
+        // Alice sends the olm & megolm messages to Bob ...
+        //
+
+        // Bob creates an inbound olm session to receive the session data.
+        auto inbound_olm_session = bob->create_inbound_session(encrypted_session_data);
+
+        // and validates that the message was indeed from Alice.
+        ASSERT_EQ(1,
+                  matches_inbound_session_from(inbound_olm_session.get(),
+                                               alice->identity_keys().curve25519,
+                                               encrypted_session_data_str));
+
+        // Bob decrypts the encrypted olm message.
+        auto plaintext_session_data =
+          bob->decrypt_message(inbound_olm_session.get(), msg_type, encrypted_session_data_str);
+        auto session_str_data = json::parse(
+          std::string((char *)plaintext_session_data.data(), plaintext_session_data.size()));
+
+        // Validate that the output matches the input.
+        ASSERT_EQ(session_id, session_str_data.at("session_id").get<std::string>());
+        ASSERT_EQ(session_key, session_str_data.at("session_key").get<std::string>());
+
+        // Bob will use the session_key to create an inbound megolm session.
+        // The session_id will be used to map future messages to this session.
+        auto inbound_megolm_session = bob->init_inbound_group_session(session_key);
+
+        // Bob can finally decrypt Alice's original message.
+        auto ciphertext =
+          std::string((char *)encrypted_secret_message.data(), encrypted_secret_message.size());
+        auto bob_plaintext = bob->decrypt_group_message(inbound_megolm_session.get(), ciphertext);
+
+        auto output_str = std::string((char *)bob_plaintext.data.data(), bob_plaintext.data.size());
+        ASSERT_EQ(output_str, secret_message);
+}
-- 
GitLab