From 3fcc0c5acb160364e819688cc67a8aaf814fafef Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Sat, 14 May 2022 20:51:05 +0200 Subject: Optimise #includes for QOlm* classes --- lib/connection.cpp | 15 ++++++++------- lib/e2ee/qolmaccount.cpp | 3 +++ lib/e2ee/qolmaccount.h | 6 ------ lib/e2ee/qolmsession.cpp | 7 ++++--- lib/e2ee/qolmsession.h | 7 ++----- 5 files changed, 17 insertions(+), 21 deletions(-) (limited to 'lib') diff --git a/lib/connection.cpp b/lib/connection.cpp index bd4d9251..511b64e2 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -35,16 +35,17 @@ #include "jobs/syncjob.h" #ifdef Quotient_E2EE_ENABLED -# include "e2ee/qolmaccount.h" -# include "e2ee/qolmutils.h" # include "database.h" +# include "e2ee/qolmaccount.h" # include "e2ee/qolminboundsession.h" +# include "e2ee/qolmsession.h" +# include "e2ee/qolmutils.h" -#if QT_VERSION_MAJOR >= 6 -# include -#else -# include -#endif +# if QT_VERSION_MAJOR >= 6 +# include +# else +# include +# endif #endif // Quotient_E2EE_ENABLED #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) diff --git a/lib/e2ee/qolmaccount.cpp b/lib/e2ee/qolmaccount.cpp index 476a60bd..af9eb1bf 100644 --- a/lib/e2ee/qolmaccount.cpp +++ b/lib/e2ee/qolmaccount.cpp @@ -5,6 +5,7 @@ #include "qolmaccount.h" #include "connection.h" +#include "e2ee/qolmsession.h" #include "e2ee/qolmutility.h" #include "e2ee/qolmutils.h" @@ -12,6 +13,8 @@ #include +#include + using namespace Quotient; QHash OneTimeKeys::curve25519() const diff --git a/lib/e2ee/qolmaccount.h b/lib/e2ee/qolmaccount.h index 17f43f1a..08301998 100644 --- a/lib/e2ee/qolmaccount.h +++ b/lib/e2ee/qolmaccount.h @@ -9,18 +9,12 @@ #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); diff --git a/lib/e2ee/qolmsession.cpp b/lib/e2ee/qolmsession.cpp index e575ff39..531a6696 100644 --- a/lib/e2ee/qolmsession.cpp +++ b/lib/e2ee/qolmsession.cpp @@ -3,10 +3,12 @@ // SPDX-License-Identifier: LGPL-2.1-or-later #include "qolmsession.h" + #include "e2ee/qolmutils.h" #include "logging.h" + #include -#include +#include using namespace Quotient; @@ -247,5 +249,4 @@ std::variant QOlmSession::matchesInboundSessionFrom(const QStri QOlmSession::QOlmSession(OlmSession *session) : m_session(session) -{ -} +{} diff --git a/lib/e2ee/qolmsession.h b/lib/e2ee/qolmsession.h index f20c9837..4eb151b6 100644 --- a/lib/e2ee/qolmsession.h +++ b/lib/e2ee/qolmsession.h @@ -4,17 +4,14 @@ #pragma once -#include -#include // FIXME: OlmSession #include "e2ee/e2ee.h" #include "e2ee/qolmmessage.h" #include "e2ee/qolmerrors.h" #include "e2ee/qolmaccount.h" -namespace Quotient { +struct OlmSession; -class QOlmAccount; -class QOlmSession; +namespace Quotient { //! Either an outbound or inbound session for secure communication. class QUOTIENT_API QOlmSession -- cgit v1.2.3 From 9c8c33ab0b2138b45cbfe29f4be235f631730826 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Mon, 16 May 2022 10:41:36 +0200 Subject: Expected<> This is a minimal implementation along the lines of `std::expected<>` introduced in C++23; once compilers catch up with C++23 support, it may become simply a typedef of std::expected. There are no tests as yet; but the following commits will introduce QOlmExpected that would replace the current `std::variant` pattern used throughout `QOlm*` classes, automatically pulling Expected under the coverage of `QOlm*` unit tests. --- CMakeLists.txt | 1 + lib/expected.h | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 lib/expected.h (limited to 'lib') diff --git a/CMakeLists.txt b/CMakeLists.txt index 537d580e..404ba87c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,6 +118,7 @@ list(APPEND lib_SRCS lib/quotient_export.h lib/function_traits.h lib/function_traits.cpp lib/omittable.h + lib/expected.h lib/networkaccessmanager.h lib/networkaccessmanager.cpp lib/connectiondata.h lib/connectiondata.cpp lib/connection.h lib/connection.cpp diff --git a/lib/expected.h b/lib/expected.h new file mode 100644 index 00000000..c8b8fd64 --- /dev/null +++ b/lib/expected.h @@ -0,0 +1,74 @@ +#pragma once + +#include + +namespace Quotient { + +//! \brief A minimal subset of std::expected from C++23 +template , bool> = true> +class Expected { +private: + template + using enable_if_constructible_t = std::enable_if_t< + std::is_constructible_v || std::is_constructible_v>; + +public: + using value_type = T; + using error_type = E; + + Expected() = default; + explicit Expected(const Expected&) = default; + explicit Expected(Expected&&) noexcept = default; + + template > + Expected(X&& x) + : data(std::forward(x)) + {} + + Expected& operator=(const Expected&) = default; + Expected& operator=(Expected&&) noexcept = default; + + template > + Expected& operator=(X&& x) + { + data = std::forward(x); + return *this; + } + + bool has_value() const { return std::holds_alternative(data); } + explicit operator bool() const { return has_value(); } + + const value_type& value() const& { return std::get(data); } + value_type& value() & { return std::get(data); } + value_type value() && { return std::get(std::move(data)); } + + const value_type& operator*() const& { return value(); } + value_type& operator*() & { return value(); } + + const value_type* operator->() const& { return std::get_if(&data); } + value_type* operator->() & { return std::get_if(&data); } + + template + T value_or(U&& fallback) const& + { + if (has_value()) + return value(); + return std::forward(fallback); + } + template + T value_or(U&& fallback) && + { + if (has_value()) + return value(); + return std::forward(fallback); + } + + const E& error() const& { return std::get(data); } + E& error() & { return std::get(data); } + +private: + std::variant data; +}; + +} // namespace Quotient -- cgit v1.2.3 From 1fe8dc00de4d9bb7072ec9677ec7f8e73e4fc769 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Sun, 15 May 2022 21:52:31 +0200 Subject: QOlmAccount::needsSave() shouldn't be const Making Qt signals const is an impossible commitment - once the signal is out, you can't control if any called slot will change the emitting class or not. The code compiles but const-ness is not preserved. --- lib/e2ee/qolmaccount.cpp | 5 +++-- lib/e2ee/qolmaccount.h | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) (limited to 'lib') diff --git a/lib/e2ee/qolmaccount.cpp b/lib/e2ee/qolmaccount.cpp index af9eb1bf..3339ce46 100644 --- a/lib/e2ee/qolmaccount.cpp +++ b/lib/e2ee/qolmaccount.cpp @@ -143,7 +143,7 @@ size_t QOlmAccount::maxNumberOfOneTimeKeys() const return olm_account_max_number_of_one_time_keys(m_account); } -size_t QOlmAccount::generateOneTimeKeys(size_t numberOfKeys) const +size_t QOlmAccount::generateOneTimeKeys(size_t numberOfKeys) { const size_t randomLength = olm_account_generate_one_time_keys_random_length(m_account, numberOfKeys); QByteArray randomBuffer = getRandom(randomLength); @@ -196,7 +196,8 @@ QByteArray QOlmAccount::signOneTimeKey(const QString &key) const return sign(j.toJson(QJsonDocument::Compact)); } -std::optional QOlmAccount::removeOneTimeKeys(const QOlmSessionPtr &session) const +std::optional QOlmAccount::removeOneTimeKeys( + const QOlmSessionPtr& session) { const auto error = olm_remove_one_time_keys(m_account, session->raw()); diff --git a/lib/e2ee/qolmaccount.h b/lib/e2ee/qolmaccount.h index 08301998..1f36919a 100644 --- a/lib/e2ee/qolmaccount.h +++ b/lib/e2ee/qolmaccount.h @@ -24,7 +24,7 @@ class QUOTIENT_API QOlmAccount : public QObject Q_OBJECT public: QOlmAccount(const QString &userId, const QString &deviceId, QObject *parent = nullptr); - ~QOlmAccount(); + ~QOlmAccount() override; //! Creates a new instance of OlmAccount. During the instantiation //! the Ed25519 fingerprint key pair and the Curve25519 identity key @@ -55,7 +55,7 @@ public: size_t maxNumberOfOneTimeKeys() const; //! Generates the supplied number of one time keys. - size_t generateOneTimeKeys(size_t numberOfKeys) const; + size_t generateOneTimeKeys(size_t numberOfKeys); //! Gets the OlmAccount's one time keys formatted as JSON. OneTimeKeys oneTimeKeys() const; @@ -97,7 +97,7 @@ public: OlmAccount *data(); Q_SIGNALS: - void needsSave() const; + void needsSave(); private: OlmAccount *m_account = nullptr; // owning -- cgit v1.2.3 From a3486fd0e9786c47564ce8173a2e4039135b10f9 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Sun, 15 May 2022 22:08:09 +0200 Subject: Simplify QOlmSession::matchesInboundSession*() There's no particular use in letting `QOlmError` out, only to confirm that, well, `QOlmError` is just another form of no-match. --- autotests/testolmsession.cpp | 2 +- lib/connection.cpp | 3 +-- lib/e2ee/qolmsession.cpp | 43 +++++++++++++++++-------------------------- 3 files changed, 19 insertions(+), 29 deletions(-) (limited to 'lib') diff --git a/autotests/testolmsession.cpp b/autotests/testolmsession.cpp index 5436c392..2674b60b 100644 --- a/autotests/testolmsession.cpp +++ b/autotests/testolmsession.cpp @@ -45,7 +45,7 @@ void TestOlmSession::olmEncryptDecrypt() const auto encrypted = outboundSession->encrypt("Hello world!"); if (encrypted.type() == QOlmMessage::PreKey) { QOlmMessage m(encrypted); // clone - QVERIFY(std::get(inboundSession->matchesInboundSession(m))); + QVERIFY(inboundSession->matchesInboundSession(m)); } const auto decrypted = std::get(inboundSession->decrypt(encrypted)); diff --git a/lib/connection.cpp b/lib/connection.cpp index 511b64e2..955b5b1a 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -226,8 +226,7 @@ public: { Q_ASSERT(message.type() == QOlmMessage::PreKey); for(auto& session : olmSessions[senderKey]) { - const auto matches = session->matchesInboundSessionFrom(senderKey, message); - if(std::holds_alternative(matches) && std::get(matches)) { + if (session->matchesInboundSessionFrom(senderKey, message)) { qCDebug(E2EE) << "Found inbound session"; const auto result = session->decrypt(message); if(std::holds_alternative(result)) { diff --git a/lib/e2ee/qolmsession.cpp b/lib/e2ee/qolmsession.cpp index 531a6696..5ccbfd40 100644 --- a/lib/e2ee/qolmsession.cpp +++ b/lib/e2ee/qolmsession.cpp @@ -209,42 +209,33 @@ bool QOlmSession::hasReceivedMessage() const return olm_session_has_received_message(m_session); } -std::variant QOlmSession::matchesInboundSession(const QOlmMessage &preKeyMessage) const +bool 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()); + const auto maybeMatches = + olm_matches_inbound_session(m_session, oneTimeKeyBuf.data(), + oneTimeKeyBuf.length()); - if (matchesResult == olm_error()) { + if (maybeMatches == olm_error()) { return lastError(m_session); } - switch (matchesResult) { - case 0: - return false; - case 1: - return true; - default: - return QOlmError::Unknown; - } + return maybeMatches == 1; } -std::variant QOlmSession::matchesInboundSessionFrom(const QString &theirIdentityKey, const QOlmMessage &preKeyMessage) const + +bool 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; - } + const auto maybeMatches = olm_matches_inbound_session_from( + m_session, theirIdentityKeyBuf.data(), theirIdentityKeyBuf.length(), + oneTimeKeyMessageBuf.data(), oneTimeKeyMessageBuf.length()); + + if (maybeMatches == olm_error()) + qCWarning(E2EE) << "Error matching an inbound session:" + << olm_session_last_error(m_session); + return maybeMatches == 1; } QOlmSession::QOlmSession(OlmSession *session) -- cgit v1.2.3 From 79b3dba1ed4b6870c4e989ada88e33b1ce0ddc21 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Mon, 16 May 2022 10:41:54 +0200 Subject: QOlmExpected and associated refactoring As mentioned in the commit introducing `Expected`, `QOlmExpected` is simply an alias for `Expected`. This simplifies quite a few function signatures in `QOlm*` classes and collapses unwieldy `std::holds_alternative<>`/`std::get<>` constructs into a neat contextual bool cast and an invocation of `operator*` or `value()`/`error()` accessors that don't need to specify the type. While refactoring the code, I found a couple of cases of mismatching `uint32_t` and `qint32_t` in return values; a couple of cases where `decrypt()` returns `QString` which is in fact `QByteArray` (e.g., in `QOlmSession::decrypt()`); there's a repetitive algorithm in `Connection::Private::sessionDecryptPrekey()` and `sessionDecryptGeneral()` --- autotests/testgroupsession.cpp | 14 ++-- autotests/testolmaccount.cpp | 5 +- autotests/testolmsession.cpp | 14 ++-- autotests/testolmutility.cpp | 5 +- lib/connection.cpp | 136 ++++++++++++++++++++------------------- lib/database.cpp | 32 ++++----- lib/e2ee/e2ee.h | 8 +++ lib/e2ee/qolmaccount.cpp | 20 +++--- lib/e2ee/qolmaccount.h | 21 +++--- lib/e2ee/qolminboundsession.cpp | 10 +-- lib/e2ee/qolminboundsession.h | 14 ++-- lib/e2ee/qolmoutboundsession.cpp | 18 +++--- lib/e2ee/qolmoutboundsession.h | 19 +++--- lib/e2ee/qolmsession.cpp | 22 +++++-- lib/e2ee/qolmsession.h | 35 +++++----- lib/e2ee/qolmutility.cpp | 14 ++-- lib/e2ee/qolmutility.h | 7 +- lib/room.cpp | 17 +++-- 18 files changed, 215 insertions(+), 196 deletions(-) (limited to 'lib') diff --git a/autotests/testgroupsession.cpp b/autotests/testgroupsession.cpp index 2566669e..3c329a8a 100644 --- a/autotests/testgroupsession.cpp +++ b/autotests/testgroupsession.cpp @@ -16,11 +16,11 @@ void TestGroupSession::groupSessionPicklingValid() QVERIFY(QByteArray::fromBase64(ogsId).size() > 0); QCOMPARE(0, ogs->sessionMessageIndex()); - auto ogsPickled = std::get(ogs->pickle(Unencrypted {})); - auto ogs2 = std::get(QOlmOutboundGroupSession::unpickle(ogsPickled, Unencrypted {})); + auto ogsPickled = ogs->pickle(Unencrypted {}).value(); + auto ogs2 = QOlmOutboundGroupSession::unpickle(ogsPickled, Unencrypted {}).value(); QCOMPARE(ogsId, ogs2->sessionId()); - auto igs = QOlmInboundGroupSession::create(std::get(ogs->sessionKey())); + auto igs = QOlmInboundGroupSession::create(ogs->sessionKey().value()); const auto igsId = igs->sessionId(); // ID is valid base64? QVERIFY(QByteArray::fromBase64(igsId).size() > 0); @@ -29,22 +29,22 @@ void TestGroupSession::groupSessionPicklingValid() QCOMPARE(0, igs->firstKnownIndex()); auto igsPickled = igs->pickle(Unencrypted {}); - igs = std::get(QOlmInboundGroupSession::unpickle(igsPickled, Unencrypted {})); + igs = QOlmInboundGroupSession::unpickle(igsPickled, Unencrypted {}).value(); QCOMPARE(igsId, igs->sessionId()); } void TestGroupSession::groupSessionCryptoValid() { auto ogs = QOlmOutboundGroupSession::create(); - auto igs = QOlmInboundGroupSession::create(std::get(ogs->sessionKey())); + auto igs = QOlmInboundGroupSession::create(ogs->sessionKey().value()); QCOMPARE(ogs->sessionId(), igs->sessionId()); const auto plainText = QStringLiteral("Hello world!"); - const auto ciphertext = std::get(ogs->encrypt(plainText)); + const auto ciphertext = ogs->encrypt(plainText).value(); // ciphertext valid base64? QVERIFY(QByteArray::fromBase64(ciphertext).size() > 0); - const auto decryptionResult = std::get>(igs->decrypt(ciphertext)); + const auto decryptionResult = igs->decrypt(ciphertext).value(); //// correct plaintext? QCOMPARE(plainText, decryptionResult.first); diff --git a/autotests/testolmaccount.cpp b/autotests/testolmaccount.cpp index 9989665a..e31ff6d3 100644 --- a/autotests/testolmaccount.cpp +++ b/autotests/testolmaccount.cpp @@ -21,7 +21,7 @@ void TestOlmAccount::pickleUnpickledTest() QOlmAccount olmAccount(QStringLiteral("@foo:bar.com"), QStringLiteral("QuotientTestDevice")); olmAccount.createNewAccount(); auto identityKeys = olmAccount.identityKeys(); - auto pickled = std::get(olmAccount.pickle(Unencrypted{})); + auto pickled = olmAccount.pickle(Unencrypted{}).value(); QOlmAccount olmAccount2(QStringLiteral("@foo:bar.com"), QStringLiteral("QuotientTestDevice")); olmAccount2.unpickle(pickled, Unencrypted{}); auto identityKeys2 = olmAccount2.identityKeys(); @@ -57,8 +57,7 @@ void TestOlmAccount::signatureValid() const auto identityKeys = olmAccount.identityKeys(); const auto ed25519Key = identityKeys.ed25519; const auto verify = utility.ed25519Verify(ed25519Key, message, signature); - QVERIFY(std::holds_alternative(verify)); - QVERIFY(std::get(verify) == true); + QVERIFY(verify.value_or(false)); } void TestOlmAccount::oneTimeKeysValid() diff --git a/autotests/testolmsession.cpp b/autotests/testolmsession.cpp index 2674b60b..182659e7 100644 --- a/autotests/testolmsession.cpp +++ b/autotests/testolmsession.cpp @@ -20,8 +20,8 @@ std::pair createSessionPair() const QByteArray oneTimeKeyA("WzsbsjD85iB1R32iWxfJdwkgmdz29ClMbJSJziECYwk"); const QByteArray identityKeyB("q/YhJtog/5VHCAS9rM9uUf6AaFk1yPe4GYuyUOXyQCg"); const QByteArray oneTimeKeyB("oWvzryma+B2onYjo3hM6A3Mgo/Yepm8HvgSvwZMTnjQ"); - auto outbound = std::get(accountA - .createOutboundSession(identityKeyB, oneTimeKeyB)); + auto outbound = + accountA.createOutboundSession(identityKeyB, oneTimeKeyB).value(); const auto preKey = outbound->encrypt(""); // Payload does not matter for PreKey @@ -29,7 +29,7 @@ std::pair createSessionPair() // We can't call QFail here because it's an helper function returning a value throw "Wrong first message type received, can't create session"; } - auto inbound = std::get(accountB.createInboundSession(preKey)); + auto inbound = accountB.createInboundSession(preKey).value(); return { std::move(inbound), std::move(outbound) }; } @@ -48,7 +48,7 @@ void TestOlmSession::olmEncryptDecrypt() QVERIFY(inboundSession->matchesInboundSession(m)); } - const auto decrypted = std::get(inboundSession->decrypt(encrypted)); + const auto decrypted = inboundSession->decrypt(encrypted).value(); QCOMPARE(decrypted, "Hello world!"); } @@ -56,11 +56,11 @@ void TestOlmSession::olmEncryptDecrypt() void TestOlmSession::correctSessionOrdering() { // n0W5IJ2ZmaI9FxKRj/wohUQ6WEU0SfoKsgKKHsr4VbM - auto session1 = std::get(QOlmSession::unpickle("7g5cfQRsDk2ROXf9S01n2leZiFRon+EbvXcMOADU0UGvlaV6t/0ihD2/0QGckDIvbmE1aV+PxB0zUtHXh99bI/60N+PWkCLA84jEY4sz3d45ui/TVoFGLDHlymKxvlj7XngXrbtlxSkVntsPzDiNpKEXCa26N2ubKpQ0fbjrV5gbBTYWfU04DXHPXFDTksxpNALYt/h0eVMVhf6hB0ZzpLBsOG0mpwkLufwub0CuDEDGGmRddz3TcNCLq5NnI8R9udDWvHAkTS1UTbHuIf/y6cZg875nJyXpAvd8/XhL8TOo8ot2sE1fElBa4vrH/m9rBQMC1GPkhLBIizmY44C+Sq9PQRnF+uCZ", Unencrypted{})); + auto session1 = QOlmSession::unpickle("7g5cfQRsDk2ROXf9S01n2leZiFRon+EbvXcMOADU0UGvlaV6t/0ihD2/0QGckDIvbmE1aV+PxB0zUtHXh99bI/60N+PWkCLA84jEY4sz3d45ui/TVoFGLDHlymKxvlj7XngXrbtlxSkVntsPzDiNpKEXCa26N2ubKpQ0fbjrV5gbBTYWfU04DXHPXFDTksxpNALYt/h0eVMVhf6hB0ZzpLBsOG0mpwkLufwub0CuDEDGGmRddz3TcNCLq5NnI8R9udDWvHAkTS1UTbHuIf/y6cZg875nJyXpAvd8/XhL8TOo8ot2sE1fElBa4vrH/m9rBQMC1GPkhLBIizmY44C+Sq9PQRnF+uCZ", Unencrypted{}).value(); // +9pHJhP3K4E5/2m8PYBPLh8pS9CJodwUOh8yz3mnmw0 - auto session2 = std::get(QOlmSession::unpickle("7g5cfQRsDk2ROXf9S01n2leZiFRon+EbvXcMOADU0UFD+q37/WlfTAzQsSjCdD07FcErZ4siEy5vpiB+pyO8i53ptZvb2qRvqNKFzPaXuu33PS2PBTmmnR+kJt+DgDNqWadyaj/WqEAejc7ALqSs5GuhbZtpoLe+lRSRK0rwVX3gzz4qrl8pm0pD5pSZAUWRXDRlieGWMclz68VUvnSaQH7ElTo4S634CJk+xQfFFCD26v0yONPSN6rwouS1cWPuG5jTlnV8vCFVTU2+lduKh54Ko6FUJ/ei4xR8Nk2duBGSc/TdllX9e2lDYHSUkWoD4ti5xsFioB8Blus7JK9BZfcmRmdlxIOD", Unencrypted {})); + auto session2 = QOlmSession::unpickle("7g5cfQRsDk2ROXf9S01n2leZiFRon+EbvXcMOADU0UFD+q37/WlfTAzQsSjCdD07FcErZ4siEy5vpiB+pyO8i53ptZvb2qRvqNKFzPaXuu33PS2PBTmmnR+kJt+DgDNqWadyaj/WqEAejc7ALqSs5GuhbZtpoLe+lRSRK0rwVX3gzz4qrl8pm0pD5pSZAUWRXDRlieGWMclz68VUvnSaQH7ElTo4S634CJk+xQfFFCD26v0yONPSN6rwouS1cWPuG5jTlnV8vCFVTU2+lduKh54Ko6FUJ/ei4xR8Nk2duBGSc/TdllX9e2lDYHSUkWoD4ti5xsFioB8Blus7JK9BZfcmRmdlxIOD", Unencrypted {}).value(); // MC7n8hX1l7WlC2/WJGHZinMocgiBZa4vwGAOredb/ME - auto session3 = std::get(QOlmSession::unpickle("7g5cfQRsDk2ROXf9S01n2leZiFRon+EbvXcMOADU0UGNk2TmVDJ95K0Nywf24FNklNVtXtFDiFPHFwNSmCbHNCp3hsGtZlt0AHUkMmL48XklLqzwtVk5/v2RRmSKR5LqYdIakrtuK/fY0ENhBZIbI1sRetaJ2KMbY9l6rCJNfFg8VhpZ4KTVvEZVuP9g/eZkCnP5NxzXiBRF6nfY3O/zhcKxa3acIqs6BMhyLsfuJ80t+hQ1HvVyuhBerGujdSDzV9tJ9SPidOwfYATk81LVF9hTmnI0KaZa7qCtFzhG0dU/Z3hIWH9HOaw1aSB/IPmughbwdJOwERyhuo3YHoznlQnJ7X252BlI", Unencrypted{})); + auto session3 = QOlmSession::unpickle("7g5cfQRsDk2ROXf9S01n2leZiFRon+EbvXcMOADU0UGNk2TmVDJ95K0Nywf24FNklNVtXtFDiFPHFwNSmCbHNCp3hsGtZlt0AHUkMmL48XklLqzwtVk5/v2RRmSKR5LqYdIakrtuK/fY0ENhBZIbI1sRetaJ2KMbY9l6rCJNfFg8VhpZ4KTVvEZVuP9g/eZkCnP5NxzXiBRF6nfY3O/zhcKxa3acIqs6BMhyLsfuJ80t+hQ1HvVyuhBerGujdSDzV9tJ9SPidOwfYATk81LVF9hTmnI0KaZa7qCtFzhG0dU/Z3hIWH9HOaw1aSB/IPmughbwdJOwERyhuo3YHoznlQnJ7X252BlI", Unencrypted{}).value(); const auto session1Id = session1->sessionId(); const auto session2Id = session2->sessionId(); diff --git a/autotests/testolmutility.cpp b/autotests/testolmutility.cpp index 92b8b5b2..c1323533 100644 --- a/autotests/testolmutility.cpp +++ b/autotests/testolmutility.cpp @@ -81,7 +81,10 @@ void TestOlmUtility::verifySignedOneTimeKey() delete[](reinterpret_cast(utility)); QOlmUtility utility2; - auto res2 = std::get(utility2.ed25519Verify(aliceOlm.identityKeys().ed25519, msg, signatureBuf1)); + auto res2 = + utility2 + .ed25519Verify(aliceOlm.identityKeys().ed25519, msg, signatureBuf1) + .value_or(false); //QCOMPARE(std::string(olm_utility_last_error(utility)), "SUCCESS"); QCOMPARE(res2, true); diff --git a/lib/connection.cpp b/lib/connection.cpp index 955b5b1a..2528b70b 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -212,82 +212,83 @@ public: void loadSessions() { olmSessions = q->database()->loadOlmSessions(q->picklingMode()); } - void saveSession(QOlmSessionPtr& session, const QString &senderKey) { - auto pickleResult = session->pickle(q->picklingMode()); - if (std::holds_alternative(pickleResult)) { - qCWarning(E2EE) << "Failed to pickle olm session. Error" << std::get(pickleResult); - return; - } - q->database()->saveOlmSession(senderKey, session->sessionId(), std::get(pickleResult), QDateTime::currentDateTime()); - } - - std::pair sessionDecryptPrekey(const QOlmMessage& message, - const QString& senderKey) + void saveSession(QOlmSession& session, const QString& senderKey) { - Q_ASSERT(message.type() == QOlmMessage::PreKey); - for(auto& session : olmSessions[senderKey]) { - if (session->matchesInboundSessionFrom(senderKey, message)) { - qCDebug(E2EE) << "Found inbound session"; - const auto result = session->decrypt(message); - if(std::holds_alternative(result)) { - q->database()->setOlmSessionLastReceived(QString(session->sessionId()), QDateTime::currentDateTime()); - return { std::get(result), session->sessionId() }; - } else { - qCDebug(E2EE) << "Failed to decrypt prekey message"; - return {}; - } - } - } - qCDebug(E2EE) << "Creating new inbound session"; - auto newSessionResult = olmAccount->createInboundSessionFrom(senderKey.toUtf8(), message); - if(std::holds_alternative(newSessionResult)) { - qCWarning(E2EE) << "Failed to create inbound session for" << senderKey << std::get(newSessionResult); - return {}; - } - auto newSession = std::move(std::get(newSessionResult)); - auto error = olmAccount->removeOneTimeKeys(newSession); - if (error) { - qWarning(E2EE) << "Failed to remove one time key for session" << newSession->sessionId(); - } - const auto result = newSession->decrypt(message); - QString sessionId = newSession->sessionId(); - saveSession(newSession, senderKey); - olmSessions[senderKey].push_back(std::move(newSession)); - if(std::holds_alternative(result)) { - return { std::get(result), sessionId }; - } else { - qCDebug(E2EE) << "Failed to decrypt prekey message with new session"; - return {}; - } + if (auto pickleResult = session.pickle(q->picklingMode())) + q->database()->saveOlmSession(senderKey, session.sessionId(), + *pickleResult, + QDateTime::currentDateTime()); + else + qCWarning(E2EE) << "Failed to pickle olm session. Error" + << pickleResult.error(); } - std::pair sessionDecryptGeneral(const QOlmMessage& message, const QString &senderKey) + + template + std::pair doDecryptMessage(const QOlmSession& session, + const QOlmMessage& message, + FnT&& andThen) const { - Q_ASSERT(message.type() == QOlmMessage::General); - for(auto& session : olmSessions[senderKey]) { - const auto result = session->decrypt(message); - if(std::holds_alternative(result)) { - q->database()->setOlmSessionLastReceived(QString(session->sessionId()), QDateTime::currentDateTime()); - return { std::get(result), session->sessionId() }; - } + const auto expectedMessage = session.decrypt(message); + if (expectedMessage) { + const auto result = + std::make_pair(*expectedMessage, session.sessionId()); + andThen(); + return result; } - qCWarning(E2EE) << "Failed to decrypt message"; + const auto errorLine = message.type() == QOlmMessage::PreKey + ? "Failed to decrypt prekey message:" + : "Failed to decrypt message:"; + qCDebug(E2EE) << errorLine << expectedMessage.error(); return {}; } std::pair sessionDecryptMessage( const QJsonObject& personalCipherObject, const QByteArray& senderKey) { + const auto msgType = static_cast( + personalCipherObject.value(TypeKeyL).toInt(-1)); + if (msgType != QOlmMessage::General && msgType != QOlmMessage::PreKey) { + qCWarning(E2EE) << "Olm message has incorrect type" << msgType; + return {}; + } QOlmMessage message { - personalCipherObject.value(BodyKeyL).toString().toLatin1(), - static_cast( - personalCipherObject.value(TypeKeyL).toInt(-1)) + personalCipherObject.value(BodyKeyL).toString().toLatin1(), msgType }; - if (message.type() == QOlmMessage::PreKey) - return sessionDecryptPrekey(message, senderKey); - if (message.type() == QOlmMessage::General) - return sessionDecryptGeneral(message, senderKey); - qCWarning(E2EE) << "Olm message has incorrect type" << message.type(); - return {}; + for (const auto& session : olmSessions[senderKey]) + if (msgType == QOlmMessage::General + || session->matchesInboundSessionFrom(senderKey, message)) { + return doDecryptMessage(*session, message, [this, &session] { + q->database()->setOlmSessionLastReceived( + session->sessionId(), QDateTime::currentDateTime()); + }); + } + + if (msgType == QOlmMessage::General) { + qCWarning(E2EE) << "Failed to decrypt message"; + return {}; + } + + qCDebug(E2EE) << "Creating new inbound session"; // Pre-key messages only + auto newSessionResult = + olmAccount->createInboundSessionFrom(senderKey, message); + if (!newSessionResult) { + qCWarning(E2EE) + << "Failed to create inbound session for" << senderKey + << "with error" << newSessionResult.error(); + return {}; + } + auto newSession = std::move(*newSessionResult); + auto error = olmAccount->removeOneTimeKeys(*newSession); + if (error) { + qWarning(E2EE) << "Failed to remove one time key for session" + << newSession->sessionId(); + // Keep going though + } + return doDecryptMessage( + *newSession, message, [this, &senderKey, &newSession] { + saveSession(*newSession, senderKey); + olmSessions[senderKey].push_back(std::move(newSession)); + }); } #endif @@ -2177,8 +2178,11 @@ void Connection::saveOlmAccount() { qCDebug(E2EE) << "Saving olm account"; #ifdef Quotient_E2EE_ENABLED - auto pickle = d->olmAccount->pickle(d->picklingMode); - d->database->setAccountPickle(std::get(pickle)); + if (const auto expectedPickle = d->olmAccount->pickle(d->picklingMode)) + d->database->setAccountPickle(*expectedPickle); + else + qCWarning(E2EE) << "Couldn't save Olm account pickle:" + << expectedPickle.error(); #endif } diff --git a/lib/database.cpp b/lib/database.cpp index 3189b3f1..e2e7acc9 100644 --- a/lib/database.cpp +++ b/lib/database.cpp @@ -184,12 +184,14 @@ UnorderedMap> Database::loadOlmSessions(con commit(); UnorderedMap> sessions; while (query.next()) { - auto session = QOlmSession::unpickle(query.value("pickle").toByteArray(), picklingMode); - if (std::holds_alternative(session)) { - qCWarning(E2EE) << "Failed to unpickle olm session"; - continue; - } - sessions[query.value("senderKey").toString()].push_back(std::move(std::get(session))); + if (auto expectedSession = + QOlmSession::unpickle(query.value("pickle").toByteArray(), + picklingMode)) { + sessions[query.value("senderKey").toString()].emplace_back( + std::move(*expectedSession)); + } else + qCWarning(E2EE) + << "Failed to unpickle olm session:" << expectedSession.error(); } return sessions; } @@ -203,15 +205,15 @@ UnorderedMap Database::loadMegolmSessions(c commit(); UnorderedMap sessions; while (query.next()) { - auto session = QOlmInboundGroupSession::unpickle(query.value("pickle").toByteArray(), picklingMode); - if (std::holds_alternative(session)) { - qCWarning(E2EE) << "Failed to unpickle megolm session"; - continue; - } - - sessions[query.value("sessionId").toString()] = std::move(std::get(session)); - sessions[query.value("sessionId").toString()]->setOlmSessionId(query.value("olmSessionId").toString()); - sessions[query.value("sessionId").toString()]->setSenderId(query.value("senderId").toString()); + if (auto expectedSession = QOlmInboundGroupSession::unpickle( + query.value("pickle").toByteArray(), picklingMode)) { + auto& sessionPtr = sessions[query.value("sessionId").toString()] = + std::move(*expectedSession); + sessionPtr->setOlmSessionId(query.value("olmSessionId").toString()); + sessionPtr->setSenderId(query.value("senderId").toString()); + } else + qCWarning(E2EE) << "Failed to unpickle megolm session:" + << expectedSession.error(); } return sessions; } diff --git a/lib/e2ee/e2ee.h b/lib/e2ee/e2ee.h index 268cb525..8e433d60 100644 --- a/lib/e2ee/e2ee.h +++ b/lib/e2ee/e2ee.h @@ -6,6 +6,8 @@ #pragma once #include "converters.h" +#include "expected.h" +#include "qolmerrors.h" #include "quotient_common.h" #include @@ -55,6 +57,12 @@ using QOlmSessionPtr = std::unique_ptr; class QOlmInboundGroupSession; using QOlmInboundGroupSessionPtr = std::unique_ptr; +class QOlmOutboundGroupSession; +using QOlmOutboundGroupSessionPtr = std::unique_ptr; + +template +using QOlmExpected = Expected; + struct IdentityKeys { QByteArray curve25519; diff --git a/lib/e2ee/qolmaccount.cpp b/lib/e2ee/qolmaccount.cpp index 3339ce46..72dddafb 100644 --- a/lib/e2ee/qolmaccount.cpp +++ b/lib/e2ee/qolmaccount.cpp @@ -73,7 +73,7 @@ void QOlmAccount::unpickle(QByteArray &pickled, const PicklingMode &mode) } } -std::variant QOlmAccount::pickle(const PicklingMode &mode) +QOlmExpected QOlmAccount::pickle(const PicklingMode &mode) { const QByteArray key = toKey(mode); const size_t pickleLength = olm_pickle_account_length(m_account); @@ -197,9 +197,9 @@ QByteArray QOlmAccount::signOneTimeKey(const QString &key) const } std::optional QOlmAccount::removeOneTimeKeys( - const QOlmSessionPtr& session) + const QOlmSession& session) { - const auto error = olm_remove_one_time_keys(m_account, session->raw()); + const auto error = olm_remove_one_time_keys(m_account, session.raw()); if (error == olm_error()) { return lastError(m_account); @@ -245,19 +245,19 @@ UploadKeysJob *QOlmAccount::createUploadKeyRequest(const OneTimeKeys &oneTimeKey return new UploadKeysJob(keys, oneTimeKeysSigned); } -std::variant QOlmAccount::createInboundSession(const QOlmMessage &preKeyMessage) +QOlmExpected 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) +QOlmExpected 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) +QOlmExpected QOlmAccount::createOutboundSession(const QByteArray &theirIdentityKey, const QByteArray &theirOneTimeKey) { return QOlmSession::createOutboundSession(this, theirIdentityKey, theirOneTimeKey); } @@ -296,10 +296,6 @@ bool Quotient::ed25519VerifySignature(const QString& signingKey, 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); + return utility.ed25519Verify(signingKeyBuf, canonicalJson, signatureBuf) + .value_or(false); } diff --git a/lib/e2ee/qolmaccount.h b/lib/e2ee/qolmaccount.h index 1f36919a..ee2aa69d 100644 --- a/lib/e2ee/qolmaccount.h +++ b/lib/e2ee/qolmaccount.h @@ -5,11 +5,12 @@ #pragma once -#include "csapi/keys.h" #include "e2ee/e2ee.h" -#include "e2ee/qolmerrors.h" #include "e2ee/qolmmessage.h" -#include + +#include "csapi/keys.h" + +#include struct OlmAccount; @@ -38,7 +39,7 @@ public: void unpickle(QByteArray &pickled, const PicklingMode &mode); //! Serialises an OlmAccount to encrypted Base64. - std::variant pickle(const PicklingMode &mode); + QOlmExpected pickle(const PicklingMode &mode); //! Returns the account's public identity keys already formatted as JSON IdentityKeys identityKeys() const; @@ -73,22 +74,26 @@ public: DeviceKeys deviceKeys() const; //! Remove the one time key used to create the supplied session. - [[nodiscard]] std::optional removeOneTimeKeys(const QOlmSessionPtr &session) const; + [[nodiscard]] std::optional removeOneTimeKeys( + const QOlmSession& session); //! 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); + QOlmExpected 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); + QOlmExpected 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); + QOlmExpected createOutboundSession( + const QByteArray& theirIdentityKey, const QByteArray& theirOneTimeKey); void markKeysAsPublished(); diff --git a/lib/e2ee/qolminboundsession.cpp b/lib/e2ee/qolminboundsession.cpp index 62856831..17f06205 100644 --- a/lib/e2ee/qolminboundsession.cpp +++ b/lib/e2ee/qolminboundsession.cpp @@ -70,7 +70,8 @@ QByteArray QOlmInboundGroupSession::pickle(const PicklingMode &mode) const return pickledBuf; } -std::variant, QOlmError> QOlmInboundGroupSession::unpickle(const QByteArray &pickled, const PicklingMode &mode) +QOlmExpected 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()]); @@ -85,7 +86,8 @@ std::variant, QOlmError> QOlmInboundGro return std::make_unique(groupSession); } -std::variant, QOlmError> QOlmInboundGroupSession::decrypt(const QByteArray &message) +QOlmExpected> QOlmInboundGroupSession::decrypt( + const QByteArray& message) { // This is for capturing the output of olm_group_decrypt uint32_t messageIndex = 0; @@ -114,10 +116,10 @@ std::variant, QOlmError> QOlmInboundGroupSession::d QByteArray output(plaintextLen, '0'); std::memcpy(output.data(), plaintextBuf.data(), plaintextLen); - return std::make_pair(QString(output), messageIndex); + return std::make_pair(output, messageIndex); } -std::variant QOlmInboundGroupSession::exportSession(uint32_t messageIndex) +QOlmExpected QOlmInboundGroupSession::exportSession(uint32_t messageIndex) { const auto keyLength = olm_export_inbound_group_session_length(m_groupSession); QByteArray keyBuf(keyLength, '0'); diff --git a/lib/e2ee/qolminboundsession.h b/lib/e2ee/qolminboundsession.h index 13515434..1a9b4415 100644 --- a/lib/e2ee/qolminboundsession.h +++ b/lib/e2ee/qolminboundsession.h @@ -5,11 +5,8 @@ #pragma once #include "e2ee/e2ee.h" -#include "e2ee/qolmerrors.h" -#include "olm/olm.h" -#include -#include +#include namespace Quotient { @@ -27,14 +24,13 @@ public: 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); + static QOlmExpected unpickle( + const QByteArray& pickled, const PicklingMode& mode); //! Decrypts ciphertext received for this group session. - std::variant, QOlmError> decrypt( - const QByteArray& message); + QOlmExpected > 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); + QOlmExpected 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. diff --git a/lib/e2ee/qolmoutboundsession.cpp b/lib/e2ee/qolmoutboundsession.cpp index da32417b..96bad344 100644 --- a/lib/e2ee/qolmoutboundsession.cpp +++ b/lib/e2ee/qolmoutboundsession.cpp @@ -13,8 +13,7 @@ QOlmError lastError(OlmOutboundGroupSession *session) { QOlmOutboundGroupSession::QOlmOutboundGroupSession(OlmOutboundGroupSession *session) : m_groupSession(session) -{ -} +{} QOlmOutboundGroupSession::~QOlmOutboundGroupSession() { @@ -22,7 +21,7 @@ QOlmOutboundGroupSession::~QOlmOutboundGroupSession() delete[](reinterpret_cast(m_groupSession)); } -std::unique_ptr QOlmOutboundGroupSession::create() +QOlmOutboundGroupSessionPtr 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); @@ -45,7 +44,7 @@ std::unique_ptr QOlmOutboundGroupSession::create() return std::make_unique(olmOutboundGroupSession); } -std::variant QOlmOutboundGroupSession::pickle(const PicklingMode &mode) +QOlmExpected QOlmOutboundGroupSession::pickle(const PicklingMode &mode) { QByteArray pickledBuf(olm_pickle_outbound_group_session_length(m_groupSession), '0'); QByteArray key = toKey(mode); @@ -61,7 +60,7 @@ std::variant QOlmOutboundGroupSession::pickle(const Pickl return pickledBuf; } -std::variant, QOlmError> QOlmOutboundGroupSession::unpickle(QByteArray &pickled, const PicklingMode &mode) +QOlmExpected QOlmOutboundGroupSession::unpickle(QByteArray &pickled, const PicklingMode &mode) { QByteArray pickledBuf = pickled; auto *olmOutboundGroupSession = olm_outbound_group_session(new uint8_t[olm_outbound_group_session_size()]); @@ -80,7 +79,7 @@ std::variant, QOlmError> QOlmOutboundG return std::make_unique(olmOutboundGroupSession); } -std::variant QOlmOutboundGroupSession::encrypt(const QString &plaintext) +QOlmExpected QOlmOutboundGroupSession::encrypt(const QString &plaintext) { QByteArray plaintextBuf = plaintext.toUtf8(); const auto messageMaxLength = olm_group_encrypt_message_length(m_groupSession, plaintextBuf.length()); @@ -112,12 +111,13 @@ QByteArray QOlmOutboundGroupSession::sessionId() const return idBuffer; } -std::variant QOlmOutboundGroupSession::sessionKey() const +QOlmExpected 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); + const auto error = olm_outbound_group_session_key( + m_groupSession, reinterpret_cast(keyBuffer.data()), + keyMaxLength); if (error == olm_error()) { return lastError(m_groupSession); } diff --git a/lib/e2ee/qolmoutboundsession.h b/lib/e2ee/qolmoutboundsession.h index 32ba2b3b..8058bbb1 100644 --- a/lib/e2ee/qolmoutboundsession.h +++ b/lib/e2ee/qolmoutboundsession.h @@ -4,10 +4,10 @@ #pragma once -#include "olm/olm.h" -#include "e2ee/qolmerrors.h" #include "e2ee/e2ee.h" + #include +#include namespace Quotient { @@ -19,15 +19,15 @@ public: ~QOlmOutboundGroupSession(); //! Creates a new instance of `QOlmOutboundGroupSession`. //! Throw OlmError on errors - static std::unique_ptr create(); + static QOlmOutboundGroupSessionPtr create(); //! Serialises a `QOlmOutboundGroupSession` to encrypted Base64. - std::variant pickle(const PicklingMode &mode); + QOlmExpected 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); + static QOlmExpected unpickle( + QByteArray& pickled, const PicklingMode& mode); //! Encrypts a plaintext message using the session. - std::variant encrypt(const QString &plaintext); + QOlmExpected encrypt(const QString& plaintext); //! Get the current message index for this session. //! @@ -42,11 +42,10 @@ public: //! //! 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; + QOlmExpected sessionKey() const; QOlmOutboundGroupSession(OlmOutboundGroupSession *groupSession); private: OlmOutboundGroupSession *m_groupSession; }; -using QOlmOutboundGroupSessionPtr = std::unique_ptr; -} +} // namespace Quotient diff --git a/lib/e2ee/qolmsession.cpp b/lib/e2ee/qolmsession.cpp index 5ccbfd40..2b149aac 100644 --- a/lib/e2ee/qolmsession.cpp +++ b/lib/e2ee/qolmsession.cpp @@ -27,7 +27,9 @@ 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) +QOlmExpected 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; @@ -53,17 +55,22 @@ std::variant QOlmSession::createInbound(QOlmAccount * return std::make_unique(olmSession); } -std::variant QOlmSession::createInboundSession(QOlmAccount *account, const QOlmMessage &preKeyMessage) +QOlmExpected QOlmSession::createInboundSession( + QOlmAccount* account, const QOlmMessage& preKeyMessage) { return createInbound(account, preKeyMessage); } -std::variant QOlmSession::createInboundSessionFrom(QOlmAccount *account, const QString &theirIdentityKey, const QOlmMessage &preKeyMessage) +QOlmExpected 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) +QOlmExpected QOlmSession::createOutboundSession( + QOlmAccount* account, const QString& theirIdentityKey, + const QString& theirOneTimeKey) { auto *olmOutboundSession = create(); const auto randomLen = olm_create_outbound_session_random_length(olmOutboundSession); @@ -89,7 +96,7 @@ std::variant QOlmSession::createOutboundSession(QOlmA return std::make_unique(olmOutboundSession); } -std::variant QOlmSession::pickle(const PicklingMode &mode) +QOlmExpected QOlmSession::pickle(const PicklingMode &mode) { QByteArray pickledBuf(olm_pickle_session_length(m_session), '0'); QByteArray key = toKey(mode); @@ -105,7 +112,8 @@ std::variant QOlmSession::pickle(const PicklingMode &mode return pickledBuf; } -std::variant QOlmSession::unpickle(const QByteArray &pickled, const PicklingMode &mode) +QOlmExpected QOlmSession::unpickle(const QByteArray& pickled, + const PicklingMode& mode) { QByteArray pickledBuf = pickled; auto *olmSession = create(); @@ -140,7 +148,7 @@ QOlmMessage QOlmSession::encrypt(const QString &plaintext) return QOlmMessage(messageBuf, messageType); } -std::variant QOlmSession::decrypt(const QOlmMessage &message) const +QOlmExpected QOlmSession::decrypt(const QOlmMessage &message) const { const auto messageType = message.type(); const auto ciphertext = message.toCiphertext(); diff --git a/lib/e2ee/qolmsession.h b/lib/e2ee/qolmsession.h index 4eb151b6..faae16ef 100644 --- a/lib/e2ee/qolmsession.h +++ b/lib/e2ee/qolmsession.h @@ -19,32 +19,31 @@ class QUOTIENT_API 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 QOlmExpected createInboundSession( + QOlmAccount* account, const QOlmMessage& preKeyMessage); - static std::variant, QOlmError> - createInboundSessionFrom(QOlmAccount* account, - const QString& theirIdentityKey, - const QOlmMessage& preKeyMessage); + static QOlmExpected createInboundSessionFrom( + QOlmAccount* account, const QString& theirIdentityKey, + const QOlmMessage& preKeyMessage); - static std::variant, QOlmError> - createOutboundSession(QOlmAccount* account, const QString& theirIdentityKey, - const QString& theirOneTimeKey); + static QOlmExpected createOutboundSession( + QOlmAccount* account, const QString& theirIdentityKey, + const QString& theirOneTimeKey); //! Serialises an `QOlmSession` to encrypted Base64. - std::variant pickle(const PicklingMode &mode); + QOlmExpected pickle(const PicklingMode &mode); //! Deserialises from encrypted Base64 that was previously obtained by pickling a `QOlmSession`. - static std::variant, QOlmError> unpickle( + static QOlmExpected 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 + //! Decrypts a message using this session. Decoding is lossy, meaning if //! the decrypted plaintext contains invalid UTF-8 symbols, they will //! be returned as `U+FFFD` (�). - std::variant decrypt(const QOlmMessage &message) const; + QOlmExpected decrypt(const QOlmMessage &message) const; //! Get a base64-encoded identifier for this session. QByteArray sessionId() const; @@ -56,11 +55,10 @@ public: bool hasReceivedMessage() const; //! Checks if the 'prekey' message is for this in-bound session. - std::variant matchesInboundSession( - const QOlmMessage& preKeyMessage) const; + bool matchesInboundSession(const QOlmMessage& preKeyMessage) const; //! Checks if the 'prekey' message is for this in-bound session. - std::variant matchesInboundSessionFrom( + bool matchesInboundSessionFrom( const QString& theirIdentityKey, const QOlmMessage& preKeyMessage) const; friend bool operator<(const QOlmSession& lhs, const QOlmSession& rhs) @@ -68,8 +66,7 @@ public: return lhs.sessionId() < rhs.sessionId(); } - friend bool operator<(const std::unique_ptr& lhs, - const std::unique_ptr& rhs) + friend bool operator<(const QOlmSessionPtr& lhs, const QOlmSessionPtr& rhs) { return *lhs < *rhs; } @@ -80,7 +77,7 @@ public: private: //! Helper function for creating new sessions and handling errors. static OlmSession* create(); - static std::variant, QOlmError> createInbound( + static QOlmExpected createInbound( QOlmAccount* account, const QOlmMessage& preKeyMessage, bool from = false, const QString& theirIdentityKey = ""); OlmSession* m_session; diff --git a/lib/e2ee/qolmutility.cpp b/lib/e2ee/qolmutility.cpp index 9f09a37f..84559085 100644 --- a/lib/e2ee/qolmutility.cpp +++ b/lib/e2ee/qolmutility.cpp @@ -3,8 +3,8 @@ // SPDX-License-Identifier: LGPL-2.1-or-later #include "e2ee/qolmutility.h" -#include "olm/olm.h" -#include + +#include using namespace Quotient; @@ -40,8 +40,9 @@ QString QOlmUtility::sha256Utf8Msg(const QString &message) const return sha256Bytes(message.toUtf8()); } -std::variant QOlmUtility::ed25519Verify(const QByteArray &key, - const QByteArray &message, const QByteArray &signature) +QOlmExpected QOlmUtility::ed25519Verify(const QByteArray& key, + const QByteArray& message, + const QByteArray& signature) { QByteArray signatureBuf(signature.length(), '0'); std::copy(signature.begin(), signature.end(), signatureBuf.begin()); @@ -57,8 +58,5 @@ std::variant QOlmUtility::ed25519Verify(const QByteArray &key, return error; } - if (ret != 0) { - return false; - } - return true; + return !ret; // ret == 0 means success } diff --git a/lib/e2ee/qolmutility.h b/lib/e2ee/qolmutility.h index a12af49a..5f6bcdc5 100644 --- a/lib/e2ee/qolmutility.h +++ b/lib/e2ee/qolmutility.h @@ -4,15 +4,12 @@ #pragma once -#include -#include "e2ee/qolmerrors.h" +#include "e2ee/e2ee.h" struct OlmUtility; namespace Quotient { -class QOlmSession; - //! Allows you to make use of crytographic hashing via SHA-2 and //! verifying ed25519 signatures. class QUOTIENT_API QOlmUtility @@ -32,7 +29,7 @@ public: //! \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, + QOlmExpected ed25519Verify(const QByteArray &key, const QByteArray &message, const QByteArray &signature); private: diff --git a/lib/room.cpp b/lib/room.cpp index db49e80f..1314803e 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -380,17 +380,22 @@ public: return {}; } auto decryptResult = senderSession->decrypt(cipher); - if(std::holds_alternative(decryptResult)) { + if(!decryptResult) { qCWarning(E2EE) << "Unable to decrypt event" << eventId - << "with matching megolm session:" << std::get(decryptResult); + << "with matching megolm session:" << decryptResult.error(); return {}; } - const auto& [content, index] = std::get>(decryptResult); - const auto& [recordEventId, ts] = q->connection()->database()->groupSessionIndexRecord(q->id(), senderSession->sessionId(), index); + const auto& [content, index] = *decryptResult; + const auto& [recordEventId, ts] = + q->connection()->database()->groupSessionIndexRecord( + q->id(), senderSession->sessionId(), index); if (recordEventId.isEmpty()) { - q->connection()->database()->addGroupSessionIndexRecord(q->id(), senderSession->sessionId(), index, eventId, timestamp.toMSecsSinceEpoch()); + q->connection()->database()->addGroupSessionIndexRecord( + q->id(), senderSession->sessionId(), index, eventId, + timestamp.toMSecsSinceEpoch()); } else { - if ((eventId != recordEventId) || (ts != timestamp.toMSecsSinceEpoch())) { + if ((eventId != recordEventId) + || (ts != timestamp.toMSecsSinceEpoch())) { qCWarning(E2EE) << "Detected a replay attack on event" << eventId; return {}; } -- cgit v1.2.3 From decc676f1e469dc26c80c33da64fad15805de8f2 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Mon, 16 May 2022 17:35:58 +0200 Subject: expected.h: add a copyright notice [skip ci] --- lib/expected.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib') diff --git a/lib/expected.h b/lib/expected.h index c8b8fd64..7b9e7f1d 100644 --- a/lib/expected.h +++ b/lib/expected.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2022 Kitsune Ral +// SPDX-License-Identifier: LGPL-2.1-or-later + #pragma once #include -- cgit v1.2.3