From fe9b2f918753d40d93f8aecf182485e75d4b75bb Mon Sep 17 00:00:00 2001 From: Carl Schwan Date: Sun, 31 Jan 2021 01:18:35 +0100 Subject: More test but still failing in signing/signature verification --- autotests/testolmutility.cpp | 131 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 autotests/testolmutility.cpp (limited to 'autotests/testolmutility.cpp') diff --git a/autotests/testolmutility.cpp b/autotests/testolmutility.cpp new file mode 100644 index 00000000..cb92a0df --- /dev/null +++ b/autotests/testolmutility.cpp @@ -0,0 +1,131 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "testolmutility.h" +#include "crypto/qolmaccount.h" +#include "crypto/qolmutility.h" + +using namespace Quotient; + +void TestOlmUtility::canonicalJSON() +{ + // Examples taken from + // https://matrix.org/docs/spec/appendices.html#canonical-json + auto data = QJsonDocument::fromJson(QByteArrayLiteral(R"({ + "auth": { + "success": true, + "mxid": "@john.doe:example.com", + "profile": { + "display_name": "John Doe", + "three_pids": [{ + "medium": "email", + "address": "john.doe@example.org" + }, { + "medium": "msisdn", + "address": "123456789" + }] + }}})")); + + QCOMPARE(data.toJson(QJsonDocument::Compact), + "{\"auth\":{\"mxid\":\"@john.doe:example.com\",\"profile\":{\"display_name\":\"John " + "Doe\",\"three_pids\":[{\"address\":\"john.doe@example.org\",\"medium\":\"email\"},{" + "\"address\":\"123456789\",\"medium\":\"msisdn\"}]},\"success\":true}}"); + + auto data0 = QJsonDocument::fromJson(QByteArrayLiteral(R"({"b":"2","a":"1"})")); + QCOMPARE(data0.toJson(QJsonDocument::Compact), "{\"a\":\"1\",\"b\":\"2\"}"); + + auto data1 = QJsonDocument::fromJson(QByteArrayLiteral(R"({ "本": 2, "日": 1 })")); + QCOMPARE(data1.toJson(QJsonDocument::Compact), "{\"日\":1,\"本\":2}"); + + auto data2 = QJsonDocument::fromJson(QByteArrayLiteral(R"({"a": "\u65E5"})")); + QCOMPARE(data2.toJson(QJsonDocument::Compact), "{\"a\":\"日\"}"); + + auto data3 = QJsonDocument::fromJson(QByteArrayLiteral(R"({ "a": null })")); + QCOMPARE(data3.toJson(QJsonDocument::Compact), "{\"a\":null}"); +} + +void TestOlmUtility::verifySignedOneTimeKey() +{ + auto aliceOlm = std::make_shared("alice:matrix.org", "aliceDevice"); + aliceOlm->createNewAccount(); + aliceOlm->generateOneTimeKeys(1); + auto keys = aliceOlm->oneTimeKeys(); + + auto firstKey = keys.curve25519().keyValueBegin()->second; + auto msgObj = QJsonObject({{"key", firstKey}}); + auto sig = aliceOlm->sign(msgObj); + + auto msg = QJsonDocument(msgObj).toJson(QJsonDocument::Compact); + + auto utilityBuf = new uint8_t[olm_utility_size()]; + auto utility = olm_utility(utilityBuf); + + qDebug() << "1" << aliceOlm->identityKeys().ed25519 << msg << QString::fromUtf8(sig); + + 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()); + qDebug() << "2" << aliceOlm->identityKeys().ed25519 << msg << QString::fromUtf8(signatureBuf1); + + QCOMPARE(std::string(olm_utility_last_error(utility)), "SUCCESS"); + QCOMPARE(res, 0); + + delete[](reinterpret_cast(utility)); + + QOlmUtility utility2; + auto res2 = std::get(utility2.ed25519Verify(aliceOlm->identityKeys().ed25519, msg, signatureBuf1)); + + //QCOMPARE(std::string(olm_utility_last_error(utility)), "SUCCESS"); + QCOMPARE(res2, true); +} + +void TestOlmUtility::validUploadKeysRequest() +{ + const auto userId = QStringLiteral("@alice:matrix.org"); + const auto deviceId = QStringLiteral("FKALSOCCC"); + + auto alice = std::make_shared(userId, deviceId); + alice->createNewAccount(); + alice->generateOneTimeKeys(1); + + auto idSig = alice->signIdentityKeys(); + + QJsonObject body + { + {"algorithms", QJsonArray{"m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"}}, + {"user_id", userId}, + {"device_id", deviceId}, + {"keys", + QJsonObject{ + {QStringLiteral("curve25519:") + deviceId, QString::fromUtf8(alice->identityKeys().curve25519)}, + {QStringLiteral("ed25519:") + deviceId, QString::fromUtf8(alice->identityKeys().ed25519)} + } + }, + {"signatures", + QJsonObject{ + {userId, + QJsonObject{ + {"ed25519:" + deviceId, QString::fromUtf8(idSig)} + } + } + } + } + }; + + DeviceKeys deviceKeys = alice->getDeviceKeys(); + QCOMPARE(QJsonDocument(toJson(deviceKeys)).toJson(QJsonDocument::Compact), + QJsonDocument(body).toJson(QJsonDocument::Compact)); + + QVERIFY(verifyIdentitySignature(fromJson(body), deviceId, userId)); + QVERIFY(verifyIdentitySignature(deviceKeys, deviceId, userId)); +} + +QTEST_MAIN(TestOlmUtility) -- cgit v1.2.3 From d9dc94a8fb59c8590c4aa7cdf773c2825e69d823 Mon Sep 17 00:00:00 2001 From: Carl Schwan Date: Sun, 31 Jan 2021 21:11:23 +0100 Subject: Fix signing keys --- autotests/testolmaccount.cpp | 12 ++++++++++-- autotests/testolmutility.cpp | 2 -- lib/crypto/qolmaccount.cpp | 19 +++++++++++++------ lib/crypto/qolmutility.cpp | 6 ------ 4 files changed, 23 insertions(+), 16 deletions(-) (limited to 'autotests/testolmutility.cpp') diff --git a/autotests/testolmaccount.cpp b/autotests/testolmaccount.cpp index 8d979e0b..4eed1980 100644 --- a/autotests/testolmaccount.cpp +++ b/autotests/testolmaccount.cpp @@ -309,7 +309,6 @@ void TestOlmAccount::claimKeys() deviceKeys[bob->userId()] = QStringList(); auto job = alice->callApi(deviceKeys); connect(job, &BaseJob::result, this, [bob, alice, aliceOlm, job, this] { - qDebug() << job->jsonData(); auto bobDevices = job->deviceKeys()[bob->userId()]; QVERIFY(bobDevices.size() > 0); @@ -337,7 +336,16 @@ void TestOlmAccount::claimKeys() // The key is the one bob sent. auto oneTimeKey = job->oneTimeKeys()[userId][deviceId]; - QVERIFY(oneTimeKey.canConvert()); + QVERIFY(oneTimeKey.canConvert()); + + QVariantMap varMap = oneTimeKey.toMap(); + bool found = false; + for (const auto key : varMap.keys()) { + if (key.startsWith(QStringLiteral("signed_curve25519"))) { + found = true; + } + } + QVERIFY(found); //auto algo = oneTimeKey.begin().key(); //auto contents = oneTimeKey.begin().value(); diff --git a/autotests/testolmutility.cpp b/autotests/testolmutility.cpp index cb92a0df..1d9978d3 100644 --- a/autotests/testolmutility.cpp +++ b/autotests/testolmutility.cpp @@ -61,7 +61,6 @@ void TestOlmUtility::verifySignedOneTimeKey() auto utilityBuf = new uint8_t[olm_utility_size()]; auto utility = olm_utility(utilityBuf); - qDebug() << "1" << aliceOlm->identityKeys().ed25519 << msg << QString::fromUtf8(sig); QByteArray signatureBuf1(sig.length(), '0'); std::copy(sig.begin(), sig.end(), signatureBuf1.begin()); @@ -73,7 +72,6 @@ void TestOlmUtility::verifySignedOneTimeKey() msg.size(), (void *)sig.data(), sig.size()); - qDebug() << "2" << aliceOlm->identityKeys().ed25519 << msg << QString::fromUtf8(signatureBuf1); QCOMPARE(std::string(olm_utility_last_error(utility)), "SUCCESS"); QCOMPARE(res, 0); diff --git a/lib/crypto/qolmaccount.cpp b/lib/crypto/qolmaccount.cpp index e27bbee1..0f354d3e 100644 --- a/lib/crypto/qolmaccount.cpp +++ b/lib/crypto/qolmaccount.cpp @@ -129,10 +129,19 @@ QByteArray QOlmAccount::sign(const QJsonObject &message) const QByteArray QOlmAccount::signIdentityKeys() const { const auto keys = identityKeys(); - const QJsonObject j{ {Curve25519Key, QString(keys.curve25519)}, {Ed25519Key, QString(keys.ed25519)} }; - QJsonDocument doc; - doc.setObject(j); - return sign(doc.toJson()); + QJsonObject body + { + {"algorithms", QJsonArray{"m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"}}, + {"user_id", m_userId}, + {"device_id", m_deviceId}, + {"keys", + QJsonObject{ + {QStringLiteral("curve25519:") + m_deviceId, QString::fromUtf8(keys.curve25519)}, + {QStringLiteral("ed25519:") + m_deviceId, QString::fromUtf8(keys.ed25519)} + } + } + }; + return sign(QJsonDocument(body).toJson(QJsonDocument::Compact)); } @@ -279,7 +288,6 @@ bool Quotient::verifyIdentitySignature(const DeviceKeys &deviceKeys, const auto signature = deviceKeys.signatures[userId][signKeyId]; if (signature.isEmpty()) { - qDebug() << "signature empty"; return false; } @@ -305,7 +313,6 @@ bool Quotient::ed25519VerifySignature(const QString &signingKey, auto signatureBuf = signature.toUtf8(); auto result = utility.ed25519Verify(signingKeyBuf, canonicalJson, signatureBuf); if (std::holds_alternative(result)) { - qDebug() << "error:" << std::get(result); return false; } diff --git a/lib/crypto/qolmutility.cpp b/lib/crypto/qolmutility.cpp index ad78a226..87615770 100644 --- a/lib/crypto/qolmutility.cpp +++ b/lib/crypto/qolmutility.cpp @@ -20,12 +20,10 @@ QOlmUtility::QOlmUtility() { auto utility = new uint8_t[olm_utility_size()]; m_utility = olm_utility(utility); - qDebug() << "created"; } QOlmUtility::~QOlmUtility() { - qDebug() << "deleted"; olm_clear_utility(m_utility); delete[](reinterpret_cast(m_utility)); } @@ -51,19 +49,15 @@ std::variant QOlmUtility::ed25519Verify(const QByteArray &key, QByteArray signatureBuf(signature.length(), '0'); std::copy(signature.begin(), signature.end(), signatureBuf.begin()); - qDebug() << "3" << key << message << signature; - const auto ret = olm_ed25519_verify(m_utility, key.data(), key.size(), message.data(), message.size(), (void *)signatureBuf.data(), signatureBuf.size()); const auto error = ret; if (error == olm_error()) { - qDebug() << QString(olm_utility_last_error(m_utility)); return lastError(m_utility); } if (ret != 0) { - qDebug() << "ed25519Verify" << ret; return false; } return true; -- cgit v1.2.3 From 0a75a095665101d4ffcbec10b43633eee5a0d6d3 Mon Sep 17 00:00:00 2001 From: Carl Schwan Date: Thu, 6 May 2021 01:08:53 +0200 Subject: Fix everything --- autotests/testolmaccount.cpp | 3 ++- autotests/testolmaccount.h | 2 +- autotests/testolmsession.cpp | 3 ++- autotests/testolmsession.h | 2 +- autotests/testolmutility.cpp | 2 +- lib/connection.cpp | 5 ----- lib/connection.h | 5 ----- lib/connectiondata.cpp | 6 ------ lib/connectiondata.h | 1 - lib/crypto/qolmaccount.cpp | 6 +++--- lib/crypto/qolmaccount.h | 2 +- lib/crypto/qolmoutboundsession.h | 2 +- 12 files changed, 12 insertions(+), 27 deletions(-) (limited to 'autotests/testolmutility.cpp') diff --git a/autotests/testolmaccount.cpp b/autotests/testolmaccount.cpp index eb44791a..1c296db9 100644 --- a/autotests/testolmaccount.cpp +++ b/autotests/testolmaccount.cpp @@ -6,6 +6,7 @@ #include "crypto/qolmaccount.h" #include "connection.h" #include "events/encryptedfile.h" +#include "networkaccessmanager.h" using namespace Quotient; @@ -164,8 +165,8 @@ void TestOlmAccount::encryptedFile() } #define CREATE_CONNECTION(VAR, USERNAME, SECRET, DEVICE_NAME) \ + NetworkAccessManager::instance()->ignoreSslErrors(true); \ auto VAR = std::make_shared(); \ - VAR->ignoreSslErrors(true); \ (VAR) ->resolveServer("@alice:localhost:" + QString::number(443)); \ connect( (VAR) .get(), &Connection::loginFlowsChanged, this, [this, VAR ] () { \ (VAR) ->loginWithPassword( (USERNAME) , SECRET , DEVICE_NAME , ""); \ diff --git a/autotests/testolmaccount.h b/autotests/testolmaccount.h index 8901f095..bab9eed2 100644 --- a/autotests/testolmaccount.h +++ b/autotests/testolmaccount.h @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: LGPL-2.1-or-later -#include +#include #include namespace Quotient { diff --git a/autotests/testolmsession.cpp b/autotests/testolmsession.cpp index 0803cc6d..dba78277 100644 --- a/autotests/testolmsession.cpp +++ b/autotests/testolmsession.cpp @@ -26,7 +26,8 @@ std::pair, std::unique_ptr> createSess const auto preKey = outbound->encrypt(""); // Payload does not matter for PreKey if (preKey.type() != QOlmMessage::PreKey) { - QFAIL("Wrong first message type received, can't create session"); + // We can't call QFail here because it's an helper function returning a value + throw "Wrong first message type received, can't create session"; } auto inbound = std::get>(accountB.createInboundSession(preKey)); return std::make_pair, std::unique_ptr>(std::move(inbound), std::move(outbound)); diff --git a/autotests/testolmsession.h b/autotests/testolmsession.h index bd670c9b..9a5798fa 100644 --- a/autotests/testolmsession.h +++ b/autotests/testolmsession.h @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: LGPL-2.1-or-later -#include +#include class TestOlmSession : public QObject { diff --git a/autotests/testolmutility.cpp b/autotests/testolmutility.cpp index 1d9978d3..2eec7e00 100644 --- a/autotests/testolmutility.cpp +++ b/autotests/testolmutility.cpp @@ -118,7 +118,7 @@ void TestOlmUtility::validUploadKeysRequest() } }; - DeviceKeys deviceKeys = alice->getDeviceKeys(); + DeviceKeys deviceKeys = alice->deviceKeys(); QCOMPARE(QJsonDocument(toJson(deviceKeys)).toJson(QJsonDocument::Compact), QJsonDocument(body).toJson(QJsonDocument::Compact)); diff --git a/lib/connection.cpp b/lib/connection.cpp index 62427ae1..704bc1b4 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -572,11 +572,6 @@ void Connection::sync(int timeout) }); } -void Connection::ignoreSslErrors(bool ignore) -{ - connectionData()->ignoreSslErrors(ignore); -} - void Connection::syncLoop(int timeout) { if (d->syncLoopConnection && d->syncTimeout == timeout) { diff --git a/lib/connection.h b/lib/connection.h index 93e22da2..6729b23d 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -476,11 +476,6 @@ public: setUserFactory(defaultUserFactory()); } - /// Ignore ssl errors (usefull for automated testing with local synapse - /// instance). - /// \internal - void ignoreSslErrors(bool ignore); - public Q_SLOTS: /// \brief Set the homeserver base URL and retrieve its login flows /// diff --git a/lib/connectiondata.cpp b/lib/connectiondata.cpp index 672feb06..87ad4577 100644 --- a/lib/connectiondata.cpp +++ b/lib/connectiondata.cpp @@ -128,12 +128,6 @@ bool ConnectionData::needsToken(const QString& requestName) const != d->needToken.cend(); } -void ConnectionData::ignoreSslErrors(bool ignore) const -{ - auto quotientNam = static_cast(nam()); - quotientNam.ignoreSslErrors(ignore); -} - void ConnectionData::setDeviceId(const QString& deviceId) { d->deviceId = deviceId; diff --git a/lib/connectiondata.h b/lib/connectiondata.h index 203dc9e8..e16a2dac 100644 --- a/lib/connectiondata.h +++ b/lib/connectiondata.h @@ -29,7 +29,6 @@ public: bool needsToken(const QString& requestName) const; QNetworkAccessManager* nam() const; - void ignoreSslErrors(bool ignore = true) const; void setBaseUrl(QUrl baseUrl); void setToken(QByteArray accessToken); void setDeviceId(const QString& deviceId); diff --git a/lib/crypto/qolmaccount.cpp b/lib/crypto/qolmaccount.cpp index 8b964c9f..9368de4f 100644 --- a/lib/crypto/qolmaccount.cpp +++ b/lib/crypto/qolmaccount.cpp @@ -243,10 +243,10 @@ DeviceKeys QOlmAccount::deviceKeys() const UploadKeysJob *QOlmAccount::createUploadKeyRequest(const OneTimeKeys &oneTimeKeys) { - auto deviceKeys = deviceKeys(); + auto keys = deviceKeys(); if (oneTimeKeys.curve25519().isEmpty()) { - return new UploadKeysJob(deviceKeys); + return new UploadKeysJob(keys); } // Sign & append the one time keys. @@ -256,7 +256,7 @@ UploadKeysJob *QOlmAccount::createUploadKeyRequest(const OneTimeKeys &oneTimeKey oneTimeKeysSigned[keyId] = QVariant::fromValue(key); } - return new UploadKeysJob(deviceKeys, oneTimeKeysSigned); + return new UploadKeysJob(keys, oneTimeKeysSigned); } std::variant, QOlmError> QOlmAccount::createInboundSession(const QOlmMessage &preKeyMessage) diff --git a/lib/crypto/qolmaccount.h b/lib/crypto/qolmaccount.h index c93a8354..f3ca82f0 100644 --- a/lib/crypto/qolmaccount.h +++ b/lib/crypto/qolmaccount.h @@ -73,7 +73,7 @@ public: UploadKeysJob *createUploadKeyRequest(const OneTimeKeys &oneTimeKeys); - DeviceKeys getDeviceKeys() const; + DeviceKeys deviceKeys() const; //! Remove the one time key used to create the supplied session. [[nodiscard]] std::optional removeOneTimeKeys(const std::unique_ptr &session) const; diff --git a/lib/crypto/qolmoutboundsession.h b/lib/crypto/qolmoutboundsession.h index 201a178a..4e06561e 100644 --- a/lib/crypto/qolmoutboundsession.h +++ b/lib/crypto/qolmoutboundsession.h @@ -26,7 +26,7 @@ public: std::variant pickle(const PicklingMode &mode); //! Deserialises from encrypted Base64 that was previously obtained by //! pickling a `QOlmOutboundGroupSession`. - static std::variant, QOlmError> unpickle(const QByteArray &pickled, const PicklingMode &mode); + static std::variant, QOlmError> unpickle(QByteArray &pickled, const PicklingMode &mode); //! Encrypts a plaintext message using the session. std::variant encrypt(const QString &plaintext); -- cgit v1.2.3 From 2c6fa33ca52842e9dfba0dd3893a9d5526e10e60 Mon Sep 17 00:00:00 2001 From: Tobias Fella Date: Tue, 7 Dec 2021 19:08:29 +0100 Subject: Rename "crypto" -> "e2ee" --- CMakeLists.txt | 18 +- autotests/testgroupsession.cpp | 6 +- autotests/testolmaccount.cpp | 4 +- autotests/testolmsession.cpp | 2 +- autotests/testolmutility.cpp | 4 +- lib/connection.cpp | 4 +- lib/connection.h | 2 +- lib/converters.cpp | 2 +- lib/crypto/e2ee.h | 132 --------------- lib/crypto/qolmaccount.cpp | 330 ------------------------------------- lib/crypto/qolmaccount.h | 123 -------------- lib/crypto/qolmerrors.cpp | 22 --- lib/crypto/qolmerrors.h | 28 ---- lib/crypto/qolminboundsession.cpp | 153 ----------------- lib/crypto/qolminboundsession.h | 48 ------ lib/crypto/qolmmessage.cpp | 35 ---- lib/crypto/qolmmessage.h | 41 ----- lib/crypto/qolmoutboundsession.cpp | 128 -------------- lib/crypto/qolmoutboundsession.h | 54 ------ lib/crypto/qolmsession.cpp | 253 ---------------------------- lib/crypto/qolmsession.h | 76 --------- lib/crypto/qolmutility.cpp | 63 ------- lib/crypto/qolmutility.h | 45 ----- lib/crypto/qolmutils.cpp | 24 --- lib/crypto/qolmutils.h | 15 -- lib/database.cpp | 6 +- lib/database.h | 2 +- lib/e2ee/e2ee.h | 132 +++++++++++++++ lib/e2ee/qolmaccount.cpp | 330 +++++++++++++++++++++++++++++++++++++ lib/e2ee/qolmaccount.h | 123 ++++++++++++++ lib/e2ee/qolmerrors.cpp | 22 +++ lib/e2ee/qolmerrors.h | 28 ++++ lib/e2ee/qolminboundsession.cpp | 153 +++++++++++++++++ lib/e2ee/qolminboundsession.h | 48 ++++++ lib/e2ee/qolmmessage.cpp | 35 ++++ lib/e2ee/qolmmessage.h | 41 +++++ lib/e2ee/qolmoutboundsession.cpp | 128 ++++++++++++++ lib/e2ee/qolmoutboundsession.h | 54 ++++++ lib/e2ee/qolmsession.cpp | 253 ++++++++++++++++++++++++++++ lib/e2ee/qolmsession.h | 76 +++++++++ lib/e2ee/qolmutility.cpp | 63 +++++++ lib/e2ee/qolmutility.h | 45 +++++ lib/e2ee/qolmutils.cpp | 24 +++ lib/e2ee/qolmutils.h | 15 ++ lib/encryptionmanager.cpp | 12 +- lib/events/encryptedevent.h | 2 +- lib/events/encryptionevent.cpp | 2 +- lib/room.cpp | 8 +- 48 files changed, 1607 insertions(+), 1607 deletions(-) delete mode 100644 lib/crypto/e2ee.h delete mode 100644 lib/crypto/qolmaccount.cpp delete mode 100644 lib/crypto/qolmaccount.h delete mode 100644 lib/crypto/qolmerrors.cpp delete mode 100644 lib/crypto/qolmerrors.h delete mode 100644 lib/crypto/qolminboundsession.cpp delete mode 100644 lib/crypto/qolminboundsession.h delete mode 100644 lib/crypto/qolmmessage.cpp delete mode 100644 lib/crypto/qolmmessage.h delete mode 100644 lib/crypto/qolmoutboundsession.cpp delete mode 100644 lib/crypto/qolmoutboundsession.h delete mode 100644 lib/crypto/qolmsession.cpp delete mode 100644 lib/crypto/qolmsession.h delete mode 100644 lib/crypto/qolmutility.cpp delete mode 100644 lib/crypto/qolmutility.h delete mode 100644 lib/crypto/qolmutils.cpp delete mode 100644 lib/crypto/qolmutils.h create mode 100644 lib/e2ee/e2ee.h create mode 100644 lib/e2ee/qolmaccount.cpp create mode 100644 lib/e2ee/qolmaccount.h create mode 100644 lib/e2ee/qolmerrors.cpp create mode 100644 lib/e2ee/qolmerrors.h create mode 100644 lib/e2ee/qolminboundsession.cpp create mode 100644 lib/e2ee/qolminboundsession.h create mode 100644 lib/e2ee/qolmmessage.cpp create mode 100644 lib/e2ee/qolmmessage.h create mode 100644 lib/e2ee/qolmoutboundsession.cpp create mode 100644 lib/e2ee/qolmoutboundsession.h create mode 100644 lib/e2ee/qolmsession.cpp create mode 100644 lib/e2ee/qolmsession.h create mode 100644 lib/e2ee/qolmutility.cpp create mode 100644 lib/e2ee/qolmutility.h create mode 100644 lib/e2ee/qolmutils.cpp create mode 100644 lib/e2ee/qolmutils.h (limited to 'autotests/testolmutility.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f886094..a84a70fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -164,15 +164,15 @@ list(APPEND lib_SRCS ) if (${PROJECT_NAME}_ENABLE_E2EE) list(APPEND lib_SRCS - lib/crypto/qolmaccount.cpp - lib/crypto/qolmsession.cpp - lib/crypto/qolminboundsession.cpp - lib/crypto/qolmoutboundsession.cpp - lib/crypto/qolmutils.cpp - lib/crypto/qolmutility.cpp - lib/crypto/qolmerrors.cpp - lib/crypto/qolmsession.cpp - lib/crypto/qolmmessage.cpp + lib/e2ee/qolmaccount.cpp + lib/e2ee/qolmsession.cpp + lib/e2ee/qolminboundsession.cpp + lib/e2ee/qolmoutboundsession.cpp + lib/e2ee/qolmutils.cpp + lib/e2ee/qolmutility.cpp + lib/e2ee/qolmerrors.cpp + lib/e2ee/qolmsession.cpp + lib/e2ee/qolmmessage.cpp lib/encryptionmanager.cpp ) endif() diff --git a/autotests/testgroupsession.cpp b/autotests/testgroupsession.cpp index ea1bb4a9..afd5ef81 100644 --- a/autotests/testgroupsession.cpp +++ b/autotests/testgroupsession.cpp @@ -3,9 +3,9 @@ // SPDX-License-Identifier: LGPL-2.1-or-later #include "testgroupsession.h" -#include "crypto/qolminboundsession.h" -#include "crypto/qolmoutboundsession.h" -#include "crypto/qolmutils.h" +#include "e2ee/qolminboundsession.h" +#include "e2ee/qolmoutboundsession.h" +#include "e2ee/qolmutils.h" using namespace Quotient; diff --git a/autotests/testolmaccount.cpp b/autotests/testolmaccount.cpp index 4fd129b5..22c457aa 100644 --- a/autotests/testolmaccount.cpp +++ b/autotests/testolmaccount.cpp @@ -4,8 +4,8 @@ // SPDX-License-Identifier: LGPL-2.1-or-later #include "testolmaccount.h" -#include -#include +#include +#include #include #include #include diff --git a/autotests/testolmsession.cpp b/autotests/testolmsession.cpp index 00d76d4e..41baf8e3 100644 --- a/autotests/testolmsession.cpp +++ b/autotests/testolmsession.cpp @@ -2,7 +2,7 @@ // // SPDX-License-Identifier: LGPL-2.1-or-later -#include "crypto/qolmsession.h" +#include "e2ee/qolmsession.h" #include "testolmsession.h" using namespace Quotient; diff --git a/autotests/testolmutility.cpp b/autotests/testolmutility.cpp index 2eec7e00..bbf3a055 100644 --- a/autotests/testolmutility.cpp +++ b/autotests/testolmutility.cpp @@ -3,8 +3,8 @@ // SPDX-License-Identifier: LGPL-2.1-or-later #include "testolmutility.h" -#include "crypto/qolmaccount.h" -#include "crypto/qolmutility.h" +#include "e2ee/qolmaccount.h" +#include "e2ee/qolmutility.h" using namespace Quotient; diff --git a/lib/connection.cpp b/lib/connection.cpp index f344807e..c7591e43 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -38,8 +38,8 @@ #include "jobs/syncjob.h" #ifdef Quotient_E2EE_ENABLED -# include "crypto/qolmaccount.h" -# include "crypto/qolmutils.h" +# include "e2ee/qolmaccount.h" +# include "e2ee/qolmutils.h" #endif // Quotient_E2EE_ENABLED #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) diff --git a/lib/connection.h b/lib/connection.h index d2347d1d..3a12ec39 100644 --- a/lib/connection.h +++ b/lib/connection.h @@ -22,7 +22,7 @@ #include #ifdef Quotient_E2EE_ENABLED -#include "crypto/e2ee.h" +#include "e2ee/e2ee.h" #endif Q_DECLARE_METATYPE(Quotient::GetLoginFlowsJob::LoginFlow) diff --git a/lib/converters.cpp b/lib/converters.cpp index a3ac44c5..4136940f 100644 --- a/lib/converters.cpp +++ b/lib/converters.cpp @@ -4,7 +4,7 @@ #include "converters.h" #include -#include "crypto/e2ee.h" +#include "e2ee/e2ee.h" QJsonValue Quotient::JsonConverter::dump(const QVariant& v) { diff --git a/lib/crypto/e2ee.h b/lib/crypto/e2ee.h deleted file mode 100644 index 41cd2878..00000000 --- a/lib/crypto/e2ee.h +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-FileCopyrightText: 2019 Alexey Andreyev -// SPDX-FileCopyrightText: 2019 Kitsune Ral -// SPDX-FileCopyrightText: 2021 Carl Schwan -// SPDX-License-Identifier: LGPL-2.1-or-later - -#pragma once - -#include -#include -#include "converters.h" -#include - -#include -#include -#include -#include - -#include "util.h" - -namespace Quotient { - -inline const auto CiphertextKeyL = "ciphertext"_ls; -inline const auto SenderKeyKeyL = "sender_key"_ls; -inline const auto DeviceIdKeyL = "device_id"_ls; -inline const auto SessionIdKeyL = "session_id"_ls; - -inline const auto AlgorithmKeyL = "algorithm"_ls; -inline const auto RotationPeriodMsKeyL = "rotation_period_ms"_ls; -inline const auto RotationPeriodMsgsKeyL = "rotation_period_msgs"_ls; - -inline const auto AlgorithmKey = QStringLiteral("algorithm"); -inline const auto RotationPeriodMsKey = QStringLiteral("rotation_period_ms"); -inline const auto RotationPeriodMsgsKey = - QStringLiteral("rotation_period_msgs"); - -inline const auto Ed25519Key = QStringLiteral("ed25519"); -inline const auto Curve25519Key = QStringLiteral("curve25519"); -inline const auto SignedCurve25519Key = QStringLiteral("signed_curve25519"); -inline const auto OlmV1Curve25519AesSha2AlgoKey = - QStringLiteral("m.olm.v1.curve25519-aes-sha2"); -inline const auto MegolmV1AesSha2AlgoKey = - QStringLiteral("m.megolm.v1.aes-sha2"); -inline const QStringList SupportedAlgorithms = { OlmV1Curve25519AesSha2AlgoKey, - MegolmV1AesSha2AlgoKey }; -struct Unencrypted {}; -struct Encrypted { - QByteArray key; -}; - -using PicklingMode = std::variant; - -class QOlmSession; -using QOlmSessionPtr = std::unique_ptr; - -class QOlmInboundGroupSession; -using QOlmInboundGroupSessionPtr = std::unique_ptr; - -template struct overloaded : Ts... { using Ts::operator()...; }; -template overloaded(Ts...) -> overloaded; - -struct IdentityKeys -{ - QByteArray curve25519; - QByteArray ed25519; -}; - -//! Struct representing the one-time keys. -struct OneTimeKeys -{ - QMap> keys; - - //! Get the HashMap containing the curve25519 one-time keys. - QMap curve25519() const; - - //! Get a reference to the hashmap corresponding to given key type. - std::optional> get(QString keyType) const; -}; - -//! Struct representing the signed one-time keys. -class SignedOneTimeKey -{ -public: - SignedOneTimeKey() = default; - SignedOneTimeKey(const SignedOneTimeKey &) = default; - SignedOneTimeKey &operator=(const SignedOneTimeKey &) = default; - //! Required. The unpadded Base64-encoded 32-byte Curve25519 public key. - QString key; - - //! Required. Signatures of the key object. - //! The signature is calculated using the process described at Signing JSON. - QHash> signatures; -}; - - -template <> -struct JsonObjectConverter { - static void fillFrom(const QJsonObject& jo, - SignedOneTimeKey& result) - { - fromJson(jo.value("key"_ls), result.key); - fromJson(jo.value("signatures"_ls), result.signatures); - } - - static void dumpTo(QJsonObject &jo, const SignedOneTimeKey &result) - { - addParam<>(jo, QStringLiteral("key"), result.key); - addParam<>(jo, QStringLiteral("signatures"), result.signatures); - } -}; - -bool operator==(const IdentityKeys& lhs, const IdentityKeys& rhs); - -template -class asKeyValueRange -{ -public: - asKeyValueRange(T &data) - : m_data{data} - { - } - - auto begin() { return m_data.keyValueBegin(); } - - auto end() { return m_data.keyValueEnd(); } - -private: - T &m_data; -}; - -} // namespace Quotient - -Q_DECLARE_METATYPE(Quotient::SignedOneTimeKey) diff --git a/lib/crypto/qolmaccount.cpp b/lib/crypto/qolmaccount.cpp deleted file mode 100644 index 5c9f5db4..00000000 --- a/lib/crypto/qolmaccount.cpp +++ /dev/null @@ -1,330 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#include "qolmaccount.h" -#include "connection.h" -#include "csapi/keys.h" -#include "crypto/qolmutils.h" -#include "crypto/qolmutility.h" -#include -#include -#include -#include - -using namespace Quotient; - -QMap OneTimeKeys::curve25519() const -{ - return keys[QStringLiteral("curve25519")]; -} - -std::optional> OneTimeKeys::get(QString keyType) const -{ - if (!keys.contains(keyType)) { - return std::nullopt; - } - return keys[keyType]; -} - -bool operator==(const IdentityKeys& lhs, const IdentityKeys& rhs) -{ - return lhs.curve25519 == rhs.curve25519 &&& lhs.ed25519 == rhs.ed25519; -} - -// Convert olm error to enum -QOlmError lastError(OlmAccount *account) { - const std::string error_raw = olm_account_last_error(account); - - return fromString(error_raw); -} - -QByteArray getRandom(size_t bufferSize) -{ - QByteArray buffer(bufferSize, '0'); - std::generate(buffer.begin(), buffer.end(), std::rand); - return buffer; -} - -QOlmAccount::QOlmAccount(const QString &userId, const QString &deviceId, QObject *parent) - : QObject(parent) - , m_userId(userId) - , m_deviceId(deviceId) -{ -} - -QOlmAccount::~QOlmAccount() -{ - olm_clear_account(m_account); - delete[](reinterpret_cast(m_account)); -} - -void QOlmAccount::createNewAccount() -{ - m_account = olm_account(new uint8_t[olm_account_size()]); - size_t randomSize = olm_create_account_random_length(m_account); - QByteArray randomData = getRandom(randomSize); - const auto error = olm_create_account(m_account, randomData.data(), randomSize); - if (error == olm_error()) { - throw lastError(m_account); - } - Q_EMIT needsSave(); -} - -void QOlmAccount::unpickle(QByteArray &pickled, const PicklingMode &mode) -{ - m_account = olm_account(new uint8_t[olm_account_size()]); - const QByteArray key = toKey(mode); - const auto error = olm_unpickle_account(m_account, key.data(), key.length(), pickled.data(), pickled.size()); - if (error == olm_error()) { - qCWarning(E2EE) << "Failed to unpickle olm account"; - //TODO: Do something that is not dying - // Probably log the user out since we have no way of getting to the keys - //throw lastError(m_account); - } -} - -std::variant QOlmAccount::pickle(const PicklingMode &mode) -{ - const QByteArray key = toKey(mode); - const size_t pickleLength = olm_pickle_account_length(m_account); - QByteArray pickleBuffer(pickleLength, '0'); - const auto error = olm_pickle_account(m_account, key.data(), - key.length(), pickleBuffer.data(), pickleLength); - if (error == olm_error()) { - return lastError(m_account); - } - return pickleBuffer; -} - -IdentityKeys QOlmAccount::identityKeys() const -{ - const size_t keyLength = olm_account_identity_keys_length(m_account); - QByteArray keyBuffer(keyLength, '0'); - const auto error = olm_account_identity_keys(m_account, keyBuffer.data(), keyLength); - if (error == olm_error()) { - throw lastError(m_account); - } - const QJsonObject key = QJsonDocument::fromJson(keyBuffer).object(); - return IdentityKeys { - key.value(QStringLiteral("curve25519")).toString().toUtf8(), - key.value(QStringLiteral("ed25519")).toString().toUtf8() - }; -} - -QByteArray QOlmAccount::sign(const QByteArray &message) const -{ - QByteArray signatureBuffer(olm_account_signature_length(m_account), '0'); - - const auto error = olm_account_sign(m_account, message.data(), message.length(), - signatureBuffer.data(), signatureBuffer.length()); - - if (error == olm_error()) { - throw lastError(m_account); - } - return signatureBuffer; -} - -QByteArray QOlmAccount::sign(const QJsonObject &message) const -{ - return sign(QJsonDocument(message).toJson(QJsonDocument::Compact)); -} - -QByteArray QOlmAccount::signIdentityKeys() const -{ - const auto keys = identityKeys(); - QJsonObject body - { - {"algorithms", QJsonArray{"m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"}}, - {"user_id", m_userId}, - {"device_id", m_deviceId}, - {"keys", - QJsonObject{ - {QStringLiteral("curve25519:") + m_deviceId, QString::fromUtf8(keys.curve25519)}, - {QStringLiteral("ed25519:") + m_deviceId, QString::fromUtf8(keys.ed25519)} - } - } - }; - return sign(QJsonDocument(body).toJson(QJsonDocument::Compact)); - -} - -size_t QOlmAccount::maxNumberOfOneTimeKeys() const -{ - return olm_account_max_number_of_one_time_keys(m_account); -} - -size_t QOlmAccount::generateOneTimeKeys(size_t numberOfKeys) const -{ - const size_t randomLength = olm_account_generate_one_time_keys_random_length(m_account, numberOfKeys); - QByteArray randomBuffer = getRandom(randomLength); - const auto error = olm_account_generate_one_time_keys(m_account, numberOfKeys, randomBuffer.data(), randomLength); - - if (error == olm_error()) { - throw lastError(m_account); - } - Q_EMIT needsSave(); - return error; -} - -OneTimeKeys QOlmAccount::oneTimeKeys() const -{ - const size_t oneTimeKeyLength = olm_account_one_time_keys_length(m_account); - QByteArray oneTimeKeysBuffer(oneTimeKeyLength, '0'); - - const auto error = olm_account_one_time_keys(m_account, oneTimeKeysBuffer.data(), oneTimeKeyLength); - if (error == olm_error()) { - throw lastError(m_account); - } - const auto json = QJsonDocument::fromJson(oneTimeKeysBuffer).object(); - OneTimeKeys oneTimeKeys; - - for (const QString& key1 : json.keys()) { - auto oneTimeKeyObject = json[key1].toObject(); - auto keyMap = QMap(); - for (const QString &key2 : oneTimeKeyObject.keys()) { - keyMap[key2] = oneTimeKeyObject[key2].toString(); - } - oneTimeKeys.keys[key1] = keyMap; - } - return oneTimeKeys; -} - -QMap QOlmAccount::signOneTimeKeys(const OneTimeKeys &keys) const -{ - QMap signedOneTimeKeys; - for (const auto &keyid : keys.curve25519().keys()) { - const auto oneTimeKey = keys.curve25519()[keyid]; - QByteArray sign = signOneTimeKey(oneTimeKey); - signedOneTimeKeys["signed_curve25519:" + keyid] = signedOneTimeKey(oneTimeKey.toUtf8(), sign); - } - return signedOneTimeKeys; -} - -SignedOneTimeKey QOlmAccount::signedOneTimeKey(const QByteArray &key, const QString &signature) const -{ - SignedOneTimeKey sign{}; - sign.key = key; - sign.signatures = {{m_userId, {{"ed25519:" + m_deviceId, signature}}}}; - return sign; -} - -QByteArray QOlmAccount::signOneTimeKey(const QString &key) const -{ - QJsonDocument j(QJsonObject{{"key", key}}); - return sign(j.toJson(QJsonDocument::Compact)); -} - -std::optional QOlmAccount::removeOneTimeKeys(const QOlmSessionPtr &session) const -{ - const auto error = olm_remove_one_time_keys(m_account, session->raw()); - - if (error == olm_error()) { - return lastError(m_account); - } - Q_EMIT needsSave(); - return std::nullopt; -} - -OlmAccount *QOlmAccount::data() -{ - return m_account; -} - -DeviceKeys QOlmAccount::deviceKeys() const -{ - DeviceKeys deviceKeys; - deviceKeys.userId = m_userId; - deviceKeys.deviceId = m_deviceId; - deviceKeys.algorithms = QStringList {"m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"}; - - const auto idKeys = identityKeys(); - deviceKeys.keys["curve25519:" + m_deviceId] = idKeys.curve25519; - deviceKeys.keys["ed25519:" + m_deviceId] = idKeys.ed25519; - - const auto sign = signIdentityKeys(); - deviceKeys.signatures[m_userId]["ed25519:" + m_deviceId] = sign; - - return deviceKeys; -} - -UploadKeysJob *QOlmAccount::createUploadKeyRequest(const OneTimeKeys &oneTimeKeys) -{ - auto keys = deviceKeys(); - - if (oneTimeKeys.curve25519().isEmpty()) { - return new UploadKeysJob(keys); - } - - // Sign & append the one time keys. - auto temp = signOneTimeKeys(oneTimeKeys); - QHash oneTimeKeysSigned; - for (const auto &[keyId, key] : asKeyValueRange(temp)) { - oneTimeKeysSigned[keyId] = QVariant::fromValue(toJson(key)); - } - - return new UploadKeysJob(keys, oneTimeKeysSigned); -} - -std::variant QOlmAccount::createInboundSession(const QOlmMessage &preKeyMessage) -{ - Q_ASSERT(preKeyMessage.type() == QOlmMessage::PreKey); - return QOlmSession::createInboundSession(this, preKeyMessage); -} - -std::variant QOlmAccount::createInboundSessionFrom(const QByteArray &theirIdentityKey, const QOlmMessage &preKeyMessage) -{ - Q_ASSERT(preKeyMessage.type() == QOlmMessage::PreKey); - return QOlmSession::createInboundSessionFrom(this, theirIdentityKey, preKeyMessage); -} - -std::variant QOlmAccount::createOutboundSession(const QByteArray &theirIdentityKey, const QByteArray &theirOneTimeKey) -{ - return QOlmSession::createOutboundSession(this, theirIdentityKey, theirOneTimeKey); -} - -void QOlmAccount::markKeysAsPublished() -{ - olm_account_mark_keys_as_published(m_account); - Q_EMIT needsSave(); -} - -bool Quotient::verifyIdentitySignature(const DeviceKeys &deviceKeys, - const QString &deviceId, - const QString &userId) -{ - const auto signKeyId = "ed25519:" + deviceId; - const auto signingKey = deviceKeys.keys[signKeyId]; - const auto signature = deviceKeys.signatures[userId][signKeyId]; - - if (signature.isEmpty()) { - return false; - } - - return ed25519VerifySignature(signingKey, toJson(deviceKeys), signature); -} - -bool Quotient::ed25519VerifySignature(const QString &signingKey, - const QJsonObject &obj, - const QString &signature) -{ - if (signature.isEmpty()) { - return false; - } - QJsonObject obj1 = obj; - - obj1.remove("unsigned"); - obj1.remove("signatures"); - - auto canonicalJson = QJsonDocument(obj1).toJson(QJsonDocument::Compact); - - QByteArray signingKeyBuf = signingKey.toUtf8(); - QOlmUtility utility; - auto signatureBuf = signature.toUtf8(); - auto result = utility.ed25519Verify(signingKeyBuf, canonicalJson, signatureBuf); - if (std::holds_alternative(result)) { - return false; - } - - return std::get(result); -} diff --git a/lib/crypto/qolmaccount.h b/lib/crypto/qolmaccount.h deleted file mode 100644 index dd461e8b..00000000 --- a/lib/crypto/qolmaccount.h +++ /dev/null @@ -1,123 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - - -#pragma once - -#include "csapi/keys.h" -#include "crypto/e2ee.h" -#include "crypto/qolmerrors.h" -#include "crypto/qolmmessage.h" -#include "crypto/qolmsession.h" -#include - -struct OlmAccount; - -namespace Quotient { - -class QOlmSession; -class Connection; - -using QOlmSessionPtr = std::unique_ptr; - -//! An olm account manages all cryptographic keys used on a device. -//! \code{.cpp} -//! const auto olmAccount = new QOlmAccount(this); -//! \endcode -class QOlmAccount : public QObject -{ - Q_OBJECT -public: - QOlmAccount(const QString &userId, const QString &deviceId, QObject *parent = nullptr); - ~QOlmAccount(); - - //! Creates a new instance of OlmAccount. During the instantiation - //! the Ed25519 fingerprint key pair and the Curve25519 identity key - //! pair are generated. For more information see here. - //! This needs to be called before any other action or use unpickle() instead. - void createNewAccount(); - - //! Deserialises from encrypted Base64 that was previously obtained by pickling a `QOlmAccount`. - //! This needs to be called before any other action or use createNewAccount() instead. - void unpickle(QByteArray &pickled, const PicklingMode &mode); - - //! Serialises an OlmAccount to encrypted Base64. - std::variant pickle(const PicklingMode &mode); - - //! Returns the account's public identity keys already formatted as JSON - IdentityKeys identityKeys() const; - - //! Returns the signature of the supplied message. - QByteArray sign(const QByteArray &message) const; - QByteArray sign(const QJsonObject& message) const; - - //! Sign identity keys. - QByteArray signIdentityKeys() const; - - //! Maximum number of one time keys that this OlmAccount can - //! currently hold. - size_t maxNumberOfOneTimeKeys() const; - - //! Generates the supplied number of one time keys. - size_t generateOneTimeKeys(size_t numberOfKeys) const; - - //! Gets the OlmAccount's one time keys formatted as JSON. - OneTimeKeys oneTimeKeys() const; - - //! Sign all one time keys. - QMap signOneTimeKeys(const OneTimeKeys &keys) const; - - //! Sign one time key. - QByteArray signOneTimeKey(const QString &key) const; - - SignedOneTimeKey signedOneTimeKey(const QByteArray &key, const QString &signature) const; - - UploadKeysJob *createUploadKeyRequest(const OneTimeKeys &oneTimeKeys); - - DeviceKeys deviceKeys() const; - - //! Remove the one time key used to create the supplied session. - [[nodiscard]] std::optional removeOneTimeKeys(const QOlmSessionPtr &session) const; - - //! Creates an inbound session for sending/receiving messages from a received 'prekey' message. - //! - //! \param message An Olm pre-key message that was encrypted for this account. - std::variant createInboundSession(const QOlmMessage &preKeyMessage); - - //! Creates an inbound session for sending/receiving messages from a received 'prekey' message. - //! - //! \param theirIdentityKey - The identity key of the Olm account that - //! encrypted this Olm message. - std::variant createInboundSessionFrom(const QByteArray &theirIdentityKey, const QOlmMessage &preKeyMessage); - - //! Creates an outbound session for sending messages to a specific - /// identity and one time key. - std::variant createOutboundSession(const QByteArray &theirIdentityKey, const QByteArray &theirOneTimeKey); - - void markKeysAsPublished(); - - // HACK do not use directly - QOlmAccount(OlmAccount *account); - OlmAccount *data(); - -Q_SIGNALS: - void needsSave() const; - -private: - OlmAccount *m_account = nullptr; // owning - QString m_userId; - QString m_deviceId; -}; - -bool verifyIdentitySignature(const DeviceKeys &deviceKeys, - const QString &deviceId, - const QString &userId); - -//! checks if the signature is signed by the signing_key -bool ed25519VerifySignature(const QString &signingKey, - const QJsonObject &obj, - const QString &signature); - -} // namespace Quotient diff --git a/lib/crypto/qolmerrors.cpp b/lib/crypto/qolmerrors.cpp deleted file mode 100644 index 6db1803c..00000000 --- a/lib/crypto/qolmerrors.cpp +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - - -#include "qolmerrors.h" - -Quotient::QOlmError Quotient::fromString(const std::string &error_raw) { - if (!error_raw.compare("BAD_ACCOUNT_KEY")) { - return QOlmError::BadAccountKey; - } else if (!error_raw.compare("BAD_MESSAGE_KEY_ID")) { - return QOlmError::BadMessageKeyId; - } else if (!error_raw.compare("INVALID_BASE64")) { - return QOlmError::InvalidBase64; - } else if (!error_raw.compare("NOT_ENOUGH_RANDOM")) { - return QOlmError::NotEnoughRandom; - } else if (!error_raw.compare("OUTPUT_BUFFER_TOO_SMALL")) { - return QOlmError::OutputBufferTooSmall; - } else { - return QOlmError::Unknown; - } -} diff --git a/lib/crypto/qolmerrors.h b/lib/crypto/qolmerrors.h deleted file mode 100644 index f8390d2a..00000000 --- a/lib/crypto/qolmerrors.h +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#pragma once - -#include - -namespace Quotient { -//! All errors that could be caused by an operation regarding Olm -//! Errors are named exactly like the ones in libolm. -enum QOlmError -{ - BadAccountKey, - BadMessageFormat, - BadMessageKeyId, - BadMessageMac, - BadMessageVersion, - InvalidBase64, - NotEnoughRandom, - OutputBufferTooSmall, - UnknownMessageIndex, - Unknown, -}; - -QOlmError fromString(const std::string &error_raw); - -} //namespace Quotient diff --git a/lib/crypto/qolminboundsession.cpp b/lib/crypto/qolminboundsession.cpp deleted file mode 100644 index 31d699f1..00000000 --- a/lib/crypto/qolminboundsession.cpp +++ /dev/null @@ -1,153 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#include "crypto/qolminboundsession.h" -#include -#include -using namespace Quotient; - -QOlmError lastError(OlmInboundGroupSession *session) { - const std::string error_raw = olm_inbound_group_session_last_error(session); - - return fromString(error_raw); -} - -QOlmInboundGroupSession::QOlmInboundGroupSession(OlmInboundGroupSession *session) - : m_groupSession(session) -{ -} - -QOlmInboundGroupSession::~QOlmInboundGroupSession() -{ - olm_clear_inbound_group_session(m_groupSession); - //delete[](reinterpret_cast(m_groupSession)); -} - -std::unique_ptr QOlmInboundGroupSession::create(const QByteArray &key) -{ - const auto olmInboundGroupSession = olm_inbound_group_session(new uint8_t[olm_inbound_group_session_size()]); - const auto temp = key; - const auto error = olm_init_inbound_group_session(olmInboundGroupSession, - reinterpret_cast(temp.data()), temp.size()); - - if (error == olm_error()) { - throw lastError(olmInboundGroupSession); - } - - return std::make_unique(olmInboundGroupSession); -} - -std::unique_ptr QOlmInboundGroupSession::import(const QByteArray &key) -{ - const auto olmInboundGroupSession = olm_inbound_group_session(new uint8_t[olm_inbound_group_session_size()]); - QByteArray keyBuf = key; - - const auto error = olm_import_inbound_group_session(olmInboundGroupSession, - reinterpret_cast(keyBuf.data()), keyBuf.size()); - if (error == olm_error()) { - throw lastError(olmInboundGroupSession); - } - - return std::make_unique(olmInboundGroupSession); -} - -QByteArray toKey(const PicklingMode &mode) -{ - if (std::holds_alternative(mode)) { - return ""; - } - return std::get(mode).key; -} - -QByteArray QOlmInboundGroupSession::pickle(const PicklingMode &mode) const -{ - QByteArray pickledBuf(olm_pickle_inbound_group_session_length(m_groupSession), '0'); - const QByteArray key = toKey(mode); - const auto error = olm_pickle_inbound_group_session(m_groupSession, key.data(), key.length(), pickledBuf.data(), - pickledBuf.length()); - if (error == olm_error()) { - throw lastError(m_groupSession); - } - return pickledBuf; -} - -std::variant, QOlmError> QOlmInboundGroupSession::unpickle(const QByteArray &pickled, const PicklingMode &mode) -{ - QByteArray pickledBuf = pickled; - const auto groupSession = olm_inbound_group_session(new uint8_t[olm_inbound_group_session_size()]); - QByteArray key = toKey(mode); - const auto error = olm_unpickle_inbound_group_session(groupSession, key.data(), key.length(), - pickledBuf.data(), pickledBuf.size()); - if (error == olm_error()) { - return lastError(groupSession); - } - key.clear(); - - return std::make_unique(groupSession); -} - -std::variant, QOlmError> QOlmInboundGroupSession::decrypt(const QByteArray &message) -{ - // This is for capturing the output of olm_group_decrypt - uint32_t messageIndex = 0; - - // We need to clone the message because - // olm_decrypt_max_plaintext_length destroys the input buffer - QByteArray messageBuf(message.length(), '0'); - std::copy(message.begin(), message.end(), messageBuf.begin()); - - QByteArray plaintextBuf(olm_group_decrypt_max_plaintext_length(m_groupSession, - reinterpret_cast(messageBuf.data()), messageBuf.length()), '0'); - - messageBuf = QByteArray(message.length(), '0'); - std::copy(message.begin(), message.end(), messageBuf.begin()); - - const auto plaintextLen = olm_group_decrypt(m_groupSession, reinterpret_cast(messageBuf.data()), - messageBuf.length(), reinterpret_cast(plaintextBuf.data()), plaintextBuf.length(), &messageIndex); - - // Error code or plaintext length is returned - const auto decryptError = plaintextLen; - - if (decryptError == olm_error()) { - return lastError(m_groupSession); - } - - QByteArray output(plaintextLen, '0'); - std::memcpy(output.data(), plaintextBuf.data(), plaintextLen); - - return std::make_pair(QString(output), messageIndex); -} - -std::variant QOlmInboundGroupSession::exportSession(uint32_t messageIndex) -{ - const auto keyLength = olm_export_inbound_group_session_length(m_groupSession); - QByteArray keyBuf(keyLength, '0'); - const auto error = olm_export_inbound_group_session(m_groupSession, reinterpret_cast(keyBuf.data()), keyLength, messageIndex); - - if (error == olm_error()) { - return lastError(m_groupSession); - } - return keyBuf; -} - -uint32_t QOlmInboundGroupSession::firstKnownIndex() const -{ - return olm_inbound_group_session_first_known_index(m_groupSession); -} - -QByteArray QOlmInboundGroupSession::sessionId() const -{ - QByteArray sessionIdBuf(olm_inbound_group_session_id_length(m_groupSession), '0'); - const auto error = olm_inbound_group_session_id(m_groupSession, reinterpret_cast(sessionIdBuf.data()), - sessionIdBuf.length()); - if (error == olm_error()) { - throw lastError(m_groupSession); - } - return sessionIdBuf; -} - -bool QOlmInboundGroupSession::isVerified() const -{ - return olm_inbound_group_session_is_verified(m_groupSession) != 0; -} diff --git a/lib/crypto/qolminboundsession.h b/lib/crypto/qolminboundsession.h deleted file mode 100644 index 362e42ba..00000000 --- a/lib/crypto/qolminboundsession.h +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#pragma once - -#include -#include -#include -#include "olm/olm.h" -#include "crypto/qolmerrors.h" -#include "crypto/e2ee.h" - -namespace Quotient { - -//! An in-bound group session is responsible for decrypting incoming -//! communication in a Megolm session. -struct QOlmInboundGroupSession -{ -public: - ~QOlmInboundGroupSession(); - //! Creates a new instance of `OlmInboundGroupSession`. - static std::unique_ptr create(const QByteArray &key); - //! Import an inbound group session, from a previous export. - static std::unique_ptr import(const QByteArray &key); - //! Serialises an `OlmInboundGroupSession` to encrypted Base64. - QByteArray pickle(const PicklingMode &mode) const; - //! Deserialises from encrypted Base64 that was previously obtained by pickling - //! an `OlmInboundGroupSession`. - static std::variant, QOlmError> unpickle(const QByteArray &picked, const PicklingMode &mode); - //! Decrypts ciphertext received for this group session. - std::variant, QOlmError> decrypt(const QByteArray &message); - //! Export the base64-encoded ratchet key for this session, at the given index, - //! in a format which can be used by import. - std::variant exportSession(uint32_t messageIndex); - //! Get the first message index we know how to decrypt. - uint32_t firstKnownIndex() const; - //! Get a base64-encoded identifier for this session. - QByteArray sessionId() const; - bool isVerified() const; - QOlmInboundGroupSession(OlmInboundGroupSession *session); -private: - OlmInboundGroupSession *m_groupSession; -}; - -using QOlmInboundGroupSessionPtr = std::unique_ptr; -using OlmInboundGroupSessionPtr = std::unique_ptr; -} // namespace Quotient diff --git a/lib/crypto/qolmmessage.cpp b/lib/crypto/qolmmessage.cpp deleted file mode 100644 index 15008b75..00000000 --- a/lib/crypto/qolmmessage.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Alexey Andreyev -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#include "qolmmessage.h" - -using namespace Quotient; - -QOlmMessage::QOlmMessage(const QByteArray &ciphertext, QOlmMessage::Type type) - : QByteArray(std::move(ciphertext)) - , m_messageType(type) -{ - Q_ASSERT_X(!ciphertext.isEmpty(), "olm message", "Ciphertext is empty"); -} - -QOlmMessage::QOlmMessage(const QOlmMessage &message) - : QByteArray(message) - , m_messageType(message.type()) -{ -} - -QOlmMessage::Type QOlmMessage::type() const -{ - return m_messageType; -} - -QByteArray QOlmMessage::toCiphertext() const -{ - return QByteArray(*this); -} - -QOlmMessage QOlmMessage::fromCiphertext(const QByteArray &ciphertext) -{ - return QOlmMessage(ciphertext, QOlmMessage::General); -} diff --git a/lib/crypto/qolmmessage.h b/lib/crypto/qolmmessage.h deleted file mode 100644 index 52aba78c..00000000 --- a/lib/crypto/qolmmessage.h +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Alexey Andreyev -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#pragma once - -#include -#include - -namespace Quotient { - -/*! \brief A wrapper around an olm encrypted message - * - * This class encapsulates a Matrix olm encrypted message, - * passed in either of 2 forms: a general message or a pre-key message. - * - * The class provides functions to get a type and the ciphertext. - */ -class QOlmMessage : public QByteArray { - Q_GADGET -public: - enum Type { - General, - PreKey, - }; - Q_ENUM(Type) - - QOlmMessage() = default; - explicit QOlmMessage(const QByteArray &ciphertext, Type type = General); - explicit QOlmMessage(const QOlmMessage &message); - - static QOlmMessage fromCiphertext(const QByteArray &ciphertext); - - Q_INVOKABLE Type type() const; - Q_INVOKABLE QByteArray toCiphertext() const; - -private: - Type m_messageType = General; -}; - -} //namespace Quotient diff --git a/lib/crypto/qolmoutboundsession.cpp b/lib/crypto/qolmoutboundsession.cpp deleted file mode 100644 index bc572ba5..00000000 --- a/lib/crypto/qolmoutboundsession.cpp +++ /dev/null @@ -1,128 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#include "qolmoutboundsession.h" -#include "crypto/qolmutils.h" - -using namespace Quotient; - -QOlmError lastError(OlmOutboundGroupSession *session) { - const std::string error_raw = olm_outbound_group_session_last_error(session); - - return fromString(error_raw); -} - -QOlmOutboundGroupSession::QOlmOutboundGroupSession(OlmOutboundGroupSession *session) - : m_groupSession(session) -{ -} - -QOlmOutboundGroupSession::~QOlmOutboundGroupSession() -{ - olm_clear_outbound_group_session(m_groupSession); - delete[](reinterpret_cast(m_groupSession)); -} - -std::unique_ptr QOlmOutboundGroupSession::create() -{ - auto *olmOutboundGroupSession = olm_outbound_group_session(new uint8_t[olm_outbound_group_session_size()]); - const auto randomLength = olm_init_outbound_group_session_random_length(olmOutboundGroupSession); - QByteArray randomBuf = getRandom(randomLength); - - const auto error = olm_init_outbound_group_session(olmOutboundGroupSession, - reinterpret_cast(randomBuf.data()), randomBuf.length()); - - if (error == olm_error()) { - throw lastError(olmOutboundGroupSession); - } - - const auto keyMaxLength = olm_outbound_group_session_key_length(olmOutboundGroupSession); - QByteArray keyBuffer(keyMaxLength, '0'); - olm_outbound_group_session_key(olmOutboundGroupSession, reinterpret_cast(keyBuffer.data()), - keyMaxLength); - - randomBuf.clear(); - - return std::make_unique(olmOutboundGroupSession); -} - -std::variant QOlmOutboundGroupSession::pickle(const PicklingMode &mode) -{ - QByteArray pickledBuf(olm_pickle_outbound_group_session_length(m_groupSession), '0'); - QByteArray key = toKey(mode); - const auto error = olm_pickle_outbound_group_session(m_groupSession, key.data(), key.length(), - pickledBuf.data(), pickledBuf.length()); - - if (error == olm_error()) { - return lastError(m_groupSession); - } - - key.clear(); - - return pickledBuf; -} - - -std::variant, QOlmError> QOlmOutboundGroupSession::unpickle(QByteArray &pickled, const PicklingMode &mode) -{ - QByteArray pickledBuf = pickled; - auto *olmOutboundGroupSession = olm_outbound_group_session(new uint8_t[olm_outbound_group_session_size()]); - QByteArray key = toKey(mode); - const auto error = olm_unpickle_outbound_group_session(olmOutboundGroupSession, key.data(), key.length(), - pickled.data(), pickled.length()); - if (error == olm_error()) { - return lastError(olmOutboundGroupSession); - } - const auto idMaxLength = olm_outbound_group_session_id_length(olmOutboundGroupSession); - QByteArray idBuffer(idMaxLength, '0'); - olm_outbound_group_session_id(olmOutboundGroupSession, reinterpret_cast(idBuffer.data()), - idBuffer.length()); - - key.clear(); - return std::make_unique(olmOutboundGroupSession); -} - -std::variant QOlmOutboundGroupSession::encrypt(const QString &plaintext) -{ - QByteArray plaintextBuf = plaintext.toUtf8(); - const auto messageMaxLength = olm_group_encrypt_message_length(m_groupSession, plaintextBuf.length()); - QByteArray messageBuf(messageMaxLength, '0'); - const auto error = olm_group_encrypt(m_groupSession, reinterpret_cast(plaintextBuf.data()), - plaintextBuf.length(), reinterpret_cast(messageBuf.data()), messageBuf.length()); - - if (error == olm_error()) { - return lastError(m_groupSession); - } - - return messageBuf; -} - -uint32_t QOlmOutboundGroupSession::sessionMessageIndex() const -{ - return olm_outbound_group_session_message_index(m_groupSession); -} - -QByteArray QOlmOutboundGroupSession::sessionId() const -{ - const auto idMaxLength = olm_outbound_group_session_id_length(m_groupSession); - QByteArray idBuffer(idMaxLength, '0'); - const auto error = olm_outbound_group_session_id(m_groupSession, reinterpret_cast(idBuffer.data()), - idBuffer.length()); - if (error == olm_error()) { - throw lastError(m_groupSession); - } - return idBuffer; -} - -std::variant QOlmOutboundGroupSession::sessionKey() const -{ - const auto keyMaxLength = olm_outbound_group_session_key_length(m_groupSession); - QByteArray keyBuffer(keyMaxLength, '0'); - const auto error = olm_outbound_group_session_key(m_groupSession, reinterpret_cast(keyBuffer.data()), - keyMaxLength); - if (error == olm_error()) { - return lastError(m_groupSession); - } - return keyBuffer; -} diff --git a/lib/crypto/qolmoutboundsession.h b/lib/crypto/qolmoutboundsession.h deleted file mode 100644 index 4e06561e..00000000 --- a/lib/crypto/qolmoutboundsession.h +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - - -#pragma once - -#include "olm/olm.h" -#include "crypto/qolmerrors.h" -#include "crypto/e2ee.h" -#include - -namespace Quotient { - - -//! An out-bound group session is responsible for encrypting outgoing -//! communication in a Megolm session. -class QOlmOutboundGroupSession -{ -public: - ~QOlmOutboundGroupSession(); - //! Creates a new instance of `QOlmOutboundGroupSession`. - //! Throw OlmError on errors - static std::unique_ptr create(); - //! Serialises a `QOlmOutboundGroupSession` to encrypted Base64. - std::variant pickle(const PicklingMode &mode); - //! Deserialises from encrypted Base64 that was previously obtained by - //! pickling a `QOlmOutboundGroupSession`. - static std::variant, QOlmError> unpickle(QByteArray &pickled, const PicklingMode &mode); - //! Encrypts a plaintext message using the session. - std::variant encrypt(const QString &plaintext); - - //! Get the current message index for this session. - //! - //! Each message is sent with an increasing index; this returns the - //! index for the next message. - uint32_t sessionMessageIndex() const; - - //! Get a base64-encoded identifier for this session. - QByteArray sessionId() const; - - //! Get the base64-encoded current ratchet key for this session. - //! - //! Each message is sent with a different ratchet key. This function returns the - //! ratchet key that will be used for the next message. - std::variant sessionKey() const; - QOlmOutboundGroupSession(OlmOutboundGroupSession *groupSession); -private: - OlmOutboundGroupSession *m_groupSession; -}; - -using QOlmOutboundGroupSessionPtr = std::unique_ptr; -using OlmOutboundGroupSessionPtr = std::unique_ptr; -} diff --git a/lib/crypto/qolmsession.cpp b/lib/crypto/qolmsession.cpp deleted file mode 100644 index a0386613..00000000 --- a/lib/crypto/qolmsession.cpp +++ /dev/null @@ -1,253 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Alexey Andreyev -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#include "qolmsession.h" -#include "crypto/qolmutils.h" -#include "logging.h" -#include -#include - -using namespace Quotient; - -QOlmError lastError(OlmSession* session) { - const std::string error_raw = olm_session_last_error(session); - - return fromString(error_raw); -} - -Quotient::QOlmSession::~QOlmSession() -{ - olm_clear_session(m_session); - delete[](reinterpret_cast(m_session)); -} - -OlmSession* QOlmSession::create() -{ - return olm_session(new uint8_t[olm_session_size()]); -} - -std::variant QOlmSession::createInbound(QOlmAccount *account, const QOlmMessage &preKeyMessage, bool from, const QString &theirIdentityKey) -{ - if (preKeyMessage.type() != QOlmMessage::PreKey) { - qCCritical(E2EE) << "The message is not a pre-key in when creating inbound session" << BadMessageFormat; - } - - const auto olmSession = create(); - - QByteArray oneTimeKeyMessageBuf = preKeyMessage.toCiphertext(); - QByteArray theirIdentityKeyBuf = theirIdentityKey.toUtf8(); - size_t error = 0; - if (from) { - error = olm_create_inbound_session_from(olmSession, account->data(), theirIdentityKeyBuf.data(), theirIdentityKeyBuf.length(), oneTimeKeyMessageBuf.data(), oneTimeKeyMessageBuf.length()); - } else { - error = olm_create_inbound_session(olmSession, account->data(), oneTimeKeyMessageBuf.data(), oneTimeKeyMessageBuf.length()); - } - - if (error == olm_error()) { - const auto lastErr = lastError(olmSession); - qCWarning(E2EE) << "Error when creating inbound session" << lastErr; - return lastErr; - } - - return std::make_unique(olmSession); -} - -std::variant QOlmSession::createInboundSession(QOlmAccount *account, const QOlmMessage &preKeyMessage) -{ - return createInbound(account, preKeyMessage); -} - -std::variant QOlmSession::createInboundSessionFrom(QOlmAccount *account, const QString &theirIdentityKey, const QOlmMessage &preKeyMessage) -{ - return createInbound(account, preKeyMessage, true, theirIdentityKey); -} - -std::variant QOlmSession::createOutboundSession(QOlmAccount *account, const QString &theirIdentityKey, const QString &theirOneTimeKey) -{ - auto *olmOutboundSession = create(); - const auto randomLen = olm_create_outbound_session_random_length(olmOutboundSession); - QByteArray randomBuf = getRandom(randomLen); - - QByteArray theirIdentityKeyBuf = theirIdentityKey.toUtf8(); - QByteArray theirOneTimeKeyBuf = theirOneTimeKey.toUtf8(); - const auto error = olm_create_outbound_session(olmOutboundSession, - account->data(), - reinterpret_cast(theirIdentityKeyBuf.data()), theirIdentityKeyBuf.length(), - reinterpret_cast(theirOneTimeKeyBuf.data()), theirOneTimeKeyBuf.length(), - reinterpret_cast(randomBuf.data()), randomBuf.length()); - - if (error == olm_error()) { - const auto lastErr = lastError(olmOutboundSession); - if (lastErr == QOlmError::NotEnoughRandom) { - throw lastErr; - } - return lastErr; - } - - randomBuf.clear(); - return std::make_unique(olmOutboundSession); -} - -std::variant QOlmSession::pickle(const PicklingMode &mode) -{ - QByteArray pickledBuf(olm_pickle_session_length(m_session), '0'); - QByteArray key = toKey(mode); - const auto error = olm_pickle_session(m_session, key.data(), key.length(), - pickledBuf.data(), pickledBuf.length()); - - if (error == olm_error()) { - return lastError(m_session); - } - - key.clear(); - - return pickledBuf; -} - -std::variant QOlmSession::unpickle(const QByteArray &pickled, const PicklingMode &mode) -{ - QByteArray pickledBuf = pickled; - auto *olmSession = create(); - QByteArray key = toKey(mode); - const auto error = olm_unpickle_session(olmSession, key.data(), key.length(), - pickledBuf.data(), pickledBuf.length()); - if (error == olm_error()) { - return lastError(olmSession); - } - - key.clear(); - return std::make_unique(olmSession); -} - -QOlmMessage QOlmSession::encrypt(const QString &plaintext) -{ - QByteArray plaintextBuf = plaintext.toUtf8(); - const auto messageMaxLen = olm_encrypt_message_length(m_session, plaintextBuf.length()); - QByteArray messageBuf(messageMaxLen, '0'); - const auto messageType = encryptMessageType(); - const auto randomLen = olm_encrypt_random_length(m_session); - QByteArray randomBuf = getRandom(randomLen); - const auto error = olm_encrypt(m_session, - reinterpret_cast(plaintextBuf.data()), plaintextBuf.length(), - reinterpret_cast(randomBuf.data()), randomBuf.length(), - reinterpret_cast(messageBuf.data()), messageBuf.length()); - - if (error == olm_error()) { - throw lastError(m_session); - } - - return QOlmMessage(messageBuf, messageType); -} - -std::variant QOlmSession::decrypt(const QOlmMessage &message) const -{ - const auto messageType = message.type(); - const auto ciphertext = message.toCiphertext(); - const auto messageTypeValue = messageType == QOlmMessage::Type::General - ? OLM_MESSAGE_TYPE_MESSAGE : OLM_MESSAGE_TYPE_PRE_KEY; - - // We need to clone the message because - // olm_decrypt_max_plaintext_length destroys the input buffer - QByteArray messageBuf(ciphertext.length(), '0'); - std::copy(message.begin(), message.end(), messageBuf.begin()); - - const auto plaintextMaxLen = olm_decrypt_max_plaintext_length(m_session, messageTypeValue, - reinterpret_cast(messageBuf.data()), messageBuf.length()); - - if (plaintextMaxLen == olm_error()) { - return lastError(m_session); - } - - QByteArray plaintextBuf(plaintextMaxLen, '0'); - QByteArray messageBuf2(ciphertext.length(), '0'); - std::copy(message.begin(), message.end(), messageBuf2.begin()); - - const auto plaintextResultLen = olm_decrypt(m_session, messageTypeValue, - reinterpret_cast(messageBuf2.data()), messageBuf2.length(), - reinterpret_cast(plaintextBuf.data()), plaintextMaxLen); - - if (plaintextResultLen == olm_error()) { - const auto lastErr = lastError(m_session); - if (lastErr == QOlmError::OutputBufferTooSmall) { - throw lastErr; - } - return lastErr; - } - QByteArray output(plaintextResultLen, '0'); - std::memcpy(output.data(), plaintextBuf.data(), plaintextResultLen); - plaintextBuf.clear(); - return output; -} - -QOlmMessage::Type QOlmSession::encryptMessageType() -{ - const auto messageTypeResult = olm_encrypt_message_type(m_session); - if (messageTypeResult == olm_error()) { - throw lastError(m_session); - } - if (messageTypeResult == OLM_MESSAGE_TYPE_PRE_KEY) { - return QOlmMessage::PreKey; - } - return QOlmMessage::General; -} - -QByteArray QOlmSession::sessionId() const -{ - const auto idMaxLength = olm_session_id_length(m_session); - QByteArray idBuffer(idMaxLength, '0'); - const auto error = olm_session_id(m_session, reinterpret_cast(idBuffer.data()), - idBuffer.length()); - if (error == olm_error()) { - throw lastError(m_session); - } - return idBuffer; -} - -bool QOlmSession::hasReceivedMessage() const -{ - return olm_session_has_received_message(m_session); -} - -std::variant QOlmSession::matchesInboundSession(const QOlmMessage &preKeyMessage) const -{ - Q_ASSERT(preKeyMessage.type() == QOlmMessage::Type::PreKey); - QByteArray oneTimeKeyBuf(preKeyMessage.data()); - const auto matchesResult = olm_matches_inbound_session(m_session, oneTimeKeyBuf.data(), oneTimeKeyBuf.length()); - - if (matchesResult == olm_error()) { - return lastError(m_session); - } - switch (matchesResult) { - case 0: - return false; - case 1: - return true; - default: - return QOlmError::Unknown; - } -} -std::variant QOlmSession::matchesInboundSessionFrom(const QString &theirIdentityKey, const QOlmMessage &preKeyMessage) const -{ - const auto theirIdentityKeyBuf = theirIdentityKey.toUtf8(); - auto oneTimeKeyMessageBuf = preKeyMessage.toCiphertext(); - const auto error = olm_matches_inbound_session_from(m_session, theirIdentityKeyBuf.data(), theirIdentityKeyBuf.length(), - oneTimeKeyMessageBuf.data(), oneTimeKeyMessageBuf.length()); - - if (error == olm_error()) { - return lastError(m_session); - } - switch (error) { - case 0: - return false; - case 1: - return true; - default: - return QOlmError::Unknown; - } -} - -QOlmSession::QOlmSession(OlmSession *session) - : m_session(session) -{ -} diff --git a/lib/crypto/qolmsession.h b/lib/crypto/qolmsession.h deleted file mode 100644 index 711ca66b..00000000 --- a/lib/crypto/qolmsession.h +++ /dev/null @@ -1,76 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Alexey Andreyev -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#pragma once - -#include -#include // FIXME: OlmSession -#include "crypto/e2ee.h" -#include "crypto/qolmmessage.h" -#include "crypto/qolmerrors.h" -#include "crypto/qolmaccount.h" - -namespace Quotient { - -class QOlmAccount; -class QOlmSession; - - -//! Either an outbound or inbound session for secure communication. -class QOlmSession -{ -public: - ~QOlmSession(); - //! Creates an inbound session for sending/receiving messages from a received 'prekey' message. - static std::variant, QOlmError> createInboundSession(QOlmAccount *account, const QOlmMessage &preKeyMessage); - static std::variant, QOlmError> createInboundSessionFrom(QOlmAccount *account, const QString &theirIdentityKey, const QOlmMessage &preKeyMessage); - static std::variant, QOlmError> createOutboundSession(QOlmAccount *account, const QString &theirIdentityKey, const QString &theirOneTimeKey); - //! Serialises an `QOlmSession` to encrypted Base64. - std::variant pickle(const PicklingMode &mode); - //! Deserialises from encrypted Base64 that was previously obtained by pickling a `QOlmSession`. - static std::variant, QOlmError> unpickle(const QByteArray &pickled, const PicklingMode &mode); - //! Encrypts a plaintext message using the session. - QOlmMessage encrypt(const QString &plaintext); - - //! Decrypts a message using this session. Decoding is lossy, meaing if - //! the decrypted plaintext contains invalid UTF-8 symbols, they will - //! be returned as `U+FFFD` (�). - std::variant decrypt(const QOlmMessage &message) const; - - //! Get a base64-encoded identifier for this session. - QByteArray sessionId() const; - - //! The type of the next message that will be returned from encryption. - QOlmMessage::Type encryptMessageType(); - - //! Checker for any received messages for this session. - bool hasReceivedMessage() const; - - //! Checks if the 'prekey' message is for this in-bound session. - std::variant matchesInboundSession(const QOlmMessage &preKeyMessage) const; - - //! Checks if the 'prekey' message is for this in-bound session. - std::variant matchesInboundSessionFrom(const QString &theirIdentityKey, const QOlmMessage &preKeyMessage) const; - - friend bool operator<(const QOlmSession& lhs, const QOlmSession& rhs) - { - return lhs.sessionId() < rhs.sessionId(); - } - - friend bool operator<(const std::unique_ptr &lhs, const std::unique_ptr &rhs) { - return *lhs < *rhs; - } - - OlmSession *raw() const - { - return m_session; - } - QOlmSession(OlmSession* session); -private: - //! Helper function for creating new sessions and handling errors. - static OlmSession* create(); - static std::variant, QOlmError> createInbound(QOlmAccount *account, const QOlmMessage& preKeyMessage, bool from = false, const QString& theirIdentityKey = ""); - OlmSession* m_session; -}; -} //namespace Quotient diff --git a/lib/crypto/qolmutility.cpp b/lib/crypto/qolmutility.cpp deleted file mode 100644 index bb50b4d0..00000000 --- a/lib/crypto/qolmutility.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#include "crypto/qolmutility.h" -#include "olm/olm.h" -#include - -using namespace Quotient; - -// Convert olm error to enum -QOlmError lastError(OlmUtility *utility) { - const std::string error_raw = olm_utility_last_error(utility); - - return fromString(error_raw); -} - -QOlmUtility::QOlmUtility() -{ - auto utility = new uint8_t[olm_utility_size()]; - m_utility = olm_utility(utility); -} - -QOlmUtility::~QOlmUtility() -{ - olm_clear_utility(m_utility); - delete[](reinterpret_cast(m_utility)); -} - -QString QOlmUtility::sha256Bytes(const QByteArray &inputBuf) const -{ - const auto outputLen = olm_sha256_length(m_utility); - QByteArray outputBuf(outputLen, '0'); - olm_sha256(m_utility, inputBuf.data(), inputBuf.length(), - outputBuf.data(), outputBuf.length()); - - return QString::fromUtf8(outputBuf); -} - -QString QOlmUtility::sha256Utf8Msg(const QString &message) const -{ - return sha256Bytes(message.toUtf8()); -} - -std::variant QOlmUtility::ed25519Verify(const QByteArray &key, - const QByteArray &message, const QByteArray &signature) -{ - QByteArray signatureBuf(signature.length(), '0'); - std::copy(signature.begin(), signature.end(), signatureBuf.begin()); - - const auto ret = olm_ed25519_verify(m_utility, key.data(), key.size(), - message.data(), message.size(), (void *)signatureBuf.data(), signatureBuf.size()); - - const auto error = ret; - if (error == olm_error()) { - return lastError(m_utility); - } - - if (ret != 0) { - return false; - } - return true; -} diff --git a/lib/crypto/qolmutility.h b/lib/crypto/qolmutility.h deleted file mode 100644 index 5fd28dcc..00000000 --- a/lib/crypto/qolmutility.h +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#pragma once - -#include -#include -#include "crypto/qolmerrors.h" - -struct OlmUtility; - -namespace Quotient { - -class QOlmSession; -class Connection; - -//! Allows you to make use of crytographic hashing via SHA-2 and -//! verifying ed25519 signatures. -class QOlmUtility -{ -public: - QOlmUtility(); - ~QOlmUtility(); - - //! Returns a sha256 of the supplied byte slice. - QString sha256Bytes(const QByteArray &inputBuf) const; - - //! Convenience function that converts the UTF-8 message - //! to bytes and then calls `sha256Bytes()`, returning its output. - QString sha256Utf8Msg(const QString &message) const; - - //! Verify a ed25519 signature. - //! \param key QByteArray The public part of the ed25519 key that signed the message. - //! \param message QByteArray The message that was signed. - //! \param signature QByteArray The signature of the message. - std::variant ed25519Verify(const QByteArray &key, - const QByteArray &message, const QByteArray &signature); - - -private: - OlmUtility *m_utility; - -}; -} diff --git a/lib/crypto/qolmutils.cpp b/lib/crypto/qolmutils.cpp deleted file mode 100644 index cd5ac83c..00000000 --- a/lib/crypto/qolmutils.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#include "crypto/qolmutils.h" -#include -#include - -using namespace Quotient; - -QByteArray Quotient::toKey(const Quotient::PicklingMode &mode) -{ - if (std::holds_alternative(mode)) { - return {}; - } - return std::get(mode).key; -} - -QByteArray Quotient::getRandom(size_t bufferSize) -{ - QByteArray buffer(bufferSize, '0'); - RAND_bytes(reinterpret_cast(buffer.data()), buffer.size()); - return buffer; -} diff --git a/lib/crypto/qolmutils.h b/lib/crypto/qolmutils.h deleted file mode 100644 index 8b1c01ce..00000000 --- a/lib/crypto/qolmutils.h +++ /dev/null @@ -1,15 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Carl Schwan -// -// SPDX-License-Identifier: LGPL-2.1-or-later - -#pragma once - -#include - -#include "crypto/e2ee.h" - -namespace Quotient { -// Convert PicklingMode to key -QByteArray toKey(const PicklingMode &mode); -QByteArray getRandom(size_t bufferSize); -} diff --git a/lib/database.cpp b/lib/database.cpp index 153aab31..ec285d22 100644 --- a/lib/database.cpp +++ b/lib/database.cpp @@ -10,9 +10,9 @@ #include #include -#include "crypto/e2ee.h" -#include "crypto/qolmsession.h" -#include "crypto/qolminboundsession.h" +#include "e2ee/e2ee.h" +#include "e2ee/qolmsession.h" +#include "e2ee/qolminboundsession.h" //TODO: delete room specific data when leaving room diff --git a/lib/database.h b/lib/database.h index ed356820..8f8cd6cd 100644 --- a/lib/database.h +++ b/lib/database.h @@ -7,7 +7,7 @@ #include #include -#include "crypto/e2ee.h" +#include "e2ee/e2ee.h" namespace Quotient { class Database : public QObject diff --git a/lib/e2ee/e2ee.h b/lib/e2ee/e2ee.h new file mode 100644 index 00000000..41cd2878 --- /dev/null +++ b/lib/e2ee/e2ee.h @@ -0,0 +1,132 @@ +// SPDX-FileCopyrightText: 2019 Alexey Andreyev +// SPDX-FileCopyrightText: 2019 Kitsune Ral +// SPDX-FileCopyrightText: 2021 Carl Schwan +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include +#include +#include "converters.h" +#include + +#include +#include +#include +#include + +#include "util.h" + +namespace Quotient { + +inline const auto CiphertextKeyL = "ciphertext"_ls; +inline const auto SenderKeyKeyL = "sender_key"_ls; +inline const auto DeviceIdKeyL = "device_id"_ls; +inline const auto SessionIdKeyL = "session_id"_ls; + +inline const auto AlgorithmKeyL = "algorithm"_ls; +inline const auto RotationPeriodMsKeyL = "rotation_period_ms"_ls; +inline const auto RotationPeriodMsgsKeyL = "rotation_period_msgs"_ls; + +inline const auto AlgorithmKey = QStringLiteral("algorithm"); +inline const auto RotationPeriodMsKey = QStringLiteral("rotation_period_ms"); +inline const auto RotationPeriodMsgsKey = + QStringLiteral("rotation_period_msgs"); + +inline const auto Ed25519Key = QStringLiteral("ed25519"); +inline const auto Curve25519Key = QStringLiteral("curve25519"); +inline const auto SignedCurve25519Key = QStringLiteral("signed_curve25519"); +inline const auto OlmV1Curve25519AesSha2AlgoKey = + QStringLiteral("m.olm.v1.curve25519-aes-sha2"); +inline const auto MegolmV1AesSha2AlgoKey = + QStringLiteral("m.megolm.v1.aes-sha2"); +inline const QStringList SupportedAlgorithms = { OlmV1Curve25519AesSha2AlgoKey, + MegolmV1AesSha2AlgoKey }; +struct Unencrypted {}; +struct Encrypted { + QByteArray key; +}; + +using PicklingMode = std::variant; + +class QOlmSession; +using QOlmSessionPtr = std::unique_ptr; + +class QOlmInboundGroupSession; +using QOlmInboundGroupSessionPtr = std::unique_ptr; + +template struct overloaded : Ts... { using Ts::operator()...; }; +template overloaded(Ts...) -> overloaded; + +struct IdentityKeys +{ + QByteArray curve25519; + QByteArray ed25519; +}; + +//! Struct representing the one-time keys. +struct OneTimeKeys +{ + QMap> keys; + + //! Get the HashMap containing the curve25519 one-time keys. + QMap curve25519() const; + + //! Get a reference to the hashmap corresponding to given key type. + std::optional> get(QString keyType) const; +}; + +//! Struct representing the signed one-time keys. +class SignedOneTimeKey +{ +public: + SignedOneTimeKey() = default; + SignedOneTimeKey(const SignedOneTimeKey &) = default; + SignedOneTimeKey &operator=(const SignedOneTimeKey &) = default; + //! Required. The unpadded Base64-encoded 32-byte Curve25519 public key. + QString key; + + //! Required. Signatures of the key object. + //! The signature is calculated using the process described at Signing JSON. + QHash> signatures; +}; + + +template <> +struct JsonObjectConverter { + static void fillFrom(const QJsonObject& jo, + SignedOneTimeKey& result) + { + fromJson(jo.value("key"_ls), result.key); + fromJson(jo.value("signatures"_ls), result.signatures); + } + + static void dumpTo(QJsonObject &jo, const SignedOneTimeKey &result) + { + addParam<>(jo, QStringLiteral("key"), result.key); + addParam<>(jo, QStringLiteral("signatures"), result.signatures); + } +}; + +bool operator==(const IdentityKeys& lhs, const IdentityKeys& rhs); + +template +class asKeyValueRange +{ +public: + asKeyValueRange(T &data) + : m_data{data} + { + } + + auto begin() { return m_data.keyValueBegin(); } + + auto end() { return m_data.keyValueEnd(); } + +private: + T &m_data; +}; + +} // namespace Quotient + +Q_DECLARE_METATYPE(Quotient::SignedOneTimeKey) diff --git a/lib/e2ee/qolmaccount.cpp b/lib/e2ee/qolmaccount.cpp new file mode 100644 index 00000000..aaf51946 --- /dev/null +++ b/lib/e2ee/qolmaccount.cpp @@ -0,0 +1,330 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "qolmaccount.h" +#include "connection.h" +#include "csapi/keys.h" +#include "e2ee/qolmutils.h" +#include "e2ee/qolmutility.h" +#include +#include +#include +#include + +using namespace Quotient; + +QMap OneTimeKeys::curve25519() const +{ + return keys[QStringLiteral("curve25519")]; +} + +std::optional> OneTimeKeys::get(QString keyType) const +{ + if (!keys.contains(keyType)) { + return std::nullopt; + } + return keys[keyType]; +} + +bool operator==(const IdentityKeys& lhs, const IdentityKeys& rhs) +{ + return lhs.curve25519 == rhs.curve25519 &&& lhs.ed25519 == rhs.ed25519; +} + +// Convert olm error to enum +QOlmError lastError(OlmAccount *account) { + const std::string error_raw = olm_account_last_error(account); + + return fromString(error_raw); +} + +QByteArray getRandom(size_t bufferSize) +{ + QByteArray buffer(bufferSize, '0'); + std::generate(buffer.begin(), buffer.end(), std::rand); + return buffer; +} + +QOlmAccount::QOlmAccount(const QString &userId, const QString &deviceId, QObject *parent) + : QObject(parent) + , m_userId(userId) + , m_deviceId(deviceId) +{ +} + +QOlmAccount::~QOlmAccount() +{ + olm_clear_account(m_account); + delete[](reinterpret_cast(m_account)); +} + +void QOlmAccount::createNewAccount() +{ + m_account = olm_account(new uint8_t[olm_account_size()]); + size_t randomSize = olm_create_account_random_length(m_account); + QByteArray randomData = getRandom(randomSize); + const auto error = olm_create_account(m_account, randomData.data(), randomSize); + if (error == olm_error()) { + throw lastError(m_account); + } + Q_EMIT needsSave(); +} + +void QOlmAccount::unpickle(QByteArray &pickled, const PicklingMode &mode) +{ + m_account = olm_account(new uint8_t[olm_account_size()]); + const QByteArray key = toKey(mode); + const auto error = olm_unpickle_account(m_account, key.data(), key.length(), pickled.data(), pickled.size()); + if (error == olm_error()) { + qCWarning(E2EE) << "Failed to unpickle olm account"; + //TODO: Do something that is not dying + // Probably log the user out since we have no way of getting to the keys + //throw lastError(m_account); + } +} + +std::variant QOlmAccount::pickle(const PicklingMode &mode) +{ + const QByteArray key = toKey(mode); + const size_t pickleLength = olm_pickle_account_length(m_account); + QByteArray pickleBuffer(pickleLength, '0'); + const auto error = olm_pickle_account(m_account, key.data(), + key.length(), pickleBuffer.data(), pickleLength); + if (error == olm_error()) { + return lastError(m_account); + } + return pickleBuffer; +} + +IdentityKeys QOlmAccount::identityKeys() const +{ + const size_t keyLength = olm_account_identity_keys_length(m_account); + QByteArray keyBuffer(keyLength, '0'); + const auto error = olm_account_identity_keys(m_account, keyBuffer.data(), keyLength); + if (error == olm_error()) { + throw lastError(m_account); + } + const QJsonObject key = QJsonDocument::fromJson(keyBuffer).object(); + return IdentityKeys { + key.value(QStringLiteral("curve25519")).toString().toUtf8(), + key.value(QStringLiteral("ed25519")).toString().toUtf8() + }; +} + +QByteArray QOlmAccount::sign(const QByteArray &message) const +{ + QByteArray signatureBuffer(olm_account_signature_length(m_account), '0'); + + const auto error = olm_account_sign(m_account, message.data(), message.length(), + signatureBuffer.data(), signatureBuffer.length()); + + if (error == olm_error()) { + throw lastError(m_account); + } + return signatureBuffer; +} + +QByteArray QOlmAccount::sign(const QJsonObject &message) const +{ + return sign(QJsonDocument(message).toJson(QJsonDocument::Compact)); +} + +QByteArray QOlmAccount::signIdentityKeys() const +{ + const auto keys = identityKeys(); + QJsonObject body + { + {"algorithms", QJsonArray{"m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"}}, + {"user_id", m_userId}, + {"device_id", m_deviceId}, + {"keys", + QJsonObject{ + {QStringLiteral("curve25519:") + m_deviceId, QString::fromUtf8(keys.curve25519)}, + {QStringLiteral("ed25519:") + m_deviceId, QString::fromUtf8(keys.ed25519)} + } + } + }; + return sign(QJsonDocument(body).toJson(QJsonDocument::Compact)); + +} + +size_t QOlmAccount::maxNumberOfOneTimeKeys() const +{ + return olm_account_max_number_of_one_time_keys(m_account); +} + +size_t QOlmAccount::generateOneTimeKeys(size_t numberOfKeys) const +{ + const size_t randomLength = olm_account_generate_one_time_keys_random_length(m_account, numberOfKeys); + QByteArray randomBuffer = getRandom(randomLength); + const auto error = olm_account_generate_one_time_keys(m_account, numberOfKeys, randomBuffer.data(), randomLength); + + if (error == olm_error()) { + throw lastError(m_account); + } + Q_EMIT needsSave(); + return error; +} + +OneTimeKeys QOlmAccount::oneTimeKeys() const +{ + const size_t oneTimeKeyLength = olm_account_one_time_keys_length(m_account); + QByteArray oneTimeKeysBuffer(oneTimeKeyLength, '0'); + + const auto error = olm_account_one_time_keys(m_account, oneTimeKeysBuffer.data(), oneTimeKeyLength); + if (error == olm_error()) { + throw lastError(m_account); + } + const auto json = QJsonDocument::fromJson(oneTimeKeysBuffer).object(); + OneTimeKeys oneTimeKeys; + + for (const QString& key1 : json.keys()) { + auto oneTimeKeyObject = json[key1].toObject(); + auto keyMap = QMap(); + for (const QString &key2 : oneTimeKeyObject.keys()) { + keyMap[key2] = oneTimeKeyObject[key2].toString(); + } + oneTimeKeys.keys[key1] = keyMap; + } + return oneTimeKeys; +} + +QMap QOlmAccount::signOneTimeKeys(const OneTimeKeys &keys) const +{ + QMap signedOneTimeKeys; + for (const auto &keyid : keys.curve25519().keys()) { + const auto oneTimeKey = keys.curve25519()[keyid]; + QByteArray sign = signOneTimeKey(oneTimeKey); + signedOneTimeKeys["signed_curve25519:" + keyid] = signedOneTimeKey(oneTimeKey.toUtf8(), sign); + } + return signedOneTimeKeys; +} + +SignedOneTimeKey QOlmAccount::signedOneTimeKey(const QByteArray &key, const QString &signature) const +{ + SignedOneTimeKey sign{}; + sign.key = key; + sign.signatures = {{m_userId, {{"ed25519:" + m_deviceId, signature}}}}; + return sign; +} + +QByteArray QOlmAccount::signOneTimeKey(const QString &key) const +{ + QJsonDocument j(QJsonObject{{"key", key}}); + return sign(j.toJson(QJsonDocument::Compact)); +} + +std::optional QOlmAccount::removeOneTimeKeys(const QOlmSessionPtr &session) const +{ + const auto error = olm_remove_one_time_keys(m_account, session->raw()); + + if (error == olm_error()) { + return lastError(m_account); + } + Q_EMIT needsSave(); + return std::nullopt; +} + +OlmAccount *QOlmAccount::data() +{ + return m_account; +} + +DeviceKeys QOlmAccount::deviceKeys() const +{ + DeviceKeys deviceKeys; + deviceKeys.userId = m_userId; + deviceKeys.deviceId = m_deviceId; + deviceKeys.algorithms = QStringList {"m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"}; + + const auto idKeys = identityKeys(); + deviceKeys.keys["curve25519:" + m_deviceId] = idKeys.curve25519; + deviceKeys.keys["ed25519:" + m_deviceId] = idKeys.ed25519; + + const auto sign = signIdentityKeys(); + deviceKeys.signatures[m_userId]["ed25519:" + m_deviceId] = sign; + + return deviceKeys; +} + +UploadKeysJob *QOlmAccount::createUploadKeyRequest(const OneTimeKeys &oneTimeKeys) +{ + auto keys = deviceKeys(); + + if (oneTimeKeys.curve25519().isEmpty()) { + return new UploadKeysJob(keys); + } + + // Sign & append the one time keys. + auto temp = signOneTimeKeys(oneTimeKeys); + QHash oneTimeKeysSigned; + for (const auto &[keyId, key] : asKeyValueRange(temp)) { + oneTimeKeysSigned[keyId] = QVariant::fromValue(toJson(key)); + } + + return new UploadKeysJob(keys, oneTimeKeysSigned); +} + +std::variant QOlmAccount::createInboundSession(const QOlmMessage &preKeyMessage) +{ + Q_ASSERT(preKeyMessage.type() == QOlmMessage::PreKey); + return QOlmSession::createInboundSession(this, preKeyMessage); +} + +std::variant QOlmAccount::createInboundSessionFrom(const QByteArray &theirIdentityKey, const QOlmMessage &preKeyMessage) +{ + Q_ASSERT(preKeyMessage.type() == QOlmMessage::PreKey); + return QOlmSession::createInboundSessionFrom(this, theirIdentityKey, preKeyMessage); +} + +std::variant QOlmAccount::createOutboundSession(const QByteArray &theirIdentityKey, const QByteArray &theirOneTimeKey) +{ + return QOlmSession::createOutboundSession(this, theirIdentityKey, theirOneTimeKey); +} + +void QOlmAccount::markKeysAsPublished() +{ + olm_account_mark_keys_as_published(m_account); + Q_EMIT needsSave(); +} + +bool Quotient::verifyIdentitySignature(const DeviceKeys &deviceKeys, + const QString &deviceId, + const QString &userId) +{ + const auto signKeyId = "ed25519:" + deviceId; + const auto signingKey = deviceKeys.keys[signKeyId]; + const auto signature = deviceKeys.signatures[userId][signKeyId]; + + if (signature.isEmpty()) { + return false; + } + + return ed25519VerifySignature(signingKey, toJson(deviceKeys), signature); +} + +bool Quotient::ed25519VerifySignature(const QString &signingKey, + const QJsonObject &obj, + const QString &signature) +{ + if (signature.isEmpty()) { + return false; + } + QJsonObject obj1 = obj; + + obj1.remove("unsigned"); + obj1.remove("signatures"); + + auto canonicalJson = QJsonDocument(obj1).toJson(QJsonDocument::Compact); + + QByteArray signingKeyBuf = signingKey.toUtf8(); + QOlmUtility utility; + auto signatureBuf = signature.toUtf8(); + auto result = utility.ed25519Verify(signingKeyBuf, canonicalJson, signatureBuf); + if (std::holds_alternative(result)) { + return false; + } + + return std::get(result); +} diff --git a/lib/e2ee/qolmaccount.h b/lib/e2ee/qolmaccount.h new file mode 100644 index 00000000..00afc0e6 --- /dev/null +++ b/lib/e2ee/qolmaccount.h @@ -0,0 +1,123 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + + +#pragma once + +#include "csapi/keys.h" +#include "e2ee/e2ee.h" +#include "e2ee/qolmerrors.h" +#include "e2ee/qolmmessage.h" +#include "e2ee/qolmsession.h" +#include + +struct OlmAccount; + +namespace Quotient { + +class QOlmSession; +class Connection; + +using QOlmSessionPtr = std::unique_ptr; + +//! An olm account manages all cryptographic keys used on a device. +//! \code{.cpp} +//! const auto olmAccount = new QOlmAccount(this); +//! \endcode +class QOlmAccount : public QObject +{ + Q_OBJECT +public: + QOlmAccount(const QString &userId, const QString &deviceId, QObject *parent = nullptr); + ~QOlmAccount(); + + //! Creates a new instance of OlmAccount. During the instantiation + //! the Ed25519 fingerprint key pair and the Curve25519 identity key + //! pair are generated. For more information see here. + //! This needs to be called before any other action or use unpickle() instead. + void createNewAccount(); + + //! Deserialises from encrypted Base64 that was previously obtained by pickling a `QOlmAccount`. + //! This needs to be called before any other action or use createNewAccount() instead. + void unpickle(QByteArray &pickled, const PicklingMode &mode); + + //! Serialises an OlmAccount to encrypted Base64. + std::variant pickle(const PicklingMode &mode); + + //! Returns the account's public identity keys already formatted as JSON + IdentityKeys identityKeys() const; + + //! Returns the signature of the supplied message. + QByteArray sign(const QByteArray &message) const; + QByteArray sign(const QJsonObject& message) const; + + //! Sign identity keys. + QByteArray signIdentityKeys() const; + + //! Maximum number of one time keys that this OlmAccount can + //! currently hold. + size_t maxNumberOfOneTimeKeys() const; + + //! Generates the supplied number of one time keys. + size_t generateOneTimeKeys(size_t numberOfKeys) const; + + //! Gets the OlmAccount's one time keys formatted as JSON. + OneTimeKeys oneTimeKeys() const; + + //! Sign all one time keys. + QMap signOneTimeKeys(const OneTimeKeys &keys) const; + + //! Sign one time key. + QByteArray signOneTimeKey(const QString &key) const; + + SignedOneTimeKey signedOneTimeKey(const QByteArray &key, const QString &signature) const; + + UploadKeysJob *createUploadKeyRequest(const OneTimeKeys &oneTimeKeys); + + DeviceKeys deviceKeys() const; + + //! Remove the one time key used to create the supplied session. + [[nodiscard]] std::optional removeOneTimeKeys(const QOlmSessionPtr &session) const; + + //! Creates an inbound session for sending/receiving messages from a received 'prekey' message. + //! + //! \param message An Olm pre-key message that was encrypted for this account. + std::variant createInboundSession(const QOlmMessage &preKeyMessage); + + //! Creates an inbound session for sending/receiving messages from a received 'prekey' message. + //! + //! \param theirIdentityKey - The identity key of the Olm account that + //! encrypted this Olm message. + std::variant createInboundSessionFrom(const QByteArray &theirIdentityKey, const QOlmMessage &preKeyMessage); + + //! Creates an outbound session for sending messages to a specific + /// identity and one time key. + std::variant createOutboundSession(const QByteArray &theirIdentityKey, const QByteArray &theirOneTimeKey); + + void markKeysAsPublished(); + + // HACK do not use directly + QOlmAccount(OlmAccount *account); + OlmAccount *data(); + +Q_SIGNALS: + void needsSave() const; + +private: + OlmAccount *m_account = nullptr; // owning + QString m_userId; + QString m_deviceId; +}; + +bool verifyIdentitySignature(const DeviceKeys &deviceKeys, + const QString &deviceId, + const QString &userId); + +//! checks if the signature is signed by the signing_key +bool ed25519VerifySignature(const QString &signingKey, + const QJsonObject &obj, + const QString &signature); + +} // namespace Quotient diff --git a/lib/e2ee/qolmerrors.cpp b/lib/e2ee/qolmerrors.cpp new file mode 100644 index 00000000..6db1803c --- /dev/null +++ b/lib/e2ee/qolmerrors.cpp @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + + +#include "qolmerrors.h" + +Quotient::QOlmError Quotient::fromString(const std::string &error_raw) { + if (!error_raw.compare("BAD_ACCOUNT_KEY")) { + return QOlmError::BadAccountKey; + } else if (!error_raw.compare("BAD_MESSAGE_KEY_ID")) { + return QOlmError::BadMessageKeyId; + } else if (!error_raw.compare("INVALID_BASE64")) { + return QOlmError::InvalidBase64; + } else if (!error_raw.compare("NOT_ENOUGH_RANDOM")) { + return QOlmError::NotEnoughRandom; + } else if (!error_raw.compare("OUTPUT_BUFFER_TOO_SMALL")) { + return QOlmError::OutputBufferTooSmall; + } else { + return QOlmError::Unknown; + } +} diff --git a/lib/e2ee/qolmerrors.h b/lib/e2ee/qolmerrors.h new file mode 100644 index 00000000..f8390d2a --- /dev/null +++ b/lib/e2ee/qolmerrors.h @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include + +namespace Quotient { +//! All errors that could be caused by an operation regarding Olm +//! Errors are named exactly like the ones in libolm. +enum QOlmError +{ + BadAccountKey, + BadMessageFormat, + BadMessageKeyId, + BadMessageMac, + BadMessageVersion, + InvalidBase64, + NotEnoughRandom, + OutputBufferTooSmall, + UnknownMessageIndex, + Unknown, +}; + +QOlmError fromString(const std::string &error_raw); + +} //namespace Quotient diff --git a/lib/e2ee/qolminboundsession.cpp b/lib/e2ee/qolminboundsession.cpp new file mode 100644 index 00000000..9bf56b6c --- /dev/null +++ b/lib/e2ee/qolminboundsession.cpp @@ -0,0 +1,153 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "e2ee/qolminboundsession.h" +#include +#include +using namespace Quotient; + +QOlmError lastError(OlmInboundGroupSession *session) { + const std::string error_raw = olm_inbound_group_session_last_error(session); + + return fromString(error_raw); +} + +QOlmInboundGroupSession::QOlmInboundGroupSession(OlmInboundGroupSession *session) + : m_groupSession(session) +{ +} + +QOlmInboundGroupSession::~QOlmInboundGroupSession() +{ + olm_clear_inbound_group_session(m_groupSession); + //delete[](reinterpret_cast(m_groupSession)); +} + +std::unique_ptr QOlmInboundGroupSession::create(const QByteArray &key) +{ + const auto olmInboundGroupSession = olm_inbound_group_session(new uint8_t[olm_inbound_group_session_size()]); + const auto temp = key; + const auto error = olm_init_inbound_group_session(olmInboundGroupSession, + reinterpret_cast(temp.data()), temp.size()); + + if (error == olm_error()) { + throw lastError(olmInboundGroupSession); + } + + return std::make_unique(olmInboundGroupSession); +} + +std::unique_ptr QOlmInboundGroupSession::import(const QByteArray &key) +{ + const auto olmInboundGroupSession = olm_inbound_group_session(new uint8_t[olm_inbound_group_session_size()]); + QByteArray keyBuf = key; + + const auto error = olm_import_inbound_group_session(olmInboundGroupSession, + reinterpret_cast(keyBuf.data()), keyBuf.size()); + if (error == olm_error()) { + throw lastError(olmInboundGroupSession); + } + + return std::make_unique(olmInboundGroupSession); +} + +QByteArray toKey(const PicklingMode &mode) +{ + if (std::holds_alternative(mode)) { + return ""; + } + return std::get(mode).key; +} + +QByteArray QOlmInboundGroupSession::pickle(const PicklingMode &mode) const +{ + QByteArray pickledBuf(olm_pickle_inbound_group_session_length(m_groupSession), '0'); + const QByteArray key = toKey(mode); + const auto error = olm_pickle_inbound_group_session(m_groupSession, key.data(), key.length(), pickledBuf.data(), + pickledBuf.length()); + if (error == olm_error()) { + throw lastError(m_groupSession); + } + return pickledBuf; +} + +std::variant, QOlmError> QOlmInboundGroupSession::unpickle(const QByteArray &pickled, const PicklingMode &mode) +{ + QByteArray pickledBuf = pickled; + const auto groupSession = olm_inbound_group_session(new uint8_t[olm_inbound_group_session_size()]); + QByteArray key = toKey(mode); + const auto error = olm_unpickle_inbound_group_session(groupSession, key.data(), key.length(), + pickledBuf.data(), pickledBuf.size()); + if (error == olm_error()) { + return lastError(groupSession); + } + key.clear(); + + return std::make_unique(groupSession); +} + +std::variant, QOlmError> QOlmInboundGroupSession::decrypt(const QByteArray &message) +{ + // This is for capturing the output of olm_group_decrypt + uint32_t messageIndex = 0; + + // We need to clone the message because + // olm_decrypt_max_plaintext_length destroys the input buffer + QByteArray messageBuf(message.length(), '0'); + std::copy(message.begin(), message.end(), messageBuf.begin()); + + QByteArray plaintextBuf(olm_group_decrypt_max_plaintext_length(m_groupSession, + reinterpret_cast(messageBuf.data()), messageBuf.length()), '0'); + + messageBuf = QByteArray(message.length(), '0'); + std::copy(message.begin(), message.end(), messageBuf.begin()); + + const auto plaintextLen = olm_group_decrypt(m_groupSession, reinterpret_cast(messageBuf.data()), + messageBuf.length(), reinterpret_cast(plaintextBuf.data()), plaintextBuf.length(), &messageIndex); + + // Error code or plaintext length is returned + const auto decryptError = plaintextLen; + + if (decryptError == olm_error()) { + return lastError(m_groupSession); + } + + QByteArray output(plaintextLen, '0'); + std::memcpy(output.data(), plaintextBuf.data(), plaintextLen); + + return std::make_pair(QString(output), messageIndex); +} + +std::variant QOlmInboundGroupSession::exportSession(uint32_t messageIndex) +{ + const auto keyLength = olm_export_inbound_group_session_length(m_groupSession); + QByteArray keyBuf(keyLength, '0'); + const auto error = olm_export_inbound_group_session(m_groupSession, reinterpret_cast(keyBuf.data()), keyLength, messageIndex); + + if (error == olm_error()) { + return lastError(m_groupSession); + } + return keyBuf; +} + +uint32_t QOlmInboundGroupSession::firstKnownIndex() const +{ + return olm_inbound_group_session_first_known_index(m_groupSession); +} + +QByteArray QOlmInboundGroupSession::sessionId() const +{ + QByteArray sessionIdBuf(olm_inbound_group_session_id_length(m_groupSession), '0'); + const auto error = olm_inbound_group_session_id(m_groupSession, reinterpret_cast(sessionIdBuf.data()), + sessionIdBuf.length()); + if (error == olm_error()) { + throw lastError(m_groupSession); + } + return sessionIdBuf; +} + +bool QOlmInboundGroupSession::isVerified() const +{ + return olm_inbound_group_session_is_verified(m_groupSession) != 0; +} diff --git a/lib/e2ee/qolminboundsession.h b/lib/e2ee/qolminboundsession.h new file mode 100644 index 00000000..7d52991c --- /dev/null +++ b/lib/e2ee/qolminboundsession.h @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include +#include +#include +#include "olm/olm.h" +#include "e2ee/qolmerrors.h" +#include "e2ee/e2ee.h" + +namespace Quotient { + +//! An in-bound group session is responsible for decrypting incoming +//! communication in a Megolm session. +struct QOlmInboundGroupSession +{ +public: + ~QOlmInboundGroupSession(); + //! Creates a new instance of `OlmInboundGroupSession`. + static std::unique_ptr create(const QByteArray &key); + //! Import an inbound group session, from a previous export. + static std::unique_ptr import(const QByteArray &key); + //! Serialises an `OlmInboundGroupSession` to encrypted Base64. + QByteArray pickle(const PicklingMode &mode) const; + //! Deserialises from encrypted Base64 that was previously obtained by pickling + //! an `OlmInboundGroupSession`. + static std::variant, QOlmError> unpickle(const QByteArray &picked, const PicklingMode &mode); + //! Decrypts ciphertext received for this group session. + std::variant, QOlmError> decrypt(const QByteArray &message); + //! Export the base64-encoded ratchet key for this session, at the given index, + //! in a format which can be used by import. + std::variant exportSession(uint32_t messageIndex); + //! Get the first message index we know how to decrypt. + uint32_t firstKnownIndex() const; + //! Get a base64-encoded identifier for this session. + QByteArray sessionId() const; + bool isVerified() const; + QOlmInboundGroupSession(OlmInboundGroupSession *session); +private: + OlmInboundGroupSession *m_groupSession; +}; + +using QOlmInboundGroupSessionPtr = std::unique_ptr; +using OlmInboundGroupSessionPtr = std::unique_ptr; +} // namespace Quotient diff --git a/lib/e2ee/qolmmessage.cpp b/lib/e2ee/qolmmessage.cpp new file mode 100644 index 00000000..15008b75 --- /dev/null +++ b/lib/e2ee/qolmmessage.cpp @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2021 Alexey Andreyev +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "qolmmessage.h" + +using namespace Quotient; + +QOlmMessage::QOlmMessage(const QByteArray &ciphertext, QOlmMessage::Type type) + : QByteArray(std::move(ciphertext)) + , m_messageType(type) +{ + Q_ASSERT_X(!ciphertext.isEmpty(), "olm message", "Ciphertext is empty"); +} + +QOlmMessage::QOlmMessage(const QOlmMessage &message) + : QByteArray(message) + , m_messageType(message.type()) +{ +} + +QOlmMessage::Type QOlmMessage::type() const +{ + return m_messageType; +} + +QByteArray QOlmMessage::toCiphertext() const +{ + return QByteArray(*this); +} + +QOlmMessage QOlmMessage::fromCiphertext(const QByteArray &ciphertext) +{ + return QOlmMessage(ciphertext, QOlmMessage::General); +} diff --git a/lib/e2ee/qolmmessage.h b/lib/e2ee/qolmmessage.h new file mode 100644 index 00000000..52aba78c --- /dev/null +++ b/lib/e2ee/qolmmessage.h @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: 2021 Alexey Andreyev +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include +#include + +namespace Quotient { + +/*! \brief A wrapper around an olm encrypted message + * + * This class encapsulates a Matrix olm encrypted message, + * passed in either of 2 forms: a general message or a pre-key message. + * + * The class provides functions to get a type and the ciphertext. + */ +class QOlmMessage : public QByteArray { + Q_GADGET +public: + enum Type { + General, + PreKey, + }; + Q_ENUM(Type) + + QOlmMessage() = default; + explicit QOlmMessage(const QByteArray &ciphertext, Type type = General); + explicit QOlmMessage(const QOlmMessage &message); + + static QOlmMessage fromCiphertext(const QByteArray &ciphertext); + + Q_INVOKABLE Type type() const; + Q_INVOKABLE QByteArray toCiphertext() const; + +private: + Type m_messageType = General; +}; + +} //namespace Quotient diff --git a/lib/e2ee/qolmoutboundsession.cpp b/lib/e2ee/qolmoutboundsession.cpp new file mode 100644 index 00000000..88e6b2e1 --- /dev/null +++ b/lib/e2ee/qolmoutboundsession.cpp @@ -0,0 +1,128 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "e2ee/qolmoutboundsession.h" +#include "e2ee/qolmutils.h" + +using namespace Quotient; + +QOlmError lastError(OlmOutboundGroupSession *session) { + const std::string error_raw = olm_outbound_group_session_last_error(session); + + return fromString(error_raw); +} + +QOlmOutboundGroupSession::QOlmOutboundGroupSession(OlmOutboundGroupSession *session) + : m_groupSession(session) +{ +} + +QOlmOutboundGroupSession::~QOlmOutboundGroupSession() +{ + olm_clear_outbound_group_session(m_groupSession); + delete[](reinterpret_cast(m_groupSession)); +} + +std::unique_ptr QOlmOutboundGroupSession::create() +{ + auto *olmOutboundGroupSession = olm_outbound_group_session(new uint8_t[olm_outbound_group_session_size()]); + const auto randomLength = olm_init_outbound_group_session_random_length(olmOutboundGroupSession); + QByteArray randomBuf = getRandom(randomLength); + + const auto error = olm_init_outbound_group_session(olmOutboundGroupSession, + reinterpret_cast(randomBuf.data()), randomBuf.length()); + + if (error == olm_error()) { + throw lastError(olmOutboundGroupSession); + } + + const auto keyMaxLength = olm_outbound_group_session_key_length(olmOutboundGroupSession); + QByteArray keyBuffer(keyMaxLength, '0'); + olm_outbound_group_session_key(olmOutboundGroupSession, reinterpret_cast(keyBuffer.data()), + keyMaxLength); + + randomBuf.clear(); + + return std::make_unique(olmOutboundGroupSession); +} + +std::variant QOlmOutboundGroupSession::pickle(const PicklingMode &mode) +{ + QByteArray pickledBuf(olm_pickle_outbound_group_session_length(m_groupSession), '0'); + QByteArray key = toKey(mode); + const auto error = olm_pickle_outbound_group_session(m_groupSession, key.data(), key.length(), + pickledBuf.data(), pickledBuf.length()); + + if (error == olm_error()) { + return lastError(m_groupSession); + } + + key.clear(); + + return pickledBuf; +} + + +std::variant, QOlmError> QOlmOutboundGroupSession::unpickle(QByteArray &pickled, const PicklingMode &mode) +{ + QByteArray pickledBuf = pickled; + auto *olmOutboundGroupSession = olm_outbound_group_session(new uint8_t[olm_outbound_group_session_size()]); + QByteArray key = toKey(mode); + const auto error = olm_unpickle_outbound_group_session(olmOutboundGroupSession, key.data(), key.length(), + pickled.data(), pickled.length()); + if (error == olm_error()) { + return lastError(olmOutboundGroupSession); + } + const auto idMaxLength = olm_outbound_group_session_id_length(olmOutboundGroupSession); + QByteArray idBuffer(idMaxLength, '0'); + olm_outbound_group_session_id(olmOutboundGroupSession, reinterpret_cast(idBuffer.data()), + idBuffer.length()); + + key.clear(); + return std::make_unique(olmOutboundGroupSession); +} + +std::variant QOlmOutboundGroupSession::encrypt(const QString &plaintext) +{ + QByteArray plaintextBuf = plaintext.toUtf8(); + const auto messageMaxLength = olm_group_encrypt_message_length(m_groupSession, plaintextBuf.length()); + QByteArray messageBuf(messageMaxLength, '0'); + const auto error = olm_group_encrypt(m_groupSession, reinterpret_cast(plaintextBuf.data()), + plaintextBuf.length(), reinterpret_cast(messageBuf.data()), messageBuf.length()); + + if (error == olm_error()) { + return lastError(m_groupSession); + } + + return messageBuf; +} + +uint32_t QOlmOutboundGroupSession::sessionMessageIndex() const +{ + return olm_outbound_group_session_message_index(m_groupSession); +} + +QByteArray QOlmOutboundGroupSession::sessionId() const +{ + const auto idMaxLength = olm_outbound_group_session_id_length(m_groupSession); + QByteArray idBuffer(idMaxLength, '0'); + const auto error = olm_outbound_group_session_id(m_groupSession, reinterpret_cast(idBuffer.data()), + idBuffer.length()); + if (error == olm_error()) { + throw lastError(m_groupSession); + } + return idBuffer; +} + +std::variant QOlmOutboundGroupSession::sessionKey() const +{ + const auto keyMaxLength = olm_outbound_group_session_key_length(m_groupSession); + QByteArray keyBuffer(keyMaxLength, '0'); + const auto error = olm_outbound_group_session_key(m_groupSession, reinterpret_cast(keyBuffer.data()), + keyMaxLength); + if (error == olm_error()) { + return lastError(m_groupSession); + } + return keyBuffer; +} diff --git a/lib/e2ee/qolmoutboundsession.h b/lib/e2ee/qolmoutboundsession.h new file mode 100644 index 00000000..967f563f --- /dev/null +++ b/lib/e2ee/qolmoutboundsession.h @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + + +#pragma once + +#include "olm/olm.h" +#include "e2ee/qolmerrors.h" +#include "e2ee/e2ee.h" +#include + +namespace Quotient { + + +//! An out-bound group session is responsible for encrypting outgoing +//! communication in a Megolm session. +class QOlmOutboundGroupSession +{ +public: + ~QOlmOutboundGroupSession(); + //! Creates a new instance of `QOlmOutboundGroupSession`. + //! Throw OlmError on errors + static std::unique_ptr create(); + //! Serialises a `QOlmOutboundGroupSession` to encrypted Base64. + std::variant pickle(const PicklingMode &mode); + //! Deserialises from encrypted Base64 that was previously obtained by + //! pickling a `QOlmOutboundGroupSession`. + static std::variant, QOlmError> unpickle(QByteArray &pickled, const PicklingMode &mode); + //! Encrypts a plaintext message using the session. + std::variant encrypt(const QString &plaintext); + + //! Get the current message index for this session. + //! + //! Each message is sent with an increasing index; this returns the + //! index for the next message. + uint32_t sessionMessageIndex() const; + + //! Get a base64-encoded identifier for this session. + QByteArray sessionId() const; + + //! Get the base64-encoded current ratchet key for this session. + //! + //! Each message is sent with a different ratchet key. This function returns the + //! ratchet key that will be used for the next message. + std::variant sessionKey() const; + QOlmOutboundGroupSession(OlmOutboundGroupSession *groupSession); +private: + OlmOutboundGroupSession *m_groupSession; +}; + +using QOlmOutboundGroupSessionPtr = std::unique_ptr; +using OlmOutboundGroupSessionPtr = std::unique_ptr; +} diff --git a/lib/e2ee/qolmsession.cpp b/lib/e2ee/qolmsession.cpp new file mode 100644 index 00000000..69d8b431 --- /dev/null +++ b/lib/e2ee/qolmsession.cpp @@ -0,0 +1,253 @@ +// SPDX-FileCopyrightText: 2021 Alexey Andreyev +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "qolmsession.h" +#include "e2ee/qolmutils.h" +#include "logging.h" +#include +#include + +using namespace Quotient; + +QOlmError lastError(OlmSession* session) { + const std::string error_raw = olm_session_last_error(session); + + return fromString(error_raw); +} + +Quotient::QOlmSession::~QOlmSession() +{ + olm_clear_session(m_session); + delete[](reinterpret_cast(m_session)); +} + +OlmSession* QOlmSession::create() +{ + return olm_session(new uint8_t[olm_session_size()]); +} + +std::variant QOlmSession::createInbound(QOlmAccount *account, const QOlmMessage &preKeyMessage, bool from, const QString &theirIdentityKey) +{ + if (preKeyMessage.type() != QOlmMessage::PreKey) { + qCCritical(E2EE) << "The message is not a pre-key in when creating inbound session" << BadMessageFormat; + } + + const auto olmSession = create(); + + QByteArray oneTimeKeyMessageBuf = preKeyMessage.toCiphertext(); + QByteArray theirIdentityKeyBuf = theirIdentityKey.toUtf8(); + size_t error = 0; + if (from) { + error = olm_create_inbound_session_from(olmSession, account->data(), theirIdentityKeyBuf.data(), theirIdentityKeyBuf.length(), oneTimeKeyMessageBuf.data(), oneTimeKeyMessageBuf.length()); + } else { + error = olm_create_inbound_session(olmSession, account->data(), oneTimeKeyMessageBuf.data(), oneTimeKeyMessageBuf.length()); + } + + if (error == olm_error()) { + const auto lastErr = lastError(olmSession); + qCWarning(E2EE) << "Error when creating inbound session" << lastErr; + return lastErr; + } + + return std::make_unique(olmSession); +} + +std::variant QOlmSession::createInboundSession(QOlmAccount *account, const QOlmMessage &preKeyMessage) +{ + return createInbound(account, preKeyMessage); +} + +std::variant QOlmSession::createInboundSessionFrom(QOlmAccount *account, const QString &theirIdentityKey, const QOlmMessage &preKeyMessage) +{ + return createInbound(account, preKeyMessage, true, theirIdentityKey); +} + +std::variant QOlmSession::createOutboundSession(QOlmAccount *account, const QString &theirIdentityKey, const QString &theirOneTimeKey) +{ + auto *olmOutboundSession = create(); + const auto randomLen = olm_create_outbound_session_random_length(olmOutboundSession); + QByteArray randomBuf = getRandom(randomLen); + + QByteArray theirIdentityKeyBuf = theirIdentityKey.toUtf8(); + QByteArray theirOneTimeKeyBuf = theirOneTimeKey.toUtf8(); + const auto error = olm_create_outbound_session(olmOutboundSession, + account->data(), + reinterpret_cast(theirIdentityKeyBuf.data()), theirIdentityKeyBuf.length(), + reinterpret_cast(theirOneTimeKeyBuf.data()), theirOneTimeKeyBuf.length(), + reinterpret_cast(randomBuf.data()), randomBuf.length()); + + if (error == olm_error()) { + const auto lastErr = lastError(olmOutboundSession); + if (lastErr == QOlmError::NotEnoughRandom) { + throw lastErr; + } + return lastErr; + } + + randomBuf.clear(); + return std::make_unique(olmOutboundSession); +} + +std::variant QOlmSession::pickle(const PicklingMode &mode) +{ + QByteArray pickledBuf(olm_pickle_session_length(m_session), '0'); + QByteArray key = toKey(mode); + const auto error = olm_pickle_session(m_session, key.data(), key.length(), + pickledBuf.data(), pickledBuf.length()); + + if (error == olm_error()) { + return lastError(m_session); + } + + key.clear(); + + return pickledBuf; +} + +std::variant QOlmSession::unpickle(const QByteArray &pickled, const PicklingMode &mode) +{ + QByteArray pickledBuf = pickled; + auto *olmSession = create(); + QByteArray key = toKey(mode); + const auto error = olm_unpickle_session(olmSession, key.data(), key.length(), + pickledBuf.data(), pickledBuf.length()); + if (error == olm_error()) { + return lastError(olmSession); + } + + key.clear(); + return std::make_unique(olmSession); +} + +QOlmMessage QOlmSession::encrypt(const QString &plaintext) +{ + QByteArray plaintextBuf = plaintext.toUtf8(); + const auto messageMaxLen = olm_encrypt_message_length(m_session, plaintextBuf.length()); + QByteArray messageBuf(messageMaxLen, '0'); + const auto messageType = encryptMessageType(); + const auto randomLen = olm_encrypt_random_length(m_session); + QByteArray randomBuf = getRandom(randomLen); + const auto error = olm_encrypt(m_session, + reinterpret_cast(plaintextBuf.data()), plaintextBuf.length(), + reinterpret_cast(randomBuf.data()), randomBuf.length(), + reinterpret_cast(messageBuf.data()), messageBuf.length()); + + if (error == olm_error()) { + throw lastError(m_session); + } + + return QOlmMessage(messageBuf, messageType); +} + +std::variant QOlmSession::decrypt(const QOlmMessage &message) const +{ + const auto messageType = message.type(); + const auto ciphertext = message.toCiphertext(); + const auto messageTypeValue = messageType == QOlmMessage::Type::General + ? OLM_MESSAGE_TYPE_MESSAGE : OLM_MESSAGE_TYPE_PRE_KEY; + + // We need to clone the message because + // olm_decrypt_max_plaintext_length destroys the input buffer + QByteArray messageBuf(ciphertext.length(), '0'); + std::copy(message.begin(), message.end(), messageBuf.begin()); + + const auto plaintextMaxLen = olm_decrypt_max_plaintext_length(m_session, messageTypeValue, + reinterpret_cast(messageBuf.data()), messageBuf.length()); + + if (plaintextMaxLen == olm_error()) { + return lastError(m_session); + } + + QByteArray plaintextBuf(plaintextMaxLen, '0'); + QByteArray messageBuf2(ciphertext.length(), '0'); + std::copy(message.begin(), message.end(), messageBuf2.begin()); + + const auto plaintextResultLen = olm_decrypt(m_session, messageTypeValue, + reinterpret_cast(messageBuf2.data()), messageBuf2.length(), + reinterpret_cast(plaintextBuf.data()), plaintextMaxLen); + + if (plaintextResultLen == olm_error()) { + const auto lastErr = lastError(m_session); + if (lastErr == QOlmError::OutputBufferTooSmall) { + throw lastErr; + } + return lastErr; + } + QByteArray output(plaintextResultLen, '0'); + std::memcpy(output.data(), plaintextBuf.data(), plaintextResultLen); + plaintextBuf.clear(); + return output; +} + +QOlmMessage::Type QOlmSession::encryptMessageType() +{ + const auto messageTypeResult = olm_encrypt_message_type(m_session); + if (messageTypeResult == olm_error()) { + throw lastError(m_session); + } + if (messageTypeResult == OLM_MESSAGE_TYPE_PRE_KEY) { + return QOlmMessage::PreKey; + } + return QOlmMessage::General; +} + +QByteArray QOlmSession::sessionId() const +{ + const auto idMaxLength = olm_session_id_length(m_session); + QByteArray idBuffer(idMaxLength, '0'); + const auto error = olm_session_id(m_session, reinterpret_cast(idBuffer.data()), + idBuffer.length()); + if (error == olm_error()) { + throw lastError(m_session); + } + return idBuffer; +} + +bool QOlmSession::hasReceivedMessage() const +{ + return olm_session_has_received_message(m_session); +} + +std::variant QOlmSession::matchesInboundSession(const QOlmMessage &preKeyMessage) const +{ + Q_ASSERT(preKeyMessage.type() == QOlmMessage::Type::PreKey); + QByteArray oneTimeKeyBuf(preKeyMessage.data()); + const auto matchesResult = olm_matches_inbound_session(m_session, oneTimeKeyBuf.data(), oneTimeKeyBuf.length()); + + if (matchesResult == olm_error()) { + return lastError(m_session); + } + switch (matchesResult) { + case 0: + return false; + case 1: + return true; + default: + return QOlmError::Unknown; + } +} +std::variant QOlmSession::matchesInboundSessionFrom(const QString &theirIdentityKey, const QOlmMessage &preKeyMessage) const +{ + const auto theirIdentityKeyBuf = theirIdentityKey.toUtf8(); + auto oneTimeKeyMessageBuf = preKeyMessage.toCiphertext(); + const auto error = olm_matches_inbound_session_from(m_session, theirIdentityKeyBuf.data(), theirIdentityKeyBuf.length(), + oneTimeKeyMessageBuf.data(), oneTimeKeyMessageBuf.length()); + + if (error == olm_error()) { + return lastError(m_session); + } + switch (error) { + case 0: + return false; + case 1: + return true; + default: + return QOlmError::Unknown; + } +} + +QOlmSession::QOlmSession(OlmSession *session) + : m_session(session) +{ +} diff --git a/lib/e2ee/qolmsession.h b/lib/e2ee/qolmsession.h new file mode 100644 index 00000000..1febfa0f --- /dev/null +++ b/lib/e2ee/qolmsession.h @@ -0,0 +1,76 @@ +// SPDX-FileCopyrightText: 2021 Alexey Andreyev +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include +#include // FIXME: OlmSession +#include "e2ee/e2ee.h" +#include "e2ee/qolmmessage.h" +#include "e2ee/qolmerrors.h" +#include "e2ee/qolmaccount.h" + +namespace Quotient { + +class QOlmAccount; +class QOlmSession; + + +//! Either an outbound or inbound session for secure communication. +class QOlmSession +{ +public: + ~QOlmSession(); + //! Creates an inbound session for sending/receiving messages from a received 'prekey' message. + static std::variant, QOlmError> createInboundSession(QOlmAccount *account, const QOlmMessage &preKeyMessage); + static std::variant, QOlmError> createInboundSessionFrom(QOlmAccount *account, const QString &theirIdentityKey, const QOlmMessage &preKeyMessage); + static std::variant, QOlmError> createOutboundSession(QOlmAccount *account, const QString &theirIdentityKey, const QString &theirOneTimeKey); + //! Serialises an `QOlmSession` to encrypted Base64. + std::variant pickle(const PicklingMode &mode); + //! Deserialises from encrypted Base64 that was previously obtained by pickling a `QOlmSession`. + static std::variant, QOlmError> unpickle(const QByteArray &pickled, const PicklingMode &mode); + //! Encrypts a plaintext message using the session. + QOlmMessage encrypt(const QString &plaintext); + + //! Decrypts a message using this session. Decoding is lossy, meaing if + //! the decrypted plaintext contains invalid UTF-8 symbols, they will + //! be returned as `U+FFFD` (�). + std::variant decrypt(const QOlmMessage &message) const; + + //! Get a base64-encoded identifier for this session. + QByteArray sessionId() const; + + //! The type of the next message that will be returned from encryption. + QOlmMessage::Type encryptMessageType(); + + //! Checker for any received messages for this session. + bool hasReceivedMessage() const; + + //! Checks if the 'prekey' message is for this in-bound session. + std::variant matchesInboundSession(const QOlmMessage &preKeyMessage) const; + + //! Checks if the 'prekey' message is for this in-bound session. + std::variant matchesInboundSessionFrom(const QString &theirIdentityKey, const QOlmMessage &preKeyMessage) const; + + friend bool operator<(const QOlmSession& lhs, const QOlmSession& rhs) + { + return lhs.sessionId() < rhs.sessionId(); + } + + friend bool operator<(const std::unique_ptr &lhs, const std::unique_ptr &rhs) { + return *lhs < *rhs; + } + + OlmSession *raw() const + { + return m_session; + } + QOlmSession(OlmSession* session); +private: + //! Helper function for creating new sessions and handling errors. + static OlmSession* create(); + static std::variant, QOlmError> createInbound(QOlmAccount *account, const QOlmMessage& preKeyMessage, bool from = false, const QString& theirIdentityKey = ""); + OlmSession* m_session; +}; +} //namespace Quotient diff --git a/lib/e2ee/qolmutility.cpp b/lib/e2ee/qolmutility.cpp new file mode 100644 index 00000000..d0684055 --- /dev/null +++ b/lib/e2ee/qolmutility.cpp @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "e2ee/qolmutility.h" +#include "olm/olm.h" +#include + +using namespace Quotient; + +// Convert olm error to enum +QOlmError lastError(OlmUtility *utility) { + const std::string error_raw = olm_utility_last_error(utility); + + return fromString(error_raw); +} + +QOlmUtility::QOlmUtility() +{ + auto utility = new uint8_t[olm_utility_size()]; + m_utility = olm_utility(utility); +} + +QOlmUtility::~QOlmUtility() +{ + olm_clear_utility(m_utility); + delete[](reinterpret_cast(m_utility)); +} + +QString QOlmUtility::sha256Bytes(const QByteArray &inputBuf) const +{ + const auto outputLen = olm_sha256_length(m_utility); + QByteArray outputBuf(outputLen, '0'); + olm_sha256(m_utility, inputBuf.data(), inputBuf.length(), + outputBuf.data(), outputBuf.length()); + + return QString::fromUtf8(outputBuf); +} + +QString QOlmUtility::sha256Utf8Msg(const QString &message) const +{ + return sha256Bytes(message.toUtf8()); +} + +std::variant QOlmUtility::ed25519Verify(const QByteArray &key, + const QByteArray &message, const QByteArray &signature) +{ + QByteArray signatureBuf(signature.length(), '0'); + std::copy(signature.begin(), signature.end(), signatureBuf.begin()); + + const auto ret = olm_ed25519_verify(m_utility, key.data(), key.size(), + message.data(), message.size(), (void *)signatureBuf.data(), signatureBuf.size()); + + const auto error = ret; + if (error == olm_error()) { + return lastError(m_utility); + } + + if (ret != 0) { + return false; + } + return true; +} diff --git a/lib/e2ee/qolmutility.h b/lib/e2ee/qolmutility.h new file mode 100644 index 00000000..b360d625 --- /dev/null +++ b/lib/e2ee/qolmutility.h @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include +#include +#include "e2ee/qolmerrors.h" + +struct OlmUtility; + +namespace Quotient { + +class QOlmSession; +class Connection; + +//! Allows you to make use of crytographic hashing via SHA-2 and +//! verifying ed25519 signatures. +class QOlmUtility +{ +public: + QOlmUtility(); + ~QOlmUtility(); + + //! Returns a sha256 of the supplied byte slice. + QString sha256Bytes(const QByteArray &inputBuf) const; + + //! Convenience function that converts the UTF-8 message + //! to bytes and then calls `sha256Bytes()`, returning its output. + QString sha256Utf8Msg(const QString &message) const; + + //! Verify a ed25519 signature. + //! \param key QByteArray The public part of the ed25519 key that signed the message. + //! \param message QByteArray The message that was signed. + //! \param signature QByteArray The signature of the message. + std::variant ed25519Verify(const QByteArray &key, + const QByteArray &message, const QByteArray &signature); + + +private: + OlmUtility *m_utility; + +}; +} diff --git a/lib/e2ee/qolmutils.cpp b/lib/e2ee/qolmutils.cpp new file mode 100644 index 00000000..ce27710d --- /dev/null +++ b/lib/e2ee/qolmutils.cpp @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "e2ee/qolmutils.h" +#include +#include + +using namespace Quotient; + +QByteArray Quotient::toKey(const Quotient::PicklingMode &mode) +{ + if (std::holds_alternative(mode)) { + return {}; + } + return std::get(mode).key; +} + +QByteArray Quotient::getRandom(size_t bufferSize) +{ + QByteArray buffer(bufferSize, '0'); + RAND_bytes(reinterpret_cast(buffer.data()), buffer.size()); + return buffer; +} diff --git a/lib/e2ee/qolmutils.h b/lib/e2ee/qolmutils.h new file mode 100644 index 00000000..bbd71332 --- /dev/null +++ b/lib/e2ee/qolmutils.h @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include + +#include "e2ee/e2ee.h" + +namespace Quotient { +// Convert PicklingMode to key +QByteArray toKey(const PicklingMode &mode); +QByteArray getRandom(size_t bufferSize); +} diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp index e5fa978f..3d616965 100644 --- a/lib/encryptionmanager.cpp +++ b/lib/encryptionmanager.cpp @@ -6,7 +6,6 @@ #include "encryptionmanager.h" #include "connection.h" -#include "crypto/e2ee.h" #include "events/encryptedfile.h" #include "database.h" @@ -16,11 +15,12 @@ #include #include -#include "crypto/qolmaccount.h" -#include "crypto/qolmsession.h" -#include "crypto/qolmmessage.h" -#include "crypto/qolmerrors.h" -#include "crypto/qolmutils.h" +#include "e2ee/e2ee.h" +#include "e2ee/qolmaccount.h" +#include "e2ee/qolmsession.h" +#include "e2ee/qolmmessage.h" +#include "e2ee/qolmerrors.h" +#include "e2ee/qolmutils.h" #include #include diff --git a/lib/events/encryptedevent.h b/lib/events/encryptedevent.h index 28398827..4cc3bf8e 100644 --- a/lib/events/encryptedevent.h +++ b/lib/events/encryptedevent.h @@ -3,7 +3,7 @@ #pragma once -#include "crypto/e2ee.h" +#include "e2ee/e2ee.h" #include "roomevent.h" namespace Quotient { diff --git a/lib/events/encryptionevent.cpp b/lib/events/encryptionevent.cpp index d7bb953a..6272c668 100644 --- a/lib/events/encryptionevent.cpp +++ b/lib/events/encryptionevent.cpp @@ -4,7 +4,7 @@ #include "encryptionevent.h" -#include "crypto/e2ee.h" +#include "e2ee/e2ee.h" #include diff --git a/lib/room.cpp b/lib/room.cpp index 8181f16a..755f677a 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -12,7 +12,6 @@ #include "avatar.h" #include "connection.h" #include "converters.h" -#include "crypto/e2ee.h" #include "syncdata.h" #include "user.h" #include "eventstats.h" @@ -65,9 +64,10 @@ #include #ifdef Quotient_E2EE_ENABLED -#include "crypto/qolmaccount.h" -#include "crypto/qolmerrors.h" -#include "crypto/qolminboundsession.h" +#include "e2ee/e2ee.h" +#include "e2ee/qolmaccount.h" +#include "e2ee/qolmerrors.h" +#include "e2ee/qolminboundsession.h" #endif // Quotient_E2EE_ENABLED #include "database.h" -- cgit v1.2.3 From 27baabc9b8c9476fc550aef4462c193d5a9997a6 Mon Sep 17 00:00:00 2001 From: Tobias Fella <9750016+TobiasFella@users.noreply.github.com> Date: Sun, 30 Jan 2022 23:15:42 +0100 Subject: Apply suggestions from code review Co-authored-by: Alexey Rusakov --- autotests/testolmutility.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) (limited to 'autotests/testolmutility.cpp') diff --git a/autotests/testolmutility.cpp b/autotests/testolmutility.cpp index bbf3a055..d0476af0 100644 --- a/autotests/testolmutility.cpp +++ b/autotests/testolmutility.cpp @@ -47,14 +47,14 @@ void TestOlmUtility::canonicalJSON() void TestOlmUtility::verifySignedOneTimeKey() { - auto aliceOlm = std::make_shared("alice:matrix.org", "aliceDevice"); - aliceOlm->createNewAccount(); - aliceOlm->generateOneTimeKeys(1); - auto keys = aliceOlm->oneTimeKeys(); + QOlmAccount aliceOlm { "@alice:matrix.org", "aliceDevice" }; + aliceOlm.createNewAccount(); + aliceOlm.generateOneTimeKeys(1); + auto keys = aliceOlm.oneTimeKeys(); auto firstKey = keys.curve25519().keyValueBegin()->second; auto msgObj = QJsonObject({{"key", firstKey}}); - auto sig = aliceOlm->sign(msgObj); + auto sig = aliceOlm.sign(msgObj); auto msg = QJsonDocument(msgObj).toJson(QJsonDocument::Compact); @@ -66,8 +66,8 @@ void TestOlmUtility::verifySignedOneTimeKey() std::copy(sig.begin(), sig.end(), signatureBuf1.begin()); auto res = olm_ed25519_verify(utility, - aliceOlm->identityKeys().ed25519.data(), - aliceOlm->identityKeys().ed25519.size(), + aliceOlm.identityKeys().ed25519.data(), + aliceOlm.identityKeys().ed25519.size(), msg.data(), msg.size(), (void *)sig.data(), @@ -79,7 +79,7 @@ void TestOlmUtility::verifySignedOneTimeKey() delete[](reinterpret_cast(utility)); QOlmUtility utility2; - auto res2 = std::get(utility2.ed25519Verify(aliceOlm->identityKeys().ed25519, msg, signatureBuf1)); + auto res2 = std::get(utility2.ed25519Verify(aliceOlm.identityKeys().ed25519, msg, signatureBuf1)); //QCOMPARE(std::string(olm_utility_last_error(utility)), "SUCCESS"); QCOMPARE(res2, true); @@ -90,11 +90,11 @@ void TestOlmUtility::validUploadKeysRequest() const auto userId = QStringLiteral("@alice:matrix.org"); const auto deviceId = QStringLiteral("FKALSOCCC"); - auto alice = std::make_shared(userId, deviceId); - alice->createNewAccount(); - alice->generateOneTimeKeys(1); + QOlmAccount alice { userId, deviceId }; + alice.createNewAccount(); + alice.generateOneTimeKeys(1); - auto idSig = alice->signIdentityKeys(); + auto idSig = alice.signIdentityKeys(); QJsonObject body { @@ -103,8 +103,8 @@ void TestOlmUtility::validUploadKeysRequest() {"device_id", deviceId}, {"keys", QJsonObject{ - {QStringLiteral("curve25519:") + deviceId, QString::fromUtf8(alice->identityKeys().curve25519)}, - {QStringLiteral("ed25519:") + deviceId, QString::fromUtf8(alice->identityKeys().ed25519)} + {QStringLiteral("curve25519:") + deviceId, QString::fromUtf8(alice.identityKeys().curve25519)}, + {QStringLiteral("ed25519:") + deviceId, QString::fromUtf8(alice.identityKeys().ed25519)} } }, {"signatures", @@ -118,7 +118,7 @@ void TestOlmUtility::validUploadKeysRequest() } }; - DeviceKeys deviceKeys = alice->deviceKeys(); + DeviceKeys deviceKeys = alice.deviceKeys(); QCOMPARE(QJsonDocument(toJson(deviceKeys)).toJson(QJsonDocument::Compact), QJsonDocument(body).toJson(QJsonDocument::Compact)); -- cgit v1.2.3 From a42fd5d188e58da95169ea21b4cae23cccd26819 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Wed, 16 Feb 2022 17:28:29 +0100 Subject: TestOlmUtility: fix building with Qt 5.12 QKeyValueIterator::operator->() only arrived in Qt 5.15. --- autotests/testolmutility.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'autotests/testolmutility.cpp') diff --git a/autotests/testolmutility.cpp b/autotests/testolmutility.cpp index d0476af0..7a55b61e 100644 --- a/autotests/testolmutility.cpp +++ b/autotests/testolmutility.cpp @@ -52,7 +52,7 @@ void TestOlmUtility::verifySignedOneTimeKey() aliceOlm.generateOneTimeKeys(1); auto keys = aliceOlm.oneTimeKeys(); - auto firstKey = keys.curve25519().keyValueBegin()->second; + auto firstKey = *keys.curve25519().begin(); auto msgObj = QJsonObject({{"key", firstKey}}); auto sig = aliceOlm.sign(msgObj); -- cgit v1.2.3 From 6a7e21cfc542588d6bcf7efe839439bf9b632600 Mon Sep 17 00:00:00 2001 From: Tobias Fella Date: Thu, 17 Feb 2022 00:29:06 +0100 Subject: Don't create QApplications in tests --- autotests/testgroupsession.cpp | 2 +- autotests/testolmaccount.cpp | 2 +- autotests/testolmsession.cpp | 2 +- autotests/testolmutility.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'autotests/testolmutility.cpp') diff --git a/autotests/testgroupsession.cpp b/autotests/testgroupsession.cpp index 5024ccea..4425a006 100644 --- a/autotests/testgroupsession.cpp +++ b/autotests/testgroupsession.cpp @@ -51,4 +51,4 @@ void TestOlmSession::groupSessionCryptoValid() QCOMPARE(0, decryptionResult.second); } -QTEST_MAIN(TestOlmSession) +QTEST_APPLESS_MAIN(TestOlmSession) diff --git a/autotests/testolmaccount.cpp b/autotests/testolmaccount.cpp index 62b786d0..37d9f1d1 100644 --- a/autotests/testolmaccount.cpp +++ b/autotests/testolmaccount.cpp @@ -573,4 +573,4 @@ void TestOlmAccount::enableEncryption() QVERIFY(spy2.wait(10000)); } -QTEST_MAIN(TestOlmAccount) +QTEST_APPLESS_MAIN(TestOlmAccount) diff --git a/autotests/testolmsession.cpp b/autotests/testolmsession.cpp index 41baf8e3..12279cb6 100644 --- a/autotests/testolmsession.cpp +++ b/autotests/testolmsession.cpp @@ -77,4 +77,4 @@ void TestOlmSession::correctSessionOrdering() QCOMPARE(sessionList[2]->sessionId(), session1Id); } -QTEST_MAIN(TestOlmSession) +QTEST_APPLESS_MAIN(TestOlmSession) diff --git a/autotests/testolmutility.cpp b/autotests/testolmutility.cpp index 7a55b61e..ca5aa1fd 100644 --- a/autotests/testolmutility.cpp +++ b/autotests/testolmutility.cpp @@ -126,4 +126,4 @@ void TestOlmUtility::validUploadKeysRequest() QVERIFY(verifyIdentitySignature(deviceKeys, deviceId, userId)); } -QTEST_MAIN(TestOlmUtility) +QTEST_APPLESS_MAIN(TestOlmUtility) -- cgit v1.2.3 From 6be59df7036f8df385da29051c5320563518728a Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Fri, 18 Feb 2022 11:06:49 +0100 Subject: Use QCoreApplication in autotests QEventLoop refuses to work without an application object instance. --- autotests/testgroupsession.cpp | 2 +- autotests/testolmaccount.cpp | 2 +- autotests/testolmsession.cpp | 2 +- autotests/testolmutility.cpp | 3 +-- 4 files changed, 4 insertions(+), 5 deletions(-) (limited to 'autotests/testolmutility.cpp') diff --git a/autotests/testgroupsession.cpp b/autotests/testgroupsession.cpp index 4425a006..2b949578 100644 --- a/autotests/testgroupsession.cpp +++ b/autotests/testgroupsession.cpp @@ -51,4 +51,4 @@ void TestOlmSession::groupSessionCryptoValid() QCOMPARE(0, decryptionResult.second); } -QTEST_APPLESS_MAIN(TestOlmSession) +QTEST_GUILESS_MAIN(TestOlmSession) diff --git a/autotests/testolmaccount.cpp b/autotests/testolmaccount.cpp index 37d9f1d1..760276c8 100644 --- a/autotests/testolmaccount.cpp +++ b/autotests/testolmaccount.cpp @@ -573,4 +573,4 @@ void TestOlmAccount::enableEncryption() QVERIFY(spy2.wait(10000)); } -QTEST_APPLESS_MAIN(TestOlmAccount) +QTEST_GUILESS_MAIN(TestOlmAccount) diff --git a/autotests/testolmsession.cpp b/autotests/testolmsession.cpp index 12279cb6..5436c392 100644 --- a/autotests/testolmsession.cpp +++ b/autotests/testolmsession.cpp @@ -77,4 +77,4 @@ void TestOlmSession::correctSessionOrdering() QCOMPARE(sessionList[2]->sessionId(), session1Id); } -QTEST_APPLESS_MAIN(TestOlmSession) +QTEST_GUILESS_MAIN(TestOlmSession) diff --git a/autotests/testolmutility.cpp b/autotests/testolmutility.cpp index ca5aa1fd..b4532c8d 100644 --- a/autotests/testolmutility.cpp +++ b/autotests/testolmutility.cpp @@ -125,5 +125,4 @@ void TestOlmUtility::validUploadKeysRequest() QVERIFY(verifyIdentitySignature(fromJson(body), deviceId, userId)); QVERIFY(verifyIdentitySignature(deviceKeys, deviceId, userId)); } - -QTEST_APPLESS_MAIN(TestOlmUtility) +QTEST_GUILESS_MAIN(TestOlmUtility) -- cgit v1.2.3 From 3fcc0c5acb160364e819688cc67a8aaf814fafef Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Sat, 14 May 2022 20:51:05 +0200 Subject: Optimise #includes for QOlm* classes --- autotests/testolmutility.cpp | 2 ++ lib/connection.cpp | 15 ++++++++------- lib/e2ee/qolmaccount.cpp | 3 +++ lib/e2ee/qolmaccount.h | 6 ------ lib/e2ee/qolmsession.cpp | 7 ++++--- lib/e2ee/qolmsession.h | 7 ++----- 6 files changed, 19 insertions(+), 21 deletions(-) (limited to 'autotests/testolmutility.cpp') diff --git a/autotests/testolmutility.cpp b/autotests/testolmutility.cpp index b4532c8d..92b8b5b2 100644 --- a/autotests/testolmutility.cpp +++ b/autotests/testolmutility.cpp @@ -6,6 +6,8 @@ #include "e2ee/qolmaccount.h" #include "e2ee/qolmutility.h" +#include + using namespace Quotient; void TestOlmUtility::canonicalJSON() diff --git a/lib/connection.cpp b/lib/connection.cpp index bd4d9251..511b64e2 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -35,16 +35,17 @@ #include "jobs/syncjob.h" #ifdef Quotient_E2EE_ENABLED -# include "e2ee/qolmaccount.h" -# include "e2ee/qolmutils.h" # include "database.h" +# include "e2ee/qolmaccount.h" # include "e2ee/qolminboundsession.h" +# include "e2ee/qolmsession.h" +# include "e2ee/qolmutils.h" -#if QT_VERSION_MAJOR >= 6 -# include -#else -# include -#endif +# if QT_VERSION_MAJOR >= 6 +# include +# else +# include +# endif #endif // Quotient_E2EE_ENABLED #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) diff --git a/lib/e2ee/qolmaccount.cpp b/lib/e2ee/qolmaccount.cpp index 476a60bd..af9eb1bf 100644 --- a/lib/e2ee/qolmaccount.cpp +++ b/lib/e2ee/qolmaccount.cpp @@ -5,6 +5,7 @@ #include "qolmaccount.h" #include "connection.h" +#include "e2ee/qolmsession.h" #include "e2ee/qolmutility.h" #include "e2ee/qolmutils.h" @@ -12,6 +13,8 @@ #include +#include + using namespace Quotient; QHash OneTimeKeys::curve25519() const diff --git a/lib/e2ee/qolmaccount.h b/lib/e2ee/qolmaccount.h index 17f43f1a..08301998 100644 --- a/lib/e2ee/qolmaccount.h +++ b/lib/e2ee/qolmaccount.h @@ -9,18 +9,12 @@ #include "e2ee/e2ee.h" #include "e2ee/qolmerrors.h" #include "e2ee/qolmmessage.h" -#include "e2ee/qolmsession.h" #include struct OlmAccount; namespace Quotient { -class QOlmSession; -class Connection; - -using QOlmSessionPtr = std::unique_ptr; - //! An olm account manages all cryptographic keys used on a device. //! \code{.cpp} //! const auto olmAccount = new QOlmAccount(this); diff --git a/lib/e2ee/qolmsession.cpp b/lib/e2ee/qolmsession.cpp index e575ff39..531a6696 100644 --- a/lib/e2ee/qolmsession.cpp +++ b/lib/e2ee/qolmsession.cpp @@ -3,10 +3,12 @@ // SPDX-License-Identifier: LGPL-2.1-or-later #include "qolmsession.h" + #include "e2ee/qolmutils.h" #include "logging.h" + #include -#include +#include using namespace Quotient; @@ -247,5 +249,4 @@ std::variant QOlmSession::matchesInboundSessionFrom(const QStri QOlmSession::QOlmSession(OlmSession *session) : m_session(session) -{ -} +{} diff --git a/lib/e2ee/qolmsession.h b/lib/e2ee/qolmsession.h index f20c9837..4eb151b6 100644 --- a/lib/e2ee/qolmsession.h +++ b/lib/e2ee/qolmsession.h @@ -4,17 +4,14 @@ #pragma once -#include -#include // FIXME: OlmSession #include "e2ee/e2ee.h" #include "e2ee/qolmmessage.h" #include "e2ee/qolmerrors.h" #include "e2ee/qolmaccount.h" -namespace Quotient { +struct OlmSession; -class QOlmAccount; -class QOlmSession; +namespace Quotient { //! Either an outbound or inbound session for secure communication. class QUOTIENT_API QOlmSession -- cgit v1.2.3 From 79b3dba1ed4b6870c4e989ada88e33b1ce0ddc21 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Mon, 16 May 2022 10:41:54 +0200 Subject: QOlmExpected and associated refactoring As mentioned in the commit introducing `Expected`, `QOlmExpected` is simply an alias for `Expected`. This simplifies quite a few function signatures in `QOlm*` classes and collapses unwieldy `std::holds_alternative<>`/`std::get<>` constructs into a neat contextual bool cast and an invocation of `operator*` or `value()`/`error()` accessors that don't need to specify the type. While refactoring the code, I found a couple of cases of mismatching `uint32_t` and `qint32_t` in return values; a couple of cases where `decrypt()` returns `QString` which is in fact `QByteArray` (e.g., in `QOlmSession::decrypt()`); there's a repetitive algorithm in `Connection::Private::sessionDecryptPrekey()` and `sessionDecryptGeneral()` --- autotests/testgroupsession.cpp | 14 ++-- autotests/testolmaccount.cpp | 5 +- autotests/testolmsession.cpp | 14 ++-- autotests/testolmutility.cpp | 5 +- lib/connection.cpp | 136 ++++++++++++++++++++------------------- lib/database.cpp | 32 ++++----- lib/e2ee/e2ee.h | 8 +++ lib/e2ee/qolmaccount.cpp | 20 +++--- lib/e2ee/qolmaccount.h | 21 +++--- lib/e2ee/qolminboundsession.cpp | 10 +-- lib/e2ee/qolminboundsession.h | 14 ++-- lib/e2ee/qolmoutboundsession.cpp | 18 +++--- lib/e2ee/qolmoutboundsession.h | 19 +++--- lib/e2ee/qolmsession.cpp | 22 +++++-- lib/e2ee/qolmsession.h | 35 +++++----- lib/e2ee/qolmutility.cpp | 14 ++-- lib/e2ee/qolmutility.h | 7 +- lib/room.cpp | 17 +++-- 18 files changed, 215 insertions(+), 196 deletions(-) (limited to 'autotests/testolmutility.cpp') diff --git a/autotests/testgroupsession.cpp b/autotests/testgroupsession.cpp index 2566669e..3c329a8a 100644 --- a/autotests/testgroupsession.cpp +++ b/autotests/testgroupsession.cpp @@ -16,11 +16,11 @@ void TestGroupSession::groupSessionPicklingValid() QVERIFY(QByteArray::fromBase64(ogsId).size() > 0); QCOMPARE(0, ogs->sessionMessageIndex()); - auto ogsPickled = std::get(ogs->pickle(Unencrypted {})); - auto ogs2 = std::get(QOlmOutboundGroupSession::unpickle(ogsPickled, Unencrypted {})); + auto ogsPickled = ogs->pickle(Unencrypted {}).value(); + auto ogs2 = QOlmOutboundGroupSession::unpickle(ogsPickled, Unencrypted {}).value(); QCOMPARE(ogsId, ogs2->sessionId()); - auto igs = QOlmInboundGroupSession::create(std::get(ogs->sessionKey())); + auto igs = QOlmInboundGroupSession::create(ogs->sessionKey().value()); const auto igsId = igs->sessionId(); // ID is valid base64? QVERIFY(QByteArray::fromBase64(igsId).size() > 0); @@ -29,22 +29,22 @@ void TestGroupSession::groupSessionPicklingValid() QCOMPARE(0, igs->firstKnownIndex()); auto igsPickled = igs->pickle(Unencrypted {}); - igs = std::get(QOlmInboundGroupSession::unpickle(igsPickled, Unencrypted {})); + igs = QOlmInboundGroupSession::unpickle(igsPickled, Unencrypted {}).value(); QCOMPARE(igsId, igs->sessionId()); } void TestGroupSession::groupSessionCryptoValid() { auto ogs = QOlmOutboundGroupSession::create(); - auto igs = QOlmInboundGroupSession::create(std::get(ogs->sessionKey())); + auto igs = QOlmInboundGroupSession::create(ogs->sessionKey().value()); QCOMPARE(ogs->sessionId(), igs->sessionId()); const auto plainText = QStringLiteral("Hello world!"); - const auto ciphertext = std::get(ogs->encrypt(plainText)); + const auto ciphertext = ogs->encrypt(plainText).value(); // ciphertext valid base64? QVERIFY(QByteArray::fromBase64(ciphertext).size() > 0); - const auto decryptionResult = std::get>(igs->decrypt(ciphertext)); + const auto decryptionResult = igs->decrypt(ciphertext).value(); //// correct plaintext? QCOMPARE(plainText, decryptionResult.first); diff --git a/autotests/testolmaccount.cpp b/autotests/testolmaccount.cpp index 9989665a..e31ff6d3 100644 --- a/autotests/testolmaccount.cpp +++ b/autotests/testolmaccount.cpp @@ -21,7 +21,7 @@ void TestOlmAccount::pickleUnpickledTest() QOlmAccount olmAccount(QStringLiteral("@foo:bar.com"), QStringLiteral("QuotientTestDevice")); olmAccount.createNewAccount(); auto identityKeys = olmAccount.identityKeys(); - auto pickled = std::get(olmAccount.pickle(Unencrypted{})); + auto pickled = olmAccount.pickle(Unencrypted{}).value(); QOlmAccount olmAccount2(QStringLiteral("@foo:bar.com"), QStringLiteral("QuotientTestDevice")); olmAccount2.unpickle(pickled, Unencrypted{}); auto identityKeys2 = olmAccount2.identityKeys(); @@ -57,8 +57,7 @@ void TestOlmAccount::signatureValid() const auto identityKeys = olmAccount.identityKeys(); const auto ed25519Key = identityKeys.ed25519; const auto verify = utility.ed25519Verify(ed25519Key, message, signature); - QVERIFY(std::holds_alternative(verify)); - QVERIFY(std::get(verify) == true); + QVERIFY(verify.value_or(false)); } void TestOlmAccount::oneTimeKeysValid() diff --git a/autotests/testolmsession.cpp b/autotests/testolmsession.cpp index 2674b60b..182659e7 100644 --- a/autotests/testolmsession.cpp +++ b/autotests/testolmsession.cpp @@ -20,8 +20,8 @@ std::pair createSessionPair() const QByteArray oneTimeKeyA("WzsbsjD85iB1R32iWxfJdwkgmdz29ClMbJSJziECYwk"); const QByteArray identityKeyB("q/YhJtog/5VHCAS9rM9uUf6AaFk1yPe4GYuyUOXyQCg"); const QByteArray oneTimeKeyB("oWvzryma+B2onYjo3hM6A3Mgo/Yepm8HvgSvwZMTnjQ"); - auto outbound = std::get(accountA - .createOutboundSession(identityKeyB, oneTimeKeyB)); + auto outbound = + accountA.createOutboundSession(identityKeyB, oneTimeKeyB).value(); const auto preKey = outbound->encrypt(""); // Payload does not matter for PreKey @@ -29,7 +29,7 @@ std::pair createSessionPair() // We can't call QFail here because it's an helper function returning a value throw "Wrong first message type received, can't create session"; } - auto inbound = std::get(accountB.createInboundSession(preKey)); + auto inbound = accountB.createInboundSession(preKey).value(); return { std::move(inbound), std::move(outbound) }; } @@ -48,7 +48,7 @@ void TestOlmSession::olmEncryptDecrypt() QVERIFY(inboundSession->matchesInboundSession(m)); } - const auto decrypted = std::get(inboundSession->decrypt(encrypted)); + const auto decrypted = inboundSession->decrypt(encrypted).value(); QCOMPARE(decrypted, "Hello world!"); } @@ -56,11 +56,11 @@ void TestOlmSession::olmEncryptDecrypt() void TestOlmSession::correctSessionOrdering() { // n0W5IJ2ZmaI9FxKRj/wohUQ6WEU0SfoKsgKKHsr4VbM - auto session1 = std::get(QOlmSession::unpickle("7g5cfQRsDk2ROXf9S01n2leZiFRon+EbvXcMOADU0UGvlaV6t/0ihD2/0QGckDIvbmE1aV+PxB0zUtHXh99bI/60N+PWkCLA84jEY4sz3d45ui/TVoFGLDHlymKxvlj7XngXrbtlxSkVntsPzDiNpKEXCa26N2ubKpQ0fbjrV5gbBTYWfU04DXHPXFDTksxpNALYt/h0eVMVhf6hB0ZzpLBsOG0mpwkLufwub0CuDEDGGmRddz3TcNCLq5NnI8R9udDWvHAkTS1UTbHuIf/y6cZg875nJyXpAvd8/XhL8TOo8ot2sE1fElBa4vrH/m9rBQMC1GPkhLBIizmY44C+Sq9PQRnF+uCZ", Unencrypted{})); + auto session1 = QOlmSession::unpickle("7g5cfQRsDk2ROXf9S01n2leZiFRon+EbvXcMOADU0UGvlaV6t/0ihD2/0QGckDIvbmE1aV+PxB0zUtHXh99bI/60N+PWkCLA84jEY4sz3d45ui/TVoFGLDHlymKxvlj7XngXrbtlxSkVntsPzDiNpKEXCa26N2ubKpQ0fbjrV5gbBTYWfU04DXHPXFDTksxpNALYt/h0eVMVhf6hB0ZzpLBsOG0mpwkLufwub0CuDEDGGmRddz3TcNCLq5NnI8R9udDWvHAkTS1UTbHuIf/y6cZg875nJyXpAvd8/XhL8TOo8ot2sE1fElBa4vrH/m9rBQMC1GPkhLBIizmY44C+Sq9PQRnF+uCZ", Unencrypted{}).value(); // +9pHJhP3K4E5/2m8PYBPLh8pS9CJodwUOh8yz3mnmw0 - auto session2 = std::get(QOlmSession::unpickle("7g5cfQRsDk2ROXf9S01n2leZiFRon+EbvXcMOADU0UFD+q37/WlfTAzQsSjCdD07FcErZ4siEy5vpiB+pyO8i53ptZvb2qRvqNKFzPaXuu33PS2PBTmmnR+kJt+DgDNqWadyaj/WqEAejc7ALqSs5GuhbZtpoLe+lRSRK0rwVX3gzz4qrl8pm0pD5pSZAUWRXDRlieGWMclz68VUvnSaQH7ElTo4S634CJk+xQfFFCD26v0yONPSN6rwouS1cWPuG5jTlnV8vCFVTU2+lduKh54Ko6FUJ/ei4xR8Nk2duBGSc/TdllX9e2lDYHSUkWoD4ti5xsFioB8Blus7JK9BZfcmRmdlxIOD", Unencrypted {})); + auto session2 = QOlmSession::unpickle("7g5cfQRsDk2ROXf9S01n2leZiFRon+EbvXcMOADU0UFD+q37/WlfTAzQsSjCdD07FcErZ4siEy5vpiB+pyO8i53ptZvb2qRvqNKFzPaXuu33PS2PBTmmnR+kJt+DgDNqWadyaj/WqEAejc7ALqSs5GuhbZtpoLe+lRSRK0rwVX3gzz4qrl8pm0pD5pSZAUWRXDRlieGWMclz68VUvnSaQH7ElTo4S634CJk+xQfFFCD26v0yONPSN6rwouS1cWPuG5jTlnV8vCFVTU2+lduKh54Ko6FUJ/ei4xR8Nk2duBGSc/TdllX9e2lDYHSUkWoD4ti5xsFioB8Blus7JK9BZfcmRmdlxIOD", Unencrypted {}).value(); // MC7n8hX1l7WlC2/WJGHZinMocgiBZa4vwGAOredb/ME - auto session3 = std::get(QOlmSession::unpickle("7g5cfQRsDk2ROXf9S01n2leZiFRon+EbvXcMOADU0UGNk2TmVDJ95K0Nywf24FNklNVtXtFDiFPHFwNSmCbHNCp3hsGtZlt0AHUkMmL48XklLqzwtVk5/v2RRmSKR5LqYdIakrtuK/fY0ENhBZIbI1sRetaJ2KMbY9l6rCJNfFg8VhpZ4KTVvEZVuP9g/eZkCnP5NxzXiBRF6nfY3O/zhcKxa3acIqs6BMhyLsfuJ80t+hQ1HvVyuhBerGujdSDzV9tJ9SPidOwfYATk81LVF9hTmnI0KaZa7qCtFzhG0dU/Z3hIWH9HOaw1aSB/IPmughbwdJOwERyhuo3YHoznlQnJ7X252BlI", Unencrypted{})); + auto session3 = QOlmSession::unpickle("7g5cfQRsDk2ROXf9S01n2leZiFRon+EbvXcMOADU0UGNk2TmVDJ95K0Nywf24FNklNVtXtFDiFPHFwNSmCbHNCp3hsGtZlt0AHUkMmL48XklLqzwtVk5/v2RRmSKR5LqYdIakrtuK/fY0ENhBZIbI1sRetaJ2KMbY9l6rCJNfFg8VhpZ4KTVvEZVuP9g/eZkCnP5NxzXiBRF6nfY3O/zhcKxa3acIqs6BMhyLsfuJ80t+hQ1HvVyuhBerGujdSDzV9tJ9SPidOwfYATk81LVF9hTmnI0KaZa7qCtFzhG0dU/Z3hIWH9HOaw1aSB/IPmughbwdJOwERyhuo3YHoznlQnJ7X252BlI", Unencrypted{}).value(); const auto session1Id = session1->sessionId(); const auto session2Id = session2->sessionId(); diff --git a/autotests/testolmutility.cpp b/autotests/testolmutility.cpp index 92b8b5b2..c1323533 100644 --- a/autotests/testolmutility.cpp +++ b/autotests/testolmutility.cpp @@ -81,7 +81,10 @@ void TestOlmUtility::verifySignedOneTimeKey() delete[](reinterpret_cast(utility)); QOlmUtility utility2; - auto res2 = std::get(utility2.ed25519Verify(aliceOlm.identityKeys().ed25519, msg, signatureBuf1)); + auto res2 = + utility2 + .ed25519Verify(aliceOlm.identityKeys().ed25519, msg, signatureBuf1) + .value_or(false); //QCOMPARE(std::string(olm_utility_last_error(utility)), "SUCCESS"); QCOMPARE(res2, true); diff --git a/lib/connection.cpp b/lib/connection.cpp index 955b5b1a..2528b70b 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -212,82 +212,83 @@ public: void loadSessions() { olmSessions = q->database()->loadOlmSessions(q->picklingMode()); } - void saveSession(QOlmSessionPtr& session, const QString &senderKey) { - auto pickleResult = session->pickle(q->picklingMode()); - if (std::holds_alternative(pickleResult)) { - qCWarning(E2EE) << "Failed to pickle olm session. Error" << std::get(pickleResult); - return; - } - q->database()->saveOlmSession(senderKey, session->sessionId(), std::get(pickleResult), QDateTime::currentDateTime()); - } - - std::pair sessionDecryptPrekey(const QOlmMessage& message, - const QString& senderKey) + void saveSession(QOlmSession& session, const QString& senderKey) { - Q_ASSERT(message.type() == QOlmMessage::PreKey); - for(auto& session : olmSessions[senderKey]) { - if (session->matchesInboundSessionFrom(senderKey, message)) { - qCDebug(E2EE) << "Found inbound session"; - const auto result = session->decrypt(message); - if(std::holds_alternative(result)) { - q->database()->setOlmSessionLastReceived(QString(session->sessionId()), QDateTime::currentDateTime()); - return { std::get(result), session->sessionId() }; - } else { - qCDebug(E2EE) << "Failed to decrypt prekey message"; - return {}; - } - } - } - qCDebug(E2EE) << "Creating new inbound session"; - auto newSessionResult = olmAccount->createInboundSessionFrom(senderKey.toUtf8(), message); - if(std::holds_alternative(newSessionResult)) { - qCWarning(E2EE) << "Failed to create inbound session for" << senderKey << std::get(newSessionResult); - return {}; - } - auto newSession = std::move(std::get(newSessionResult)); - auto error = olmAccount->removeOneTimeKeys(newSession); - if (error) { - qWarning(E2EE) << "Failed to remove one time key for session" << newSession->sessionId(); - } - const auto result = newSession->decrypt(message); - QString sessionId = newSession->sessionId(); - saveSession(newSession, senderKey); - olmSessions[senderKey].push_back(std::move(newSession)); - if(std::holds_alternative(result)) { - return { std::get(result), sessionId }; - } else { - qCDebug(E2EE) << "Failed to decrypt prekey message with new session"; - return {}; - } + if (auto pickleResult = session.pickle(q->picklingMode())) + q->database()->saveOlmSession(senderKey, session.sessionId(), + *pickleResult, + QDateTime::currentDateTime()); + else + qCWarning(E2EE) << "Failed to pickle olm session. Error" + << pickleResult.error(); } - std::pair sessionDecryptGeneral(const QOlmMessage& message, const QString &senderKey) + + template + std::pair doDecryptMessage(const QOlmSession& session, + const QOlmMessage& message, + FnT&& andThen) const { - Q_ASSERT(message.type() == QOlmMessage::General); - for(auto& session : olmSessions[senderKey]) { - const auto result = session->decrypt(message); - if(std::holds_alternative(result)) { - q->database()->setOlmSessionLastReceived(QString(session->sessionId()), QDateTime::currentDateTime()); - return { std::get(result), session->sessionId() }; - } + const auto expectedMessage = session.decrypt(message); + if (expectedMessage) { + const auto result = + std::make_pair(*expectedMessage, session.sessionId()); + andThen(); + return result; } - qCWarning(E2EE) << "Failed to decrypt message"; + const auto errorLine = message.type() == QOlmMessage::PreKey + ? "Failed to decrypt prekey message:" + : "Failed to decrypt message:"; + qCDebug(E2EE) << errorLine << expectedMessage.error(); return {}; } std::pair sessionDecryptMessage( const QJsonObject& personalCipherObject, const QByteArray& senderKey) { + const auto msgType = static_cast( + personalCipherObject.value(TypeKeyL).toInt(-1)); + if (msgType != QOlmMessage::General && msgType != QOlmMessage::PreKey) { + qCWarning(E2EE) << "Olm message has incorrect type" << msgType; + return {}; + } QOlmMessage message { - personalCipherObject.value(BodyKeyL).toString().toLatin1(), - static_cast( - personalCipherObject.value(TypeKeyL).toInt(-1)) + personalCipherObject.value(BodyKeyL).toString().toLatin1(), msgType }; - if (message.type() == QOlmMessage::PreKey) - return sessionDecryptPrekey(message, senderKey); - if (message.type() == QOlmMessage::General) - return sessionDecryptGeneral(message, senderKey); - qCWarning(E2EE) << "Olm message has incorrect type" << message.type(); - return {}; + for (const auto& session : olmSessions[senderKey]) + if (msgType == QOlmMessage::General + || session->matchesInboundSessionFrom(senderKey, message)) { + return doDecryptMessage(*session, message, [this, &session] { + q->database()->setOlmSessionLastReceived( + session->sessionId(), QDateTime::currentDateTime()); + }); + } + + if (msgType == QOlmMessage::General) { + qCWarning(E2EE) << "Failed to decrypt message"; + return {}; + } + + qCDebug(E2EE) << "Creating new inbound session"; // Pre-key messages only + auto newSessionResult = + olmAccount->createInboundSessionFrom(senderKey, message); + if (!newSessionResult) { + qCWarning(E2EE) + << "Failed to create inbound session for" << senderKey + << "with error" << newSessionResult.error(); + return {}; + } + auto newSession = std::move(*newSessionResult); + auto error = olmAccount->removeOneTimeKeys(*newSession); + if (error) { + qWarning(E2EE) << "Failed to remove one time key for session" + << newSession->sessionId(); + // Keep going though + } + return doDecryptMessage( + *newSession, message, [this, &senderKey, &newSession] { + saveSession(*newSession, senderKey); + olmSessions[senderKey].push_back(std::move(newSession)); + }); } #endif @@ -2177,8 +2178,11 @@ void Connection::saveOlmAccount() { qCDebug(E2EE) << "Saving olm account"; #ifdef Quotient_E2EE_ENABLED - auto pickle = d->olmAccount->pickle(d->picklingMode); - d->database->setAccountPickle(std::get(pickle)); + if (const auto expectedPickle = d->olmAccount->pickle(d->picklingMode)) + d->database->setAccountPickle(*expectedPickle); + else + qCWarning(E2EE) << "Couldn't save Olm account pickle:" + << expectedPickle.error(); #endif } diff --git a/lib/database.cpp b/lib/database.cpp index 3189b3f1..e2e7acc9 100644 --- a/lib/database.cpp +++ b/lib/database.cpp @@ -184,12 +184,14 @@ UnorderedMap> Database::loadOlmSessions(con commit(); UnorderedMap> sessions; while (query.next()) { - auto session = QOlmSession::unpickle(query.value("pickle").toByteArray(), picklingMode); - if (std::holds_alternative(session)) { - qCWarning(E2EE) << "Failed to unpickle olm session"; - continue; - } - sessions[query.value("senderKey").toString()].push_back(std::move(std::get(session))); + if (auto expectedSession = + QOlmSession::unpickle(query.value("pickle").toByteArray(), + picklingMode)) { + sessions[query.value("senderKey").toString()].emplace_back( + std::move(*expectedSession)); + } else + qCWarning(E2EE) + << "Failed to unpickle olm session:" << expectedSession.error(); } return sessions; } @@ -203,15 +205,15 @@ UnorderedMap Database::loadMegolmSessions(c commit(); UnorderedMap sessions; while (query.next()) { - auto session = QOlmInboundGroupSession::unpickle(query.value("pickle").toByteArray(), picklingMode); - if (std::holds_alternative(session)) { - qCWarning(E2EE) << "Failed to unpickle megolm session"; - continue; - } - - sessions[query.value("sessionId").toString()] = std::move(std::get(session)); - sessions[query.value("sessionId").toString()]->setOlmSessionId(query.value("olmSessionId").toString()); - sessions[query.value("sessionId").toString()]->setSenderId(query.value("senderId").toString()); + if (auto expectedSession = QOlmInboundGroupSession::unpickle( + query.value("pickle").toByteArray(), picklingMode)) { + auto& sessionPtr = sessions[query.value("sessionId").toString()] = + std::move(*expectedSession); + sessionPtr->setOlmSessionId(query.value("olmSessionId").toString()); + sessionPtr->setSenderId(query.value("senderId").toString()); + } else + qCWarning(E2EE) << "Failed to unpickle megolm session:" + << expectedSession.error(); } return sessions; } diff --git a/lib/e2ee/e2ee.h b/lib/e2ee/e2ee.h index 268cb525..8e433d60 100644 --- a/lib/e2ee/e2ee.h +++ b/lib/e2ee/e2ee.h @@ -6,6 +6,8 @@ #pragma once #include "converters.h" +#include "expected.h" +#include "qolmerrors.h" #include "quotient_common.h" #include @@ -55,6 +57,12 @@ using QOlmSessionPtr = std::unique_ptr; class QOlmInboundGroupSession; using QOlmInboundGroupSessionPtr = std::unique_ptr; +class QOlmOutboundGroupSession; +using QOlmOutboundGroupSessionPtr = std::unique_ptr; + +template +using QOlmExpected = Expected; + struct IdentityKeys { QByteArray curve25519; diff --git a/lib/e2ee/qolmaccount.cpp b/lib/e2ee/qolmaccount.cpp index 3339ce46..72dddafb 100644 --- a/lib/e2ee/qolmaccount.cpp +++ b/lib/e2ee/qolmaccount.cpp @@ -73,7 +73,7 @@ void QOlmAccount::unpickle(QByteArray &pickled, const PicklingMode &mode) } } -std::variant QOlmAccount::pickle(const PicklingMode &mode) +QOlmExpected QOlmAccount::pickle(const PicklingMode &mode) { const QByteArray key = toKey(mode); const size_t pickleLength = olm_pickle_account_length(m_account); @@ -197,9 +197,9 @@ QByteArray QOlmAccount::signOneTimeKey(const QString &key) const } std::optional QOlmAccount::removeOneTimeKeys( - const QOlmSessionPtr& session) + const QOlmSession& session) { - const auto error = olm_remove_one_time_keys(m_account, session->raw()); + const auto error = olm_remove_one_time_keys(m_account, session.raw()); if (error == olm_error()) { return lastError(m_account); @@ -245,19 +245,19 @@ UploadKeysJob *QOlmAccount::createUploadKeyRequest(const OneTimeKeys &oneTimeKey return new UploadKeysJob(keys, oneTimeKeysSigned); } -std::variant QOlmAccount::createInboundSession(const QOlmMessage &preKeyMessage) +QOlmExpected QOlmAccount::createInboundSession(const QOlmMessage &preKeyMessage) { Q_ASSERT(preKeyMessage.type() == QOlmMessage::PreKey); return QOlmSession::createInboundSession(this, preKeyMessage); } -std::variant QOlmAccount::createInboundSessionFrom(const QByteArray &theirIdentityKey, const QOlmMessage &preKeyMessage) +QOlmExpected QOlmAccount::createInboundSessionFrom(const QByteArray &theirIdentityKey, const QOlmMessage &preKeyMessage) { Q_ASSERT(preKeyMessage.type() == QOlmMessage::PreKey); return QOlmSession::createInboundSessionFrom(this, theirIdentityKey, preKeyMessage); } -std::variant QOlmAccount::createOutboundSession(const QByteArray &theirIdentityKey, const QByteArray &theirOneTimeKey) +QOlmExpected QOlmAccount::createOutboundSession(const QByteArray &theirIdentityKey, const QByteArray &theirOneTimeKey) { return QOlmSession::createOutboundSession(this, theirIdentityKey, theirOneTimeKey); } @@ -296,10 +296,6 @@ bool Quotient::ed25519VerifySignature(const QString& signingKey, QByteArray signingKeyBuf = signingKey.toUtf8(); QOlmUtility utility; auto signatureBuf = signature.toUtf8(); - auto result = utility.ed25519Verify(signingKeyBuf, canonicalJson, signatureBuf); - if (std::holds_alternative(result)) { - return false; - } - - return std::get(result); + return utility.ed25519Verify(signingKeyBuf, canonicalJson, signatureBuf) + .value_or(false); } diff --git a/lib/e2ee/qolmaccount.h b/lib/e2ee/qolmaccount.h index 1f36919a..ee2aa69d 100644 --- a/lib/e2ee/qolmaccount.h +++ b/lib/e2ee/qolmaccount.h @@ -5,11 +5,12 @@ #pragma once -#include "csapi/keys.h" #include "e2ee/e2ee.h" -#include "e2ee/qolmerrors.h" #include "e2ee/qolmmessage.h" -#include + +#include "csapi/keys.h" + +#include struct OlmAccount; @@ -38,7 +39,7 @@ public: void unpickle(QByteArray &pickled, const PicklingMode &mode); //! Serialises an OlmAccount to encrypted Base64. - std::variant pickle(const PicklingMode &mode); + QOlmExpected pickle(const PicklingMode &mode); //! Returns the account's public identity keys already formatted as JSON IdentityKeys identityKeys() const; @@ -73,22 +74,26 @@ public: DeviceKeys deviceKeys() const; //! Remove the one time key used to create the supplied session. - [[nodiscard]] std::optional removeOneTimeKeys(const QOlmSessionPtr &session) const; + [[nodiscard]] std::optional removeOneTimeKeys( + const QOlmSession& session); //! Creates an inbound session for sending/receiving messages from a received 'prekey' message. //! //! \param message An Olm pre-key message that was encrypted for this account. - std::variant createInboundSession(const QOlmMessage &preKeyMessage); + QOlmExpected createInboundSession( + const QOlmMessage& preKeyMessage); //! Creates an inbound session for sending/receiving messages from a received 'prekey' message. //! //! \param theirIdentityKey - The identity key of the Olm account that //! encrypted this Olm message. - std::variant createInboundSessionFrom(const QByteArray &theirIdentityKey, const QOlmMessage &preKeyMessage); + QOlmExpected createInboundSessionFrom( + const QByteArray& theirIdentityKey, const QOlmMessage& preKeyMessage); //! Creates an outbound session for sending messages to a specific /// identity and one time key. - std::variant createOutboundSession(const QByteArray &theirIdentityKey, const QByteArray &theirOneTimeKey); + QOlmExpected createOutboundSession( + const QByteArray& theirIdentityKey, const QByteArray& theirOneTimeKey); void markKeysAsPublished(); diff --git a/lib/e2ee/qolminboundsession.cpp b/lib/e2ee/qolminboundsession.cpp index 62856831..17f06205 100644 --- a/lib/e2ee/qolminboundsession.cpp +++ b/lib/e2ee/qolminboundsession.cpp @@ -70,7 +70,8 @@ QByteArray QOlmInboundGroupSession::pickle(const PicklingMode &mode) const return pickledBuf; } -std::variant, QOlmError> QOlmInboundGroupSession::unpickle(const QByteArray &pickled, const PicklingMode &mode) +QOlmExpected QOlmInboundGroupSession::unpickle( + const QByteArray& pickled, const PicklingMode& mode) { QByteArray pickledBuf = pickled; const auto groupSession = olm_inbound_group_session(new uint8_t[olm_inbound_group_session_size()]); @@ -85,7 +86,8 @@ std::variant, QOlmError> QOlmInboundGro return std::make_unique(groupSession); } -std::variant, QOlmError> QOlmInboundGroupSession::decrypt(const QByteArray &message) +QOlmExpected> QOlmInboundGroupSession::decrypt( + const QByteArray& message) { // This is for capturing the output of olm_group_decrypt uint32_t messageIndex = 0; @@ -114,10 +116,10 @@ std::variant, QOlmError> QOlmInboundGroupSession::d QByteArray output(plaintextLen, '0'); std::memcpy(output.data(), plaintextBuf.data(), plaintextLen); - return std::make_pair(QString(output), messageIndex); + return std::make_pair(output, messageIndex); } -std::variant QOlmInboundGroupSession::exportSession(uint32_t messageIndex) +QOlmExpected QOlmInboundGroupSession::exportSession(uint32_t messageIndex) { const auto keyLength = olm_export_inbound_group_session_length(m_groupSession); QByteArray keyBuf(keyLength, '0'); diff --git a/lib/e2ee/qolminboundsession.h b/lib/e2ee/qolminboundsession.h index 13515434..1a9b4415 100644 --- a/lib/e2ee/qolminboundsession.h +++ b/lib/e2ee/qolminboundsession.h @@ -5,11 +5,8 @@ #pragma once #include "e2ee/e2ee.h" -#include "e2ee/qolmerrors.h" -#include "olm/olm.h" -#include -#include +#include namespace Quotient { @@ -27,14 +24,13 @@ public: QByteArray pickle(const PicklingMode &mode) const; //! Deserialises from encrypted Base64 that was previously obtained by pickling //! an `OlmInboundGroupSession`. - static std::variant, QOlmError> - unpickle(const QByteArray& picked, const PicklingMode& mode); + static QOlmExpected unpickle( + const QByteArray& pickled, const PicklingMode& mode); //! Decrypts ciphertext received for this group session. - std::variant, QOlmError> decrypt( - const QByteArray& message); + QOlmExpected > decrypt(const QByteArray& message); //! Export the base64-encoded ratchet key for this session, at the given index, //! in a format which can be used by import. - std::variant exportSession(uint32_t messageIndex); + QOlmExpected exportSession(uint32_t messageIndex); //! Get the first message index we know how to decrypt. uint32_t firstKnownIndex() const; //! Get a base64-encoded identifier for this session. diff --git a/lib/e2ee/qolmoutboundsession.cpp b/lib/e2ee/qolmoutboundsession.cpp index da32417b..96bad344 100644 --- a/lib/e2ee/qolmoutboundsession.cpp +++ b/lib/e2ee/qolmoutboundsession.cpp @@ -13,8 +13,7 @@ QOlmError lastError(OlmOutboundGroupSession *session) { QOlmOutboundGroupSession::QOlmOutboundGroupSession(OlmOutboundGroupSession *session) : m_groupSession(session) -{ -} +{} QOlmOutboundGroupSession::~QOlmOutboundGroupSession() { @@ -22,7 +21,7 @@ QOlmOutboundGroupSession::~QOlmOutboundGroupSession() delete[](reinterpret_cast(m_groupSession)); } -std::unique_ptr QOlmOutboundGroupSession::create() +QOlmOutboundGroupSessionPtr QOlmOutboundGroupSession::create() { auto *olmOutboundGroupSession = olm_outbound_group_session(new uint8_t[olm_outbound_group_session_size()]); const auto randomLength = olm_init_outbound_group_session_random_length(olmOutboundGroupSession); @@ -45,7 +44,7 @@ std::unique_ptr QOlmOutboundGroupSession::create() return std::make_unique(olmOutboundGroupSession); } -std::variant QOlmOutboundGroupSession::pickle(const PicklingMode &mode) +QOlmExpected QOlmOutboundGroupSession::pickle(const PicklingMode &mode) { QByteArray pickledBuf(olm_pickle_outbound_group_session_length(m_groupSession), '0'); QByteArray key = toKey(mode); @@ -61,7 +60,7 @@ std::variant QOlmOutboundGroupSession::pickle(const Pickl return pickledBuf; } -std::variant, QOlmError> QOlmOutboundGroupSession::unpickle(QByteArray &pickled, const PicklingMode &mode) +QOlmExpected QOlmOutboundGroupSession::unpickle(QByteArray &pickled, const PicklingMode &mode) { QByteArray pickledBuf = pickled; auto *olmOutboundGroupSession = olm_outbound_group_session(new uint8_t[olm_outbound_group_session_size()]); @@ -80,7 +79,7 @@ std::variant, QOlmError> QOlmOutboundG return std::make_unique(olmOutboundGroupSession); } -std::variant QOlmOutboundGroupSession::encrypt(const QString &plaintext) +QOlmExpected QOlmOutboundGroupSession::encrypt(const QString &plaintext) { QByteArray plaintextBuf = plaintext.toUtf8(); const auto messageMaxLength = olm_group_encrypt_message_length(m_groupSession, plaintextBuf.length()); @@ -112,12 +111,13 @@ QByteArray QOlmOutboundGroupSession::sessionId() const return idBuffer; } -std::variant QOlmOutboundGroupSession::sessionKey() const +QOlmExpected QOlmOutboundGroupSession::sessionKey() const { const auto keyMaxLength = olm_outbound_group_session_key_length(m_groupSession); QByteArray keyBuffer(keyMaxLength, '0'); - const auto error = olm_outbound_group_session_key(m_groupSession, reinterpret_cast(keyBuffer.data()), - keyMaxLength); + const auto error = olm_outbound_group_session_key( + m_groupSession, reinterpret_cast(keyBuffer.data()), + keyMaxLength); if (error == olm_error()) { return lastError(m_groupSession); } diff --git a/lib/e2ee/qolmoutboundsession.h b/lib/e2ee/qolmoutboundsession.h index 32ba2b3b..8058bbb1 100644 --- a/lib/e2ee/qolmoutboundsession.h +++ b/lib/e2ee/qolmoutboundsession.h @@ -4,10 +4,10 @@ #pragma once -#include "olm/olm.h" -#include "e2ee/qolmerrors.h" #include "e2ee/e2ee.h" + #include +#include namespace Quotient { @@ -19,15 +19,15 @@ public: ~QOlmOutboundGroupSession(); //! Creates a new instance of `QOlmOutboundGroupSession`. //! Throw OlmError on errors - static std::unique_ptr create(); + static QOlmOutboundGroupSessionPtr create(); //! Serialises a `QOlmOutboundGroupSession` to encrypted Base64. - std::variant pickle(const PicklingMode &mode); + QOlmExpected pickle(const PicklingMode &mode); //! Deserialises from encrypted Base64 that was previously obtained by //! pickling a `QOlmOutboundGroupSession`. - static std::variant, QOlmError> - unpickle(QByteArray& pickled, const PicklingMode& mode); + static QOlmExpected unpickle( + QByteArray& pickled, const PicklingMode& mode); //! Encrypts a plaintext message using the session. - std::variant encrypt(const QString &plaintext); + QOlmExpected encrypt(const QString& plaintext); //! Get the current message index for this session. //! @@ -42,11 +42,10 @@ public: //! //! Each message is sent with a different ratchet key. This function returns the //! ratchet key that will be used for the next message. - std::variant sessionKey() const; + QOlmExpected sessionKey() const; QOlmOutboundGroupSession(OlmOutboundGroupSession *groupSession); private: OlmOutboundGroupSession *m_groupSession; }; -using QOlmOutboundGroupSessionPtr = std::unique_ptr; -} +} // namespace Quotient diff --git a/lib/e2ee/qolmsession.cpp b/lib/e2ee/qolmsession.cpp index 5ccbfd40..2b149aac 100644 --- a/lib/e2ee/qolmsession.cpp +++ b/lib/e2ee/qolmsession.cpp @@ -27,7 +27,9 @@ OlmSession* QOlmSession::create() return olm_session(new uint8_t[olm_session_size()]); } -std::variant QOlmSession::createInbound(QOlmAccount *account, const QOlmMessage &preKeyMessage, bool from, const QString &theirIdentityKey) +QOlmExpected QOlmSession::createInbound( + QOlmAccount* account, const QOlmMessage& preKeyMessage, bool from, + const QString& theirIdentityKey) { if (preKeyMessage.type() != QOlmMessage::PreKey) { qCCritical(E2EE) << "The message is not a pre-key in when creating inbound session" << BadMessageFormat; @@ -53,17 +55,22 @@ std::variant QOlmSession::createInbound(QOlmAccount * return std::make_unique(olmSession); } -std::variant QOlmSession::createInboundSession(QOlmAccount *account, const QOlmMessage &preKeyMessage) +QOlmExpected QOlmSession::createInboundSession( + QOlmAccount* account, const QOlmMessage& preKeyMessage) { return createInbound(account, preKeyMessage); } -std::variant QOlmSession::createInboundSessionFrom(QOlmAccount *account, const QString &theirIdentityKey, const QOlmMessage &preKeyMessage) +QOlmExpected QOlmSession::createInboundSessionFrom( + QOlmAccount* account, const QString& theirIdentityKey, + const QOlmMessage& preKeyMessage) { return createInbound(account, preKeyMessage, true, theirIdentityKey); } -std::variant QOlmSession::createOutboundSession(QOlmAccount *account, const QString &theirIdentityKey, const QString &theirOneTimeKey) +QOlmExpected QOlmSession::createOutboundSession( + QOlmAccount* account, const QString& theirIdentityKey, + const QString& theirOneTimeKey) { auto *olmOutboundSession = create(); const auto randomLen = olm_create_outbound_session_random_length(olmOutboundSession); @@ -89,7 +96,7 @@ std::variant QOlmSession::createOutboundSession(QOlmA return std::make_unique(olmOutboundSession); } -std::variant QOlmSession::pickle(const PicklingMode &mode) +QOlmExpected QOlmSession::pickle(const PicklingMode &mode) { QByteArray pickledBuf(olm_pickle_session_length(m_session), '0'); QByteArray key = toKey(mode); @@ -105,7 +112,8 @@ std::variant QOlmSession::pickle(const PicklingMode &mode return pickledBuf; } -std::variant QOlmSession::unpickle(const QByteArray &pickled, const PicklingMode &mode) +QOlmExpected QOlmSession::unpickle(const QByteArray& pickled, + const PicklingMode& mode) { QByteArray pickledBuf = pickled; auto *olmSession = create(); @@ -140,7 +148,7 @@ QOlmMessage QOlmSession::encrypt(const QString &plaintext) return QOlmMessage(messageBuf, messageType); } -std::variant QOlmSession::decrypt(const QOlmMessage &message) const +QOlmExpected QOlmSession::decrypt(const QOlmMessage &message) const { const auto messageType = message.type(); const auto ciphertext = message.toCiphertext(); diff --git a/lib/e2ee/qolmsession.h b/lib/e2ee/qolmsession.h index 4eb151b6..faae16ef 100644 --- a/lib/e2ee/qolmsession.h +++ b/lib/e2ee/qolmsession.h @@ -19,32 +19,31 @@ class QUOTIENT_API QOlmSession public: ~QOlmSession(); //! Creates an inbound session for sending/receiving messages from a received 'prekey' message. - static std::variant, QOlmError> - createInboundSession(QOlmAccount* account, const QOlmMessage& preKeyMessage); + static QOlmExpected createInboundSession( + QOlmAccount* account, const QOlmMessage& preKeyMessage); - static std::variant, QOlmError> - createInboundSessionFrom(QOlmAccount* account, - const QString& theirIdentityKey, - const QOlmMessage& preKeyMessage); + static QOlmExpected createInboundSessionFrom( + QOlmAccount* account, const QString& theirIdentityKey, + const QOlmMessage& preKeyMessage); - static std::variant, QOlmError> - createOutboundSession(QOlmAccount* account, const QString& theirIdentityKey, - const QString& theirOneTimeKey); + static QOlmExpected createOutboundSession( + QOlmAccount* account, const QString& theirIdentityKey, + const QString& theirOneTimeKey); //! Serialises an `QOlmSession` to encrypted Base64. - std::variant pickle(const PicklingMode &mode); + QOlmExpected pickle(const PicklingMode &mode); //! Deserialises from encrypted Base64 that was previously obtained by pickling a `QOlmSession`. - static std::variant, QOlmError> unpickle( + static QOlmExpected unpickle( const QByteArray& pickled, const PicklingMode& mode); //! Encrypts a plaintext message using the session. QOlmMessage encrypt(const QString &plaintext); - //! Decrypts a message using this session. Decoding is lossy, meaing if + //! Decrypts a message using this session. Decoding is lossy, meaning if //! the decrypted plaintext contains invalid UTF-8 symbols, they will //! be returned as `U+FFFD` (�). - std::variant decrypt(const QOlmMessage &message) const; + QOlmExpected decrypt(const QOlmMessage &message) const; //! Get a base64-encoded identifier for this session. QByteArray sessionId() const; @@ -56,11 +55,10 @@ public: bool hasReceivedMessage() const; //! Checks if the 'prekey' message is for this in-bound session. - std::variant matchesInboundSession( - const QOlmMessage& preKeyMessage) const; + bool matchesInboundSession(const QOlmMessage& preKeyMessage) const; //! Checks if the 'prekey' message is for this in-bound session. - std::variant matchesInboundSessionFrom( + bool matchesInboundSessionFrom( const QString& theirIdentityKey, const QOlmMessage& preKeyMessage) const; friend bool operator<(const QOlmSession& lhs, const QOlmSession& rhs) @@ -68,8 +66,7 @@ public: return lhs.sessionId() < rhs.sessionId(); } - friend bool operator<(const std::unique_ptr& lhs, - const std::unique_ptr& rhs) + friend bool operator<(const QOlmSessionPtr& lhs, const QOlmSessionPtr& rhs) { return *lhs < *rhs; } @@ -80,7 +77,7 @@ public: private: //! Helper function for creating new sessions and handling errors. static OlmSession* create(); - static std::variant, QOlmError> createInbound( + static QOlmExpected createInbound( QOlmAccount* account, const QOlmMessage& preKeyMessage, bool from = false, const QString& theirIdentityKey = ""); OlmSession* m_session; diff --git a/lib/e2ee/qolmutility.cpp b/lib/e2ee/qolmutility.cpp index 9f09a37f..84559085 100644 --- a/lib/e2ee/qolmutility.cpp +++ b/lib/e2ee/qolmutility.cpp @@ -3,8 +3,8 @@ // SPDX-License-Identifier: LGPL-2.1-or-later #include "e2ee/qolmutility.h" -#include "olm/olm.h" -#include + +#include using namespace Quotient; @@ -40,8 +40,9 @@ QString QOlmUtility::sha256Utf8Msg(const QString &message) const return sha256Bytes(message.toUtf8()); } -std::variant QOlmUtility::ed25519Verify(const QByteArray &key, - const QByteArray &message, const QByteArray &signature) +QOlmExpected QOlmUtility::ed25519Verify(const QByteArray& key, + const QByteArray& message, + const QByteArray& signature) { QByteArray signatureBuf(signature.length(), '0'); std::copy(signature.begin(), signature.end(), signatureBuf.begin()); @@ -57,8 +58,5 @@ std::variant QOlmUtility::ed25519Verify(const QByteArray &key, return error; } - if (ret != 0) { - return false; - } - return true; + return !ret; // ret == 0 means success } diff --git a/lib/e2ee/qolmutility.h b/lib/e2ee/qolmutility.h index a12af49a..5f6bcdc5 100644 --- a/lib/e2ee/qolmutility.h +++ b/lib/e2ee/qolmutility.h @@ -4,15 +4,12 @@ #pragma once -#include -#include "e2ee/qolmerrors.h" +#include "e2ee/e2ee.h" struct OlmUtility; namespace Quotient { -class QOlmSession; - //! Allows you to make use of crytographic hashing via SHA-2 and //! verifying ed25519 signatures. class QUOTIENT_API QOlmUtility @@ -32,7 +29,7 @@ public: //! \param key QByteArray The public part of the ed25519 key that signed the message. //! \param message QByteArray The message that was signed. //! \param signature QByteArray The signature of the message. - std::variant ed25519Verify(const QByteArray &key, + QOlmExpected ed25519Verify(const QByteArray &key, const QByteArray &message, const QByteArray &signature); private: diff --git a/lib/room.cpp b/lib/room.cpp index db49e80f..1314803e 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -380,17 +380,22 @@ public: return {}; } auto decryptResult = senderSession->decrypt(cipher); - if(std::holds_alternative(decryptResult)) { + if(!decryptResult) { qCWarning(E2EE) << "Unable to decrypt event" << eventId - << "with matching megolm session:" << std::get(decryptResult); + << "with matching megolm session:" << decryptResult.error(); return {}; } - const auto& [content, index] = std::get>(decryptResult); - const auto& [recordEventId, ts] = q->connection()->database()->groupSessionIndexRecord(q->id(), senderSession->sessionId(), index); + const auto& [content, index] = *decryptResult; + const auto& [recordEventId, ts] = + q->connection()->database()->groupSessionIndexRecord( + q->id(), senderSession->sessionId(), index); if (recordEventId.isEmpty()) { - q->connection()->database()->addGroupSessionIndexRecord(q->id(), senderSession->sessionId(), index, eventId, timestamp.toMSecsSinceEpoch()); + q->connection()->database()->addGroupSessionIndexRecord( + q->id(), senderSession->sessionId(), index, eventId, + timestamp.toMSecsSinceEpoch()); } else { - if ((eventId != recordEventId) || (ts != timestamp.toMSecsSinceEpoch())) { + if ((eventId != recordEventId) + || (ts != timestamp.toMSecsSinceEpoch())) { qCWarning(E2EE) << "Detected a replay attack on event" << eventId; return {}; } -- cgit v1.2.3 From 56dbaab8fc8edc314ef7f7962697e7eb6ba71343 Mon Sep 17 00:00:00 2001 From: Alexey Rusakov Date: Mon, 16 May 2022 14:15:39 +0200 Subject: Update autotests/testolmutility.cpp Co-authored-by: Tobias Fella <9750016+TobiasFella@users.noreply.github.com> --- autotests/testolmutility.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'autotests/testolmutility.cpp') diff --git a/autotests/testolmutility.cpp b/autotests/testolmutility.cpp index c1323533..5b67c805 100644 --- a/autotests/testolmutility.cpp +++ b/autotests/testolmutility.cpp @@ -87,7 +87,7 @@ void TestOlmUtility::verifySignedOneTimeKey() .value_or(false); //QCOMPARE(std::string(olm_utility_last_error(utility)), "SUCCESS"); - QCOMPARE(res2, true); + QVERIFY(res2); } void TestOlmUtility::validUploadKeysRequest() -- 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(-) (limited to 'autotests/testolmutility.cpp') 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 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(-) (limited to 'autotests/testolmutility.cpp') 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