From 2c6fa33ca52842e9dfba0dd3893a9d5526e10e60 Mon Sep 17 00:00:00 2001 From: Tobias Fella Date: Tue, 7 Dec 2021 19:08:29 +0100 Subject: Rename "crypto" -> "e2ee" --- CMakeLists.txt | 18 +- autotests/testgroupsession.cpp | 6 +- autotests/testolmaccount.cpp | 4 +- autotests/testolmsession.cpp | 2 +- autotests/testolmutility.cpp | 4 +- lib/connection.cpp | 4 +- lib/connection.h | 2 +- lib/converters.cpp | 2 +- lib/crypto/e2ee.h | 132 --------------- lib/crypto/qolmaccount.cpp | 330 ------------------------------------- lib/crypto/qolmaccount.h | 123 -------------- lib/crypto/qolmerrors.cpp | 22 --- lib/crypto/qolmerrors.h | 28 ---- lib/crypto/qolminboundsession.cpp | 153 ----------------- lib/crypto/qolminboundsession.h | 48 ------ lib/crypto/qolmmessage.cpp | 35 ---- lib/crypto/qolmmessage.h | 41 ----- lib/crypto/qolmoutboundsession.cpp | 128 -------------- lib/crypto/qolmoutboundsession.h | 54 ------ lib/crypto/qolmsession.cpp | 253 ---------------------------- lib/crypto/qolmsession.h | 76 --------- lib/crypto/qolmutility.cpp | 63 ------- lib/crypto/qolmutility.h | 45 ----- lib/crypto/qolmutils.cpp | 24 --- lib/crypto/qolmutils.h | 15 -- lib/database.cpp | 6 +- lib/database.h | 2 +- lib/e2ee/e2ee.h | 132 +++++++++++++++ lib/e2ee/qolmaccount.cpp | 330 +++++++++++++++++++++++++++++++++++++ lib/e2ee/qolmaccount.h | 123 ++++++++++++++ lib/e2ee/qolmerrors.cpp | 22 +++ lib/e2ee/qolmerrors.h | 28 ++++ lib/e2ee/qolminboundsession.cpp | 153 +++++++++++++++++ lib/e2ee/qolminboundsession.h | 48 ++++++ lib/e2ee/qolmmessage.cpp | 35 ++++ lib/e2ee/qolmmessage.h | 41 +++++ lib/e2ee/qolmoutboundsession.cpp | 128 ++++++++++++++ lib/e2ee/qolmoutboundsession.h | 54 ++++++ lib/e2ee/qolmsession.cpp | 253 ++++++++++++++++++++++++++++ lib/e2ee/qolmsession.h | 76 +++++++++ lib/e2ee/qolmutility.cpp | 63 +++++++ lib/e2ee/qolmutility.h | 45 +++++ lib/e2ee/qolmutils.cpp | 24 +++ lib/e2ee/qolmutils.h | 15 ++ lib/encryptionmanager.cpp | 12 +- lib/events/encryptedevent.h | 2 +- lib/events/encryptionevent.cpp | 2 +- lib/room.cpp | 8 +- 48 files changed, 1607 insertions(+), 1607 deletions(-) delete mode 100644 lib/crypto/e2ee.h delete mode 100644 lib/crypto/qolmaccount.cpp delete mode 100644 lib/crypto/qolmaccount.h delete mode 100644 lib/crypto/qolmerrors.cpp delete mode 100644 lib/crypto/qolmerrors.h delete mode 100644 lib/crypto/qolminboundsession.cpp delete mode 100644 lib/crypto/qolminboundsession.h delete mode 100644 lib/crypto/qolmmessage.cpp delete mode 100644 lib/crypto/qolmmessage.h delete mode 100644 lib/crypto/qolmoutboundsession.cpp delete mode 100644 lib/crypto/qolmoutboundsession.h delete mode 100644 lib/crypto/qolmsession.cpp delete mode 100644 lib/crypto/qolmsession.h delete mode 100644 lib/crypto/qolmutility.cpp delete mode 100644 lib/crypto/qolmutility.h delete mode 100644 lib/crypto/qolmutils.cpp delete mode 100644 lib/crypto/qolmutils.h create mode 100644 lib/e2ee/e2ee.h create mode 100644 lib/e2ee/qolmaccount.cpp create mode 100644 lib/e2ee/qolmaccount.h create mode 100644 lib/e2ee/qolmerrors.cpp create mode 100644 lib/e2ee/qolmerrors.h create mode 100644 lib/e2ee/qolminboundsession.cpp create mode 100644 lib/e2ee/qolminboundsession.h create mode 100644 lib/e2ee/qolmmessage.cpp create mode 100644 lib/e2ee/qolmmessage.h create mode 100644 lib/e2ee/qolmoutboundsession.cpp create mode 100644 lib/e2ee/qolmoutboundsession.h create mode 100644 lib/e2ee/qolmsession.cpp create mode 100644 lib/e2ee/qolmsession.h create mode 100644 lib/e2ee/qolmutility.cpp create mode 100644 lib/e2ee/qolmutility.h create mode 100644 lib/e2ee/qolmutils.cpp create mode 100644 lib/e2ee/qolmutils.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f886094..a84a70fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -164,15 +164,15 @@ list(APPEND lib_SRCS ) if (${PROJECT_NAME}_ENABLE_E2EE) list(APPEND lib_SRCS - lib/crypto/qolmaccount.cpp - lib/crypto/qolmsession.cpp - lib/crypto/qolminboundsession.cpp - lib/crypto/qolmoutboundsession.cpp - lib/crypto/qolmutils.cpp - lib/crypto/qolmutility.cpp - lib/crypto/qolmerrors.cpp - lib/crypto/qolmsession.cpp - lib/crypto/qolmmessage.cpp + lib/e2ee/qolmaccount.cpp + lib/e2ee/qolmsession.cpp + lib/e2ee/qolminboundsession.cpp + lib/e2ee/qolmoutboundsession.cpp + lib/e2ee/qolmutils.cpp + lib/e2ee/qolmutility.cpp + lib/e2ee/qolmerrors.cpp + lib/e2ee/qolmsession.cpp + lib/e2ee/qolmmessage.cpp lib/encryptionmanager.cpp ) endif() diff --git a/autotests/testgroupsession.cpp b/autotests/testgroupsession.cpp index ea1bb4a9..afd5ef81 100644 --- a/autotests/testgroupsession.cpp +++ b/autotests/testgroupsession.cpp @@ -3,9 +3,9 @@ // SPDX-License-Identifier: LGPL-2.1-or-later #include "testgroupsession.h" -#include "crypto/qolminboundsession.h" -#include "crypto/qolmoutboundsession.h" -#include "crypto/qolmutils.h" +#include "e2ee/qolminboundsession.h" +#include "e2ee/qolmoutboundsession.h" +#include "e2ee/qolmutils.h" using namespace Quotient; diff --git a/autotests/testolmaccount.cpp b/autotests/testolmaccount.cpp index 4fd129b5..22c457aa 100644 --- a/autotests/testolmaccount.cpp +++ b/autotests/testolmaccount.cpp @@ -4,8 +4,8 @@ // SPDX-License-Identifier: LGPL-2.1-or-later #include "testolmaccount.h" -#include -#include +#include +#include #include #include #include diff --git a/autotests/testolmsession.cpp b/autotests/testolmsession.cpp index 00d76d4e..41baf8e3 100644 --- a/autotests/testolmsession.cpp +++ b/autotests/testolmsession.cpp @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: LGPL-2.1-or-later -#include "crypto/qolmsession.h" +#include "e2ee/qolmsession.h" #include "testolmsession.h" using namespace Quotient; diff --git a/autotests/testolmutility.cpp b/autotests/testolmutility.cpp index 2eec7e00..bbf3a055 100644 --- a/autotests/testolmutility.cpp +++ b/autotests/testolmutility.cpp @@ -3,8 +3,8 @@ // SPDX-License-Identifier: LGPL-2.1-or-later #include "testolmutility.h" -#include "crypto/qolmaccount.h" -#include "crypto/qolmutility.h" +#include "e2ee/qolmaccount.h" +#include "e2ee/qolmutility.h" using namespace Quotient; diff --git a/lib/connection.cpp b/lib/connection.cpp index f344807e..c7591e43 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -38,8 +38,8 @@ #include "jobs/syncjob.h" #ifdef Quotient_E2EE_ENABLED -# include "crypto/qolmaccount.h" -# include "crypto/qolmutils.h" +# include "e2ee/qolmaccount.h" +# include "e2ee/qolmutils.h" #endif // Quotient_E2EE_ENABLED #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) diff --git a/lib/connection.h b/lib/connection.h index d2347d1d..3a12ec39 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -22,7 +22,7 @@ #include #ifdef Quotient_E2EE_ENABLED -#include "crypto/e2ee.h" +#include "e2ee/e2ee.h" #endif Q_DECLARE_METATYPE(Quotient::GetLoginFlowsJob::LoginFlow) diff --git a/lib/converters.cpp b/lib/converters.cpp index a3ac44c5..4136940f 100644 --- a/lib/converters.cpp +++ b/lib/converters.cpp @@ -4,7 +4,7 @@ #include "converters.h" #include -#include "crypto/e2ee.h" +#include "e2ee/e2ee.h" QJsonValue Quotient::JsonConverter::dump(const QVariant& v) { diff --git a/lib/crypto/e2ee.h b/lib/crypto/e2ee.h deleted file mode 100644 index 41cd2878..00000000 --- a/lib/crypto/e2ee.h +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-FileCopyrightText: 2019 Alexey Andreyev -// SPDX-FileCopyrightText: 2019 Kitsune Ral -// SPDX-FileCopyrightText: 2021 Carl Schwan -// SPDX-License-Identifier: LGPL-2.1-or-later - -#pragma once - -#include -#include -#include "converters.h" -#include - -#include -#include -#include -#include - -#include "util.h" - -namespace Quotient { - -inline const auto CiphertextKeyL = "ciphertext"_ls; -inline const auto SenderKeyKeyL = "sender_key"_ls; -inline const auto DeviceIdKeyL = "device_id"_ls; -inline const auto SessionIdKeyL = "session_id"_ls; - -inline const auto AlgorithmKeyL = "algorithm"_ls; -inline const auto RotationPeriodMsKeyL = "rotation_period_ms"_ls; -inline const auto RotationPeriodMsgsKeyL = "rotation_period_msgs"_ls; - -inline const auto AlgorithmKey = QStringLiteral("algorithm"); -inline const auto RotationPeriodMsKey = QStringLiteral("rotation_period_ms"); -inline const auto RotationPeriodMsgsKey = - QStringLiteral("rotation_period_msgs"); - -inline const auto Ed25519Key = QStringLiteral("ed25519"); -inline const auto Curve25519Key = QStringLiteral("curve25519"); -inline const auto SignedCurve25519Key = QStringLiteral("signed_curve25519"); -inline const auto OlmV1Curve25519AesSha2AlgoKey = - QStringLiteral("m.olm.v1.curve25519-aes-sha2"); -inline const auto MegolmV1AesSha2AlgoKey = - QStringLiteral("m.megolm.v1.aes-sha2"); -inline const QStringList SupportedAlgorithms = { OlmV1Curve25519AesSha2AlgoKey, - MegolmV1AesSha2AlgoKey }; -struct Unencrypted {}; -struct Encrypted { - QByteArray key; -}; - -using PicklingMode = std::variant; - -class QOlmSession; -using QOlmSessionPtr = std::unique_ptr; - -class QOlmInboundGroupSession; -using QOlmInboundGroupSessionPtr = std::unique_ptr; - -template struct overloaded : Ts... { using Ts::operator()...; }; -template overloaded(Ts...) -> overloaded; - -struct IdentityKeys -{ - QByteArray curve25519; - QByteArray ed25519; -}; - -//! Struct representing the one-time keys. -struct OneTimeKeys -{ - QMap> keys; - - //! Get the HashMap containing the curve25519 one-time keys. - QMap curve25519() const; - - //! Get a reference to the hashmap corresponding to given key type. - std::optional> get(QString keyType) const; -}; - -//! Struct representing the signed one-time keys. -class SignedOneTimeKey -{ -public: - SignedOneTimeKey() = default; - SignedOneTimeKey(const SignedOneTimeKey &) = default; - SignedOneTimeKey &operator=(const SignedOneTimeKey &) = default; - //! Required. The unpadded Base64-encoded 32-byte Curve25519 public key. - QString key; - - //! Required. Signatures of the key object. - //! The signature is calculated using the process described at Signing JSON. - QHash> signatures; -}; - - -template <> -struct JsonObjectConverter { - static void fillFrom(const QJsonObject& jo, - SignedOneTimeKey& result) - { - fromJson(jo.value("key"_ls), result.key); - fromJson(jo.value("signatures"_ls), result.signatures); - } - - static void dumpTo(QJsonObject &jo, const SignedOneTimeKey &result) - { - addParam<>(jo, QStringLiteral("key"), result.key); - addParam<>(jo, QStringLiteral("signatures"), result.signatures); - } -}; - -bool operator==(const IdentityKeys& lhs, const IdentityKeys& rhs); - -template -class asKeyValueRange -{ -public: - asKeyValueRange(T &data) - : m_data{data} - { - } - - auto begin() { return m_data.keyValueBegin(); } - - auto end() { return m_data.keyValueEnd(); } - -private: - T &m_data; -}; - -} // namespace Quotient - -Q_DECLARE_METATYPE(Quotient::SignedOneTimeKey) diff --git a/lib/crypto/qolmaccount.cpp b/lib/crypto/qolmaccount.cpp deleted file mode 100644 index 5c9f5db4..00000000 --- a/lib/crypto/qolmaccount.cpp +++ /dev/null @@ -1,330 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#include "qolmaccount.h" -#include "connection.h" -#include "csapi/keys.h" -#include "crypto/qolmutils.h" -#include "crypto/qolmutility.h" -#include -#include -#include -#include - -using namespace Quotient; - -QMap OneTimeKeys::curve25519() const -{ - return keys[QStringLiteral("curve25519")]; -} - -std::optional> OneTimeKeys::get(QString keyType) const -{ - if (!keys.contains(keyType)) { - return std::nullopt; - } - return keys[keyType]; -} - -bool operator==(const IdentityKeys& lhs, const IdentityKeys& rhs) -{ - return lhs.curve25519 == rhs.curve25519 &&& lhs.ed25519 == rhs.ed25519; -} - -// Convert olm error to enum -QOlmError lastError(OlmAccount *account) { - const std::string error_raw = olm_account_last_error(account); - - return fromString(error_raw); -} - -QByteArray getRandom(size_t bufferSize) -{ - QByteArray buffer(bufferSize, '0'); - std::generate(buffer.begin(), buffer.end(), std::rand); - return buffer; -} - -QOlmAccount::QOlmAccount(const QString &userId, const QString &deviceId, QObject *parent) - : QObject(parent) - , m_userId(userId) - , m_deviceId(deviceId) -{ -} - -QOlmAccount::~QOlmAccount() -{ - olm_clear_account(m_account); - delete[](reinterpret_cast(m_account)); -} - -void QOlmAccount::createNewAccount() -{ - m_account = olm_account(new uint8_t[olm_account_size()]); - size_t randomSize = olm_create_account_random_length(m_account); - QByteArray randomData = getRandom(randomSize); - const auto error = olm_create_account(m_account, randomData.data(), randomSize); - if (error == olm_error()) { - throw lastError(m_account); - } - Q_EMIT needsSave(); -} - -void QOlmAccount::unpickle(QByteArray &pickled, const PicklingMode &mode) -{ - m_account = olm_account(new uint8_t[olm_account_size()]); - const QByteArray key = toKey(mode); - const auto error = olm_unpickle_account(m_account, key.data(), key.length(), pickled.data(), pickled.size()); - if (error == olm_error()) { - qCWarning(E2EE) << "Failed to unpickle olm account"; - //TODO: Do something that is not dying - // Probably log the user out since we have no way of getting to the keys - //throw lastError(m_account); - } -} - -std::variant QOlmAccount::pickle(const PicklingMode &mode) -{ - const QByteArray key = toKey(mode); - const size_t pickleLength = olm_pickle_account_length(m_account); - QByteArray pickleBuffer(pickleLength, '0'); - const auto error = olm_pickle_account(m_account, key.data(), - key.length(), pickleBuffer.data(), pickleLength); - if (error == olm_error()) { - return lastError(m_account); - } - return pickleBuffer; -} - -IdentityKeys QOlmAccount::identityKeys() const -{ - const size_t keyLength = olm_account_identity_keys_length(m_account); - QByteArray keyBuffer(keyLength, '0'); - const auto error = olm_account_identity_keys(m_account, keyBuffer.data(), keyLength); - if (error == olm_error()) { - throw lastError(m_account); - } - const QJsonObject key = QJsonDocument::fromJson(keyBuffer).object(); - return IdentityKeys { - key.value(QStringLiteral("curve25519")).toString().toUtf8(), - key.value(QStringLiteral("ed25519")).toString().toUtf8() - }; -} - -QByteArray QOlmAccount::sign(const QByteArray &message) const -{ - QByteArray signatureBuffer(olm_account_signature_length(m_account), '0'); - - const auto error = olm_account_sign(m_account, message.data(), message.length(), - signatureBuffer.data(), signatureBuffer.length()); - - if (error == olm_error()) { - throw lastError(m_account); - } - return signatureBuffer; -} - -QByteArray QOlmAccount::sign(const QJsonObject &message) const -{ - return sign(QJsonDocument(message).toJson(QJsonDocument::Compact)); -} - -QByteArray QOlmAccount::signIdentityKeys() const -{ - const auto keys = identityKeys(); - QJsonObject body - { - {"algorithms", QJsonArray{"m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"}}, - {"user_id", m_userId}, - {"device_id", m_deviceId}, - {"keys", - QJsonObject{ - {QStringLiteral("curve25519:") + m_deviceId, QString::fromUtf8(keys.curve25519)}, - {QStringLiteral("ed25519:") + m_deviceId, QString::fromUtf8(keys.ed25519)} - } - } - }; - return sign(QJsonDocument(body).toJson(QJsonDocument::Compact)); - -} - -size_t QOlmAccount::maxNumberOfOneTimeKeys() const -{ - return olm_account_max_number_of_one_time_keys(m_account); -} - -size_t QOlmAccount::generateOneTimeKeys(size_t numberOfKeys) const -{ - const size_t randomLength = olm_account_generate_one_time_keys_random_length(m_account, numberOfKeys); - QByteArray randomBuffer = getRandom(randomLength); - const auto error = olm_account_generate_one_time_keys(m_account, numberOfKeys, randomBuffer.data(), randomLength); - - if (error == olm_error()) { - throw lastError(m_account); - } - Q_EMIT needsSave(); - return error; -} - -OneTimeKeys QOlmAccount::oneTimeKeys() const -{ - const size_t oneTimeKeyLength = olm_account_one_time_keys_length(m_account); - QByteArray oneTimeKeysBuffer(oneTimeKeyLength, '0'); - - const auto error = olm_account_one_time_keys(m_account, oneTimeKeysBuffer.data(), oneTimeKeyLength); - if (error == olm_error()) { - throw lastError(m_account); - } - const auto json = QJsonDocument::fromJson(oneTimeKeysBuffer).object(); - OneTimeKeys oneTimeKeys; - - for (const QString& key1 : json.keys()) { - auto oneTimeKeyObject = json[key1].toObject(); - auto keyMap = QMap(); - for (const QString &key2 : oneTimeKeyObject.keys()) { - keyMap[key2] = oneTimeKeyObject[key2].toString(); - } - oneTimeKeys.keys[key1] = keyMap; - } - return oneTimeKeys; -} - -QMap QOlmAccount::signOneTimeKeys(const OneTimeKeys &keys) const -{ - QMap signedOneTimeKeys; - for (const auto &keyid : keys.curve25519().keys()) { - const auto oneTimeKey = keys.curve25519()[keyid]; - QByteArray sign = signOneTimeKey(oneTimeKey); - signedOneTimeKeys["signed_curve25519:" + keyid] = signedOneTimeKey(oneTimeKey.toUtf8(), sign); - } - return signedOneTimeKeys; -} - -SignedOneTimeKey QOlmAccount::signedOneTimeKey(const QByteArray &key, const QString &signature) const -{ - SignedOneTimeKey sign{}; - sign.key = key; - sign.signatures = {{m_userId, {{"ed25519:" + m_deviceId, signature}}}}; - return sign; -} - -QByteArray QOlmAccount::signOneTimeKey(const QString &key) const -{ - QJsonDocument j(QJsonObject{{"key", key}}); - return sign(j.toJson(QJsonDocument::Compact)); -} - -std::optional QOlmAccount::removeOneTimeKeys(const QOlmSessionPtr &session) const -{ - const auto error = olm_remove_one_time_keys(m_account, session->raw()); - - if (error == olm_error()) { - return lastError(m_account); - } - Q_EMIT needsSave(); - return std::nullopt; -} - -OlmAccount *QOlmAccount::data() -{ - return m_account; -} - -DeviceKeys QOlmAccount::deviceKeys() const -{ - DeviceKeys deviceKeys; - deviceKeys.userId = m_userId; - deviceKeys.deviceId = m_deviceId; - deviceKeys.algorithms = QStringList {"m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"}; - - const auto idKeys = identityKeys(); - deviceKeys.keys["curve25519:" + m_deviceId] = idKeys.curve25519; - deviceKeys.keys["ed25519:" + m_deviceId] = idKeys.ed25519; - - const auto sign = signIdentityKeys(); - deviceKeys.signatures[m_userId]["ed25519:" + m_deviceId] = sign; - - return deviceKeys; -} - -UploadKeysJob *QOlmAccount::createUploadKeyRequest(const OneTimeKeys &oneTimeKeys) -{ - auto keys = deviceKeys(); - - if (oneTimeKeys.curve25519().isEmpty()) { - return new UploadKeysJob(keys); - } - - // Sign & append the one time keys. - auto temp = signOneTimeKeys(oneTimeKeys); - QHash oneTimeKeysSigned; - for (const auto &[keyId, key] : asKeyValueRange(temp)) { - oneTimeKeysSigned[keyId] = QVariant::fromValue(toJson(key)); - } - - return new UploadKeysJob(keys, oneTimeKeysSigned); -} - -std::variant QOlmAccount::createInboundSession(const QOlmMessage &preKeyMessage) -{ - Q_ASSERT(preKeyMessage.type() == QOlmMessage::PreKey); - return QOlmSession::createInboundSession(this, preKeyMessage); -} - -std::variant QOlmAccount::createInboundSessionFrom(const QByteArray &theirIdentityKey, const QOlmMessage &preKeyMessage) -{ - Q_ASSERT(preKeyMessage.type() == QOlmMessage::PreKey); - return QOlmSession::createInboundSessionFrom(this, theirIdentityKey, preKeyMessage); -} - -std::variant QOlmAccount::createOutboundSession(const QByteArray &theirIdentityKey, const QByteArray &theirOneTimeKey) -{ - return QOlmSession::createOutboundSession(this, theirIdentityKey, theirOneTimeKey); -} - -void QOlmAccount::markKeysAsPublished() -{ - olm_account_mark_keys_as_published(m_account); - Q_EMIT needsSave(); -} - -bool Quotient::verifyIdentitySignature(const DeviceKeys &deviceKeys, - const QString &deviceId, - const QString &userId) -{ - const auto signKeyId = "ed25519:" + deviceId; - const auto signingKey = deviceKeys.keys[signKeyId]; - const auto signature = deviceKeys.signatures[userId][signKeyId]; - - if (signature.isEmpty()) { - return false; - } - - return ed25519VerifySignature(signingKey, toJson(deviceKeys), signature); -} - -bool Quotient::ed25519VerifySignature(const QString &signingKey, - const QJsonObject &obj, - const QString &signature) -{ - if (signature.isEmpty()) { - return false; - } - QJsonObject obj1 = obj; - - obj1.remove("unsigned"); - obj1.remove("signatures"); - - auto canonicalJson = QJsonDocument(obj1).toJson(QJsonDocument::Compact); - - QByteArray signingKeyBuf = signingKey.toUtf8(); - QOlmUtility utility; - auto signatureBuf = signature.toUtf8(); - auto result = utility.ed25519Verify(signingKeyBuf, canonicalJson, signatureBuf); - if (std::holds_alternative(result)) { - return false; - } - - return std::get(result); -} diff --git a/lib/crypto/qolmaccount.h b/lib/crypto/qolmaccount.h deleted file mode 100644 index dd461e8b..00000000 --- a/lib/crypto/qolmaccount.h +++ /dev/null @@ -1,123 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - - -#pragma once - -#include "csapi/keys.h" -#include "crypto/e2ee.h" -#include "crypto/qolmerrors.h" -#include "crypto/qolmmessage.h" -#include "crypto/qolmsession.h" -#include - -struct OlmAccount; - -namespace Quotient { - -class QOlmSession; -class Connection; - -using QOlmSessionPtr = std::unique_ptr; - -//! An olm account manages all cryptographic keys used on a device. -//! \code{.cpp} -//! const auto olmAccount = new QOlmAccount(this); -//! \endcode -class QOlmAccount : public QObject -{ - Q_OBJECT -public: - QOlmAccount(const QString &userId, const QString &deviceId, QObject *parent = nullptr); - ~QOlmAccount(); - - //! Creates a new instance of OlmAccount. During the instantiation - //! the Ed25519 fingerprint key pair and the Curve25519 identity key - //! pair are generated. For more information see here. - //! This needs to be called before any other action or use unpickle() instead. - void createNewAccount(); - - //! Deserialises from encrypted Base64 that was previously obtained by pickling a `QOlmAccount`. - //! This needs to be called before any other action or use createNewAccount() instead. - void unpickle(QByteArray &pickled, const PicklingMode &mode); - - //! Serialises an OlmAccount to encrypted Base64. - std::variant pickle(const PicklingMode &mode); - - //! Returns the account's public identity keys already formatted as JSON - IdentityKeys identityKeys() const; - - //! Returns the signature of the supplied message. - QByteArray sign(const QByteArray &message) const; - QByteArray sign(const QJsonObject& message) const; - - //! Sign identity keys. - QByteArray signIdentityKeys() const; - - //! Maximum number of one time keys that this OlmAccount can - //! currently hold. - size_t maxNumberOfOneTimeKeys() const; - - //! Generates the supplied number of one time keys. - size_t generateOneTimeKeys(size_t numberOfKeys) const; - - //! Gets the OlmAccount's one time keys formatted as JSON. - OneTimeKeys oneTimeKeys() const; - - //! Sign all one time keys. - QMap signOneTimeKeys(const OneTimeKeys &keys) const; - - //! Sign one time key. - QByteArray signOneTimeKey(const QString &key) const; - - SignedOneTimeKey signedOneTimeKey(const QByteArray &key, const QString &signature) const; - - UploadKeysJob *createUploadKeyRequest(const OneTimeKeys &oneTimeKeys); - - DeviceKeys deviceKeys() const; - - //! Remove the one time key used to create the supplied session. - [[nodiscard]] std::optional removeOneTimeKeys(const QOlmSessionPtr &session) const; - - //! Creates an inbound session for sending/receiving messages from a received 'prekey' message. - //! - //! \param message An Olm pre-key message that was encrypted for this account. - std::variant createInboundSession(const QOlmMessage &preKeyMessage); - - //! Creates an inbound session for sending/receiving messages from a received 'prekey' message. - //! - //! \param theirIdentityKey - The identity key of the Olm account that - //! encrypted this Olm message. - std::variant createInboundSessionFrom(const QByteArray &theirIdentityKey, const QOlmMessage &preKeyMessage); - - //! Creates an outbound session for sending messages to a specific - /// identity and one time key. - std::variant createOutboundSession(const QByteArray &theirIdentityKey, const QByteArray &theirOneTimeKey); - - void markKeysAsPublished(); - - // HACK do not use directly - QOlmAccount(OlmAccount *account); - OlmAccount *data(); - -Q_SIGNALS: - void needsSave() const; - -private: - OlmAccount *m_account = nullptr; // owning - QString m_userId; - QString m_deviceId; -}; - -bool verifyIdentitySignature(const DeviceKeys &deviceKeys, - const QString &deviceId, - const QString &userId); - -//! checks if the signature is signed by the signing_key -bool ed25519VerifySignature(const QString &signingKey, - const QJsonObject &obj, - const QString &signature); - -} // namespace Quotient diff --git a/lib/crypto/qolmerrors.cpp b/lib/crypto/qolmerrors.cpp deleted file mode 100644 index 6db1803c..00000000 --- a/lib/crypto/qolmerrors.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - - -#include "qolmerrors.h" - -Quotient::QOlmError Quotient::fromString(const std::string &error_raw) { - if (!error_raw.compare("BAD_ACCOUNT_KEY")) { - return QOlmError::BadAccountKey; - } else if (!error_raw.compare("BAD_MESSAGE_KEY_ID")) { - return QOlmError::BadMessageKeyId; - } else if (!error_raw.compare("INVALID_BASE64")) { - return QOlmError::InvalidBase64; - } else if (!error_raw.compare("NOT_ENOUGH_RANDOM")) { - return QOlmError::NotEnoughRandom; - } else if (!error_raw.compare("OUTPUT_BUFFER_TOO_SMALL")) { - return QOlmError::OutputBufferTooSmall; - } else { - return QOlmError::Unknown; - } -} diff --git a/lib/crypto/qolmerrors.h b/lib/crypto/qolmerrors.h deleted file mode 100644 index f8390d2a..00000000 --- a/lib/crypto/qolmerrors.h +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#pragma once - -#include - -namespace Quotient { -//! All errors that could be caused by an operation regarding Olm -//! Errors are named exactly like the ones in libolm. -enum QOlmError -{ - BadAccountKey, - BadMessageFormat, - BadMessageKeyId, - BadMessageMac, - BadMessageVersion, - InvalidBase64, - NotEnoughRandom, - OutputBufferTooSmall, - UnknownMessageIndex, - Unknown, -}; - -QOlmError fromString(const std::string &error_raw); - -} //namespace Quotient diff --git a/lib/crypto/qolminboundsession.cpp b/lib/crypto/qolminboundsession.cpp deleted file mode 100644 index 31d699f1..00000000 --- a/lib/crypto/qolminboundsession.cpp +++ /dev/null @@ -1,153 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#include "crypto/qolminboundsession.h" -#include -#include -using namespace Quotient; - -QOlmError lastError(OlmInboundGroupSession *session) { - const std::string error_raw = olm_inbound_group_session_last_error(session); - - return fromString(error_raw); -} - -QOlmInboundGroupSession::QOlmInboundGroupSession(OlmInboundGroupSession *session) - : m_groupSession(session) -{ -} - -QOlmInboundGroupSession::~QOlmInboundGroupSession() -{ - olm_clear_inbound_group_session(m_groupSession); - //delete[](reinterpret_cast(m_groupSession)); -} - -std::unique_ptr QOlmInboundGroupSession::create(const QByteArray &key) -{ - const auto olmInboundGroupSession = olm_inbound_group_session(new uint8_t[olm_inbound_group_session_size()]); - const auto temp = key; - const auto error = olm_init_inbound_group_session(olmInboundGroupSession, - reinterpret_cast(temp.data()), temp.size()); - - if (error == olm_error()) { - throw lastError(olmInboundGroupSession); - } - - return std::make_unique(olmInboundGroupSession); -} - -std::unique_ptr QOlmInboundGroupSession::import(const QByteArray &key) -{ - const auto olmInboundGroupSession = olm_inbound_group_session(new uint8_t[olm_inbound_group_session_size()]); - QByteArray keyBuf = key; - - const auto error = olm_import_inbound_group_session(olmInboundGroupSession, - reinterpret_cast(keyBuf.data()), keyBuf.size()); - if (error == olm_error()) { - throw lastError(olmInboundGroupSession); - } - - return std::make_unique(olmInboundGroupSession); -} - -QByteArray toKey(const PicklingMode &mode) -{ - if (std::holds_alternative(mode)) { - return ""; - } - return std::get(mode).key; -} - -QByteArray QOlmInboundGroupSession::pickle(const PicklingMode &mode) const -{ - QByteArray pickledBuf(olm_pickle_inbound_group_session_length(m_groupSession), '0'); - const QByteArray key = toKey(mode); - const auto error = olm_pickle_inbound_group_session(m_groupSession, key.data(), key.length(), pickledBuf.data(), - pickledBuf.length()); - if (error == olm_error()) { - throw lastError(m_groupSession); - } - return pickledBuf; -} - -std::variant, QOlmError> QOlmInboundGroupSession::unpickle(const QByteArray &pickled, const PicklingMode &mode) -{ - QByteArray pickledBuf = pickled; - const auto groupSession = olm_inbound_group_session(new uint8_t[olm_inbound_group_session_size()]); - QByteArray key = toKey(mode); - const auto error = olm_unpickle_inbound_group_session(groupSession, key.data(), key.length(), - pickledBuf.data(), pickledBuf.size()); - if (error == olm_error()) { - return lastError(groupSession); - } - key.clear(); - - return std::make_unique(groupSession); -} - -std::variant, QOlmError> QOlmInboundGroupSession::decrypt(const QByteArray &message) -{ - // This is for capturing the output of olm_group_decrypt - uint32_t messageIndex = 0; - - // We need to clone the message because - // olm_decrypt_max_plaintext_length destroys the input buffer - QByteArray messageBuf(message.length(), '0'); - std::copy(message.begin(), message.end(), messageBuf.begin()); - - QByteArray plaintextBuf(olm_group_decrypt_max_plaintext_length(m_groupSession, - reinterpret_cast(messageBuf.data()), messageBuf.length()), '0'); - - messageBuf = QByteArray(message.length(), '0'); - std::copy(message.begin(), message.end(), messageBuf.begin()); - - const auto plaintextLen = olm_group_decrypt(m_groupSession, reinterpret_cast(messageBuf.data()), - messageBuf.length(), reinterpret_cast(plaintextBuf.data()), plaintextBuf.length(), &messageIndex); - - // Error code or plaintext length is returned - const auto decryptError = plaintextLen; - - if (decryptError == olm_error()) { - return lastError(m_groupSession); - } - - QByteArray output(plaintextLen, '0'); - std::memcpy(output.data(), plaintextBuf.data(), plaintextLen); - - return std::make_pair(QString(output), messageIndex); -} - -std::variant QOlmInboundGroupSession::exportSession(uint32_t messageIndex) -{ - const auto keyLength = olm_export_inbound_group_session_length(m_groupSession); - QByteArray keyBuf(keyLength, '0'); - const auto error = olm_export_inbound_group_session(m_groupSession, reinterpret_cast(keyBuf.data()), keyLength, messageIndex); - - if (error == olm_error()) { - return lastError(m_groupSession); - } - return keyBuf; -} - -uint32_t QOlmInboundGroupSession::firstKnownIndex() const -{ - return olm_inbound_group_session_first_known_index(m_groupSession); -} - -QByteArray QOlmInboundGroupSession::sessionId() const -{ - QByteArray sessionIdBuf(olm_inbound_group_session_id_length(m_groupSession), '0'); - const auto error = olm_inbound_group_session_id(m_groupSession, reinterpret_cast(sessionIdBuf.data()), - sessionIdBuf.length()); - if (error == olm_error()) { - throw lastError(m_groupSession); - } - return sessionIdBuf; -} - -bool QOlmInboundGroupSession::isVerified() const -{ - return olm_inbound_group_session_is_verified(m_groupSession) != 0; -} diff --git a/lib/crypto/qolminboundsession.h b/lib/crypto/qolminboundsession.h deleted file mode 100644 index 362e42ba..00000000 --- a/lib/crypto/qolminboundsession.h +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#pragma once - -#include -#include -#include -#include "olm/olm.h" -#include "crypto/qolmerrors.h" -#include "crypto/e2ee.h" - -namespace Quotient { - -//! An in-bound group session is responsible for decrypting incoming -//! communication in a Megolm session. -struct QOlmInboundGroupSession -{ -public: - ~QOlmInboundGroupSession(); - //! Creates a new instance of `OlmInboundGroupSession`. - static std::unique_ptr create(const QByteArray &key); - //! Import an inbound group session, from a previous export. - static std::unique_ptr import(const QByteArray &key); - //! Serialises an `OlmInboundGroupSession` to encrypted Base64. - QByteArray pickle(const PicklingMode &mode) const; - //! Deserialises from encrypted Base64 that was previously obtained by pickling - //! an `OlmInboundGroupSession`. - static std::variant, QOlmError> unpickle(const QByteArray &picked, const PicklingMode &mode); - //! Decrypts ciphertext received for this group session. - std::variant, QOlmError> decrypt(const QByteArray &message); - //! Export the base64-encoded ratchet key for this session, at the given index, - //! in a format which can be used by import. - std::variant exportSession(uint32_t messageIndex); - //! Get the first message index we know how to decrypt. - uint32_t firstKnownIndex() const; - //! Get a base64-encoded identifier for this session. - QByteArray sessionId() const; - bool isVerified() const; - QOlmInboundGroupSession(OlmInboundGroupSession *session); -private: - OlmInboundGroupSession *m_groupSession; -}; - -using QOlmInboundGroupSessionPtr = std::unique_ptr; -using OlmInboundGroupSessionPtr = std::unique_ptr; -} // namespace Quotient diff --git a/lib/crypto/qolmmessage.cpp b/lib/crypto/qolmmessage.cpp deleted file mode 100644 index 15008b75..00000000 --- a/lib/crypto/qolmmessage.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Alexey Andreyev -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#include "qolmmessage.h" - -using namespace Quotient; - -QOlmMessage::QOlmMessage(const QByteArray &ciphertext, QOlmMessage::Type type) - : QByteArray(std::move(ciphertext)) - , m_messageType(type) -{ - Q_ASSERT_X(!ciphertext.isEmpty(), "olm message", "Ciphertext is empty"); -} - -QOlmMessage::QOlmMessage(const QOlmMessage &message) - : QByteArray(message) - , m_messageType(message.type()) -{ -} - -QOlmMessage::Type QOlmMessage::type() const -{ - return m_messageType; -} - -QByteArray QOlmMessage::toCiphertext() const -{ - return QByteArray(*this); -} - -QOlmMessage QOlmMessage::fromCiphertext(const QByteArray &ciphertext) -{ - return QOlmMessage(ciphertext, QOlmMessage::General); -} diff --git a/lib/crypto/qolmmessage.h b/lib/crypto/qolmmessage.h deleted file mode 100644 index 52aba78c..00000000 --- a/lib/crypto/qolmmessage.h +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Alexey Andreyev -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#pragma once - -#include -#include - -namespace Quotient { - -/*! \brief A wrapper around an olm encrypted message - * - * This class encapsulates a Matrix olm encrypted message, - * passed in either of 2 forms: a general message or a pre-key message. - * - * The class provides functions to get a type and the ciphertext. - */ -class QOlmMessage : public QByteArray { - Q_GADGET -public: - enum Type { - General, - PreKey, - }; - Q_ENUM(Type) - - QOlmMessage() = default; - explicit QOlmMessage(const QByteArray &ciphertext, Type type = General); - explicit QOlmMessage(const QOlmMessage &message); - - static QOlmMessage fromCiphertext(const QByteArray &ciphertext); - - Q_INVOKABLE Type type() const; - Q_INVOKABLE QByteArray toCiphertext() const; - -private: - Type m_messageType = General; -}; - -} //namespace Quotient diff --git a/lib/crypto/qolmoutboundsession.cpp b/lib/crypto/qolmoutboundsession.cpp deleted file mode 100644 index bc572ba5..00000000 --- a/lib/crypto/qolmoutboundsession.cpp +++ /dev/null @@ -1,128 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#include "qolmoutboundsession.h" -#include "crypto/qolmutils.h" - -using namespace Quotient; - -QOlmError lastError(OlmOutboundGroupSession *session) { - const std::string error_raw = olm_outbound_group_session_last_error(session); - - return fromString(error_raw); -} - -QOlmOutboundGroupSession::QOlmOutboundGroupSession(OlmOutboundGroupSession *session) - : m_groupSession(session) -{ -} - -QOlmOutboundGroupSession::~QOlmOutboundGroupSession() -{ - olm_clear_outbound_group_session(m_groupSession); - delete[](reinterpret_cast(m_groupSession)); -} - -std::unique_ptr QOlmOutboundGroupSession::create() -{ - auto *olmOutboundGroupSession = olm_outbound_group_session(new uint8_t[olm_outbound_group_session_size()]); - const auto randomLength = olm_init_outbound_group_session_random_length(olmOutboundGroupSession); - QByteArray randomBuf = getRandom(randomLength); - - const auto error = olm_init_outbound_group_session(olmOutboundGroupSession, - reinterpret_cast(randomBuf.data()), randomBuf.length()); - - if (error == olm_error()) { - throw lastError(olmOutboundGroupSession); - } - - const auto keyMaxLength = olm_outbound_group_session_key_length(olmOutboundGroupSession); - QByteArray keyBuffer(keyMaxLength, '0'); - olm_outbound_group_session_key(olmOutboundGroupSession, reinterpret_cast(keyBuffer.data()), - keyMaxLength); - - randomBuf.clear(); - - return std::make_unique(olmOutboundGroupSession); -} - -std::variant QOlmOutboundGroupSession::pickle(const PicklingMode &mode) -{ - QByteArray pickledBuf(olm_pickle_outbound_group_session_length(m_groupSession), '0'); - QByteArray key = toKey(mode); - const auto error = olm_pickle_outbound_group_session(m_groupSession, key.data(), key.length(), - pickledBuf.data(), pickledBuf.length()); - - if (error == olm_error()) { - return lastError(m_groupSession); - } - - key.clear(); - - return pickledBuf; -} - - -std::variant, QOlmError> QOlmOutboundGroupSession::unpickle(QByteArray &pickled, const PicklingMode &mode) -{ - QByteArray pickledBuf = pickled; - auto *olmOutboundGroupSession = olm_outbound_group_session(new uint8_t[olm_outbound_group_session_size()]); - QByteArray key = toKey(mode); - const auto error = olm_unpickle_outbound_group_session(olmOutboundGroupSession, key.data(), key.length(), - pickled.data(), pickled.length()); - if (error == olm_error()) { - return lastError(olmOutboundGroupSession); - } - const auto idMaxLength = olm_outbound_group_session_id_length(olmOutboundGroupSession); - QByteArray idBuffer(idMaxLength, '0'); - olm_outbound_group_session_id(olmOutboundGroupSession, reinterpret_cast(idBuffer.data()), - idBuffer.length()); - - key.clear(); - return std::make_unique(olmOutboundGroupSession); -} - -std::variant QOlmOutboundGroupSession::encrypt(const QString &plaintext) -{ - QByteArray plaintextBuf = plaintext.toUtf8(); - const auto messageMaxLength = olm_group_encrypt_message_length(m_groupSession, plaintextBuf.length()); - QByteArray messageBuf(messageMaxLength, '0'); - const auto error = olm_group_encrypt(m_groupSession, reinterpret_cast(plaintextBuf.data()), - plaintextBuf.length(), reinterpret_cast(messageBuf.data()), messageBuf.length()); - - if (error == olm_error()) { - return lastError(m_groupSession); - } - - return messageBuf; -} - -uint32_t QOlmOutboundGroupSession::sessionMessageIndex() const -{ - return olm_outbound_group_session_message_index(m_groupSession); -} - -QByteArray QOlmOutboundGroupSession::sessionId() const -{ - const auto idMaxLength = olm_outbound_group_session_id_length(m_groupSession); - QByteArray idBuffer(idMaxLength, '0'); - const auto error = olm_outbound_group_session_id(m_groupSession, reinterpret_cast(idBuffer.data()), - idBuffer.length()); - if (error == olm_error()) { - throw lastError(m_groupSession); - } - return idBuffer; -} - -std::variant QOlmOutboundGroupSession::sessionKey() const -{ - const auto keyMaxLength = olm_outbound_group_session_key_length(m_groupSession); - QByteArray keyBuffer(keyMaxLength, '0'); - const auto error = olm_outbound_group_session_key(m_groupSession, reinterpret_cast(keyBuffer.data()), - keyMaxLength); - if (error == olm_error()) { - return lastError(m_groupSession); - } - return keyBuffer; -} diff --git a/lib/crypto/qolmoutboundsession.h b/lib/crypto/qolmoutboundsession.h deleted file mode 100644 index 4e06561e..00000000 --- a/lib/crypto/qolmoutboundsession.h +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - - -#pragma once - -#include "olm/olm.h" -#include "crypto/qolmerrors.h" -#include "crypto/e2ee.h" -#include - -namespace Quotient { - - -//! An out-bound group session is responsible for encrypting outgoing -//! communication in a Megolm session. -class QOlmOutboundGroupSession -{ -public: - ~QOlmOutboundGroupSession(); - //! Creates a new instance of `QOlmOutboundGroupSession`. - //! Throw OlmError on errors - static std::unique_ptr create(); - //! Serialises a `QOlmOutboundGroupSession` to encrypted Base64. - std::variant pickle(const PicklingMode &mode); - //! Deserialises from encrypted Base64 that was previously obtained by - //! pickling a `QOlmOutboundGroupSession`. - static std::variant, QOlmError> unpickle(QByteArray &pickled, const PicklingMode &mode); - //! Encrypts a plaintext message using the session. - std::variant encrypt(const QString &plaintext); - - //! Get the current message index for this session. - //! - //! Each message is sent with an increasing index; this returns the - //! index for the next message. - uint32_t sessionMessageIndex() const; - - //! Get a base64-encoded identifier for this session. - QByteArray sessionId() const; - - //! Get the base64-encoded current ratchet key for this session. - //! - //! Each message is sent with a different ratchet key. This function returns the - //! ratchet key that will be used for the next message. - std::variant sessionKey() const; - QOlmOutboundGroupSession(OlmOutboundGroupSession *groupSession); -private: - OlmOutboundGroupSession *m_groupSession; -}; - -using QOlmOutboundGroupSessionPtr = std::unique_ptr; -using OlmOutboundGroupSessionPtr = std::unique_ptr; -} diff --git a/lib/crypto/qolmsession.cpp b/lib/crypto/qolmsession.cpp deleted file mode 100644 index a0386613..00000000 --- a/lib/crypto/qolmsession.cpp +++ /dev/null @@ -1,253 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Alexey Andreyev -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#include "qolmsession.h" -#include "crypto/qolmutils.h" -#include "logging.h" -#include -#include - -using namespace Quotient; - -QOlmError lastError(OlmSession* session) { - const std::string error_raw = olm_session_last_error(session); - - return fromString(error_raw); -} - -Quotient::QOlmSession::~QOlmSession() -{ - olm_clear_session(m_session); - delete[](reinterpret_cast(m_session)); -} - -OlmSession* QOlmSession::create() -{ - return olm_session(new uint8_t[olm_session_size()]); -} - -std::variant QOlmSession::createInbound(QOlmAccount *account, const QOlmMessage &preKeyMessage, bool from, const QString &theirIdentityKey) -{ - if (preKeyMessage.type() != QOlmMessage::PreKey) { - qCCritical(E2EE) << "The message is not a pre-key in when creating inbound session" << BadMessageFormat; - } - - const auto olmSession = create(); - - QByteArray oneTimeKeyMessageBuf = preKeyMessage.toCiphertext(); - QByteArray theirIdentityKeyBuf = theirIdentityKey.toUtf8(); - size_t error = 0; - if (from) { - error = olm_create_inbound_session_from(olmSession, account->data(), theirIdentityKeyBuf.data(), theirIdentityKeyBuf.length(), oneTimeKeyMessageBuf.data(), oneTimeKeyMessageBuf.length()); - } else { - error = olm_create_inbound_session(olmSession, account->data(), oneTimeKeyMessageBuf.data(), oneTimeKeyMessageBuf.length()); - } - - if (error == olm_error()) { - const auto lastErr = lastError(olmSession); - qCWarning(E2EE) << "Error when creating inbound session" << lastErr; - return lastErr; - } - - return std::make_unique(olmSession); -} - -std::variant QOlmSession::createInboundSession(QOlmAccount *account, const QOlmMessage &preKeyMessage) -{ - return createInbound(account, preKeyMessage); -} - -std::variant QOlmSession::createInboundSessionFrom(QOlmAccount *account, const QString &theirIdentityKey, const QOlmMessage &preKeyMessage) -{ - return createInbound(account, preKeyMessage, true, theirIdentityKey); -} - -std::variant QOlmSession::createOutboundSession(QOlmAccount *account, const QString &theirIdentityKey, const QString &theirOneTimeKey) -{ - auto *olmOutboundSession = create(); - const auto randomLen = olm_create_outbound_session_random_length(olmOutboundSession); - QByteArray randomBuf = getRandom(randomLen); - - QByteArray theirIdentityKeyBuf = theirIdentityKey.toUtf8(); - QByteArray theirOneTimeKeyBuf = theirOneTimeKey.toUtf8(); - const auto error = olm_create_outbound_session(olmOutboundSession, - account->data(), - reinterpret_cast(theirIdentityKeyBuf.data()), theirIdentityKeyBuf.length(), - reinterpret_cast(theirOneTimeKeyBuf.data()), theirOneTimeKeyBuf.length(), - reinterpret_cast(randomBuf.data()), randomBuf.length()); - - if (error == olm_error()) { - const auto lastErr = lastError(olmOutboundSession); - if (lastErr == QOlmError::NotEnoughRandom) { - throw lastErr; - } - return lastErr; - } - - randomBuf.clear(); - return std::make_unique(olmOutboundSession); -} - -std::variant QOlmSession::pickle(const PicklingMode &mode) -{ - QByteArray pickledBuf(olm_pickle_session_length(m_session), '0'); - QByteArray key = toKey(mode); - const auto error = olm_pickle_session(m_session, key.data(), key.length(), - pickledBuf.data(), pickledBuf.length()); - - if (error == olm_error()) { - return lastError(m_session); - } - - key.clear(); - - return pickledBuf; -} - -std::variant QOlmSession::unpickle(const QByteArray &pickled, const PicklingMode &mode) -{ - QByteArray pickledBuf = pickled; - auto *olmSession = create(); - QByteArray key = toKey(mode); - const auto error = olm_unpickle_session(olmSession, key.data(), key.length(), - pickledBuf.data(), pickledBuf.length()); - if (error == olm_error()) { - return lastError(olmSession); - } - - key.clear(); - return std::make_unique(olmSession); -} - -QOlmMessage QOlmSession::encrypt(const QString &plaintext) -{ - QByteArray plaintextBuf = plaintext.toUtf8(); - const auto messageMaxLen = olm_encrypt_message_length(m_session, plaintextBuf.length()); - QByteArray messageBuf(messageMaxLen, '0'); - const auto messageType = encryptMessageType(); - const auto randomLen = olm_encrypt_random_length(m_session); - QByteArray randomBuf = getRandom(randomLen); - const auto error = olm_encrypt(m_session, - reinterpret_cast(plaintextBuf.data()), plaintextBuf.length(), - reinterpret_cast(randomBuf.data()), randomBuf.length(), - reinterpret_cast(messageBuf.data()), messageBuf.length()); - - if (error == olm_error()) { - throw lastError(m_session); - } - - return QOlmMessage(messageBuf, messageType); -} - -std::variant QOlmSession::decrypt(const QOlmMessage &message) const -{ - const auto messageType = message.type(); - const auto ciphertext = message.toCiphertext(); - const auto messageTypeValue = messageType == QOlmMessage::Type::General - ? OLM_MESSAGE_TYPE_MESSAGE : OLM_MESSAGE_TYPE_PRE_KEY; - - // We need to clone the message because - // olm_decrypt_max_plaintext_length destroys the input buffer - QByteArray messageBuf(ciphertext.length(), '0'); - std::copy(message.begin(), message.end(), messageBuf.begin()); - - const auto plaintextMaxLen = olm_decrypt_max_plaintext_length(m_session, messageTypeValue, - reinterpret_cast(messageBuf.data()), messageBuf.length()); - - if (plaintextMaxLen == olm_error()) { - return lastError(m_session); - } - - QByteArray plaintextBuf(plaintextMaxLen, '0'); - QByteArray messageBuf2(ciphertext.length(), '0'); - std::copy(message.begin(), message.end(), messageBuf2.begin()); - - const auto plaintextResultLen = olm_decrypt(m_session, messageTypeValue, - reinterpret_cast(messageBuf2.data()), messageBuf2.length(), - reinterpret_cast(plaintextBuf.data()), plaintextMaxLen); - - if (plaintextResultLen == olm_error()) { - const auto lastErr = lastError(m_session); - if (lastErr == QOlmError::OutputBufferTooSmall) { - throw lastErr; - } - return lastErr; - } - QByteArray output(plaintextResultLen, '0'); - std::memcpy(output.data(), plaintextBuf.data(), plaintextResultLen); - plaintextBuf.clear(); - return output; -} - -QOlmMessage::Type QOlmSession::encryptMessageType() -{ - const auto messageTypeResult = olm_encrypt_message_type(m_session); - if (messageTypeResult == olm_error()) { - throw lastError(m_session); - } - if (messageTypeResult == OLM_MESSAGE_TYPE_PRE_KEY) { - return QOlmMessage::PreKey; - } - return QOlmMessage::General; -} - -QByteArray QOlmSession::sessionId() const -{ - const auto idMaxLength = olm_session_id_length(m_session); - QByteArray idBuffer(idMaxLength, '0'); - const auto error = olm_session_id(m_session, reinterpret_cast(idBuffer.data()), - idBuffer.length()); - if (error == olm_error()) { - throw lastError(m_session); - } - return idBuffer; -} - -bool QOlmSession::hasReceivedMessage() const -{ - return olm_session_has_received_message(m_session); -} - -std::variant QOlmSession::matchesInboundSession(const QOlmMessage &preKeyMessage) const -{ - Q_ASSERT(preKeyMessage.type() == QOlmMessage::Type::PreKey); - QByteArray oneTimeKeyBuf(preKeyMessage.data()); - const auto matchesResult = olm_matches_inbound_session(m_session, oneTimeKeyBuf.data(), oneTimeKeyBuf.length()); - - if (matchesResult == olm_error()) { - return lastError(m_session); - } - switch (matchesResult) { - case 0: - return false; - case 1: - return true; - default: - return QOlmError::Unknown; - } -} -std::variant QOlmSession::matchesInboundSessionFrom(const QString &theirIdentityKey, const QOlmMessage &preKeyMessage) const -{ - const auto theirIdentityKeyBuf = theirIdentityKey.toUtf8(); - auto oneTimeKeyMessageBuf = preKeyMessage.toCiphertext(); - const auto error = olm_matches_inbound_session_from(m_session, theirIdentityKeyBuf.data(), theirIdentityKeyBuf.length(), - oneTimeKeyMessageBuf.data(), oneTimeKeyMessageBuf.length()); - - if (error == olm_error()) { - return lastError(m_session); - } - switch (error) { - case 0: - return false; - case 1: - return true; - default: - return QOlmError::Unknown; - } -} - -QOlmSession::QOlmSession(OlmSession *session) - : m_session(session) -{ -} diff --git a/lib/crypto/qolmsession.h b/lib/crypto/qolmsession.h deleted file mode 100644 index 711ca66b..00000000 --- a/lib/crypto/qolmsession.h +++ /dev/null @@ -1,76 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Alexey Andreyev -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#pragma once - -#include -#include // FIXME: OlmSession -#include "crypto/e2ee.h" -#include "crypto/qolmmessage.h" -#include "crypto/qolmerrors.h" -#include "crypto/qolmaccount.h" - -namespace Quotient { - -class QOlmAccount; -class QOlmSession; - - -//! Either an outbound or inbound session for secure communication. -class QOlmSession -{ -public: - ~QOlmSession(); - //! Creates an inbound session for sending/receiving messages from a received 'prekey' message. - static std::variant, QOlmError> createInboundSession(QOlmAccount *account, const QOlmMessage &preKeyMessage); - static std::variant, QOlmError> createInboundSessionFrom(QOlmAccount *account, const QString &theirIdentityKey, const QOlmMessage &preKeyMessage); - static std::variant, QOlmError> createOutboundSession(QOlmAccount *account, const QString &theirIdentityKey, const QString &theirOneTimeKey); - //! Serialises an `QOlmSession` to encrypted Base64. - std::variant pickle(const PicklingMode &mode); - //! Deserialises from encrypted Base64 that was previously obtained by pickling a `QOlmSession`. - static std::variant, QOlmError> unpickle(const QByteArray &pickled, const PicklingMode &mode); - //! Encrypts a plaintext message using the session. - QOlmMessage encrypt(const QString &plaintext); - - //! Decrypts a message using this session. Decoding is lossy, meaing if - //! the decrypted plaintext contains invalid UTF-8 symbols, they will - //! be returned as `U+FFFD` (�). - std::variant decrypt(const QOlmMessage &message) const; - - //! Get a base64-encoded identifier for this session. - QByteArray sessionId() const; - - //! The type of the next message that will be returned from encryption. - QOlmMessage::Type encryptMessageType(); - - //! Checker for any received messages for this session. - bool hasReceivedMessage() const; - - //! Checks if the 'prekey' message is for this in-bound session. - std::variant matchesInboundSession(const QOlmMessage &preKeyMessage) const; - - //! Checks if the 'prekey' message is for this in-bound session. - std::variant matchesInboundSessionFrom(const QString &theirIdentityKey, const QOlmMessage &preKeyMessage) const; - - friend bool operator<(const QOlmSession& lhs, const QOlmSession& rhs) - { - return lhs.sessionId() < rhs.sessionId(); - } - - friend bool operator<(const std::unique_ptr &lhs, const std::unique_ptr &rhs) { - return *lhs < *rhs; - } - - OlmSession *raw() const - { - return m_session; - } - QOlmSession(OlmSession* session); -private: - //! Helper function for creating new sessions and handling errors. - static OlmSession* create(); - static std::variant, QOlmError> createInbound(QOlmAccount *account, const QOlmMessage& preKeyMessage, bool from = false, const QString& theirIdentityKey = ""); - OlmSession* m_session; -}; -} //namespace Quotient diff --git a/lib/crypto/qolmutility.cpp b/lib/crypto/qolmutility.cpp deleted file mode 100644 index bb50b4d0..00000000 --- a/lib/crypto/qolmutility.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#include "crypto/qolmutility.h" -#include "olm/olm.h" -#include - -using namespace Quotient; - -// Convert olm error to enum -QOlmError lastError(OlmUtility *utility) { - const std::string error_raw = olm_utility_last_error(utility); - - return fromString(error_raw); -} - -QOlmUtility::QOlmUtility() -{ - auto utility = new uint8_t[olm_utility_size()]; - m_utility = olm_utility(utility); -} - -QOlmUtility::~QOlmUtility() -{ - olm_clear_utility(m_utility); - delete[](reinterpret_cast(m_utility)); -} - -QString QOlmUtility::sha256Bytes(const QByteArray &inputBuf) const -{ - const auto outputLen = olm_sha256_length(m_utility); - QByteArray outputBuf(outputLen, '0'); - olm_sha256(m_utility, inputBuf.data(), inputBuf.length(), - outputBuf.data(), outputBuf.length()); - - return QString::fromUtf8(outputBuf); -} - -QString QOlmUtility::sha256Utf8Msg(const QString &message) const -{ - return sha256Bytes(message.toUtf8()); -} - -std::variant QOlmUtility::ed25519Verify(const QByteArray &key, - const QByteArray &message, const QByteArray &signature) -{ - QByteArray signatureBuf(signature.length(), '0'); - std::copy(signature.begin(), signature.end(), signatureBuf.begin()); - - const auto ret = olm_ed25519_verify(m_utility, key.data(), key.size(), - message.data(), message.size(), (void *)signatureBuf.data(), signatureBuf.size()); - - const auto error = ret; - if (error == olm_error()) { - return lastError(m_utility); - } - - if (ret != 0) { - return false; - } - return true; -} diff --git a/lib/crypto/qolmutility.h b/lib/crypto/qolmutility.h deleted file mode 100644 index 5fd28dcc..00000000 --- a/lib/crypto/qolmutility.h +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#pragma once - -#include -#include -#include "crypto/qolmerrors.h" - -struct OlmUtility; - -namespace Quotient { - -class QOlmSession; -class Connection; - -//! Allows you to make use of crytographic hashing via SHA-2 and -//! verifying ed25519 signatures. -class QOlmUtility -{ -public: - QOlmUtility(); - ~QOlmUtility(); - - //! Returns a sha256 of the supplied byte slice. - QString sha256Bytes(const QByteArray &inputBuf) const; - - //! Convenience function that converts the UTF-8 message - //! to bytes and then calls `sha256Bytes()`, returning its output. - QString sha256Utf8Msg(const QString &message) const; - - //! Verify a ed25519 signature. - //! \param key QByteArray The public part of the ed25519 key that signed the message. - //! \param message QByteArray The message that was signed. - //! \param signature QByteArray The signature of the message. - std::variant ed25519Verify(const QByteArray &key, - const QByteArray &message, const QByteArray &signature); - - -private: - OlmUtility *m_utility; - -}; -} diff --git a/lib/crypto/qolmutils.cpp b/lib/crypto/qolmutils.cpp deleted file mode 100644 index cd5ac83c..00000000 --- a/lib/crypto/qolmutils.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#include "crypto/qolmutils.h" -#include -#include - -using namespace Quotient; - -QByteArray Quotient::toKey(const Quotient::PicklingMode &mode) -{ - if (std::holds_alternative(mode)) { - return {}; - } - return std::get(mode).key; -} - -QByteArray Quotient::getRandom(size_t bufferSize) -{ - QByteArray buffer(bufferSize, '0'); - RAND_bytes(reinterpret_cast(buffer.data()), buffer.size()); - return buffer; -} diff --git a/lib/crypto/qolmutils.h b/lib/crypto/qolmutils.h deleted file mode 100644 index 8b1c01ce..00000000 --- a/lib/crypto/qolmutils.h +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#pragma once - -#include - -#include "crypto/e2ee.h" - -namespace Quotient { -// Convert PicklingMode to key -QByteArray toKey(const PicklingMode &mode); -QByteArray getRandom(size_t bufferSize); -} diff --git a/lib/database.cpp b/lib/database.cpp index 153aab31..ec285d22 100644 --- a/lib/database.cpp +++ b/lib/database.cpp @@ -10,9 +10,9 @@ #include #include -#include "crypto/e2ee.h" -#include "crypto/qolmsession.h" -#include "crypto/qolminboundsession.h" +#include "e2ee/e2ee.h" +#include "e2ee/qolmsession.h" +#include "e2ee/qolminboundsession.h" //TODO: delete room specific data when leaving room diff --git a/lib/database.h b/lib/database.h index ed356820..8f8cd6cd 100644 --- a/lib/database.h +++ b/lib/database.h @@ -7,7 +7,7 @@ #include #include -#include "crypto/e2ee.h" +#include "e2ee/e2ee.h" namespace Quotient { class Database : public QObject diff --git a/lib/e2ee/e2ee.h b/lib/e2ee/e2ee.h new file mode 100644 index 00000000..41cd2878 --- /dev/null +++ b/lib/e2ee/e2ee.h @@ -0,0 +1,132 @@ +// SPDX-FileCopyrightText: 2019 Alexey Andreyev +// SPDX-FileCopyrightText: 2019 Kitsune Ral +// SPDX-FileCopyrightText: 2021 Carl Schwan +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include +#include +#include "converters.h" +#include + +#include +#include +#include +#include + +#include "util.h" + +namespace Quotient { + +inline const auto CiphertextKeyL = "ciphertext"_ls; +inline const auto SenderKeyKeyL = "sender_key"_ls; +inline const auto DeviceIdKeyL = "device_id"_ls; +inline const auto SessionIdKeyL = "session_id"_ls; + +inline const auto AlgorithmKeyL = "algorithm"_ls; +inline const auto RotationPeriodMsKeyL = "rotation_period_ms"_ls; +inline const auto RotationPeriodMsgsKeyL = "rotation_period_msgs"_ls; + +inline const auto AlgorithmKey = QStringLiteral("algorithm"); +inline const auto RotationPeriodMsKey = QStringLiteral("rotation_period_ms"); +inline const auto RotationPeriodMsgsKey = + QStringLiteral("rotation_period_msgs"); + +inline const auto Ed25519Key = QStringLiteral("ed25519"); +inline const auto Curve25519Key = QStringLiteral("curve25519"); +inline const auto SignedCurve25519Key = QStringLiteral("signed_curve25519"); +inline const auto OlmV1Curve25519AesSha2AlgoKey = + QStringLiteral("m.olm.v1.curve25519-aes-sha2"); +inline const auto MegolmV1AesSha2AlgoKey = + QStringLiteral("m.megolm.v1.aes-sha2"); +inline const QStringList SupportedAlgorithms = { OlmV1Curve25519AesSha2AlgoKey, + MegolmV1AesSha2AlgoKey }; +struct Unencrypted {}; +struct Encrypted { + QByteArray key; +}; + +using PicklingMode = std::variant; + +class QOlmSession; +using QOlmSessionPtr = std::unique_ptr; + +class QOlmInboundGroupSession; +using QOlmInboundGroupSessionPtr = std::unique_ptr; + +template struct overloaded : Ts... { using Ts::operator()...; }; +template overloaded(Ts...) -> overloaded; + +struct IdentityKeys +{ + QByteArray curve25519; + QByteArray ed25519; +}; + +//! Struct representing the one-time keys. +struct OneTimeKeys +{ + QMap> keys; + + //! Get the HashMap containing the curve25519 one-time keys. + QMap curve25519() const; + + //! Get a reference to the hashmap corresponding to given key type. + std::optional> get(QString keyType) const; +}; + +//! Struct representing the signed one-time keys. +class SignedOneTimeKey +{ +public: + SignedOneTimeKey() = default; + SignedOneTimeKey(const SignedOneTimeKey &) = default; + SignedOneTimeKey &operator=(const SignedOneTimeKey &) = default; + //! Required. The unpadded Base64-encoded 32-byte Curve25519 public key. + QString key; + + //! Required. Signatures of the key object. + //! The signature is calculated using the process described at Signing JSON. + QHash> signatures; +}; + + +template <> +struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + SignedOneTimeKey& result) + { + fromJson(jo.value("key"_ls), result.key); + fromJson(jo.value("signatures"_ls), result.signatures); + } + + static void dumpTo(QJsonObject &jo, const SignedOneTimeKey &result) + { + addParam<>(jo, QStringLiteral("key"), result.key); + addParam<>(jo, QStringLiteral("signatures"), result.signatures); + } +}; + +bool operator==(const IdentityKeys& lhs, const IdentityKeys& rhs); + +template +class asKeyValueRange +{ +public: + asKeyValueRange(T &data) + : m_data{data} + { + } + + auto begin() { return m_data.keyValueBegin(); } + + auto end() { return m_data.keyValueEnd(); } + +private: + T &m_data; +}; + +} // namespace Quotient + +Q_DECLARE_METATYPE(Quotient::SignedOneTimeKey) diff --git a/lib/e2ee/qolmaccount.cpp b/lib/e2ee/qolmaccount.cpp new file mode 100644 index 00000000..aaf51946 --- /dev/null +++ b/lib/e2ee/qolmaccount.cpp @@ -0,0 +1,330 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "qolmaccount.h" +#include "connection.h" +#include "csapi/keys.h" +#include "e2ee/qolmutils.h" +#include "e2ee/qolmutility.h" +#include +#include +#include +#include + +using namespace Quotient; + +QMap OneTimeKeys::curve25519() const +{ + return keys[QStringLiteral("curve25519")]; +} + +std::optional> OneTimeKeys::get(QString keyType) const +{ + if (!keys.contains(keyType)) { + return std::nullopt; + } + return keys[keyType]; +} + +bool operator==(const IdentityKeys& lhs, const IdentityKeys& rhs) +{ + return lhs.curve25519 == rhs.curve25519 &&& lhs.ed25519 == rhs.ed25519; +} + +// Convert olm error to enum +QOlmError lastError(OlmAccount *account) { + const std::string error_raw = olm_account_last_error(account); + + return fromString(error_raw); +} + +QByteArray getRandom(size_t bufferSize) +{ + QByteArray buffer(bufferSize, '0'); + std::generate(buffer.begin(), buffer.end(), std::rand); + return buffer; +} + +QOlmAccount::QOlmAccount(const QString &userId, const QString &deviceId, QObject *parent) + : QObject(parent) + , m_userId(userId) + , m_deviceId(deviceId) +{ +} + +QOlmAccount::~QOlmAccount() +{ + olm_clear_account(m_account); + delete[](reinterpret_cast(m_account)); +} + +void QOlmAccount::createNewAccount() +{ + m_account = olm_account(new uint8_t[olm_account_size()]); + size_t randomSize = olm_create_account_random_length(m_account); + QByteArray randomData = getRandom(randomSize); + const auto error = olm_create_account(m_account, randomData.data(), randomSize); + if (error == olm_error()) { + throw lastError(m_account); + } + Q_EMIT needsSave(); +} + +void QOlmAccount::unpickle(QByteArray &pickled, const PicklingMode &mode) +{ + m_account = olm_account(new uint8_t[olm_account_size()]); + const QByteArray key = toKey(mode); + const auto error = olm_unpickle_account(m_account, key.data(), key.length(), pickled.data(), pickled.size()); + if (error == olm_error()) { + qCWarning(E2EE) << "Failed to unpickle olm account"; + //TODO: Do something that is not dying + // Probably log the user out since we have no way of getting to the keys + //throw lastError(m_account); + } +} + +std::variant QOlmAccount::pickle(const PicklingMode &mode) +{ + const QByteArray key = toKey(mode); + const size_t pickleLength = olm_pickle_account_length(m_account); + QByteArray pickleBuffer(pickleLength, '0'); + const auto error = olm_pickle_account(m_account, key.data(), + key.length(), pickleBuffer.data(), pickleLength); + if (error == olm_error()) { + return lastError(m_account); + } + return pickleBuffer; +} + +IdentityKeys QOlmAccount::identityKeys() const +{ + const size_t keyLength = olm_account_identity_keys_length(m_account); + QByteArray keyBuffer(keyLength, '0'); + const auto error = olm_account_identity_keys(m_account, keyBuffer.data(), keyLength); + if (error == olm_error()) { + throw lastError(m_account); + } + const QJsonObject key = QJsonDocument::fromJson(keyBuffer).object(); + return IdentityKeys { + key.value(QStringLiteral("curve25519")).toString().toUtf8(), + key.value(QStringLiteral("ed25519")).toString().toUtf8() + }; +} + +QByteArray QOlmAccount::sign(const QByteArray &message) const +{ + QByteArray signatureBuffer(olm_account_signature_length(m_account), '0'); + + const auto error = olm_account_sign(m_account, message.data(), message.length(), + signatureBuffer.data(), signatureBuffer.length()); + + if (error == olm_error()) { + throw lastError(m_account); + } + return signatureBuffer; +} + +QByteArray QOlmAccount::sign(const QJsonObject &message) const +{ + return sign(QJsonDocument(message).toJson(QJsonDocument::Compact)); +} + +QByteArray QOlmAccount::signIdentityKeys() const +{ + const auto keys = identityKeys(); + QJsonObject body + { + {"algorithms", QJsonArray{"m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"}}, + {"user_id", m_userId}, + {"device_id", m_deviceId}, + {"keys", + QJsonObject{ + {QStringLiteral("curve25519:") + m_deviceId, QString::fromUtf8(keys.curve25519)}, + {QStringLiteral("ed25519:") + m_deviceId, QString::fromUtf8(keys.ed25519)} + } + } + }; + return sign(QJsonDocument(body).toJson(QJsonDocument::Compact)); + +} + +size_t QOlmAccount::maxNumberOfOneTimeKeys() const +{ + return olm_account_max_number_of_one_time_keys(m_account); +} + +size_t QOlmAccount::generateOneTimeKeys(size_t numberOfKeys) const +{ + const size_t randomLength = olm_account_generate_one_time_keys_random_length(m_account, numberOfKeys); + QByteArray randomBuffer = getRandom(randomLength); + const auto error = olm_account_generate_one_time_keys(m_account, numberOfKeys, randomBuffer.data(), randomLength); + + if (error == olm_error()) { + throw lastError(m_account); + } + Q_EMIT needsSave(); + return error; +} + +OneTimeKeys QOlmAccount::oneTimeKeys() const +{ + const size_t oneTimeKeyLength = olm_account_one_time_keys_length(m_account); + QByteArray oneTimeKeysBuffer(oneTimeKeyLength, '0'); + + const auto error = olm_account_one_time_keys(m_account, oneTimeKeysBuffer.data(), oneTimeKeyLength); + if (error == olm_error()) { + throw lastError(m_account); + } + const auto json = QJsonDocument::fromJson(oneTimeKeysBuffer).object(); + OneTimeKeys oneTimeKeys; + + for (const QString& key1 : json.keys()) { + auto oneTimeKeyObject = json[key1].toObject(); + auto keyMap = QMap(); + for (const QString &key2 : oneTimeKeyObject.keys()) { + keyMap[key2] = oneTimeKeyObject[key2].toString(); + } + oneTimeKeys.keys[key1] = keyMap; + } + return oneTimeKeys; +} + +QMap QOlmAccount::signOneTimeKeys(const OneTimeKeys &keys) const +{ + QMap signedOneTimeKeys; + for (const auto &keyid : keys.curve25519().keys()) { + const auto oneTimeKey = keys.curve25519()[keyid]; + QByteArray sign = signOneTimeKey(oneTimeKey); + signedOneTimeKeys["signed_curve25519:" + keyid] = signedOneTimeKey(oneTimeKey.toUtf8(), sign); + } + return signedOneTimeKeys; +} + +SignedOneTimeKey QOlmAccount::signedOneTimeKey(const QByteArray &key, const QString &signature) const +{ + SignedOneTimeKey sign{}; + sign.key = key; + sign.signatures = {{m_userId, {{"ed25519:" + m_deviceId, signature}}}}; + return sign; +} + +QByteArray QOlmAccount::signOneTimeKey(const QString &key) const +{ + QJsonDocument j(QJsonObject{{"key", key}}); + return sign(j.toJson(QJsonDocument::Compact)); +} + +std::optional QOlmAccount::removeOneTimeKeys(const QOlmSessionPtr &session) const +{ + const auto error = olm_remove_one_time_keys(m_account, session->raw()); + + if (error == olm_error()) { + return lastError(m_account); + } + Q_EMIT needsSave(); + return std::nullopt; +} + +OlmAccount *QOlmAccount::data() +{ + return m_account; +} + +DeviceKeys QOlmAccount::deviceKeys() const +{ + DeviceKeys deviceKeys; + deviceKeys.userId = m_userId; + deviceKeys.deviceId = m_deviceId; + deviceKeys.algorithms = QStringList {"m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"}; + + const auto idKeys = identityKeys(); + deviceKeys.keys["curve25519:" + m_deviceId] = idKeys.curve25519; + deviceKeys.keys["ed25519:" + m_deviceId] = idKeys.ed25519; + + const auto sign = signIdentityKeys(); + deviceKeys.signatures[m_userId]["ed25519:" + m_deviceId] = sign; + + return deviceKeys; +} + +UploadKeysJob *QOlmAccount::createUploadKeyRequest(const OneTimeKeys &oneTimeKeys) +{ + auto keys = deviceKeys(); + + if (oneTimeKeys.curve25519().isEmpty()) { + return new UploadKeysJob(keys); + } + + // Sign & append the one time keys. + auto temp = signOneTimeKeys(oneTimeKeys); + QHash oneTimeKeysSigned; + for (const auto &[keyId, key] : asKeyValueRange(temp)) { + oneTimeKeysSigned[keyId] = QVariant::fromValue(toJson(key)); + } + + return new UploadKeysJob(keys, oneTimeKeysSigned); +} + +std::variant QOlmAccount::createInboundSession(const QOlmMessage &preKeyMessage) +{ + Q_ASSERT(preKeyMessage.type() == QOlmMessage::PreKey); + return QOlmSession::createInboundSession(this, preKeyMessage); +} + +std::variant QOlmAccount::createInboundSessionFrom(const QByteArray &theirIdentityKey, const QOlmMessage &preKeyMessage) +{ + Q_ASSERT(preKeyMessage.type() == QOlmMessage::PreKey); + return QOlmSession::createInboundSessionFrom(this, theirIdentityKey, preKeyMessage); +} + +std::variant QOlmAccount::createOutboundSession(const QByteArray &theirIdentityKey, const QByteArray &theirOneTimeKey) +{ + return QOlmSession::createOutboundSession(this, theirIdentityKey, theirOneTimeKey); +} + +void QOlmAccount::markKeysAsPublished() +{ + olm_account_mark_keys_as_published(m_account); + Q_EMIT needsSave(); +} + +bool Quotient::verifyIdentitySignature(const DeviceKeys &deviceKeys, + const QString &deviceId, + const QString &userId) +{ + const auto signKeyId = "ed25519:" + deviceId; + const auto signingKey = deviceKeys.keys[signKeyId]; + const auto signature = deviceKeys.signatures[userId][signKeyId]; + + if (signature.isEmpty()) { + return false; + } + + return ed25519VerifySignature(signingKey, toJson(deviceKeys), signature); +} + +bool Quotient::ed25519VerifySignature(const QString &signingKey, + const QJsonObject &obj, + const QString &signature) +{ + if (signature.isEmpty()) { + return false; + } + QJsonObject obj1 = obj; + + obj1.remove("unsigned"); + obj1.remove("signatures"); + + auto canonicalJson = QJsonDocument(obj1).toJson(QJsonDocument::Compact); + + QByteArray signingKeyBuf = signingKey.toUtf8(); + QOlmUtility utility; + auto signatureBuf = signature.toUtf8(); + auto result = utility.ed25519Verify(signingKeyBuf, canonicalJson, signatureBuf); + if (std::holds_alternative(result)) { + return false; + } + + return std::get(result); +} diff --git a/lib/e2ee/qolmaccount.h b/lib/e2ee/qolmaccount.h new file mode 100644 index 00000000..00afc0e6 --- /dev/null +++ b/lib/e2ee/qolmaccount.h @@ -0,0 +1,123 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + + +#pragma once + +#include "csapi/keys.h" +#include "e2ee/e2ee.h" +#include "e2ee/qolmerrors.h" +#include "e2ee/qolmmessage.h" +#include "e2ee/qolmsession.h" +#include + +struct OlmAccount; + +namespace Quotient { + +class QOlmSession; +class Connection; + +using QOlmSessionPtr = std::unique_ptr; + +//! An olm account manages all cryptographic keys used on a device. +//! \code{.cpp} +//! const auto olmAccount = new QOlmAccount(this); +//! \endcode +class QOlmAccount : public QObject +{ + Q_OBJECT +public: + QOlmAccount(const QString &userId, const QString &deviceId, QObject *parent = nullptr); + ~QOlmAccount(); + + //! Creates a new instance of OlmAccount. During the instantiation + //! the Ed25519 fingerprint key pair and the Curve25519 identity key + //! pair are generated. For more information see here. + //! This needs to be called before any other action or use unpickle() instead. + void createNewAccount(); + + //! Deserialises from encrypted Base64 that was previously obtained by pickling a `QOlmAccount`. + //! This needs to be called before any other action or use createNewAccount() instead. + void unpickle(QByteArray &pickled, const PicklingMode &mode); + + //! Serialises an OlmAccount to encrypted Base64. + std::variant pickle(const PicklingMode &mode); + + //! Returns the account's public identity keys already formatted as JSON + IdentityKeys identityKeys() const; + + //! Returns the signature of the supplied message. + QByteArray sign(const QByteArray &message) const; + QByteArray sign(const QJsonObject& message) const; + + //! Sign identity keys. + QByteArray signIdentityKeys() const; + + //! Maximum number of one time keys that this OlmAccount can + //! currently hold. + size_t maxNumberOfOneTimeKeys() const; + + //! Generates the supplied number of one time keys. + size_t generateOneTimeKeys(size_t numberOfKeys) const; + + //! Gets the OlmAccount's one time keys formatted as JSON. + OneTimeKeys oneTimeKeys() const; + + //! Sign all one time keys. + QMap signOneTimeKeys(const OneTimeKeys &keys) const; + + //! Sign one time key. + QByteArray signOneTimeKey(const QString &key) const; + + SignedOneTimeKey signedOneTimeKey(const QByteArray &key, const QString &signature) const; + + UploadKeysJob *createUploadKeyRequest(const OneTimeKeys &oneTimeKeys); + + DeviceKeys deviceKeys() const; + + //! Remove the one time key used to create the supplied session. + [[nodiscard]] std::optional removeOneTimeKeys(const QOlmSessionPtr &session) const; + + //! Creates an inbound session for sending/receiving messages from a received 'prekey' message. + //! + //! \param message An Olm pre-key message that was encrypted for this account. + std::variant createInboundSession(const QOlmMessage &preKeyMessage); + + //! Creates an inbound session for sending/receiving messages from a received 'prekey' message. + //! + //! \param theirIdentityKey - The identity key of the Olm account that + //! encrypted this Olm message. + std::variant createInboundSessionFrom(const QByteArray &theirIdentityKey, const QOlmMessage &preKeyMessage); + + //! Creates an outbound session for sending messages to a specific + /// identity and one time key. + std::variant createOutboundSession(const QByteArray &theirIdentityKey, const QByteArray &theirOneTimeKey); + + void markKeysAsPublished(); + + // HACK do not use directly + QOlmAccount(OlmAccount *account); + OlmAccount *data(); + +Q_SIGNALS: + void needsSave() const; + +private: + OlmAccount *m_account = nullptr; // owning + QString m_userId; + QString m_deviceId; +}; + +bool verifyIdentitySignature(const DeviceKeys &deviceKeys, + const QString &deviceId, + const QString &userId); + +//! checks if the signature is signed by the signing_key +bool ed25519VerifySignature(const QString &signingKey, + const QJsonObject &obj, + const QString &signature); + +} // namespace Quotient diff --git a/lib/e2ee/qolmerrors.cpp b/lib/e2ee/qolmerrors.cpp new file mode 100644 index 00000000..6db1803c --- /dev/null +++ b/lib/e2ee/qolmerrors.cpp @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + + +#include "qolmerrors.h" + +Quotient::QOlmError Quotient::fromString(const std::string &error_raw) { + if (!error_raw.compare("BAD_ACCOUNT_KEY")) { + return QOlmError::BadAccountKey; + } else if (!error_raw.compare("BAD_MESSAGE_KEY_ID")) { + return QOlmError::BadMessageKeyId; + } else if (!error_raw.compare("INVALID_BASE64")) { + return QOlmError::InvalidBase64; + } else if (!error_raw.compare("NOT_ENOUGH_RANDOM")) { + return QOlmError::NotEnoughRandom; + } else if (!error_raw.compare("OUTPUT_BUFFER_TOO_SMALL")) { + return QOlmError::OutputBufferTooSmall; + } else { + return QOlmError::Unknown; + } +} diff --git a/lib/e2ee/qolmerrors.h b/lib/e2ee/qolmerrors.h new file mode 100644 index 00000000..f8390d2a --- /dev/null +++ b/lib/e2ee/qolmerrors.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include + +namespace Quotient { +//! All errors that could be caused by an operation regarding Olm +//! Errors are named exactly like the ones in libolm. +enum QOlmError +{ + BadAccountKey, + BadMessageFormat, + BadMessageKeyId, + BadMessageMac, + BadMessageVersion, + InvalidBase64, + NotEnoughRandom, + OutputBufferTooSmall, + UnknownMessageIndex, + Unknown, +}; + +QOlmError fromString(const std::string &error_raw); + +} //namespace Quotient diff --git a/lib/e2ee/qolminboundsession.cpp b/lib/e2ee/qolminboundsession.cpp new file mode 100644 index 00000000..9bf56b6c --- /dev/null +++ b/lib/e2ee/qolminboundsession.cpp @@ -0,0 +1,153 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "e2ee/qolminboundsession.h" +#include +#include +using namespace Quotient; + +QOlmError lastError(OlmInboundGroupSession *session) { + const std::string error_raw = olm_inbound_group_session_last_error(session); + + return fromString(error_raw); +} + +QOlmInboundGroupSession::QOlmInboundGroupSession(OlmInboundGroupSession *session) + : m_groupSession(session) +{ +} + +QOlmInboundGroupSession::~QOlmInboundGroupSession() +{ + olm_clear_inbound_group_session(m_groupSession); + //delete[](reinterpret_cast(m_groupSession)); +} + +std::unique_ptr QOlmInboundGroupSession::create(const QByteArray &key) +{ + const auto olmInboundGroupSession = olm_inbound_group_session(new uint8_t[olm_inbound_group_session_size()]); + const auto temp = key; + const auto error = olm_init_inbound_group_session(olmInboundGroupSession, + reinterpret_cast(temp.data()), temp.size()); + + if (error == olm_error()) { + throw lastError(olmInboundGroupSession); + } + + return std::make_unique(olmInboundGroupSession); +} + +std::unique_ptr QOlmInboundGroupSession::import(const QByteArray &key) +{ + const auto olmInboundGroupSession = olm_inbound_group_session(new uint8_t[olm_inbound_group_session_size()]); + QByteArray keyBuf = key; + + const auto error = olm_import_inbound_group_session(olmInboundGroupSession, + reinterpret_cast(keyBuf.data()), keyBuf.size()); + if (error == olm_error()) { + throw lastError(olmInboundGroupSession); + } + + return std::make_unique(olmInboundGroupSession); +} + +QByteArray toKey(const PicklingMode &mode) +{ + if (std::holds_alternative(mode)) { + return ""; + } + return std::get(mode).key; +} + +QByteArray QOlmInboundGroupSession::pickle(const PicklingMode &mode) const +{ + QByteArray pickledBuf(olm_pickle_inbound_group_session_length(m_groupSession), '0'); + const QByteArray key = toKey(mode); + const auto error = olm_pickle_inbound_group_session(m_groupSession, key.data(), key.length(), pickledBuf.data(), + pickledBuf.length()); + if (error == olm_error()) { + throw lastError(m_groupSession); + } + return pickledBuf; +} + +std::variant, QOlmError> QOlmInboundGroupSession::unpickle(const QByteArray &pickled, const PicklingMode &mode) +{ + QByteArray pickledBuf = pickled; + const auto groupSession = olm_inbound_group_session(new uint8_t[olm_inbound_group_session_size()]); + QByteArray key = toKey(mode); + const auto error = olm_unpickle_inbound_group_session(groupSession, key.data(), key.length(), + pickledBuf.data(), pickledBuf.size()); + if (error == olm_error()) { + return lastError(groupSession); + } + key.clear(); + + return std::make_unique(groupSession); +} + +std::variant, QOlmError> QOlmInboundGroupSession::decrypt(const QByteArray &message) +{ + // This is for capturing the output of olm_group_decrypt + uint32_t messageIndex = 0; + + // We need to clone the message because + // olm_decrypt_max_plaintext_length destroys the input buffer + QByteArray messageBuf(message.length(), '0'); + std::copy(message.begin(), message.end(), messageBuf.begin()); + + QByteArray plaintextBuf(olm_group_decrypt_max_plaintext_length(m_groupSession, + reinterpret_cast(messageBuf.data()), messageBuf.length()), '0'); + + messageBuf = QByteArray(message.length(), '0'); + std::copy(message.begin(), message.end(), messageBuf.begin()); + + const auto plaintextLen = olm_group_decrypt(m_groupSession, reinterpret_cast(messageBuf.data()), + messageBuf.length(), reinterpret_cast(plaintextBuf.data()), plaintextBuf.length(), &messageIndex); + + // Error code or plaintext length is returned + const auto decryptError = plaintextLen; + + if (decryptError == olm_error()) { + return lastError(m_groupSession); + } + + QByteArray output(plaintextLen, '0'); + std::memcpy(output.data(), plaintextBuf.data(), plaintextLen); + + return std::make_pair(QString(output), messageIndex); +} + +std::variant QOlmInboundGroupSession::exportSession(uint32_t messageIndex) +{ + const auto keyLength = olm_export_inbound_group_session_length(m_groupSession); + QByteArray keyBuf(keyLength, '0'); + const auto error = olm_export_inbound_group_session(m_groupSession, reinterpret_cast(keyBuf.data()), keyLength, messageIndex); + + if (error == olm_error()) { + return lastError(m_groupSession); + } + return keyBuf; +} + +uint32_t QOlmInboundGroupSession::firstKnownIndex() const +{ + return olm_inbound_group_session_first_known_index(m_groupSession); +} + +QByteArray QOlmInboundGroupSession::sessionId() const +{ + QByteArray sessionIdBuf(olm_inbound_group_session_id_length(m_groupSession), '0'); + const auto error = olm_inbound_group_session_id(m_groupSession, reinterpret_cast(sessionIdBuf.data()), + sessionIdBuf.length()); + if (error == olm_error()) { + throw lastError(m_groupSession); + } + return sessionIdBuf; +} + +bool QOlmInboundGroupSession::isVerified() const +{ + return olm_inbound_group_session_is_verified(m_groupSession) != 0; +} diff --git a/lib/e2ee/qolminboundsession.h b/lib/e2ee/qolminboundsession.h new file mode 100644 index 00000000..7d52991c --- /dev/null +++ b/lib/e2ee/qolminboundsession.h @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include +#include +#include +#include "olm/olm.h" +#include "e2ee/qolmerrors.h" +#include "e2ee/e2ee.h" + +namespace Quotient { + +//! An in-bound group session is responsible for decrypting incoming +//! communication in a Megolm session. +struct QOlmInboundGroupSession +{ +public: + ~QOlmInboundGroupSession(); + //! Creates a new instance of `OlmInboundGroupSession`. + static std::unique_ptr create(const QByteArray &key); + //! Import an inbound group session, from a previous export. + static std::unique_ptr import(const QByteArray &key); + //! Serialises an `OlmInboundGroupSession` to encrypted Base64. + QByteArray pickle(const PicklingMode &mode) const; + //! Deserialises from encrypted Base64 that was previously obtained by pickling + //! an `OlmInboundGroupSession`. + static std::variant, QOlmError> unpickle(const QByteArray &picked, const PicklingMode &mode); + //! Decrypts ciphertext received for this group session. + std::variant, QOlmError> decrypt(const QByteArray &message); + //! Export the base64-encoded ratchet key for this session, at the given index, + //! in a format which can be used by import. + std::variant exportSession(uint32_t messageIndex); + //! Get the first message index we know how to decrypt. + uint32_t firstKnownIndex() const; + //! Get a base64-encoded identifier for this session. + QByteArray sessionId() const; + bool isVerified() const; + QOlmInboundGroupSession(OlmInboundGroupSession *session); +private: + OlmInboundGroupSession *m_groupSession; +}; + +using QOlmInboundGroupSessionPtr = std::unique_ptr; +using OlmInboundGroupSessionPtr = std::unique_ptr; +} // namespace Quotient diff --git a/lib/e2ee/qolmmessage.cpp b/lib/e2ee/qolmmessage.cpp new file mode 100644 index 00000000..15008b75 --- /dev/null +++ b/lib/e2ee/qolmmessage.cpp @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2021 Alexey Andreyev +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "qolmmessage.h" + +using namespace Quotient; + +QOlmMessage::QOlmMessage(const QByteArray &ciphertext, QOlmMessage::Type type) + : QByteArray(std::move(ciphertext)) + , m_messageType(type) +{ + Q_ASSERT_X(!ciphertext.isEmpty(), "olm message", "Ciphertext is empty"); +} + +QOlmMessage::QOlmMessage(const QOlmMessage &message) + : QByteArray(message) + , m_messageType(message.type()) +{ +} + +QOlmMessage::Type QOlmMessage::type() const +{ + return m_messageType; +} + +QByteArray QOlmMessage::toCiphertext() const +{ + return QByteArray(*this); +} + +QOlmMessage QOlmMessage::fromCiphertext(const QByteArray &ciphertext) +{ + return QOlmMessage(ciphertext, QOlmMessage::General); +} diff --git a/lib/e2ee/qolmmessage.h b/lib/e2ee/qolmmessage.h new file mode 100644 index 00000000..52aba78c --- /dev/null +++ b/lib/e2ee/qolmmessage.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2021 Alexey Andreyev +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include +#include + +namespace Quotient { + +/*! \brief A wrapper around an olm encrypted message + * + * This class encapsulates a Matrix olm encrypted message, + * passed in either of 2 forms: a general message or a pre-key message. + * + * The class provides functions to get a type and the ciphertext. + */ +class QOlmMessage : public QByteArray { + Q_GADGET +public: + enum Type { + General, + PreKey, + }; + Q_ENUM(Type) + + QOlmMessage() = default; + explicit QOlmMessage(const QByteArray &ciphertext, Type type = General); + explicit QOlmMessage(const QOlmMessage &message); + + static QOlmMessage fromCiphertext(const QByteArray &ciphertext); + + Q_INVOKABLE Type type() const; + Q_INVOKABLE QByteArray toCiphertext() const; + +private: + Type m_messageType = General; +}; + +} //namespace Quotient diff --git a/lib/e2ee/qolmoutboundsession.cpp b/lib/e2ee/qolmoutboundsession.cpp new file mode 100644 index 00000000..88e6b2e1 --- /dev/null +++ b/lib/e2ee/qolmoutboundsession.cpp @@ -0,0 +1,128 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "e2ee/qolmoutboundsession.h" +#include "e2ee/qolmutils.h" + +using namespace Quotient; + +QOlmError lastError(OlmOutboundGroupSession *session) { + const std::string error_raw = olm_outbound_group_session_last_error(session); + + return fromString(error_raw); +} + +QOlmOutboundGroupSession::QOlmOutboundGroupSession(OlmOutboundGroupSession *session) + : m_groupSession(session) +{ +} + +QOlmOutboundGroupSession::~QOlmOutboundGroupSession() +{ + olm_clear_outbound_group_session(m_groupSession); + delete[](reinterpret_cast(m_groupSession)); +} + +std::unique_ptr QOlmOutboundGroupSession::create() +{ + auto *olmOutboundGroupSession = olm_outbound_group_session(new uint8_t[olm_outbound_group_session_size()]); + const auto randomLength = olm_init_outbound_group_session_random_length(olmOutboundGroupSession); + QByteArray randomBuf = getRandom(randomLength); + + const auto error = olm_init_outbound_group_session(olmOutboundGroupSession, + reinterpret_cast(randomBuf.data()), randomBuf.length()); + + if (error == olm_error()) { + throw lastError(olmOutboundGroupSession); + } + + const auto keyMaxLength = olm_outbound_group_session_key_length(olmOutboundGroupSession); + QByteArray keyBuffer(keyMaxLength, '0'); + olm_outbound_group_session_key(olmOutboundGroupSession, reinterpret_cast(keyBuffer.data()), + keyMaxLength); + + randomBuf.clear(); + + return std::make_unique(olmOutboundGroupSession); +} + +std::variant QOlmOutboundGroupSession::pickle(const PicklingMode &mode) +{ + QByteArray pickledBuf(olm_pickle_outbound_group_session_length(m_groupSession), '0'); + QByteArray key = toKey(mode); + const auto error = olm_pickle_outbound_group_session(m_groupSession, key.data(), key.length(), + pickledBuf.data(), pickledBuf.length()); + + if (error == olm_error()) { + return lastError(m_groupSession); + } + + key.clear(); + + return pickledBuf; +} + + +std::variant, QOlmError> QOlmOutboundGroupSession::unpickle(QByteArray &pickled, const PicklingMode &mode) +{ + QByteArray pickledBuf = pickled; + auto *olmOutboundGroupSession = olm_outbound_group_session(new uint8_t[olm_outbound_group_session_size()]); + QByteArray key = toKey(mode); + const auto error = olm_unpickle_outbound_group_session(olmOutboundGroupSession, key.data(), key.length(), + pickled.data(), pickled.length()); + if (error == olm_error()) { + return lastError(olmOutboundGroupSession); + } + const auto idMaxLength = olm_outbound_group_session_id_length(olmOutboundGroupSession); + QByteArray idBuffer(idMaxLength, '0'); + olm_outbound_group_session_id(olmOutboundGroupSession, reinterpret_cast(idBuffer.data()), + idBuffer.length()); + + key.clear(); + return std::make_unique(olmOutboundGroupSession); +} + +std::variant QOlmOutboundGroupSession::encrypt(const QString &plaintext) +{ + QByteArray plaintextBuf = plaintext.toUtf8(); + const auto messageMaxLength = olm_group_encrypt_message_length(m_groupSession, plaintextBuf.length()); + QByteArray messageBuf(messageMaxLength, '0'); + const auto error = olm_group_encrypt(m_groupSession, reinterpret_cast(plaintextBuf.data()), + plaintextBuf.length(), reinterpret_cast(messageBuf.data()), messageBuf.length()); + + if (error == olm_error()) { + return lastError(m_groupSession); + } + + return messageBuf; +} + +uint32_t QOlmOutboundGroupSession::sessionMessageIndex() const +{ + return olm_outbound_group_session_message_index(m_groupSession); +} + +QByteArray QOlmOutboundGroupSession::sessionId() const +{ + const auto idMaxLength = olm_outbound_group_session_id_length(m_groupSession); + QByteArray idBuffer(idMaxLength, '0'); + const auto error = olm_outbound_group_session_id(m_groupSession, reinterpret_cast(idBuffer.data()), + idBuffer.length()); + if (error == olm_error()) { + throw lastError(m_groupSession); + } + return idBuffer; +} + +std::variant QOlmOutboundGroupSession::sessionKey() const +{ + const auto keyMaxLength = olm_outbound_group_session_key_length(m_groupSession); + QByteArray keyBuffer(keyMaxLength, '0'); + const auto error = olm_outbound_group_session_key(m_groupSession, reinterpret_cast(keyBuffer.data()), + keyMaxLength); + if (error == olm_error()) { + return lastError(m_groupSession); + } + return keyBuffer; +} diff --git a/lib/e2ee/qolmoutboundsession.h b/lib/e2ee/qolmoutboundsession.h new file mode 100644 index 00000000..967f563f --- /dev/null +++ b/lib/e2ee/qolmoutboundsession.h @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + + +#pragma once + +#include "olm/olm.h" +#include "e2ee/qolmerrors.h" +#include "e2ee/e2ee.h" +#include + +namespace Quotient { + + +//! An out-bound group session is responsible for encrypting outgoing +//! communication in a Megolm session. +class QOlmOutboundGroupSession +{ +public: + ~QOlmOutboundGroupSession(); + //! Creates a new instance of `QOlmOutboundGroupSession`. + //! Throw OlmError on errors + static std::unique_ptr create(); + //! Serialises a `QOlmOutboundGroupSession` to encrypted Base64. + std::variant pickle(const PicklingMode &mode); + //! Deserialises from encrypted Base64 that was previously obtained by + //! pickling a `QOlmOutboundGroupSession`. + static std::variant, QOlmError> unpickle(QByteArray &pickled, const PicklingMode &mode); + //! Encrypts a plaintext message using the session. + std::variant encrypt(const QString &plaintext); + + //! Get the current message index for this session. + //! + //! Each message is sent with an increasing index; this returns the + //! index for the next message. + uint32_t sessionMessageIndex() const; + + //! Get a base64-encoded identifier for this session. + QByteArray sessionId() const; + + //! Get the base64-encoded current ratchet key for this session. + //! + //! Each message is sent with a different ratchet key. This function returns the + //! ratchet key that will be used for the next message. + std::variant sessionKey() const; + QOlmOutboundGroupSession(OlmOutboundGroupSession *groupSession); +private: + OlmOutboundGroupSession *m_groupSession; +}; + +using QOlmOutboundGroupSessionPtr = std::unique_ptr; +using OlmOutboundGroupSessionPtr = std::unique_ptr; +} diff --git a/lib/e2ee/qolmsession.cpp b/lib/e2ee/qolmsession.cpp new file mode 100644 index 00000000..69d8b431 --- /dev/null +++ b/lib/e2ee/qolmsession.cpp @@ -0,0 +1,253 @@ +// SPDX-FileCopyrightText: 2021 Alexey Andreyev +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "qolmsession.h" +#include "e2ee/qolmutils.h" +#include "logging.h" +#include +#include + +using namespace Quotient; + +QOlmError lastError(OlmSession* session) { + const std::string error_raw = olm_session_last_error(session); + + return fromString(error_raw); +} + +Quotient::QOlmSession::~QOlmSession() +{ + olm_clear_session(m_session); + delete[](reinterpret_cast(m_session)); +} + +OlmSession* QOlmSession::create() +{ + return olm_session(new uint8_t[olm_session_size()]); +} + +std::variant QOlmSession::createInbound(QOlmAccount *account, const QOlmMessage &preKeyMessage, bool from, const QString &theirIdentityKey) +{ + if (preKeyMessage.type() != QOlmMessage::PreKey) { + qCCritical(E2EE) << "The message is not a pre-key in when creating inbound session" << BadMessageFormat; + } + + const auto olmSession = create(); + + QByteArray oneTimeKeyMessageBuf = preKeyMessage.toCiphertext(); + QByteArray theirIdentityKeyBuf = theirIdentityKey.toUtf8(); + size_t error = 0; + if (from) { + error = olm_create_inbound_session_from(olmSession, account->data(), theirIdentityKeyBuf.data(), theirIdentityKeyBuf.length(), oneTimeKeyMessageBuf.data(), oneTimeKeyMessageBuf.length()); + } else { + error = olm_create_inbound_session(olmSession, account->data(), oneTimeKeyMessageBuf.data(), oneTimeKeyMessageBuf.length()); + } + + if (error == olm_error()) { + const auto lastErr = lastError(olmSession); + qCWarning(E2EE) << "Error when creating inbound session" << lastErr; + return lastErr; + } + + return std::make_unique(olmSession); +} + +std::variant QOlmSession::createInboundSession(QOlmAccount *account, const QOlmMessage &preKeyMessage) +{ + return createInbound(account, preKeyMessage); +} + +std::variant QOlmSession::createInboundSessionFrom(QOlmAccount *account, const QString &theirIdentityKey, const QOlmMessage &preKeyMessage) +{ + return createInbound(account, preKeyMessage, true, theirIdentityKey); +} + +std::variant QOlmSession::createOutboundSession(QOlmAccount *account, const QString &theirIdentityKey, const QString &theirOneTimeKey) +{ + auto *olmOutboundSession = create(); + const auto randomLen = olm_create_outbound_session_random_length(olmOutboundSession); + QByteArray randomBuf = getRandom(randomLen); + + QByteArray theirIdentityKeyBuf = theirIdentityKey.toUtf8(); + QByteArray theirOneTimeKeyBuf = theirOneTimeKey.toUtf8(); + const auto error = olm_create_outbound_session(olmOutboundSession, + account->data(), + reinterpret_cast(theirIdentityKeyBuf.data()), theirIdentityKeyBuf.length(), + reinterpret_cast(theirOneTimeKeyBuf.data()), theirOneTimeKeyBuf.length(), + reinterpret_cast(randomBuf.data()), randomBuf.length()); + + if (error == olm_error()) { + const auto lastErr = lastError(olmOutboundSession); + if (lastErr == QOlmError::NotEnoughRandom) { + throw lastErr; + } + return lastErr; + } + + randomBuf.clear(); + return std::make_unique(olmOutboundSession); +} + +std::variant QOlmSession::pickle(const PicklingMode &mode) +{ + QByteArray pickledBuf(olm_pickle_session_length(m_session), '0'); + QByteArray key = toKey(mode); + const auto error = olm_pickle_session(m_session, key.data(), key.length(), + pickledBuf.data(), pickledBuf.length()); + + if (error == olm_error()) { + return lastError(m_session); + } + + key.clear(); + + return pickledBuf; +} + +std::variant QOlmSession::unpickle(const QByteArray &pickled, const PicklingMode &mode) +{ + QByteArray pickledBuf = pickled; + auto *olmSession = create(); + QByteArray key = toKey(mode); + const auto error = olm_unpickle_session(olmSession, key.data(), key.length(), + pickledBuf.data(), pickledBuf.length()); + if (error == olm_error()) { + return lastError(olmSession); + } + + key.clear(); + return std::make_unique(olmSession); +} + +QOlmMessage QOlmSession::encrypt(const QString &plaintext) +{ + QByteArray plaintextBuf = plaintext.toUtf8(); + const auto messageMaxLen = olm_encrypt_message_length(m_session, plaintextBuf.length()); + QByteArray messageBuf(messageMaxLen, '0'); + const auto messageType = encryptMessageType(); + const auto randomLen = olm_encrypt_random_length(m_session); + QByteArray randomBuf = getRandom(randomLen); + const auto error = olm_encrypt(m_session, + reinterpret_cast(plaintextBuf.data()), plaintextBuf.length(), + reinterpret_cast(randomBuf.data()), randomBuf.length(), + reinterpret_cast(messageBuf.data()), messageBuf.length()); + + if (error == olm_error()) { + throw lastError(m_session); + } + + return QOlmMessage(messageBuf, messageType); +} + +std::variant QOlmSession::decrypt(const QOlmMessage &message) const +{ + const auto messageType = message.type(); + const auto ciphertext = message.toCiphertext(); + const auto messageTypeValue = messageType == QOlmMessage::Type::General + ? OLM_MESSAGE_TYPE_MESSAGE : OLM_MESSAGE_TYPE_PRE_KEY; + + // We need to clone the message because + // olm_decrypt_max_plaintext_length destroys the input buffer + QByteArray messageBuf(ciphertext.length(), '0'); + std::copy(message.begin(), message.end(), messageBuf.begin()); + + const auto plaintextMaxLen = olm_decrypt_max_plaintext_length(m_session, messageTypeValue, + reinterpret_cast(messageBuf.data()), messageBuf.length()); + + if (plaintextMaxLen == olm_error()) { + return lastError(m_session); + } + + QByteArray plaintextBuf(plaintextMaxLen, '0'); + QByteArray messageBuf2(ciphertext.length(), '0'); + std::copy(message.begin(), message.end(), messageBuf2.begin()); + + const auto plaintextResultLen = olm_decrypt(m_session, messageTypeValue, + reinterpret_cast(messageBuf2.data()), messageBuf2.length(), + reinterpret_cast(plaintextBuf.data()), plaintextMaxLen); + + if (plaintextResultLen == olm_error()) { + const auto lastErr = lastError(m_session); + if (lastErr == QOlmError::OutputBufferTooSmall) { + throw lastErr; + } + return lastErr; + } + QByteArray output(plaintextResultLen, '0'); + std::memcpy(output.data(), plaintextBuf.data(), plaintextResultLen); + plaintextBuf.clear(); + return output; +} + +QOlmMessage::Type QOlmSession::encryptMessageType() +{ + const auto messageTypeResult = olm_encrypt_message_type(m_session); + if (messageTypeResult == olm_error()) { + throw lastError(m_session); + } + if (messageTypeResult == OLM_MESSAGE_TYPE_PRE_KEY) { + return QOlmMessage::PreKey; + } + return QOlmMessage::General; +} + +QByteArray QOlmSession::sessionId() const +{ + const auto idMaxLength = olm_session_id_length(m_session); + QByteArray idBuffer(idMaxLength, '0'); + const auto error = olm_session_id(m_session, reinterpret_cast(idBuffer.data()), + idBuffer.length()); + if (error == olm_error()) { + throw lastError(m_session); + } + return idBuffer; +} + +bool QOlmSession::hasReceivedMessage() const +{ + return olm_session_has_received_message(m_session); +} + +std::variant QOlmSession::matchesInboundSession(const QOlmMessage &preKeyMessage) const +{ + Q_ASSERT(preKeyMessage.type() == QOlmMessage::Type::PreKey); + QByteArray oneTimeKeyBuf(preKeyMessage.data()); + const auto matchesResult = olm_matches_inbound_session(m_session, oneTimeKeyBuf.data(), oneTimeKeyBuf.length()); + + if (matchesResult == olm_error()) { + return lastError(m_session); + } + switch (matchesResult) { + case 0: + return false; + case 1: + return true; + default: + return QOlmError::Unknown; + } +} +std::variant QOlmSession::matchesInboundSessionFrom(const QString &theirIdentityKey, const QOlmMessage &preKeyMessage) const +{ + const auto theirIdentityKeyBuf = theirIdentityKey.toUtf8(); + auto oneTimeKeyMessageBuf = preKeyMessage.toCiphertext(); + const auto error = olm_matches_inbound_session_from(m_session, theirIdentityKeyBuf.data(), theirIdentityKeyBuf.length(), + oneTimeKeyMessageBuf.data(), oneTimeKeyMessageBuf.length()); + + if (error == olm_error()) { + return lastError(m_session); + } + switch (error) { + case 0: + return false; + case 1: + return true; + default: + return QOlmError::Unknown; + } +} + +QOlmSession::QOlmSession(OlmSession *session) + : m_session(session) +{ +} diff --git a/lib/e2ee/qolmsession.h b/lib/e2ee/qolmsession.h new file mode 100644 index 00000000..1febfa0f --- /dev/null +++ b/lib/e2ee/qolmsession.h @@ -0,0 +1,76 @@ +// SPDX-FileCopyrightText: 2021 Alexey Andreyev +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include +#include // FIXME: OlmSession +#include "e2ee/e2ee.h" +#include "e2ee/qolmmessage.h" +#include "e2ee/qolmerrors.h" +#include "e2ee/qolmaccount.h" + +namespace Quotient { + +class QOlmAccount; +class QOlmSession; + + +//! Either an outbound or inbound session for secure communication. +class QOlmSession +{ +public: + ~QOlmSession(); + //! Creates an inbound session for sending/receiving messages from a received 'prekey' message. + static std::variant, QOlmError> createInboundSession(QOlmAccount *account, const QOlmMessage &preKeyMessage); + static std::variant, QOlmError> createInboundSessionFrom(QOlmAccount *account, const QString &theirIdentityKey, const QOlmMessage &preKeyMessage); + static std::variant, QOlmError> createOutboundSession(QOlmAccount *account, const QString &theirIdentityKey, const QString &theirOneTimeKey); + //! Serialises an `QOlmSession` to encrypted Base64. + std::variant pickle(const PicklingMode &mode); + //! Deserialises from encrypted Base64 that was previously obtained by pickling a `QOlmSession`. + static std::variant, QOlmError> unpickle(const QByteArray &pickled, const PicklingMode &mode); + //! Encrypts a plaintext message using the session. + QOlmMessage encrypt(const QString &plaintext); + + //! Decrypts a message using this session. Decoding is lossy, meaing if + //! the decrypted plaintext contains invalid UTF-8 symbols, they will + //! be returned as `U+FFFD` (�). + std::variant decrypt(const QOlmMessage &message) const; + + //! Get a base64-encoded identifier for this session. + QByteArray sessionId() const; + + //! The type of the next message that will be returned from encryption. + QOlmMessage::Type encryptMessageType(); + + //! Checker for any received messages for this session. + bool hasReceivedMessage() const; + + //! Checks if the 'prekey' message is for this in-bound session. + std::variant matchesInboundSession(const QOlmMessage &preKeyMessage) const; + + //! Checks if the 'prekey' message is for this in-bound session. + std::variant matchesInboundSessionFrom(const QString &theirIdentityKey, const QOlmMessage &preKeyMessage) const; + + friend bool operator<(const QOlmSession& lhs, const QOlmSession& rhs) + { + return lhs.sessionId() < rhs.sessionId(); + } + + friend bool operator<(const std::unique_ptr &lhs, const std::unique_ptr &rhs) { + return *lhs < *rhs; + } + + OlmSession *raw() const + { + return m_session; + } + QOlmSession(OlmSession* session); +private: + //! Helper function for creating new sessions and handling errors. + static OlmSession* create(); + static std::variant, QOlmError> createInbound(QOlmAccount *account, const QOlmMessage& preKeyMessage, bool from = false, const QString& theirIdentityKey = ""); + OlmSession* m_session; +}; +} //namespace Quotient diff --git a/lib/e2ee/qolmutility.cpp b/lib/e2ee/qolmutility.cpp new file mode 100644 index 00000000..d0684055 --- /dev/null +++ b/lib/e2ee/qolmutility.cpp @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "e2ee/qolmutility.h" +#include "olm/olm.h" +#include + +using namespace Quotient; + +// Convert olm error to enum +QOlmError lastError(OlmUtility *utility) { + const std::string error_raw = olm_utility_last_error(utility); + + return fromString(error_raw); +} + +QOlmUtility::QOlmUtility() +{ + auto utility = new uint8_t[olm_utility_size()]; + m_utility = olm_utility(utility); +} + +QOlmUtility::~QOlmUtility() +{ + olm_clear_utility(m_utility); + delete[](reinterpret_cast(m_utility)); +} + +QString QOlmUtility::sha256Bytes(const QByteArray &inputBuf) const +{ + const auto outputLen = olm_sha256_length(m_utility); + QByteArray outputBuf(outputLen, '0'); + olm_sha256(m_utility, inputBuf.data(), inputBuf.length(), + outputBuf.data(), outputBuf.length()); + + return QString::fromUtf8(outputBuf); +} + +QString QOlmUtility::sha256Utf8Msg(const QString &message) const +{ + return sha256Bytes(message.toUtf8()); +} + +std::variant QOlmUtility::ed25519Verify(const QByteArray &key, + const QByteArray &message, const QByteArray &signature) +{ + QByteArray signatureBuf(signature.length(), '0'); + std::copy(signature.begin(), signature.end(), signatureBuf.begin()); + + const auto ret = olm_ed25519_verify(m_utility, key.data(), key.size(), + message.data(), message.size(), (void *)signatureBuf.data(), signatureBuf.size()); + + const auto error = ret; + if (error == olm_error()) { + return lastError(m_utility); + } + + if (ret != 0) { + return false; + } + return true; +} diff --git a/lib/e2ee/qolmutility.h b/lib/e2ee/qolmutility.h new file mode 100644 index 00000000..b360d625 --- /dev/null +++ b/lib/e2ee/qolmutility.h @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include +#include +#include "e2ee/qolmerrors.h" + +struct OlmUtility; + +namespace Quotient { + +class QOlmSession; +class Connection; + +//! Allows you to make use of crytographic hashing via SHA-2 and +//! verifying ed25519 signatures. +class QOlmUtility +{ +public: + QOlmUtility(); + ~QOlmUtility(); + + //! Returns a sha256 of the supplied byte slice. + QString sha256Bytes(const QByteArray &inputBuf) const; + + //! Convenience function that converts the UTF-8 message + //! to bytes and then calls `sha256Bytes()`, returning its output. + QString sha256Utf8Msg(const QString &message) const; + + //! Verify a ed25519 signature. + //! \param key QByteArray The public part of the ed25519 key that signed the message. + //! \param message QByteArray The message that was signed. + //! \param signature QByteArray The signature of the message. + std::variant ed25519Verify(const QByteArray &key, + const QByteArray &message, const QByteArray &signature); + + +private: + OlmUtility *m_utility; + +}; +} diff --git a/lib/e2ee/qolmutils.cpp b/lib/e2ee/qolmutils.cpp new file mode 100644 index 00000000..ce27710d --- /dev/null +++ b/lib/e2ee/qolmutils.cpp @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "e2ee/qolmutils.h" +#include +#include + +using namespace Quotient; + +QByteArray Quotient::toKey(const Quotient::PicklingMode &mode) +{ + if (std::holds_alternative(mode)) { + return {}; + } + return std::get(mode).key; +} + +QByteArray Quotient::getRandom(size_t bufferSize) +{ + QByteArray buffer(bufferSize, '0'); + RAND_bytes(reinterpret_cast(buffer.data()), buffer.size()); + return buffer; +} diff --git a/lib/e2ee/qolmutils.h b/lib/e2ee/qolmutils.h new file mode 100644 index 00000000..bbd71332 --- /dev/null +++ b/lib/e2ee/qolmutils.h @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include + +#include "e2ee/e2ee.h" + +namespace Quotient { +// Convert PicklingMode to key +QByteArray toKey(const PicklingMode &mode); +QByteArray getRandom(size_t bufferSize); +} diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp index e5fa978f..3d616965 100644 --- a/lib/encryptionmanager.cpp +++ b/lib/encryptionmanager.cpp @@ -6,7 +6,6 @@ #include "encryptionmanager.h" #include "connection.h" -#include "crypto/e2ee.h" #include "events/encryptedfile.h" #include "database.h" @@ -16,11 +15,12 @@ #include #include -#include "crypto/qolmaccount.h" -#include "crypto/qolmsession.h" -#include "crypto/qolmmessage.h" -#include "crypto/qolmerrors.h" -#include "crypto/qolmutils.h" +#include "e2ee/e2ee.h" +#include "e2ee/qolmaccount.h" +#include "e2ee/qolmsession.h" +#include "e2ee/qolmmessage.h" +#include "e2ee/qolmerrors.h" +#include "e2ee/qolmutils.h" #include #include diff --git a/lib/events/encryptedevent.h b/lib/events/encryptedevent.h index 28398827..4cc3bf8e 100644 --- a/lib/events/encryptedevent.h +++ b/lib/events/encryptedevent.h @@ -3,7 +3,7 @@ #pragma once -#include "crypto/e2ee.h" +#include "e2ee/e2ee.h" #include "roomevent.h" namespace Quotient { diff --git a/lib/events/encryptionevent.cpp b/lib/events/encryptionevent.cpp index d7bb953a..6272c668 100644 --- a/lib/events/encryptionevent.cpp +++ b/lib/events/encryptionevent.cpp @@ -4,7 +4,7 @@ #include "encryptionevent.h" -#include "crypto/e2ee.h" +#include "e2ee/e2ee.h" #include diff --git a/lib/room.cpp b/lib/room.cpp index 8181f16a..755f677a 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -12,7 +12,6 @@ #include "avatar.h" #include "connection.h" #include "converters.h" -#include "crypto/e2ee.h" #include "syncdata.h" #include "user.h" #include "eventstats.h" @@ -65,9 +64,10 @@ #include #ifdef Quotient_E2EE_ENABLED -#include "crypto/qolmaccount.h" -#include "crypto/qolmerrors.h" -#include "crypto/qolminboundsession.h" +#include "e2ee/e2ee.h" +#include "e2ee/qolmaccount.h" +#include "e2ee/qolmerrors.h" +#include "e2ee/qolminboundsession.h" #endif // Quotient_E2EE_ENABLED #include "database.h" -- cgit v1.2.3