From 82e23011e0336a719fe3f2c003496d112bc5ca65 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Tue, 20 Sep 2022 23:20:53 +0200 Subject: .clang-tidy: drop readability-qualified-auto Too many suggestions to add, e.g., * in places where it may eventually be a smart pointer. [skip ci] --- .clang-tidy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clang-tidy b/.clang-tidy index d15c6eb4..db460c99 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,5 +1,5 @@ --- -Checks: '-*,bugprone-argument-comment,bugprone-assert-side-effect,bugprone-bool-pointer-implicit-conversion,bugprone-copy-constructor-init,bugprone-dangling-handle,bugprone-fold-init-type,bugprone-forward-declaration-namespace,bugprone-forwarding-reference-overload,bugprone-inaccurate-erase,bugprone-integer-division,bugprone-lambda-function-name,bugprone-macro-*,bugprone-move-forwarding-reference,bugprone-multiple-statement-macro,bugprone-parent-virtual-call,bugprone-redundant-branch-condition,bugprone-reserved-identifier,bugprone-signed-char-misuse,bugprone-sizeof-*,bugprone-string-*,bugprone-stringview-nullptr,bugprone-suspicious-*,bugprone-swapped-arguments,bugprone-terminating-continue,bugprone-too-small-loop-variable,bugprone-undefined-memory-manipulation,bugprone-undelegated-constructor,bugprone-unhandled-self-assignment,bugprone-unused-*,bugprone-use-after-move,bugprone-virtual-near-miss,cert-dcl50-cpp,cert-dcl58-cpp,cert-dcl59-cpp,cert-env33-c,cert-err33-c,cert-err34-c,cert-err60-cpp,cert-fio38-c,cert-flp30-c,cert-mem57-cpp,cert-msc30-c,cert-msc32-c,cert-msc50-cpp,cert-msc51-cpp,cert-oop57-cpp,cert-oop58-cpp,clang-analyzer-core.CallAndMessage,clang-analyzer-core.DivideZero,clang-analyzer-core.NullDereference,clang-analyzer-core.StackAddrEscapeBase,clang-analyzer-core.StackAddressEscape,clang-analyzer-core.UndefinedBinaryOperatorResult,clang-analyzer-core.uninitialized.*,clang-analyzer-cplusplus.*,clang-analyzer-deadcode.DeadStores,clang-analyzer-optin.cplusplus.*,cppcoreguidelines-c-copy-assignment-signature,cppcoreguidelines-init-variables,cppcoreguidelines-interfaces-global-init,cppcoreguidelines-narrowing-conversions,cppcoreguidelines-no-malloc,cppcoreguidelines-prefer-member-initializer,cppcoreguidelines-pro-bounds-array-to-pointer-decay,cppcoreguidelines-pro-bounds-pointer-arithmetic,cppcoreguidelines-pro-type-cstyle-cast,cppcoreguidelines-pro-type-member-init,cppcoreguidelines-slicing,cppcoreguidelines-special-member-functions,cppcoreguidelines-virtual-class-destructor,google-explicit-constructor,google-readability-namespace-comments,google-runtime-int,misc-*,-misc-definitions-in-headers,modernize-avoid-*,modernize-concat-nested-namespaces,modernize-deprecated-*,modernize-loop-convert,modernize-make-*,modernize-pass-by-value,modernize-raw-string-literal,modernize-redundant-void-arg,modernize-replace-random-shuffle,modernize-return-braced-init-list,modernize-shrink-to-fit,modernize-unary-static-assert,modernize-use-auto,modernize-use-bool-literals,modernize-use-default-member-init,modernize-use-emplace,modernize-use-equals-*,modernize-use-noexcept,modernize-use-nullptr,modernize-use-override,modernize-use-transparent-functors,modernize-use-uncaught-exceptions,modernize-use-using,performance-*,-performance-no-automatic-move,readability-avoid-const-params-in-decls,readability-container-*,readability-convert-member-functions-to-static,readability-delete-null-pointer,readability-duplicate-include,readability-else-after-return,readability-function-*,readability-implicit-bool-conversion,readability-inconsistent-declaration-parameter-name,readability-make-member-function-const,readability-misleading-indentation,readability-misplaced-array-index,readability-non-const-parameter,readability-qualified-auto,readability-redundant-control-flow,readability-redundant-declaration,readability-redundant-function-ptr-dereference,readability-redundant-member-init,readability-redundant-preprocessor,readability-redundant-smartptr-get,readability-redundant-string-*,readability-simplify-*,readability-static-*,readability-string-compare,readability-suspicious-call-argument,readability-uniqueptr-delete-release,readability-uppercase-literal-suffix,readability-use-anyofallof' +Checks: '-*,bugprone-argument-comment,bugprone-assert-side-effect,bugprone-bool-pointer-implicit-conversion,bugprone-copy-constructor-init,bugprone-dangling-handle,bugprone-fold-init-type,bugprone-forward-declaration-namespace,bugprone-forwarding-reference-overload,bugprone-inaccurate-erase,bugprone-integer-division,bugprone-lambda-function-name,bugprone-macro-*,bugprone-move-forwarding-reference,bugprone-multiple-statement-macro,bugprone-parent-virtual-call,bugprone-redundant-branch-condition,bugprone-reserved-identifier,bugprone-signed-char-misuse,bugprone-sizeof-*,bugprone-string-*,bugprone-stringview-nullptr,bugprone-suspicious-*,bugprone-swapped-arguments,bugprone-terminating-continue,bugprone-too-small-loop-variable,bugprone-undefined-memory-manipulation,bugprone-undelegated-constructor,bugprone-unhandled-self-assignment,bugprone-unused-*,bugprone-use-after-move,bugprone-virtual-near-miss,cert-dcl50-cpp,cert-dcl58-cpp,cert-dcl59-cpp,cert-env33-c,cert-err33-c,cert-err34-c,cert-err60-cpp,cert-fio38-c,cert-flp30-c,cert-mem57-cpp,cert-msc30-c,cert-msc32-c,cert-msc50-cpp,cert-msc51-cpp,cert-oop57-cpp,cert-oop58-cpp,clang-analyzer-core.CallAndMessage,clang-analyzer-core.DivideZero,clang-analyzer-core.NullDereference,clang-analyzer-core.StackAddrEscapeBase,clang-analyzer-core.StackAddressEscape,clang-analyzer-core.UndefinedBinaryOperatorResult,clang-analyzer-core.uninitialized.*,clang-analyzer-cplusplus.*,clang-analyzer-deadcode.DeadStores,clang-analyzer-optin.cplusplus.*,cppcoreguidelines-c-copy-assignment-signature,cppcoreguidelines-init-variables,cppcoreguidelines-interfaces-global-init,cppcoreguidelines-narrowing-conversions,cppcoreguidelines-no-malloc,cppcoreguidelines-prefer-member-initializer,cppcoreguidelines-pro-bounds-array-to-pointer-decay,cppcoreguidelines-pro-bounds-pointer-arithmetic,cppcoreguidelines-pro-type-cstyle-cast,cppcoreguidelines-pro-type-member-init,cppcoreguidelines-slicing,cppcoreguidelines-special-member-functions,cppcoreguidelines-virtual-class-destructor,google-explicit-constructor,google-readability-namespace-comments,google-runtime-int,misc-*,-misc-definitions-in-headers,modernize-avoid-*,modernize-concat-nested-namespaces,modernize-deprecated-*,modernize-loop-convert,modernize-make-*,modernize-pass-by-value,modernize-raw-string-literal,modernize-redundant-void-arg,modernize-replace-random-shuffle,modernize-return-braced-init-list,modernize-shrink-to-fit,modernize-unary-static-assert,modernize-use-auto,modernize-use-bool-literals,modernize-use-default-member-init,modernize-use-emplace,modernize-use-equals-*,modernize-use-noexcept,modernize-use-nullptr,modernize-use-override,modernize-use-transparent-functors,modernize-use-uncaught-exceptions,modernize-use-using,performance-*,-performance-no-automatic-move,readability-avoid-const-params-in-decls,readability-container-*,readability-convert-member-functions-to-static,readability-delete-null-pointer,readability-duplicate-include,readability-else-after-return,readability-function-*,readability-implicit-bool-conversion,readability-inconsistent-declaration-parameter-name,readability-make-member-function-const,readability-misleading-indentation,readability-misplaced-array-index,readability-non-const-parameter,readability-redundant-control-flow,readability-redundant-declaration,readability-redundant-function-ptr-dereference,readability-redundant-member-init,readability-redundant-preprocessor,readability-redundant-smartptr-get,readability-redundant-string-*,readability-simplify-*,readability-static-*,readability-string-compare,readability-suspicious-call-argument,readability-uniqueptr-delete-release,readability-uppercase-literal-suffix,readability-use-anyofallof' WarningsAsErrors: '' HeaderFilterRegex: '' AnalyzeTemporaryDtors: false -- cgit v1.2.3 From a0cc4eaf6af6f047e32713b2926f784fa4087a64 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Sat, 17 Sep 2022 13:21:35 +0200 Subject: Bump required Olm version to 3.2.5 This is the earliest version shipping olm/error.h. Conversely, stock libolm that comes with Ubuntu 20.04 (version 3.1.3) is no more enough so CI completely switches to jammy now. --- .github/workflows/ci.yml | 8 ++++---- CMakeLists.txt | 2 +- README.md | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f84356b0..1c0940eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: fail-fast: false max-parallel: 1 matrix: - os: [ ubuntu-20.04, macos-11 ] + os: [ ubuntu-22.04, macos-11 ] qt-version: [ '6.3.1', '5.15.2' ] compiler: [ LLVM ] # Not using binary values here, to make the job captions more readable @@ -31,7 +31,7 @@ jobs: exclude: - qt-version: '6.3.1' update-api: update-api # Generated code is not specific to Qt version - - os: ubuntu-20.04 + - os: ubuntu-22.04 e2ee: e2ee # Will be re-added with static analysis below # TODO: Enable E2EE on Windows and macOS - os: macos-11 @@ -42,7 +42,7 @@ jobs: compiler: MSVC platform: x64 qt-arch: win64_msvc2019_64 - - os: ubuntu-20.04 + - os: ubuntu-22.04 qt-version: '5.15.2' compiler: LLVM e2ee: e2ee @@ -57,7 +57,7 @@ jobs: compiler: GCC e2ee: e2ee update-api: update-api - - os: ubuntu-20.04 + - os: ubuntu-22.04 qt-version: '5.15.2' compiler: LLVM update-api: update-api diff --git a/CMakeLists.txt b/CMakeLists.txt index 91ee8217..a56115e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,7 +101,7 @@ find_package(${Qt}Keychain REQUIRED) if (${PROJECT_NAME}_ENABLE_E2EE) find_package(${Qt} ${QtMinVersion} REQUIRED Sql) - find_package(Olm 3.1.3 REQUIRED) + find_package(Olm 3.2.5 REQUIRED) set_package_properties(Olm PROPERTIES DESCRIPTION "Implementation of the Olm and Megolm cryptographic ratchets" URL "https://gitlab.matrix.org/matrix-org/olm" diff --git a/README.md b/README.md index e7597fce..d9f50f0a 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ your application, as described below. - GCC 11 (Windows, Linux, macOS), Clang 11 (Linux), Apple Clang 12 (macOS) and Visual Studio 2019 (Windows) are the oldest officially supported. - If using E2EE (beta, as of libQuotient 0.7): - - libolm 3.x (the latest 3.x strongly recommended) + - libolm 3.2.5 or newer (the latest 3.x strongly recommended) - OpenSSL (1.1.x is known to work; 3.x should likely work too). - Any build system that works with CMake should be fine: GNU Make and ninja on any platform, NMake and jom on Windows are known to work. -- cgit v1.2.3 From a923750c7a1efadaa66f24dc17010063776e6246 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Sun, 18 Sep 2022 13:04:41 +0200 Subject: CI: only use Valgrind with GCC As of the current GHA jammy image, Valgrind cannot cope with some of Clang 14 output: https://bugs.kde.org/show_bug.cgi?id=452758 This is most likely a band-aid, before the work on switching from Valgrind to ASan. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1c0940eb..40ed85d1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -106,6 +106,7 @@ jobs: sed -i 's/ThreadEngineStarter(ThreadEngine \*_threadEngine)/ThreadEngineStarter(ThreadEngine \*_threadEngine)/' \ $Qt5_DIR/include/QtConcurrent/qtconcurrentthreadengine.h fi + echo "VALGRIND=valgrind --tool=memcheck --leak-check=yes --gen-suppressions=all --suppressions=$GITHUB_WORKSPACE/quotest/.valgrind.supp" >>$GITHUB_ENV elif [[ '${{ runner.os }}' != 'Windows' ]]; then echo "CC=clang" >>$GITHUB_ENV echo "CXX=clang++" >>$GITHUB_ENV @@ -155,7 +156,6 @@ jobs: echo "QUOTEST_ORIGIN=$QUOTEST_ORIGIN with E2EE" >>$GITHUB_ENV fi sudo apt-get -qq install ninja-build valgrind $EXTRA_DEPS - echo "VALGRIND=valgrind --tool=memcheck --leak-check=yes --gen-suppressions=all --suppressions=$GITHUB_WORKSPACE/quotest/.valgrind.supp" >>$GITHUB_ENV - name: Setup MSVC uses: ilammy/msvc-dev-cmd@v1 -- cgit v1.2.3 From 363a7e40e8aa12cb780b076cca8db4f47b70f4fa Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Mon, 26 Sep 2022 09:44:20 +0200 Subject: Replace QOlmError with OlmErrorCode QOlmError represents a subset of OlmErrorCode, and the associated fromString() function uses undocumented strings produced inside Olm; meanwhile OlmErrorCode is documented in its own header file. Each QOlm* class now has lastErrorCode() next to lastError() (that, from now, returns a textual representation straight from Olm, not QOlmError enum). Also: including olm/error.h in e2ee/e2ee.h required some rearrangement of the code to make sure non-E2EE configuration still builds. --- CMakeLists.txt | 2 +- autotests/testolmaccount.cpp | 3 +- autotests/testolmsession.cpp | 8 +++- lib/connection.cpp | 3 +- lib/e2ee/e2ee.h | 36 ++++----------- lib/e2ee/qolmaccount.cpp | 82 +++++++++++++++++---------------- lib/e2ee/qolmaccount.h | 9 ++-- lib/e2ee/qolmerrors.cpp | 25 ---------- lib/e2ee/qolmerrors.h | 28 ------------ lib/e2ee/qolminboundsession.cpp | 84 +++++++++++++++++++--------------- lib/e2ee/qolminboundsession.h | 5 +- lib/e2ee/qolmoutboundsession.cpp | 81 +++++++++++++++++++-------------- lib/e2ee/qolmoutboundsession.h | 7 ++- lib/e2ee/qolmsession.cpp | 98 +++++++++++++++++++++------------------- lib/e2ee/qolmsession.h | 4 +- lib/e2ee/qolmutility.cpp | 22 +++++---- lib/e2ee/qolmutility.h | 4 +- lib/events/encryptedevent.cpp | 1 + lib/events/encryptedevent.h | 7 ++- lib/room.cpp | 1 - lib/util.h | 17 +++++++ 21 files changed, 266 insertions(+), 261 deletions(-) delete mode 100644 lib/e2ee/qolmerrors.cpp delete mode 100644 lib/e2ee/qolmerrors.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a56115e3..b021411c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -150,6 +150,7 @@ list(APPEND lib_SRCS lib/eventitem.h lib/eventitem.cpp lib/accountregistry.h lib/accountregistry.cpp lib/mxcreply.h lib/mxcreply.cpp + lib/e2ee/e2ee.h # because it's used by generated API lib/events/event.h lib/events/event.cpp lib/events/eventloader.h lib/events/roomevent.h lib/events/roomevent.cpp @@ -193,7 +194,6 @@ if (${PROJECT_NAME}_ENABLE_E2EE) lib/e2ee/qolmoutboundsession.h lib/e2ee/qolmoutboundsession.cpp lib/e2ee/qolmutils.h lib/e2ee/qolmutils.cpp lib/e2ee/qolmutility.h lib/e2ee/qolmutility.cpp - lib/e2ee/qolmerrors.h lib/e2ee/qolmerrors.cpp lib/e2ee/qolmsession.h lib/e2ee/qolmsession.cpp lib/e2ee/qolmmessage.h lib/e2ee/qolmmessage.cpp lib/events/keyverificationevent.h diff --git a/autotests/testolmaccount.cpp b/autotests/testolmaccount.cpp index 280705d0..eb428661 100644 --- a/autotests/testolmaccount.cpp +++ b/autotests/testolmaccount.cpp @@ -25,7 +25,8 @@ void TestOlmAccount::pickleUnpickledTest() auto identityKeys = olmAccount.identityKeys(); auto pickled = olmAccount.pickle(Unencrypted{}).value(); QOlmAccount olmAccount2(QStringLiteral("@foo:bar.com"), QStringLiteral("QuotientTestDevice")); - olmAccount2.unpickle(pickled, Unencrypted{}); + auto unpickleResult = olmAccount2.unpickle(pickled, Unencrypted{}); + QCOMPARE(unpickleResult, 0); auto identityKeys2 = olmAccount2.identityKeys(); QCOMPARE(identityKeys.curve25519, identityKeys2.curve25519); QCOMPARE(identityKeys.ed25519, identityKeys2.ed25519); diff --git a/autotests/testolmsession.cpp b/autotests/testolmsession.cpp index 182659e7..66a04241 100644 --- a/autotests/testolmsession.cpp +++ b/autotests/testolmsession.cpp @@ -12,9 +12,13 @@ std::pair createSessionPair() QByteArray pickledAccountA("eOBXIKivUT6YYowRH031BNv7zNmzqM5B7CpXdyeaPvala5mt7/OeqrG1qVA7vA1SYloFyvJPIy0QNkD3j1HiPl5vtZHN53rtfZ9exXDok03zjmssqn4IJsqcA7Fbo1FZeKafG0NFcWwCPTdmcV7REqxjqGm3I4K8MQFa45AdTGSUu2C12cWeOcbSMlcINiMral+Uyah1sgPmLJ18h1qcnskXUXQvpffZ5DiUw1Iz5zxnwOQF1GVyowPJD7Zdugvj75RQnDxAn6CzyvrY2k2CuedwqDC3fIXM2xdUNWttW4nC2g4InpBhCVvNwhZYxlUb5BUEjmPI2AB3dAL5ry6o9MFncmbN6x5x"); QByteArray pickledAccountB("eModTvoFi9oOIkax4j4nuxw9Tcl/J8mOmUctUWI68Q89HSaaPTqR+tdlKQ85v2GOs5NlZCp7EuycypN9GQ4fFbHUCrS7nspa3GFBWsR8PnM8+wez5PWmfFZLg3drOvT0jbMjpDx0MjGYClHBqcrEpKx9oFaIRGBaX6HXzT4lRaWSJkXxuX92q8iGNrLn96PuAWFNcD+2JXpPcNFntslwLUNgqzpZ04aIFYwL80GmzyOgq3Bz1GO6u3TgCQEAmTIYN2QkO0MQeuSfe7UoMumhlAJ6R8GPcdSSPtmXNk4tdyzzlgpVq1hm7ZLKto+g8/5Aq3PvnvA8wCqno2+Pi1duK1pZFTIlActr"); auto accountA = QOlmAccount("accountA:foo.com", "Device1UserA"); - accountA.unpickle(pickledAccountA, Unencrypted{}); + if (accountA.unpickle(pickledAccountA, Unencrypted{}) != OLM_SUCCESS) + qFatal("Failed to unpickle account A: %s", accountA.lastError()); + auto accountB = QOlmAccount("accountB:foo.com", "Device1UserB"); - accountB.unpickle(pickledAccountB, Unencrypted{}); + if (accountB.unpickle(pickledAccountB, Unencrypted{}) != OLM_SUCCESS) + qFatal("Failed to unpickle account B: %s", accountB.lastError()); + const QByteArray identityKeyA("qIEr3TWcJQt4CP8QoKKJcCaukByIOpgh6erBkhLEa2o"); const QByteArray oneTimeKeyA("WzsbsjD85iB1R32iWxfJdwkgmdz29ClMbJSJziECYwk"); diff --git a/lib/connection.cpp b/lib/connection.cpp index 8ca76ceb..865dff79 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -270,8 +270,7 @@ public: return {}; } auto newSession = std::move(*newSessionResult); - auto error = olmAccount->removeOneTimeKeys(*newSession); - if (error) { + if (olmAccount->removeOneTimeKeys(*newSession) != OLM_SUCCESS) { qWarning(E2EE) << "Failed to remove one time key for session" << newSession->sessionId(); // Keep going though diff --git a/lib/e2ee/e2ee.h b/lib/e2ee/e2ee.h index 0772b70a..51ceff67 100644 --- a/lib/e2ee/e2ee.h +++ b/lib/e2ee/e2ee.h @@ -6,21 +6,20 @@ #pragma once #include "converters.h" -#include "expected.h" -#include "qolmerrors.h" #include #include #include -#include -namespace Quotient { +#ifdef Quotient_E2EE_ENABLED +# include "expected.h" + +# include +# include +#endif -constexpr auto CiphertextKeyL = "ciphertext"_ls; -constexpr auto SenderKeyKeyL = "sender_key"_ls; -constexpr auto DeviceIdKeyL = "device_id"_ls; -constexpr auto SessionIdKeyL = "session_id"_ls; +namespace Quotient { constexpr auto AlgorithmKeyL = "algorithm"_ls; constexpr auto RotationPeriodMsKeyL = "rotation_period_ms"_ls; @@ -47,6 +46,7 @@ inline bool isSupportedAlgorithm(const QString& algorithm) != SupportedAlgorithms.cend(); } +#ifdef Quotient_E2EE_ENABLED struct Unencrypted {}; struct Encrypted { QByteArray key; @@ -64,7 +64,8 @@ class QOlmOutboundGroupSession; using QOlmOutboundGroupSessionPtr = std::unique_ptr; template -using QOlmExpected = Expected; +using QOlmExpected = Expected; +#endif struct IdentityKeys { @@ -133,23 +134,6 @@ private: using OneTimeKeys = QHash>; -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; -}; -template -asKeyValueRange(T&) -> asKeyValueRange; - } // namespace Quotient Q_DECLARE_METATYPE(Quotient::SignedOneTimeKey) diff --git a/lib/e2ee/qolmaccount.cpp b/lib/e2ee/qolmaccount.cpp index ccb191f4..d33db8e3 100644 --- a/lib/e2ee/qolmaccount.cpp +++ b/lib/e2ee/qolmaccount.cpp @@ -18,8 +18,13 @@ using namespace Quotient; // Convert olm error to enum -QOlmError lastError(OlmAccount *account) { - return fromString(olm_account_last_error(account)); +OlmErrorCode QOlmAccount::lastErrorCode() const { + return olm_account_last_error_code(m_account); +} + +const char* QOlmAccount::lastError() const +{ + return olm_account_last_error(m_account); } QOlmAccount::QOlmAccount(const QString& userId, const QString& deviceId, @@ -40,24 +45,24 @@ 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); + if (olm_create_account(m_account, randomData.data(), randomSize) + == olm_error()) { + throw lastError(); } emit needsSave(); } -void QOlmAccount::unpickle(QByteArray &pickled, const PicklingMode &mode) +OlmErrorCode 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 + if (olm_unpickle_account(m_account, key.data(), key.length(), + pickled.data(), pickled.size()) + == olm_error()) { // Probably log the user out since we have no way of getting to the keys - //throw lastError(m_account); + return lastErrorCode(); } + return OLM_SUCCESS; } QOlmExpected QOlmAccount::pickle(const PicklingMode &mode) @@ -65,11 +70,10 @@ QOlmExpected 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); - } + if (olm_pickle_account(m_account, key.data(), key.length(), + pickleBuffer.data(), pickleLength) + == olm_error()) + return lastErrorCode(); return pickleBuffer; } @@ -77,9 +81,9 @@ 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); + if (olm_account_identity_keys(m_account, keyBuffer.data(), keyLength) + == olm_error()) { + throw lastError(); } const QJsonObject key = QJsonDocument::fromJson(keyBuffer).object(); return IdentityKeys { @@ -92,11 +96,10 @@ 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); + if (olm_account_sign(m_account, message.data(), message.length(), + signatureBuffer.data(), signatureBuffer.length()) + == olm_error()) { + throw lastError(); } return signatureBuffer; } @@ -131,15 +134,15 @@ size_t QOlmAccount::generateOneTimeKeys(size_t numberOfKeys) olm_account_generate_one_time_keys_random_length(m_account, numberOfKeys); QByteArray randomBuffer = getRandom(randomLength); - const auto error = + const auto result = olm_account_generate_one_time_keys(m_account, numberOfKeys, randomBuffer.data(), randomLength); - if (error == olm_error()) { - throw lastError(m_account); + if (result == olm_error()) { + throw lastError(); } emit needsSave(); - return error; + return result; } UnsignedOneTimeKeys QOlmAccount::oneTimeKeys() const @@ -147,11 +150,10 @@ UnsignedOneTimeKeys QOlmAccount::oneTimeKeys() const const size_t oneTimeKeyLength = olm_account_one_time_keys_length(m_account); QByteArray oneTimeKeysBuffer(static_cast(oneTimeKeyLength), '0'); - const auto error = olm_account_one_time_keys(m_account, - oneTimeKeysBuffer.data(), - oneTimeKeyLength); - if (error == olm_error()) { - throw lastError(m_account); + if (olm_account_one_time_keys(m_account, oneTimeKeysBuffer.data(), + oneTimeKeyLength) + == olm_error()) { + throw lastError(); } const auto json = QJsonDocument::fromJson(oneTimeKeysBuffer).object(); UnsignedOneTimeKeys oneTimeKeys; @@ -171,16 +173,16 @@ OneTimeKeys QOlmAccount::signOneTimeKeys(const UnsignedOneTimeKeys &keys) const return signedOneTimeKeys; } -std::optional QOlmAccount::removeOneTimeKeys( - const QOlmSession& session) +OlmErrorCode QOlmAccount::removeOneTimeKeys(const QOlmSession& session) { - const auto error = olm_remove_one_time_keys(m_account, session.raw()); - - if (error == olm_error()) { - return lastError(m_account); + if (olm_remove_one_time_keys(m_account, session.raw()) == olm_error()) { + qWarning(E2EE).nospace() + << "Failed to remove one-time keys for session " + << session.sessionId() << ": " << lastError(); + return lastErrorCode(); } emit needsSave(); - return std::nullopt; + return OLM_SUCCESS; } OlmAccount* QOlmAccount::data() { return m_account; } diff --git a/lib/e2ee/qolmaccount.h b/lib/e2ee/qolmaccount.h index f2a31314..5ad98e47 100644 --- a/lib/e2ee/qolmaccount.h +++ b/lib/e2ee/qolmaccount.h @@ -36,7 +36,8 @@ public: //! 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); + [[nodiscard]] OlmErrorCode unpickle(QByteArray& pickled, + const PicklingMode& mode); //! Serialises an OlmAccount to encrypted Base64. QOlmExpected pickle(const PicklingMode &mode); @@ -69,8 +70,7 @@ public: DeviceKeys deviceKeys() const; //! Remove the one time key used to create the supplied session. - [[nodiscard]] std::optional removeOneTimeKeys( - const QOlmSession& session); + [[nodiscard]] OlmErrorCode removeOneTimeKeys(const QOlmSession& session); //! Creates an inbound session for sending/receiving messages from a received 'prekey' message. //! @@ -92,6 +92,9 @@ public: void markKeysAsPublished(); + OlmErrorCode lastErrorCode() const; + const char *lastError() const; + // HACK do not use directly QOlmAccount(OlmAccount *account); OlmAccount *data(); diff --git a/lib/e2ee/qolmerrors.cpp b/lib/e2ee/qolmerrors.cpp deleted file mode 100644 index 5a60b7e6..00000000 --- a/lib/e2ee/qolmerrors.cpp +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - - -#include "qolmerrors.h" -#include "util.h" -#include - -Quotient::QOlmError Quotient::fromString(const char* error_raw) { - const QLatin1String error { error_raw }; - if (error_raw == "BAD_ACCOUNT_KEY"_ls) { - return QOlmError::BadAccountKey; - } else if (error_raw == "BAD_MESSAGE_KEY_ID"_ls) { - return QOlmError::BadMessageKeyId; - } else if (error_raw == "INVALID_BASE64"_ls) { - return QOlmError::InvalidBase64; - } else if (error_raw == "NOT_ENOUGH_RANDOM"_ls) { - return QOlmError::NotEnoughRandom; - } else if (error_raw == "OUTPUT_BUFFER_TOO_SMALL"_ls) { - return QOlmError::OutputBufferTooSmall; - } else { - return QOlmError::Unknown; - } -} diff --git a/lib/e2ee/qolmerrors.h b/lib/e2ee/qolmerrors.h deleted file mode 100644 index 20e61c12..00000000 --- a/lib/e2ee/qolmerrors.h +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#pragma once - -#include "quotient_export.h" - -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, -}; - -QUOTIENT_API QOlmError fromString(const char* error_raw); - -} //namespace Quotient diff --git a/lib/e2ee/qolminboundsession.cpp b/lib/e2ee/qolminboundsession.cpp index 17f06205..870070c2 100644 --- a/lib/e2ee/qolminboundsession.cpp +++ b/lib/e2ee/qolminboundsession.cpp @@ -3,20 +3,27 @@ // SPDX-License-Identifier: LGPL-2.1-or-later #include "e2ee/qolminboundsession.h" -#include +#include "logging.h" + #include +#include +#include using namespace Quotient; -QOlmError lastError(OlmInboundGroupSession *session) { - return fromString(olm_inbound_group_session_last_error(session)); +OlmErrorCode QOlmInboundGroupSession::lastErrorCode() const { + return olm_inbound_group_session_last_error_code(m_groupSession); } -QOlmInboundGroupSession::QOlmInboundGroupSession(OlmInboundGroupSession *session) - : m_groupSession(session) +const char* QOlmInboundGroupSession::lastError() const { + return olm_inbound_group_session_last_error(m_groupSession); } +QOlmInboundGroupSession::QOlmInboundGroupSession(OlmInboundGroupSession *session) + : m_groupSession(session) +{} + QOlmInboundGroupSession::~QOlmInboundGroupSession() { olm_clear_inbound_group_session(m_groupSession); @@ -26,11 +33,12 @@ QOlmInboundGroupSession::~QOlmInboundGroupSession() 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 error = olm_init_inbound_group_session(olmInboundGroupSession, - reinterpret_cast(key.constData()), key.size()); - - if (error == olm_error()) { - throw lastError(olmInboundGroupSession); + if (olm_init_inbound_group_session( + olmInboundGroupSession, + reinterpret_cast(key.constData()), key.size()) + == olm_error()) { + // FIXME: create QOlmInboundGroupSession earlier and use lastError() + throw olm_inbound_group_session_last_error_code(olmInboundGroupSession); } return std::make_unique(olmInboundGroupSession); @@ -41,10 +49,12 @@ std::unique_ptr QOlmInboundGroupSession::import(const Q 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); + if (olm_import_inbound_group_session( + olmInboundGroupSession, + reinterpret_cast(keyBuf.data()), keyBuf.size()) + == olm_error()) { + // FIXME: create QOlmInboundGroupSession earlier and use lastError() + throw olm_inbound_group_session_last_error_code(olmInboundGroupSession); } return std::make_unique(olmInboundGroupSession); @@ -62,10 +72,11 @@ 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); + if (olm_pickle_inbound_group_session(m_groupSession, key.data(), + key.length(), pickledBuf.data(), + pickledBuf.length()) + == olm_error()) { + throw lastError(); } return pickledBuf; } @@ -76,10 +87,12 @@ QOlmExpected QOlmInboundGroupSession::unpickle( 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); + if (olm_unpickle_inbound_group_session(groupSession, key.data(), + key.length(), pickledBuf.data(), + pickledBuf.size()) + == olm_error()) { + // FIXME: create QOlmInboundGroupSession earlier and use lastError() + return olm_inbound_group_session_last_error_code(groupSession); } key.clear(); @@ -105,12 +118,9 @@ QOlmExpected> QOlmInboundGroupSession::decrypt( 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); + if (plaintextLen == olm_error()) { + qWarning(E2EE) << "Failed to decrypt the message:" << lastError(); + return lastErrorCode(); } QByteArray output(plaintextLen, '0'); @@ -123,10 +133,11 @@ QOlmExpected QOlmInboundGroupSession::exportSession(uint32_t message { 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); + if (olm_export_inbound_group_session( + m_groupSession, reinterpret_cast(keyBuf.data()), + keyLength, messageIndex) + == olm_error()) { + return lastErrorCode(); } return keyBuf; } @@ -139,10 +150,11 @@ uint32_t QOlmInboundGroupSession::firstKnownIndex() const 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); + if (olm_inbound_group_session_id( + m_groupSession, reinterpret_cast(sessionIdBuf.data()), + sessionIdBuf.length()) + == olm_error()) { + throw lastError(); } return sessionIdBuf; } diff --git a/lib/e2ee/qolminboundsession.h b/lib/e2ee/qolminboundsession.h index 1a9b4415..e8da6355 100644 --- a/lib/e2ee/qolminboundsession.h +++ b/lib/e2ee/qolminboundsession.h @@ -6,7 +6,7 @@ #include "e2ee/e2ee.h" -#include +struct OlmInboundGroupSession; namespace Quotient { @@ -46,6 +46,9 @@ public: QString senderId() const; void setSenderId(const QString& senderId); + OlmErrorCode lastErrorCode() const; + const char* lastError() const; + QOlmInboundGroupSession(OlmInboundGroupSession* session); private: OlmInboundGroupSession* m_groupSession; diff --git a/lib/e2ee/qolmoutboundsession.cpp b/lib/e2ee/qolmoutboundsession.cpp index a2eff2c8..79c16e01 100644 --- a/lib/e2ee/qolmoutboundsession.cpp +++ b/lib/e2ee/qolmoutboundsession.cpp @@ -3,12 +3,20 @@ // SPDX-License-Identifier: LGPL-2.1-or-later #include "e2ee/qolmoutboundsession.h" + #include "e2ee/qolmutils.h" +#include + using namespace Quotient; -QOlmError lastError(OlmOutboundGroupSession *session) { - return fromString(olm_outbound_group_session_last_error(session)); +OlmErrorCode QOlmOutboundGroupSession::lastErrorCode() const { + return olm_outbound_group_session_last_error_code(m_groupSession); +} + +const char* QOlmOutboundGroupSession::lastError() const +{ + return olm_outbound_group_session_last_error(m_groupSession); } QOlmOutboundGroupSession::QOlmOutboundGroupSession(OlmOutboundGroupSession *session) @@ -27,11 +35,12 @@ QOlmOutboundGroupSessionPtr QOlmOutboundGroupSession::create() 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); + if (olm_init_outbound_group_session( + olmOutboundGroupSession, + reinterpret_cast(randomBuf.data()), randomBuf.length()) + == olm_error()) { + // FIXME: create the session object earlier and use lastError() + throw olm_outbound_group_session_last_error_code(olmOutboundGroupSession); } const auto keyMaxLength = olm_outbound_group_session_key_length(olmOutboundGroupSession); @@ -48,12 +57,11 @@ QOlmExpected QOlmOutboundGroupSession::pickle(const PicklingMode &mo { 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); - } + if (olm_pickle_outbound_group_session(m_groupSession, key.data(), + key.length(), pickledBuf.data(), + pickledBuf.length()) + == olm_error()) + return lastErrorCode(); key.clear(); @@ -65,10 +73,13 @@ QOlmExpected QOlmOutboundGroupSession::unpickle(con 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(), - pickledBuf.data(), pickledBuf.length()); - if (error == olm_error()) { - return lastError(olmOutboundGroupSession); + if (olm_unpickle_outbound_group_session(olmOutboundGroupSession, key.data(), + key.length(), pickledBuf.data(), + pickledBuf.length()) + == olm_error()) { + // FIXME: create the session object earlier and use lastError() + return olm_outbound_group_session_last_error_code( + olmOutboundGroupSession); } const auto idMaxLength = olm_outbound_group_session_id_length(olmOutboundGroupSession); QByteArray idBuffer(idMaxLength, '0'); @@ -84,12 +95,13 @@ QOlmExpected QOlmOutboundGroupSession::encrypt(const QString &plaint 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); - } + if (olm_group_encrypt(m_groupSession, + reinterpret_cast(plaintextBuf.data()), + plaintextBuf.length(), + reinterpret_cast(messageBuf.data()), + messageBuf.length()) + == olm_error()) + return lastErrorCode(); return messageBuf; } @@ -103,11 +115,12 @@ 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); - } + if (olm_outbound_group_session_id( + m_groupSession, reinterpret_cast(idBuffer.data()), + idBuffer.length()) + == olm_error()) + throw lastError(); + return idBuffer; } @@ -115,12 +128,12 @@ 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); - if (error == olm_error()) { - return lastError(m_groupSession); - } + if (olm_outbound_group_session_key( + m_groupSession, reinterpret_cast(keyBuffer.data()), + keyMaxLength) + == olm_error()) + return lastErrorCode(); + return keyBuffer; } diff --git a/lib/e2ee/qolmoutboundsession.h b/lib/e2ee/qolmoutboundsession.h index 9a82d22a..cd26fc67 100644 --- a/lib/e2ee/qolmoutboundsession.h +++ b/lib/e2ee/qolmoutboundsession.h @@ -6,8 +6,7 @@ #include "e2ee/e2ee.h" -#include -#include +struct OlmOutboundGroupSession; namespace Quotient { @@ -51,6 +50,10 @@ public: QDateTime creationTime() const; void setCreationTime(const QDateTime& creationTime); + + OlmErrorCode lastErrorCode() const; + const char* lastError() const; + private: OlmOutboundGroupSession *m_groupSession; int m_messageCount = 0; diff --git a/lib/e2ee/qolmsession.cpp b/lib/e2ee/qolmsession.cpp index 2a98d5d8..771d310d 100644 --- a/lib/e2ee/qolmsession.cpp +++ b/lib/e2ee/qolmsession.cpp @@ -12,8 +12,13 @@ using namespace Quotient; -QOlmError lastError(OlmSession* session) { - return fromString(olm_session_last_error(session)); +OlmErrorCode QOlmSession::lastErrorCode() const { + return olm_session_last_error_code(m_session); +} + +const char* QOlmSession::lastError() const +{ + return olm_session_last_error(m_session); } Quotient::QOlmSession::~QOlmSession() @@ -32,7 +37,8 @@ QOlmExpected QOlmSession::createInbound( const QString& theirIdentityKey) { if (preKeyMessage.type() != QOlmMessage::PreKey) { - qCCritical(E2EE) << "The message is not a pre-key in when creating inbound session" << BadMessageFormat; + qCCritical(E2EE) << "The message is not a pre-key; will try to create " + "the inbound session anyway"; } const auto olmSession = create(); @@ -47,7 +53,8 @@ QOlmExpected QOlmSession::createInbound( } if (error == olm_error()) { - const auto lastErr = lastError(olmSession); + // FIXME: the QOlmSession object should be created earlier + const auto lastErr = olm_session_last_error_code(olmSession); qCWarning(E2EE) << "Error when creating inbound session" << lastErr; return lastErr; } @@ -78,15 +85,17 @@ QOlmExpected QOlmSession::createOutboundSession( 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) { + if (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()) + == olm_error()) { + // FIXME: the QOlmSession object should be created earlier + const auto lastErr = olm_session_last_error_code(olmOutboundSession); + if (lastErr == OLM_NOT_ENOUGH_RANDOM) { throw lastErr; } return lastErr; @@ -100,16 +109,12 @@ QOlmExpected QOlmSession::pickle(const PicklingMode &mode) const { 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); - } + if (olm_pickle_session(m_session, key.data(), key.length(), + pickledBuf.data(), pickledBuf.length()) + == olm_error()) + return lastErrorCode(); key.clear(); - return pickledBuf; } @@ -119,10 +124,11 @@ QOlmExpected QOlmSession::unpickle(const QByteArray& pickled, 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); + if (olm_unpickle_session(olmSession, key.data(), key.length(), + pickledBuf.data(), pickledBuf.length()) + == olm_error()) { + // FIXME: the QOlmSession object should be created earlier + return olm_session_last_error_code(olmSession); } key.clear(); @@ -137,13 +143,14 @@ QOlmMessage QOlmSession::encrypt(const QString &plaintext) 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); + if (olm_encrypt(m_session, reinterpret_cast(plaintextBuf.data()), + plaintextBuf.length(), + reinterpret_cast(randomBuf.data()), + randomBuf.length(), + reinterpret_cast(messageBuf.data()), + messageBuf.length()) + == olm_error()) { + throw lastError(); } return QOlmMessage(messageBuf, messageType); @@ -163,9 +170,8 @@ QOlmExpected QOlmSession::decrypt(const QOlmMessage &message) const 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); + return lastError(); } QByteArray plaintextBuf(plaintextMaxLen, '0'); @@ -175,10 +181,9 @@ QOlmExpected QOlmSession::decrypt(const QOlmMessage &message) const 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) { + const auto lastErr = lastErrorCode(); + if (lastErr == OLM_OUTPUT_BUFFER_TOO_SMALL) { throw lastErr; } return lastErr; @@ -193,7 +198,7 @@ QOlmMessage::Type QOlmSession::encryptMessageType() { const auto messageTypeResult = olm_encrypt_message_type(m_session); if (messageTypeResult == olm_error()) { - throw lastError(m_session); + throw lastError(); } if (messageTypeResult == OLM_MESSAGE_TYPE_PRE_KEY) { return QOlmMessage::PreKey; @@ -205,10 +210,10 @@ 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); + if (olm_session_id(m_session, reinterpret_cast(idBuffer.data()), + idBuffer.length()) + == olm_error()) { + throw lastError(); } return idBuffer; } @@ -225,11 +230,10 @@ bool QOlmSession::matchesInboundSession(const QOlmMessage& preKeyMessage) const const auto maybeMatches = olm_matches_inbound_session(m_session, oneTimeKeyBuf.data(), oneTimeKeyBuf.length()); - - if (maybeMatches == olm_error()) { - return lastError(m_session); - } - return maybeMatches == 1; + if (maybeMatches == olm_error()) + qWarning(E2EE) << "Error matching an inbound session:" + << olm_session_last_error(m_session); + return maybeMatches == 1; // Any errors are treated as non-match } bool QOlmSession::matchesInboundSessionFrom( diff --git a/lib/e2ee/qolmsession.h b/lib/e2ee/qolmsession.h index 021092c7..cc988a03 100644 --- a/lib/e2ee/qolmsession.h +++ b/lib/e2ee/qolmsession.h @@ -6,7 +6,6 @@ #include "e2ee/e2ee.h" #include "e2ee/qolmmessage.h" -#include "e2ee/qolmerrors.h" #include "e2ee/qolmaccount.h" struct OlmSession; @@ -71,6 +70,9 @@ public: return *lhs < *rhs; } + OlmErrorCode lastErrorCode() const; + const char* lastError() const; + OlmSession* raw() const { return m_session; } QOlmSession(OlmSession* session); diff --git a/lib/e2ee/qolmutility.cpp b/lib/e2ee/qolmutility.cpp index 84559085..15c875c0 100644 --- a/lib/e2ee/qolmutility.cpp +++ b/lib/e2ee/qolmutility.cpp @@ -8,9 +8,13 @@ using namespace Quotient; -// Convert olm error to enum -QOlmError lastError(OlmUtility *utility) { - return fromString(olm_utility_last_error(utility)); +OlmErrorCode QOlmUtility::lastErrorCode() const { + return olm_utility_last_error_code(m_utility); +} + +const char* QOlmUtility::lastError() const +{ + return olm_utility_last_error(m_utility); } QOlmUtility::QOlmUtility() @@ -48,15 +52,15 @@ QOlmExpected QOlmUtility::ed25519Verify(const QByteArray& key, 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()); - + message.data(), message.size(), + (void*)signatureBuf.data(), + signatureBuf.size()); if (ret == olm_error()) { - auto error = lastError(m_utility); - if (error == QOlmError::BadMessageMac) { + auto error = lastErrorCode(); + if (error == OLM_BAD_MESSAGE_MAC) return false; - } return error; } - return !ret; // ret == 0 means success + return ret == 0; } diff --git a/lib/e2ee/qolmutility.h b/lib/e2ee/qolmutility.h index 5f6bcdc5..89277385 100644 --- a/lib/e2ee/qolmutility.h +++ b/lib/e2ee/qolmutility.h @@ -32,8 +32,10 @@ public: QOlmExpected ed25519Verify(const QByteArray &key, const QByteArray &message, const QByteArray &signature); + OlmErrorCode lastErrorCode() const; + const char* lastError() const; + private: OlmUtility *m_utility; - }; } diff --git a/lib/events/encryptedevent.cpp b/lib/events/encryptedevent.cpp index 94b44901..114addb5 100644 --- a/lib/events/encryptedevent.cpp +++ b/lib/events/encryptedevent.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-2.1-or-later #include "encryptedevent.h" +#include "e2ee/e2ee.h" #include "logging.h" using namespace Quotient; diff --git a/lib/events/encryptedevent.h b/lib/events/encryptedevent.h index 02d4c7aa..e24e5745 100644 --- a/lib/events/encryptedevent.h +++ b/lib/events/encryptedevent.h @@ -3,10 +3,15 @@ #pragma once -#include "e2ee/e2ee.h" #include "roomevent.h" namespace Quotient { + +constexpr auto CiphertextKeyL = "ciphertext"_ls; +constexpr auto SenderKeyKeyL = "sender_key"_ls; +constexpr auto DeviceIdKeyL = "device_id"_ls; +constexpr auto SessionIdKeyL = "session_id"_ls; + /* * While the specification states: * diff --git a/lib/room.cpp b/lib/room.cpp index 95dd0ab7..382d0243 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -65,7 +65,6 @@ #ifdef Quotient_E2EE_ENABLED #include "e2ee/e2ee.h" #include "e2ee/qolmaccount.h" -#include "e2ee/qolmerrors.h" #include "e2ee/qolminboundsession.h" #include "e2ee/qolmutility.h" #include "database.h" diff --git a/lib/util.h b/lib/util.h index 9efda5d1..ab219488 100644 --- a/lib/util.h +++ b/lib/util.h @@ -111,6 +111,23 @@ private: iterator to; }; +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; +}; +template +asKeyValueRange(T&) -> asKeyValueRange; + /** A replica of std::find_first_of that returns a pair of iterators * * Convenient for cases when you need to know which particular "first of" -- cgit v1.2.3 From bcc05aa1d52cae2b6d8e70bb6cf04fa49904687a Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Wed, 21 Sep 2022 15:45:59 +0200 Subject: Cleanup across E2EE code Notably: - simplified unnecessarily verbose constructs; - formally aligned (no re-numeration was necessary) QOlmMessage::Type with corresponding OLM_ constants; - dropped QOlmSession::encryptMessageType() because it's very sensitive to the order of calling with QOlmSession::encrypt() (and encrypt() itself already calls it and returns the message type); - simplify the return type of pickle() calls that can only fail due to an internal error; - replace const QString& with QStringView or const QByteArray& where appropriate; - use '\0' where it was meant to be instead of '0'. --- autotests/testgroupsession.cpp | 9 ++-- autotests/testolmaccount.cpp | 3 +- autotests/testolmsession.cpp | 10 +++-- autotests/testolmutility.cpp | 15 +++---- lib/connection.cpp | 8 ++-- lib/e2ee/e2ee.h | 2 +- lib/e2ee/qolmaccount.cpp | 54 +++++++++++----------- lib/e2ee/qolmaccount.h | 9 ++-- lib/e2ee/qolminboundsession.cpp | 57 +++++++++++------------- lib/e2ee/qolminboundsession.h | 4 +- lib/e2ee/qolmmessage.cpp | 6 --- lib/e2ee/qolmmessage.h | 12 +++-- lib/e2ee/qolmoutboundsession.cpp | 44 +++++++++--------- lib/e2ee/qolmoutboundsession.h | 4 +- lib/e2ee/qolmsession.cpp | 96 +++++++++++++++------------------------- lib/e2ee/qolmsession.h | 15 +++---- lib/e2ee/qolmutility.cpp | 24 +++++----- lib/e2ee/qolmutils.h | 1 + lib/events/filesourceinfo.cpp | 35 ++++++--------- lib/keyverificationsession.cpp | 6 +-- 20 files changed, 186 insertions(+), 228 deletions(-) diff --git a/autotests/testgroupsession.cpp b/autotests/testgroupsession.cpp index 3c329a8a..18ace73b 100644 --- a/autotests/testgroupsession.cpp +++ b/autotests/testgroupsession.cpp @@ -17,7 +17,9 @@ void TestGroupSession::groupSessionPicklingValid() QCOMPARE(0, ogs->sessionMessageIndex()); auto ogsPickled = ogs->pickle(Unencrypted {}).value(); - auto ogs2 = QOlmOutboundGroupSession::unpickle(ogsPickled, Unencrypted {}).value(); + auto ogs2 = + QOlmOutboundGroupSession::unpickle(std::move(ogsPickled), Unencrypted{}) + .value(); QCOMPARE(ogsId, ogs2->sessionId()); auto igs = QOlmInboundGroupSession::create(ogs->sessionKey().value()); @@ -29,7 +31,8 @@ void TestGroupSession::groupSessionPicklingValid() QCOMPARE(0, igs->firstKnownIndex()); auto igsPickled = igs->pickle(Unencrypted {}); - igs = QOlmInboundGroupSession::unpickle(igsPickled, Unencrypted {}).value(); + igs = QOlmInboundGroupSession::unpickle(std::move(igsPickled), Unencrypted{}) + .value(); QCOMPARE(igsId, igs->sessionId()); } @@ -39,7 +42,7 @@ void TestGroupSession::groupSessionCryptoValid() auto igs = QOlmInboundGroupSession::create(ogs->sessionKey().value()); QCOMPARE(ogs->sessionId(), igs->sessionId()); - const auto plainText = QStringLiteral("Hello world!"); + const auto plainText = "Hello world!"; const auto ciphertext = ogs->encrypt(plainText).value(); // ciphertext valid base64? QVERIFY(QByteArray::fromBase64(ciphertext).size() > 0); diff --git a/autotests/testolmaccount.cpp b/autotests/testolmaccount.cpp index eb428661..0e1eab84 100644 --- a/autotests/testolmaccount.cpp +++ b/autotests/testolmaccount.cpp @@ -25,7 +25,8 @@ void TestOlmAccount::pickleUnpickledTest() auto identityKeys = olmAccount.identityKeys(); auto pickled = olmAccount.pickle(Unencrypted{}).value(); QOlmAccount olmAccount2(QStringLiteral("@foo:bar.com"), QStringLiteral("QuotientTestDevice")); - auto unpickleResult = olmAccount2.unpickle(pickled, Unencrypted{}); + auto unpickleResult = olmAccount2.unpickle(std::move(pickled), + Unencrypted{}); QCOMPARE(unpickleResult, 0); auto identityKeys2 = olmAccount2.identityKeys(); QCOMPARE(identityKeys.curve25519, identityKeys2.curve25519); diff --git a/autotests/testolmsession.cpp b/autotests/testolmsession.cpp index 66a04241..18b0d5f2 100644 --- a/autotests/testolmsession.cpp +++ b/autotests/testolmsession.cpp @@ -11,12 +11,14 @@ std::pair createSessionPair() { QByteArray pickledAccountA("eOBXIKivUT6YYowRH031BNv7zNmzqM5B7CpXdyeaPvala5mt7/OeqrG1qVA7vA1SYloFyvJPIy0QNkD3j1HiPl5vtZHN53rtfZ9exXDok03zjmssqn4IJsqcA7Fbo1FZeKafG0NFcWwCPTdmcV7REqxjqGm3I4K8MQFa45AdTGSUu2C12cWeOcbSMlcINiMral+Uyah1sgPmLJ18h1qcnskXUXQvpffZ5DiUw1Iz5zxnwOQF1GVyowPJD7Zdugvj75RQnDxAn6CzyvrY2k2CuedwqDC3fIXM2xdUNWttW4nC2g4InpBhCVvNwhZYxlUb5BUEjmPI2AB3dAL5ry6o9MFncmbN6x5x"); QByteArray pickledAccountB("eModTvoFi9oOIkax4j4nuxw9Tcl/J8mOmUctUWI68Q89HSaaPTqR+tdlKQ85v2GOs5NlZCp7EuycypN9GQ4fFbHUCrS7nspa3GFBWsR8PnM8+wez5PWmfFZLg3drOvT0jbMjpDx0MjGYClHBqcrEpKx9oFaIRGBaX6HXzT4lRaWSJkXxuX92q8iGNrLn96PuAWFNcD+2JXpPcNFntslwLUNgqzpZ04aIFYwL80GmzyOgq3Bz1GO6u3TgCQEAmTIYN2QkO0MQeuSfe7UoMumhlAJ6R8GPcdSSPtmXNk4tdyzzlgpVq1hm7ZLKto+g8/5Aq3PvnvA8wCqno2+Pi1duK1pZFTIlActr"); - auto accountA = QOlmAccount("accountA:foo.com", "Device1UserA"); - if (accountA.unpickle(pickledAccountA, Unencrypted{}) != OLM_SUCCESS) + auto accountA = QOlmAccount(u"accountA:foo.com", u"Device1UserA"); + if (accountA.unpickle(std::move(pickledAccountA), Unencrypted{}) + != OLM_SUCCESS) qFatal("Failed to unpickle account A: %s", accountA.lastError()); - auto accountB = QOlmAccount("accountB:foo.com", "Device1UserB"); - if (accountB.unpickle(pickledAccountB, Unencrypted{}) != OLM_SUCCESS) + auto accountB = QOlmAccount(u"accountB:foo.com", u"Device1UserB"); + if (accountB.unpickle(std::move(pickledAccountB), Unencrypted{}) + != OLM_SUCCESS) qFatal("Failed to unpickle account B: %s", accountB.lastError()); diff --git a/autotests/testolmutility.cpp b/autotests/testolmutility.cpp index 5b67c805..64ceb3e7 100644 --- a/autotests/testolmutility.cpp +++ b/autotests/testolmutility.cpp @@ -49,7 +49,7 @@ void TestOlmUtility::canonicalJSON() void TestOlmUtility::verifySignedOneTimeKey() { - QOlmAccount aliceOlm { "@alice:matrix.org", "aliceDevice" }; + QOlmAccount aliceOlm { u"@alice:matrix.org", u"aliceDevice" }; aliceOlm.createNewAccount(); aliceOlm.generateOneTimeKeys(1); auto keys = aliceOlm.oneTimeKeys(); @@ -64,16 +64,13 @@ void TestOlmUtility::verifySignedOneTimeKey() auto utility = olm_utility(utilityBuf); - QByteArray signatureBuf1(sig.length(), '0'); + QByteArray signatureBuf1(sig.length(), '\0'); std::copy(sig.begin(), sig.end(), signatureBuf1.begin()); - auto res = olm_ed25519_verify(utility, - aliceOlm.identityKeys().ed25519.data(), - aliceOlm.identityKeys().ed25519.size(), - msg.data(), - msg.size(), - (void *)sig.data(), - sig.size()); + auto res = + olm_ed25519_verify(utility, aliceOlm.identityKeys().ed25519.data(), + aliceOlm.identityKeys().ed25519.size(), msg.data(), + msg.size(), sig.data(), sig.size()); QCOMPARE(std::string(olm_utility_last_error(utility)), "SUCCESS"); QCOMPARE(res, 0); diff --git a/lib/connection.cpp b/lib/connection.cpp index 865dff79..f38bb751 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -652,8 +652,7 @@ void Connection::Private::completeSetup(const QString& mxId) }); } else { // account already existing - auto pickle = database->accountPickle(); - olmAccount->unpickle(pickle, picklingMode); + olmAccount->unpickle(database->accountPickle(), picklingMode); } #endif // Quotient_E2EE_ENABLED emit q->stateChanged(); @@ -2300,14 +2299,13 @@ std::pair Connection::Private::olmEncryptMessage( { const auto& curveKey = curveKeyForUserDevice(userId, device); const auto& olmSession = olmSessions.at(curveKey).front(); - QOlmMessage::Type type = olmSession->encryptMessageType(); const auto result = olmSession->encrypt(message); if (const auto pickle = olmSession->pickle(picklingMode)) { database->updateOlmSession(curveKey, olmSession->sessionId(), *pickle); } else { qWarning(E2EE) << "Failed to pickle olm session: " << pickle.error(); } - return { type, result.toCiphertext() }; + return { result.type(), result.toCiphertext() }; } bool Connection::Private::createOlmSession(const QString& targetUserId, @@ -2343,7 +2341,7 @@ bool Connection::Private::createOlmSession(const QString& targetUserId, return false; } const auto recipientCurveKey = - curveKeyForUserDevice(targetUserId, targetDeviceId); + curveKeyForUserDevice(targetUserId, targetDeviceId).toLatin1(); auto session = QOlmSession::createOutboundSession(olmAccount.get(), recipientCurveKey, signedOneTimeKey->key()); diff --git a/lib/e2ee/e2ee.h b/lib/e2ee/e2ee.h index 51ceff67..5999c0be 100644 --- a/lib/e2ee/e2ee.h +++ b/lib/e2ee/e2ee.h @@ -98,7 +98,7 @@ public: {} //! Unpadded Base64-encoded 32-byte Curve25519 public key - QString key() const { return payload["key"_ls].toString(); } + QByteArray key() const { return payload["key"_ls].toString().toLatin1(); } //! \brief Signatures of the key object //! diff --git a/lib/e2ee/qolmaccount.cpp b/lib/e2ee/qolmaccount.cpp index d33db8e3..556a8274 100644 --- a/lib/e2ee/qolmaccount.cpp +++ b/lib/e2ee/qolmaccount.cpp @@ -27,11 +27,11 @@ const char* QOlmAccount::lastError() const return olm_account_last_error(m_account); } -QOlmAccount::QOlmAccount(const QString& userId, const QString& deviceId, +QOlmAccount::QOlmAccount(QStringView userId, QStringView deviceId, QObject* parent) : QObject(parent) - , m_userId(userId) - , m_deviceId(deviceId) + , m_userId(userId.toString()) + , m_deviceId(deviceId.toString()) {} QOlmAccount::~QOlmAccount() @@ -43,20 +43,20 @@ QOlmAccount::~QOlmAccount() 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); - if (olm_create_account(m_account, randomData.data(), randomSize) + const auto randomLength = olm_create_account_random_length(m_account); + QByteArray randomData = getRandom(randomLength); + if (olm_create_account(m_account, randomData.data(), randomLength) == olm_error()) { throw lastError(); } emit needsSave(); } -OlmErrorCode QOlmAccount::unpickle(QByteArray &pickled, const PicklingMode &mode) +OlmErrorCode QOlmAccount::unpickle(QByteArray&& pickled, const PicklingMode &mode) { m_account = olm_account(new uint8_t[olm_account_size()]); - const QByteArray key = toKey(mode); - if (olm_unpickle_account(m_account, key.data(), key.length(), + if (const auto key = toKey(mode); + olm_unpickle_account(m_account, key.data(), key.length(), pickled.data(), pickled.size()) == olm_error()) { // Probably log the user out since we have no way of getting to the keys @@ -69,7 +69,7 @@ QOlmExpected QOlmAccount::pickle(const PicklingMode &mode) { const QByteArray key = toKey(mode); const size_t pickleLength = olm_pickle_account_length(m_account); - QByteArray pickleBuffer(pickleLength, '0'); + QByteArray pickleBuffer(pickleLength, '\0'); if (olm_pickle_account(m_account, key.data(), key.length(), pickleBuffer.data(), pickleLength) == olm_error()) @@ -80,12 +80,12 @@ QOlmExpected QOlmAccount::pickle(const PicklingMode &mode) IdentityKeys QOlmAccount::identityKeys() const { const size_t keyLength = olm_account_identity_keys_length(m_account); - QByteArray keyBuffer(keyLength, '0'); + QByteArray keyBuffer(keyLength, '\0'); if (olm_account_identity_keys(m_account, keyBuffer.data(), keyLength) == olm_error()) { throw lastError(); } - const QJsonObject key = QJsonDocument::fromJson(keyBuffer).object(); + const auto key = QJsonDocument::fromJson(keyBuffer).object(); return IdentityKeys { key.value(QStringLiteral("curve25519")).toString().toUtf8(), key.value(QStringLiteral("ed25519")).toString().toUtf8() @@ -94,7 +94,7 @@ IdentityKeys QOlmAccount::identityKeys() const QByteArray QOlmAccount::sign(const QByteArray &message) const { - QByteArray signatureBuffer(olm_account_signature_length(m_account), '0'); + QByteArray signatureBuffer(olm_account_signature_length(m_account), '\0'); if (olm_account_sign(m_account, message.data(), message.length(), signatureBuffer.data(), signatureBuffer.length()) @@ -112,15 +112,15 @@ QByteArray QOlmAccount::sign(const QJsonObject &message) const QByteArray QOlmAccount::signIdentityKeys() const { const auto keys = identityKeys(); - return sign(QJsonObject { - { "algorithms", QJsonArray { "m.olm.v1.curve25519-aes-sha2", - "m.megolm.v1.aes-sha2" } }, + return sign(QJsonObject{ + { "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) } } } }); + { "keys", QJsonObject{ { QStringLiteral("curve25519:") + m_deviceId, + QString::fromUtf8(keys.curve25519) }, + { QStringLiteral("ed25519:") + m_deviceId, + QString::fromUtf8(keys.ed25519) } } } }); } size_t QOlmAccount::maxNumberOfOneTimeKeys() const @@ -130,7 +130,7 @@ size_t QOlmAccount::maxNumberOfOneTimeKeys() const size_t QOlmAccount::generateOneTimeKeys(size_t numberOfKeys) { - const size_t randomLength = + const auto randomLength = olm_account_generate_one_time_keys_random_length(m_account, numberOfKeys); QByteArray randomBuffer = getRandom(randomLength); @@ -147,8 +147,8 @@ size_t QOlmAccount::generateOneTimeKeys(size_t numberOfKeys) UnsignedOneTimeKeys QOlmAccount::oneTimeKeys() const { - const size_t oneTimeKeyLength = olm_account_one_time_keys_length(m_account); - QByteArray oneTimeKeysBuffer(static_cast(oneTimeKeyLength), '0'); + const auto oneTimeKeyLength = olm_account_one_time_keys_length(m_account); + QByteArray oneTimeKeysBuffer(static_cast(oneTimeKeyLength), '\0'); if (olm_account_one_time_keys(m_account, oneTimeKeysBuffer.data(), oneTimeKeyLength) @@ -193,13 +193,13 @@ DeviceKeys QOlmAccount::deviceKeys() const SupportedAlgorithms.cend()); const auto idKeys = identityKeys(); - return DeviceKeys { + return DeviceKeys{ .userId = m_userId, .deviceId = m_deviceId, .algorithms = Algorithms, - .keys { { "curve25519:" + m_deviceId, idKeys.curve25519 }, - { "ed25519:" + m_deviceId, idKeys.ed25519 } }, - .signatures { + .keys{ { "curve25519:" + m_deviceId, idKeys.curve25519 }, + { "ed25519:" + m_deviceId, idKeys.ed25519 } }, + .signatures{ { m_userId, { { "ed25519:" + m_deviceId, signIdentityKeys() } } } } }; } diff --git a/lib/e2ee/qolmaccount.h b/lib/e2ee/qolmaccount.h index 5ad98e47..0fb9803f 100644 --- a/lib/e2ee/qolmaccount.h +++ b/lib/e2ee/qolmaccount.h @@ -24,7 +24,8 @@ class QUOTIENT_API QOlmAccount : public QObject { Q_OBJECT public: - QOlmAccount(const QString &userId, const QString &deviceId, QObject *parent = nullptr); + QOlmAccount(QStringView userId, QStringView deviceId, + QObject* parent = nullptr); ~QOlmAccount() override; //! Creates a new instance of OlmAccount. During the instantiation @@ -36,7 +37,7 @@ public: //! 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. - [[nodiscard]] OlmErrorCode unpickle(QByteArray& pickled, + [[nodiscard]] OlmErrorCode unpickle(QByteArray&& pickled, const PicklingMode& mode); //! Serialises an OlmAccount to encrypted Base64. @@ -74,7 +75,7 @@ public: //! 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. + //! \param preKeyMessage An Olm pre-key message that was encrypted for this account. QOlmExpected createInboundSession( const QOlmMessage& preKeyMessage); @@ -93,7 +94,7 @@ public: void markKeysAsPublished(); OlmErrorCode lastErrorCode() const; - const char *lastError() const; + const char* lastError() const; // HACK do not use directly QOlmAccount(OlmAccount *account); diff --git a/lib/e2ee/qolminboundsession.cpp b/lib/e2ee/qolminboundsession.cpp index 870070c2..a05ddf62 100644 --- a/lib/e2ee/qolminboundsession.cpp +++ b/lib/e2ee/qolminboundsession.cpp @@ -2,8 +2,9 @@ // // SPDX-License-Identifier: LGPL-2.1-or-later -#include "e2ee/qolminboundsession.h" -#include "logging.h" +#include "qolminboundsession.h" +#include "qolmutils.h" +#include "../logging.h" #include #include @@ -37,7 +38,7 @@ std::unique_ptr QOlmInboundGroupSession::create(const Q olmInboundGroupSession, reinterpret_cast(key.constData()), key.size()) == olm_error()) { - // FIXME: create QOlmInboundGroupSession earlier and use lastError() + // FIXME: create QOlmInboundGroupSession earlier and use lastErrorCode() throw olm_inbound_group_session_last_error_code(olmInboundGroupSession); } @@ -47,11 +48,10 @@ std::unique_ptr QOlmInboundGroupSession::create(const Q 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; if (olm_import_inbound_group_session( olmInboundGroupSession, - reinterpret_cast(keyBuf.data()), keyBuf.size()) + reinterpret_cast(key.data()), key.size()) == olm_error()) { // FIXME: create QOlmInboundGroupSession earlier and use lastError() throw olm_inbound_group_session_last_error_code(olmInboundGroupSession); @@ -60,19 +60,12 @@ std::unique_ptr QOlmInboundGroupSession::import(const Q 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 QOlmInboundGroupSession::pickle(const PicklingMode& mode) const { - QByteArray pickledBuf(olm_pickle_inbound_group_session_length(m_groupSession), '0'); - const QByteArray key = toKey(mode); - if (olm_pickle_inbound_group_session(m_groupSession, key.data(), + QByteArray pickledBuf( + olm_pickle_inbound_group_session_length(m_groupSession), '\0'); + if (const auto key = toKey(mode); + olm_pickle_inbound_group_session(m_groupSession, key.data(), key.length(), pickledBuf.data(), pickledBuf.length()) == olm_error()) { @@ -82,14 +75,13 @@ QByteArray QOlmInboundGroupSession::pickle(const PicklingMode &mode) const } QOlmExpected QOlmInboundGroupSession::unpickle( - const QByteArray& pickled, const PicklingMode& mode) + 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); + auto key = toKey(mode); if (olm_unpickle_inbound_group_session(groupSession, key.data(), - key.length(), pickledBuf.data(), - pickledBuf.size()) + key.length(), pickled.data(), + pickled.size()) == olm_error()) { // FIXME: create QOlmInboundGroupSession earlier and use lastError() return olm_inbound_group_session_last_error_code(groupSession); @@ -107,13 +99,16 @@ QOlmExpected> QOlmInboundGroupSession::decrypt( // We need to clone the message because // olm_decrypt_max_plaintext_length destroys the input buffer - QByteArray messageBuf(message.length(), '0'); + 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'); + QByteArray plaintextBuf(olm_group_decrypt_max_plaintext_length( + m_groupSession, + reinterpret_cast(messageBuf.data()), + messageBuf.length()), + '\0'); - messageBuf = QByteArray(message.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()), @@ -123,16 +118,17 @@ QOlmExpected> QOlmInboundGroupSession::decrypt( return lastErrorCode(); } - QByteArray output(plaintextLen, '0'); + QByteArray output(plaintextLen, '\0'); std::memcpy(output.data(), plaintextBuf.data(), plaintextLen); return std::make_pair(output, messageIndex); } -QOlmExpected 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'); + QByteArray keyBuf(keyLength, '\0'); if (olm_export_inbound_group_session( m_groupSession, reinterpret_cast(keyBuf.data()), keyLength, messageIndex) @@ -149,7 +145,8 @@ uint32_t QOlmInboundGroupSession::firstKnownIndex() const QByteArray QOlmInboundGroupSession::sessionId() const { - QByteArray sessionIdBuf(olm_inbound_group_session_id_length(m_groupSession), '0'); + QByteArray sessionIdBuf(olm_inbound_group_session_id_length(m_groupSession), + '\0'); if (olm_inbound_group_session_id( m_groupSession, reinterpret_cast(sessionIdBuf.data()), sessionIdBuf.length()) diff --git a/lib/e2ee/qolminboundsession.h b/lib/e2ee/qolminboundsession.h index e8da6355..0b89349a 100644 --- a/lib/e2ee/qolminboundsession.h +++ b/lib/e2ee/qolminboundsession.h @@ -21,11 +21,11 @@ public: //! 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; + QByteArray pickle(const PicklingMode& mode) const; //! Deserialises from encrypted Base64 that was previously obtained by pickling //! an `OlmInboundGroupSession`. static QOlmExpected unpickle( - const QByteArray& pickled, const PicklingMode& mode); + QByteArray&& pickled, const PicklingMode& mode); //! Decrypts ciphertext received for this group session. QOlmExpected > decrypt(const QByteArray& message); //! Export the base64-encoded ratchet key for this session, at the given index, diff --git a/lib/e2ee/qolmmessage.cpp b/lib/e2ee/qolmmessage.cpp index f9b4a5c2..b9cb8bd2 100644 --- a/lib/e2ee/qolmmessage.cpp +++ b/lib/e2ee/qolmmessage.cpp @@ -15,12 +15,6 @@ QOlmMessage::QOlmMessage(QByteArray ciphertext, QOlmMessage::Type type) Q_ASSERT_X(!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; diff --git a/lib/e2ee/qolmmessage.h b/lib/e2ee/qolmmessage.h index b4285a93..ea73b3e3 100644 --- a/lib/e2ee/qolmmessage.h +++ b/lib/e2ee/qolmmessage.h @@ -6,8 +6,9 @@ #include "quotient_export.h" -#include -#include +#include +#include +#include namespace Quotient { @@ -22,15 +23,12 @@ class QUOTIENT_API QOlmMessage : public QByteArray { Q_GADGET public: enum Type { - PreKey = 0, - General, + PreKey = OLM_MESSAGE_TYPE_PRE_KEY, + General = OLM_MESSAGE_TYPE_MESSAGE, }; Q_ENUM(Type) - QOlmMessage() = default; explicit QOlmMessage(QByteArray ciphertext, Type type = General); - explicit QOlmMessage(const QOlmMessage &message); - ~QOlmMessage() = default; static QOlmMessage fromCiphertext(const QByteArray &ciphertext); diff --git a/lib/e2ee/qolmoutboundsession.cpp b/lib/e2ee/qolmoutboundsession.cpp index 79c16e01..22107a21 100644 --- a/lib/e2ee/qolmoutboundsession.cpp +++ b/lib/e2ee/qolmoutboundsession.cpp @@ -2,9 +2,10 @@ // // SPDX-License-Identifier: LGPL-2.1-or-later -#include "e2ee/qolmoutboundsession.h" +#include "qolmoutboundsession.h" -#include "e2ee/qolmutils.h" +#include "logging.h" +#include "qolmutils.h" #include @@ -43,8 +44,9 @@ QOlmOutboundGroupSessionPtr QOlmOutboundGroupSession::create() throw olm_outbound_group_session_last_error_code(olmOutboundGroupSession); } + // FIXME: is it used anywhere? const auto keyMaxLength = olm_outbound_group_session_key_length(olmOutboundGroupSession); - QByteArray keyBuffer(keyMaxLength, '0'); + QByteArray keyBuffer(keyMaxLength, '\0'); olm_outbound_group_session_key(olmOutboundGroupSession, reinterpret_cast(keyBuffer.data()), keyMaxLength); @@ -55,8 +57,9 @@ QOlmOutboundGroupSessionPtr QOlmOutboundGroupSession::create() QOlmExpected QOlmOutboundGroupSession::pickle(const PicklingMode &mode) const { - QByteArray pickledBuf(olm_pickle_outbound_group_session_length(m_groupSession), '0'); - QByteArray key = toKey(mode); + QByteArray pickledBuf( + olm_pickle_outbound_group_session_length(m_groupSession), '\0'); + auto key = toKey(mode); if (olm_pickle_outbound_group_session(m_groupSession, key.data(), key.length(), pickledBuf.data(), pickledBuf.length()) @@ -64,40 +67,41 @@ QOlmExpected QOlmOutboundGroupSession::pickle(const PicklingMode &mo return lastErrorCode(); key.clear(); - return pickledBuf; } -QOlmExpected QOlmOutboundGroupSession::unpickle(const 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()]); - QByteArray key = toKey(mode); + auto key = toKey(mode); if (olm_unpickle_outbound_group_session(olmOutboundGroupSession, key.data(), - key.length(), pickledBuf.data(), - pickledBuf.length()) + key.length(), pickled.data(), + pickled.length()) == olm_error()) { // FIXME: create the session object earlier and use lastError() return olm_outbound_group_session_last_error_code( olmOutboundGroupSession); } const auto idMaxLength = olm_outbound_group_session_id_length(olmOutboundGroupSession); - QByteArray idBuffer(idMaxLength, '0'); + QByteArray idBuffer(idMaxLength, '\0'); olm_outbound_group_session_id(olmOutboundGroupSession, reinterpret_cast(idBuffer.data()), idBuffer.length()); + // FIXME: idBuffer doesn't seem to be used, is it needed here? key.clear(); return std::make_unique(olmOutboundGroupSession); } -QOlmExpected QOlmOutboundGroupSession::encrypt(const QString &plaintext) const +QOlmExpected QOlmOutboundGroupSession::encrypt( + const QByteArray& plaintext) const { - QByteArray plaintextBuf = plaintext.toUtf8(); - const auto messageMaxLength = olm_group_encrypt_message_length(m_groupSession, plaintextBuf.length()); - QByteArray messageBuf(messageMaxLength, '0'); + const auto messageMaxLength = + olm_group_encrypt_message_length(m_groupSession, plaintext.length()); + QByteArray messageBuf(messageMaxLength, '\0'); if (olm_group_encrypt(m_groupSession, - reinterpret_cast(plaintextBuf.data()), - plaintextBuf.length(), + reinterpret_cast(plaintext.data()), + plaintext.length(), reinterpret_cast(messageBuf.data()), messageBuf.length()) == olm_error()) @@ -114,7 +118,7 @@ uint32_t QOlmOutboundGroupSession::sessionMessageIndex() const QByteArray QOlmOutboundGroupSession::sessionId() const { const auto idMaxLength = olm_outbound_group_session_id_length(m_groupSession); - QByteArray idBuffer(idMaxLength, '0'); + QByteArray idBuffer(idMaxLength, '\0'); if (olm_outbound_group_session_id( m_groupSession, reinterpret_cast(idBuffer.data()), idBuffer.length()) @@ -127,7 +131,7 @@ QByteArray QOlmOutboundGroupSession::sessionId() const QOlmExpected QOlmOutboundGroupSession::sessionKey() const { const auto keyMaxLength = olm_outbound_group_session_key_length(m_groupSession); - QByteArray keyBuffer(keyMaxLength, '0'); + QByteArray keyBuffer(keyMaxLength, '\0'); if (olm_outbound_group_session_key( m_groupSession, reinterpret_cast(keyBuffer.data()), keyMaxLength) diff --git a/lib/e2ee/qolmoutboundsession.h b/lib/e2ee/qolmoutboundsession.h index cd26fc67..e5e73ddf 100644 --- a/lib/e2ee/qolmoutboundsession.h +++ b/lib/e2ee/qolmoutboundsession.h @@ -24,10 +24,10 @@ public: //! Deserialises from encrypted Base64 that was previously obtained by //! pickling a `QOlmOutboundGroupSession`. static QOlmExpected unpickle( - const QByteArray& pickled, const PicklingMode& mode); + QByteArray&& pickled, const PicklingMode& mode); //! Encrypts a plaintext message using the session. - QOlmExpected encrypt(const QString& plaintext) const; + QOlmExpected encrypt(const QByteArray& plaintext) const; //! Get the current message index for this session. //! diff --git a/lib/e2ee/qolmsession.cpp b/lib/e2ee/qolmsession.cpp index 771d310d..e252c37f 100644 --- a/lib/e2ee/qolmsession.cpp +++ b/lib/e2ee/qolmsession.cpp @@ -76,22 +76,17 @@ QOlmExpected QOlmSession::createInboundSessionFrom( } QOlmExpected QOlmSession::createOutboundSession( - QOlmAccount* account, const QString& theirIdentityKey, - const QString& theirOneTimeKey) + QOlmAccount* account, const QByteArray& theirIdentityKey, + const QByteArray& theirOneTimeKey) { - auto *olmOutboundSession = create(); - const auto randomLen = olm_create_outbound_session_random_length(olmOutboundSession); - QByteArray randomBuf = getRandom(randomLen); + auto* olmOutboundSession = create(); + auto randomBuf = getRandom( + olm_create_outbound_session_random_length(olmOutboundSession)); - QByteArray theirIdentityKeyBuf = theirIdentityKey.toUtf8(); - QByteArray theirOneTimeKeyBuf = theirOneTimeKey.toUtf8(); if (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()) + olmOutboundSession, account->data(), theirIdentityKey.data(), + theirIdentityKey.length(), theirOneTimeKey.data(), + theirOneTimeKey.length(), randomBuf.data(), randomBuf.length()) == olm_error()) { // FIXME: the QOlmSession object should be created earlier const auto lastErr = olm_session_last_error_code(olmOutboundSession); @@ -107,7 +102,7 @@ QOlmExpected QOlmSession::createOutboundSession( QOlmExpected QOlmSession::pickle(const PicklingMode &mode) const { - QByteArray pickledBuf(olm_pickle_session_length(m_session), '0'); + QByteArray pickledBuf(olm_pickle_session_length(m_session), '\0'); QByteArray key = toKey(mode); if (olm_pickle_session(m_session, key.data(), key.length(), pickledBuf.data(), pickledBuf.length()) @@ -118,14 +113,13 @@ QOlmExpected QOlmSession::pickle(const PicklingMode &mode) const return pickledBuf; } -QOlmExpected QOlmSession::unpickle(const QByteArray& pickled, +QOlmExpected QOlmSession::unpickle(QByteArray&& pickled, const PicklingMode& mode) { - QByteArray pickledBuf = pickled; auto *olmSession = create(); - QByteArray key = toKey(mode); + auto key = toKey(mode); if (olm_unpickle_session(olmSession, key.data(), key.length(), - pickledBuf.data(), pickledBuf.length()) + pickled.data(), pickled.length()) == olm_error()) { // FIXME: the QOlmSession object should be created earlier return olm_session_last_error_code(olmSession); @@ -135,52 +129,48 @@ QOlmExpected QOlmSession::unpickle(const QByteArray& pickled, return std::make_unique(olmSession); } -QOlmMessage QOlmSession::encrypt(const QString &plaintext) +QOlmMessage QOlmSession::encrypt(const QByteArray& 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); - if (olm_encrypt(m_session, reinterpret_cast(plaintextBuf.data()), - plaintextBuf.length(), - reinterpret_cast(randomBuf.data()), - randomBuf.length(), - reinterpret_cast(messageBuf.data()), + const auto messageMaxLength = + olm_encrypt_message_length(m_session, plaintext.length()); + QByteArray messageBuf(messageMaxLength, '0'); + // NB: The type has to be calculated before calling olm_encrypt() + const auto messageType = olm_encrypt_message_type(m_session); + auto randomBuf = getRandom(olm_encrypt_random_length(m_session)); + if (olm_encrypt(m_session, plaintext.data(), plaintext.length(), + randomBuf.data(), randomBuf.length(), messageBuf.data(), messageBuf.length()) == olm_error()) { throw lastError(); } - return QOlmMessage(messageBuf, messageType); + randomBuf.clear(); + return QOlmMessage(messageBuf, QOlmMessage::Type(messageType)); } QOlmExpected 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; + const auto messageTypeValue = message.type(); // We need to clone the message because // olm_decrypt_max_plaintext_length destroys the input buffer - QByteArray messageBuf(ciphertext.length(), '0'); + 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()); + const auto plaintextMaxLen = olm_decrypt_max_plaintext_length( + m_session, messageTypeValue, messageBuf.data(), messageBuf.length()); if (plaintextMaxLen == olm_error()) { return lastError(); } - QByteArray plaintextBuf(plaintextMaxLen, '0'); - QByteArray messageBuf2(ciphertext.length(), '0'); + 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); + const auto plaintextResultLen = + olm_decrypt(m_session, messageTypeValue, messageBuf2.data(), + messageBuf2.length(), plaintextBuf.data(), plaintextMaxLen); if (plaintextResultLen == olm_error()) { const auto lastErr = lastErrorCode(); if (lastErr == OLM_OUTPUT_BUFFER_TOO_SMALL) { @@ -188,31 +178,15 @@ QOlmExpected QOlmSession::decrypt(const QOlmMessage &message) const } 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(); - } - if (messageTypeResult == OLM_MESSAGE_TYPE_PRE_KEY) { - return QOlmMessage::PreKey; - } - return QOlmMessage::General; + plaintextBuf.truncate(plaintextResultLen); + return plaintextBuf; } QByteArray QOlmSession::sessionId() const { const auto idMaxLength = olm_session_id_length(m_session); QByteArray idBuffer(idMaxLength, '0'); - if (olm_session_id(m_session, reinterpret_cast(idBuffer.data()), - idBuffer.length()) - == olm_error()) { + if (olm_session_id(m_session, idBuffer.data(), idMaxLength) == olm_error()) { throw lastError(); } return idBuffer; diff --git a/lib/e2ee/qolmsession.h b/lib/e2ee/qolmsession.h index cc988a03..174e98ef 100644 --- a/lib/e2ee/qolmsession.h +++ b/lib/e2ee/qolmsession.h @@ -26,18 +26,18 @@ public: const QOlmMessage& preKeyMessage); static QOlmExpected createOutboundSession( - QOlmAccount* account, const QString& theirIdentityKey, - const QString& theirOneTimeKey); + QOlmAccount* account, const QByteArray& theirIdentityKey, + const QByteArray& theirOneTimeKey); //! Serialises an `QOlmSession` to encrypted Base64. QOlmExpected pickle(const PicklingMode &mode) const; - //! Deserialises from encrypted Base64 that was previously obtained by pickling a `QOlmSession`. - static QOlmExpected unpickle( - const QByteArray& pickled, const PicklingMode& mode); + //! Deserialises from encrypted Base64 previously made with pickle() + static QOlmExpected unpickle(QByteArray&& pickled, + const PicklingMode& mode); //! Encrypts a plaintext message using the session. - QOlmMessage encrypt(const QString &plaintext); + QOlmMessage encrypt(const QByteArray& plaintext); //! Decrypts a message using this session. Decoding is lossy, meaning if //! the decrypted plaintext contains invalid UTF-8 symbols, they will @@ -47,9 +47,6 @@ public: //! 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; diff --git a/lib/e2ee/qolmutility.cpp b/lib/e2ee/qolmutility.cpp index 15c875c0..08c2b699 100644 --- a/lib/e2ee/qolmutility.cpp +++ b/lib/e2ee/qolmutility.cpp @@ -32,7 +32,7 @@ QOlmUtility::~QOlmUtility() QString QOlmUtility::sha256Bytes(const QByteArray &inputBuf) const { const auto outputLen = olm_sha256_length(m_utility); - QByteArray outputBuf(outputLen, '0'); + QByteArray outputBuf(outputLen, '\0'); olm_sha256(m_utility, inputBuf.data(), inputBuf.length(), outputBuf.data(), outputBuf.length()); @@ -48,19 +48,17 @@ QOlmExpected QOlmUtility::ed25519Verify(const QByteArray& key, const QByteArray& message, const QByteArray& signature) { - QByteArray signatureBuf(signature.length(), '0'); + 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()); - if (ret == olm_error()) { - auto error = lastErrorCode(); - if (error == OLM_BAD_MESSAGE_MAC) - return false; - return error; - } + if (olm_ed25519_verify(m_utility, key.data(), key.size(), message.data(), + message.size(), signatureBuf.data(), + signatureBuf.size()) + == 0) + return true; - return ret == 0; + auto error = lastErrorCode(); + if (error == OLM_BAD_MESSAGE_MAC) + return false; + return error; } diff --git a/lib/e2ee/qolmutils.h b/lib/e2ee/qolmutils.h index f218e628..7a8511c3 100644 --- a/lib/e2ee/qolmutils.h +++ b/lib/e2ee/qolmutils.h @@ -9,6 +9,7 @@ #include "e2ee/e2ee.h" namespace Quotient { + // Convert PicklingMode to key QUOTIENT_API QByteArray toKey(const PicklingMode &mode); QUOTIENT_API QByteArray getRandom(size_t bufferSize); diff --git a/lib/events/filesourceinfo.cpp b/lib/events/filesourceinfo.cpp index e8b6794b..6abe6a08 100644 --- a/lib/events/filesourceinfo.cpp +++ b/lib/events/filesourceinfo.cpp @@ -59,19 +59,15 @@ std::pair Quotient::encryptFile( const QByteArray& plainText) { #ifdef Quotient_E2EE_ENABLED - QByteArray k = getRandom(32); - auto kBase64 = k.toBase64(); - QByteArray iv = getRandom(16); - JWK key = { "oct"_ls, - { "encrypt"_ls, "decrypt"_ls }, - "A256CTR"_ls, - QString(k.toBase64()) - .replace(u'/', u'_') - .replace(u'+', u'-') - .left(kBase64.indexOf('=')), - true }; - - int length; + auto k = getRandom(32); + auto kBase64 = k.toBase64(QByteArray::Base64UrlEncoding + | QByteArray::OmitTrailingEquals); + auto iv = getRandom(16); + JWK key = { + "oct"_ls, { "encrypt"_ls, "decrypt"_ls }, "A256CTR"_ls, kBase64, true + }; + + int length = -1; auto* ctx = EVP_CIPHER_CTX_new(); EVP_EncryptInit_ex(ctx, EVP_aes_256_ctr(), nullptr, reinterpret_cast(k.data()), @@ -89,14 +85,11 @@ std::pair Quotient::encryptFile( EVP_CIPHER_CTX_free(ctx); auto hash = QCryptographicHash::hash(cipherText, QCryptographicHash::Sha256) - .toBase64(); - auto ivBase64 = iv.toBase64(); - EncryptedFileMetadata efm = { {}, - key, - ivBase64.left(ivBase64.indexOf('=')), - { { QStringLiteral("sha256"), - hash.left(hash.indexOf('=')) } }, - "v2"_ls }; + .toBase64(QByteArray::OmitTrailingEquals); + auto ivBase64 = iv.toBase64(QByteArray::OmitTrailingEquals); + EncryptedFileMetadata efm = { + {}, key, ivBase64, { { QStringLiteral("sha256"), hash } }, "v2"_ls + }; return { efm, cipherText }; #else return {}; diff --git a/lib/keyverificationsession.cpp b/lib/keyverificationsession.cpp index 3f76eac1..0f24c743 100644 --- a/lib/keyverificationsession.cpp +++ b/lib/keyverificationsession.cpp @@ -71,9 +71,9 @@ void KeyVerificationSession::init(milliseconds timeout) QTimer::singleShot(timeout, this, [this] { cancelVerification(TIMEOUT); }); m_sas = olm_sas(new std::byte[olm_sas_size()]); - auto randomSize = olm_create_sas_random_length(m_sas); - auto random = getRandom(randomSize); - olm_create_sas(m_sas, random.data(), randomSize); + const auto randomLength = olm_create_sas_random_length(m_sas); + auto random = getRandom(randomLength); + olm_create_sas(m_sas, random.data(), randomLength); } KeyVerificationSession::~KeyVerificationSession() -- cgit v1.2.3 From bc1ded73bedf593acda80b00eb7da32f688c4843 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Wed, 21 Sep 2022 16:11:39 +0200 Subject: RandomBuffer A convenient abstraction swallowing all the type casts and, more importantly, cleanup on destruction (previous code only cleaned up the buffer upon a successful call to Olm API but not upon an error). --- lib/connection.cpp | 2 +- lib/e2ee/qolmaccount.cpp | 9 +++------ lib/e2ee/qolmoutboundsession.cpp | 9 +++------ lib/e2ee/qolmsession.cpp | 12 +++++------- lib/e2ee/qolmutils.cpp | 7 +++---- lib/e2ee/qolmutils.h | 23 ++++++++++++++++++++++- lib/events/filesourceinfo.cpp | 8 +++----- lib/keyverificationsession.cpp | 3 +-- 8 files changed, 41 insertions(+), 32 deletions(-) diff --git a/lib/connection.cpp b/lib/connection.cpp index f38bb751..cd8ee727 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -614,7 +614,7 @@ void Connection::Private::completeSetup(const QString& mxId) loop.exec(); if (job.error() == QKeychain::Error::EntryNotFound) { - picklingMode = Encrypted { getRandom(128) }; + picklingMode = Encrypted { RandomBuffer(128) }; QKeychain::WritePasswordJob job(qAppName()); job.setAutoDelete(false); job.setKey(accountSettings.userId() + QStringLiteral("-Pickle")); diff --git a/lib/e2ee/qolmaccount.cpp b/lib/e2ee/qolmaccount.cpp index 556a8274..b56272ef 100644 --- a/lib/e2ee/qolmaccount.cpp +++ b/lib/e2ee/qolmaccount.cpp @@ -44,8 +44,7 @@ void QOlmAccount::createNewAccount() { m_account = olm_account(new uint8_t[olm_account_size()]); const auto randomLength = olm_create_account_random_length(m_account); - QByteArray randomData = getRandom(randomLength); - if (olm_create_account(m_account, randomData.data(), randomLength) + if (olm_create_account(m_account, RandomBuffer(randomLength), randomLength) == olm_error()) { throw lastError(); } @@ -133,10 +132,8 @@ size_t QOlmAccount::generateOneTimeKeys(size_t numberOfKeys) const auto randomLength = olm_account_generate_one_time_keys_random_length(m_account, numberOfKeys); - QByteArray randomBuffer = getRandom(randomLength); - const auto result = - olm_account_generate_one_time_keys(m_account, numberOfKeys, - randomBuffer.data(), randomLength); + const auto result = olm_account_generate_one_time_keys( + m_account, numberOfKeys, RandomBuffer(randomLength), randomLength); if (result == olm_error()) { throw lastError(); diff --git a/lib/e2ee/qolmoutboundsession.cpp b/lib/e2ee/qolmoutboundsession.cpp index 22107a21..3d176274 100644 --- a/lib/e2ee/qolmoutboundsession.cpp +++ b/lib/e2ee/qolmoutboundsession.cpp @@ -34,11 +34,10 @@ 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); - QByteArray randomBuf = getRandom(randomLength); - if (olm_init_outbound_group_session( - olmOutboundGroupSession, - reinterpret_cast(randomBuf.data()), randomBuf.length()) + if (olm_init_outbound_group_session(olmOutboundGroupSession, + RandomBuffer(randomLength).bytes(), + randomLength) == olm_error()) { // FIXME: create the session object earlier and use lastError() throw olm_outbound_group_session_last_error_code(olmOutboundGroupSession); @@ -50,8 +49,6 @@ QOlmOutboundGroupSessionPtr QOlmOutboundGroupSession::create() olm_outbound_group_session_key(olmOutboundGroupSession, reinterpret_cast(keyBuffer.data()), keyMaxLength); - randomBuf.clear(); - return std::make_unique(olmOutboundGroupSession); } diff --git a/lib/e2ee/qolmsession.cpp b/lib/e2ee/qolmsession.cpp index e252c37f..7c102a96 100644 --- a/lib/e2ee/qolmsession.cpp +++ b/lib/e2ee/qolmsession.cpp @@ -80,13 +80,13 @@ QOlmExpected QOlmSession::createOutboundSession( const QByteArray& theirOneTimeKey) { auto* olmOutboundSession = create(); - auto randomBuf = getRandom( - olm_create_outbound_session_random_length(olmOutboundSession)); + const auto randomLength = + olm_create_outbound_session_random_length(olmOutboundSession); if (olm_create_outbound_session( olmOutboundSession, account->data(), theirIdentityKey.data(), theirIdentityKey.length(), theirOneTimeKey.data(), - theirOneTimeKey.length(), randomBuf.data(), randomBuf.length()) + theirOneTimeKey.length(), RandomBuffer(randomLength), randomLength) == olm_error()) { // FIXME: the QOlmSession object should be created earlier const auto lastErr = olm_session_last_error_code(olmOutboundSession); @@ -96,7 +96,6 @@ QOlmExpected QOlmSession::createOutboundSession( return lastErr; } - randomBuf.clear(); return std::make_unique(olmOutboundSession); } @@ -136,15 +135,14 @@ QOlmMessage QOlmSession::encrypt(const QByteArray& plaintext) QByteArray messageBuf(messageMaxLength, '0'); // NB: The type has to be calculated before calling olm_encrypt() const auto messageType = olm_encrypt_message_type(m_session); - auto randomBuf = getRandom(olm_encrypt_random_length(m_session)); + const auto randomLength = olm_encrypt_random_length(m_session); if (olm_encrypt(m_session, plaintext.data(), plaintext.length(), - randomBuf.data(), randomBuf.length(), messageBuf.data(), + RandomBuffer(randomLength), randomLength, messageBuf.data(), messageBuf.length()) == olm_error()) { throw lastError(); } - randomBuf.clear(); return QOlmMessage(messageBuf, QOlmMessage::Type(messageType)); } diff --git a/lib/e2ee/qolmutils.cpp b/lib/e2ee/qolmutils.cpp index 6f7937e8..c6e51bcd 100644 --- a/lib/e2ee/qolmutils.cpp +++ b/lib/e2ee/qolmutils.cpp @@ -15,9 +15,8 @@ QByteArray Quotient::toKey(const Quotient::PicklingMode &mode) return std::get(mode).key; } -QByteArray Quotient::getRandom(size_t bufferSize) +RandomBuffer::RandomBuffer(size_t size) + : QByteArray(static_cast(size), '\0') { - QByteArray buffer(bufferSize, '0'); - QRandomGenerator::system()->generate(buffer.begin(), buffer.end()); - return buffer; + QRandomGenerator::system()->generate(begin(), end()); } diff --git a/lib/e2ee/qolmutils.h b/lib/e2ee/qolmutils.h index 7a8511c3..da9d2d18 100644 --- a/lib/e2ee/qolmutils.h +++ b/lib/e2ee/qolmutils.h @@ -12,5 +12,26 @@ namespace Quotient { // Convert PicklingMode to key QUOTIENT_API QByteArray toKey(const PicklingMode &mode); -QUOTIENT_API QByteArray getRandom(size_t bufferSize); + +class QUOTIENT_API RandomBuffer : public QByteArray { +public: + explicit RandomBuffer(size_t size); + ~RandomBuffer() { clear(); } + + // NOLINTNEXTLINE(google-explicit-constructor) + QUO_IMPLICIT operator void*() { return data(); } + char* chars() { return data(); } + uint8_t* bytes() { return reinterpret_cast(data()); } + + Q_DISABLE_COPY(RandomBuffer) + RandomBuffer(RandomBuffer&&) = default; + void operator=(RandomBuffer&&) = delete; +}; + +[[deprecated("Create RandomBuffer directly")]] inline auto getRandom( + size_t bufferSize) +{ + return RandomBuffer(bufferSize); } + +} // namespace Quotient diff --git a/lib/events/filesourceinfo.cpp b/lib/events/filesourceinfo.cpp index 6abe6a08..a60d86d2 100644 --- a/lib/events/filesourceinfo.cpp +++ b/lib/events/filesourceinfo.cpp @@ -59,19 +59,17 @@ std::pair Quotient::encryptFile( const QByteArray& plainText) { #ifdef Quotient_E2EE_ENABLED - auto k = getRandom(32); + auto k = RandomBuffer(32); auto kBase64 = k.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); - auto iv = getRandom(16); + auto iv = RandomBuffer(16); JWK key = { "oct"_ls, { "encrypt"_ls, "decrypt"_ls }, "A256CTR"_ls, kBase64, true }; int length = -1; auto* ctx = EVP_CIPHER_CTX_new(); - EVP_EncryptInit_ex(ctx, EVP_aes_256_ctr(), nullptr, - reinterpret_cast(k.data()), - reinterpret_cast(iv.data())); + EVP_EncryptInit_ex(ctx, EVP_aes_256_ctr(), nullptr, k.bytes(), iv.bytes()); const auto blockSize = EVP_CIPHER_CTX_block_size(ctx); QByteArray cipherText(plainText.size() + blockSize - 1, '\0'); EVP_EncryptUpdate(ctx, reinterpret_cast(cipherText.data()), diff --git a/lib/keyverificationsession.cpp b/lib/keyverificationsession.cpp index 0f24c743..171596c0 100644 --- a/lib/keyverificationsession.cpp +++ b/lib/keyverificationsession.cpp @@ -72,8 +72,7 @@ void KeyVerificationSession::init(milliseconds timeout) m_sas = olm_sas(new std::byte[olm_sas_size()]); const auto randomLength = olm_create_sas_random_length(m_sas); - auto random = getRandom(randomLength); - olm_create_sas(m_sas, random.data(), randomLength); + olm_create_sas(m_sas, RandomBuffer(randomLength), randomLength); } KeyVerificationSession::~KeyVerificationSession() -- cgit v1.2.3 From 2ff045d8b381bfbd64100d083f81b61c5fe87b23 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Wed, 21 Sep 2022 21:09:49 +0200 Subject: Wrap error reporting into facility macros Facility macros to report Olm errors: QOLM_INTERNAL_ERROR[_X], QOLM_FAIL_OR_LOG[_X] --- autotests/testgroupsession.cpp | 8 +++---- autotests/testolmaccount.cpp | 2 +- lib/connection.cpp | 23 ++++++-------------- lib/database.cpp | 32 +++++++++++++--------------- lib/e2ee/qolmaccount.cpp | 37 +++++++++++++++++++------------- lib/e2ee/qolmaccount.h | 4 +++- lib/e2ee/qolminboundsession.cpp | 28 +++++++++++++++++------- lib/e2ee/qolminboundsession.h | 4 ++-- lib/e2ee/qolmoutboundsession.cpp | 24 ++++++++++++--------- lib/e2ee/qolmoutboundsession.h | 6 +++--- lib/e2ee/qolmsession.cpp | 46 ++++++++++++++++++++++------------------ lib/e2ee/qolmsession.h | 2 +- lib/e2ee/qolmutils.h | 18 ++++++++++++++++ lib/room.cpp | 45 ++++++++++++++------------------------- 14 files changed, 150 insertions(+), 129 deletions(-) diff --git a/autotests/testgroupsession.cpp b/autotests/testgroupsession.cpp index 18ace73b..1054a160 100644 --- a/autotests/testgroupsession.cpp +++ b/autotests/testgroupsession.cpp @@ -16,13 +16,13 @@ void TestGroupSession::groupSessionPicklingValid() QVERIFY(QByteArray::fromBase64(ogsId).size() > 0); QCOMPARE(0, ogs->sessionMessageIndex()); - auto ogsPickled = ogs->pickle(Unencrypted {}).value(); + auto&& ogsPickled = ogs->pickle(Unencrypted {}); auto ogs2 = QOlmOutboundGroupSession::unpickle(std::move(ogsPickled), Unencrypted{}) .value(); QCOMPARE(ogsId, ogs2->sessionId()); - auto igs = QOlmInboundGroupSession::create(ogs->sessionKey().value()); + auto igs = QOlmInboundGroupSession::create(ogs->sessionKey()).value(); const auto igsId = igs->sessionId(); // ID is valid base64? QVERIFY(QByteArray::fromBase64(igsId).size() > 0); @@ -39,11 +39,11 @@ void TestGroupSession::groupSessionPicklingValid() void TestGroupSession::groupSessionCryptoValid() { auto ogs = QOlmOutboundGroupSession::create(); - auto igs = QOlmInboundGroupSession::create(ogs->sessionKey().value()); + auto igs = QOlmInboundGroupSession::create(ogs->sessionKey()).value(); QCOMPARE(ogs->sessionId(), igs->sessionId()); const auto plainText = "Hello world!"; - const auto ciphertext = ogs->encrypt(plainText).value(); + const auto ciphertext = ogs->encrypt(plainText); // ciphertext valid base64? QVERIFY(QByteArray::fromBase64(ciphertext).size() > 0); diff --git a/autotests/testolmaccount.cpp b/autotests/testolmaccount.cpp index 0e1eab84..56532698 100644 --- a/autotests/testolmaccount.cpp +++ b/autotests/testolmaccount.cpp @@ -23,7 +23,7 @@ void TestOlmAccount::pickleUnpickledTest() QOlmAccount olmAccount(QStringLiteral("@foo:bar.com"), QStringLiteral("QuotientTestDevice")); olmAccount.createNewAccount(); auto identityKeys = olmAccount.identityKeys(); - auto pickled = olmAccount.pickle(Unencrypted{}).value(); + auto pickled = olmAccount.pickle(Unencrypted{}); QOlmAccount olmAccount2(QStringLiteral("@foo:bar.com"), QStringLiteral("QuotientTestDevice")); auto unpickleResult = olmAccount2.unpickle(std::move(pickled), Unencrypted{}); diff --git a/lib/connection.cpp b/lib/connection.cpp index cd8ee727..0afbffbc 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -206,13 +206,9 @@ public: } void saveSession(const QOlmSession& session, const QString& senderKey) const { - if (auto pickleResult = session.pickle(picklingMode)) - q->database()->saveOlmSession(senderKey, session.sessionId(), - *pickleResult, - QDateTime::currentDateTime()); - else - qCWarning(E2EE) << "Failed to pickle olm session. Error" - << pickleResult.error(); + q->database()->saveOlmSession(senderKey, session.sessionId(), + session.pickle(picklingMode), + QDateTime::currentDateTime()); } template @@ -2219,11 +2215,7 @@ void Connection::saveOlmAccount() { #ifdef Quotient_E2EE_ENABLED qCDebug(E2EE) << "Saving olm account"; - if (const auto expectedPickle = d->olmAccount->pickle(d->picklingMode)) - d->database->setAccountPickle(*expectedPickle); - else - qCWarning(E2EE) << "Couldn't save Olm account pickle:" - << expectedPickle.error(); + d->database->setAccountPickle(d->olmAccount->pickle(d->picklingMode)); #endif } @@ -2300,11 +2292,8 @@ std::pair Connection::Private::olmEncryptMessage( const auto& curveKey = curveKeyForUserDevice(userId, device); const auto& olmSession = olmSessions.at(curveKey).front(); const auto result = olmSession->encrypt(message); - if (const auto pickle = olmSession->pickle(picklingMode)) { - database->updateOlmSession(curveKey, olmSession->sessionId(), *pickle); - } else { - qWarning(E2EE) << "Failed to pickle olm session: " << pickle.error(); - } + database->updateOlmSession(curveKey, olmSession->sessionId(), + olmSession->pickle(picklingMode)); return { result.type(), result.toCiphertext() }; } diff --git a/lib/database.cpp b/lib/database.cpp index 4eb82cf5..2b472648 100644 --- a/lib/database.cpp +++ b/lib/database.cpp @@ -323,23 +323,21 @@ void Database::saveCurrentOutboundMegolmSession( const QOlmOutboundGroupSession& session) { const auto pickle = session.pickle(picklingMode); - if (pickle) { - auto deleteQuery = prepareQuery(QStringLiteral("DELETE FROM outbound_megolm_sessions WHERE roomId=:roomId AND sessionId=:sessionId;")); - deleteQuery.bindValue(":roomId", roomId); - deleteQuery.bindValue(":sessionId", session.sessionId()); - - auto insertQuery = prepareQuery(QStringLiteral("INSERT INTO outbound_megolm_sessions(roomId, sessionId, pickle, creationTime, messageCount) VALUES(:roomId, :sessionId, :pickle, :creationTime, :messageCount);")); - insertQuery.bindValue(":roomId", roomId); - insertQuery.bindValue(":sessionId", session.sessionId()); - insertQuery.bindValue(":pickle", pickle.value()); - insertQuery.bindValue(":creationTime", session.creationTime()); - insertQuery.bindValue(":messageCount", session.messageCount()); - - transaction(); - execute(deleteQuery); - execute(insertQuery); - commit(); - } + auto deleteQuery = prepareQuery(QStringLiteral("DELETE FROM outbound_megolm_sessions WHERE roomId=:roomId AND sessionId=:sessionId;")); + deleteQuery.bindValue(":roomId", roomId); + deleteQuery.bindValue(":sessionId", session.sessionId()); + + auto insertQuery = prepareQuery(QStringLiteral("INSERT INTO outbound_megolm_sessions(roomId, sessionId, pickle, creationTime, messageCount) VALUES(:roomId, :sessionId, :pickle, :creationTime, :messageCount);")); + insertQuery.bindValue(":roomId", roomId); + insertQuery.bindValue(":sessionId", session.sessionId()); + insertQuery.bindValue(":pickle", pickle); + insertQuery.bindValue(":creationTime", session.creationTime()); + insertQuery.bindValue(":messageCount", session.messageCount()); + + transaction(); + execute(deleteQuery); + execute(insertQuery); + commit(); } QOlmOutboundGroupSessionPtr Database::loadCurrentOutboundMegolmSession(const QString& roomId, const PicklingMode& picklingMode) diff --git a/lib/e2ee/qolmaccount.cpp b/lib/e2ee/qolmaccount.cpp index b56272ef..d03bcb3b 100644 --- a/lib/e2ee/qolmaccount.cpp +++ b/lib/e2ee/qolmaccount.cpp @@ -45,9 +45,9 @@ void QOlmAccount::createNewAccount() m_account = olm_account(new uint8_t[olm_account_size()]); const auto randomLength = olm_create_account_random_length(m_account); if (olm_create_account(m_account, RandomBuffer(randomLength), randomLength) - == olm_error()) { - throw lastError(); - } + == olm_error()) + QOLM_INTERNAL_ERROR("Failed to create a new account"); + emit needsSave(); } @@ -64,7 +64,7 @@ OlmErrorCode QOlmAccount::unpickle(QByteArray&& pickled, const PicklingMode &mod return OLM_SUCCESS; } -QOlmExpected QOlmAccount::pickle(const PicklingMode &mode) +QByteArray QOlmAccount::pickle(const PicklingMode &mode) { const QByteArray key = toKey(mode); const size_t pickleLength = olm_pickle_account_length(m_account); @@ -72,7 +72,9 @@ QOlmExpected QOlmAccount::pickle(const PicklingMode &mode) if (olm_pickle_account(m_account, key.data(), key.length(), pickleBuffer.data(), pickleLength) == olm_error()) - return lastErrorCode(); + QOLM_INTERNAL_ERROR(qPrintable("Failed to pickle Olm account " + + accountId())); + return pickleBuffer; } @@ -82,7 +84,8 @@ IdentityKeys QOlmAccount::identityKeys() const QByteArray keyBuffer(keyLength, '\0'); if (olm_account_identity_keys(m_account, keyBuffer.data(), keyLength) == olm_error()) { - throw lastError(); + QOLM_INTERNAL_ERROR( + qPrintable("Failed to get " % accountId() % " identity keys")); } const auto key = QJsonDocument::fromJson(keyBuffer).object(); return IdentityKeys { @@ -97,9 +100,9 @@ QByteArray QOlmAccount::sign(const QByteArray &message) const if (olm_account_sign(m_account, message.data(), message.length(), signatureBuffer.data(), signatureBuffer.length()) - == olm_error()) { - throw lastError(); - } + == olm_error()) + QOLM_INTERNAL_ERROR("Failed to sign a message"); + return signatureBuffer; } @@ -135,9 +138,10 @@ size_t QOlmAccount::generateOneTimeKeys(size_t numberOfKeys) const auto result = olm_account_generate_one_time_keys( m_account, numberOfKeys, RandomBuffer(randomLength), randomLength); - if (result == olm_error()) { - throw lastError(); - } + if (result == olm_error()) + QOLM_INTERNAL_ERROR(qPrintable( + "Failed to generate one-time keys for account " + accountId())); + emit needsSave(); return result; } @@ -149,9 +153,10 @@ UnsignedOneTimeKeys QOlmAccount::oneTimeKeys() const if (olm_account_one_time_keys(m_account, oneTimeKeysBuffer.data(), oneTimeKeyLength) - == olm_error()) { - throw lastError(); - } + == olm_error()) + QOLM_INTERNAL_ERROR(qPrintable( + "Failed to obtain one-time keys for account" % accountId())); + const auto json = QJsonDocument::fromJson(oneTimeKeysBuffer).object(); UnsignedOneTimeKeys oneTimeKeys; fromJson(json, oneTimeKeys.keys); @@ -266,3 +271,5 @@ bool Quotient::ed25519VerifySignature(const QString& signingKey, return utility.ed25519Verify(signingKeyBuf, canonicalJson, signatureBuf) .value_or(false); } + +QString QOlmAccount::accountId() const { return m_userId % '/' % m_deviceId; } diff --git a/lib/e2ee/qolmaccount.h b/lib/e2ee/qolmaccount.h index 0fb9803f..a5faa82a 100644 --- a/lib/e2ee/qolmaccount.h +++ b/lib/e2ee/qolmaccount.h @@ -41,7 +41,7 @@ public: const PicklingMode& mode); //! Serialises an OlmAccount to encrypted Base64. - QOlmExpected pickle(const PicklingMode &mode); + QByteArray pickle(const PicklingMode &mode); //! Returns the account's public identity keys already formatted as JSON IdentityKeys identityKeys() const; @@ -107,6 +107,8 @@ private: OlmAccount *m_account = nullptr; // owning QString m_userId; QString m_deviceId; + + QString accountId() const; }; QUOTIENT_API bool verifyIdentitySignature(const DeviceKeys& deviceKeys, diff --git a/lib/e2ee/qolminboundsession.cpp b/lib/e2ee/qolminboundsession.cpp index a05ddf62..1e3803f2 100644 --- a/lib/e2ee/qolminboundsession.cpp +++ b/lib/e2ee/qolminboundsession.cpp @@ -31,7 +31,8 @@ QOlmInboundGroupSession::~QOlmInboundGroupSession() //delete[](reinterpret_cast(m_groupSession)); } -std::unique_ptr QOlmInboundGroupSession::create(const QByteArray &key) +QOlmExpected QOlmInboundGroupSession::create( + const QByteArray& key) { const auto olmInboundGroupSession = olm_inbound_group_session(new uint8_t[olm_inbound_group_session_size()]); if (olm_init_inbound_group_session( @@ -39,13 +40,17 @@ std::unique_ptr QOlmInboundGroupSession::create(const Q reinterpret_cast(key.constData()), key.size()) == olm_error()) { // FIXME: create QOlmInboundGroupSession earlier and use lastErrorCode() - throw olm_inbound_group_session_last_error_code(olmInboundGroupSession); + qWarning(E2EE) << "Failed to create an inbound group session:" + << olm_inbound_group_session_last_error( + olmInboundGroupSession); + return olm_inbound_group_session_last_error_code(olmInboundGroupSession); } return std::make_unique(olmInboundGroupSession); } -std::unique_ptr QOlmInboundGroupSession::import(const QByteArray &key) +QOlmExpected QOlmInboundGroupSession::import( + const QByteArray& key) { const auto olmInboundGroupSession = olm_inbound_group_session(new uint8_t[olm_inbound_group_session_size()]); @@ -54,7 +59,10 @@ std::unique_ptr QOlmInboundGroupSession::import(const Q reinterpret_cast(key.data()), key.size()) == olm_error()) { // FIXME: create QOlmInboundGroupSession earlier and use lastError() - throw olm_inbound_group_session_last_error_code(olmInboundGroupSession); + qWarning(E2EE) << "Failed to import an inbound group session:" + << olm_inbound_group_session_last_error( + olmInboundGroupSession); + return olm_inbound_group_session_last_error_code(olmInboundGroupSession); } return std::make_unique(olmInboundGroupSession); @@ -69,7 +77,7 @@ QByteArray QOlmInboundGroupSession::pickle(const PicklingMode& mode) const key.length(), pickledBuf.data(), pickledBuf.length()) == olm_error()) { - throw lastError(); + QOLM_INTERNAL_ERROR("Failed to pickle the inbound group session"); } return pickledBuf; } @@ -84,6 +92,8 @@ QOlmExpected QOlmInboundGroupSession::unpickle( pickled.size()) == olm_error()) { // FIXME: create QOlmInboundGroupSession earlier and use lastError() + qWarning(E2EE) << "Failed to unpickle an inbound group session:" + << olm_inbound_group_session_last_error(groupSession); return olm_inbound_group_session_last_error_code(groupSession); } key.clear(); @@ -133,6 +143,8 @@ QOlmExpected QOlmInboundGroupSession::exportSession( m_groupSession, reinterpret_cast(keyBuf.data()), keyLength, messageIndex) == olm_error()) { + QOLM_FAIL_OR_LOG(OLM_OUTPUT_BUFFER_TOO_SMALL, + "Failed to export the inbound group session"); return lastErrorCode(); } return keyBuf; @@ -150,9 +162,9 @@ QByteArray QOlmInboundGroupSession::sessionId() const if (olm_inbound_group_session_id( m_groupSession, reinterpret_cast(sessionIdBuf.data()), sessionIdBuf.length()) - == olm_error()) { - throw lastError(); - } + == olm_error()) + QOLM_INTERNAL_ERROR("Failed to obtain the group session id"); + return sessionIdBuf; } diff --git a/lib/e2ee/qolminboundsession.h b/lib/e2ee/qolminboundsession.h index 0b89349a..15979a05 100644 --- a/lib/e2ee/qolminboundsession.h +++ b/lib/e2ee/qolminboundsession.h @@ -17,9 +17,9 @@ class QUOTIENT_API QOlmInboundGroupSession public: ~QOlmInboundGroupSession(); //! Creates a new instance of `OlmInboundGroupSession`. - static std::unique_ptr create(const QByteArray& key); + static QOlmExpected create(const QByteArray& key); //! Import an inbound group session, from a previous export. - static std::unique_ptr import(const QByteArray& key); + static QOlmExpected 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 diff --git a/lib/e2ee/qolmoutboundsession.cpp b/lib/e2ee/qolmoutboundsession.cpp index 3d176274..18af5569 100644 --- a/lib/e2ee/qolmoutboundsession.cpp +++ b/lib/e2ee/qolmoutboundsession.cpp @@ -39,8 +39,10 @@ QOlmOutboundGroupSessionPtr QOlmOutboundGroupSession::create() RandomBuffer(randomLength).bytes(), randomLength) == olm_error()) { - // FIXME: create the session object earlier and use lastError() - throw olm_outbound_group_session_last_error_code(olmOutboundGroupSession); + // FIXME: create the session object earlier + QOLM_INTERNAL_ERROR_X("Failed to initialise an outbound group session", + olm_outbound_group_session_last_error( + olmOutboundGroupSession)); } // FIXME: is it used anywhere? @@ -52,7 +54,7 @@ QOlmOutboundGroupSessionPtr QOlmOutboundGroupSession::create() return std::make_unique(olmOutboundGroupSession); } -QOlmExpected QOlmOutboundGroupSession::pickle(const PicklingMode &mode) const +QByteArray QOlmOutboundGroupSession::pickle(const PicklingMode &mode) const { QByteArray pickledBuf( olm_pickle_outbound_group_session_length(m_groupSession), '\0'); @@ -61,7 +63,7 @@ QOlmExpected QOlmOutboundGroupSession::pickle(const PicklingMode &mo key.length(), pickledBuf.data(), pickledBuf.length()) == olm_error()) - return lastErrorCode(); + QOLM_INTERNAL_ERROR("Failed to pickle the outbound group session"); key.clear(); return pickledBuf; @@ -77,6 +79,9 @@ QOlmExpected QOlmOutboundGroupSession::unpickle( pickled.length()) == olm_error()) { // FIXME: create the session object earlier and use lastError() + qWarning(E2EE) << "Failed to unpickle an outbound group session:" + << olm_outbound_group_session_last_error( + olmOutboundGroupSession); return olm_outbound_group_session_last_error_code( olmOutboundGroupSession); } @@ -90,8 +95,7 @@ QOlmExpected QOlmOutboundGroupSession::unpickle( return std::make_unique(olmOutboundGroupSession); } -QOlmExpected QOlmOutboundGroupSession::encrypt( - const QByteArray& plaintext) const +QByteArray QOlmOutboundGroupSession::encrypt(const QByteArray& plaintext) const { const auto messageMaxLength = olm_group_encrypt_message_length(m_groupSession, plaintext.length()); @@ -102,7 +106,7 @@ QOlmExpected QOlmOutboundGroupSession::encrypt( reinterpret_cast(messageBuf.data()), messageBuf.length()) == olm_error()) - return lastErrorCode(); + QOLM_INTERNAL_ERROR("Failed to encrypt a message"); return messageBuf; } @@ -120,12 +124,12 @@ QByteArray QOlmOutboundGroupSession::sessionId() const m_groupSession, reinterpret_cast(idBuffer.data()), idBuffer.length()) == olm_error()) - throw lastError(); + QOLM_INTERNAL_ERROR("Failed to obtain group session id"); return idBuffer; } -QOlmExpected QOlmOutboundGroupSession::sessionKey() const +QByteArray QOlmOutboundGroupSession::sessionKey() const { const auto keyMaxLength = olm_outbound_group_session_key_length(m_groupSession); QByteArray keyBuffer(keyMaxLength, '\0'); @@ -133,7 +137,7 @@ QOlmExpected QOlmOutboundGroupSession::sessionKey() const m_groupSession, reinterpret_cast(keyBuffer.data()), keyMaxLength) == olm_error()) - return lastErrorCode(); + QOLM_INTERNAL_ERROR("Failed to obtain group session key"); return keyBuffer; } diff --git a/lib/e2ee/qolmoutboundsession.h b/lib/e2ee/qolmoutboundsession.h index e5e73ddf..d36fbf69 100644 --- a/lib/e2ee/qolmoutboundsession.h +++ b/lib/e2ee/qolmoutboundsession.h @@ -20,14 +20,14 @@ public: //! Throw OlmError on errors static QOlmOutboundGroupSessionPtr create(); //! Serialises a `QOlmOutboundGroupSession` to encrypted Base64. - QOlmExpected pickle(const PicklingMode &mode) const; + QByteArray pickle(const PicklingMode &mode) const; //! Deserialises from encrypted Base64 that was previously obtained by //! pickling a `QOlmOutboundGroupSession`. static QOlmExpected unpickle( QByteArray&& pickled, const PicklingMode& mode); //! Encrypts a plaintext message using the session. - QOlmExpected encrypt(const QByteArray& plaintext) const; + QByteArray encrypt(const QByteArray& plaintext) const; //! Get the current message index for this session. //! @@ -42,7 +42,7 @@ public: //! //! Each message is sent with a different ratchet key. This function returns the //! ratchet key that will be used for the next message. - QOlmExpected sessionKey() const; + QByteArray sessionKey() const; QOlmOutboundGroupSession(OlmOutboundGroupSession *groupSession); int messageCount() const; diff --git a/lib/e2ee/qolmsession.cpp b/lib/e2ee/qolmsession.cpp index 7c102a96..0d6edd21 100644 --- a/lib/e2ee/qolmsession.cpp +++ b/lib/e2ee/qolmsession.cpp @@ -90,23 +90,23 @@ QOlmExpected QOlmSession::createOutboundSession( == olm_error()) { // FIXME: the QOlmSession object should be created earlier const auto lastErr = olm_session_last_error_code(olmOutboundSession); - if (lastErr == OLM_NOT_ENOUGH_RANDOM) { - throw lastErr; - } + QOLM_FAIL_OR_LOG_X(lastErr == OLM_NOT_ENOUGH_RANDOM, + "Failed to create an outbound Olm session", + olm_session_last_error(olmOutboundSession)); return lastErr; } return std::make_unique(olmOutboundSession); } -QOlmExpected QOlmSession::pickle(const PicklingMode &mode) const +QByteArray QOlmSession::pickle(const PicklingMode &mode) const { QByteArray pickledBuf(olm_pickle_session_length(m_session), '\0'); QByteArray key = toKey(mode); if (olm_pickle_session(m_session, key.data(), key.length(), pickledBuf.data(), pickledBuf.length()) == olm_error()) - return lastErrorCode(); + QOLM_INTERNAL_ERROR("Failed to pickle an Olm session"); key.clear(); return pickledBuf; @@ -121,7 +121,11 @@ QOlmExpected QOlmSession::unpickle(QByteArray&& pickled, pickled.data(), pickled.length()) == olm_error()) { // FIXME: the QOlmSession object should be created earlier - return olm_session_last_error_code(olmSession); + const auto errorCode = olm_session_last_error_code(olmSession); + QOLM_FAIL_OR_LOG_X(errorCode == OLM_OUTPUT_BUFFER_TOO_SMALL, + "Failed to unpickle an Olm session", + olm_session_last_error(olmSession)); + return errorCode; } key.clear(); @@ -140,7 +144,7 @@ QOlmMessage QOlmSession::encrypt(const QByteArray& plaintext) RandomBuffer(randomLength), randomLength, messageBuf.data(), messageBuf.length()) == olm_error()) { - throw lastError(); + QOLM_INTERNAL_ERROR("Failed to encrypt the message"); } return QOlmMessage(messageBuf, QOlmMessage::Type(messageType)); @@ -159,7 +163,9 @@ QOlmExpected QOlmSession::decrypt(const QOlmMessage &message) const const auto plaintextMaxLen = olm_decrypt_max_plaintext_length( m_session, messageTypeValue, messageBuf.data(), messageBuf.length()); if (plaintextMaxLen == olm_error()) { - return lastError(); + qWarning(E2EE) << "Couldn't calculate decrypted message length:" + << lastError(); + return lastErrorCode(); } QByteArray plaintextBuf(plaintextMaxLen, '\0'); @@ -170,11 +176,9 @@ QOlmExpected QOlmSession::decrypt(const QOlmMessage &message) const olm_decrypt(m_session, messageTypeValue, messageBuf2.data(), messageBuf2.length(), plaintextBuf.data(), plaintextMaxLen); if (plaintextResultLen == olm_error()) { - const auto lastErr = lastErrorCode(); - if (lastErr == OLM_OUTPUT_BUFFER_TOO_SMALL) { - throw lastErr; - } - return lastErr; + QOLM_FAIL_OR_LOG(OLM_OUTPUT_BUFFER_TOO_SMALL, + "Failed to decrypt the message"); + return lastErrorCode(); } plaintextBuf.truncate(plaintextResultLen); return plaintextBuf; @@ -183,10 +187,10 @@ QOlmExpected QOlmSession::decrypt(const QOlmMessage &message) const QByteArray QOlmSession::sessionId() const { const auto idMaxLength = olm_session_id_length(m_session); - QByteArray idBuffer(idMaxLength, '0'); - if (olm_session_id(m_session, idBuffer.data(), idMaxLength) == olm_error()) { - throw lastError(); - } + QByteArray idBuffer(idMaxLength, '\0'); + if (olm_session_id(m_session, idBuffer.data(), idMaxLength) == olm_error()) + QOLM_INTERNAL_ERROR("Failed to obtain Olm session id"); + return idBuffer; } @@ -203,8 +207,8 @@ bool QOlmSession::matchesInboundSession(const QOlmMessage& preKeyMessage) const olm_matches_inbound_session(m_session, oneTimeKeyBuf.data(), oneTimeKeyBuf.length()); if (maybeMatches == olm_error()) - qWarning(E2EE) << "Error matching an inbound session:" - << olm_session_last_error(m_session); + qWarning(E2EE) << "Error matching an inbound session:" << lastError(); + return maybeMatches == 1; // Any errors are treated as non-match } @@ -218,8 +222,8 @@ bool QOlmSession::matchesInboundSessionFrom( oneTimeKeyMessageBuf.data(), oneTimeKeyMessageBuf.length()); if (maybeMatches == olm_error()) - qCWarning(E2EE) << "Error matching an inbound session:" - << olm_session_last_error(m_session); + qCWarning(E2EE) << "Error matching an inbound session:" << lastError(); + return maybeMatches == 1; } diff --git a/lib/e2ee/qolmsession.h b/lib/e2ee/qolmsession.h index 174e98ef..400fb854 100644 --- a/lib/e2ee/qolmsession.h +++ b/lib/e2ee/qolmsession.h @@ -30,7 +30,7 @@ public: const QByteArray& theirOneTimeKey); //! Serialises an `QOlmSession` to encrypted Base64. - QOlmExpected pickle(const PicklingMode &mode) const; + QByteArray pickle(const PicklingMode &mode) const; //! Deserialises from encrypted Base64 previously made with pickle() static QOlmExpected unpickle(QByteArray&& pickled, diff --git a/lib/e2ee/qolmutils.h b/lib/e2ee/qolmutils.h index da9d2d18..17eee7a3 100644 --- a/lib/e2ee/qolmutils.h +++ b/lib/e2ee/qolmutils.h @@ -34,4 +34,22 @@ public: return RandomBuffer(bufferSize); } +#define QOLM_INTERNAL_ERROR_X(Message_, LastError_) \ + qFatal("%s, internal error: %s", Message_, LastError_) + +#define QOLM_INTERNAL_ERROR(Message_) \ + QOLM_INTERNAL_ERROR_X(Message_, lastError()) + +#define QOLM_FAIL_OR_LOG_X(InternalCondition_, Message_, LastErrorText_) \ + do { \ + const QString errorMsg{ (Message_) }; \ + if (InternalCondition_) \ + QOLM_INTERNAL_ERROR_X(qPrintable(errorMsg), (LastErrorText_)); \ + qWarning(E2EE).nospace() << errorMsg << ": " << (LastErrorText_); \ + } while (false) /* End of macro */ + +#define QOLM_FAIL_OR_LOG(InternalFailureValue_, Message_) \ + QOLM_FAIL_OR_LOG_X(lastErrorCode() == (InternalFailureValue_), (Message_), \ + lastError()) + } // namespace Quotient diff --git a/lib/room.cpp b/lib/room.cpp index 382d0243..5fdff036 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -357,7 +357,9 @@ public: return false; } - auto megolmSession = QOlmInboundGroupSession::create(sessionKey); + auto expectedMegolmSession = QOlmInboundGroupSession::create(sessionKey); + Q_ASSERT(expectedMegolmSession.has_value()); + auto&& megolmSession = *expectedMegolmSession; if (megolmSession->sessionId() != sessionId) { qCWarning(E2EE) << "Session ID mismatch in m.room_key event"; return false; @@ -442,12 +444,10 @@ public: connection->saveCurrentOutboundMegolmSession( id, *currentOutboundMegolmSession); - const auto sessionKey = currentOutboundMegolmSession->sessionKey(); - if(!sessionKey) { - qCWarning(E2EE) << "Failed to load key for new megolm session"; - return; - } - addInboundGroupSession(currentOutboundMegolmSession->sessionId(), *sessionKey, q->localUser()->id(), "SELF"_ls); + currentOutboundMegolmSession->sessionKey(); + addInboundGroupSession(currentOutboundMegolmSession->sessionId(), + currentOutboundMegolmSession->sessionKey(), + q->localUser()->id(), "SELF"_ls); } QMultiHash getDevicesWithoutKey() const @@ -460,22 +460,6 @@ public: return connection->database()->devicesWithoutKey( id, devices, currentOutboundMegolmSession->sessionId()); } - - void sendMegolmSession(const QMultiHash& devices) const { - // Save the session to this device - const auto sessionId = currentOutboundMegolmSession->sessionId(); - const auto sessionKey = currentOutboundMegolmSession->sessionKey(); - if(!sessionKey) { - qCWarning(E2EE) << "Error loading session key"; - return; - } - - // Send the session to other people - connection->sendSessionKeyToDevices( - id, sessionId, *sessionKey, devices, - currentOutboundMegolmSession->sessionMessageIndex()); - } - #endif // Quotient_E2EE_ENABLED private: @@ -2029,17 +2013,20 @@ QString Room::Private::doSendEvent(const RoomEvent* pEvent) if (!hasValidMegolmSession() || shouldRotateMegolmSession()) { createMegolmSession(); } - sendMegolmSession(getDevicesWithoutKey()); + // Send the session to other people + connection->sendSessionKeyToDevices( + id, currentOutboundMegolmSession->sessionId(), + currentOutboundMegolmSession->sessionKey(), getDevicesWithoutKey(), + currentOutboundMegolmSession->sessionMessageIndex()); const auto encrypted = currentOutboundMegolmSession->encrypt(QJsonDocument(pEvent->fullJson()).toJson()); currentOutboundMegolmSession->setMessageCount(currentOutboundMegolmSession->messageCount() + 1); connection->saveCurrentOutboundMegolmSession( id, *currentOutboundMegolmSession); - if(!encrypted) { - qWarning(E2EE) << "Error encrypting message" << encrypted.error(); - return {}; - } - encryptedEvent = makeEvent(*encrypted, q->connection()->olmAccount()->identityKeys().curve25519, q->connection()->deviceId(), currentOutboundMegolmSession->sessionId()); + encryptedEvent = makeEvent( + encrypted, q->connection()->olmAccount()->identityKeys().curve25519, + q->connection()->deviceId(), + currentOutboundMegolmSession->sessionId()); encryptedEvent->setTransactionId(connection->generateTxnId()); encryptedEvent->setRoomId(id); encryptedEvent->setSender(connection->userId()); -- cgit v1.2.3 From d5c1afc536dde87f460ccadfcfd51444b5e9bb82 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Thu, 22 Sep 2022 14:01:01 +0200 Subject: Trample Sonar warnings --- lib/connection.cpp | 4 +++- lib/e2ee/qolmaccount.cpp | 7 ++++--- lib/e2ee/qolminboundsession.cpp | 2 +- lib/e2ee/qolminboundsession.h | 2 +- lib/e2ee/qolmoutboundsession.cpp | 6 +++--- lib/e2ee/qolmsession.cpp | 13 ++++++------- 6 files changed, 18 insertions(+), 16 deletions(-) diff --git a/lib/connection.cpp b/lib/connection.cpp index 0afbffbc..51c2062f 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -648,7 +648,9 @@ void Connection::Private::completeSetup(const QString& mxId) }); } else { // account already existing - olmAccount->unpickle(database->accountPickle(), picklingMode); + if (!olmAccount->unpickle(database->accountPickle(), picklingMode)) + qWarning(E2EE) + << "Could not unpickle Olm account, E2EE won't be available"; } #endif // Quotient_E2EE_ENABLED emit q->stateChanged(); diff --git a/lib/e2ee/qolmaccount.cpp b/lib/e2ee/qolmaccount.cpp index d03bcb3b..7392a9f5 100644 --- a/lib/e2ee/qolmaccount.cpp +++ b/lib/e2ee/qolmaccount.cpp @@ -43,15 +43,16 @@ QOlmAccount::~QOlmAccount() void QOlmAccount::createNewAccount() { m_account = olm_account(new uint8_t[olm_account_size()]); - const auto randomLength = olm_create_account_random_length(m_account); - if (olm_create_account(m_account, RandomBuffer(randomLength), randomLength) + if (const auto randomLength = olm_create_account_random_length(m_account); + olm_create_account(m_account, RandomBuffer(randomLength), randomLength) == olm_error()) QOLM_INTERNAL_ERROR("Failed to create a new account"); emit needsSave(); } -OlmErrorCode QOlmAccount::unpickle(QByteArray&& pickled, const PicklingMode &mode) +OlmErrorCode QOlmAccount::unpickle(QByteArray&& pickled, + const PicklingMode& mode) { m_account = olm_account(new uint8_t[olm_account_size()]); if (const auto key = toKey(mode); diff --git a/lib/e2ee/qolminboundsession.cpp b/lib/e2ee/qolminboundsession.cpp index 1e3803f2..18275dc0 100644 --- a/lib/e2ee/qolminboundsession.cpp +++ b/lib/e2ee/qolminboundsession.cpp @@ -49,7 +49,7 @@ QOlmExpected QOlmInboundGroupSession::create( return std::make_unique(olmInboundGroupSession); } -QOlmExpected QOlmInboundGroupSession::import( +QOlmExpected QOlmInboundGroupSession::importSession( const QByteArray& key) { const auto olmInboundGroupSession = olm_inbound_group_session(new uint8_t[olm_inbound_group_session_size()]); diff --git a/lib/e2ee/qolminboundsession.h b/lib/e2ee/qolminboundsession.h index 15979a05..b9710354 100644 --- a/lib/e2ee/qolminboundsession.h +++ b/lib/e2ee/qolminboundsession.h @@ -19,7 +19,7 @@ public: //! Creates a new instance of `OlmInboundGroupSession`. static QOlmExpected create(const QByteArray& key); //! Import an inbound group session, from a previous export. - static QOlmExpected import(const QByteArray& key); + static QOlmExpected importSession(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 diff --git a/lib/e2ee/qolmoutboundsession.cpp b/lib/e2ee/qolmoutboundsession.cpp index 18af5569..56f78906 100644 --- a/lib/e2ee/qolmoutboundsession.cpp +++ b/lib/e2ee/qolmoutboundsession.cpp @@ -33,9 +33,9 @@ QOlmOutboundGroupSession::~QOlmOutboundGroupSession() 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); - - if (olm_init_outbound_group_session(olmOutboundGroupSession, + if (const auto randomLength = olm_init_outbound_group_session_random_length( + olmOutboundGroupSession); + olm_init_outbound_group_session(olmOutboundGroupSession, RandomBuffer(randomLength).bytes(), randomLength) == olm_error()) { diff --git a/lib/e2ee/qolmsession.cpp b/lib/e2ee/qolmsession.cpp index 0d6edd21..e3f69132 100644 --- a/lib/e2ee/qolmsession.cpp +++ b/lib/e2ee/qolmsession.cpp @@ -80,10 +80,9 @@ QOlmExpected QOlmSession::createOutboundSession( const QByteArray& theirOneTimeKey) { auto* olmOutboundSession = create(); - const auto randomLength = - olm_create_outbound_session_random_length(olmOutboundSession); - - if (olm_create_outbound_session( + if (const auto randomLength = + olm_create_outbound_session_random_length(olmOutboundSession); + olm_create_outbound_session( olmOutboundSession, account->data(), theirIdentityKey.data(), theirIdentityKey.length(), theirOneTimeKey.data(), theirOneTimeKey.length(), RandomBuffer(randomLength), randomLength) @@ -136,11 +135,11 @@ QOlmMessage QOlmSession::encrypt(const QByteArray& plaintext) { const auto messageMaxLength = olm_encrypt_message_length(m_session, plaintext.length()); - QByteArray messageBuf(messageMaxLength, '0'); + QByteArray messageBuf(messageMaxLength, '\0'); // NB: The type has to be calculated before calling olm_encrypt() const auto messageType = olm_encrypt_message_type(m_session); - const auto randomLength = olm_encrypt_random_length(m_session); - if (olm_encrypt(m_session, plaintext.data(), plaintext.length(), + if (const auto randomLength = olm_encrypt_random_length(m_session); + olm_encrypt(m_session, plaintext.data(), plaintext.length(), RandomBuffer(randomLength), randomLength, messageBuf.data(), messageBuf.length()) == olm_error()) { -- cgit v1.2.3 From 388c301222586583a0144f7229a564cf07b597d4 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Sat, 24 Sep 2022 20:51:00 +0200 Subject: Remove no-op code --- lib/e2ee/qolmoutboundsession.cpp | 11 ----------- lib/room.cpp | 1 - 2 files changed, 12 deletions(-) diff --git a/lib/e2ee/qolmoutboundsession.cpp b/lib/e2ee/qolmoutboundsession.cpp index 56f78906..1176d790 100644 --- a/lib/e2ee/qolmoutboundsession.cpp +++ b/lib/e2ee/qolmoutboundsession.cpp @@ -45,12 +45,6 @@ QOlmOutboundGroupSessionPtr QOlmOutboundGroupSession::create() olmOutboundGroupSession)); } - // FIXME: is it used anywhere? - 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); - return std::make_unique(olmOutboundGroupSession); } @@ -85,11 +79,6 @@ QOlmExpected QOlmOutboundGroupSession::unpickle( return olm_outbound_group_session_last_error_code( 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()); - // FIXME: idBuffer doesn't seem to be used, is it needed here? key.clear(); return std::make_unique(olmOutboundGroupSession); diff --git a/lib/room.cpp b/lib/room.cpp index 5fdff036..0cf818ce 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -444,7 +444,6 @@ public: connection->saveCurrentOutboundMegolmSession( id, *currentOutboundMegolmSession); - currentOutboundMegolmSession->sessionKey(); addInboundGroupSession(currentOutboundMegolmSession->sessionId(), currentOutboundMegolmSession->sessionKey(), q->localUser()->id(), "SELF"_ls); -- cgit v1.2.3 From 72e14cb1bdff68dfe0fb61fff0defd6c50dff43c Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Sun, 25 Sep 2022 21:37:49 +0200 Subject: QOlmUtility::ed25519Verify: just return bool It's too easy to incorrectly test the previous return type. (cherry picked from commit 5904a61c59f0eef00aef07ef998658fd791ff139) --- autotests/testolmaccount.cpp | 3 +-- autotests/testolmutility.cpp | 6 ++---- lib/e2ee/qolmaccount.cpp | 3 +-- lib/e2ee/qolmutility.cpp | 21 +++++---------------- lib/e2ee/qolmutility.h | 4 ++-- 5 files changed, 11 insertions(+), 26 deletions(-) diff --git a/autotests/testolmaccount.cpp b/autotests/testolmaccount.cpp index 56532698..53a0c955 100644 --- a/autotests/testolmaccount.cpp +++ b/autotests/testolmaccount.cpp @@ -60,8 +60,7 @@ void TestOlmAccount::signatureValid() QOlmUtility utility; const auto identityKeys = olmAccount.identityKeys(); const auto ed25519Key = identityKeys.ed25519; - const auto verify = utility.ed25519Verify(ed25519Key, message, signature); - QVERIFY(verify.value_or(false)); + QVERIFY(utility.ed25519Verify(ed25519Key, message, signature)); } void TestOlmAccount::oneTimeKeysValid() diff --git a/autotests/testolmutility.cpp b/autotests/testolmutility.cpp index 64ceb3e7..4de5afdf 100644 --- a/autotests/testolmutility.cpp +++ b/autotests/testolmutility.cpp @@ -78,10 +78,8 @@ void TestOlmUtility::verifySignedOneTimeKey() delete[](reinterpret_cast(utility)); QOlmUtility utility2; - auto res2 = - utility2 - .ed25519Verify(aliceOlm.identityKeys().ed25519, msg, signatureBuf1) - .value_or(false); + auto res2 = utility2.ed25519Verify(aliceOlm.identityKeys().ed25519, msg, + signatureBuf1); //QCOMPARE(std::string(olm_utility_last_error(utility)), "SUCCESS"); QVERIFY(res2); diff --git a/lib/e2ee/qolmaccount.cpp b/lib/e2ee/qolmaccount.cpp index 7392a9f5..345ab16b 100644 --- a/lib/e2ee/qolmaccount.cpp +++ b/lib/e2ee/qolmaccount.cpp @@ -269,8 +269,7 @@ bool Quotient::ed25519VerifySignature(const QString& signingKey, QByteArray signingKeyBuf = signingKey.toUtf8(); QOlmUtility utility; auto signatureBuf = signature.toUtf8(); - return utility.ed25519Verify(signingKeyBuf, canonicalJson, signatureBuf) - .value_or(false); + return utility.ed25519Verify(signingKeyBuf, canonicalJson, signatureBuf); } QString QOlmAccount::accountId() const { return m_userId % '/' % m_deviceId; } diff --git a/lib/e2ee/qolmutility.cpp b/lib/e2ee/qolmutility.cpp index 08c2b699..46f7f4f3 100644 --- a/lib/e2ee/qolmutility.cpp +++ b/lib/e2ee/qolmutility.cpp @@ -44,21 +44,10 @@ QString QOlmUtility::sha256Utf8Msg(const QString &message) const return sha256Bytes(message.toUtf8()); } -QOlmExpected QOlmUtility::ed25519Verify(const QByteArray& key, - const QByteArray& message, - const QByteArray& signature) +bool QOlmUtility::ed25519Verify(const QByteArray& key, const QByteArray& message, + QByteArray signature) { - QByteArray signatureBuf(signature.length(), '\0'); - std::copy(signature.begin(), signature.end(), signatureBuf.begin()); - - if (olm_ed25519_verify(m_utility, key.data(), key.size(), message.data(), - message.size(), signatureBuf.data(), - signatureBuf.size()) - == 0) - return true; - - auto error = lastErrorCode(); - if (error == OLM_BAD_MESSAGE_MAC) - return false; - return error; + return olm_ed25519_verify(m_utility, key.data(), key.size(), message.data(), + message.size(), signature.data(), signature.size()) + == 0; } diff --git a/lib/e2ee/qolmutility.h b/lib/e2ee/qolmutility.h index 89277385..508767bf 100644 --- a/lib/e2ee/qolmutility.h +++ b/lib/e2ee/qolmutility.h @@ -29,8 +29,8 @@ 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. - QOlmExpected ed25519Verify(const QByteArray &key, - const QByteArray &message, const QByteArray &signature); + bool ed25519Verify(const QByteArray &key, + const QByteArray &message, QByteArray signature); OlmErrorCode lastErrorCode() const; const char* lastError() const; -- cgit v1.2.3