diff options
| author | Linus Jahn <lnj@kaidan.im> | 2022-09-16 19:08:02 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-09-16 19:08:02 +0200 |
| commit | d858e4ae6e0adeaad8d03b055883f411e6d19ab0 (patch) | |
| tree | e306f607339bd553ba79030fcc90686c5d1422e3 /src | |
| parent | 7878aeb22cc49b31ae527dfc683f2f16b0f79075 (diff) | |
| download | qxmpp-d858e4ae6e0adeaad8d03b055883f411e6d19ab0.tar.gz | |
Implement XEP-0448: Encryption for stateless file sharing parsing (#463)
https://xmpp.org/extensions/xep-0448.html
Co-authored-by: Jonah Brüchert <jbb@kaidan.im>
Diffstat (limited to 'src')
| -rw-r--r-- | src/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/base/QXmppConstants.cpp | 2 | ||||
| -rw-r--r-- | src/base/QXmppConstants_p.h | 2 | ||||
| -rw-r--r-- | src/base/QXmppEncryptedFileSource.cpp | 157 | ||||
| -rw-r--r-- | src/base/QXmppEncryptedFileSource_p.h | 59 | ||||
| -rw-r--r-- | src/base/QXmppHash.cpp | 1 |
6 files changed, 222 insertions, 0 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c719dc3f..fb4273c9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -161,6 +161,7 @@ set(SOURCE_FILES base/QXmppDataFormBase.cpp base/QXmppDiscoveryIq.cpp base/QXmppElement.cpp + base/QXmppEncryptedFileSource.cpp base/QXmppEntityTimeIq.cpp base/QXmppError.cpp base/QXmppFileMetadata.cpp diff --git a/src/base/QXmppConstants.cpp b/src/base/QXmppConstants.cpp index 7f58911c..196ee89b 100644 --- a/src/base/QXmppConstants.cpp +++ b/src/base/QXmppConstants.cpp @@ -192,5 +192,7 @@ const char *ns_tm = "urn:xmpp:tm:1"; const char *ns_file_metadata = "urn:xmpp:file:metadata:0"; // XEP-0447: Stateless file sharing const char *ns_sfs = "urn:xmpp:sfs:0"; +// XEP-0448: Encryption for stateless file sharing +const char *ns_esfs = "urn:xmpp:esfs:0"; // XEP-0450: Automatic Trust Management (ATM) const char *ns_atm = "urn:xmpp:atm:1"; diff --git a/src/base/QXmppConstants_p.h b/src/base/QXmppConstants_p.h index d040ed27..83521c5a 100644 --- a/src/base/QXmppConstants_p.h +++ b/src/base/QXmppConstants_p.h @@ -204,6 +204,8 @@ extern const char *ns_tm; extern const char *ns_file_metadata; // XEP-0447: Stateless file sharing extern const char *ns_sfs; +// XEP-0448: Encryption for stateless file sharing +extern const char *ns_esfs; // XEP-0450: Automatic Trust Management (ATM) extern const char *ns_atm; diff --git a/src/base/QXmppEncryptedFileSource.cpp b/src/base/QXmppEncryptedFileSource.cpp new file mode 100644 index 00000000..d2c2b592 --- /dev/null +++ b/src/base/QXmppEncryptedFileSource.cpp @@ -0,0 +1,157 @@ +// SPDX-FileCopyrightText: 2022 Linus Jahn <lnj@kaidan.im> +// SPDX-FileCopyrightText: 2022 Jonah Brüchert <jbb@kaidan.im> +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "QXmppConstants_p.h" +#include "QXmppEncryptedFileSource_p.h" +#include "QXmppHttpFileSource.h" + +#include <optional> + +#include <QDomElement> +#include <QXmlStreamWriter> + +/// \cond +static QString cipherToString(QXmppEncryptedFileSource::Cipher cipher) +{ + switch (cipher) { + case QXmppEncryptedFileSource::Aes128GcmNopadding: + return "urn:xmpp:ciphers:aes-128-gcm-nopadding:0"; + case QXmppEncryptedFileSource::Aes256GcmNopadding: + return "urn:xmpp:ciphers:aes-256-gcm-nopadding:0"; + case QXmppEncryptedFileSource::Aes256CbcPkcs7: + return "urn:xmpp:ciphers:aes-256-cbc-pkcs7:0"; + } + Q_UNREACHABLE(); +} + +static std::optional<QXmppEncryptedFileSource::Cipher> cipherFromString(const QString &cipher) +{ + if (cipher == "urn:xmpp:ciphers:aes-128-gcm-nopadding:0") { + return QXmppEncryptedFileSource::Aes128GcmNopadding; + } else if (cipher == "urn:xmpp:ciphers:aes-256-gcm-nopadding:0") { + return QXmppEncryptedFileSource::Aes256GcmNopadding; + } else if (cipher == "urn:xmpp:ciphers:aes-256-cbc-pkcs7:0") { + return QXmppEncryptedFileSource::Aes256CbcPkcs7; + } + return {}; +} + +QXmppEncryptedFileSource::QXmppEncryptedFileSource() = default; + +QXmppEncryptedFileSource::Cipher QXmppEncryptedFileSource::cipher() const +{ + return m_cipher; +} + +void QXmppEncryptedFileSource::setCipher(Cipher newCipher) +{ + m_cipher = newCipher; +} + +const QByteArray &QXmppEncryptedFileSource::key() const +{ + return m_key; +} + +void QXmppEncryptedFileSource::setKey(const QByteArray &newKey) +{ + m_key = newKey; +} + +const QByteArray &QXmppEncryptedFileSource::iv() const +{ + return m_iv; +} + +void QXmppEncryptedFileSource::setIv(const QByteArray &newIv) +{ + m_iv = newIv; +} + +const QVector<QXmppHash> &QXmppEncryptedFileSource::hashes() const +{ + return m_hashes; +} + +void QXmppEncryptedFileSource::setHashes(const QVector<QXmppHash> &newHashes) +{ + m_hashes = newHashes; +} + +const QVector<QXmppHttpFileSource> &QXmppEncryptedFileSource::httpSources() const +{ + return m_httpSources; +} + +void QXmppEncryptedFileSource::setHttpSources(const QVector<QXmppHttpFileSource> &newHttpSources) +{ + m_httpSources = newHttpSources; +} + +bool QXmppEncryptedFileSource::parse(const QDomElement &el) +{ + QString cipher = el.attribute(QStringLiteral("cipher")); + if (auto parsedCipher = cipherFromString(cipher)) { + m_cipher = *parsedCipher; + } else { + return false; + } + + auto keyEl = el.firstChildElement(QStringLiteral("key")); + if (keyEl.isNull()) { + return false; + } + m_key = QByteArray::fromBase64(keyEl.text().toUtf8()); + + auto ivEl = el.firstChildElement(QStringLiteral("iv")); + if (ivEl.isNull()) { + return false; + } + m_iv = QByteArray::fromBase64(ivEl.text().toUtf8()); + + for (auto childEl = el.firstChildElement(QStringLiteral("hash")); + !childEl.isNull(); + childEl = childEl.nextSiblingElement(QStringLiteral("hash"))) { + QXmppHash hash; + if (!hash.parse(childEl)) { + return false; + } + m_hashes.push_back(std::move(hash)); + } + + auto sourcesEl = el.firstChildElement(QStringLiteral("sources")); + if (sourcesEl.isNull()) { + return false; + } + for (auto childEl = sourcesEl.firstChildElement(QStringLiteral("url-data")); + !childEl.isNull(); + childEl = childEl.nextSiblingElement(QStringLiteral("url-data"))) { + QXmppHttpFileSource source; + source.parse(childEl); + m_httpSources.push_back(std::move(source)); + } + + return true; +} + +void QXmppEncryptedFileSource::toXml(QXmlStreamWriter *writer) const +{ + writer->writeStartElement(QStringLiteral("encrypted")); + writer->writeDefaultNamespace(ns_esfs); + writer->writeAttribute(QStringLiteral("cipher"), cipherToString(m_cipher)); + writer->writeTextElement(QStringLiteral("key"), m_key.toBase64()); + writer->writeTextElement(QStringLiteral("iv"), m_iv.toBase64()); + for (const auto &hash : m_hashes) { + hash.toXml(writer); + } + writer->writeStartElement(QStringLiteral("sources")); + writer->writeDefaultNamespace(ns_sfs); + for (const auto &source : m_httpSources) { + source.toXml(writer); + } + writer->writeEndElement(); + writer->writeEndElement(); +} +/// \endcond diff --git a/src/base/QXmppEncryptedFileSource_p.h b/src/base/QXmppEncryptedFileSource_p.h new file mode 100644 index 00000000..21270de2 --- /dev/null +++ b/src/base/QXmppEncryptedFileSource_p.h @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: 2022 Linus Jahn <lnj@kaidan.im> +// SPDX-FileCopyrightText: 2022 Jonah Brüchert <jbb@kaidan.im> +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#ifndef QXMPPENCRYPTEDFILESOURCE_H +#define QXMPPENCRYPTEDFILESOURCE_H + +#include "QXmppGlobal.h" +#include "QXmppHash.h" +#include "QXmppHttpFileSource.h" + +#include <QSharedDataPointer> +#include <QUrl> +#include <QVector> + +class QXmppEncryptedFileSourcePrivate; + +// exported for tests +class QXMPP_EXPORT QXmppEncryptedFileSource +{ +public: + enum Cipher { + Aes128GcmNopadding, + Aes256GcmNopadding, + Aes256CbcPkcs7, + }; + + QXmppEncryptedFileSource(); + + Cipher cipher() const; + void setCipher(Cipher newCipher); + + const QByteArray &key() const; + void setKey(const QByteArray &newKey); + + const QByteArray &iv() const; + void setIv(const QByteArray &newIv); + + const QVector<QXmppHash> &hashes() const; + void setHashes(const QVector<QXmppHash> &newHashes); + + const QVector<QXmppHttpFileSource> &httpSources() const; + void setHttpSources(const QVector<QXmppHttpFileSource> &newHttpSources); + + /// \cond + bool parse(const QDomElement &el); + void toXml(QXmlStreamWriter *writer) const; + /// \endcond + +private: + Cipher m_cipher = Aes128GcmNopadding; + QByteArray m_key; + QByteArray m_iv; + QVector<QXmppHash> m_hashes; + QVector<QXmppHttpFileSource> m_httpSources; +}; + +#endif // QXMPPENCRYPTEDFILESOURCE_H diff --git a/src/base/QXmppHash.cpp b/src/base/QXmppHash.cpp index c246d200..6d1dcb07 100644 --- a/src/base/QXmppHash.cpp +++ b/src/base/QXmppHash.cpp @@ -123,6 +123,7 @@ bool QXmppHash::parse(const QDomElement &el) #else m_hash = QByteArray::fromBase64(el.text().toUtf8()); #endif + return true; } return false; } |
