From 006752b31ca21cd2b911911d002a09d6c4d3b322 Mon Sep 17 00:00:00 2001 From: vae Date: Wed, 12 May 2021 17:33:34 +0300 Subject: [PATCH] feat(OMEMO): QXmppClientExtension for OMEMO --- core/account.cpp | 6 ++- core/account.h | 21 ++++++---- qomemo/CMakeLists.txt | 2 + qomemo/device_service.cpp | 27 +++--------- qomemo/device_service.h | 10 ++--- qomemo/qomemo.cpp | 76 ++++++++++++++-------------------- qomemo/qomemo.h | 21 +++------- qomemo/qxmpp_omemo_manager.cpp | 66 +++++++++++++++++++++++++++++ qomemo/qxmpp_omemo_manager.h | 36 ++++++++++++++++ 9 files changed, 167 insertions(+), 98 deletions(-) create mode 100644 qomemo/qxmpp_omemo_manager.cpp create mode 100644 qomemo/qxmpp_omemo_manager.h diff --git a/core/account.cpp b/core/account.cpp index 5ce29ee..d38e889 100644 --- a/core/account.cpp +++ b/core/account.cpp @@ -47,7 +47,8 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& network(p_net), passwordType(Shared::AccountPassword::plain), mh(new MessageHandler(this)), - rh(new RosterHandler(this)) + rh(new RosterHandler(this)), + omemo(new QXmpp::Omemo::Manager()) { config.setUser(p_login); config.setDomain(p_server); @@ -90,7 +91,8 @@ Account::Account(const QString& p_login, const QString& p_server, const QString& client.addExtension(rcpm); QObject::connect(rcpm, &QXmppMessageReceiptManager::messageDelivered, mh, &MessageHandler::onReceiptReceived); - + + client.addExtension(omemo.get()); QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); path += "/" + name; diff --git a/core/account.h b/core/account.h index a0db9f9..2a3816e 100644 --- a/core/account.h +++ b/core/account.h @@ -30,23 +30,24 @@ #include #include -#include -#include -#include -#include -#include -#include #include #include +#include +#include +#include +#include +#include +#include +#include #include #include #include -#include +#include -#include "shared/shared.h" -#include "contact.h" #include "conference.h" +#include "contact.h" #include "networkaccess.h" +#include "shared/shared.h" #include "handlers/messagehandler.h" #include "handlers/rosterhandler.h" @@ -165,6 +166,8 @@ private: MessageHandler* mh; RosterHandler* rh; + + QScopedPointer omemo; private slots: void onClientStateChange(QXmppClient::State state); diff --git a/qomemo/CMakeLists.txt b/qomemo/CMakeLists.txt index 40abc25..1512264 100644 --- a/qomemo/CMakeLists.txt +++ b/qomemo/CMakeLists.txt @@ -11,4 +11,6 @@ target_sources(squawk PRIVATE sce.h user_device_list.cpp user_device_list.h + qxmpp_omemo_manager.cpp + qxmpp_omemo_manager.h ) \ No newline at end of file diff --git a/qomemo/device_service.cpp b/qomemo/device_service.cpp index 4333d7f..5e9a460 100644 --- a/qomemo/device_service.cpp +++ b/qomemo/device_service.cpp @@ -4,27 +4,12 @@ #include "device_service.h" -#include -#include +QXmpp::Omemo::DeviceService::DeviceService(QObject *parent) : QObject(parent) {} -#include +void QXmpp::Omemo::DeviceService::onDeviceListReceived( + const QString &jid, const QXmpp::Omemo::DeviceList &list) { -QXmpp::Omemo::DeviceService::DeviceService(QXmppClient &client, QObject *parent) - : QObject(parent), client(client) { - connect(&client, &QXmppClient::iqReceived, this, - &DeviceService::onIqReceived); -} - -void QXmpp::Omemo::DeviceService::onIqReceived(const QXmppIq &iq) { - // Update OMEMO device list -} - -void QXmpp::Omemo::DeviceService::fetch() { - QXmppPubSubIq fetchOwnDevices{}; - fetchOwnDevices.setFrom(client.configuration().jid()); - fetchOwnDevices.setTo(client.configuration().jidBare()); - fetchOwnDevices.setType(QXmppIq::Get); - fetchOwnDevices.setQueryNode("urn:xmpp:omemo:1:devices"); - - client.sendPacket(fetchOwnDevices); + for (const auto &device : list.devices) { + qInfo() << "Got device for" << jid << ":" << device.id; + } } diff --git a/qomemo/device_service.h b/qomemo/device_service.h index fc2e4d5..9d87efb 100644 --- a/qomemo/device_service.h +++ b/qomemo/device_service.h @@ -4,6 +4,7 @@ #pragma once +#include "qomemo.h" #include "user_device_list.h" #include @@ -14,17 +15,12 @@ class DeviceService : public QObject { Q_OBJECT public: - DeviceService(QXmppClient& client, QObject *parent); - - void fetch(); + explicit DeviceService(QObject *parent); public slots: - void onIqReceived(const QXmppIq& iq); + void onDeviceListReceived(const QString& jid, const DeviceList& list); private: - void announce(); - - QXmppClient& client; QMap device_lists{}; }; diff --git a/qomemo/qomemo.cpp b/qomemo/qomemo.cpp index 9d4266a..ab863f2 100644 --- a/qomemo/qomemo.cpp +++ b/qomemo/qomemo.cpp @@ -13,7 +13,7 @@ using namespace QXmpp::Factories; QXmppElement QXmpp::Omemo::DeviceList::toXml() const { - auto element = createElement("devices", "urn:xmpp:omemo:1"); + auto element = createElement("list", "eu.siacs.conversations.axolotl"); for (const auto &d : devices) { element.appendChild(d.toXml()); @@ -28,11 +28,10 @@ QXmppIq QXmpp::Omemo::DeviceList::toIq() const { iq.setType(QXmppIq::Set); auto item = createElement("item"); - item.setAttribute("id", "current"); item.appendChild(toXml()); auto publish = createElement("publish"); - publish.setAttribute("node", "urn:xmpp:omemo:1:devices"); + publish.setAttribute("node", "eu.siacs.conversations.axolotl.devicelist"); publish.appendChild(item); auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); @@ -45,7 +44,7 @@ QXmppIq QXmpp::Omemo::DeviceList::toIq() const { } void QXmpp::Omemo::DeviceList::fromXml(const QXmppElement &element) { - if (!elementMatches(element, "devices", "urn:xmpp:omemo:1")) + if (!elementMatches(element, "list", "eu.siacs.conversations.axolotl")) return; devices.clear(); @@ -64,10 +63,6 @@ QXmppElement QXmpp::Omemo::Device::toXml() const { auto result = createElement("device"); result.setAttribute("id", QString::number(id)); - if (!label.isEmpty()) { - result.setAttribute("label", label); - } - return result; } @@ -76,12 +71,11 @@ void QXmpp::Omemo::Device::fromXml(const QXmppElement &element) { return; id = element.attribute("id").toInt(); - label = element.attribute("label"); } QXmppElement QXmpp::Omemo::PreKey::toXml() const { - auto pk = createElement("pk"); - pk.setAttribute("id", QString::number(id)); + auto pk = createElement("preKeyPublic"); + pk.setAttribute("preKeyId", QString::number(id)); // TODO: Base64 pk.setValue(data); @@ -89,23 +83,23 @@ QXmppElement QXmpp::Omemo::PreKey::toXml() const { } void QXmpp::Omemo::PreKey::fromXml(const QXmppElement &element) { - if (!elementMatches(element, "pk")) + if (!elementMatches(element, "preKeyPublic")) return; - id = element.attribute("id").toInt(); + id = element.attribute("preKeyId").toInt(); // TODO: Base64 data = element.value(); } QXmppElement QXmpp::Omemo::Bundle::toXml() const { - auto spkNode = createElement("spk"); - spkNode.setAttribute("id", QString::number(spkId)); + auto spkNode = createElement("signedPreKeyPublic"); + spkNode.setAttribute("signedPreKeyId", QString::number(spkId)); spkNode.setValue(spk); - auto spksNode = createElement("spks"); + auto spksNode = createElement("signedPreKeySignature"); spksNode.setValue(spks); - auto ikNode = createElement("ik"); + auto ikNode = createElement("identityKey"); ikNode.setValue(ik); auto prekeysNode = createElement("prekeys"); @@ -113,7 +107,7 @@ QXmppElement QXmpp::Omemo::Bundle::toXml() const { prekeysNode.appendChild(pk.toXml()); } - auto result = createElement("bundle", "urn:xmpp:omemo:1"); + auto result = createElement("bundle", "eu.siacs.conversations.axolotl"); result.appendChild(spkNode); result.appendChild(spksNode); result.appendChild(ikNode); @@ -122,22 +116,21 @@ QXmppElement QXmpp::Omemo::Bundle::toXml() const { return result; } -QXmppIq QXmpp::Omemo::Bundle::toIq() const { +QXmppIq QXmpp::Omemo::Bundle::toIq(int deviceId) const { QXmppIq iq{}; iq.setType(QXmppIq::Set); auto item = createElement("item"); - item.setAttribute("id", QString::number(deviceId)); item.appendChild(toXml()); auto publish = createElement("publish"); - publish.setAttribute("node", "urn:xmpp:omemo:1:bundles"); + 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("max")); + pubSub.appendChild(createOpenPublishOptions()); iq.extensions().push_back(pubSub); @@ -145,7 +138,7 @@ QXmppIq QXmpp::Omemo::Bundle::toIq() const { } void QXmpp::Omemo::Bundle::fromXml(const QXmppElement &element) { - if (!elementMatches(element, "bundle", "urn:xmpp:omemo:1")) + if (!elementMatches(element, "bundle", "eu.siacs.conversations.axolotl")) return; auto spkNode = element.firstChildElement("spk"); @@ -184,7 +177,7 @@ void QXmpp::Omemo::Bundle::fromXml(const QXmppElement &element) { QXmppPubSubIq QXmpp::Omemo::Bundle::fetchDeviceBundleIq(int deviceId) { QXmppPubSubIq iq{}; iq.setType(QXmppIq::Get); - iq.setQueryNode("urn:xmpp:omemo:1:bundles"); + iq.setQueryNode(QString("eu.siacs.conversations.axolotl.bundles:%1").arg(deviceId)); QXmppPubSubItem item{}; item.setId(QString::number(deviceId)); @@ -197,13 +190,8 @@ QXmppElement QXmpp::Omemo::EncryptedMessage::header() const { auto result = createElement("header"); result.setAttribute("sid", QString::number(fromDeviceId)); - return result; -} - -QXmppElement QXmpp::Omemo::EncryptedMessage::toXml() const { - auto result = createElement("encrypted", "urn:xmpp:omemo:1"); - - result.appendChild(header()); + auto ivNode = createElement("iv"); + ivNode.setValue(iv); for (const auto &key : keys) { result.appendChild(key.toXml()); @@ -212,6 +200,16 @@ QXmppElement QXmpp::Omemo::EncryptedMessage::toXml() const { return result; } +QXmppElement QXmpp::Omemo::EncryptedMessage::toXml() const { + auto result = createElement("encrypted", "eu.siacs.conversations.axolotl"); + + result.appendChild(header()); + // TODO: Payload is optional + result.appendChild(payload()); + + return result; +} + QXmppElement QXmpp::Omemo::EncryptedMessage::payload() const { QBuffer buffer; buffer.open(QIODevice::ReadWrite); @@ -260,21 +258,11 @@ QXmppElement QXmpp::Omemo::EncryptedMessage::content() const { QXmppElement QXmpp::Omemo::MessageKey::toXml() const { auto result = createElement("key"); - if (kex) - result.setAttribute("kex", "true"); + result.setAttribute("rid", QString::number(receivingDeviceId)); + if (prekey) + result.setAttribute("prekey", "true"); result.setValue(key); return result; } - -QXmppElement QXmpp::Omemo::HeaderKeys::toXml() const { - auto result = createElement("keys"); - result.setAttribute("jid", jid); - - for (const auto &key : keys) { - result.appendChild(key.toXml()); - } - - return result; -} diff --git a/qomemo/qomemo.h b/qomemo/qomemo.h index e815d11..346cc90 100644 --- a/qomemo/qomemo.h +++ b/qomemo/qomemo.h @@ -17,7 +17,6 @@ public: void fromXml(const QXmppElement &element); int id; - QString label; }; class DeviceList { @@ -27,7 +26,6 @@ public: /// Expects a urn:xmpp:omemo:1:devices node void fromXml(const QXmppElement &element); -private: QList devices; }; @@ -46,11 +44,9 @@ public: [[nodiscard]] static QXmppPubSubIq fetchDeviceBundleIq(int deviceId); [[nodiscard]] QXmppElement toXml() const; - [[nodiscard]] QXmppIq toIq() const; + [[nodiscard]] QXmppIq toIq(int deviceId) const; void fromXml(const QXmppElement &element); - int deviceId; - QString spk; int spkId; QString spks; @@ -62,18 +58,11 @@ class MessageKey { public: [[nodiscard]] QXmppElement toXml() const; - bool kex{}; + int receivingDeviceId{}; + bool prekey{}; QString key{}; }; -class HeaderKeys { -public: - [[nodiscard]] QXmppElement toXml() const; - - QString jid{}; - QList keys{}; -}; - class EncryptedMessage { public: [[nodiscard]] QXmppElement header() const; @@ -83,11 +72,13 @@ public: int fromDeviceId{}; - QList keys{}; + QList keys{}; QString from{}; QString to{}; QDateTime timestamp{}; + QString iv{}; + QXmppMessage message{}; }; diff --git a/qomemo/qxmpp_omemo_manager.cpp b/qomemo/qxmpp_omemo_manager.cpp new file mode 100644 index 0000000..26598f7 --- /dev/null +++ b/qomemo/qxmpp_omemo_manager.cpp @@ -0,0 +1,66 @@ +/* + * Created by victoria on 2021-05-12. + */ + +#include "qxmpp_omemo_manager.h" + +#include + +#include +#include +#include + +QXmpp::Omemo::Manager::Manager() : deviceService(new QXmpp::Omemo::DeviceService(this)) { + connect(this, &Manager::deviceListReceived, deviceService.get(), &DeviceService::onDeviceListReceived); +} + +bool QXmpp::Omemo::Manager::handleStanza(const QDomElement &stanza) { + QString str{}; + QTextStream info(&str); + stanza.save(info, 4); + + std::cout << str.toStdString(); + + if (stanza.tagName() == "iq") { + if (stanza.attribute("type") == "result") { + auto pubsub = stanza.firstChildElement("pubsub"); + if (!pubsub.isNull()) { + auto items = pubsub.firstChildElement("items"); + if (items.attribute("node") == "eu.siacs.conversations.axolotl.devicelist") { + auto item = items.firstChildElement("item"); + if (!item.isNull()) { + auto list = item.firstChildElement("list"); + if (!list.isNull()) { + DeviceList deviceList{}; + deviceList.fromXml(list); + emit deviceListReceived(stanza.attribute("from"), deviceList); + + return true; + } + } + } + } + } + } + + return false; +} + +void QXmpp::Omemo::Manager::setClient(QXmppClient *client) { + QXmppClientExtension::setClient(client); + + if (!client) + return; + + QObject::connect(client, &QXmppClient::connected, this, &Manager::fetchOwnDevices); +} + +void QXmpp::Omemo::Manager::fetchOwnDevices() { + QXmppPubSubIq iq{}; + iq.setFrom(client()->configuration().jid()); + iq.setTo(client()->configuration().jidBare()); + iq.setType(QXmppIq::Get); + iq.setQueryNode("eu.siacs.conversations.axolotl.devicelist"); + + client()->sendPacket(iq); +} diff --git a/qomemo/qxmpp_omemo_manager.h b/qomemo/qxmpp_omemo_manager.h new file mode 100644 index 0000000..7a6ddf4 --- /dev/null +++ b/qomemo/qxmpp_omemo_manager.h @@ -0,0 +1,36 @@ +/* + * Created by victoria on 2021-05-12. + */ + +#pragma once + +#include "device_service.h" +#include "qomemo.h" + +#include + +namespace QXmpp::Omemo { + +class Manager : public QXmppClientExtension { + Q_OBJECT; + +public: + Manager(); + ~Manager() override = default; + + bool handleStanza(const QDomElement &stanza) override; + +public slots: + void fetchOwnDevices(); + +signals: + void deviceListReceived(const QString& jid, const DeviceList& list); + +protected: + void setClient(QXmppClient *client) override; + +private: + QScopedPointer deviceService; +}; + +} // namespace QXmpp::Omemo