From 0623aa38f2ead734dddea4cbad899a868f01cb1e Mon Sep 17 00:00:00 2001 From: Melvin Keskin Date: Thu, 2 Sep 2021 23:41:16 +0200 Subject: Add QXmppOmemoElement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Germán Márquez Mejía --- src/base/QXmppMessage.cpp | 34 ++++++++ src/base/QXmppMessage.h | 5 ++ src/base/QXmppOmemoData.cpp | 187 +++++++++++++++++++++++++++++++++++++++++++ src/base/QXmppOmemoElement.h | 70 ++++++++++++++++ 4 files changed, 296 insertions(+) create mode 100644 src/base/QXmppOmemoElement.h (limited to 'src/base') 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 omemoElement; + // XEP-0407: Mediated Information eXchange (MIX): Miscellaneous Capabilities std::optional mixInvitation; @@ -1083,6 +1087,26 @@ void QXmppMessage::setSpoilerHint(const QString &spoilerHint) d->isSpoiler = true; } +/// +/// Returns an included OMEMO element as defined by \xep{0384, OMEMO Encryption}. +/// +/// \since QXmpp 1.5 +/// +std::optional 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 &omemoElement) +{ + d->omemoElement = omemoElement; +} + /// /// Returns an included \xep{0369}: Mediated Information eXchange (MIX) /// invitation as defined by \xep{0407}: Mediated Information eXchange (MIX): @@ -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 omemoElement() const; + void setOmemoElement(const std::optional &omemoElement); + // XEP-0407: Mediated Information eXchange (MIX): Miscellaneous Capabilities std::optional mixInvitation() const; void setMixInvitation(const std::optional &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 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 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 + +#include + +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 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 d; +}; + +Q_DECLARE_TYPEINFO(QXmppOmemoElement, Q_MOVABLE_TYPE); + +#endif // QXMPPOMEMOELEMENT_H -- cgit v1.2.3