From f0e0e1cd227c08ffd97aa42813a479b8c3ca6d23 Mon Sep 17 00:00:00 2001 From: Linus Jahn Date: Tue, 31 May 2022 12:22:48 +0200 Subject: Split up OMEMO into extra module --- src/base/QXmppOmemoDataBase.cpp | 290 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) create mode 100644 src/base/QXmppOmemoDataBase.cpp (limited to 'src/base/QXmppOmemoDataBase.cpp') diff --git a/src/base/QXmppOmemoDataBase.cpp b/src/base/QXmppOmemoDataBase.cpp new file mode 100644 index 00000000..a75d54ea --- /dev/null +++ b/src/base/QXmppOmemoDataBase.cpp @@ -0,0 +1,290 @@ +// SPDX-FileCopyrightText: 2021 Germán Márquez Mejía +// SPDX-FileCopyrightText: 2021 Melvin Keskin +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "QXmppConstants_p.h" +#include "QXmppOmemoElement_p.h" +#include "QXmppOmemoEnvelope_p.h" +#include "QXmppUtils.h" + +#include +#include + +/// \cond +/// +/// \class QXmppOmemoEnvelope +/// +/// \brief The QXmppOmemoEnvelope class represents an OMEMO envelope as +/// defined by \xep{0384, OMEMO Encryption}. +/// + +/// +/// Returns the ID of the recipient's device. +/// +/// The ID is 0 if it is unset. +/// +/// \return the recipient's device ID +/// +uint32_t QXmppOmemoEnvelope::recipientDeviceId() const +{ + return m_recipientDeviceId; +} + +/// +/// Sets the ID of the recipient's device. +/// +/// The ID must be at least 1 and at most \c std::numeric_limits::max(). +/// +/// \param id recipient's device ID +/// +void QXmppOmemoEnvelope::setRecipientDeviceId(uint32_t id) +{ + m_recipientDeviceId = id; +} + +/// +/// Returns true if a pre-key was used to prepare this envelope. +/// +/// The default is false. +/// +/// \return true if a pre-key was used to prepare this envelope, otherwise false +/// +bool QXmppOmemoEnvelope::isUsedForKeyExchange() const +{ + return m_isUsedForKeyExchange; +} + +/// +/// Sets whether a pre-key was used to prepare this envelope. +/// +/// \param isUsed whether a pre-key was used to prepare this envelope +/// +void QXmppOmemoEnvelope::setIsUsedForKeyExchange(bool isUsed) +{ + m_isUsedForKeyExchange = isUsed; +} + +/// +/// Returns the BLOB containing the data for the underlying double ratchet library. +/// +/// It should be treated like an obscure BLOB being passed as is to the ratchet +/// library for further processing. +/// +/// \return the binary data for the ratchet library +/// +QByteArray QXmppOmemoEnvelope::data() const +{ + return m_data; +} + +/// +/// Sets the BLOB containing the data from the underlying double ratchet library. +/// +/// It should be treated like an obscure BLOB produced by the ratchet library. +/// +/// \param data binary data from the ratchet library +/// +void QXmppOmemoEnvelope::setData(const QByteArray &data) +{ + m_data = data; +} + +void QXmppOmemoEnvelope::parse(const QDomElement &element) +{ + m_recipientDeviceId = element.attribute(QStringLiteral("rid")).toInt(); + + const auto isUsedForKeyExchange = element.attribute(QStringLiteral("kex")); + if (isUsedForKeyExchange == QStringLiteral("true") || + isUsedForKeyExchange == QStringLiteral("1")) { + m_isUsedForKeyExchange = true; + } + + m_data = QByteArray::fromBase64(element.text().toLatin1()); +} + +void QXmppOmemoEnvelope::toXml(QXmlStreamWriter *writer) const +{ + writer->writeStartElement(QStringLiteral("key")); + writer->writeAttribute(QStringLiteral("rid"), QString::number(m_recipientDeviceId)); + + if (m_isUsedForKeyExchange) { + helperToXmlAddAttribute(writer, QStringLiteral("kex"), QStringLiteral("true")); + } + + writer->writeCharacters(m_data.toBase64()); + writer->writeEndElement(); +} + +/// +/// Determines whether the given DOM element is an OMEMO envelope. +/// +/// \param element DOM element being checked +/// +/// \return true if element is an OMEMO envelope, otherwise false +/// +bool QXmppOmemoEnvelope::isOmemoEnvelope(const QDomElement &element) +{ + return element.tagName() == QStringLiteral("key") && + element.namespaceURI() == ns_omemo_2; +} + +/// +/// \class QXmppOmemoElement +/// +/// \brief The QXmppOmemoElement class represents an OMEMO element as +/// defined by \xep{0384, OMEMO Encryption}. +/// + +/// +/// 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 m_senderDeviceId; +} + +/// +/// Sets the ID of the sender's device. +/// +/// The ID must be at least 1 and at most +/// \c std::numeric_limits::max(). +/// +/// \param id sender's device ID +/// +void QXmppOmemoElement::setSenderDeviceId(uint32_t id) +{ + m_senderDeviceId = id; +} + +/// +/// Returns the payload which consists of the encrypted SCE envelope. +/// +/// \return the encrypted payload +/// +QByteArray QXmppOmemoElement::payload() const +{ + return m_payload; +} + +/// +/// Sets the payload which consists of the encrypted SCE envelope. +/// +/// \param payload encrypted payload +/// +void QXmppOmemoElement::setPayload(const QByteArray &payload) +{ + m_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 = m_envelopes.constFind(recipientJid); + itr != m_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, const QXmppOmemoEnvelope &envelope) +{ + m_envelopes.insert(QXmppUtils::jidToBareJid(recipientJid), envelope); +} + +void QXmppOmemoElement::parse(const QDomElement &element) +{ + const auto header = element.firstChildElement(QStringLiteral("header")); + + m_senderDeviceId = header.attribute(QStringLiteral("sid")).toInt(); + + for (auto recipient = header.firstChildElement(QStringLiteral("keys")); + !recipient.isNull(); + recipient = recipient.nextSiblingElement(QStringLiteral("keys"))) { + const auto recipientJid = recipient.attribute(QStringLiteral("jid")); + + for (auto envelope = recipient.firstChildElement(QStringLiteral("key")); + !envelope.isNull(); + envelope = envelope.nextSiblingElement(QStringLiteral("key"))) { + QXmppOmemoEnvelope omemoEnvelope; + omemoEnvelope.parse(envelope); + addEnvelope(recipientJid, omemoEnvelope); + } + } + + m_payload = QByteArray::fromBase64(element.firstChildElement(QStringLiteral("payload")).text().toLatin1()); +} + +void QXmppOmemoElement::toXml(QXmlStreamWriter *writer) const +{ + writer->writeStartElement(QStringLiteral("encrypted")); + writer->writeDefaultNamespace(ns_omemo_2); + + writer->writeStartElement(QStringLiteral("header")); + writer->writeAttribute(QStringLiteral("sid"), QString::number(m_senderDeviceId)); + + const auto recipientJids = m_envelopes.uniqueKeys(); + for (const auto &recipientJid : recipientJids) { + writer->writeStartElement(QStringLiteral("keys")); + writer->writeAttribute(QStringLiteral("jid"), recipientJid); + + for (auto itr = m_envelopes.constFind(recipientJid); + itr != m_envelopes.constEnd() && itr.key() == recipientJid; + ++itr) { + const auto &envelope = itr.value(); + envelope.toXml(writer); + } + + writer->writeEndElement(); // keys + } + + writer->writeEndElement(); // header + + // The payload element is only included if there is a payload. + // An empty OMEMO message does not contain a payload. + if (!m_payload.isEmpty()) { + writer->writeTextElement(QStringLiteral("payload"), m_payload.toBase64()); + } + + writer->writeEndElement(); // encrypted +} + +/// +/// 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_2; +} +/// \endcond -- cgit v1.2.3