forked from blue/squawk
feat: omemo signal lib wip
This commit is contained in:
parent
08fe37bfb2
commit
442ad37300
@ -9,6 +9,8 @@ target_sources(squawk PRIVATE
|
||||
device_key_storage.h
|
||||
device_service.cpp
|
||||
device_service.h
|
||||
key.cpp
|
||||
key.h
|
||||
qomemo.cpp
|
||||
qomemo.h
|
||||
sce.cpp
|
||||
|
@ -3,127 +3,3 @@
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QBuffer>
|
||||
|
||||
class QXmppPubSubIq;
|
||||
|
||||
@ -17,26 +18,16 @@ namespace QXmpp::Omemo {
|
||||
|
||||
class PreKey {
|
||||
public:
|
||||
[[nodiscard]] QXmppElement toXml() const;
|
||||
/// Expects a <pk>
|
||||
void fromXml(const QXmppElement &element);
|
||||
|
||||
int id;
|
||||
QString data;
|
||||
QByteArray 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;
|
||||
QByteArray spk;
|
||||
int spkId;
|
||||
QString spks;
|
||||
QString ik;
|
||||
QByteArray spks;
|
||||
QByteArray ik;
|
||||
QList<PreKey> prekeys;
|
||||
};
|
||||
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
#include "database.h"
|
||||
|
||||
#include "bundle.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QException>
|
||||
@ -44,17 +46,17 @@ Database::~Database() {
|
||||
mdb_env_close(env);
|
||||
}
|
||||
|
||||
QBuffer Database::loadIdentityKeySecret(int deviceId) { return QBuffer(); }
|
||||
std::optional<QByteArray> Database::loadIdentityKeySecret(int deviceId) { return std::nullopt; }
|
||||
|
||||
bool Database::saveIdentityKeySecret(int deviceId,
|
||||
const QBuffer &identityKeySecret) {
|
||||
const QByteArray &identityKeySecret) {
|
||||
MDB_val mdbKey, mdbValue;
|
||||
auto key = QString("%1/secret").arg(QString::number(deviceId)).toStdString();
|
||||
|
||||
mdbKey.mv_data = key.data();
|
||||
mdbKey.mv_size = key.size();
|
||||
|
||||
mdbValue.mv_data = const_cast<char *>(identityKeySecret.data().data());
|
||||
mdbValue.mv_data = const_cast<char *>(identityKeySecret.data());
|
||||
mdbValue.mv_size = identityKeySecret.size();
|
||||
|
||||
MDB_txn *txn;
|
||||
@ -71,7 +73,7 @@ bool Database::saveIdentityKeySecret(int deviceId,
|
||||
return false;
|
||||
}
|
||||
|
||||
int Database::loadActiveDeviceId() {
|
||||
std::optional<int> Database::loadActiveDeviceId() {
|
||||
MDB_val key, value;
|
||||
|
||||
key.mv_data = (void *) "active";
|
||||
@ -83,12 +85,14 @@ int Database::loadActiveDeviceId() {
|
||||
auto err = mdb_get(txn, dbiIdentityKeys, &key, &value);
|
||||
if (err) {
|
||||
qWarning() << "could not load active device id:" << mdb_strerror(err);
|
||||
return 0;
|
||||
mdb_txn_abort(txn);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (value.mv_size != sizeof(int)) {
|
||||
qWarning() << "mv_size is" << value.mv_size << "instead of" << sizeof(int);
|
||||
return 0;
|
||||
mdb_txn_abort(txn);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto id = *reinterpret_cast<int *>(value.mv_data);
|
||||
@ -124,3 +128,53 @@ bool Database::saveActiveDeviceId(int deviceId) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Database::saveIdentityKey(int deviceId, const QByteArray &identityKey) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<QByteArray> Database::loadIdentityKey(int deviceId) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool Database::containsPreKey() {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<KeyPair> Database::loadPreKey(int deviceId, int id) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool Database::savePreKey(int deviceId, int id, const KeyPair &preKey) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<Bundle> Database::loadBundle(int deviceId) {
|
||||
Bundle result{};
|
||||
|
||||
auto ik = loadIdentityKey(deviceId);
|
||||
|
||||
result.ik = ik.value();
|
||||
|
||||
auto spk = loadSignedPreKey(deviceId);
|
||||
|
||||
result.spk = spk->key.publicKey;
|
||||
result.spks = spk->signature;
|
||||
result.spkId = spk->id;
|
||||
|
||||
// PreKeys
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Database::saveBundle(int deviceId, const Bundle &bundle) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::optional<SignedPreKey> Database::loadSignedPreKey(int deviceId) {
|
||||
return std::optional<SignedPreKey>();
|
||||
}
|
||||
|
||||
bool Database::saveSignedPreKey(int deviceId, const SignedPreKey &signedPreKey) {
|
||||
return false;
|
||||
}
|
||||
|
@ -8,8 +8,12 @@
|
||||
#include <QString>
|
||||
#include <lmdb.h>
|
||||
|
||||
#include "key.h"
|
||||
|
||||
namespace QXmpp::Omemo {
|
||||
|
||||
class Bundle;
|
||||
|
||||
class Database {
|
||||
public:
|
||||
explicit Database(QString jid);
|
||||
@ -18,14 +22,26 @@ namespace QXmpp::Omemo {
|
||||
Database(Database &&) = delete;
|
||||
Database &operator=(const Database &) = delete;
|
||||
|
||||
QBuffer loadIdentityKey();
|
||||
bool saveIdentityKey(const QBuffer &identityKey);
|
||||
|
||||
int loadActiveDeviceId();
|
||||
// For local user
|
||||
std::optional<int> loadActiveDeviceId();
|
||||
bool saveActiveDeviceId(int deviceId);
|
||||
|
||||
QBuffer loadIdentityKeySecret(int deviceId);
|
||||
bool saveIdentityKeySecret(int deviceId, const QBuffer &identityKeySecret);
|
||||
std::optional<QByteArray> loadIdentityKeySecret(int deviceId);
|
||||
bool saveIdentityKeySecret(int deviceId, const QByteArray &identityKeySecret);
|
||||
|
||||
std::optional<Bundle> loadBundle(int deviceId);
|
||||
bool saveBundle(int deviceId, const Bundle& bundle);
|
||||
|
||||
// For any user
|
||||
std::optional<QByteArray> loadIdentityKey(int deviceId);
|
||||
bool saveIdentityKey(int deviceId, const QByteArray &identityKey);
|
||||
|
||||
bool containsPreKey();
|
||||
std::optional<KeyPair> loadPreKey(int deviceId, int id);
|
||||
bool savePreKey(int deviceId, int id, const KeyPair &preKey);
|
||||
|
||||
std::optional<SignedPreKey> loadSignedPreKey(int deviceId);
|
||||
bool saveSignedPreKey(int deviceId, const SignedPreKey& signedPreKey);
|
||||
|
||||
const QString jid;
|
||||
|
||||
|
@ -5,11 +5,31 @@
|
||||
#include "device_service.h"
|
||||
#include "device.h"
|
||||
|
||||
QXmpp::Omemo::DeviceService::DeviceService(QObject *parent) : QObject(parent) {}
|
||||
using namespace QXmpp::Omemo;
|
||||
|
||||
void QXmpp::Omemo::DeviceService::onDeviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list) {
|
||||
DeviceService::DeviceService(QObject *parent) : QObject(parent) {}
|
||||
|
||||
void DeviceService::onDeviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list) {
|
||||
|
||||
for (const auto &device : list.devices) {
|
||||
qInfo() << "Got device for" << jid << ":" << device.id;
|
||||
}
|
||||
}
|
||||
|
||||
QSharedPointer<Database> DeviceService::getDatabase(const QString &jid) {
|
||||
if (!databases.contains(jid)) {
|
||||
databases.insert(jid, QSharedPointer<Database>::create(jid));
|
||||
}
|
||||
|
||||
return databases[jid];
|
||||
}
|
||||
|
||||
void DeviceService::addIdentity(const QString &jid, int deviceId, const QByteArray& publicKey) {
|
||||
auto db = getDatabase(jid);
|
||||
|
||||
db->saveIdentityKey(deviceId, publicKey);
|
||||
}
|
||||
|
||||
void DeviceService::onDeviceListNotFound(const QString &jid) {
|
||||
qInfo() << "Device list not found:" << jid;
|
||||
}
|
||||
|
@ -4,8 +4,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QBuffer>
|
||||
|
||||
#include "qomemo.h"
|
||||
#include "user_device_list.h"
|
||||
#include "database.h"
|
||||
|
||||
#include <QXmppClient.h>
|
||||
|
||||
@ -19,11 +22,17 @@ namespace QXmpp::Omemo {
|
||||
public:
|
||||
explicit DeviceService(QObject *parent);
|
||||
|
||||
QSharedPointer<Database> getDatabase(const QString& jid);
|
||||
|
||||
public slots:
|
||||
void addIdentity(const QString& jid, int deviceId, const QByteArray& publicKey);
|
||||
|
||||
void onDeviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list);
|
||||
|
||||
void onDeviceListNotFound(const QString &jid);
|
||||
|
||||
private:
|
||||
QMap<QString, UserDeviceList> device_lists{};
|
||||
QMap<QString, QSharedPointer<Database>> databases{};
|
||||
};
|
||||
|
||||
} // namespace QXmpp::Omemo
|
||||
|
6
qomemo/key.cpp
Normal file
6
qomemo/key.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
/*
|
||||
* Created by victoria on 2021-05-15.
|
||||
*/
|
||||
|
||||
#include "key.h"
|
||||
|
27
qomemo/key.h
Normal file
27
qomemo/key.h
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Created by victoria on 2021-05-15.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include <QByteArray>
|
||||
|
||||
namespace QXmpp::Omemo {
|
||||
|
||||
class KeyPair {
|
||||
public:
|
||||
QByteArray publicKey{};
|
||||
std::optional<QByteArray> secretKey{ std::nullopt };
|
||||
};
|
||||
|
||||
class SignedPreKey {
|
||||
public:
|
||||
int id{ 0 };
|
||||
|
||||
KeyPair key{};
|
||||
QByteArray signature{};
|
||||
};
|
||||
|
||||
}
|
@ -4,6 +4,9 @@
|
||||
|
||||
#include "qxmpp_omemo_manager.h"
|
||||
|
||||
#include "qomemo/signal/context.h"
|
||||
|
||||
#include "bundle.h"
|
||||
#include "device.h"
|
||||
#include "variant/conversations.h"
|
||||
|
||||
@ -12,13 +15,24 @@
|
||||
#include <QXmppClient.h>
|
||||
#include <QXmppPubSubIq.h>
|
||||
#include <iostream>
|
||||
#include <QRandomGenerator64>
|
||||
#include <external/signal-protocol-c/src/signal_protocol_internal.h>
|
||||
|
||||
using namespace QXmpp::Omemo;
|
||||
|
||||
Manager::Manager()
|
||||
: deviceService(new DeviceService(this)),
|
||||
: deviceService{new DeviceService(this)},
|
||||
omemoVariant(new Variant::Conversations) {
|
||||
connect(this, &Manager::deviceListReceived, deviceService.get(), &DeviceService::onDeviceListReceived);
|
||||
connect(this, &Manager::deviceListNotFound, deviceService.get(), &DeviceService::onDeviceListNotFound);
|
||||
connect(this, &Manager::deviceListNotFound, this, [this](const QString &jid) {
|
||||
if (jid == client()->configuration().jidBare())
|
||||
generateDeviceListForSelf();
|
||||
});
|
||||
connect(this, &Manager::deviceListReceived, this, [this](const QString &jid, const DeviceList ¤tList) {
|
||||
if (jid == client()->configuration().jidBare())
|
||||
generateDeviceForSelfIfNeeded(currentList);
|
||||
});
|
||||
}
|
||||
|
||||
bool QXmpp::Omemo::Manager::handleStanza(const QDomElement &stanza) {
|
||||
@ -28,29 +42,60 @@ bool QXmpp::Omemo::Manager::handleStanza(const QDomElement &stanza) {
|
||||
|
||||
std::cout << str.toStdString();
|
||||
|
||||
if (stanza.tagName() == "iq") {
|
||||
if (stanza.attribute("type") == "result") {
|
||||
if (handleDeviceList(stanza) || handleMissingDeviceList(stanza))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Manager::handleDeviceList(const QDomElement &stanza) {
|
||||
if (!(stanza.tagName() == "iq"))
|
||||
return false;
|
||||
|
||||
if (!(stanza.attribute("type") == "result"))
|
||||
return false;
|
||||
|
||||
auto pubsub = stanza.firstChildElement("pubsub");
|
||||
if (!pubsub.isNull()) {
|
||||
if (pubsub.isNull())
|
||||
return false;
|
||||
|
||||
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 = omemoVariant->deviceListFromXml(list);
|
||||
emit deviceListReceived(stanza.attribute("from"), deviceList);
|
||||
if (!(items.attribute("node") == omemoVariant->getDeviceListNode()))
|
||||
return false;
|
||||
|
||||
auto deviceList = omemoVariant->latestDeviceListFromPubSubNode(items);
|
||||
if (!deviceList.has_value())
|
||||
return false;
|
||||
|
||||
emit deviceListReceived(stanza.attribute("from"), deviceList.value());
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Manager::handleMissingDeviceList(const QDomElement &stanza) {
|
||||
if (stanza.tagName() != "iq")
|
||||
return false;
|
||||
|
||||
if (stanza.attribute("type") != "error")
|
||||
return false;
|
||||
|
||||
auto pubsub = stanza.firstChildElement("pubsub");
|
||||
if (pubsub.isNull())
|
||||
return false;
|
||||
|
||||
auto items = pubsub.firstChildElement("items");
|
||||
if (items.attribute("node") != omemoVariant->getDeviceListNode())
|
||||
return false;
|
||||
|
||||
auto error = stanza.firstChildElement("error");
|
||||
|
||||
if (error.namespaceURI() != "jabber:client" || error.attribute("code") != "404")
|
||||
return false;
|
||||
|
||||
qDebug() << "Got 404 deviceList for" << stanza.attribute("from");
|
||||
emit deviceListNotFound(stanza.attribute("from"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QXmpp::Omemo::Manager::setClient(QXmppClient *client) {
|
||||
@ -68,7 +113,152 @@ void QXmpp::Omemo::Manager::fetchOwnDevices() {
|
||||
iq.setFrom(client()->configuration().jid());
|
||||
iq.setTo(client()->configuration().jidBare());
|
||||
iq.setType(QXmppIq::Get);
|
||||
iq.setQueryNode("eu.siacs.conversations.axolotl.devicelist");
|
||||
iq.setQueryNode(omemoVariant->getDeviceListNode());
|
||||
|
||||
client()->sendPacket(iq);
|
||||
}
|
||||
|
||||
QSharedPointer<DeviceService> Manager::getDeviceService() {
|
||||
return deviceService;
|
||||
}
|
||||
|
||||
void Manager::publishDeviceList(const DeviceList &deviceList) {
|
||||
// QXmppPubSubIq iq{};
|
||||
// iq.setFrom(client()->configuration().jid());
|
||||
// iq.setType(QXmppIq::Set);
|
||||
// iq.setQueryNode(omemoVariant->getDeviceListNode());
|
||||
// iq.setItems()
|
||||
|
||||
auto iq = omemoVariant->deviceListSetIq(deviceList);
|
||||
iq.setFrom(client()->configuration().jid());
|
||||
|
||||
QString str{};
|
||||
QXmlStreamWriter info(&str);
|
||||
iq.toXml(&info);
|
||||
qInfo() << str;
|
||||
|
||||
client()->sendPacket(iq);
|
||||
}
|
||||
|
||||
void Manager::generateDeviceListForSelf() {
|
||||
qInfo() << "Generate device for self...";
|
||||
|
||||
generateDeviceForSelfIfNeeded(DeviceList());
|
||||
}
|
||||
|
||||
void Manager::publishBundle(int deviceId, const Bundle &bundle) {
|
||||
auto iq = omemoVariant->bundleSetIq(deviceId, bundle);
|
||||
iq.setFrom(client()->configuration().jid());
|
||||
|
||||
QString str{};
|
||||
QXmlStreamWriter info(&str);
|
||||
iq.toXml(&info);
|
||||
qInfo() << str;
|
||||
|
||||
client()->sendPacket(iq);
|
||||
}
|
||||
|
||||
void Manager::generateDeviceForSelfIfNeeded(const DeviceList ¤tList) {
|
||||
auto db = deviceService->getDatabase(client()->configuration().jidBare());
|
||||
auto activeId = db->loadActiveDeviceId();
|
||||
|
||||
if (activeId.has_value()) {
|
||||
qInfo() << "Current device:" << *activeId;
|
||||
|
||||
bool found = false;
|
||||
for (const auto &d : currentList.devices)
|
||||
if (d.id == *activeId)
|
||||
found = true;
|
||||
|
||||
if (found)
|
||||
return;
|
||||
|
||||
qInfo() << "Could not find device" << *activeId << ", generating new one";
|
||||
}
|
||||
|
||||
qInfo() << "Generating device";
|
||||
|
||||
auto deviceId = QRandomGenerator64{}.bounded(INT32_MAX);
|
||||
db->saveActiveDeviceId(deviceId);
|
||||
|
||||
Device device{};
|
||||
device.id = deviceId;
|
||||
|
||||
auto updatedList = currentList;
|
||||
|
||||
auto bundle = Bundle();
|
||||
|
||||
updatedList.devices.push_back(device);
|
||||
|
||||
publishDeviceList(updatedList);
|
||||
publishBundle(deviceId, bundle);
|
||||
}
|
||||
|
||||
Bundle Manager::generateAndSaveBundle(int deviceId) {
|
||||
auto database = deviceService->getDatabase(client()->configuration().jidBare());
|
||||
Bundle result{};
|
||||
|
||||
ec_key_pair *ecKeyPair;
|
||||
curve_generate_key_pair(signalContext->temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped(), &ecKeyPair);
|
||||
|
||||
signal_buffer *ecPublic;
|
||||
ec_public_key_serialize(&ecPublic, ec_key_pair_get_public(ecKeyPair));
|
||||
|
||||
signal_buffer *ecSecret;
|
||||
ec_private_key_serialize(&ecSecret, ec_key_pair_get_private(ecKeyPair));
|
||||
|
||||
QByteArray identityKey((const char *) signal_buffer_const_data(ecPublic), (int) signal_buffer_len(ecPublic));
|
||||
QByteArray identityKeySecret((const char *) signal_buffer_const_data(ecSecret), (int) signal_buffer_len(ecSecret));
|
||||
|
||||
database->saveIdentityKey(deviceId, identityKey);
|
||||
database->saveIdentityKeySecret(deviceId, identityKeySecret);
|
||||
|
||||
// Generate SPK
|
||||
ec_key_pair *ecSpk;
|
||||
curve_generate_key_pair(signalContext->temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped(), &ecSpk);
|
||||
signal_buffer *spkPublic, *spkSecret;
|
||||
ec_public_key_serialize(&spkPublic, ec_key_pair_get_public(ecSpk));
|
||||
ec_private_key_serialize(&spkSecret, ec_key_pair_get_private(ecSpk));
|
||||
|
||||
// Generate SPKs
|
||||
signal_buffer *signature;
|
||||
curve_calculate_signature(signalContext->temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped(), &signature,
|
||||
ec_key_pair_get_private(ecKeyPair), signal_buffer_const_data(spkPublic),
|
||||
signal_buffer_len(spkPublic));
|
||||
|
||||
SignedPreKey spk{};
|
||||
spk.id = 0;
|
||||
spk.key.publicKey = QByteArray((const char *) signal_buffer_const_data(spkPublic), (int) signal_buffer_len(spkPublic));
|
||||
spk.key.secretKey = QByteArray((const char *) signal_buffer_const_data(spkSecret), (int) signal_buffer_len(spkSecret));
|
||||
spk.signature = QByteArray((const char *) signal_buffer_const_data(signature), (int) signal_buffer_len(signature));
|
||||
|
||||
// Generate 100 PK
|
||||
for (auto i = 1; i <= 100; ++i) {
|
||||
ec_key_pair *currentPreKey;
|
||||
curve_generate_key_pair(signalContext->temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped(), ¤tPreKey);
|
||||
|
||||
signal_buffer *pkPublic, *pkSecret;
|
||||
ec_public_key_serialize(&pkPublic, ec_key_pair_get_public(currentPreKey));
|
||||
ec_private_key_serialize(&pkSecret, ec_key_pair_get_private(currentPreKey));
|
||||
|
||||
KeyPair preKey{};
|
||||
preKey.publicKey = QByteArray((const char *) signal_buffer_const_data(pkPublic), (int) signal_buffer_len(pkPublic));
|
||||
preKey.secretKey = QByteArray((const char *) signal_buffer_const_data(pkSecret), (int) signal_buffer_len(pkSecret));
|
||||
|
||||
database->savePreKey(deviceId, i, preKey);
|
||||
|
||||
signal_buffer_free(pkPublic);
|
||||
signal_buffer_free(pkSecret);
|
||||
SIGNAL_UNREF(currentPreKey);
|
||||
}
|
||||
|
||||
signal_buffer_free(signature);
|
||||
|
||||
signal_buffer_free(ecPublic);
|
||||
signal_buffer_free(ecSecret);
|
||||
SIGNAL_UNREF(ecKeyPair);
|
||||
|
||||
signal_buffer_free(spkPublic);
|
||||
signal_buffer_free(spkSecret);
|
||||
SIGNAL_UNREF(ecSpk);
|
||||
}
|
||||
|
@ -10,6 +10,10 @@
|
||||
|
||||
#include <QXmppClientExtension.h>
|
||||
|
||||
namespace Signal {
|
||||
class Context;
|
||||
}
|
||||
|
||||
namespace QXmpp::Omemo {
|
||||
|
||||
class Manager : public QXmppClientExtension {
|
||||
@ -21,18 +25,37 @@ namespace QXmpp::Omemo {
|
||||
|
||||
bool handleStanza(const QDomElement &stanza) override;
|
||||
|
||||
bool handleDeviceList(const QDomElement& stanza);
|
||||
|
||||
bool handleMissingDeviceList(const QDomElement& stanza);
|
||||
|
||||
QSharedPointer<DeviceService> getDeviceService();
|
||||
|
||||
Bundle generateAndSaveBundle(int deviceId);
|
||||
|
||||
public slots:
|
||||
void fetchOwnDevices();
|
||||
|
||||
void publishDeviceList(const QXmpp::Omemo::DeviceList& deviceList);
|
||||
|
||||
void generateDeviceListForSelf();
|
||||
|
||||
void generateDeviceForSelfIfNeeded(const QXmpp::Omemo::DeviceList ¤tList);
|
||||
|
||||
void publishBundle(int deviceId, const QXmpp::Omemo::Bundle& bundle);
|
||||
|
||||
signals:
|
||||
void deviceListReceived(const QString &jid, const QXmpp::Omemo::DeviceList &list);
|
||||
|
||||
void deviceListNotFound(const QString &jid);
|
||||
|
||||
protected:
|
||||
void setClient(QXmppClient *client) override;
|
||||
|
||||
private:
|
||||
QScopedPointer<DeviceService> deviceService;
|
||||
QSharedPointer<DeviceService> deviceService;
|
||||
QScopedPointer<Variant::Base> omemoVariant;
|
||||
QScopedPointer<Signal::Context> signalContext;
|
||||
};
|
||||
|
||||
} // namespace QXmpp::Omemo
|
||||
|
@ -1,6 +1,8 @@
|
||||
target_sources(squawk PRIVATE
|
||||
context.cpp
|
||||
context.h
|
||||
util.cpp
|
||||
util.h
|
||||
)
|
||||
|
||||
add_subdirectory(crypto)
|
||||
|
@ -4,6 +4,19 @@
|
||||
|
||||
#include "context.h"
|
||||
|
||||
Signal::Context::Context() {}
|
||||
using namespace Signal;
|
||||
|
||||
Signal::Context::~Context() {}
|
||||
Context::Context() {}
|
||||
|
||||
Context::~Context() {}
|
||||
|
||||
std::unique_ptr<Crypto::ECKeyPair> Context::generateCurveKeyPair() {
|
||||
auto result = std::unique_ptr<Crypto::ECKeyPair>();
|
||||
// TODO
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
signal_context *Context::temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped() {
|
||||
return ctx;
|
||||
}
|
||||
|
@ -4,6 +4,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "crypto/ec.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <signal/signal_protocol.h>
|
||||
|
||||
namespace Signal {
|
||||
@ -16,6 +20,10 @@ namespace Signal {
|
||||
Context(Context &&) = delete;
|
||||
Context &operator=(const Context &) = delete;
|
||||
|
||||
std::unique_ptr<Crypto::ECKeyPair> generateCurveKeyPair();
|
||||
|
||||
signal_context *temporaryGetContextUnsafeForRawAccessThatNeedsToBeWrapped();
|
||||
|
||||
private:
|
||||
signal_context *ctx{nullptr};
|
||||
};
|
||||
|
@ -7,4 +7,6 @@ target_sources(squawk PRIVATE
|
||||
hmac_sha256_openssl.h
|
||||
sha512_digest_openssl.cpp
|
||||
sha512_digest_openssl.h
|
||||
ec.cpp
|
||||
ec.h
|
||||
)
|
||||
|
5
qomemo/signal/crypto/ec.cpp
Normal file
5
qomemo/signal/crypto/ec.cpp
Normal file
@ -0,0 +1,5 @@
|
||||
/*
|
||||
* Created by victoria on 2021-06-17.
|
||||
*/
|
||||
|
||||
#include "ec.h"
|
20
qomemo/signal/crypto/ec.h
Normal file
20
qomemo/signal/crypto/ec.h
Normal file
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Created by victoria on 2021-06-17.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <signal/signal_protocol.h>
|
||||
|
||||
namespace Signal::Crypto {
|
||||
|
||||
class ECKeyPair {
|
||||
public:
|
||||
ECKeyPair();
|
||||
~ECKeyPair();
|
||||
|
||||
private:
|
||||
ec_key_pair *ec;
|
||||
};
|
||||
|
||||
}
|
@ -4,13 +4,56 @@
|
||||
|
||||
#include "identity_key_store.h"
|
||||
|
||||
void Signal::Store::IdentityKeyStore::boundToContext(
|
||||
signal_protocol_store_context *ctx) {
|
||||
signal_protocol_identity_key_store store{};
|
||||
#include "qomemo/signal/util.h"
|
||||
#include <utility>
|
||||
|
||||
store.user_data = nullptr;
|
||||
store.destroy_func = nullptr;
|
||||
Signal::Store::IdentityKeyStore::IdentityKeyStore(QXmpp::Omemo::DeviceService &deviceService, QString jid, int deviceId)
|
||||
: jid(std::move(jid)), deviceId(deviceId), deviceService(deviceService) {
|
||||
database = deviceService.getDatabase(jid);
|
||||
}
|
||||
|
||||
int Signal::Store::IdentityKeyStore::getIdentityKeyPair(signal_buffer **public_data, signal_buffer **private_data) {
|
||||
auto pk = database->loadIdentityKey(deviceId);
|
||||
auto sk = database->loadIdentityKeySecret(deviceId);
|
||||
|
||||
*public_data = signal_buffer_create(reinterpret_cast<const uint8_t *>(pk->data()), pk->size());
|
||||
*private_data = signal_buffer_create(reinterpret_cast<const uint8_t *>(sk->data()), sk->size());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Signal::Store::IdentityKeyStore::getLocalRegistrationId(uint32_t *registration_id) {
|
||||
// TODO: Figure out what registration id is used for
|
||||
*registration_id = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Signal::Store::IdentityKeyStore::saveIdentity(const signal_protocol_address *address, uint8_t *key_data,
|
||||
size_t key_len) {
|
||||
auto identityJid = Signal::Util::jidFromAddress(address);
|
||||
auto identityKey = Signal::Util::byteArray(key_data, key_len);
|
||||
|
||||
deviceService.getDatabase(identityJid)->saveIdentityKey(address->device_id, identityKey);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Signal::Store::IdentityKeyStore::isTrustedIdentity(const signal_protocol_address *address, uint8_t *key_data,
|
||||
size_t key_len) {
|
||||
auto identityJid = Signal::Util::jidFromAddress(address);
|
||||
auto actualIdentityKey = deviceService.getDatabase(identityJid)->loadIdentityKey(address->device_id);
|
||||
|
||||
if (!actualIdentityKey.has_value()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto givenIdentityKey = Signal::Util::byteArray(key_data, key_len);
|
||||
|
||||
return givenIdentityKey == actualIdentityKey ? 1 : 0;
|
||||
}
|
||||
|
||||
void Signal::Store::IdentityKeyStore::fillCallbacks(signal_protocol_identity_key_store &store) {
|
||||
store.get_identity_key_pair = [](signal_buffer **public_data, signal_buffer **private_data, void *ptr) {
|
||||
return static_cast<IdentityKeyStore *>(ptr)->getIdentityKeyPair(public_data, private_data);
|
||||
};
|
||||
@ -24,24 +67,4 @@ void Signal::Store::IdentityKeyStore::boundToContext(
|
||||
store.save_identity = [](const signal_protocol_address *address, uint8_t *key_data, size_t key_len, void *ptr) {
|
||||
return static_cast<IdentityKeyStore *>(ptr)->saveIdentity(address, key_data, key_len);
|
||||
};
|
||||
|
||||
signal_protocol_store_context_set_identity_key_store(ctx, &store);
|
||||
}
|
||||
|
||||
int Signal::Store::IdentityKeyStore::getIdentityKeyPair(signal_buffer **public_data, signal_buffer **private_data) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Signal::Store::IdentityKeyStore::getLocalRegistrationId(uint32_t *registration_id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Signal::Store::IdentityKeyStore::saveIdentity(const signal_protocol_address *address, uint8_t *key_data,
|
||||
size_t key_len) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Signal::Store::IdentityKeyStore::isTrustedIdentity(const signal_protocol_address *address, uint8_t *key_data,
|
||||
size_t key_len) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -6,16 +6,27 @@
|
||||
|
||||
#include <signal/signal_protocol.h>
|
||||
|
||||
#include "qomemo/device_service.h"
|
||||
|
||||
namespace Signal::Store {
|
||||
|
||||
class IdentityKeyStore {
|
||||
public:
|
||||
static void boundToContext(signal_protocol_store_context *ctx);
|
||||
IdentityKeyStore(QXmpp::Omemo::DeviceService &deviceService, QString jid, int deviceId);
|
||||
|
||||
int getIdentityKeyPair(signal_buffer **public_data, signal_buffer **private_data);
|
||||
int getLocalRegistrationId(uint32_t *registration_id);
|
||||
int saveIdentity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len);
|
||||
int isTrustedIdentity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len);
|
||||
|
||||
void fillCallbacks(signal_protocol_identity_key_store &store);
|
||||
|
||||
const QString jid;
|
||||
const int deviceId;
|
||||
|
||||
private:
|
||||
QXmpp::Omemo::DeviceService &deviceService;
|
||||
QSharedPointer<QXmpp::Omemo::Database> database;
|
||||
};
|
||||
|
||||
} // namespace Signal::Store
|
||||
|
@ -4,29 +4,8 @@
|
||||
|
||||
#include "pre_key_store.h"
|
||||
|
||||
void Signal::Store::PreKeyStore::boundToContext(
|
||||
signal_protocol_store_context *ctx) {
|
||||
signal_protocol_pre_key_store store{};
|
||||
|
||||
store.destroy_func = nullptr;
|
||||
store.user_data = nullptr;
|
||||
|
||||
store.contains_pre_key = [](uint32_t id, void *ptr) {
|
||||
return static_cast<PreKeyStore *>(ptr)->containsPreKey(id);
|
||||
};
|
||||
store.load_pre_key = [](signal_buffer **record, uint32_t id, void *ptr) {
|
||||
return static_cast<PreKeyStore *>(ptr)->loadPreKey(record, id);
|
||||
};
|
||||
store.remove_pre_key = [](uint32_t id, void *ptr) {
|
||||
return static_cast<PreKeyStore *>(ptr)->removePreKey(id);
|
||||
};
|
||||
store.store_pre_key = [](uint32_t id, uint8_t *record, size_t size,
|
||||
void *ptr) {
|
||||
return static_cast<PreKeyStore *>(ptr)->storePreKey(id, record, size);
|
||||
};
|
||||
|
||||
signal_protocol_store_context_set_pre_key_store(ctx, &store);
|
||||
}
|
||||
Signal::Store::PreKeyStore::PreKeyStore(QSharedPointer<QXmpp::Omemo::Database> database) : database(
|
||||
std::move(database)) {}
|
||||
|
||||
int Signal::Store::PreKeyStore::containsPreKey(uint32_t pre_key_id) {
|
||||
return 0;
|
||||
@ -41,3 +20,19 @@ int Signal::Store::PreKeyStore::storePreKey(uint32_t pre_key_id, uint8_t *record
|
||||
}
|
||||
|
||||
int Signal::Store::PreKeyStore::removePreKey(uint32_t pre_key_id) { return 0; }
|
||||
|
||||
void Signal::Store::PreKeyStore::fillCallbacks(signal_protocol_pre_key_store &store) {
|
||||
store.contains_pre_key = [](uint32_t id, void *ptr) {
|
||||
return static_cast<PreKeyStore *>(ptr)->containsPreKey(id);
|
||||
};
|
||||
store.load_pre_key = [](signal_buffer **record, uint32_t id, void *ptr) {
|
||||
return static_cast<PreKeyStore *>(ptr)->loadPreKey(record, id);
|
||||
};
|
||||
store.remove_pre_key = [](uint32_t id, void *ptr) {
|
||||
return static_cast<PreKeyStore *>(ptr)->removePreKey(id);
|
||||
};
|
||||
store.store_pre_key = [](uint32_t id, uint8_t *record, size_t size,
|
||||
void *ptr) {
|
||||
return static_cast<PreKeyStore *>(ptr)->storePreKey(id, record, size);
|
||||
};
|
||||
}
|
||||
|
@ -6,16 +6,23 @@
|
||||
|
||||
#include <signal/signal_protocol.h>
|
||||
|
||||
#include "qomemo/device_service.h"
|
||||
|
||||
namespace Signal::Store {
|
||||
|
||||
class PreKeyStore {
|
||||
public:
|
||||
static void boundToContext(signal_protocol_store_context *ctx);
|
||||
explicit PreKeyStore(QSharedPointer<QXmpp::Omemo::Database> database);
|
||||
|
||||
int containsPreKey(uint32_t pre_key_id);
|
||||
int loadPreKey(signal_buffer **record, uint32_t pre_key_id);
|
||||
int storePreKey(uint32_t pre_key_id, uint8_t *record, size_t record_len);
|
||||
int removePreKey(uint32_t pre_key_id);
|
||||
|
||||
void fillCallbacks(signal_protocol_pre_key_store &store);
|
||||
|
||||
private:
|
||||
QSharedPointer<QXmpp::Omemo::Database> database;
|
||||
};
|
||||
|
||||
} // namespace Signal::Store
|
||||
|
14
qomemo/signal/util.cpp
Normal file
14
qomemo/signal/util.cpp
Normal file
@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Created by victoria on 2021-05-15.
|
||||
*/
|
||||
|
||||
#include "util.h"
|
||||
|
||||
QString Signal::Util::jidFromAddress(const signal_protocol_address *address) {
|
||||
// TODO: Validate this
|
||||
return QString::fromRawData(reinterpret_cast<const QChar *>(address->name), static_cast<int>(address->name_len));
|
||||
}
|
||||
|
||||
QByteArray Signal::Util::byteArray(const uint8_t *data, size_t len) {
|
||||
return QByteArray::fromRawData(reinterpret_cast<const char *>(data), static_cast<int>(len));
|
||||
}
|
18
qomemo/signal/util.h
Normal file
18
qomemo/signal/util.h
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Created by victoria on 2021-05-15.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <signal/signal_protocol.h>
|
||||
|
||||
#include <QString>
|
||||
#include <QByteArray>
|
||||
|
||||
namespace Signal::Util {
|
||||
|
||||
QString jidFromAddress(const signal_protocol_address *address);
|
||||
|
||||
QByteArray byteArray(const uint8_t *data, size_t len);
|
||||
|
||||
}
|
@ -4,10 +4,13 @@
|
||||
|
||||
#include "conversations.h"
|
||||
|
||||
#include "qomemo/bundle.h"
|
||||
#include "qomemo/device.h"
|
||||
#include "shared/qxmppfactories.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QXmppIq.h>
|
||||
#include <QXmppPubSubIq.h>
|
||||
|
||||
using namespace QXmpp::Omemo;
|
||||
using namespace QXmpp::Factories;
|
||||
@ -41,13 +44,21 @@ Variant::Conversations::deviceListToXml(const DeviceList &deviceList) {
|
||||
return element;
|
||||
}
|
||||
|
||||
DeviceList Variant::Conversations::deviceListFromXml(const QXmppElement &xml) {
|
||||
std::optional<DeviceList> Variant::Conversations::latestDeviceListFromPubSubNode(const QXmppElement &items) {
|
||||
auto item = items.firstChildElement("item");
|
||||
if (item.isNull())
|
||||
return std::nullopt;
|
||||
|
||||
auto list = item.firstChildElement("list");
|
||||
if (list.isNull())
|
||||
return std::nullopt;
|
||||
|
||||
if (!elementMatches(list, "list", "eu.siacs.conversations.axolotl"))
|
||||
return std::nullopt;
|
||||
|
||||
DeviceList result{};
|
||||
|
||||
if (!elementMatches(xml, "list", "eu.siacs.conversations.axolotl"))
|
||||
return result;
|
||||
|
||||
auto deviceElement = xml.firstChildElement("device");
|
||||
auto deviceElement = list.firstChildElement("device");
|
||||
while (!deviceElement.isNull()) {
|
||||
result.devices.push_back(deviceFromXml(deviceElement));
|
||||
deviceElement = deviceElement.nextSiblingElement("device");
|
||||
@ -65,7 +76,34 @@ QXmppIq Variant::Conversations::deviceListSetIq(const DeviceList &deviceList) {
|
||||
item.appendChild(deviceListToXml(deviceList));
|
||||
|
||||
auto publish = createElement("publish");
|
||||
publish.setAttribute("node", "eu.siacs.conversations.axolotl.devicelist");
|
||||
publish.setAttribute("node", getDeviceListNode());
|
||||
publish.appendChild(item);
|
||||
|
||||
auto pubSub = createElement("pubsub", "http://jabber.org/protocol/pubsub");
|
||||
pubSub.appendChild(publish);
|
||||
pubSub.appendChild(createOpenPublishOptions());
|
||||
|
||||
iq.setExtensions({ pubSub });
|
||||
|
||||
return iq;
|
||||
}
|
||||
|
||||
QString Variant::Conversations::getDeviceListNode() const {
|
||||
return "eu.siacs.conversations.axolotl.devicelist";
|
||||
}
|
||||
|
||||
QXmppIq Variant::Conversations::bundleSetIq(int deviceId, const Bundle &bundle) {
|
||||
QXmppIq iq{};
|
||||
|
||||
iq.setType(QXmppIq::Set);
|
||||
|
||||
auto item = createElement("item");
|
||||
item.appendChild(bundleToXml(bundle));
|
||||
|
||||
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");
|
||||
@ -76,3 +114,92 @@ QXmppIq Variant::Conversations::deviceListSetIq(const DeviceList &deviceList) {
|
||||
|
||||
return iq;
|
||||
}
|
||||
|
||||
QXmppElement Variant::Conversations::bundleToXml(const Bundle &bundle) {
|
||||
auto spkNode = createElement("signedPreKeyPublic");
|
||||
spkNode.setAttribute("signedPreKeyId", QString::number(bundle.spkId));
|
||||
spkNode.setValue(bundle.spk.toBase64());
|
||||
|
||||
auto spksNode = createElement("signedPreKeySignature");
|
||||
spksNode.setValue(bundle.spks.toBase64());
|
||||
|
||||
auto ikNode = createElement("identityKey");
|
||||
ikNode.setValue(bundle.ik.toBase64());
|
||||
|
||||
auto prekeysNode = createElement("prekeys");
|
||||
for (const auto &pk : bundle.prekeys) {
|
||||
prekeysNode.appendChild(preKeyToXml(pk));
|
||||
}
|
||||
|
||||
auto result = createElement("bundle", "eu.siacs.conversations.axolotl");
|
||||
result.appendChild(spkNode);
|
||||
result.appendChild(spksNode);
|
||||
result.appendChild(ikNode);
|
||||
result.appendChild(prekeysNode);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QXmppElement Variant::Conversations::preKeyToXml(const PreKey &pk) {
|
||||
auto x = createElement("preKeyPublic");
|
||||
x.setAttribute("preKeyId", QString::number(pk.id));
|
||||
x.setValue(pk.data.toBase64());
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
std::optional<PreKey> Variant::Conversations::preKeyFromXml(const QXmppElement &xml) {
|
||||
if (!elementMatches(xml, "preKeyPublic"))
|
||||
return std::nullopt;
|
||||
|
||||
auto pk = PreKey();
|
||||
pk.id = xml.attribute("preKeyId").toInt();
|
||||
pk.data = QByteArray::fromBase64Encoding(xml.value().toUtf8()).decoded;
|
||||
|
||||
return pk;
|
||||
}
|
||||
|
||||
std::optional<Bundle> Variant::Conversations::bundleFromXml(const QXmppElement &xml) {
|
||||
if (!elementMatches(xml, "bundle", "eu.siacs.conversations.axolotl"))
|
||||
return std::nullopt;
|
||||
|
||||
auto spkNode = xml.firstChildElement("signedPreKeyPublic");
|
||||
if (spkNode.isNull()) {
|
||||
qWarning() << "'bundle': missing 'signedPreKeyPublic'";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Bundle result{};
|
||||
|
||||
result.spk = QByteArray::fromBase64Encoding(spkNode.value().toUtf8()).decoded;
|
||||
result.spkId = spkNode.attribute("signedPreKeyId").toInt();
|
||||
|
||||
auto spksNode = xml.firstChildElement("signedPreKeySignature");
|
||||
if (spksNode.isNull()) {
|
||||
qWarning() << "'bundle': missing 'signedPreKeySignature'";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
result.spks = QByteArray::fromBase64Encoding(spksNode.value().toUtf8()).decoded;
|
||||
|
||||
auto ikNode = xml.firstChildElement("identityKey");
|
||||
if (ikNode.isNull()) {
|
||||
qWarning() << "'bundle': missing 'identityKey'";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
result.ik = QByteArray::fromBase64Encoding(ikNode.value().toUtf8()).decoded;
|
||||
|
||||
auto prekeysNode = xml.firstChildElement("prekeys");
|
||||
auto pkNode = prekeysNode.firstChildElement("preKeyPublic");
|
||||
|
||||
result.prekeys.clear();
|
||||
while (!pkNode.isNull()) {
|
||||
auto maybePk = preKeyFromXml(pkNode);
|
||||
if (maybePk)
|
||||
result.prekeys.push_back(*maybePk);
|
||||
pkNode = pkNode.nextSiblingElement("preKeyPublic");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -15,9 +15,18 @@ namespace QXmpp::Omemo::Variant {
|
||||
QXmppElement deviceToXml(const Device &device) override;
|
||||
Device deviceFromXml(const QXmppElement &xml) override;
|
||||
|
||||
[[nodiscard]] QString getDeviceListNode() const override;
|
||||
QXmppElement deviceListToXml(const DeviceList &deviceList) override;
|
||||
DeviceList deviceListFromXml(const QXmppElement &xml) override;
|
||||
std::optional<DeviceList> latestDeviceListFromPubSubNode(const QXmppElement &items) override;
|
||||
QXmppIq deviceListSetIq(const DeviceList &deviceList) override;
|
||||
|
||||
QXmppElement preKeyToXml(const PreKey &pk) override;
|
||||
std::optional<PreKey> preKeyFromXml(const QXmppElement &xml) override;
|
||||
|
||||
QXmppElement bundleToXml(const Bundle &bundle) override;
|
||||
std::optional<Bundle> bundleFromXml(const QXmppElement &xml) override;
|
||||
|
||||
QXmppIq bundleSetIq(int deviceId, const Bundle &bundle) override;
|
||||
};
|
||||
|
||||
} // namespace QXmpp::Omemo::Variant
|
||||
|
@ -4,14 +4,17 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
class QXmppElement;
|
||||
#include <optional>
|
||||
|
||||
class QString;
|
||||
class QXmppElement;
|
||||
class QXmppIq;
|
||||
|
||||
namespace QXmpp::Omemo {
|
||||
|
||||
class PreKey;
|
||||
class Bundle;
|
||||
class Device;
|
||||
|
||||
class DeviceList;
|
||||
|
||||
namespace Variant {
|
||||
@ -23,9 +26,18 @@ namespace QXmpp::Omemo {
|
||||
virtual QXmppElement deviceToXml(const Device &device) = 0;
|
||||
virtual Device deviceFromXml(const QXmppElement &xml) = 0;
|
||||
|
||||
[[nodiscard]] virtual QString getDeviceListNode() const = 0;
|
||||
virtual QXmppElement deviceListToXml(const DeviceList &deviceList) = 0;
|
||||
virtual DeviceList deviceListFromXml(const QXmppElement &xml) = 0;
|
||||
virtual std::optional<DeviceList> latestDeviceListFromPubSubNode(const QXmppElement &xml) = 0;
|
||||
virtual QXmppIq deviceListSetIq(const DeviceList &deviceList) = 0;
|
||||
|
||||
virtual QXmppElement preKeyToXml(const PreKey &pk) = 0;
|
||||
virtual std::optional<PreKey> preKeyFromXml(const QXmppElement &xml) = 0;
|
||||
|
||||
virtual QXmppElement bundleToXml(const Bundle& bundle) = 0;
|
||||
virtual std::optional<Bundle> bundleFromXml(const QXmppElement& xml) = 0;
|
||||
|
||||
virtual QXmppIq bundleSetIq(int deviceId, const Bundle& bundle) = 0;
|
||||
};
|
||||
|
||||
} // namespace Variant
|
||||
|
@ -6,17 +6,19 @@
|
||||
#include "ui_contactsettings.h"
|
||||
#include "omemodevices.h"
|
||||
|
||||
ContactSettings::ContactSettings(QWidget *parent)
|
||||
: QDialog(parent), m_ui(new Ui::ContactSettings()) {
|
||||
ContactSettings::ContactSettings(QString jid, QWidget *parent)
|
||||
: QDialog(parent), jid(std::move(jid)), m_ui(new Ui::ContactSettings()) {
|
||||
m_ui->setupUi(this);
|
||||
|
||||
connect(m_ui->devicesButton, &QPushButton::clicked, this, &ContactSettings::openDeviceList);
|
||||
|
||||
setWindowTitle(QString("Encryption settings for %1").arg(this->jid));
|
||||
}
|
||||
|
||||
ContactSettings::~ContactSettings() {}
|
||||
|
||||
void ContactSettings::openDeviceList() {
|
||||
auto devices = new OMEMODevices(this);
|
||||
auto devices = new OMEMODevices(jid, this);
|
||||
|
||||
devices->setAttribute(Qt::WA_DeleteOnClose);
|
||||
devices->show();
|
||||
|
@ -13,9 +13,11 @@ namespace Ui {
|
||||
class ContactSettings : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ContactSettings(QWidget *parent = nullptr);
|
||||
explicit ContactSettings(QString jid, QWidget *parent = nullptr);
|
||||
~ContactSettings() override;
|
||||
|
||||
const QString jid;
|
||||
|
||||
private slots:
|
||||
void openDeviceList();
|
||||
|
||||
|
@ -5,9 +5,11 @@
|
||||
#include "omemodevices.h"
|
||||
#include "ui_omemodevices.h"
|
||||
|
||||
OMEMODevices::OMEMODevices(QWidget *parent)
|
||||
: QDialog(parent), m_ui(new Ui::OMEMODevices()) {
|
||||
OMEMODevices::OMEMODevices(QString jid, QWidget *parent)
|
||||
: QDialog(parent), jid(std::move(jid)), m_ui(new Ui::OMEMODevices()) {
|
||||
m_ui->setupUi(this);
|
||||
|
||||
setWindowTitle(QString("%1's OMEMO devices").arg(this->jid));
|
||||
}
|
||||
|
||||
OMEMODevices::~OMEMODevices() {}
|
||||
|
@ -13,9 +13,11 @@ namespace Ui {
|
||||
class OMEMODevices : public QDialog {
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit OMEMODevices(QWidget *parent = nullptr);
|
||||
explicit OMEMODevices(QString jid, QWidget *parent = nullptr);
|
||||
~OMEMODevices() override;
|
||||
|
||||
const QString jid;
|
||||
|
||||
private:
|
||||
QScopedPointer<Ui::OMEMODevices> m_ui;
|
||||
};
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "ui_squawk.h"
|
||||
#include <QDebug>
|
||||
#include <QIcon>
|
||||
#include <utility>
|
||||
#include <ui/omemo/omemodevices.h>
|
||||
|
||||
Squawk::Squawk(QWidget *parent) :
|
||||
@ -61,7 +62,6 @@ Squawk::Squawk(QWidget *parent) :
|
||||
connect(m_ui->roster, &QTreeView::collapsed, this, &Squawk::onItemCollepsed);
|
||||
connect(m_ui->roster->selectionModel(), &QItemSelectionModel::currentRowChanged, this, &Squawk::onRosterSelectionChanged);
|
||||
connect(&rosterModel, &Models::Roster::unnoticedMessage, this, &Squawk::onUnnoticedMessage);
|
||||
connect(m_ui->actionDeviceList, &QAction::triggered, this, &Squawk::onOMEMODevices);
|
||||
|
||||
connect(rosterModel.accountsModel, &Models::Accounts::sizeChanged, this, &Squawk::onAccountsSizeChanged);
|
||||
connect(&rosterModel, &Models::Roster::requestArchive, this, &Squawk::onRequestArchive);
|
||||
@ -136,8 +136,8 @@ void Squawk::onNewContact()
|
||||
nc->exec();
|
||||
}
|
||||
|
||||
void Squawk::onOMEMODevices() {
|
||||
auto od = new OMEMODevices(this);
|
||||
void Squawk::openDeviceList(QString bareJid) {
|
||||
auto od = new OMEMODevices(std::move(bareJid), this);
|
||||
|
||||
od->setAttribute(Qt::WA_DeleteOnClose);
|
||||
od->show();
|
||||
@ -540,6 +540,10 @@ void Squawk::onRosterContextMenu(const QPoint& point)
|
||||
});
|
||||
}
|
||||
|
||||
QAction* devices = contextMenu->addAction(Shared::icon("security-high"), tr("Devices"));
|
||||
devices->setEnabled(active);
|
||||
connect(devices, &QAction::triggered, [this, acc]() { openDeviceList(acc->getBareJid()); });
|
||||
|
||||
QAction* card = contextMenu->addAction(Shared::icon("user-properties"), tr("VCard"));
|
||||
card->setEnabled(active);
|
||||
connect(card, &QAction::triggered, std::bind(&Squawk::onActivateVCard, this, name, acc->getBareJid(), true));
|
||||
|
@ -155,7 +155,7 @@ private slots:
|
||||
void onPasswordPromptRejected();
|
||||
void onRosterSelectionChanged(const QModelIndex& current, const QModelIndex& previous);
|
||||
void onContextAboutToHide();
|
||||
void onOMEMODevices();
|
||||
void openDeviceList(QString bareJid);
|
||||
|
||||
void onUnnoticedMessage(const QString& account, const Shared::Message& msg);
|
||||
|
||||
|
13
ui/squawk.ui
13
ui/squawk.ui
@ -169,17 +169,7 @@
|
||||
<property name="title">
|
||||
<string>Settings</string>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuOMEMO">
|
||||
<property name="title">
|
||||
<string>OMEMO</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="security-high"/>
|
||||
</property>
|
||||
<addaction name="actionDeviceList"/>
|
||||
</widget>
|
||||
<addaction name="actionAccounts"/>
|
||||
<addaction name="menuOMEMO"/>
|
||||
</widget>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
<property name="title">
|
||||
@ -236,7 +226,8 @@
|
||||
</action>
|
||||
<action name="actionDeviceList">
|
||||
<property name="icon">
|
||||
<iconset theme="computer-laptop"/>
|
||||
<iconset theme="computer-laptop">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Device list</string>
|
||||
|
@ -437,7 +437,7 @@ void Conversation::onFeedContext(const QPoint& pos)
|
||||
}
|
||||
|
||||
void Conversation::openEncryptionSettings() {
|
||||
auto cs = new ContactSettings(this);
|
||||
auto cs = new ContactSettings(palJid, this);
|
||||
|
||||
cs->setAttribute(Qt::WA_DeleteOnClose);
|
||||
cs->show();
|
||||
|
Loading…
Reference in New Issue
Block a user