Page MenuHomePhorge

verification-tracker.cpp
No OneTemporary

Size
9 KB
Referenced Files
None
Subscribers
None

verification-tracker.cpp

/*
* 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
{
using namespace VerificationTrackerActions;
using TrackerAct = VerificationTrackerAction;
using TrackerRes = VerificationTrackerResult;
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); });
}
std::string idealMethod(immer::flex_vector<std::string> /* methods */)
{
return SASVerificationProcess::name();
}
static std::size_t processRequestRandomSize(std::string method)
{
if (method == SASVerificationProcess::name()) {
return SASVerificationProcess::constructRandomSize();
}
return 0;
}
static std::size_t processStartRandomSize(std::string method)
{
if (method == SASVerificationProcess::name()) {
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("from_device")
&& event["content"]["from_device"].is_string()
&& (
event["content"].contains("transaction_id")
&& event["content"]["transaction_id"].is_string()
);
}
namespace
{
enum CheckStatus
{
Ok,
NotOfType,
NotWellFormed
};
}
static CheckStatus isRequest(const nlohmann::json &event)
{
if (!(event["type"].template get<std::string>() == "m.key.verification.request")) {
return NotOfType;
}
if (!(event["content"].contains("methods") && event["content"]["methods"].is_array())) {
kzo.crypto.dbg() << "methods not an array" << std::endl;
return NotWellFormed;
}
return Ok;
}
static CheckStatus isReady(const nlohmann::json &event)
{
if (!(event["type"].template get<std::string>() == "m.key.verification.ready")) {
return NotOfType;
}
if (!(event["content"].contains("methods") && event["content"]["methods"].is_array())) {
kzo.crypto.dbg() << "methods not an array" << std::endl;
return NotWellFormed;
}
return Ok;
}
using Methods = immer::flex_vector<std::string>;
static Methods methodsForEvent(const nlohmann::json &event)
{
return event["content"]["methods"]
.template get<immer::flex_vector<std::string>>();
}
struct VerificationTracker::Private
{
std::string userId;
std::string deviceId;
using ProcessMap = std::unordered_map<std::string, std::unordered_map<std::string, VerificationProcess>>;
ProcessMap processes;
nlohmann::json makeEvent(std::string type, Timestamp ts, std::string txnId, nlohmann::json content) const;
std::string nextTxnId(Timestamp ts);
bool hasExistingProcessForIncomingEvent(const nlohmann::json &event) const;
void addProcess(std::string userId, immer::flex_vector<std::string> deviceIds, VerificationProcess process);
void removeProcess(std::string userId, immer::flex_vector<std::string> deviceIds);
};
nlohmann::json VerificationTracker::Private::makeEvent(std::string type, Timestamp ts, std::string txnId, nlohmann::json content) const
{
auto ev = nlohmann::json{
{"type", type},
{"content", content}
};
ev["content"]["from_device"] = deviceId;
ev["content"]["timestamp"] = ts;
ev["content"]["transaction_id"] = txnId;
return ev;
}
std::string VerificationTracker::Private::nextTxnId(Timestamp ts)
{
return std::to_string(ts);
}
void VerificationTracker::Private::addProcess(std::string userId, immer::flex_vector<std::string> deviceIds, VerificationProcess process)
{
for (auto deviceId : deviceIds) {
processes.try_emplace(userId);
processes[userId][deviceId] = process;
}
}
VerificationTracker::VerificationTracker()
: m_d(new Private{})
{
}
VerificationTracker::VerificationTracker(std::string userId, std::string deviceId)
: m_d(new Private{userId, deviceId, {}})
{
}
KAZV_DEFINE_COPYABLE_UNIQUE_PTR(VerificationTracker, m_d);
VerificationTracker::~VerificationTracker() = default;
std::size_t VerificationTracker::processRandomSize(const nlohmann::json &event)
{
if (!isEventWellFormed(event)) {
return 0;
}
kzo.crypto.dbg() << "event: " << event.dump() << std::endl;
auto checkRequestRes = isRequest(event);
if (checkRequestRes == NotWellFormed) {
return 0;
}
if (checkRequestRes != NotOfType) {
auto methods = methodsForEvent(event);
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(std::string theirUserId, const nlohmann::json &event, RandomData random, Timestamp ts)
{
auto sendCancel = TrackerRes{}; // TODO
if (!isEventWellFormed(event)) {
return sendCancel;
}
// Process request event
auto checkRequestRes = isRequest(event);
if (checkRequestRes != NotOfType) {
if (checkRequestRes == NotWellFormed) {
return sendCancel;
}
auto methods = event["content"]["methods"]
.template get<immer::flex_vector<std::string>>();
if (!methodSupported(methods)) {
kzo.crypto.warn() << "We do not support any methods provided." << std::endl;
return sendCancel;
}
auto methodToUse = idealMethod(methods);
kzo.crypto.dbg() << "choosing method: " << methodToUse << std::endl;
auto theirDeviceId = event["content"]["from_device"].template get<std::string>();
auto txnId = m_d->nextTxnId(ts);
m_d->addProcess(theirUserId, { theirDeviceId }, VerificationProcess(VerificationProcess::ToDeviceTag{}, ts, txnId, methods));
auto notifyAction = TrackerAct{ShowStatus{theirUserId, theirDeviceId, ShowStatus::Status::Requested}};
auto sendReadyAction = TrackerAct{SendEvent{
theirUserId,
{ theirDeviceId },
m_d->makeEvent(
"m.key.verification.ready",
ts,
txnId,
nlohmann::json::object({{"methods", nlohmann::json::array({ methodToUse })}})
)
}};
auto sendStartAction = TrackerAct{SendEvent{
theirUserId,
{ theirDeviceId },
m_d->makeEvent(
"m.key.verification.start",
ts,
txnId,
m_d->processes[theirUserId][theirDeviceId].obtainStartEventContent(random)
)
}};
return TrackerRes{notifyAction, sendReadyAction, sendStartAction};
}
return {};
}
VerificationTrackerResult VerificationTracker::requestVerification(
std::string userId, immer::flex_vector<std::string> deviceIds, Timestamp ts)
{
auto txnId = m_d->nextTxnId(ts);
auto methods = immer::flex_vector<std::string>(supportedVerificationMethods.begin(), supportedVerificationMethods.end());
auto proc = VerificationProcess(VerificationProcess::ToDeviceTag{}, ts, txnId, methods);
m_d->addProcess(userId, deviceIds, proc);
auto sendEventAction = TrackerAct(SendEvent{
userId,
deviceIds,
m_d->makeEvent(
"m.key.verification.request",
ts,
txnId,
nlohmann::json::object({{"methods", methods}})
)
});
return TrackerRes{sendEventAction};
}
}

File Metadata

Mime Type
text/x-c++
Expires
Sun, Dec 7, 7:25 PM (1 d, 12 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
700120
Default Alt Text
verification-tracker.cpp (9 KB)

Event Timeline