aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMelvin Keskin <melvo@olomono.de>2021-09-02 23:41:16 +0200
committerLinus Jahn <lnj@kaidan.im>2021-09-03 16:16:15 +0200
commit0623aa38f2ead734dddea4cbad899a868f01cb1e (patch)
treece6539732310386d2ba0faf7383c054a4690f3a2
parent12c63eddded9f02369bd4e569f62acfdd95cc723 (diff)
downloadqxmpp-0623aa38f2ead734dddea4cbad899a868f01cb1e.tar.gz
Add QXmppOmemoElement
Co-authored-by: Germán Márquez Mejía <mancho@olomono.de>
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/base/QXmppMessage.cpp34
-rw-r--r--src/base/QXmppMessage.h5
-rw-r--r--src/base/QXmppOmemoData.cpp187
-rw-r--r--src/base/QXmppOmemoElement.h70
-rw-r--r--tests/qxmppmessage/tst_qxmppmessage.cpp84
-rw-r--r--tests/qxmppomemodata/tst_qxmppomemodata.cpp171
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"