/* * Created by victoria on 2021-05-12. */ #include "qxmpp_omemo_manager.h" #include "qomemo/signal/context.h" #include "bundle.h" #include "device.h" #include "variant/conversations.h" #include #include #include #include #include #include using namespace QXmpp::Omemo; Manager::Manager() : deviceService{new DeviceService(this)}, omemoVariant(new Variant::Conversations), signalContext(new Signal::Context) { 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) { QString str{}; QTextStream info(&str); stanza.save(info, 4); std::cout << str.toStdString(); if (handleDeviceList(stanza) || handleMissingDeviceList(stanza) || handleEncryptedMessage(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()) return false; auto items = pubsub.firstChildElement("items"); 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; } bool Manager::handleEncryptedMessage(const QDomElement &stanza) { if (stanza.tagName() != "message") return false; auto encrypted = stanza.firstChildElement("encrypted"); if (encrypted.isNull()) return false; qDebug() << "!!!! Got encrypted message!!"; return true; } void QXmpp::Omemo::Manager::setClient(QXmppClient *client) { QXmppClientExtension::setClient(client); if (!client) return; QObject::connect(client, &QXmppClient::connected, this, &Manager::fetchOwnDevices); } void QXmpp::Omemo::Manager::fetchOwnDevices() { QXmppPubSubIq iq{}; iq.setFrom(client()->configuration().jid()); iq.setTo(client()->configuration().jidBare()); iq.setType(QXmppIq::Get); iq.setQueryNode(omemoVariant->getDeviceListNode()); client()->sendPacket(iq); } QSharedPointer 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::system()->bounded(INT32_MAX); db->saveActiveDeviceId(deviceId); Device device{}; device.id = deviceId; auto updatedList = currentList; updatedList.devices.push_back(device); publishDeviceList(updatedList); auto bundle = generateAndSaveBundle(deviceId); 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); result.ik = identityKey; // 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 = 1; 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)); result.spk = spk.key.publicKey; result.spks = spk.signature; result.spkId = 1; // 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); PreKey pk{}; pk.data = preKey.publicKey; pk.id = i; result.prekeys.append(pk); 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); return result; }