From 755cc434d0d8672d3f1c3c5db718f273886d3e6d Mon Sep 17 00:00:00 2001
From: Konstantinos Sideris <sideris.konstantin@gmail.com>
Date: Sat, 15 Sep 2018 17:56:03 +0300
Subject: [PATCH] Add methods for importing/exporting inbound megolm sessions

---
 include/mtxclient/crypto/client.hpp | 10 ++++-
 lib/crypto/client.cpp               | 30 +++++++++++++++
 tests/e2ee.cpp                      | 59 +++++++++++++++++++++++++++++
 3 files changed, 98 insertions(+), 1 deletion(-)

diff --git a/include/mtxclient/crypto/client.hpp b/include/mtxclient/crypto/client.hpp
index ef16c3f5f..7c46b5d2d 100644
--- a/include/mtxclient/crypto/client.hpp
+++ b/include/mtxclient/crypto/client.hpp
@@ -214,10 +214,18 @@ session_id(OlmSession *s);
 std::string
 session_id(OlmOutboundGroupSession *s);
 
-//! Retrieve the session key.
+//! Retrieve the session key from an *outbound* megolm session.
 std::string
 session_key(OlmOutboundGroupSession *s);
 
+//! Retrieve the session key from an *inbound* megolm session.
+std::string
+export_session(OlmInboundGroupSession *s);
+
+//! Create an *inbound* megolm session from an exported session key.
+InboundGroupSessionPtr
+import_session(const std::string &session_key);
+
 bool
 matches_inbound_session(OlmSession *session, const std::string &one_time_key_message);
 
diff --git a/lib/crypto/client.cpp b/lib/crypto/client.cpp
index 22f95ecd7..0d0f39619 100644
--- a/lib/crypto/client.cpp
+++ b/lib/crypto/client.cpp
@@ -437,6 +437,36 @@ mtx::crypto::session_key(OlmOutboundGroupSession *s)
         return std::string(tmp.begin(), tmp.end());
 }
 
+std::string
+mtx::crypto::export_session(OlmInboundGroupSession *s)
+{
+        const size_t len     = olm_export_inbound_group_session_length(s);
+        const uint32_t index = olm_inbound_group_session_first_known_index(s);
+
+        auto session_key = create_buffer(len);
+        const int rc =
+          olm_export_inbound_group_session(s, session_key.data(), session_key.size(), index);
+
+        if (rc == -1)
+                throw olm_exception("session_key", s);
+
+        return std::string(session_key.begin(), session_key.end());
+}
+
+InboundGroupSessionPtr
+mtx::crypto::import_session(const std::string &session_key)
+{
+        auto session = create_olm_object<InboundSessionObject>();
+
+        const int rc = olm_import_inbound_group_session(
+          session.get(), reinterpret_cast<const uint8_t *>(session_key.data()), session_key.size());
+
+        if (rc == -1)
+                throw olm_exception("import_session", session.get());
+
+        return session;
+}
+
 bool
 mtx::crypto::matches_inbound_session(OlmSession *session, const std::string &one_time_key_message)
 {
diff --git a/tests/e2ee.cpp b/tests/e2ee.cpp
index 1a1f0e05f..dd0aa9ed8 100644
--- a/tests/e2ee.cpp
+++ b/tests/e2ee.cpp
@@ -1071,6 +1071,65 @@ TEST(ExportSessions, EncryptDecrypt)
         EXPECT_EQ(json(keys).dump(), json(restored_keys).dump());
 }
 
+TEST(ExportSessions, InboundMegolmSessions)
+{
+        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);
+
+        // ==================== SESSION SETUP =================== //
+
+        // 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);
+
+        // 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);
+
+        // ==================== SESSION IMPORT/EXPORT =================== //
+
+        auto exported_session_key     = export_session(inbound_megolm_session.get());
+        auto restored_inbound_session = import_session(exported_session_key);
+
+        // Decrypt message again.
+        auto restored_ciphertext =
+          std::string((char *)encrypted_secret_message.data(), encrypted_secret_message.size());
+        auto restored_plaintext =
+          bob->decrypt_group_message(restored_inbound_session.get(), restored_ciphertext);
+
+        auto restored_output_str =
+          std::string((char *)restored_plaintext.data.data(), restored_plaintext.data.size());
+        ASSERT_EQ(restored_output_str, secret_message);
+}
+
 TEST(Encryption, DISABLED_HandleRoomKeyEvent) {}
 TEST(Encryption, DISABLED_HandleRoomKeyRequestEvent) {}
 TEST(Encryption, DISABLED_HandleNewDevices) {}
-- 
GitLab