ref(omemo): reformat qomemo/

This commit is contained in:
vae 2021-05-13 17:54:37 +03:00
parent 574210f5d9
commit bbeeee4c8a
Signed by: vae
GPG Key ID: A9A33351400E00E5
23 changed files with 447 additions and 455 deletions

View File

@ -1,23 +1,23 @@
target_sources(squawk PRIVATE target_sources(squawk PRIVATE
bundle.cpp bundle.cpp
bundle.h bundle.h
database.cpp database.cpp
database.h database.h
device.cpp device.cpp
device.h device.h
device_key_storage.cpp device_key_storage.cpp
device_key_storage.h device_key_storage.h
device_service.cpp device_service.cpp
device_service.h device_service.h
qomemo.cpp qomemo.cpp
qomemo.h qomemo.h
sce.cpp sce.cpp
sce.h sce.h
user_device_list.cpp user_device_list.cpp
user_device_list.h user_device_list.h
qxmpp_omemo_manager.cpp qxmpp_omemo_manager.cpp
qxmpp_omemo_manager.h qxmpp_omemo_manager.h
) )
add_subdirectory(signal) add_subdirectory(signal)
add_subdirectory(variant) add_subdirectory(variant)

View File

@ -1,12 +0,0 @@
* Generate device w/ keys
* PubSub set urn:xmpp:omemo:1:devices to announce new device
* PubSub set urn:xmpp:omemo:1:bundles to announce new key bundles
* PubSub get urn:xmpp:omemo:1:bundles to get user bundles
Sending a message:
* Create urn:xmpp:sce:0 with padding and content
* Add <encrypted xmlns='urn:xmpp:omemo:1'> with header (key list) and payload (b64)
Receiving a message:
* Check <keys> => <key>
* Decrypt (TODO)

View File

@ -13,117 +13,117 @@
using namespace QXmpp::Factories; using namespace QXmpp::Factories;
QXmppElement QXmpp::Omemo::PreKey::toXml() const { QXmppElement QXmpp::Omemo::PreKey::toXml() const {
auto pk = createElement("preKeyPublic"); auto pk = createElement("preKeyPublic");
pk.setAttribute("preKeyId", QString::number(id)); pk.setAttribute("preKeyId", QString::number(id));
// TODO: Base64 // TODO: Base64
pk.setValue(data); pk.setValue(data);
return pk; return pk;
} }
void QXmpp::Omemo::PreKey::fromXml(const QXmppElement &element) { void QXmpp::Omemo::PreKey::fromXml(const QXmppElement &element) {
if (!elementMatches(element, "preKeyPublic")) if (!elementMatches(element, "preKeyPublic"))
return; return;
id = element.attribute("preKeyId").toInt(); id = element.attribute("preKeyId").toInt();
// TODO: Base64 // TODO: Base64
data = element.value(); data = element.value();
} }
QXmppElement QXmpp::Omemo::Bundle::toXml() const { QXmppElement QXmpp::Omemo::Bundle::toXml() const {
auto spkNode = createElement("signedPreKeyPublic"); auto spkNode = createElement("signedPreKeyPublic");
spkNode.setAttribute("signedPreKeyId", QString::number(spkId)); spkNode.setAttribute("signedPreKeyId", QString::number(spkId));
spkNode.setValue(spk); spkNode.setValue(spk);
auto spksNode = createElement("signedPreKeySignature"); auto spksNode = createElement("signedPreKeySignature");
spksNode.setValue(spks); spksNode.setValue(spks);
auto ikNode = createElement("identityKey"); auto ikNode = createElement("identityKey");
ikNode.setValue(ik); ikNode.setValue(ik);
auto prekeysNode = createElement("prekeys"); auto prekeysNode = createElement("prekeys");
for (const auto &pk : prekeys) { for (const auto &pk : prekeys) {
prekeysNode.appendChild(pk.toXml()); prekeysNode.appendChild(pk.toXml());
} }
auto result = createElement("bundle", "eu.siacs.conversations.axolotl"); auto result = createElement("bundle", "eu.siacs.conversations.axolotl");
result.appendChild(spkNode); result.appendChild(spkNode);
result.appendChild(spksNode); result.appendChild(spksNode);
result.appendChild(ikNode); result.appendChild(ikNode);
result.appendChild(prekeysNode); result.appendChild(prekeysNode);
return result; return result;
} }
QXmppIq QXmpp::Omemo::Bundle::toIq(int deviceId) const { QXmppIq QXmpp::Omemo::Bundle::toIq(int deviceId) const {
QXmppIq iq{}; QXmppIq iq{};
iq.setType(QXmppIq::Set); iq.setType(QXmppIq::Set);
auto item = createElement("item"); auto item = createElement("item");
item.appendChild(toXml()); item.appendChild(toXml());
auto publish = createElement("publish"); auto publish = createElement("publish");
publish.setAttribute( publish.setAttribute(
"node", "node",
QString("eu.siacs.conversations.axolotl.bundles:%s").arg(deviceId)); QString("eu.siacs.conversations.axolotl.bundles:%s").arg(deviceId));
publish.appendChild(item); publish.appendChild(item);
auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub");
pubSub.appendChild(publish); pubSub.appendChild(publish);
pubSub.appendChild(createOpenPublishOptions()); pubSub.appendChild(createOpenPublishOptions());
iq.extensions().push_back(pubSub); iq.extensions().push_back(pubSub);
return iq; return iq;
} }
void QXmpp::Omemo::Bundle::fromXml(const QXmppElement &element) { void QXmpp::Omemo::Bundle::fromXml(const QXmppElement &element) {
if (!elementMatches(element, "bundle", "eu.siacs.conversations.axolotl")) if (!elementMatches(element, "bundle", "eu.siacs.conversations.axolotl"))
return; return;
auto spkNode = element.firstChildElement("spk"); auto spkNode = element.firstChildElement("spk");
if (spkNode.isNull()) { if (spkNode.isNull()) {
qWarning() << "'bundle': missing 'spk'"; qWarning() << "'bundle': missing 'spk'";
return; return;
} }
spk = spkNode.value(); spk = spkNode.value();
spkId = spkNode.attribute("id").toInt(); spkId = spkNode.attribute("id").toInt();
auto spksNode = element.firstChildElement("spks"); auto spksNode = element.firstChildElement("spks");
if (spksNode.isNull()) { if (spksNode.isNull()) {
qWarning() << "'bundle': missing 'spks'"; qWarning() << "'bundle': missing 'spks'";
return; return;
} }
spks = spksNode.value(); spks = spksNode.value();
auto ikNode = element.firstChildElement("ik"); auto ikNode = element.firstChildElement("ik");
if (ikNode.isNull()) { if (ikNode.isNull()) {
qWarning() << "'bundle': missing 'ik'"; qWarning() << "'bundle': missing 'ik'";
return; return;
} }
ik = ikNode.value(); ik = ikNode.value();
auto prekeysNode = element.firstChildElement("prekeys"); auto prekeysNode = element.firstChildElement("prekeys");
auto pkNode = prekeysNode.firstChildElement("pk"); auto pkNode = prekeysNode.firstChildElement("pk");
prekeys.clear(); prekeys.clear();
while (!pkNode.isNull()) { while (!pkNode.isNull()) {
PreKey pk{}; PreKey pk{};
pk.fromXml(pkNode); pk.fromXml(pkNode);
prekeys.push_back(pk); prekeys.push_back(pk);
} }
} }
QXmppPubSubIq QXmpp::Omemo::Bundle::fetchDeviceBundleIq(int deviceId) { QXmppPubSubIq QXmpp::Omemo::Bundle::fetchDeviceBundleIq(int deviceId) {
QXmppPubSubIq iq{}; QXmppPubSubIq iq{};
iq.setType(QXmppIq::Get); iq.setType(QXmppIq::Get);
iq.setQueryNode( iq.setQueryNode(
QString("eu.siacs.conversations.axolotl.bundles:%1").arg(deviceId)); QString("eu.siacs.conversations.axolotl.bundles:%1").arg(deviceId));
QXmppPubSubItem item{}; QXmppPubSubItem item{};
item.setId(QString::number(deviceId)); item.setId(QString::number(deviceId));
iq.setItems({item}); iq.setItems({item});
return iq; return iq;
} }

View File

@ -8,34 +8,36 @@
#include <QString> #include <QString>
class QXmppPubSubIq; class QXmppPubSubIq;
class QXmppElement; class QXmppElement;
class QXmppIq; class QXmppIq;
namespace QXmpp::Omemo { namespace QXmpp::Omemo {
class PreKey { class PreKey {
public: public:
[[nodiscard]] QXmppElement toXml() const; [[nodiscard]] QXmppElement toXml() const;
/// Expects a <pk> /// Expects a <pk>
void fromXml(const QXmppElement &element); void fromXml(const QXmppElement &element);
int id; int id;
QString data; QString data;
}; };
class Bundle { class Bundle {
public: public:
[[nodiscard]] static QXmppPubSubIq fetchDeviceBundleIq(int deviceId); [[nodiscard]] static QXmppPubSubIq fetchDeviceBundleIq(int deviceId);
[[nodiscard]] QXmppElement toXml() const; [[nodiscard]] QXmppElement toXml() const;
[[nodiscard]] QXmppIq toIq(int deviceId) const; [[nodiscard]] QXmppIq toIq(int deviceId) const;
void fromXml(const QXmppElement &element); void fromXml(const QXmppElement &element);
QString spk; QString spk;
int spkId; int spkId;
QString spks; QString spks;
QString ik; QString ik;
QList<PreKey> prekeys; QList<PreKey> prekeys;
}; };
} // namespace QXmpp::Omemo } // namespace QXmpp::Omemo

View File

@ -13,114 +13,114 @@
using namespace QXmpp::Omemo; using namespace QXmpp::Omemo;
Database::Database(QString jid) : jid(std::move(jid)) { Database::Database(QString jid) : jid(std::move(jid)) {
auto cacheLocation = auto cacheLocation =
QStandardPaths::writableLocation(QStandardPaths::CacheLocation); QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
auto path = QString("%1/.omemo/%2").arg(cacheLocation, jid); auto path = QString("%1/.omemo/%2").arg(cacheLocation, jid);
QDir cache(path); QDir cache(path);
if (!cache.exists() && !cache.mkpath(path)) { if (!cache.exists() && !cache.mkpath(path)) {
qWarning() << "Could not create:" << path; qWarning() << "Could not create:" << path;
throw QException(); throw QException();
} }
mdb_env_create(&env); mdb_env_create(&env);
mdb_env_set_maxdbs(env, 5); mdb_env_set_maxdbs(env, 5);
mdb_env_set_mapsize(env, 512UL * 1024UL * 1024UL); mdb_env_set_mapsize(env, 512UL * 1024UL * 1024UL);
mdb_env_open(env, path.toStdString().c_str(), 0, 0664); mdb_env_open(env, path.toStdString().c_str(), 0, 0664);
MDB_txn *txn; MDB_txn *txn;
mdb_txn_begin(env, nullptr, 0, &txn); mdb_txn_begin(env, nullptr, 0, &txn);
mdb_dbi_open(txn, "keys", MDB_CREATE, &dbiKeys); mdb_dbi_open(txn, "keys", MDB_CREATE, &dbiKeys);
mdb_dbi_open(txn, "devices", MDB_CREATE, &dbiDevices); mdb_dbi_open(txn, "devices", MDB_CREATE, &dbiDevices);
mdb_dbi_open(txn, "identity_keys", MDB_CREATE, &dbiIdentityKeys); mdb_dbi_open(txn, "identity_keys", MDB_CREATE, &dbiIdentityKeys);
mdb_txn_commit(txn); mdb_txn_commit(txn);
} }
Database::~Database() { Database::~Database() {
mdb_dbi_close(env, dbiKeys); mdb_dbi_close(env, dbiKeys);
mdb_dbi_close(env, dbiDevices); mdb_dbi_close(env, dbiDevices);
mdb_dbi_close(env, dbiIdentityKeys); mdb_dbi_close(env, dbiIdentityKeys);
mdb_env_close(env); mdb_env_close(env);
} }
QBuffer Database::loadIdentityKeySecret(int deviceId) { return QBuffer(); } QBuffer Database::loadIdentityKeySecret(int deviceId) { return QBuffer(); }
bool Database::saveIdentityKeySecret(int deviceId, bool Database::saveIdentityKeySecret(int deviceId,
const QBuffer &identityKeySecret) { const QBuffer &identityKeySecret) {
MDB_val mdbKey, mdbValue; MDB_val mdbKey, mdbValue;
auto key = QString("%1/secret").arg(QString::number(deviceId)).toStdString(); auto key = QString("%1/secret").arg(QString::number(deviceId)).toStdString();
mdbKey.mv_data = key.data(); mdbKey.mv_data = key.data();
mdbKey.mv_size = key.size(); mdbKey.mv_size = key.size();
mdbValue.mv_data = const_cast<char *>(identityKeySecret.data().data()); mdbValue.mv_data = const_cast<char *>(identityKeySecret.data().data());
mdbValue.mv_size = identityKeySecret.size(); mdbValue.mv_size = identityKeySecret.size();
MDB_txn *txn; MDB_txn *txn;
mdb_txn_begin(env, nullptr, 0, &txn); mdb_txn_begin(env, nullptr, 0, &txn);
auto err = mdb_put(txn, dbiIdentityKeys, &mdbKey, &mdbValue, MDB_NOOVERWRITE); auto err = mdb_put(txn, dbiIdentityKeys, &mdbKey, &mdbValue, MDB_NOOVERWRITE);
if (!err) { if (!err) {
mdb_txn_commit(txn); mdb_txn_commit(txn);
return true; return true;
} }
qWarning() << "could not save identity key secret:" << mdb_strerror(err); qWarning() << "could not save identity key secret:" << mdb_strerror(err);
mdb_txn_abort(txn); mdb_txn_abort(txn);
return false; return false;
} }
int Database::loadActiveDeviceId() { int Database::loadActiveDeviceId() {
MDB_val key, value; MDB_val key, value;
key.mv_data = (void *)"active"; key.mv_data = (void *) "active";
key.mv_size = sizeof("active"); key.mv_size = sizeof("active");
MDB_txn *txn; MDB_txn *txn;
mdb_txn_begin(env, nullptr, 0, &txn); mdb_txn_begin(env, nullptr, 0, &txn);
auto err = mdb_get(txn, dbiIdentityKeys, &key, &value); auto err = mdb_get(txn, dbiIdentityKeys, &key, &value);
if (err) { if (err) {
qWarning() << "could not load active device id:" << mdb_strerror(err); qWarning() << "could not load active device id:" << mdb_strerror(err);
return 0; return 0;
} }
if (value.mv_size != sizeof(int)) { if (value.mv_size != sizeof(int)) {
qWarning() << "mv_size is" << value.mv_size << "instead of" << sizeof(int); qWarning() << "mv_size is" << value.mv_size << "instead of" << sizeof(int);
return 0; return 0;
} }
auto id = *reinterpret_cast<int *>(value.mv_data); auto id = *reinterpret_cast<int *>(value.mv_data);
mdb_txn_abort(txn); mdb_txn_abort(txn);
return id; return id;
} }
bool Database::saveActiveDeviceId(int deviceId) { bool Database::saveActiveDeviceId(int deviceId) {
MDB_val key, value; MDB_val key, value;
key.mv_data = (void *)"active"; key.mv_data = (void *) "active";
key.mv_size = sizeof("active"); key.mv_size = sizeof("active");
value.mv_data = &deviceId; value.mv_data = &deviceId;
value.mv_size = sizeof(deviceId); value.mv_size = sizeof(deviceId);
MDB_txn *txn; MDB_txn *txn;
mdb_txn_begin(env, nullptr, 0, &txn); mdb_txn_begin(env, nullptr, 0, &txn);
auto err = mdb_put(txn, dbiIdentityKeys, &key, &value, 0); auto err = mdb_put(txn, dbiIdentityKeys, &key, &value, 0);
if (err) { if (err) {
qWarning() << "could not save active device id" << mdb_strerror(err); qWarning() << "could not save active device id" << mdb_strerror(err);
return false; return false;
} }
err = mdb_txn_commit(txn); err = mdb_txn_commit(txn);
if (err) { if (err) {
qWarning() << "could not save active device id" << mdb_strerror(err); qWarning() << "could not save active device id" << mdb_strerror(err);
return false; return false;
} }
return true; return true;
} }

View File

@ -10,31 +10,31 @@
namespace QXmpp::Omemo { namespace QXmpp::Omemo {
class Database { class Database {
public: public:
explicit Database(QString jid); explicit Database(QString jid);
~Database(); ~Database();
Database(const Database &) = delete; Database(const Database &) = delete;
Database(Database &&) = delete; Database(Database &&) = delete;
Database &operator=(const Database &) = delete; Database &operator=(const Database &) = delete;
QBuffer loadIdentityKey(); QBuffer loadIdentityKey();
bool saveIdentityKey(const QBuffer &identityKey); bool saveIdentityKey(const QBuffer &identityKey);
int loadActiveDeviceId(); int loadActiveDeviceId();
bool saveActiveDeviceId(int deviceId); bool saveActiveDeviceId(int deviceId);
QBuffer loadIdentityKeySecret(int deviceId); QBuffer loadIdentityKeySecret(int deviceId);
bool saveIdentityKeySecret(int deviceId, const QBuffer &identityKeySecret); bool saveIdentityKeySecret(int deviceId, const QBuffer &identityKeySecret);
const QString jid; const QString jid;
private: private:
MDB_env *env{}; MDB_env *env{};
MDB_dbi dbiDevices{}; MDB_dbi dbiDevices{};
MDB_dbi dbiKeys{}; MDB_dbi dbiKeys{};
MDB_dbi dbiPreKeys{}; MDB_dbi dbiPreKeys{};
MDB_dbi dbiIdentityKeys{}; MDB_dbi dbiIdentityKeys{};
}; };
} // namespace QXmpp::Omemo } // namespace QXmpp::Omemo

View File

@ -7,18 +7,19 @@
#include <QList> #include <QList>
class QXmppElement; class QXmppElement;
class QXmppIq; class QXmppIq;
namespace QXmpp::Omemo { namespace QXmpp::Omemo {
class Device { class Device {
public: public:
int id; int id;
}; };
class DeviceList { class DeviceList {
public: public:
QList<Device> devices; QList<Device> devices;
}; };
} // namespace QXmpp::Omemo } // namespace QXmpp::Omemo

View File

@ -6,10 +6,9 @@
#include <QRandomGenerator> #include <QRandomGenerator>
int QXmpp::Omemo::DeviceKeyStorage::generateDeviceId() { int QXmpp::Omemo::DeviceKeyStorage::generateDeviceId() {
QRandomGenerator random{}; QRandomGenerator random{};
return 1 + random.bounded(INT32_MAX - 1); return 1 + random.bounded(INT32_MAX - 1);
} }
QXmpp::Omemo::DeviceKeyStorage::DeviceKeyStorage(int deviceId) QXmpp::Omemo::DeviceKeyStorage::DeviceKeyStorage(int deviceId) : deviceId(deviceId) {}
: deviceId(deviceId) {}

View File

@ -6,13 +6,13 @@
namespace QXmpp::Omemo { namespace QXmpp::Omemo {
class DeviceKeyStorage { class DeviceKeyStorage {
public: public:
static int generateDeviceId(); static int generateDeviceId();
explicit DeviceKeyStorage(int deviceId); explicit DeviceKeyStorage(int deviceId);
const int deviceId; const int deviceId;
}; };
} // namespace QXmpp::Omemo } // namespace QXmpp::Omemo

View File

@ -7,10 +7,9 @@
QXmpp::Omemo::DeviceService::DeviceService(QObject *parent) : QObject(parent) {} QXmpp::Omemo::DeviceService::DeviceService(QObject *parent) : QObject(parent) {}
void QXmpp::Omemo::DeviceService::onDeviceListReceived( void QXmpp::Omemo::DeviceService::onDeviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list) {
const QString &jid, const QXmpp::Omemo::DeviceList &list) {
for (const auto &device : list.devices) { for (const auto &device : list.devices) {
qInfo() << "Got device for" << jid << ":" << device.id; qInfo() << "Got device for" << jid << ":" << device.id;
} }
} }

View File

@ -11,19 +11,19 @@
namespace QXmpp::Omemo { namespace QXmpp::Omemo {
class DeviceList; class DeviceList;
class DeviceService : public QObject { class DeviceService : public QObject {
Q_OBJECT Q_OBJECT
public: public:
explicit DeviceService(QObject *parent); explicit DeviceService(QObject *parent);
public slots: public slots:
void onDeviceListReceived(const QString &jid, const DeviceList &list); void onDeviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list);
private: private:
QMap<QString, UserDeviceList> device_lists{}; QMap<QString, UserDeviceList> device_lists{};
}; };
} // namespace QXmpp::Omemo } // namespace QXmpp::Omemo

View File

@ -13,82 +13,82 @@
using namespace QXmpp::Factories; using namespace QXmpp::Factories;
QXmppElement QXmpp::Omemo::EncryptedMessage::header() const { QXmppElement QXmpp::Omemo::EncryptedMessage::header() const {
auto result = createElement("header"); auto result = createElement("header");
result.setAttribute("sid", QString::number(fromDeviceId)); result.setAttribute("sid", QString::number(fromDeviceId));
auto ivNode = createElement("iv"); auto ivNode = createElement("iv");
ivNode.setValue(iv); ivNode.setValue(iv);
for (const auto &key : keys) { for (const auto &key : keys) {
result.appendChild(key.toXml()); result.appendChild(key.toXml());
} }
return result; return result;
} }
QXmppElement QXmpp::Omemo::EncryptedMessage::toXml() const { QXmppElement QXmpp::Omemo::EncryptedMessage::toXml() const {
auto result = createElement("encrypted", "eu.siacs.conversations.axolotl"); auto result = createElement("encrypted", "eu.siacs.conversations.axolotl");
result.appendChild(header()); result.appendChild(header());
// TODO: Payload is optional // TODO: Payload is optional
result.appendChild(payload()); result.appendChild(payload());
return result; return result;
} }
QXmppElement QXmpp::Omemo::EncryptedMessage::payload() const { QXmppElement QXmpp::Omemo::EncryptedMessage::payload() const {
QBuffer buffer; QBuffer buffer;
buffer.open(QIODevice::ReadWrite); buffer.open(QIODevice::ReadWrite);
QXmlStreamWriter writer(&buffer); QXmlStreamWriter writer(&buffer);
message.toXml(&writer); message.toXml(&writer);
QDomDocument doc; QDomDocument doc;
doc.setContent(buffer.data(), true); doc.setContent(buffer.data(), true);
QXmppElement root(doc.documentElement()); QXmppElement root(doc.documentElement());
root.setTagName("payload"); root.setTagName("payload");
return root; return root;
} }
QXmppElement QXmpp::Omemo::EncryptedMessage::content() const { QXmppElement QXmpp::Omemo::EncryptedMessage::content() const {
auto envelope = createElement("content", "urn:xmpp:sce:0"); auto envelope = createElement("content", "urn:xmpp:sce:0");
envelope.appendChild(payload()); envelope.appendChild(payload());
if (!from.isEmpty()) { if (!from.isEmpty()) {
auto fromNode = createElement("from"); auto fromNode = createElement("from");
fromNode.setAttribute("jid", from); fromNode.setAttribute("jid", from);
envelope.appendChild(fromNode); envelope.appendChild(fromNode);
} }
if (!to.isEmpty()) { if (!to.isEmpty()) {
auto toNode = createElement("to"); auto toNode = createElement("to");
toNode.setAttribute("jid", to); toNode.setAttribute("jid", to);
envelope.appendChild(toNode); envelope.appendChild(toNode);
} }
if (!timestamp.isNull()) { if (!timestamp.isNull()) {
auto timeNode = createElement("time"); auto timeNode = createElement("time");
timeNode.setAttribute("stamp", timestamp.toString(Qt::DateFormat::ISODate)); timeNode.setAttribute("stamp", timestamp.toString(Qt::DateFormat::ISODate));
envelope.appendChild(timeNode); envelope.appendChild(timeNode);
} }
auto rpad = createElement("rpad"); auto rpad = createElement("rpad");
rpad.setValue(QXmpp::Sce::generatePadding()); rpad.setValue(QXmpp::Sce::generatePadding());
envelope.appendChild(rpad); envelope.appendChild(rpad);
return envelope; return envelope;
} }
QXmppElement QXmpp::Omemo::MessageKey::toXml() const { QXmppElement QXmpp::Omemo::MessageKey::toXml() const {
auto result = createElement("key"); auto result = createElement("key");
result.setAttribute("rid", QString::number(receivingDeviceId)); result.setAttribute("rid", QString::number(receivingDeviceId));
if (prekey) if (prekey)
result.setAttribute("prekey", "true"); result.setAttribute("prekey", "true");
result.setValue(key); result.setValue(key);
return result; return result;
} }

View File

@ -11,32 +11,32 @@
namespace QXmpp::Omemo { namespace QXmpp::Omemo {
class MessageKey { class MessageKey {
public: public:
[[nodiscard]] QXmppElement toXml() const; [[nodiscard]] QXmppElement toXml() const;
int receivingDeviceId{}; int receivingDeviceId{};
bool prekey{}; bool prekey{};
QString key{}; QString key{};
}; };
class EncryptedMessage { class EncryptedMessage {
public: public:
[[nodiscard]] QXmppElement header() const; [[nodiscard]] QXmppElement header() const;
[[nodiscard]] QXmppElement content() const; [[nodiscard]] QXmppElement content() const;
[[nodiscard]] QXmppElement toXml() const; [[nodiscard]] QXmppElement toXml() const;
[[nodiscard]] QXmppElement payload() const; [[nodiscard]] QXmppElement payload() const;
int fromDeviceId{}; int fromDeviceId{};
QList<MessageKey> keys{}; QList<MessageKey> keys{};
QString from{}; QString from{};
QString to{}; QString to{};
QDateTime timestamp{}; QDateTime timestamp{};
QString iv{}; QString iv{};
QXmppMessage message{}; QXmppMessage message{};
}; };
} // namespace QXmpp::Omemo } // namespace QXmpp::Omemo

View File

@ -16,60 +16,59 @@
using namespace QXmpp::Omemo; using namespace QXmpp::Omemo;
Manager::Manager() Manager::Manager()
: deviceService(new DeviceService(this)), : deviceService(new DeviceService(this)),
omemoVariant(new Variant::Conversations) { omemoVariant(new Variant::Conversations) {
connect(this, &Manager::deviceListReceived, deviceService.get(), connect(this, &Manager::deviceListReceived, deviceService.get(), &DeviceService::onDeviceListReceived);
&DeviceService::onDeviceListReceived);
} }
bool QXmpp::Omemo::Manager::handleStanza(const QDomElement &stanza) { bool QXmpp::Omemo::Manager::handleStanza(const QDomElement &stanza) {
QString str{}; QString str{};
QTextStream info(&str); QTextStream info(&str);
stanza.save(info, 4); stanza.save(info, 4);
std::cout << str.toStdString(); std::cout << str.toStdString();
if (stanza.tagName() == "iq") { if (stanza.tagName() == "iq") {
if (stanza.attribute("type") == "result") { if (stanza.attribute("type") == "result") {
auto pubsub = stanza.firstChildElement("pubsub"); auto pubsub = stanza.firstChildElement("pubsub");
if (!pubsub.isNull()) { if (!pubsub.isNull()) {
auto items = pubsub.firstChildElement("items"); auto items = pubsub.firstChildElement("items");
if (items.attribute("node") == if (items.attribute("node") ==
"eu.siacs.conversations.axolotl.devicelist") { "eu.siacs.conversations.axolotl.devicelist") {
auto item = items.firstChildElement("item"); auto item = items.firstChildElement("item");
if (!item.isNull()) { if (!item.isNull()) {
auto list = item.firstChildElement("list"); auto list = item.firstChildElement("list");
if (!list.isNull()) { if (!list.isNull()) {
DeviceList deviceList = omemoVariant->deviceListFromXml(list); DeviceList deviceList = omemoVariant->deviceListFromXml(list);
emit deviceListReceived(stanza.attribute("from"), deviceList); emit deviceListReceived(stanza.attribute("from"), deviceList);
return true; return true;
}
}
}
} }
}
} }
}
} }
}
return false; return false;
} }
void QXmpp::Omemo::Manager::setClient(QXmppClient *client) { void QXmpp::Omemo::Manager::setClient(QXmppClient *client) {
QXmppClientExtension::setClient(client); QXmppClientExtension::setClient(client);
if (!client) if (!client)
return; return;
QObject::connect(client, &QXmppClient::connected, this, QObject::connect(client, &QXmppClient::connected, this,
&Manager::fetchOwnDevices); &Manager::fetchOwnDevices);
} }
void QXmpp::Omemo::Manager::fetchOwnDevices() { void QXmpp::Omemo::Manager::fetchOwnDevices() {
QXmppPubSubIq iq{}; QXmppPubSubIq iq{};
iq.setFrom(client()->configuration().jid()); iq.setFrom(client()->configuration().jid());
iq.setTo(client()->configuration().jidBare()); iq.setTo(client()->configuration().jidBare());
iq.setType(QXmppIq::Get); iq.setType(QXmppIq::Get);
iq.setQueryNode("eu.siacs.conversations.axolotl.devicelist"); iq.setQueryNode("eu.siacs.conversations.axolotl.devicelist");
client()->sendPacket(iq); client()->sendPacket(iq);
} }

View File

@ -12,27 +12,27 @@
namespace QXmpp::Omemo { namespace QXmpp::Omemo {
class Manager : public QXmppClientExtension { class Manager : public QXmppClientExtension {
Q_OBJECT; Q_OBJECT;
public: public:
Manager(); Manager();
~Manager() override = default; ~Manager() override = default;
bool handleStanza(const QDomElement &stanza) override; bool handleStanza(const QDomElement &stanza) override;
public slots: public slots:
void fetchOwnDevices(); void fetchOwnDevices();
signals: signals:
void deviceListReceived(const QString &jid, const DeviceList &list); void deviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list);
protected: protected:
void setClient(QXmppClient *client) override; void setClient(QXmppClient *client) override;
private: private:
QScopedPointer<DeviceService> deviceService; QScopedPointer<DeviceService> deviceService;
QScopedPointer<Variant::Base> omemoVariant; QScopedPointer<Variant::Base> omemoVariant;
}; };
} // namespace QXmpp::Omemo } // namespace QXmpp::Omemo

View File

@ -6,19 +6,21 @@
#include <QRandomGenerator> #include <QRandomGenerator>
#define RPAD_ALPHABET "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
constexpr int RPAD_MAX_LENGTH = 200; constexpr int RPAD_MAX_LENGTH = 200;
QString QXmpp::Sce::generatePadding() { QString QXmpp::Sce::generatePadding() {
QRandomGenerator random{}; QRandomGenerator random{};
QString result{}; QString result{};
QString alphabet{ QStringLiteral("!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~") }; QString alphabet{QStringLiteral(RPAD_ALPHABET)};
auto length = random.bounded(RPAD_MAX_LENGTH); auto length = random.bounded(RPAD_MAX_LENGTH);
result.resize(length); result.resize(length);
for (auto i = 0; i < length; ++i) { for (auto i = 0; i < length; ++i) {
result[i] = alphabet[random.bounded(alphabet.length())]; result[i] = alphabet[random.bounded(alphabet.length())];
} }
return result; return result;
} }

View File

@ -9,6 +9,6 @@
namespace QXmpp::Sce { namespace QXmpp::Sce {
QString generatePadding(); QString generatePadding();
} }

View File

@ -35,7 +35,8 @@ int Signal::Store::SignedPreKeyStore::loadSignedPreKey(signal_buffer **record, u
return 0; return 0;
} }
int Signal::Store::SignedPreKeyStore::storeSignedPreKey(uint32_t signed_pre_key_id, uint8_t *record, size_t record_len) { int
Signal::Store::SignedPreKeyStore::storeSignedPreKey(uint32_t signed_pre_key_id, uint8_t *record, size_t record_len) {
return 0; return 0;
} }

View File

@ -6,5 +6,4 @@
#include <utility> #include <utility>
QXmpp::Omemo::UserDeviceList::UserDeviceList(QString jid) QXmpp::Omemo::UserDeviceList::UserDeviceList(QString jid) : jid(std::move(jid)) {}
: jid(std::move(jid)) {}

View File

@ -8,11 +8,11 @@
namespace QXmpp::Omemo { namespace QXmpp::Omemo {
class UserDeviceList { class UserDeviceList {
public: public:
explicit UserDeviceList(QString jid); explicit UserDeviceList(QString jid);
const QString jid; const QString jid;
}; };
} // namespace QXmpp::Omemo } // namespace QXmpp::Omemo

View File

@ -13,66 +13,66 @@ using namespace QXmpp::Omemo;
using namespace QXmpp::Factories; using namespace QXmpp::Factories;
QXmppElement Variant::Conversations::deviceToXml(const Device &device) { QXmppElement Variant::Conversations::deviceToXml(const Device &device) {
auto result = createElement("device"); auto result = createElement("device");
result.setAttribute("id", QString::number(device.id)); result.setAttribute("id", QString::number(device.id));
return result; return result;
} }
Device Variant::Conversations::deviceFromXml(const QXmppElement &xml) { Device Variant::Conversations::deviceFromXml(const QXmppElement &xml) {
Device result{}; Device result{};
if (!elementMatches(xml, "device"))
return result;
result.id = xml.attribute("id").toInt();
if (!elementMatches(xml, "device"))
return result; return result;
result.id = xml.attribute("id").toInt();
return result;
} }
QXmppElement QXmppElement
Variant::Conversations::deviceListToXml(const DeviceList &deviceList) { Variant::Conversations::deviceListToXml(const DeviceList &deviceList) {
auto element = createElement("list", "eu.siacs.conversations.axolotl"); auto element = createElement("list", "eu.siacs.conversations.axolotl");
for (const auto &device : deviceList.devices) { for (const auto &device : deviceList.devices) {
element.appendChild(deviceToXml(device)); element.appendChild(deviceToXml(device));
} }
return element; return element;
} }
DeviceList Variant::Conversations::deviceListFromXml(const QXmppElement &xml) { DeviceList Variant::Conversations::deviceListFromXml(const QXmppElement &xml) {
DeviceList result{}; 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");
}
if (!elementMatches(xml, "list", "eu.siacs.conversations.axolotl"))
return result; 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 Variant::Conversations::deviceListSetIq(const DeviceList &deviceList) {
QXmppIq iq{}; QXmppIq iq{};
iq.setType(QXmppIq::Set); iq.setType(QXmppIq::Set);
auto item = createElement("item"); auto item = createElement("item");
item.appendChild(deviceListToXml(deviceList)); item.appendChild(deviceListToXml(deviceList));
auto publish = createElement("publish"); auto publish = createElement("publish");
publish.setAttribute("node", "eu.siacs.conversations.axolotl.devicelist"); publish.setAttribute("node", "eu.siacs.conversations.axolotl.devicelist");
publish.appendChild(item); publish.appendChild(item);
auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub"); auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub");
pubSub.appendChild(publish); pubSub.appendChild(publish);
pubSub.appendChild(createOpenPublishOptions()); pubSub.appendChild(createOpenPublishOptions());
iq.extensions().push_back(pubSub); iq.extensions().push_back(pubSub);
return iq; return iq;
} }

View File

@ -8,16 +8,16 @@
namespace QXmpp::Omemo::Variant { namespace QXmpp::Omemo::Variant {
class Conversations : public Base { class Conversations : public Base {
public: public:
~Conversations() override = default; ~Conversations() override = default;
QXmppElement deviceToXml(const Device &device) override; QXmppElement deviceToXml(const Device &device) override;
Device deviceFromXml(const QXmppElement &xml) override; Device deviceFromXml(const QXmppElement &xml) override;
QXmppElement deviceListToXml(const DeviceList &deviceList) override; QXmppElement deviceListToXml(const DeviceList &deviceList) override;
DeviceList deviceListFromXml(const QXmppElement &xml) override; DeviceList deviceListFromXml(const QXmppElement &xml) override;
QXmppIq deviceListSetIq(const DeviceList &deviceList) override; QXmppIq deviceListSetIq(const DeviceList &deviceList) override;
}; };
} // namespace QXmpp::Omemo::Variant } // namespace QXmpp::Omemo::Variant

View File

@ -5,27 +5,29 @@
#pragma once #pragma once
class QXmppElement; class QXmppElement;
class QXmppIq; class QXmppIq;
namespace QXmpp::Omemo { namespace QXmpp::Omemo {
class Device; class Device;
class DeviceList;
namespace Variant { class DeviceList;
class Base { namespace Variant {
public:
virtual ~Base() = default;
virtual QXmppElement deviceToXml(const Device &device) = 0; class Base {
virtual Device deviceFromXml(const QXmppElement &xml) = 0; public:
virtual ~Base() = default;
virtual QXmppElement deviceListToXml(const DeviceList &deviceList) = 0; virtual QXmppElement deviceToXml(const Device &device) = 0;
virtual DeviceList deviceListFromXml(const QXmppElement &xml) = 0; virtual Device deviceFromXml(const QXmppElement &xml) = 0;
virtual QXmppIq deviceListSetIq(const DeviceList &deviceList) = 0;
};
} // namespace Variant 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 } // namespace QXmpp::Omemo