Page MenuHomePhorge

No OneTemporary

Size
15 KB
Referenced Files
None
Subscribers
None
diff --git a/src/crypto/verification-tracker.cpp b/src/crypto/verification-tracker.cpp
index 11be374..f46fac2 100644
--- a/src/crypto/verification-tracker.cpp
+++ b/src/crypto/verification-tracker.cpp
@@ -1,104 +1,109 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2021 Tusooa Zhu <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <algorithm>
#include <immer/set.hpp>
#include <types.hpp>
#include <debug.hpp>
#include "verification-tracker.hpp"
#include "verification-process.hpp"
namespace Kazv
{
static const auto supportedVerificationMethods =
immer::set<std::string>{}.insert("m.sas.v1");
static bool methodSupported(immer::flex_vector<std::string> methods)
{
return std::any_of(methods.begin(), methods.end(),
[](auto method) { return supportedVerificationMethods.count(method); });
}
static std::string idealMethod(immer::flex_vector<std::string> /* methods */)
{
return "m.sas.v1";
}
static std::size_t processRequestRandomSize(std::string method)
{
if (method == "m.sas.v1") {
return SASVerificationProcess::constructRandomSize();
}
return 0;
}
static std::size_t processStartRandomSize(std::string method)
{
if (method == "m.sas.v1") {
return SASVerificationProcess::constructRandomSize();
}
return 0;
}
static bool isEventWellFormed(const nlohmann::json &event)
{
return event.is_object()
&& event.contains("content")
&& event["content"].is_object()
&& event.contains("type")
&& event["type"].is_string()
&& (event["content"].contains("transaction_id")
&& event["content"]["transaction_id"].is_string());
}
std::size_t VerificationTracker::processRandomSize(const nlohmann::json &event)
{
if (!isEventWellFormed(event)) {
return 0;
}
kzo.crypto.dbg() << "event: " << event.dump() << std::endl;
if (event["type"].template get<std::string>() == "m.key.verification.request") {
if (!(event["content"].contains("methods") && event["content"]["methods"].is_array())) {
kzo.crypto.dbg() << "methods not an array" << std::endl;
return 0;
}
auto methods = event["content"]["methods"]
.template get<immer::flex_vector<std::string>>();
if (methodSupported(methods)) {
kzo.crypto.dbg() << "methods supported" << std::endl;
return processRequestRandomSize(idealMethod(methods));
}
kzo.crypto.dbg() << "methods not supported" << std::endl;
return 0;
}
if (event["type"].template get<std::string>() == "m.key.verification.start") {
if (!(event["content"].contains("method") && event["content"]["method"].is_string())) {
return 0;
}
auto method = event["content"]["method"]
.template get<std::string>();
if (methodSupported({ method })) {
return processStartRandomSize(method);
}
return 0;
}
return 0;
}
+
+ VerificationTrackerResult VerificationTracker::process(const nlohmann::json &event, RandomData random, Timestamp ts)
+ {
+ return {};
+ }
}
diff --git a/src/crypto/verification-tracker.hpp b/src/crypto/verification-tracker.hpp
index fc53f60..130b68e 100644
--- a/src/crypto/verification-tracker.hpp
+++ b/src/crypto/verification-tracker.hpp
@@ -1,75 +1,83 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2021 Tusooa Zhu <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include <libkazv-config.hpp>
#include <immer/flex_vector.hpp>
#include <nlohmann/json.hpp>
#include <copy-helper.hpp>
#include <event.hpp>
#include "crypto-util.hpp"
namespace Kazv
{
namespace VerificationTrackerActions
{
struct SendEvent
{
std::string userId;
immer::flex_vector<std::string> deviceIds;
+ nlohmann::json event;
};
struct DisplayCodes
{
std::string emojiCode;
std::string decimalCode;
};
struct ShowStatus
{
enum Status
{
Cancelled,
Verified,
VerificationFailed
};
std::string userId;
std::string deviceId;
Status status;
};
}
using VerificationTrackerAction =
std::variant<VerificationTrackerActions::SendEvent,
VerificationTrackerActions::DisplayCodes,
VerificationTrackerActions::ShowStatus>;
using VerificationTrackerResult = immer::flex_vector<VerificationTrackerAction>;
class VerificationTracker
{
public:
/**
* @return The random size needed for process().
*/
static std::size_t processRandomSize(const nlohmann::json &event);
/**
* Process the event.
*
* @param event A json of the m.key.verification.* event.
* @param random Random data needed to process the event.
* @param ts The current timestamp.
*/
VerificationTrackerResult process(const nlohmann::json &event, RandomData random, Timestamp ts);
+ /**
+ * Create a new verification request.
+ *
+ * @param userId The user id of the user to verify.
+ * @param deviceIds The device ids of the devices to verify.
+ * @param ts The current timestamp.
+ */
VerificationTrackerResult requestVerification(std::string userId, immer::flex_vector<std::string> deviceIds, Timestamp ts);
};
}
diff --git a/src/tests/crypto/verification-process-test.cpp b/src/tests/crypto/verification-process-test.cpp
index da533d2..2a335c3 100644
--- a/src/tests/crypto/verification-process-test.cpp
+++ b/src/tests/crypto/verification-process-test.cpp
@@ -1,192 +1,275 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2021 Tusooa Zhu <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <catch2/catch.hpp>
#include <verification-process.hpp>
#include <verification-tracker.hpp>
using namespace Kazv;
// Taken from https://matrix.org/docs/spec/client_server/r0.6.1
static auto requestEvent = R"({
"content": {
"from_device": "AliceDevice2",
"methods": [
"m.sas.v1"
],
"timestamp": 1559598944869,
"transaction_id": "S0meUniqueAndOpaqueString"
},
"type": "m.key.verification.request"
})"_json;
static auto requestEventUnsupportedMethod = R"({
"content": {
"from_device": "AliceDevice2",
"methods": [
"moe.kazv.mxc.fake-method"
],
"timestamp": 1559598944869,
"transaction_id": "S0meUniqueAndOpaqueString"
},
"type": "m.key.verification.request"
})"_json;
static auto requestEventMultiMethods = R"({
"content": {
"from_device": "AliceDevice2",
"methods": [
"m.sas.v1",
"moe.kazv.mxc.fake-method"
],
"timestamp": 1559598944869,
"transaction_id": "S0meUniqueAndOpaqueString"
},
"type": "m.key.verification.request"
})"_json;
static auto startEvent = R"({
"content": {
"from_device": "BobDevice1",
"method": "m.sas.v1",
"transaction_id": "S0meUniqueAndOpaqueString"
},
"type": "m.key.verification.start"
})"_json;
static auto startEventUnsupportedMethod = R"({
"content": {
"from_device": "BobDevice1",
"method": "moe.kazv.mxc.fake-method",
"transaction_id": "S0meUniqueAndOpaqueString"
},
"type": "m.key.verification.start"
})"_json;
static auto cancelEvent = R"({
"content": {
"code": "m.user",
"reason": "User rejected the key verification request",
"transaction_id": "S0meUniqueAndOpaqueString"
},
"type": "m.key.verification.cancel"
})"_json;
static auto sasStartEvent = R"({
"content": {
"from_device": "BobDevice1",
"hashes": [
"sha256"
],
"key_agreement_protocols": [
"curve25519"
],
"message_authentication_codes": [
"hkdf-hmac-sha256"
],
"method": "m.sas.v1",
"short_authentication_string": [
"decimal",
"emoji"
],
"transaction_id": "S0meUniqueAndOpaqueString"
},
"type": "m.key.verification.start"
})"_json;
static auto sasAcceptEvent = R"({
"content": {
"commitment": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg",
"hash": "sha256",
"key_agreement_protocol": "curve25519",
"message_authentication_code": "hkdf-hmac-sha256",
"method": "m.sas.v1",
"short_authentication_string": [
"decimal",
"emoji"
],
"transaction_id": "S0meUniqueAndOpaqueString"
},
"type": "m.key.verification.accept"
})"_json;
static auto sasKeyEvent = R"({
"content": {
"key": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg",
"transaction_id": "S0meUniqueAndOpaqueString"
},
"type": "m.key.verification.key"
})"_json;
static auto sasMacEvent = R"({
"content": {
"keys": "2Wptgo4CwmLo/Y8B8qinxApKaCkBG2fjTWB7AbP5Uy+aIbygsSdLOFzvdDjww8zUVKCmI02eP9xtyJxc/cLiBA",
"mac": {
"ed25519:ABCDEF": "fQpGIW1Snz+pwLZu6sTy2aHy/DYWWTspTJRPyNp0PKkymfIsNffysMl6ObMMFdIJhk6g6pwlIqZ54rxo8SLmAg"
},
"transaction_id": "S0meUniqueAndOpaqueString"
},
"type": "m.key.verification.mac"
})"_json;
static auto unknownEvent = R"({
"content": {
"transaction_id": "xxx"
},
"type": "moe.kazv.mxc.key.verification.unknown"
})"_json;
static auto malformedEvent = R"({
"type": "moe.kazv.mxc.key.verification.unknown"
})"_json;
+static bool displays(const VerificationTrackerAction &action)
+{
+ return
+ !std::holds_alternative<VerificationTrackerActions::DisplayCodes>(action)
+ && !std::holds_alternative<VerificationTrackerActions::ShowStatus>(action);
+}
+
+static bool displaysNothing(VerificationTrackerResult res)
+{
+ return std::all_of(res.begin(), res.end(), [](const auto &action) {
+ return !displays(action);
+ });
+}
+
+static bool sendsEventOfType(VerificationTrackerResult res, std::string type)
+{
+ return std::any_of(res.begin(), res.end(), [=](const auto &action) {
+ return std::holds_alternative<VerificationTrackerActions::SendEvent>(action)
+ && std::get<VerificationTrackerActions::SendEvent>(action)
+ .event
+ .at("type")
+ .template get<std::string>() == type;
+ });
+}
+
+static bool sendsCancellation(VerificationTrackerResult res)
+{
+ return sendsEventOfType(res, "m.key.verification.cancel");
+}
+
+static bool sendsNothing(VerificationTrackerResult res)
+{
+ return std::all_of(res.begin(), res.end(), [](const auto &action) {
+ return !std::holds_alternative<VerificationTrackerActions::SendEvent>(action);
+ });
+}
+
// TEST_CASE("Construct sas verification process", "[client][verification-proc]")
// {
// auto proc1 = SASVerificationProcess();
// REQUIRE(!proc1.valid());
// auto proc2 = SASVerificationProcess(RandomTag{}, );
// }
TEST_CASE("VerificationTracker is a value type", "[client][verification-proc]")
{
auto tracker = VerificationTracker{};
auto tracker2 = tracker;
(void) tracker2;
auto tracker3 = std::move(tracker);
(void) tracker3;
}
TEST_CASE("VerificationTracker processRandomSize", "[client][verification-proc]")
{
auto sasConstructRandomSize = SASVerificationProcess::constructRandomSize();
REQUIRE(sasConstructRandomSize != 0);
REQUIRE(VerificationTracker::processRandomSize(requestEvent) == sasConstructRandomSize);
REQUIRE(VerificationTracker::processRandomSize(requestEventUnsupportedMethod) == 0);
REQUIRE(VerificationTracker::processRandomSize(requestEventMultiMethods) == sasConstructRandomSize);
REQUIRE(VerificationTracker::processRandomSize(startEvent) == sasConstructRandomSize);
REQUIRE(VerificationTracker::processRandomSize(sasStartEvent) == sasConstructRandomSize);
REQUIRE(VerificationTracker::processRandomSize(startEventUnsupportedMethod) == 0);
REQUIRE(VerificationTracker::processRandomSize(cancelEvent) == 0);
REQUIRE(VerificationTracker::processRandomSize(sasAcceptEvent) == 0);
REQUIRE(VerificationTracker::processRandomSize(sasKeyEvent) == 0);
REQUIRE(VerificationTracker::processRandomSize(sasMacEvent) == 0);
REQUIRE(VerificationTracker::processRandomSize(unknownEvent) == 0);
REQUIRE(VerificationTracker::processRandomSize(malformedEvent) == 0);
}
+
+TEST_CASE("VerificationTracker process() error handling", "[client][verification-proc]")
+{
+ auto reqRandomSize = VerificationTracker::processRandomSize(requestEvent);
+ auto random = genRandomData(reqRandomSize);
+
+ auto tracker = VerificationTracker{};
+
+ auto afterReasonablyShortTime = requestEvent["content"]["timestamp"].template get<Timestamp>() + 1;
+
+ WHEN ("processing a request way long ago") {
+ auto afterTenMins = requestEvent["content"]["timestamp"].template get<Timestamp>() + 10 * 60 * 1000 + 1;
+ auto res = tracker.process(requestEvent, random, afterTenMins);
+
+ THEN ("we should ignore and send cancellation") {
+ REQUIRE(displaysNothing(res));
+ REQUIRE(sendsCancellation(res));
+ }
+ }
+
+ WHEN ("processing a request way in the future") {
+ auto beforeFiveMins = requestEvent["content"]["timestamp"].template get<Timestamp>() - 5 * 60 * 1000 - 1;
+ auto res = tracker.process(requestEvent, random, beforeFiveMins);
+
+ THEN ("we should ignore and send cancellation") {
+ REQUIRE(displaysNothing(res));
+ REQUIRE(sendsCancellation(res));
+ }
+ }
+
+ WHEN ("processing a non-request, non-cancel event whose transaction id is never encountered") {
+ auto res = tracker.process(sasAcceptEvent, random, afterReasonablyShortTime);
+ THEN ("we should ignore and send cancellation") {
+ REQUIRE(displaysNothing(res));
+ REQUIRE(sendsCancellation(res));
+ }
+ }
+
+ WHEN ("processing a cancel event whose transaction id is never encountered") {
+ auto res = tracker.process(cancelEvent, random, afterReasonablyShortTime);
+ THEN ("we should ignore only") {
+ REQUIRE(displaysNothing(res));
+ REQUIRE(sendsNothing(res));
+ }
+ }
+}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Oct 11, 10:18 AM (1 d, 14 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1785727
Default Alt Text
(15 KB)

Event Timeline