From 5f3e33e1c15be19f09d83a0d6f44d551021a9d44 Mon Sep 17 00:00:00 2001 From: Carl Schwan Date: Fri, 5 Feb 2021 18:45:30 +0100 Subject: Implement key verification events --- lib/events/keyverificationevent.cpp | 193 ++++++++++++++++++++++++++++++++++++ lib/events/keyverificationevent.h | 167 +++++++++++++++++++++++++++++++ 2 files changed, 360 insertions(+) create mode 100644 lib/events/keyverificationevent.cpp create mode 100644 lib/events/keyverificationevent.h (limited to 'lib/events') diff --git a/lib/events/keyverificationevent.cpp b/lib/events/keyverificationevent.cpp new file mode 100644 index 00000000..938b3bde --- /dev/null +++ b/lib/events/keyverificationevent.cpp @@ -0,0 +1,193 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "keyverificationevent.h" + +using namespace Quotient; + +KeyVerificationRequestEvent::KeyVerificationRequestEvent(const QJsonObject &obj) + : Event(typeId(), obj) +{} + +QString KeyVerificationRequestEvent::fromDevice() const +{ + return contentJson()["from_device"_ls].toString(); +} + +QString KeyVerificationRequestEvent::transactionId() const +{ + return contentJson()["transaction_id"_ls].toString(); +} + +QStringList KeyVerificationRequestEvent::methods() const +{ + QStringList methods; + for (const auto &method : contentJson()["methods"].toArray()) { + methods.append(method.toString()); + } + return methods; +} + +uint64_t KeyVerificationRequestEvent::timestamp() const +{ + return contentJson()["timestamp"_ls].toDouble(); +} + +KeyVerificationStartEvent::KeyVerificationStartEvent(const QJsonObject &obj) + : Event(typeId(), obj) +{} + +QString KeyVerificationStartEvent::fromDevice() const +{ + return contentJson()["from_device"_ls].toString(); +} + +QString KeyVerificationStartEvent::transactionId() const +{ + return contentJson()["transaction_id"_ls].toString(); +} + +QString KeyVerificationStartEvent::method() const +{ + return contentJson()["method"_ls].toString(); +} + +Omittable KeyVerificationStartEvent::nextMethod() const +{ + auto next = contentJson()["method"_ls]; + if (next.isUndefined()) { + return std::nullopt; + } + return next.toString(); +} + +QStringList KeyVerificationStartEvent::keyAgreementProtocols() const +{ + Q_ASSERT(method() == QStringLiteral("m.sas.v1")); + QStringList protocols; + for (const auto &proto : contentJson()["key_agreement_protocols"_ls].toArray()) { + protocols.append(proto.toString()); + } + return protocols; +} + +QStringList KeyVerificationStartEvent::hashes() const +{ + Q_ASSERT(method() == QStringLiteral("m.sas.v1")); + QStringList hashes; + for (const auto &hashItem : contentJson()["hashes"_ls].toArray()) { + hashes.append(hashItem.toString()); + } + return hashes; +} + +QStringList KeyVerificationStartEvent::messageAuthenticationCodes() const +{ + Q_ASSERT(method() == QStringLiteral("m.sas.v1")); + + QStringList codes; + for (const auto &code : contentJson()["message_authentication_codes"_ls].toArray()) { + codes.append(code.toString()); + } + return codes; +} + +QString KeyVerificationStartEvent::shortAuthenticationString() const +{ + return contentJson()["short_authentification_string"_ls].toString(); +} + +KeyVerificationAcceptEvent::KeyVerificationAcceptEvent(const QJsonObject &obj) + : Event(typeId(), obj) +{} + +QString KeyVerificationAcceptEvent::transactionId() const +{ + return contentJson()["transaction_id"_ls].toString(); +} + +QString KeyVerificationAcceptEvent::method() const +{ + return contentJson()["method"_ls].toString(); +} + +QString KeyVerificationAcceptEvent::keyAgreementProtocol() const +{ + return contentJson()["key_agreement_protocol"_ls].toString(); +} + +QString KeyVerificationAcceptEvent::hashData() const +{ + return contentJson()["hash"_ls].toString(); +} + +QStringList KeyVerificationAcceptEvent::shortAuthenticationString() const +{ + QStringList strings; + for (const auto &authenticationString : contentJson()["short_authentification_string"].toArray()) { + strings.append(authenticationString.toString()); + } + return strings; +} + +QString KeyVerificationAcceptEvent::commitement() const +{ + return contentJson()["commitement"].toString(); +} + +KeyVerificationCancelEvent::KeyVerificationCancelEvent(const QJsonObject &obj) + : Event(typeId(), obj) +{} + +QString KeyVerificationCancelEvent::transactionId() const +{ + return contentJson()["transaction_id"_ls].toString(); +} + +QString KeyVerificationCancelEvent::reason() const +{ + return contentJson()["reason"_ls].toString(); +} + +QString KeyVerificationCancelEvent::code() const +{ + return contentJson()["code"_ls].toString(); +} + +KeyVerificationKeyEvent::KeyVerificationKeyEvent(const QJsonObject &obj) + : Event(typeId(), obj) +{} + +QString KeyVerificationKeyEvent::transactionId() const +{ + return contentJson()["transaction_id"_ls].toString(); +} + +QString KeyVerificationKeyEvent::key() const +{ + return contentJson()["key"_ls].toString(); +} + +KeyVerificationMacEvent::KeyVerificationMacEvent(const QJsonObject &obj) + : Event(typeId(), obj) +{} + +QString KeyVerificationMacEvent::transactionId() const +{ + return contentJson()["transaction_id"].toString(); +} + +QString KeyVerificationMacEvent::keys() const +{ + return contentJson()["keys"].toString(); +} + +QHash KeyVerificationMacEvent::mac() const +{ + QHash macs; + const auto macObj = contentJson()["mac"_ls].toObject(); + for (auto mac = macObj.constBegin(); mac != macObj.constEnd(); mac++) { + macs.insert(mac.key(), mac.value().toString()); + } + return macs; +} diff --git a/lib/events/keyverificationevent.h b/lib/events/keyverificationevent.h new file mode 100644 index 00000000..13e7dcdd --- /dev/null +++ b/lib/events/keyverificationevent.h @@ -0,0 +1,167 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "event.h" + +namespace Quotient { + +/// Requests a key verification with another user's devices. +/// Typically sent as a to-device event. +class KeyVerificationRequestEvent : public Event { + Q_GADGET +public: + DEFINE_EVENT_TYPEID("m.key.verification.request", KeyVerificationRequestEvent) + + explicit KeyVerificationRequestEvent(const QJsonObject& obj); + + /// The device ID which is initiating the request. + QString fromDevice() const; + + /// An opaque identifier for the verification request. Must + /// be unique with respect to the devices involved. + QString transactionId() const; + + /// The verification methods supported by the sender. + QStringList methods() const; + + /// 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. + uint64_t timestamp() const; +}; +REGISTER_EVENT_TYPE(KeyVerificationRequestEvent) + +/// Begins a key verification process. +class KeyVerificationStartEvent : public Event { + Q_GADGET +public: + DEFINE_EVENT_TYPEID("m.key.verification.start", KeyVerificationStartEvent) + + explicit KeyVerificationStartEvent(const QJsonObject &obj); + + /// The device ID which is initiating the process. + QString fromDevice() const; + + /// An opaque identifier for the verification request. Must + /// be unique with respect to the devices involved. + QString transactionId() const; + + /// The verification method to use. + QString method() const; + + /// Optional method to use to verify the other user's key with. + Omittable nextMethod() const; + + // SAS.V1 methods + + /// The key agreement protocols the sending device understands. + /// \note Only exist if method is m.sas.v1 + QStringList keyAgreementProtocols() const; + + /// The hash methods the sending device understands. + /// \note Only exist if method is m.sas.v1 + QStringList hashes() const; + + /// The message authentication codes that the sending device understands. + /// \note Only exist if method is m.sas.v1 + QStringList messageAuthenticationCodes() const; + + /// The SAS methods the sending device (and the sending device's + /// user) understands. + /// \note Only exist if method is m.sas.v1 + QString shortAuthenticationString() const; +}; +REGISTER_EVENT_TYPE(KeyVerificationStartEvent) + +/// Accepts a previously sent m.key.verification.start message. +/// Typically sent as a to-device event. +class KeyVerificationAcceptEvent : public Event { + Q_GADGET +public: + DEFINE_EVENT_TYPEID("m.key.verification.accept", KeyVerificationAcceptEvent) + + explicit KeyVerificationAcceptEvent(const QJsonObject& obj); + + /// An opaque identifier for the verification process. + QString transactionId() const; + + /// The verification method to use. Must be 'm.sas.v1'. + QString method() const; + + /// The key agreement protocol the device is choosing to use, out of + /// the options in the m.key.verification.start message. + QString keyAgreementProtocol() const; + + /// The hash method the device is choosing to use, out of the + /// options in the m.key.verification.start message. + QString hashData() const; + + /// The message authentication code the device is choosing to use, out + /// of the options in the m.key.verification.start message. + QString messageAuthenticationCode() const; + + /// The SAS methods both devices involved in the verification process understand. + QStringList shortAuthenticationString() const; + + /// The hash (encoded as unpadded base64) of the concatenation of the + /// device's ephemeral public key (encoded as unpadded base64) and the + /// canonical JSON representation of the m.key.verification.start message. + QString commitement() const; +}; +REGISTER_EVENT_TYPE(KeyVerificationAcceptEvent) + +class KeyVerificationCancelEvent : public Event { + Q_GADGET +public: + DEFINE_EVENT_TYPEID("m.key.verification.cancel", KeyVerificationCancelEvent) + + explicit KeyVerificationCancelEvent(const QJsonObject &obj); + + /// An opaque identifier for the verification process. + QString transactionId() const; + + /// A human readable description of the code. The client should only + /// rely on this string if it does not understand the code. + QString reason() const; + + /// The error code for why the process/request was cancelled by the user. + QString code() const; +}; +REGISTER_EVENT_TYPE(KeyVerificationCancelEvent) + +/// Sends the ephemeral public key for a device to the partner device. +/// Typically sent as a to-device event. +class KeyVerificationKeyEvent : public Event { + Q_GADGET +public: + DEFINE_EVENT_TYPEID("m.key.verification.key", KeyVerificationKeyEvent) + + explicit KeyVerificationKeyEvent(const QJsonObject &obj); + + /// An opaque identifier for the verification process. + QString transactionId() const; + + /// The device's ephemeral public key, encoded as unpadded base64. + QString key() const; +}; +REGISTER_EVENT_TYPE(KeyVerificationKeyEvent) + +/// Sends the MAC of a device's key to the partner device. +class KeyVerificationMacEvent : public Event { + Q_GADGET +public: + DEFINE_EVENT_TYPEID("m.key.verification.mac", KeyVerificationMacEvent) + + explicit KeyVerificationMacEvent(const QJsonObject &obj); + + /// An opaque identifier for the verification process. + QString transactionId() const; + + /// The device's ephemeral public key, encoded as unpadded base64. + QString keys() const; + + QHash mac() const; +}; +REGISTER_EVENT_TYPE(KeyVerificationMacEvent) +} // namespace Quotient -- cgit v1.2.3