From 8f17f050d8c68e230f4cb657796ed6781bebc81c Mon Sep 17 00:00:00 2001
From: Joseph Donofry <joedonofry@gmail.com>
Date: Fri, 30 Aug 2019 17:39:17 -0400
Subject: [PATCH] Very early cut at key verification addition to lib

---
 include/mtx/events.hpp             | 30 ++++++++++++++++++++++++++++++
 include/mtx/events/collections.hpp |  6 ++++++
 include/mtx/events/encrypted.hpp   | 26 ++++++++++++++++++++++++++
 lib/structs/events.cpp             | 26 +++++++++++++++++++++++++-
 lib/structs/events/encrypted.cpp   | 20 ++++++++++++++++++++
 lib/structs/responses/common.cpp   | 24 ++++++++++++++++++++++++
 tests/events.cpp                   | 25 +++++++++++++++++++++++++
 7 files changed, 156 insertions(+), 1 deletion(-)

diff --git a/include/mtx/events.hpp b/include/mtx/events.hpp
index c0e2b843f..1c0220dc1 100644
--- a/include/mtx/events.hpp
+++ b/include/mtx/events.hpp
@@ -14,6 +14,18 @@ namespace events {
 
 enum class EventType
 {
+        /// m.key.verification.cancel
+        KeyVerificationCancel,
+        /// m.key.verification.request
+        KeyVerificationRequest,
+        /// m.key.verification.start
+        KeyVerificationStart,
+        /// m.key.verification.accept
+        KeyVerificationAccept,
+        /// m.key.verification.key
+        KeyVerificationKey,
+        /// m.key.verification.mac
+        KeyVerificationMac,
         /// m.room_key_request
         RoomKeyRequest,
         /// m.room.aliases
@@ -87,6 +99,24 @@ to_json(json &obj, const Event<Content> &event)
         obj["content"] = event.content;
 
         switch (event.type) {
+        case EventType::KeyVerificationStart:
+                obj["type"] = "m.key.verification.start";
+                break;
+        case EventType::KeyVerificationAccept:
+                obj["type"] = "m.key.verification.accept";
+                break;
+        case EventType::KeyVerificationMac:
+                obj["type"] = "m.key.verification.mac";
+                break;
+        case EventType::KeyVerificationKey:
+                obj["type"] = "m.key.verification.accept";
+                break;
+        case EventType::KeyVerificationCancel:
+                obj["type"] = "m.key.verification.cancel";
+                break;
+        case EventType::KeyVerificationRequest:
+                obj["type"] = "m.key.verification.request";
+                break;
         case EventType::RoomKeyRequest:
                 obj["type"] = "m.room_key_request";
                 break;
diff --git a/include/mtx/events/collections.hpp b/include/mtx/events/collections.hpp
index 13adb68af..2e5de3d02 100644
--- a/include/mtx/events/collections.hpp
+++ b/include/mtx/events/collections.hpp
@@ -246,6 +246,12 @@ from_json(const json &obj, TimelineEvent &e)
         case events::EventType::RoomPinnedEvents:
         case events::EventType::RoomKeyRequest: // Not part of the timeline
         case events::EventType::Tag:            // Not part of the timeline
+        case events::EventType::KeyVerificationCancel:
+        case events::EventType::KeyVerificationRequest:
+        case events::EventType::KeyVerificationStart:
+        case events::EventType::KeyVerificationAccept:
+        case events::EventType::KeyVerificationKey:
+        case events::EventType::KeyVerificationMac:
         case events::EventType::Unsupported:
                 return;
         }
diff --git a/include/mtx/events/encrypted.hpp b/include/mtx/events/encrypted.hpp
index 2077c1c8e..eb9f18972 100644
--- a/include/mtx/events/encrypted.hpp
+++ b/include/mtx/events/encrypted.hpp
@@ -116,6 +116,32 @@ from_json(const json &obj, KeyRequest &event);
 void
 to_json(json &obj, const KeyRequest &event);
 
+//! Content of the `m.key.verification.request` event
+struct KeyVerificationRequest
+{
+        //! The device ID which is initiating the request.
+        std::string from_device;
+        //! An opaque identifier for the verification request. Must be unique with respect to the
+        //! devices involved.
+        std::string transaction_id;
+        //! The verification methods supported by the sender.
+        std::vector<std::string> methods;
+        //! The POSIX timestamp in milliseconds for when the request was made. If the request is in
+        //! the future by more than 5 minutes or more than 10 minutes in the past, the message
+        //! should be ignored by the receiver.
+        uint32_t timestamp;
+        //! The type of the event.
+        mtx::events::EventType type;
+};
+
+// TODO: KeyVerificationStart, KeyVerificationAccept, and KeyVerificationCancel
+
+void
+from_json(const json &obj, KeyVerificationRequest &event);
+
+void
+to_json(json &obj, const KeyVerificationRequest &event);
+
 } // namespace msg
 } // namespace events
 } // namespace mtx
diff --git a/lib/structs/events.cpp b/lib/structs/events.cpp
index 5d1a89cd1..f737649ca 100644
--- a/lib/structs/events.cpp
+++ b/lib/structs/events.cpp
@@ -8,7 +8,19 @@ namespace events {
 EventType
 getEventType(const std::string &type)
 {
-        if (type == "m.room_key_request")
+        if (type == "m.key.verification.request")
+                return EventType::KeyVerificationRequest;
+        else if (type == "m.key.verification.start")
+                return EventType::KeyVerificationStart;
+        else if (type == "m.key.verification.accept")
+                return EventType::KeyVerificationAccept;
+        else if (type == "m.key.verification.key")
+                return EventType::KeyVerificationKey;
+        else if (type == "m.key.verification.key")
+                return EventType::KeyVerificationKey;
+        else if (type == "m.key.verification.cancel")
+                return EventType::KeyVerificationCancel;
+        else if (type == "m.room_key_request")
                 return EventType::RoomKeyRequest;
         else if (type == "m.room.aliases")
                 return EventType::RoomAliases;
@@ -56,6 +68,18 @@ std::string
 to_string(EventType type)
 {
         switch (type) {
+        case EventType::KeyVerificationCancel:
+                return "m.key.verification.cancel";
+        case EventType::KeyVerificationRequest:
+                return "m.key.verification.request";
+        case EventType::KeyVerificationStart:
+                return "m.key.verification.start";
+        case EventType::KeyVerificationAccept:
+                return "m.key.verification.accept";
+        case EventType::KeyVerificationKey:
+                return "m.key.verification.key";
+        case EventType::KeyVerificationMac:
+                return "m.key.verification.mac";
         case EventType::RoomKeyRequest:
                 return "m.room_key_request";
         case EventType::RoomAliases:
diff --git a/lib/structs/events/encrypted.cpp b/lib/structs/events/encrypted.cpp
index 6dda4b699..7cc79be16 100644
--- a/lib/structs/events/encrypted.cpp
+++ b/lib/structs/events/encrypted.cpp
@@ -132,6 +132,26 @@ to_json(json &obj, const KeyRequest &event)
         }
 }
 
+void
+from_json(const json &obj, KeyVerificationRequest &event)
+{
+        event.from_device    = obj.at("from_device").get<std::string>();
+        event.methods        = obj.at("methods").get<std::vector<std::string>>();
+        event.timestamp      = obj.at("timestamp").get<uint32_t>();
+        event.transaction_id = obj.at("transaction_id").get<std::string>();
+        event.type           = mtx::events::getEventType(obj.at("type").get<std::string>());
+}
+
+void
+to_json(json &obj, const KeyVerificationRequest &event)
+{
+        obj["from_device"]    = event.from_device;
+        obj["methods"]        = event.methods;
+        obj["timestamp"]      = event.timestamp;
+        obj["transaction_id"] = event.transaction_id;
+        obj["type"]           = to_string(event.type);
+}
+
 } // namespace msg
 } // namespace events
 } // namespace mtx
diff --git a/lib/structs/responses/common.cpp b/lib/structs/responses/common.cpp
index face18d3c..b1a30b58d 100644
--- a/lib/structs/responses/common.cpp
+++ b/lib/structs/responses/common.cpp
@@ -79,6 +79,12 @@ parse_room_account_data_events(
                         }
                         break;
                 }
+                case events::EventType::KeyVerificationCancel:
+                case events::EventType::KeyVerificationRequest:
+                case events::EventType::KeyVerificationStart:
+                case events::EventType::KeyVerificationAccept:
+                case events::EventType::KeyVerificationKey:
+                case events::EventType::KeyVerificationMac:
                 case events::EventType::RoomKeyRequest:
                 case events::EventType::RoomAliases:
                 case events::EventType::RoomAvatar:
@@ -377,6 +383,12 @@ parse_timeline_events(const json &events,
                 case events::EventType::RoomKeyRequest: // Not part of the timeline
                 case events::EventType::Tag:            // Not part of the timeline or state
                 case events::EventType::Unsupported:
+                case events::EventType::KeyVerificationCancel:
+                case events::EventType::KeyVerificationRequest:
+                case events::EventType::KeyVerificationStart:
+                case events::EventType::KeyVerificationAccept:
+                case events::EventType::KeyVerificationKey:
+                case events::EventType::KeyVerificationMac:
                         continue;
                 }
         }
@@ -518,6 +530,12 @@ parse_state_events(const json &events,
                 case events::EventType::RoomRedaction:
                 case events::EventType::Tag: // Not part of the timeline or state
                 case events::EventType::Unsupported:
+                case events::EventType::KeyVerificationCancel:
+                case events::EventType::KeyVerificationRequest:
+                case events::EventType::KeyVerificationStart:
+                case events::EventType::KeyVerificationAccept:
+                case events::EventType::KeyVerificationKey:
+                case events::EventType::KeyVerificationMac:
                         continue;
                 }
         }
@@ -651,6 +669,12 @@ parse_stripped_events(const json &events,
                 case events::EventType::RoomPinnedEvents:
                 case events::EventType::Tag: // Not part of the timeline or state
                 case events::EventType::Unsupported:
+                case events::EventType::KeyVerificationCancel:
+                case events::EventType::KeyVerificationRequest:
+                case events::EventType::KeyVerificationStart:
+                case events::EventType::KeyVerificationAccept:
+                case events::EventType::KeyVerificationKey:
+                case events::EventType::KeyVerificationMac:
                         continue;
                 }
         }
diff --git a/tests/events.cpp b/tests/events.cpp
index 234426ea2..9e809dcb1 100644
--- a/tests/events.cpp
+++ b/tests/events.cpp
@@ -726,6 +726,31 @@ TEST(RoomEvents, Encrypted)
           "\"session_id\":\"/bHcdWPHsJLFd8dkyvG0n7q/RTDmfBIc+gC4laHJCQQ\"}");
 }
 
+TEST(ToDevice, KeyVerificationRequest)
+{
+        json request_data = R"({
+    "content": {
+        "from_device": "AliceDevice2",
+        "methods": [
+            "m.sas.v1"
+        ],
+        "timestamp": 1559598944869,
+        "transaction_id": "S0meUniqueAndOpaqueString"
+    },
+    "type": "m.key.verification.request"
+})"_json;
+
+        ns::msg::KeyVerificationRequest event = request_data;
+        EXPECT_EQ(event.from_device, "AliceDevice2");
+        EXPECT_EQ(event.type, mtx::events::EventType::KeyVerificationRequest);
+        EXPECT_EQ(event.transaction_id, "S0meUniqueAndOpaqueString");
+        EXPECT_EQ(event.methods[0], "m.sas.v1");
+        EXPECT_EQ(event.timestamp, 1559598944869);
+        EXPECT_EQ(request_data.dump(), json(event).dump());
+}
+
+// TODO: KeyVerificationStart, KeyVerificationAccept, and KeyVerificationCancel
+
 TEST(ToDevice, KeyRequest)
 {
         json request_data = R"({
-- 
GitLab