From 12ffe8e8e6f9edd9bb6cbcc74eedb1ce59007c69 Mon Sep 17 00:00:00 2001 From: vae Date: Thu, 13 May 2021 00:32:13 +0300 Subject: [PATCH] ref(omemo): add qomemo/variant --- qomemo/CMakeLists.txt | 8 +- qomemo/bundle.cpp | 126 ++++++++++++++++++++++ qomemo/bundle.h | 41 ++++++++ qomemo/device.cpp | 5 + qomemo/device.h | 24 +++++ qomemo/device_service.cpp | 1 + qomemo/device_service.h | 2 + qomemo/qomemo.cpp | 174 ------------------------------- qomemo/qomemo.h | 43 -------- qomemo/qxmpp_omemo_manager.cpp | 10 +- qomemo/qxmpp_omemo_manager.h | 4 +- qomemo/variant/CMakeLists.txt | 1 + qomemo/variant/conversations.cpp | 78 ++++++++++++++ qomemo/variant/conversations.h | 23 ++++ qomemo/variant/omemo_base.cpp | 5 + qomemo/variant/omemo_base.h | 31 ++++++ qomemo/variant/xep0384.cpp | 5 + qomemo/variant/xep0384.h | 7 ++ 18 files changed, 366 insertions(+), 222 deletions(-) create mode 100644 qomemo/bundle.cpp create mode 100644 qomemo/bundle.h create mode 100644 qomemo/device.cpp create mode 100644 qomemo/device.h create mode 100644 qomemo/variant/CMakeLists.txt create mode 100644 qomemo/variant/conversations.cpp create mode 100644 qomemo/variant/conversations.h create mode 100644 qomemo/variant/omemo_base.cpp create mode 100644 qomemo/variant/omemo_base.h create mode 100644 qomemo/variant/xep0384.cpp create mode 100644 qomemo/variant/xep0384.h diff --git a/qomemo/CMakeLists.txt b/qomemo/CMakeLists.txt index 1512264..95fca3f 100644 --- a/qomemo/CMakeLists.txt +++ b/qomemo/CMakeLists.txt @@ -1,4 +1,8 @@ target_sources(squawk PRIVATE + bundle.cpp + bundle.h + device.cpp + device.h device_key_storage.cpp device_key_storage.h device_service.cpp @@ -13,4 +17,6 @@ target_sources(squawk PRIVATE user_device_list.h qxmpp_omemo_manager.cpp qxmpp_omemo_manager.h - ) \ No newline at end of file + ) + +add_subdirectory(variant) diff --git a/qomemo/bundle.cpp b/qomemo/bundle.cpp new file mode 100644 index 0000000..aec605a --- /dev/null +++ b/qomemo/bundle.cpp @@ -0,0 +1,126 @@ +/* + * Created by victoria on 2021-05-12. + */ + +#include "bundle.h" + +#include + +#include + +#include "shared/qxmppfactories.h" + +using namespace QXmpp::Factories; + +QXmppElement QXmpp::Omemo::PreKey::toXml() const { + auto pk = createElement("preKeyPublic"); + pk.setAttribute("preKeyId", QString::number(id)); + // TODO: Base64 + pk.setValue(data); + + return pk; +} + +void QXmpp::Omemo::PreKey::fromXml(const QXmppElement &element) { + if (!elementMatches(element, "preKeyPublic")) + return; + + id = element.attribute("preKeyId").toInt(); + // TODO: Base64 + data = element.value(); +} + +QXmppElement QXmpp::Omemo::Bundle::toXml() const { + auto spkNode = createElement("signedPreKeyPublic"); + spkNode.setAttribute("signedPreKeyId", QString::number(spkId)); + spkNode.setValue(spk); + + auto spksNode = createElement("signedPreKeySignature"); + spksNode.setValue(spks); + + auto ikNode = createElement("identityKey"); + ikNode.setValue(ik); + + auto prekeysNode = createElement("prekeys"); + for (const auto &pk : prekeys) { + prekeysNode.appendChild(pk.toXml()); + } + + auto result = createElement("bundle", "eu.siacs.conversations.axolotl"); + result.appendChild(spkNode); + result.appendChild(spksNode); + result.appendChild(ikNode); + result.appendChild(prekeysNode); + + return result; +} + +QXmppIq QXmpp::Omemo::Bundle::toIq(int deviceId) const { + QXmppIq iq{}; + + iq.setType(QXmppIq::Set); + + auto item = createElement("item"); + item.appendChild(toXml()); + + auto publish = createElement("publish"); + publish.setAttribute("node", QString("eu.siacs.conversations.axolotl.bundles:%s").arg(deviceId)); + publish.appendChild(item); + + auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); + pubSub.appendChild(publish); + pubSub.appendChild(createOpenPublishOptions()); + + iq.extensions().push_back(pubSub); + + return iq; +} + +void QXmpp::Omemo::Bundle::fromXml(const QXmppElement &element) { + if (!elementMatches(element, "bundle", "eu.siacs.conversations.axolotl")) + return; + + auto spkNode = element.firstChildElement("spk"); + if (spkNode.isNull()) { + qWarning() << "'bundle': missing 'spk'"; + return; + } + spk = spkNode.value(); + spkId = spkNode.attribute("id").toInt(); + + auto spksNode = element.firstChildElement("spks"); + if (spksNode.isNull()) { + qWarning() << "'bundle': missing 'spks'"; + return; + } + spks = spksNode.value(); + + auto ikNode = element.firstChildElement("ik"); + if (ikNode.isNull()) { + qWarning() << "'bundle': missing 'ik'"; + return; + } + ik = ikNode.value(); + + auto prekeysNode = element.firstChildElement("prekeys"); + auto pkNode = prekeysNode.firstChildElement("pk"); + + prekeys.clear(); + while (!pkNode.isNull()) { + PreKey pk{}; + pk.fromXml(pkNode); + prekeys.push_back(pk); + } +} + +QXmppPubSubIq QXmpp::Omemo::Bundle::fetchDeviceBundleIq(int deviceId) { + QXmppPubSubIq iq{}; + iq.setType(QXmppIq::Get); + iq.setQueryNode(QString("eu.siacs.conversations.axolotl.bundles:%1").arg(deviceId)); + + QXmppPubSubItem item{}; + item.setId(QString::number(deviceId)); + iq.setItems({item}); + + return iq; +} \ No newline at end of file diff --git a/qomemo/bundle.h b/qomemo/bundle.h new file mode 100644 index 0000000..d29bf02 --- /dev/null +++ b/qomemo/bundle.h @@ -0,0 +1,41 @@ +/* + * Created by victoria on 2021-05-12. + */ + +#pragma once + +#include +#include + +class QXmppPubSubIq; +class QXmppElement; +class QXmppIq; + +namespace QXmpp::Omemo { + +class PreKey { +public: + [[nodiscard]] QXmppElement toXml() const; + /// Expects a + void fromXml(const QXmppElement &element); + + int id; + QString data; +}; + +class Bundle { +public: + [[nodiscard]] static QXmppPubSubIq fetchDeviceBundleIq(int deviceId); + + [[nodiscard]] QXmppElement toXml() const; + [[nodiscard]] QXmppIq toIq(int deviceId) const; + void fromXml(const QXmppElement &element); + + QString spk; + int spkId; + QString spks; + QString ik; + QList prekeys; +}; + +} diff --git a/qomemo/device.cpp b/qomemo/device.cpp new file mode 100644 index 0000000..edfee1c --- /dev/null +++ b/qomemo/device.cpp @@ -0,0 +1,5 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#include "device.h" diff --git a/qomemo/device.h b/qomemo/device.h new file mode 100644 index 0000000..4af6ada --- /dev/null +++ b/qomemo/device.h @@ -0,0 +1,24 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#pragma once + +#include + +class QXmppElement; +class QXmppIq; + +namespace QXmpp::Omemo { + +class Device { +public: + int id; +}; + +class DeviceList { +public: + QList devices; +}; + +} // namespace QXmpp::Omemo diff --git a/qomemo/device_service.cpp b/qomemo/device_service.cpp index 5e9a460..fcc6b72 100644 --- a/qomemo/device_service.cpp +++ b/qomemo/device_service.cpp @@ -3,6 +3,7 @@ */ #include "device_service.h" +#include "device.h" QXmpp::Omemo::DeviceService::DeviceService(QObject *parent) : QObject(parent) {} diff --git a/qomemo/device_service.h b/qomemo/device_service.h index 9d87efb..774b7f4 100644 --- a/qomemo/device_service.h +++ b/qomemo/device_service.h @@ -11,6 +11,8 @@ namespace QXmpp::Omemo { +class DeviceList; + class DeviceService : public QObject { Q_OBJECT diff --git a/qomemo/qomemo.cpp b/qomemo/qomemo.cpp index ab863f2..d078f37 100644 --- a/qomemo/qomemo.cpp +++ b/qomemo/qomemo.cpp @@ -12,180 +12,6 @@ using namespace QXmpp::Factories; -QXmppElement QXmpp::Omemo::DeviceList::toXml() const { - auto element = createElement("list", "eu.siacs.conversations.axolotl"); - - for (const auto &d : devices) { - element.appendChild(d.toXml()); - } - - return element; -} - -QXmppIq QXmpp::Omemo::DeviceList::toIq() const { - QXmppIq iq{}; - - iq.setType(QXmppIq::Set); - - auto item = createElement("item"); - item.appendChild(toXml()); - - auto publish = createElement("publish"); - publish.setAttribute("node", "eu.siacs.conversations.axolotl.devicelist"); - publish.appendChild(item); - - auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); - pubSub.appendChild(publish); - pubSub.appendChild(createOpenPublishOptions()); - - iq.extensions().push_back(pubSub); - - return iq; -} - -void QXmpp::Omemo::DeviceList::fromXml(const QXmppElement &element) { - if (!elementMatches(element, "list", "eu.siacs.conversations.axolotl")) - return; - - devices.clear(); - - auto deviceElement = element.firstChildElement("device"); - while (!deviceElement.isNull()) { - Device device{}; - device.fromXml(deviceElement); - devices.push_back(device); - - deviceElement = deviceElement.nextSiblingElement("device"); - } -} - -QXmppElement QXmpp::Omemo::Device::toXml() const { - auto result = createElement("device"); - result.setAttribute("id", QString::number(id)); - - return result; -} - -void QXmpp::Omemo::Device::fromXml(const QXmppElement &element) { - if (!elementMatches(element, "device")) - return; - - id = element.attribute("id").toInt(); -} - -QXmppElement QXmpp::Omemo::PreKey::toXml() const { - auto pk = createElement("preKeyPublic"); - pk.setAttribute("preKeyId", QString::number(id)); - // TODO: Base64 - pk.setValue(data); - - return pk; -} - -void QXmpp::Omemo::PreKey::fromXml(const QXmppElement &element) { - if (!elementMatches(element, "preKeyPublic")) - return; - - id = element.attribute("preKeyId").toInt(); - // TODO: Base64 - data = element.value(); -} - -QXmppElement QXmpp::Omemo::Bundle::toXml() const { - auto spkNode = createElement("signedPreKeyPublic"); - spkNode.setAttribute("signedPreKeyId", QString::number(spkId)); - spkNode.setValue(spk); - - auto spksNode = createElement("signedPreKeySignature"); - spksNode.setValue(spks); - - auto ikNode = createElement("identityKey"); - ikNode.setValue(ik); - - auto prekeysNode = createElement("prekeys"); - for (const auto &pk : prekeys) { - prekeysNode.appendChild(pk.toXml()); - } - - auto result = createElement("bundle", "eu.siacs.conversations.axolotl"); - result.appendChild(spkNode); - result.appendChild(spksNode); - result.appendChild(ikNode); - result.appendChild(prekeysNode); - - return result; -} - -QXmppIq QXmpp::Omemo::Bundle::toIq(int deviceId) const { - QXmppIq iq{}; - - iq.setType(QXmppIq::Set); - - auto item = createElement("item"); - item.appendChild(toXml()); - - auto publish = createElement("publish"); - publish.setAttribute("node", QString("eu.siacs.conversations.axolotl.bundles:%s").arg(deviceId)); - publish.appendChild(item); - - auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); - pubSub.appendChild(publish); - pubSub.appendChild(createOpenPublishOptions()); - - iq.extensions().push_back(pubSub); - - return iq; -} - -void QXmpp::Omemo::Bundle::fromXml(const QXmppElement &element) { - if (!elementMatches(element, "bundle", "eu.siacs.conversations.axolotl")) - return; - - auto spkNode = element.firstChildElement("spk"); - if (spkNode.isNull()) { - qWarning() << "'bundle': missing 'spk'"; - return; - } - spk = spkNode.value(); - spkId = spkNode.attribute("id").toInt(); - - auto spksNode = element.firstChildElement("spks"); - if (spksNode.isNull()) { - qWarning() << "'bundle': missing 'spks'"; - return; - } - spks = spksNode.value(); - - auto ikNode = element.firstChildElement("ik"); - if (ikNode.isNull()) { - qWarning() << "'bundle': missing 'ik'"; - return; - } - ik = ikNode.value(); - - auto prekeysNode = element.firstChildElement("prekeys"); - auto pkNode = prekeysNode.firstChildElement("pk"); - - prekeys.clear(); - while (!pkNode.isNull()) { - PreKey pk{}; - pk.fromXml(pkNode); - prekeys.push_back(pk); - } -} - -QXmppPubSubIq QXmpp::Omemo::Bundle::fetchDeviceBundleIq(int deviceId) { - QXmppPubSubIq iq{}; - iq.setType(QXmppIq::Get); - iq.setQueryNode(QString("eu.siacs.conversations.axolotl.bundles:%1").arg(deviceId)); - - QXmppPubSubItem item{}; - item.setId(QString::number(deviceId)); - iq.setItems({item}); - - return iq; -} - QXmppElement QXmpp::Omemo::EncryptedMessage::header() const { auto result = createElement("header"); result.setAttribute("sid", QString::number(fromDeviceId)); diff --git a/qomemo/qomemo.h b/qomemo/qomemo.h index 346cc90..b79ef1b 100644 --- a/qomemo/qomemo.h +++ b/qomemo/qomemo.h @@ -11,49 +11,6 @@ namespace QXmpp::Omemo { -class Device { -public: - [[nodiscard]] QXmppElement toXml() const; - void fromXml(const QXmppElement &element); - - int id; -}; - -class DeviceList { -public: - [[nodiscard]] QXmppElement toXml() const; - [[nodiscard]] QXmppIq toIq() const; - /// Expects a urn:xmpp:omemo:1:devices node - void fromXml(const QXmppElement &element); - - QList devices; -}; - -class PreKey { -public: - [[nodiscard]] QXmppElement toXml() const; - /// Expects a - void fromXml(const QXmppElement &element); - - int id; - QString data; -}; - -class Bundle { -public: - [[nodiscard]] static QXmppPubSubIq fetchDeviceBundleIq(int deviceId); - - [[nodiscard]] QXmppElement toXml() const; - [[nodiscard]] QXmppIq toIq(int deviceId) const; - void fromXml(const QXmppElement &element); - - QString spk; - int spkId; - QString spks; - QString ik; - QList prekeys; -}; - class MessageKey { public: [[nodiscard]] QXmppElement toXml() const; diff --git a/qomemo/qxmpp_omemo_manager.cpp b/qomemo/qxmpp_omemo_manager.cpp index 26598f7..f29ec98 100644 --- a/qomemo/qxmpp_omemo_manager.cpp +++ b/qomemo/qxmpp_omemo_manager.cpp @@ -4,13 +4,18 @@ #include "qxmpp_omemo_manager.h" +#include "device.h" +#include "variant/conversations.h" + #include #include #include #include -QXmpp::Omemo::Manager::Manager() : deviceService(new QXmpp::Omemo::DeviceService(this)) { +using namespace QXmpp::Omemo; + +Manager::Manager() : deviceService(new DeviceService(this)), omemoVariant(new Variant::Conversations) { connect(this, &Manager::deviceListReceived, deviceService.get(), &DeviceService::onDeviceListReceived); } @@ -31,8 +36,7 @@ bool QXmpp::Omemo::Manager::handleStanza(const QDomElement &stanza) { if (!item.isNull()) { auto list = item.firstChildElement("list"); if (!list.isNull()) { - DeviceList deviceList{}; - deviceList.fromXml(list); + DeviceList deviceList = omemoVariant->deviceListFromXml(list); emit deviceListReceived(stanza.attribute("from"), deviceList); return true; diff --git a/qomemo/qxmpp_omemo_manager.h b/qomemo/qxmpp_omemo_manager.h index 7a6ddf4..377c77d 100644 --- a/qomemo/qxmpp_omemo_manager.h +++ b/qomemo/qxmpp_omemo_manager.h @@ -6,6 +6,7 @@ #include "device_service.h" #include "qomemo.h" +#include "variant/omemo_base.h" #include @@ -24,13 +25,14 @@ public slots: void fetchOwnDevices(); signals: - void deviceListReceived(const QString& jid, const DeviceList& list); + void deviceListReceived(const QString &jid, const DeviceList &list); protected: void setClient(QXmppClient *client) override; private: QScopedPointer deviceService; + QScopedPointer omemoVariant; }; } // namespace QXmpp::Omemo diff --git a/qomemo/variant/CMakeLists.txt b/qomemo/variant/CMakeLists.txt new file mode 100644 index 0000000..a2b211a --- /dev/null +++ b/qomemo/variant/CMakeLists.txt @@ -0,0 +1 @@ +target_sources(squawk PRIVATE xep0384.cpp xep0384.h conversations.cpp conversations.h omemo_base.cpp omemo_base.h) diff --git a/qomemo/variant/conversations.cpp b/qomemo/variant/conversations.cpp new file mode 100644 index 0000000..b2f85c4 --- /dev/null +++ b/qomemo/variant/conversations.cpp @@ -0,0 +1,78 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#include "conversations.h" + +#include "qomemo/device.h" +#include "shared/qxmppfactories.h" + +#include + +using namespace QXmpp::Omemo; +using namespace QXmpp::Factories; + +QXmppElement Variant::Conversations::deviceToXml(const Device &device) { + auto result = createElement("device"); + result.setAttribute("id", QString::number(device.id)); + + return result; +} + +Device Variant::Conversations::deviceFromXml(const QXmppElement &xml) { + Device result{}; + + if (!elementMatches(xml, "device")) + return result; + + result.id = xml.attribute("id").toInt(); + + return result; +} + +QXmppElement +Variant::Conversations::deviceListToXml(const DeviceList &deviceList) { + auto element = createElement("list", "eu.siacs.conversations.axolotl"); + + for (const auto &device : deviceList.devices) { + element.appendChild(deviceToXml(device)); + } + + return element; +} + +DeviceList Variant::Conversations::deviceListFromXml(const QXmppElement &xml) { + DeviceList result{}; + + if (!elementMatches(xml, "list", "eu.siacs.conversations.axolotl")) + return result; + + auto deviceElement = xml.firstChildElement("device"); + while (!deviceElement.isNull()) { + result.devices.push_back(deviceFromXml(deviceElement)); + deviceElement = deviceElement.nextSiblingElement("device"); + } + + return result; +} + +QXmppIq Variant::Conversations::deviceListSetIq(const DeviceList &deviceList) { + QXmppIq iq{}; + + iq.setType(QXmppIq::Set); + + auto item = createElement("item"); + item.appendChild(deviceListToXml(deviceList)); + + auto publish = createElement("publish"); + publish.setAttribute("node", "eu.siacs.conversations.axolotl.devicelist"); + publish.appendChild(item); + + auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); + pubSub.appendChild(publish); + pubSub.appendChild(createOpenPublishOptions()); + + iq.extensions().push_back(pubSub); + + return iq; +} diff --git a/qomemo/variant/conversations.h b/qomemo/variant/conversations.h new file mode 100644 index 0000000..917dbcd --- /dev/null +++ b/qomemo/variant/conversations.h @@ -0,0 +1,23 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#pragma once + +#include "omemo_base.h" + +namespace QXmpp::Omemo::Variant { + +class Conversations : public Base { +public: + ~Conversations() override = default; + + QXmppElement deviceToXml(const Device &device) override; + Device deviceFromXml(const QXmppElement &xml) override; + + QXmppElement deviceListToXml(const DeviceList &deviceList) override; + DeviceList deviceListFromXml(const QXmppElement &xml) override; + QXmppIq deviceListSetIq(const DeviceList &deviceList) override; +}; + +} // namespace QXmpp::Omemo::Variant diff --git a/qomemo/variant/omemo_base.cpp b/qomemo/variant/omemo_base.cpp new file mode 100644 index 0000000..a3e8493 --- /dev/null +++ b/qomemo/variant/omemo_base.cpp @@ -0,0 +1,5 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#include "omemo_base.h" diff --git a/qomemo/variant/omemo_base.h b/qomemo/variant/omemo_base.h new file mode 100644 index 0000000..f383a59 --- /dev/null +++ b/qomemo/variant/omemo_base.h @@ -0,0 +1,31 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#pragma once + +class QXmppElement; +class QXmppIq; + +namespace QXmpp::Omemo { + +class Device; +class DeviceList; + +namespace Variant { + +class Base { +public: + virtual ~Base() = default; + + virtual QXmppElement deviceToXml(const Device& device) = 0; + virtual Device deviceFromXml(const QXmppElement& xml) = 0; + + virtual QXmppElement deviceListToXml(const DeviceList& deviceList) = 0; + virtual DeviceList deviceListFromXml(const QXmppElement& xml) = 0; + virtual QXmppIq deviceListSetIq(const DeviceList& deviceList) = 0; +}; + +} // namespace Variant + +} // namespace QXmpp::Omemo diff --git a/qomemo/variant/xep0384.cpp b/qomemo/variant/xep0384.cpp new file mode 100644 index 0000000..8ec2597 --- /dev/null +++ b/qomemo/variant/xep0384.cpp @@ -0,0 +1,5 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#include "xep0384.h" diff --git a/qomemo/variant/xep0384.h b/qomemo/variant/xep0384.h new file mode 100644 index 0000000..9298b24 --- /dev/null +++ b/qomemo/variant/xep0384.h @@ -0,0 +1,7 @@ +/* + * Created by victoria on 2021-05-13. + */ + +#pragma once + +namespace QXmpp::Omemo {}