ref(omemo): add qomemo/variant

This commit is contained in:
vae 2021-05-13 00:32:13 +03:00
parent 006752b31c
commit 12ffe8e8e6
Signed by: vae
GPG Key ID: A9A33351400E00E5
18 changed files with 366 additions and 222 deletions

View File

@ -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
)
)
add_subdirectory(variant)

126
qomemo/bundle.cpp Normal file
View File

@ -0,0 +1,126 @@
/*
* Created by victoria on 2021-05-12.
*/
#include "bundle.h"
#include <QXmppPubSubIq.h>
#include <QDebug>
#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;
}

41
qomemo/bundle.h Normal file
View File

@ -0,0 +1,41 @@
/*
* Created by victoria on 2021-05-12.
*/
#pragma once
#include <QList>
#include <QString>
class QXmppPubSubIq;
class QXmppElement;
class QXmppIq;
namespace QXmpp::Omemo {
class PreKey {
public:
[[nodiscard]] QXmppElement toXml() const;
/// Expects a <pk>
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<PreKey> prekeys;
};
}

5
qomemo/device.cpp Normal file
View File

@ -0,0 +1,5 @@
/*
* Created by victoria on 2021-05-13.
*/
#include "device.h"

24
qomemo/device.h Normal file
View File

@ -0,0 +1,24 @@
/*
* Created by victoria on 2021-05-13.
*/
#pragma once
#include <QList>
class QXmppElement;
class QXmppIq;
namespace QXmpp::Omemo {
class Device {
public:
int id;
};
class DeviceList {
public:
QList<Device> devices;
};
} // namespace QXmpp::Omemo

View File

@ -3,6 +3,7 @@
*/
#include "device_service.h"
#include "device.h"
QXmpp::Omemo::DeviceService::DeviceService(QObject *parent) : QObject(parent) {}

View File

@ -11,6 +11,8 @@
namespace QXmpp::Omemo {
class DeviceList;
class DeviceService : public QObject {
Q_OBJECT

View File

@ -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));

View File

@ -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<Device> devices;
};
class PreKey {
public:
[[nodiscard]] QXmppElement toXml() const;
/// Expects a <pk>
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<PreKey> prekeys;
};
class MessageKey {
public:
[[nodiscard]] QXmppElement toXml() const;

View File

@ -4,13 +4,18 @@
#include "qxmpp_omemo_manager.h"
#include "device.h"
#include "variant/conversations.h"
#include <QDomElement>
#include <QXmppClient.h>
#include <QXmppPubSubIq.h>
#include <iostream>
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;

View File

@ -6,6 +6,7 @@
#include "device_service.h"
#include "qomemo.h"
#include "variant/omemo_base.h"
#include <QXmppClientExtension.h>
@ -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> deviceService;
QScopedPointer<Variant::Base> omemoVariant;
};
} // namespace QXmpp::Omemo

View File

@ -0,0 +1 @@
target_sources(squawk PRIVATE xep0384.cpp xep0384.h conversations.cpp conversations.h omemo_base.cpp omemo_base.h)

View File

@ -0,0 +1,78 @@
/*
* Created by victoria on 2021-05-13.
*/
#include "conversations.h"
#include "qomemo/device.h"
#include "shared/qxmppfactories.h"
#include <QXmppIq.h>
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;
}

View File

@ -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

View File

@ -0,0 +1,5 @@
/*
* Created by victoria on 2021-05-13.
*/
#include "omemo_base.h"

View File

@ -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

View File

@ -0,0 +1,5 @@
/*
* Created by victoria on 2021-05-13.
*/
#include "xep0384.h"

7
qomemo/variant/xep0384.h Normal file
View File

@ -0,0 +1,7 @@
/*
* Created by victoria on 2021-05-13.
*/
#pragma once
namespace QXmpp::Omemo {}