aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey Rusakov <Kitsune-Ral@users.sf.net>2022-09-26 15:20:53 +0200
committerGitHub <noreply@github.com>2022-09-26 15:20:53 +0200
commit4c8dcbc308eb0f4900e416e698f5f30e71daaad8 (patch)
treeaa5fdaa81234a21c6919fac4958f84d7c26cd397
parent5904a61c59f0eef00aef07ef998658fd791ff139 (diff)
parent15b840d82d4794358fbf1271ea76e446b47db7e5 (diff)
downloadlibquotient-4c8dcbc308eb0f4900e416e698f5f30e71daaad8.tar.gz
libquotient-4c8dcbc308eb0f4900e416e698f5f30e71daaad8.zip
Merge #571: Unify reporting and handling of Olm errors
-rw-r--r--.clang-tidy2
-rw-r--r--.github/workflows/ci.yml10
-rw-r--r--CMakeLists.txt4
-rw-r--r--README.md2
-rw-r--r--autotests/testgroupsession.cpp17
-rw-r--r--autotests/testolmaccount.cpp6
-rw-r--r--autotests/testolmsession.cpp14
-rw-r--r--autotests/testolmutility.cpp15
-rw-r--r--lib/connection.cpp38
-rw-r--r--lib/database.cpp32
-rw-r--r--lib/e2ee/e2ee.h38
-rw-r--r--lib/e2ee/qolmaccount.cpp151
-rw-r--r--lib/e2ee/qolmaccount.h18
-rw-r--r--lib/e2ee/qolmerrors.cpp25
-rw-r--r--lib/e2ee/qolmerrors.h28
-rw-r--r--lib/e2ee/qolminboundsession.cpp147
-rw-r--r--lib/e2ee/qolminboundsession.h13
-rw-r--r--lib/e2ee/qolmmessage.cpp6
-rw-r--r--lib/e2ee/qolmmessage.h12
-rw-r--r--lib/e2ee/qolmoutboundsession.cpp135
-rw-r--r--lib/e2ee/qolmoutboundsession.h15
-rw-r--r--lib/e2ee/qolmsession.cpp185
-rw-r--r--lib/e2ee/qolmsession.h21
-rw-r--r--lib/e2ee/qolmutility.cpp12
-rw-r--r--lib/e2ee/qolmutility.h4
-rw-r--r--lib/e2ee/qolmutils.cpp7
-rw-r--r--lib/e2ee/qolmutils.h42
-rw-r--r--lib/events/encryptedevent.cpp1
-rw-r--r--lib/events/encryptedevent.h7
-rw-r--r--lib/events/filesourceinfo.cpp39
-rw-r--r--lib/keyverificationsession.cpp5
-rw-r--r--lib/room.cpp45
-rw-r--r--lib/util.h17
33 files changed, 549 insertions, 564 deletions
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
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f84356b0..40ed85d1 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
@@ -106,6 +106,7 @@ jobs:
sed -i 's/ThreadEngineStarter<void>(ThreadEngine<void> \*_threadEngine)/ThreadEngineStarter(ThreadEngine<void> \*_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
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 91ee8217..b021411c 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"
@@ -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/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.
diff --git a/autotests/testgroupsession.cpp b/autotests/testgroupsession.cpp
index 3c329a8a..1054a160 100644
--- a/autotests/testgroupsession.cpp
+++ b/autotests/testgroupsession.cpp
@@ -16,11 +16,13 @@ void TestGroupSession::groupSessionPicklingValid()
QVERIFY(QByteArray::fromBase64(ogsId).size() > 0);
QCOMPARE(0, ogs->sessionMessageIndex());
- auto ogsPickled = ogs->pickle(Unencrypted {}).value();
- auto ogs2 = QOlmOutboundGroupSession::unpickle(ogsPickled, 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);
@@ -29,18 +31,19 @@ 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());
}
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 = QStringLiteral("Hello world!");
- const auto ciphertext = ogs->encrypt(plainText).value();
+ const auto plainText = "Hello world!";
+ 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 a41af268..53a0c955 100644
--- a/autotests/testolmaccount.cpp
+++ b/autotests/testolmaccount.cpp
@@ -23,9 +23,11 @@ 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"));
- olmAccount2.unpickle(pickled, Unencrypted{});
+ auto unpickleResult = olmAccount2.unpickle(std::move(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..18b0d5f2 100644
--- a/autotests/testolmsession.cpp
+++ b/autotests/testolmsession.cpp
@@ -11,10 +11,16 @@ std::pair<QOlmSessionPtr, QOlmSessionPtr> 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{});
- auto accountB = QOlmAccount("accountB:foo.com", "Device1UserB");
- accountB.unpickle(pickledAccountB, Unencrypted{});
+ 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(u"accountB:foo.com", u"Device1UserB");
+ if (accountB.unpickle(std::move(pickledAccountB), Unencrypted{})
+ != OLM_SUCCESS)
+ qFatal("Failed to unpickle account B: %s", accountB.lastError());
+
const QByteArray identityKeyA("qIEr3TWcJQt4CP8QoKKJcCaukByIOpgh6erBkhLEa2o");
const QByteArray oneTimeKeyA("WzsbsjD85iB1R32iWxfJdwkgmdz29ClMbJSJziECYwk");
diff --git a/autotests/testolmutility.cpp b/autotests/testolmutility.cpp
index 1d461a94..4de5afdf 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 1048884f..4547474a 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 <typename FnT>
@@ -270,8 +266,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
@@ -615,7 +610,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"));
@@ -653,8 +648,9 @@ void Connection::Private::completeSetup(const QString& mxId)
});
} else {
// account already existing
- auto pickle = database->accountPickle();
- olmAccount->unpickle(pickle, 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();
@@ -2224,11 +2220,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
}
@@ -2304,14 +2296,10 @@ std::pair<QOlmMessage::Type, QByteArray> 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() };
+ database->updateOlmSession(curveKey, olmSession->sessionId(),
+ olmSession->pickle(picklingMode));
+ return { result.type(), result.toCiphertext() };
}
bool Connection::Private::createOlmSession(const QString& targetUserId,
@@ -2347,7 +2335,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/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/e2ee.h b/lib/e2ee/e2ee.h
index 0772b70a..5999c0be 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 <QtCore/QMetaType>
#include <QtCore/QStringBuilder>
#include <array>
-#include <variant>
-namespace Quotient {
+#ifdef Quotient_E2EE_ENABLED
+# include "expected.h"
+
+# include <olm/error.h>
+# include <variant>
+#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<QOlmOutboundGroupSession>;
template <typename T>
-using QOlmExpected = Expected<T, QOlmError>;
+using QOlmExpected = Expected<T, OlmErrorCode>;
+#endif
struct IdentityKeys
{
@@ -97,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
//!
@@ -133,23 +134,6 @@ private:
using OneTimeKeys = QHash<QString, std::variant<QString, SignedOneTimeKey>>;
-template <typename T>
-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 <typename T>
-asKeyValueRange(T&) -> asKeyValueRange<T>;
-
} // namespace Quotient
Q_DECLARE_METATYPE(Quotient::SignedOneTimeKey)
diff --git a/lib/e2ee/qolmaccount.cpp b/lib/e2ee/qolmaccount.cpp
index 1b04dae7..345ab16b 100644
--- a/lib/e2ee/qolmaccount.cpp
+++ b/lib/e2ee/qolmaccount.cpp
@@ -18,15 +18,20 @@
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);
}
-QOlmAccount::QOlmAccount(const QString& userId, const QString& deviceId,
+const char* QOlmAccount::lastError() const
+{
+ return olm_account_last_error(m_account);
+}
+
+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()
@@ -38,50 +43,52 @@ 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);
- const auto error = olm_create_account(m_account, randomData.data(), randomSize);
- if (error == olm_error()) {
- throw lastError(m_account);
- }
+ 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();
}
-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 (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
- //throw lastError(m_account);
+ return lastErrorCode();
}
+ return OLM_SUCCESS;
}
-QOlmExpected<QByteArray> 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);
- 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);
- }
+ QByteArray pickleBuffer(pickleLength, '\0');
+ if (olm_pickle_account(m_account, key.data(), key.length(),
+ pickleBuffer.data(), pickleLength)
+ == olm_error())
+ QOLM_INTERNAL_ERROR(qPrintable("Failed to pickle Olm account "
+ + accountId()));
+
return pickleBuffer;
}
IdentityKeys QOlmAccount::identityKeys() const
{
const size_t keyLength = olm_account_identity_keys_length(m_account);
- QByteArray keyBuffer(keyLength, '0');
- const auto error = olm_account_identity_keys(m_account, keyBuffer.data(), keyLength);
- if (error == olm_error()) {
- throw lastError(m_account);
+ QByteArray keyBuffer(keyLength, '\0');
+ if (olm_account_identity_keys(m_account, keyBuffer.data(), keyLength)
+ == olm_error()) {
+ QOLM_INTERNAL_ERROR(
+ qPrintable("Failed to get " % accountId() % " identity keys"));
}
- 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()
@@ -90,14 +97,13 @@ 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');
- const auto error = olm_account_sign(m_account, message.data(), message.length(),
- signatureBuffer.data(), signatureBuffer.length());
+ if (olm_account_sign(m_account, message.data(), message.length(),
+ signatureBuffer.data(), signatureBuffer.length())
+ == olm_error())
+ QOLM_INTERNAL_ERROR("Failed to sign a message");
- if (error == olm_error()) {
- throw lastError(m_account);
- }
return signatureBuffer;
}
@@ -109,15 +115,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
@@ -127,32 +133,31 @@ 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);
- const auto error =
- 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())
+ QOLM_INTERNAL_ERROR(qPrintable(
+ "Failed to generate one-time keys for account " + accountId()));
- if (error == olm_error()) {
- throw lastError(m_account);
- }
emit needsSave();
- return error;
+ return result;
}
UnsignedOneTimeKeys QOlmAccount::oneTimeKeys() const
{
- const size_t oneTimeKeyLength = olm_account_one_time_keys_length(m_account);
- QByteArray oneTimeKeysBuffer(static_cast<int>(oneTimeKeyLength), '0');
-
- const auto error = olm_account_one_time_keys(m_account,
- oneTimeKeysBuffer.data(),
- oneTimeKeyLength);
- if (error == olm_error()) {
- throw lastError(m_account);
- }
+ const auto oneTimeKeyLength = olm_account_one_time_keys_length(m_account);
+ QByteArray oneTimeKeysBuffer(static_cast<int>(oneTimeKeyLength), '\0');
+
+ if (olm_account_one_time_keys(m_account, oneTimeKeysBuffer.data(),
+ oneTimeKeyLength)
+ == 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);
@@ -171,16 +176,16 @@ OneTimeKeys QOlmAccount::signOneTimeKeys(const UnsignedOneTimeKeys &keys) const
return signedOneTimeKeys;
}
-std::optional<QOlmError> 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; }
@@ -191,13 +196,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() } } } }
};
}
@@ -266,3 +271,5 @@ bool Quotient::ed25519VerifySignature(const QString& signingKey,
auto signatureBuf = signature.toUtf8();
return utility.ed25519Verify(signingKeyBuf, canonicalJson, signatureBuf);
}
+
+QString QOlmAccount::accountId() const { return m_userId % '/' % m_deviceId; }
diff --git a/lib/e2ee/qolmaccount.h b/lib/e2ee/qolmaccount.h
index f2a31314..a5faa82a 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,10 +37,11 @@ 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<QByteArray> pickle(const PicklingMode &mode);
+ QByteArray pickle(const PicklingMode &mode);
//! Returns the account's public identity keys already formatted as JSON
IdentityKeys identityKeys() const;
@@ -69,12 +71,11 @@ public:
DeviceKeys deviceKeys() const;
//! Remove the one time key used to create the supplied session.
- [[nodiscard]] std::optional<QOlmError> removeOneTimeKeys(
- const QOlmSession& session);
+ [[nodiscard]] OlmErrorCode removeOneTimeKeys(const QOlmSession& session);
//! Creates an inbound session for sending/receiving messages from a received 'prekey' message.
//!
- //! \param message An Olm pre-key message that was encrypted for this account.
+ //! \param preKeyMessage An Olm pre-key message that was encrypted for this account.
QOlmExpected<QOlmSessionPtr> createInboundSession(
const QOlmMessage& preKeyMessage);
@@ -92,6 +93,9 @@ public:
void markKeysAsPublished();
+ OlmErrorCode lastErrorCode() const;
+ const char* lastError() const;
+
// HACK do not use directly
QOlmAccount(OlmAccount *account);
OlmAccount *data();
@@ -103,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/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 <carlschwan@kde.org>
-//
-// SPDX-License-Identifier: LGPL-2.1-or-later
-
-
-#include "qolmerrors.h"
-#include "util.h"
-#include <QtCore/QLatin1String>
-
-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 <carlschwan@kde.org>
-//
-// 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..18275dc0 100644
--- a/lib/e2ee/qolminboundsession.cpp
+++ b/lib/e2ee/qolminboundsession.cpp
@@ -2,84 +2,99 @@
//
// SPDX-License-Identifier: LGPL-2.1-or-later
-#include "e2ee/qolminboundsession.h"
-#include <iostream>
+#include "qolminboundsession.h"
+#include "qolmutils.h"
+#include "../logging.h"
+
#include <cstring>
+#include <iostream>
+#include <olm/olm.h>
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);
//delete[](reinterpret_cast<uint8_t *>(m_groupSession));
}
-std::unique_ptr<QOlmInboundGroupSession> QOlmInboundGroupSession::create(const QByteArray &key)
+QOlmExpected<QOlmInboundGroupSessionPtr> 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<const uint8_t *>(key.constData()), key.size());
-
- if (error == olm_error()) {
- throw lastError(olmInboundGroupSession);
+ if (olm_init_inbound_group_session(
+ olmInboundGroupSession,
+ reinterpret_cast<const uint8_t*>(key.constData()), key.size())
+ == olm_error()) {
+ // FIXME: create QOlmInboundGroupSession earlier and use lastErrorCode()
+ 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<QOlmInboundGroupSession>(olmInboundGroupSession);
}
-std::unique_ptr<QOlmInboundGroupSession> QOlmInboundGroupSession::import(const QByteArray &key)
+QOlmExpected<QOlmInboundGroupSessionPtr> QOlmInboundGroupSession::importSession(
+ const QByteArray& key)
{
const auto olmInboundGroupSession = olm_inbound_group_session(new uint8_t[olm_inbound_group_session_size()]);
- QByteArray keyBuf = key;
- const auto error = olm_import_inbound_group_session(olmInboundGroupSession,
- reinterpret_cast<const uint8_t *>(keyBuf.data()), keyBuf.size());
- if (error == olm_error()) {
- throw lastError(olmInboundGroupSession);
+ if (olm_import_inbound_group_session(
+ olmInboundGroupSession,
+ reinterpret_cast<const uint8_t*>(key.data()), key.size())
+ == olm_error()) {
+ // FIXME: create QOlmInboundGroupSession earlier and use lastError()
+ 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<QOlmInboundGroupSession>(olmInboundGroupSession);
}
-QByteArray toKey(const PicklingMode &mode)
+QByteArray QOlmInboundGroupSession::pickle(const PicklingMode& mode) const
{
- if (std::holds_alternative<Unencrypted>(mode)) {
- return "";
- }
- return std::get<Encrypted>(mode).key;
-}
-
-QByteArray QOlmInboundGroupSession::pickle(const PicklingMode &mode) const
-{
- QByteArray pickledBuf(olm_pickle_inbound_group_session_length(m_groupSession), '0');
- const QByteArray key = toKey(mode);
- const auto error = olm_pickle_inbound_group_session(m_groupSession, key.data(), key.length(), pickledBuf.data(),
- pickledBuf.length());
- if (error == olm_error()) {
- throw lastError(m_groupSession);
+ 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()) {
+ QOLM_INTERNAL_ERROR("Failed to pickle the inbound group session");
}
return pickledBuf;
}
QOlmExpected<QOlmInboundGroupSessionPtr> 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);
- const auto error = olm_unpickle_inbound_group_session(groupSession, key.data(), key.length(),
- pickledBuf.data(), pickledBuf.size());
- if (error == olm_error()) {
- return lastError(groupSession);
+ auto key = toKey(mode);
+ if (olm_unpickle_inbound_group_session(groupSession, key.data(),
+ key.length(), pickled.data(),
+ 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();
@@ -94,39 +109,43 @@ QOlmExpected<std::pair<QByteArray, uint32_t>> 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<uint8_t *>(messageBuf.data()), messageBuf.length()), '0');
+ QByteArray plaintextBuf(olm_group_decrypt_max_plaintext_length(
+ m_groupSession,
+ reinterpret_cast<uint8_t*>(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<uint8_t *>(messageBuf.data()),
messageBuf.length(), reinterpret_cast<uint8_t *>(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');
+ QByteArray output(plaintextLen, '\0');
std::memcpy(output.data(), plaintextBuf.data(), plaintextLen);
return std::make_pair(output, messageIndex);
}
-QOlmExpected<QByteArray> QOlmInboundGroupSession::exportSession(uint32_t messageIndex)
+QOlmExpected<QByteArray> QOlmInboundGroupSession::exportSession(
+ uint32_t messageIndex)
{
const auto keyLength = olm_export_inbound_group_session_length(m_groupSession);
- QByteArray keyBuf(keyLength, '0');
- const auto error = olm_export_inbound_group_session(m_groupSession, reinterpret_cast<uint8_t *>(keyBuf.data()), keyLength, messageIndex);
-
- if (error == olm_error()) {
- return lastError(m_groupSession);
+ QByteArray keyBuf(keyLength, '\0');
+ if (olm_export_inbound_group_session(
+ m_groupSession, reinterpret_cast<uint8_t*>(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;
}
@@ -138,12 +157,14 @@ 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<uint8_t *>(sessionIdBuf.data()),
- sessionIdBuf.length());
- if (error == olm_error()) {
- throw lastError(m_groupSession);
- }
+ QByteArray sessionIdBuf(olm_inbound_group_session_id_length(m_groupSession),
+ '\0');
+ if (olm_inbound_group_session_id(
+ m_groupSession, reinterpret_cast<uint8_t*>(sessionIdBuf.data()),
+ sessionIdBuf.length())
+ == 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 1a9b4415..b9710354 100644
--- a/lib/e2ee/qolminboundsession.h
+++ b/lib/e2ee/qolminboundsession.h
@@ -6,7 +6,7 @@
#include "e2ee/e2ee.h"
-#include <olm/olm.h>
+struct OlmInboundGroupSession;
namespace Quotient {
@@ -17,15 +17,15 @@ class QUOTIENT_API QOlmInboundGroupSession
public:
~QOlmInboundGroupSession();
//! Creates a new instance of `OlmInboundGroupSession`.
- static std::unique_ptr<QOlmInboundGroupSession> create(const QByteArray& key);
+ static QOlmExpected<QOlmInboundGroupSessionPtr> create(const QByteArray& key);
//! Import an inbound group session, from a previous export.
- static std::unique_ptr<QOlmInboundGroupSession> import(const QByteArray& key);
+ static QOlmExpected<QOlmInboundGroupSessionPtr> importSession(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<QOlmInboundGroupSessionPtr> unpickle(
- const QByteArray& pickled, const PicklingMode& mode);
+ QByteArray&& pickled, const PicklingMode& mode);
//! Decrypts ciphertext received for this group session.
QOlmExpected<std::pair<QByteArray, uint32_t> > decrypt(const QByteArray& message);
//! Export the base64-encoded ratchet key for this session, at the given index,
@@ -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/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 <QObject>
-#include <QByteArray>
+#include <QtCore/QByteArray>
+#include <qobjectdefs.h>
+#include <olm/olm.h>
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 a2eff2c8..1176d790 100644
--- a/lib/e2ee/qolmoutboundsession.cpp
+++ b/lib/e2ee/qolmoutboundsession.cpp
@@ -2,13 +2,22 @@
//
// SPDX-License-Identifier: LGPL-2.1-or-later
-#include "e2ee/qolmoutboundsession.h"
-#include "e2ee/qolmutils.h"
+#include "qolmoutboundsession.h"
+
+#include "logging.h"
+#include "qolmutils.h"
+
+#include <olm/olm.h>
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)
@@ -24,72 +33,69 @@ 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);
- QByteArray randomBuf = getRandom(randomLength);
-
- const auto error = olm_init_outbound_group_session(olmOutboundGroupSession,
- reinterpret_cast<uint8_t *>(randomBuf.data()), randomBuf.length());
-
- if (error == olm_error()) {
- throw lastError(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()) {
+ // FIXME: create the session object earlier
+ QOLM_INTERNAL_ERROR_X("Failed to initialise an outbound group session",
+ olm_outbound_group_session_last_error(
+ olmOutboundGroupSession));
}
- const auto keyMaxLength = olm_outbound_group_session_key_length(olmOutboundGroupSession);
- QByteArray keyBuffer(keyMaxLength, '0');
- olm_outbound_group_session_key(olmOutboundGroupSession, reinterpret_cast<uint8_t *>(keyBuffer.data()),
- keyMaxLength);
-
- randomBuf.clear();
-
return std::make_unique<QOlmOutboundGroupSession>(olmOutboundGroupSession);
}
-QOlmExpected<QByteArray> QOlmOutboundGroupSession::pickle(const PicklingMode &mode) const
+QByteArray QOlmOutboundGroupSession::pickle(const PicklingMode &mode) const
{
- 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);
- }
+ 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())
+ == olm_error())
+ QOLM_INTERNAL_ERROR("Failed to pickle the outbound group session");
key.clear();
-
return pickledBuf;
}
-QOlmExpected<QOlmOutboundGroupSessionPtr> QOlmOutboundGroupSession::unpickle(const QByteArray &pickled, const PicklingMode &mode)
+QOlmExpected<QOlmOutboundGroupSessionPtr> QOlmOutboundGroupSession::unpickle(
+ QByteArray&& pickled, const PicklingMode& mode)
{
- QByteArray pickledBuf = pickled;
auto *olmOutboundGroupSession = olm_outbound_group_session(new uint8_t[olm_outbound_group_session_size()]);
- QByteArray key = toKey(mode);
- const auto error = olm_unpickle_outbound_group_session(olmOutboundGroupSession, key.data(), key.length(),
- pickledBuf.data(), pickledBuf.length());
- if (error == olm_error()) {
- return lastError(olmOutboundGroupSession);
+ auto key = toKey(mode);
+ if (olm_unpickle_outbound_group_session(olmOutboundGroupSession, key.data(),
+ key.length(), pickled.data(),
+ 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);
}
- const auto idMaxLength = olm_outbound_group_session_id_length(olmOutboundGroupSession);
- QByteArray idBuffer(idMaxLength, '0');
- olm_outbound_group_session_id(olmOutboundGroupSession, reinterpret_cast<uint8_t *>(idBuffer.data()),
- idBuffer.length());
key.clear();
return std::make_unique<QOlmOutboundGroupSession>(olmOutboundGroupSession);
}
-QOlmExpected<QByteArray> QOlmOutboundGroupSession::encrypt(const QString &plaintext) const
+QByteArray 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 error = olm_group_encrypt(m_groupSession, reinterpret_cast<uint8_t *>(plaintextBuf.data()),
- plaintextBuf.length(), reinterpret_cast<uint8_t *>(messageBuf.data()), messageBuf.length());
-
- if (error == olm_error()) {
- return lastError(m_groupSession);
- }
+ const auto messageMaxLength =
+ olm_group_encrypt_message_length(m_groupSession, plaintext.length());
+ QByteArray messageBuf(messageMaxLength, '\0');
+ if (olm_group_encrypt(m_groupSession,
+ reinterpret_cast<const uint8_t*>(plaintext.data()),
+ plaintext.length(),
+ reinterpret_cast<uint8_t*>(messageBuf.data()),
+ messageBuf.length())
+ == olm_error())
+ QOLM_INTERNAL_ERROR("Failed to encrypt a message");
return messageBuf;
}
@@ -102,25 +108,26 @@ uint32_t QOlmOutboundGroupSession::sessionMessageIndex() const
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<uint8_t *>(idBuffer.data()),
- idBuffer.length());
- if (error == olm_error()) {
- throw lastError(m_groupSession);
- }
+ QByteArray idBuffer(idMaxLength, '\0');
+ if (olm_outbound_group_session_id(
+ m_groupSession, reinterpret_cast<uint8_t*>(idBuffer.data()),
+ idBuffer.length())
+ == olm_error())
+ QOLM_INTERNAL_ERROR("Failed to obtain group session id");
+
return idBuffer;
}
-QOlmExpected<QByteArray> QOlmOutboundGroupSession::sessionKey() const
+QByteArray 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<uint8_t*>(keyBuffer.data()),
- keyMaxLength);
- if (error == olm_error()) {
- return lastError(m_groupSession);
- }
+ QByteArray keyBuffer(keyMaxLength, '\0');
+ if (olm_outbound_group_session_key(
+ m_groupSession, reinterpret_cast<uint8_t*>(keyBuffer.data()),
+ keyMaxLength)
+ == olm_error())
+ QOLM_INTERNAL_ERROR("Failed to obtain group session key");
+
return keyBuffer;
}
diff --git a/lib/e2ee/qolmoutboundsession.h b/lib/e2ee/qolmoutboundsession.h
index 9a82d22a..d36fbf69 100644
--- a/lib/e2ee/qolmoutboundsession.h
+++ b/lib/e2ee/qolmoutboundsession.h
@@ -6,8 +6,7 @@
#include "e2ee/e2ee.h"
-#include <memory>
-#include <olm/olm.h>
+struct OlmOutboundGroupSession;
namespace Quotient {
@@ -21,14 +20,14 @@ public:
//! Throw OlmError on errors
static QOlmOutboundGroupSessionPtr create();
//! Serialises a `QOlmOutboundGroupSession` to encrypted Base64.
- QOlmExpected<QByteArray> 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<QOlmOutboundGroupSessionPtr> unpickle(
- const QByteArray& pickled, const PicklingMode& mode);
+ QByteArray&& pickled, const PicklingMode& mode);
//! Encrypts a plaintext message using the session.
- QOlmExpected<QByteArray> encrypt(const QString& plaintext) const;
+ QByteArray encrypt(const QByteArray& plaintext) const;
//! Get the current message index for this session.
//!
@@ -43,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<QByteArray> sessionKey() const;
+ QByteArray sessionKey() const;
QOlmOutboundGroupSession(OlmOutboundGroupSession *groupSession);
int messageCount() const;
@@ -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..e3f69132 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<QOlmSessionPtr> 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<QOlmSessionPtr> 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;
}
@@ -69,147 +76,120 @@ QOlmExpected<QOlmSessionPtr> QOlmSession::createInboundSessionFrom(
}
QOlmExpected<QOlmSessionPtr> 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);
-
- QByteArray theirIdentityKeyBuf = theirIdentityKey.toUtf8();
- QByteArray theirOneTimeKeyBuf = theirOneTimeKey.toUtf8();
- const auto error = olm_create_outbound_session(olmOutboundSession,
- account->data(),
- reinterpret_cast<uint8_t *>(theirIdentityKeyBuf.data()), theirIdentityKeyBuf.length(),
- reinterpret_cast<uint8_t *>(theirOneTimeKeyBuf.data()), theirOneTimeKeyBuf.length(),
- reinterpret_cast<uint8_t *>(randomBuf.data()), randomBuf.length());
-
- if (error == olm_error()) {
- const auto lastErr = lastError(olmOutboundSession);
- if (lastErr == QOlmError::NotEnoughRandom) {
- throw lastErr;
- }
+ auto* olmOutboundSession = create();
+ 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)
+ == olm_error()) {
+ // FIXME: the QOlmSession object should be created earlier
+ const auto lastErr = olm_session_last_error_code(olmOutboundSession);
+ QOLM_FAIL_OR_LOG_X(lastErr == OLM_NOT_ENOUGH_RANDOM,
+ "Failed to create an outbound Olm session",
+ olm_session_last_error(olmOutboundSession));
return lastErr;
}
- randomBuf.clear();
return std::make_unique<QOlmSession>(olmOutboundSession);
}
-QOlmExpected<QByteArray> QOlmSession::pickle(const PicklingMode &mode) const
+QByteArray 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);
- 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())
+ QOLM_INTERNAL_ERROR("Failed to pickle an Olm session");
key.clear();
-
return pickledBuf;
}
-QOlmExpected<QOlmSessionPtr> QOlmSession::unpickle(const QByteArray& pickled,
+QOlmExpected<QOlmSessionPtr> QOlmSession::unpickle(QByteArray&& pickled,
const PicklingMode& mode)
{
- QByteArray pickledBuf = pickled;
auto *olmSession = create();
- QByteArray key = toKey(mode);
- const auto error = olm_unpickle_session(olmSession, key.data(), key.length(),
- pickledBuf.data(), pickledBuf.length());
- if (error == olm_error()) {
- return lastError(olmSession);
+ auto key = toKey(mode);
+ if (olm_unpickle_session(olmSession, key.data(), key.length(),
+ pickled.data(), pickled.length())
+ == olm_error()) {
+ // FIXME: the QOlmSession object should be created earlier
+ 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();
return std::make_unique<QOlmSession>(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);
- const auto error = olm_encrypt(m_session,
- reinterpret_cast<uint8_t *>(plaintextBuf.data()), plaintextBuf.length(),
- reinterpret_cast<uint8_t *>(randomBuf.data()), randomBuf.length(),
- reinterpret_cast<uint8_t *>(messageBuf.data()), messageBuf.length());
-
- if (error == olm_error()) {
- throw lastError(m_session);
+ 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);
+ 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()) {
+ QOLM_INTERNAL_ERROR("Failed to encrypt the message");
}
- return QOlmMessage(messageBuf, messageType);
+ return QOlmMessage(messageBuf, QOlmMessage::Type(messageType));
}
QOlmExpected<QByteArray> 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<uint8_t *>(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(m_session);
+ qWarning(E2EE) << "Couldn't calculate decrypted message length:"
+ << lastError();
+ return lastErrorCode();
}
- 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<uint8_t *>(messageBuf2.data()), messageBuf2.length(),
- reinterpret_cast<uint8_t *>(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 = lastError(m_session);
- if (lastErr == QOlmError::OutputBufferTooSmall) {
- throw lastErr;
- }
- return lastErr;
- }
- QByteArray output(plaintextResultLen, '0');
- std::memcpy(output.data(), plaintextBuf.data(), plaintextResultLen);
- plaintextBuf.clear();
- return output;
-}
-
-QOlmMessage::Type QOlmSession::encryptMessageType()
-{
- const auto messageTypeResult = olm_encrypt_message_type(m_session);
- if (messageTypeResult == olm_error()) {
- throw lastError(m_session);
- }
- if (messageTypeResult == OLM_MESSAGE_TYPE_PRE_KEY) {
- return QOlmMessage::PreKey;
+ QOLM_FAIL_OR_LOG(OLM_OUTPUT_BUFFER_TOO_SMALL,
+ "Failed to decrypt the message");
+ return lastErrorCode();
}
- 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');
- const auto error = olm_session_id(m_session, reinterpret_cast<uint8_t *>(idBuffer.data()),
- idBuffer.length());
- if (error == olm_error()) {
- throw lastError(m_session);
- }
+ 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;
}
@@ -225,11 +205,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())
+ qWarning(E2EE) << "Error matching an inbound session:" << lastError();
- if (maybeMatches == olm_error()) {
- return lastError(m_session);
- }
- return maybeMatches == 1;
+ return maybeMatches == 1; // Any errors are treated as non-match
}
bool QOlmSession::matchesInboundSessionFrom(
@@ -242,8 +221,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 021092c7..400fb854 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;
@@ -27,18 +26,18 @@ public:
const QOlmMessage& preKeyMessage);
static QOlmExpected<QOlmSessionPtr> createOutboundSession(
- QOlmAccount* account, const QString& theirIdentityKey,
- const QString& theirOneTimeKey);
+ QOlmAccount* account, const QByteArray& theirIdentityKey,
+ const QByteArray& theirOneTimeKey);
//! Serialises an `QOlmSession` to encrypted Base64.
- QOlmExpected<QByteArray> pickle(const PicklingMode &mode) const;
+ QByteArray pickle(const PicklingMode &mode) const;
- //! Deserialises from encrypted Base64 that was previously obtained by pickling a `QOlmSession`.
- static QOlmExpected<QOlmSessionPtr> unpickle(
- const QByteArray& pickled, const PicklingMode& mode);
+ //! Deserialises from encrypted Base64 previously made with pickle()
+ static QOlmExpected<QOlmSessionPtr> 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
@@ -48,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;
@@ -71,6 +67,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 22f9ec0d..46f7f4f3 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()
@@ -28,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());
diff --git a/lib/e2ee/qolmutility.h b/lib/e2ee/qolmutility.h
index 6c1c8624..508767bf 100644
--- a/lib/e2ee/qolmutility.h
+++ b/lib/e2ee/qolmutility.h
@@ -32,8 +32,10 @@ public:
bool ed25519Verify(const QByteArray &key,
const QByteArray &message, QByteArray signature);
+ OlmErrorCode lastErrorCode() const;
+ const char* lastError() const;
+
private:
OlmUtility *m_utility;
-
};
}
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<Quotient::Encrypted>(mode).key;
}
-QByteArray Quotient::getRandom(size_t bufferSize)
+RandomBuffer::RandomBuffer(size_t size)
+ : QByteArray(static_cast<int>(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 f218e628..17eee7a3 100644
--- a/lib/e2ee/qolmutils.h
+++ b/lib/e2ee/qolmutils.h
@@ -9,7 +9,47 @@
#include "e2ee/e2ee.h"
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<uint8_t*>(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);
}
+
+#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/events/encryptedevent.cpp b/lib/events/encryptedevent.cpp
index 49df25c8..540594d1 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/events/filesourceinfo.cpp b/lib/events/filesourceinfo.cpp
index e8b6794b..a60d86d2 100644
--- a/lib/events/filesourceinfo.cpp
+++ b/lib/events/filesourceinfo.cpp
@@ -59,23 +59,17 @@ std::pair<EncryptedFileMetadata, QByteArray> 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 = RandomBuffer(32);
+ auto kBase64 = k.toBase64(QByteArray::Base64UrlEncoding
+ | QByteArray::OmitTrailingEquals);
+ 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<const unsigned char*>(k.data()),
- reinterpret_cast<const unsigned char*>(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<unsigned char*>(cipherText.data()),
@@ -89,14 +83,11 @@ std::pair<EncryptedFileMetadata, QByteArray> 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 f5d49561..4c61964c 100644
--- a/lib/keyverificationsession.cpp
+++ b/lib/keyverificationsession.cpp
@@ -71,9 +71,8 @@ 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);
+ olm_create_sas(m_sas, RandomBuffer(randomLength), randomLength);
}
KeyVerificationSession::~KeyVerificationSession()
diff --git a/lib/room.cpp b/lib/room.cpp
index 95dd0ab7..0cf818ce 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"
@@ -358,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;
@@ -443,12 +444,9 @@ 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);
+ addInboundGroupSession(currentOutboundMegolmSession->sessionId(),
+ currentOutboundMegolmSession->sessionKey(),
+ q->localUser()->id(), "SELF"_ls);
}
QMultiHash<QString, QString> getDevicesWithoutKey() const
@@ -461,22 +459,6 @@ public:
return connection->database()->devicesWithoutKey(
id, devices, currentOutboundMegolmSession->sessionId());
}
-
- void sendMegolmSession(const QMultiHash<QString, QString>& 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:
@@ -2030,17 +2012,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<EncryptedEvent>(*encrypted, q->connection()->olmAccount()->identityKeys().curve25519, q->connection()->deviceId(), currentOutboundMegolmSession->sessionId());
+ encryptedEvent = makeEvent<EncryptedEvent>(
+ encrypted, q->connection()->olmAccount()->identityKeys().curve25519,
+ q->connection()->deviceId(),
+ currentOutboundMegolmSession->sessionId());
encryptedEvent->setTransactionId(connection->generateTxnId());
encryptedEvent->setRoomId(id);
encryptedEvent->setSender(connection->userId());
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 <typename T>
+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 <typename T>
+asKeyValueRange(T&) -> asKeyValueRange<T>;
+
/** 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"