diff options
| author | Melvin Keskin <melvo@olomono.de> | 2021-09-02 23:41:16 +0200 |
|---|---|---|
| committer | Linus Jahn <lnj@kaidan.im> | 2021-09-03 16:16:15 +0200 |
| commit | 0623aa38f2ead734dddea4cbad899a868f01cb1e (patch) | |
| tree | ce6539732310386d2ba0faf7383c054a4690f3a2 | |
| parent | 12c63eddded9f02369bd4e569f62acfdd95cc723 (diff) | |
| download | qxmpp-0623aa38f2ead734dddea4cbad899a868f01cb1e.tar.gz | |
Add QXmppOmemoElement
Co-authored-by: Germán Márquez Mejía <mancho@olomono.de>
| -rw-r--r-- | src/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/base/QXmppMessage.cpp | 34 | ||||
| -rw-r--r-- | src/base/QXmppMessage.h | 5 | ||||
| -rw-r--r-- | src/base/QXmppOmemoData.cpp | 187 | ||||
| -rw-r--r-- | src/base/QXmppOmemoElement.h | 70 | ||||
| -rw-r--r-- | tests/qxmppmessage/tst_qxmppmessage.cpp | 84 | ||||
| -rw-r--r-- | tests/qxmppomemodata/tst_qxmppomemodata.cpp | 171 |
7 files changed, 552 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e166a8cb..b21e198a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -49,6 +49,7 @@ set(INSTALL_HEADER_FILES base/QXmppOmemoDeviceBundle.h base/QXmppOmemoDeviceList.h base/QXmppOmemoDeviceElement.h + base/QXmppOmemoElement.h base/QXmppOmemoEnvelope.h base/QXmppPingIq.h base/QXmppPresence.h diff --git a/src/base/QXmppMessage.cpp b/src/base/QXmppMessage.cpp index f0e74735..f975ae3c 100644 --- a/src/base/QXmppMessage.cpp +++ b/src/base/QXmppMessage.cpp @@ -28,6 +28,7 @@ #include "QXmppBitsOfBinaryDataList.h" #include "QXmppConstants_p.h" #include "QXmppMixInvitation.h" +#include "QXmppOmemoElement.h" #include "QXmppTrustMessageElement.h" #include "QXmppUtils.h" @@ -171,6 +172,9 @@ public: bool isSpoiler; QString spoilerHint; + // XEP-0384: OMEMO Encryption + std::optional<QXmppOmemoElement> omemoElement; + // XEP-0407: Mediated Information eXchange (MIX): Miscellaneous Capabilities std::optional<QXmppMixInvitation> mixInvitation; @@ -1084,6 +1088,26 @@ void QXmppMessage::setSpoilerHint(const QString &spoilerHint) } /// +/// Returns an included OMEMO element as defined by \xep{0384, OMEMO Encryption}. +/// +/// \since QXmpp 1.5 +/// +std::optional<QXmppOmemoElement> QXmppMessage::omemoElement() const +{ + return d->omemoElement; +} + +/// +/// Sets an OMEMO element as defined by \xep{0384, OMEMO Encryption}. +/// +/// \since QXmpp 1.5 +/// +void QXmppMessage::setOmemoElement(const std::optional<QXmppOmemoElement> &omemoElement) +{ + d->omemoElement = omemoElement; +} + +/// /// Returns an included \xep{0369}: Mediated Information eXchange (MIX) /// invitation as defined by \xep{0407}: Mediated Information eXchange (MIX): /// Miscellaneous Capabilities. @@ -1345,6 +1369,11 @@ bool QXmppMessage::parseExtension(const QDomElement &element) // XEP-0382: Spoiler messages d->isSpoiler = true; d->spoilerHint = element.text(); + } else if (QXmppOmemoElement::isOmemoElement(element)) { + // XEP-0384: OMEMO Encryption + QXmppOmemoElement omemoElement; + omemoElement.parse(element); + d->omemoElement = omemoElement; } else if (checkElement(element, QStringLiteral("invitation"), ns_mix_misc)) { // XEP-0407: Mediated Information eXchange (MIX): Miscellaneous Capabilities QXmppMixInvitation mixInvitation; @@ -1543,6 +1572,11 @@ void QXmppMessage::serializeExtensions(QXmlStreamWriter *xmlWriter) const xmlWriter->writeEndElement(); } + // XEP-0384: OMEMO Encryption + if (d->omemoElement) { + d->omemoElement->toXml(xmlWriter); + } + // XEP-0407: Mediated Information eXchange (MIX): Miscellaneous Capabilities if (d->mixInvitation) { d->mixInvitation->toXml(xmlWriter); diff --git a/src/base/QXmppMessage.h b/src/base/QXmppMessage.h index edd0c5c1..6688d3b6 100644 --- a/src/base/QXmppMessage.h +++ b/src/base/QXmppMessage.h @@ -36,6 +36,7 @@ class QXmppMessagePrivate; class QXmppBitsOfBinaryDataList; class QXmppMixInvitation; +class QXmppOmemoElement; class QXmppTrustMessageElement; /// @@ -247,6 +248,10 @@ public: QString spoilerHint() const; void setSpoilerHint(const QString &); + // XEP-0384: OMEMO Encryption + std::optional<QXmppOmemoElement> omemoElement() const; + void setOmemoElement(const std::optional<QXmppOmemoElement> &omemoElement); + // XEP-0407: Mediated Information eXchange (MIX): Miscellaneous Capabilities std::optional<QXmppMixInvitation> mixInvitation() const; void setMixInvitation(const std::optional<QXmppMixInvitation> &mixInvitation); diff --git a/src/base/QXmppOmemoData.cpp b/src/base/QXmppOmemoData.cpp index 6fb2580d..fbcf8261 100644 --- a/src/base/QXmppOmemoData.cpp +++ b/src/base/QXmppOmemoData.cpp @@ -26,6 +26,7 @@ #include "QXmppOmemoDeviceBundle.h" #include "QXmppOmemoDeviceElement.h" #include "QXmppOmemoDeviceList.h" +#include "QXmppOmemoElement.h" #include "QXmppOmemoEnvelope.h" #include "QXmppUtils.h" @@ -618,3 +619,189 @@ bool QXmppOmemoEnvelope::isOmemoEnvelope(const QDomElement &element) return element.tagName() == QStringLiteral("key") && element.namespaceURI() == ns_omemo_1; } + +/// +/// \class QXmppOmemoElement +/// +/// \brief The QXmppOmemoElement class represents an OMEMO element as +/// defined by \xep{0384, OMEMO Encryption}. +/// +/// \since QXmpp 1.5 +/// + +class QXmppOmemoElementPrivate : public QSharedData +{ +public: + uint32_t senderDeviceId = 0; + QByteArray payload; + QMultiMap<QString, QXmppOmemoEnvelope> envelopes; +}; + +/// +/// Constructs an OMEMO element. +/// +QXmppOmemoElement::QXmppOmemoElement() + : d(new QXmppOmemoElementPrivate) +{ +} + +/// +/// Constructs a copy of \a other. +/// +/// \param other +/// +QXmppOmemoElement::QXmppOmemoElement(const QXmppOmemoElement &other) = default; + +QXmppOmemoElement::~QXmppOmemoElement() = default; + +/// +/// Assigns \a other to this OMEMO element. +/// +/// \param other +/// +QXmppOmemoElement &QXmppOmemoElement::operator=(const QXmppOmemoElement &other) = default; + +/// +/// Returns the ID of the sender's device. +/// +/// The ID is 0 if it is unset. +/// +/// \return the sender's device ID +/// +uint32_t QXmppOmemoElement::senderDeviceId() const +{ + return d->senderDeviceId; +} + +/// +/// Sets the ID of the sender's device. +/// +/// A valid ID must be at least 1 and at most 2^32-1. +/// +/// \param id sender's device ID +/// +void QXmppOmemoElement::setSenderDeviceId(const uint32_t id) +{ + d->senderDeviceId = id; +} + +/// +/// Returns the payload which consists of the encrypted SCE envelope. +/// +/// \return the encrypted payload +/// +QByteArray QXmppOmemoElement::payload() const +{ + return d->payload; +} + +/// +/// Sets the payload which consists of the encrypted SCE envelope. +/// +/// \param payload encrypted payload +/// +void QXmppOmemoElement::setPayload(const QByteArray &payload) +{ + d->payload = payload; +} + +/// +/// Searches for an OMEMO envelope by its recipient JID and device ID. +/// +/// \param recipientJid bare JID of the recipient +/// \param recipientDeviceId ID of the recipient's device +/// +/// \return the found OMEMO envelope +/// +std::optional<QXmppOmemoEnvelope> QXmppOmemoElement::searchEnvelope(const QString &recipientJid, uint32_t recipientDeviceId) const +{ + for (auto itr = d->envelopes.constFind(recipientJid); itr != d->envelopes.constEnd() && itr.key() == recipientJid; ++itr) { + const auto &envelope = itr.value(); + if (envelope.recipientDeviceId() == recipientDeviceId) { + return envelope; + } + } + + return std::nullopt; +} + +/// +/// Adds an OMEMO envelope. +/// +/// If a full JID is passed as recipientJid, it is converted into a bare JID. +/// +/// \see QXmppOmemoEnvelope +/// +/// \param recipientJid bare JID of the recipient +/// \param envelope OMEMO envelope +/// +void QXmppOmemoElement::addEnvelope(const QString &recipientJid, QXmppOmemoEnvelope &envelope) +{ + d->envelopes.insert(QXmppUtils::jidToBareJid(recipientJid), envelope); +} + +/// \cond +void QXmppOmemoElement::parse(const QDomElement &element) +{ + const auto header = element.firstChildElement("header"); + + d->senderDeviceId = header.attribute("sid").toInt(); + + for (auto recipient = header.firstChildElement("keys"); + !recipient.isNull(); + recipient = recipient.nextSiblingElement("keys")) { + const auto recipientJid = recipient.attribute("jid"); + + for (auto envelope = recipient.firstChildElement("key"); + !envelope.isNull(); + envelope = envelope.nextSiblingElement("key")) { + QXmppOmemoEnvelope omemoEnvelope; + omemoEnvelope.parse(envelope); + addEnvelope(recipientJid, omemoEnvelope); + } + } + + d->payload = QByteArray::fromBase64(element.firstChildElement("payload").text().toLatin1()); +} + +void QXmppOmemoElement::toXml(QXmlStreamWriter *writer) const +{ + writer->writeStartElement("encrypted"); + writer->writeAttribute("xmlns", ns_omemo_1); + + writer->writeStartElement("header"); + writer->writeAttribute("sid", QString::number(d->senderDeviceId)); + + const auto recipientJids = d->envelopes.uniqueKeys(); + for (const auto &recipientJid : recipientJids) { + writer->writeStartElement("keys"); + writer->writeAttribute("jid", recipientJid); + + for (auto itr = d->envelopes.constFind(recipientJid); itr != d->envelopes.constEnd() && itr.key() == recipientJid; ++itr) { + const auto &envelope = itr.value(); + envelope.toXml(writer); + } + + writer->writeEndElement(); // keys + } + + writer->writeEndElement(); // header + + helperToXmlAddTextElement(writer, "payload", d->payload.toBase64()); + + writer->writeEndElement(); // encrypted +} +/// \endcond + +/// +/// Determines whether the given DOM element is an OMEMO element. +/// +/// \param element DOM element being checked +/// +/// \return true if element is an OMEMO element, otherwise false +/// +bool QXmppOmemoElement::isOmemoElement(const QDomElement &element) +{ + return element.tagName() == QStringLiteral("encrypted") && + element.namespaceURI() == ns_omemo_1; +} diff --git a/src/base/QXmppOmemoElement.h b/src/base/QXmppOmemoElement.h new file mode 100644 index 00000000..684ed1fa --- /dev/null +++ b/src/base/QXmppOmemoElement.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2008-2021 The QXmpp developers + * + * Author: + * Germán Márquez Mejía + * Melvin Keskin + * + * Source: + * https://github.com/qxmpp-project/qxmpp + * + * This file is a part of QXmpp library. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + */ + +#ifndef QXMPPOMEMOELEMENT_H +#define QXMPPOMEMOELEMENT_H + +#include "QXmppGlobal.h" + +#include <optional> + +#include <QSharedDataPointer> + +class QDomElement; +class QXmppOmemoElementPrivate; +class QXmppOmemoEnvelope; +class QXmlStreamWriter; + +class QXMPP_EXPORT QXmppOmemoElement +{ +public: + QXmppOmemoElement(); + QXmppOmemoElement(const QXmppOmemoElement &other); + ~QXmppOmemoElement(); + + QXmppOmemoElement &operator=(const QXmppOmemoElement &other); + + uint32_t senderDeviceId() const; + void setSenderDeviceId(uint32_t id); + + QByteArray payload() const; + void setPayload(const QByteArray &payload); + + std::optional<QXmppOmemoEnvelope> searchEnvelope(const QString &recipientJid, uint32_t recipientDeviceId) const; + void addEnvelope(const QString &recipientJid, QXmppOmemoEnvelope &envelope); + + /// \cond + void parse(const QDomElement &element); + void toXml(QXmlStreamWriter *writer) const; + /// \endcond + + static bool isOmemoElement(const QDomElement &element); + +private: + QSharedDataPointer<QXmppOmemoElementPrivate> d; +}; + +Q_DECLARE_TYPEINFO(QXmppOmemoElement, Q_MOVABLE_TYPE); + +#endif // QXMPPOMEMOELEMENT_H diff --git a/tests/qxmppmessage/tst_qxmppmessage.cpp b/tests/qxmppmessage/tst_qxmppmessage.cpp index 3b9f4208..fe3340d8 100644 --- a/tests/qxmppmessage/tst_qxmppmessage.cpp +++ b/tests/qxmppmessage/tst_qxmppmessage.cpp @@ -4,6 +4,8 @@ * Authors: * Jeremy Lainé * Manjeet Dahiya + * Germán Márquez Mejía + * Melvin Keskin * * Source: * https://github.com/qxmpp-project/qxmpp @@ -26,6 +28,7 @@ #include "QXmppBitsOfBinaryDataList.h" #include "QXmppMessage.h" #include "QXmppMixInvitation.h" +#include "QXmppOmemoElement.h" #include "QXmppTrustMessageElement.h" #include <optional> @@ -69,6 +72,7 @@ private slots: void testSlashMe(); void testMixInvitation(); void testTrustMessageElement(); + void testOmemoElement(); }; void tst_QXmppMessage::testBasic_data() @@ -1131,5 +1135,85 @@ void tst_QXmppMessage::testTrustMessageElement() QVERIFY(message2.trustMessageElement()); } +void tst_QXmppMessage::testOmemoElement() +{ + const QByteArray xmlIn(QByteArrayLiteral( + "<message id=\"send1\" to=\"juliet@capulet.lit\" from=\"romeo@montague.lit\" type=\"normal\">" + "<store xmlns=\"urn:xmpp:hints\"/>" + "<encrypted xmlns=\"urn:xmpp:omemo:1\">" + "<header sid=\"27183\">" + "<keys jid=\"juliet@capulet.lit\">" + "<key rid=\"31415\">Oy5TSG9vVVV4Wz9wUkUvI1lUXiVLIU5bbGIsUV0wRngK</key>" + "</keys>" + "<keys jid=\"romeo@montague.lit\">" + "<key rid=\"1337\">PTEoSk91VnRZSXBzcFlPXy4jZ3NKcGVZZ2d3YVJbVj8K</key>" + "<key rid=\"12321\" kex=\"true\">a012U0R9WixWKUYhYipucnZOWG06akFOR3Q1NGNOOmUK</key>" + "</keys>" + "</header>" + "<payload>" + "Vk9NPi99bHFWKmErOUVTTkAwW1VcZjJvPlElZWUoOk90Kz03YUF7OHc/WjpaQz9ieFdsZjBsSH1w" + "R1d2Zzt1bEFAMSZqP0dVJj9oaygmcWRPKGU3Kjc8aV4sJSlpSXBqaENCT2NUVFFmaFNXbCxQaHsj" + "OnthQDJyUW9qNjwoZCtpLzpzLGpbKlJRY1NtMVVeRzdsOWRQciNnXV9tajEyWztnKiEhRHs5K2hX" + "ZFloaEZtUENTQWIxM0tcVkxIVWY+aGYoeEk/SldZcyNlTzk2Q2NHW1NqWEhEPmhPXl1WZV5xNE9p" + "WDZuck8zPGE2Rk4vKWJXd3F1YV0mSXA/NVNGNEQsK18mTlJNbl9WcGJXcVE5e1E0dlFAPVQ8THM+" + "QjdcdjZSNDVJclo0QVo6cDBMQDtVcUFnNDpcd1ZXSkcsXz82QjhXLl9NSVBFdipeOmF4NC5YKnNx" + "K2dxMGx1MDkrdnJhWTovUjk1ZCZUUSNTKHIvJUgmTyE4bjJbZlZAPl9IZi8ucSM7a2FAQWUzXUJO" + "LmpALilFWGRqYlh1Siw2MzJqbipsWlZRMG91MGVQVlExLCFeayMuM3dfSn1ONiU8LixZWSx3YUlV" + "bGtIcnVWP2Y0LGwvTzFIQy8qZVVBSVZLS1peSW0xNTRPcXRDIXBkXnhmWyNxQFxHQ19cYXVAO214" + "RWw1P0AmIUAlQjk7ZFBWXW1RbWxoTFE+cUxMbk5UCg==" + "</payload>" + "</encrypted>" + "</message>")); + + // An OMEMO element having its OMEMO envelopes sorted in reverse order is + // needed since they are serialized in the reverse order in which they are + // deserialized. + const QByteArray xmlOut1(QByteArrayLiteral( + "<message id=\"send1\" to=\"juliet@capulet.lit\" from=\"romeo@montague.lit\" type=\"normal\">" + "<store xmlns=\"urn:xmpp:hints\"/>" + "<encrypted xmlns=\"urn:xmpp:omemo:1\">" + "<header sid=\"27183\">" + "<keys jid=\"juliet@capulet.lit\">" + "<key rid=\"31415\">Oy5TSG9vVVV4Wz9wUkUvI1lUXiVLIU5bbGIsUV0wRngK</key>" + "</keys>" + "<keys jid=\"romeo@montague.lit\">" + "<key rid=\"12321\" kex=\"true\">a012U0R9WixWKUYhYipucnZOWG06akFOR3Q1NGNOOmUK</key>" + "<key rid=\"1337\">PTEoSk91VnRZSXBzcFlPXy4jZ3NKcGVZZ2d3YVJbVj8K</key>" + "</keys>" + "</header>" + "<payload>" + "Vk9NPi99bHFWKmErOUVTTkAwW1VcZjJvPlElZWUoOk90Kz03YUF7OHc/WjpaQz9ieFdsZjBsSH1w" + "R1d2Zzt1bEFAMSZqP0dVJj9oaygmcWRPKGU3Kjc8aV4sJSlpSXBqaENCT2NUVFFmaFNXbCxQaHsj" + "OnthQDJyUW9qNjwoZCtpLzpzLGpbKlJRY1NtMVVeRzdsOWRQciNnXV9tajEyWztnKiEhRHs5K2hX" + "ZFloaEZtUENTQWIxM0tcVkxIVWY+aGYoeEk/SldZcyNlTzk2Q2NHW1NqWEhEPmhPXl1WZV5xNE9p" + "WDZuck8zPGE2Rk4vKWJXd3F1YV0mSXA/NVNGNEQsK18mTlJNbl9WcGJXcVE5e1E0dlFAPVQ8THM+" + "QjdcdjZSNDVJclo0QVo6cDBMQDtVcUFnNDpcd1ZXSkcsXz82QjhXLl9NSVBFdipeOmF4NC5YKnNx" + "K2dxMGx1MDkrdnJhWTovUjk1ZCZUUSNTKHIvJUgmTyE4bjJbZlZAPl9IZi8ucSM7a2FAQWUzXUJO" + "LmpALilFWGRqYlh1Siw2MzJqbipsWlZRMG91MGVQVlExLCFeayMuM3dfSn1ONiU8LixZWSx3YUlV" + "bGtIcnVWP2Y0LGwvTzFIQy8qZVVBSVZLS1peSW0xNTRPcXRDIXBkXnhmWyNxQFxHQ19cYXVAO214" + "RWw1P0AmIUAlQjk7ZFBWXW1RbWxoTFE+cUxMbk5UCg==" + "</payload>" + "</encrypted>" + "</message>")); + + const QByteArray xmlOut2(QByteArrayLiteral( + "<message type=\"chat\">" + "<encrypted xmlns=\"urn:xmpp:omemo:1\">" + "<header sid=\"0\"/>" + "<payload/>" + "</encrypted>" + "</message>")); + + QXmppMessage message1; + parsePacket(message1, xmlIn); + QVERIFY(message1.omemoElement()); + serializePacket(message1, xmlOut1); + + QXmppMessage message2; + message2.setOmemoElement(QXmppOmemoElement()); + QVERIFY(message2.omemoElement()); + serializePacket(message2, xmlOut2); +} + QTEST_MAIN(tst_QXmppMessage) #include "tst_qxmppmessage.moc" diff --git a/tests/qxmppomemodata/tst_qxmppomemodata.cpp b/tests/qxmppomemodata/tst_qxmppomemodata.cpp index 8a583622..d53c7220 100644 --- a/tests/qxmppomemodata/tst_qxmppomemodata.cpp +++ b/tests/qxmppomemodata/tst_qxmppomemodata.cpp @@ -25,6 +25,7 @@ #include "QXmppOmemoDeviceBundle.h" #include "QXmppOmemoDeviceElement.h" #include "QXmppOmemoDeviceList.h" +#include "QXmppOmemoElement.h" #include "QXmppOmemoEnvelope.h" #include "util.h" @@ -49,6 +50,9 @@ private slots: void testIsOmemoEnvelope(); void testOmemoEnvelope_data(); void testOmemoEnvelope(); + void testIsOmemoElement_data(); + void testIsOmemoElement(); + void testOmemoElement(); }; void tst_QXmppOmemoData::testIsOmemoDeviceElement_data() @@ -310,5 +314,172 @@ void tst_QXmppOmemoData::testOmemoEnvelope() serializePacket(omemoEnvelope2, xml); } +void tst_QXmppOmemoData::testIsOmemoElement_data() +{ + QTest::addColumn<QByteArray>("xml"); + QTest::addColumn<bool>("isValid"); + + QTest::newRow("valid") + << QByteArrayLiteral("<encrypted xmlns=\"urn:xmpp:omemo:1\"/>") + << true; + QTest::newRow("invalidTag") + << QByteArrayLiteral("<invalid xmlns=\"urn:xmpp:omemo:1\"/>") + << false; + QTest::newRow("invalidNamespace") + << QByteArrayLiteral("<encrypted xmlns=\"invalid\"/>") + << false; +} + +void tst_QXmppOmemoData::testIsOmemoElement() +{ + QFETCH(QByteArray, xml); + QFETCH(bool, isValid); + + QDomDocument doc; + QCOMPARE(doc.setContent(xml, true), true); + const QDomElement element = doc.documentElement(); + QCOMPARE(QXmppOmemoElement::isOmemoElement(element), isValid); +} + +void tst_QXmppOmemoData::testOmemoElement() +{ + const QByteArray xmlIn(QByteArrayLiteral( + "<encrypted xmlns=\"urn:xmpp:omemo:1\">" + "<header sid=\"27183\">" + "<keys jid=\"juliet@capulet.lit\">" + "<key rid=\"31415\">Oy5TSG9vVVV4Wz9wUkUvI1lUXiVLIU5bbGIsUV0wRngK</key>" + "</keys>" + "<keys jid=\"romeo@montague.lit\">" + "<key rid=\"1337\">PTEoSk91VnRZSXBzcFlPXy4jZ3NKcGVZZ2d3YVJbVj8K</key>" + "<key rid=\"12321\" kex=\"true\">a012U0R9WixWKUYhYipucnZOWG06akFOR3Q1NGNOOmUK</key>" + "</keys>" + "</header>" + "<payload>" + "Vk9NPi99bHFWKmErOUVTTkAwW1VcZjJvPlElZWUoOk90Kz03YUF7OHc/WjpaQz9ieFdsZjBsSH1w" + "R1d2Zzt1bEFAMSZqP0dVJj9oaygmcWRPKGU3Kjc8aV4sJSlpSXBqaENCT2NUVFFmaFNXbCxQaHsj" + "OnthQDJyUW9qNjwoZCtpLzpzLGpbKlJRY1NtMVVeRzdsOWRQciNnXV9tajEyWztnKiEhRHs5K2hX" + "ZFloaEZtUENTQWIxM0tcVkxIVWY+aGYoeEk/SldZcyNlTzk2Q2NHW1NqWEhEPmhPXl1WZV5xNE9p" + "WDZuck8zPGE2Rk4vKWJXd3F1YV0mSXA/NVNGNEQsK18mTlJNbl9WcGJXcVE5e1E0dlFAPVQ8THM+" + "QjdcdjZSNDVJclo0QVo6cDBMQDtVcUFnNDpcd1ZXSkcsXz82QjhXLl9NSVBFdipeOmF4NC5YKnNx" + "K2dxMGx1MDkrdnJhWTovUjk1ZCZUUSNTKHIvJUgmTyE4bjJbZlZAPl9IZi8ucSM7a2FAQWUzXUJO" + "LmpALilFWGRqYlh1Siw2MzJqbipsWlZRMG91MGVQVlExLCFeayMuM3dfSn1ONiU8LixZWSx3YUlV" + "bGtIcnVWP2Y0LGwvTzFIQy8qZVVBSVZLS1peSW0xNTRPcXRDIXBkXnhmWyNxQFxHQ19cYXVAO214" + "RWw1P0AmIUAlQjk7ZFBWXW1RbWxoTFE+cUxMbk5UCg==" + "</payload>" + "</encrypted>")); + + // An OMEMO element having its OMEMO envelopes sorted in reverse order is + // needed since they are serialized in the reverse order in which they are + // deserialized. + const QByteArray xmlOut(QByteArrayLiteral( + "<encrypted xmlns=\"urn:xmpp:omemo:1\">" + "<header sid=\"27183\">" + "<keys jid=\"juliet@capulet.lit\">" + "<key rid=\"31415\">Oy5TSG9vVVV4Wz9wUkUvI1lUXiVLIU5bbGIsUV0wRngK</key>" + "</keys>" + "<keys jid=\"romeo@montague.lit\">" + "<key rid=\"12321\" kex=\"true\">a012U0R9WixWKUYhYipucnZOWG06akFOR3Q1NGNOOmUK</key>" + "<key rid=\"1337\">PTEoSk91VnRZSXBzcFlPXy4jZ3NKcGVZZ2d3YVJbVj8K</key>" + "</keys>" + "</header>" + "<payload>" + "Vk9NPi99bHFWKmErOUVTTkAwW1VcZjJvPlElZWUoOk90Kz03YUF7OHc/WjpaQz9ieFdsZjBsSH1w" + "R1d2Zzt1bEFAMSZqP0dVJj9oaygmcWRPKGU3Kjc8aV4sJSlpSXBqaENCT2NUVFFmaFNXbCxQaHsj" + "OnthQDJyUW9qNjwoZCtpLzpzLGpbKlJRY1NtMVVeRzdsOWRQciNnXV9tajEyWztnKiEhRHs5K2hX" + "ZFloaEZtUENTQWIxM0tcVkxIVWY+aGYoeEk/SldZcyNlTzk2Q2NHW1NqWEhEPmhPXl1WZV5xNE9p" + "WDZuck8zPGE2Rk4vKWJXd3F1YV0mSXA/NVNGNEQsK18mTlJNbl9WcGJXcVE5e1E0dlFAPVQ8THM+" + "QjdcdjZSNDVJclo0QVo6cDBMQDtVcUFnNDpcd1ZXSkcsXz82QjhXLl9NSVBFdipeOmF4NC5YKnNx" + "K2dxMGx1MDkrdnJhWTovUjk1ZCZUUSNTKHIvJUgmTyE4bjJbZlZAPl9IZi8ucSM7a2FAQWUzXUJO" + "LmpALilFWGRqYlh1Siw2MzJqbipsWlZRMG91MGVQVlExLCFeayMuM3dfSn1ONiU8LixZWSx3YUlV" + "bGtIcnVWP2Y0LGwvTzFIQy8qZVVBSVZLS1peSW0xNTRPcXRDIXBkXnhmWyNxQFxHQ19cYXVAO214" + "RWw1P0AmIUAlQjk7ZFBWXW1RbWxoTFE+cUxMbk5UCg==" + "</payload>" + "</encrypted>")); + + QXmppOmemoElement omemoElement1; + parsePacket(omemoElement1, xmlIn); + + QCOMPARE(omemoElement1.senderDeviceId(), uint32_t(27183)); + + const auto omemoEnvelope1 = omemoElement1.searchEnvelope(("juliet@capulet.lit"), 31415); + QVERIFY(omemoEnvelope1); + QCOMPARE(omemoEnvelope1->recipientDeviceId(), uint32_t(31415)); + QVERIFY(!omemoEnvelope1->isUsedForKeyExchange()); + QCOMPARE(omemoEnvelope1->data().toBase64(), QByteArrayLiteral("Oy5TSG9vVVV4Wz9wUkUvI1lUXiVLIU5bbGIsUV0wRngK")); + + const auto omemoEnvelope2 = omemoElement1.searchEnvelope(QStringLiteral("romeo@montague.lit"), 12321); + QVERIFY(omemoEnvelope2); + QCOMPARE(omemoEnvelope2->recipientDeviceId(), uint32_t(12321)); + QVERIFY(omemoEnvelope2->isUsedForKeyExchange()); + QCOMPARE(omemoEnvelope2->data().toBase64(), QByteArrayLiteral("a012U0R9WixWKUYhYipucnZOWG06akFOR3Q1NGNOOmUK")); + + const auto omemoEnvelope3 = omemoElement1.searchEnvelope(QStringLiteral("romeo@montague.lit"), 1337); + QVERIFY(omemoEnvelope3); + QCOMPARE(omemoEnvelope3->recipientDeviceId(), uint32_t(1337)); + QVERIFY(!omemoEnvelope3->isUsedForKeyExchange()); + QCOMPARE(omemoEnvelope3->data().toBase64(), QByteArrayLiteral("PTEoSk91VnRZSXBzcFlPXy4jZ3NKcGVZZ2d3YVJbVj8K")); + + QCOMPARE( + omemoElement1.payload().toBase64(), + QByteArrayLiteral( + "Vk9NPi99bHFWKmErOUVTTkAwW1VcZjJvPlElZWUoOk90Kz03YUF7OHc/WjpaQz9ieFdsZjBsSH1w" + "R1d2Zzt1bEFAMSZqP0dVJj9oaygmcWRPKGU3Kjc8aV4sJSlpSXBqaENCT2NUVFFmaFNXbCxQaHsj" + "OnthQDJyUW9qNjwoZCtpLzpzLGpbKlJRY1NtMVVeRzdsOWRQciNnXV9tajEyWztnKiEhRHs5K2hX" + "ZFloaEZtUENTQWIxM0tcVkxIVWY+aGYoeEk/SldZcyNlTzk2Q2NHW1NqWEhEPmhPXl1WZV5xNE9p" + "WDZuck8zPGE2Rk4vKWJXd3F1YV0mSXA/NVNGNEQsK18mTlJNbl9WcGJXcVE5e1E0dlFAPVQ8THM+" + "QjdcdjZSNDVJclo0QVo6cDBMQDtVcUFnNDpcd1ZXSkcsXz82QjhXLl9NSVBFdipeOmF4NC5YKnNx" + "K2dxMGx1MDkrdnJhWTovUjk1ZCZUUSNTKHIvJUgmTyE4bjJbZlZAPl9IZi8ucSM7a2FAQWUzXUJO" + "LmpALilFWGRqYlh1Siw2MzJqbipsWlZRMG91MGVQVlExLCFeayMuM3dfSn1ONiU8LixZWSx3YUlV" + "bGtIcnVWP2Y0LGwvTzFIQy8qZVVBSVZLS1peSW0xNTRPcXRDIXBkXnhmWyNxQFxHQ19cYXVAO214" + "RWw1P0AmIUAlQjk7ZFBWXW1RbWxoTFE+cUxMbk5UCg==")); + + serializePacket(omemoElement1, xmlOut); + + QXmppOmemoElement omemoElement2; + omemoElement2.setSenderDeviceId(27183); + omemoElement2.setPayload( + QByteArray::fromBase64(QByteArrayLiteral( + "Vk9NPi99bHFWKmErOUVTTkAwW1VcZjJvPlElZWUoOk90Kz03YUF7OHc/WjpaQz9ieFdsZjBsSH1w" + "R1d2Zzt1bEFAMSZqP0dVJj9oaygmcWRPKGU3Kjc8aV4sJSlpSXBqaENCT2NUVFFmaFNXbCxQaHsj" + "OnthQDJyUW9qNjwoZCtpLzpzLGpbKlJRY1NtMVVeRzdsOWRQciNnXV9tajEyWztnKiEhRHs5K2hX" + "ZFloaEZtUENTQWIxM0tcVkxIVWY+aGYoeEk/SldZcyNlTzk2Q2NHW1NqWEhEPmhPXl1WZV5xNE9p" + "WDZuck8zPGE2Rk4vKWJXd3F1YV0mSXA/NVNGNEQsK18mTlJNbl9WcGJXcVE5e1E0dlFAPVQ8THM+" + "QjdcdjZSNDVJclo0QVo6cDBMQDtVcUFnNDpcd1ZXSkcsXz82QjhXLl9NSVBFdipeOmF4NC5YKnNx" + "K2dxMGx1MDkrdnJhWTovUjk1ZCZUUSNTKHIvJUgmTyE4bjJbZlZAPl9IZi8ucSM7a2FAQWUzXUJO" + "LmpALilFWGRqYlh1Siw2MzJqbipsWlZRMG91MGVQVlExLCFeayMuM3dfSn1ONiU8LixZWSx3YUlV" + "bGtIcnVWP2Y0LGwvTzFIQy8qZVVBSVZLS1peSW0xNTRPcXRDIXBkXnhmWyNxQFxHQ19cYXVAO214" + "RWw1P0AmIUAlQjk7ZFBWXW1RbWxoTFE+cUxMbk5UCg=="))); + + QXmppOmemoEnvelope omemoEnvelope4; + omemoEnvelope4.setRecipientDeviceId(31415); + omemoEnvelope4.setData(QByteArray::fromBase64("Oy5TSG9vVVV4Wz9wUkUvI1lUXiVLIU5bbGIsUV0wRngK")); + omemoElement2.addEnvelope(QStringLiteral("juliet@capulet.lit"), omemoEnvelope4); + + QXmppOmemoEnvelope omemoEnvelope5; + omemoEnvelope5.setRecipientDeviceId(12321); + omemoEnvelope5.setIsUsedForKeyExchange(true); + omemoEnvelope5.setData(QByteArray::fromBase64("a012U0R9WixWKUYhYipucnZOWG06akFOR3Q1NGNOOmUK")); + omemoElement2.addEnvelope(QStringLiteral("romeo@montague.lit"), omemoEnvelope5); + + QXmppOmemoEnvelope omemoEnvelope6; + omemoEnvelope6.setRecipientDeviceId(1337); + omemoEnvelope6.setData(QByteArray::fromBase64("PTEoSk91VnRZSXBzcFlPXy4jZ3NKcGVZZ2d3YVJbVj8K")); + omemoElement2.addEnvelope(QStringLiteral("romeo@montague.lit"), omemoEnvelope6); + + QCOMPARE(omemoElement2.senderDeviceId(), uint32_t(27183)); + + const auto omemoEnvelope7 = omemoElement2.searchEnvelope(QStringLiteral("romeo@montague.lit"), 12321); + QVERIFY(omemoEnvelope7); + QCOMPARE(omemoEnvelope7->recipientDeviceId(), uint32_t(12321)); + QVERIFY(omemoEnvelope7->isUsedForKeyExchange()); + QCOMPARE(omemoEnvelope7->data().toBase64(), QByteArrayLiteral("a012U0R9WixWKUYhYipucnZOWG06akFOR3Q1NGNOOmUK")); + + const auto omemoEnvelope8 = omemoElement2.searchEnvelope(QStringLiteral("juliet@capulet.lit"), 31415); + QVERIFY(omemoEnvelope8); + QVERIFY(!omemoEnvelope8->isUsedForKeyExchange()); + + serializePacket(omemoElement2, xmlIn); +} + QTEST_MAIN(tst_QXmppOmemoData) #include "tst_qxmppomemodata.moc" |
