diff options
-rw-r--r-- | autotests/testgroupsession.cpp | 8 | ||||
-rw-r--r-- | autotests/testolmaccount.cpp | 2 | ||||
-rw-r--r-- | lib/connection.cpp | 23 | ||||
-rw-r--r-- | lib/database.cpp | 32 | ||||
-rw-r--r-- | lib/e2ee/qolmaccount.cpp | 37 | ||||
-rw-r--r-- | lib/e2ee/qolmaccount.h | 4 | ||||
-rw-r--r-- | lib/e2ee/qolminboundsession.cpp | 28 | ||||
-rw-r--r-- | lib/e2ee/qolminboundsession.h | 4 | ||||
-rw-r--r-- | lib/e2ee/qolmoutboundsession.cpp | 24 | ||||
-rw-r--r-- | lib/e2ee/qolmoutboundsession.h | 6 | ||||
-rw-r--r-- | lib/e2ee/qolmsession.cpp | 46 | ||||
-rw-r--r-- | lib/e2ee/qolmsession.h | 2 | ||||
-rw-r--r-- | lib/e2ee/qolmutils.h | 18 | ||||
-rw-r--r-- | lib/room.cpp | 45 |
14 files changed, 150 insertions, 129 deletions
diff --git a/autotests/testgroupsession.cpp b/autotests/testgroupsession.cpp index 18ace73b..1054a160 100644 --- a/autotests/testgroupsession.cpp +++ b/autotests/testgroupsession.cpp @@ -16,13 +16,13 @@ void TestGroupSession::groupSessionPicklingValid() QVERIFY(QByteArray::fromBase64(ogsId).size() > 0); QCOMPARE(0, ogs->sessionMessageIndex()); - auto ogsPickled = ogs->pickle(Unencrypted {}).value(); + auto&& ogsPickled = ogs->pickle(Unencrypted {}); auto ogs2 = QOlmOutboundGroupSession::unpickle(std::move(ogsPickled), Unencrypted{}) .value(); QCOMPARE(ogsId, ogs2->sessionId()); - auto igs = QOlmInboundGroupSession::create(ogs->sessionKey().value()); + auto igs = QOlmInboundGroupSession::create(ogs->sessionKey()).value(); const auto igsId = igs->sessionId(); // ID is valid base64? QVERIFY(QByteArray::fromBase64(igsId).size() > 0); @@ -39,11 +39,11 @@ void TestGroupSession::groupSessionPicklingValid() void TestGroupSession::groupSessionCryptoValid() { auto ogs = QOlmOutboundGroupSession::create(); - auto igs = QOlmInboundGroupSession::create(ogs->sessionKey().value()); + auto igs = QOlmInboundGroupSession::create(ogs->sessionKey()).value(); QCOMPARE(ogs->sessionId(), igs->sessionId()); const auto plainText = "Hello world!"; - const auto ciphertext = ogs->encrypt(plainText).value(); + const auto ciphertext = ogs->encrypt(plainText); // ciphertext valid base64? QVERIFY(QByteArray::fromBase64(ciphertext).size() > 0); diff --git a/autotests/testolmaccount.cpp b/autotests/testolmaccount.cpp index 0e1eab84..56532698 100644 --- a/autotests/testolmaccount.cpp +++ b/autotests/testolmaccount.cpp @@ -23,7 +23,7 @@ void TestOlmAccount::pickleUnpickledTest() QOlmAccount olmAccount(QStringLiteral("@foo:bar.com"), QStringLiteral("QuotientTestDevice")); olmAccount.createNewAccount(); auto identityKeys = olmAccount.identityKeys(); - auto pickled = olmAccount.pickle(Unencrypted{}).value(); + auto pickled = olmAccount.pickle(Unencrypted{}); QOlmAccount olmAccount2(QStringLiteral("@foo:bar.com"), QStringLiteral("QuotientTestDevice")); auto unpickleResult = olmAccount2.unpickle(std::move(pickled), Unencrypted{}); diff --git a/lib/connection.cpp b/lib/connection.cpp index cd8ee727..0afbffbc 100644 --- a/lib/connection.cpp +++ b/lib/connection.cpp @@ -206,13 +206,9 @@ public: } void saveSession(const QOlmSession& session, const QString& senderKey) const { - if (auto pickleResult = session.pickle(picklingMode)) - q->database()->saveOlmSession(senderKey, session.sessionId(), - *pickleResult, - QDateTime::currentDateTime()); - else - qCWarning(E2EE) << "Failed to pickle olm session. Error" - << pickleResult.error(); + q->database()->saveOlmSession(senderKey, session.sessionId(), + session.pickle(picklingMode), + QDateTime::currentDateTime()); } template <typename FnT> @@ -2219,11 +2215,7 @@ void Connection::saveOlmAccount() { #ifdef Quotient_E2EE_ENABLED qCDebug(E2EE) << "Saving olm account"; - if (const auto expectedPickle = d->olmAccount->pickle(d->picklingMode)) - d->database->setAccountPickle(*expectedPickle); - else - qCWarning(E2EE) << "Couldn't save Olm account pickle:" - << expectedPickle.error(); + d->database->setAccountPickle(d->olmAccount->pickle(d->picklingMode)); #endif } @@ -2300,11 +2292,8 @@ std::pair<QOlmMessage::Type, QByteArray> Connection::Private::olmEncryptMessage( const auto& curveKey = curveKeyForUserDevice(userId, device); const auto& olmSession = olmSessions.at(curveKey).front(); const auto result = olmSession->encrypt(message); - if (const auto pickle = olmSession->pickle(picklingMode)) { - database->updateOlmSession(curveKey, olmSession->sessionId(), *pickle); - } else { - qWarning(E2EE) << "Failed to pickle olm session: " << pickle.error(); - } + database->updateOlmSession(curveKey, olmSession->sessionId(), + olmSession->pickle(picklingMode)); return { result.type(), result.toCiphertext() }; } diff --git a/lib/database.cpp b/lib/database.cpp index 4eb82cf5..2b472648 100644 --- a/lib/database.cpp +++ b/lib/database.cpp @@ -323,23 +323,21 @@ void Database::saveCurrentOutboundMegolmSession( const QOlmOutboundGroupSession& session) { const auto pickle = session.pickle(picklingMode); - if (pickle) { - auto deleteQuery = prepareQuery(QStringLiteral("DELETE FROM outbound_megolm_sessions WHERE roomId=:roomId AND sessionId=:sessionId;")); - deleteQuery.bindValue(":roomId", roomId); - deleteQuery.bindValue(":sessionId", session.sessionId()); - - auto insertQuery = prepareQuery(QStringLiteral("INSERT INTO outbound_megolm_sessions(roomId, sessionId, pickle, creationTime, messageCount) VALUES(:roomId, :sessionId, :pickle, :creationTime, :messageCount);")); - insertQuery.bindValue(":roomId", roomId); - insertQuery.bindValue(":sessionId", session.sessionId()); - insertQuery.bindValue(":pickle", pickle.value()); - insertQuery.bindValue(":creationTime", session.creationTime()); - insertQuery.bindValue(":messageCount", session.messageCount()); - - transaction(); - execute(deleteQuery); - execute(insertQuery); - commit(); - } + auto deleteQuery = prepareQuery(QStringLiteral("DELETE FROM outbound_megolm_sessions WHERE roomId=:roomId AND sessionId=:sessionId;")); + deleteQuery.bindValue(":roomId", roomId); + deleteQuery.bindValue(":sessionId", session.sessionId()); + + auto insertQuery = prepareQuery(QStringLiteral("INSERT INTO outbound_megolm_sessions(roomId, sessionId, pickle, creationTime, messageCount) VALUES(:roomId, :sessionId, :pickle, :creationTime, :messageCount);")); + insertQuery.bindValue(":roomId", roomId); + insertQuery.bindValue(":sessionId", session.sessionId()); + insertQuery.bindValue(":pickle", pickle); + insertQuery.bindValue(":creationTime", session.creationTime()); + insertQuery.bindValue(":messageCount", session.messageCount()); + + transaction(); + execute(deleteQuery); + execute(insertQuery); + commit(); } QOlmOutboundGroupSessionPtr Database::loadCurrentOutboundMegolmSession(const QString& roomId, const PicklingMode& picklingMode) diff --git a/lib/e2ee/qolmaccount.cpp b/lib/e2ee/qolmaccount.cpp index b56272ef..d03bcb3b 100644 --- a/lib/e2ee/qolmaccount.cpp +++ b/lib/e2ee/qolmaccount.cpp @@ -45,9 +45,9 @@ void QOlmAccount::createNewAccount() m_account = olm_account(new uint8_t[olm_account_size()]); const auto randomLength = olm_create_account_random_length(m_account); if (olm_create_account(m_account, RandomBuffer(randomLength), randomLength) - == olm_error()) { - throw lastError(); - } + == olm_error()) + QOLM_INTERNAL_ERROR("Failed to create a new account"); + emit needsSave(); } @@ -64,7 +64,7 @@ OlmErrorCode QOlmAccount::unpickle(QByteArray&& pickled, const PicklingMode &mod return OLM_SUCCESS; } -QOlmExpected<QByteArray> QOlmAccount::pickle(const PicklingMode &mode) +QByteArray QOlmAccount::pickle(const PicklingMode &mode) { const QByteArray key = toKey(mode); const size_t pickleLength = olm_pickle_account_length(m_account); @@ -72,7 +72,9 @@ QOlmExpected<QByteArray> QOlmAccount::pickle(const PicklingMode &mode) if (olm_pickle_account(m_account, key.data(), key.length(), pickleBuffer.data(), pickleLength) == olm_error()) - return lastErrorCode(); + QOLM_INTERNAL_ERROR(qPrintable("Failed to pickle Olm account " + + accountId())); + return pickleBuffer; } @@ -82,7 +84,8 @@ IdentityKeys QOlmAccount::identityKeys() const QByteArray keyBuffer(keyLength, '\0'); if (olm_account_identity_keys(m_account, keyBuffer.data(), keyLength) == olm_error()) { - throw lastError(); + QOLM_INTERNAL_ERROR( + qPrintable("Failed to get " % accountId() % " identity keys")); } const auto key = QJsonDocument::fromJson(keyBuffer).object(); return IdentityKeys { @@ -97,9 +100,9 @@ QByteArray QOlmAccount::sign(const QByteArray &message) const if (olm_account_sign(m_account, message.data(), message.length(), signatureBuffer.data(), signatureBuffer.length()) - == olm_error()) { - throw lastError(); - } + == olm_error()) + QOLM_INTERNAL_ERROR("Failed to sign a message"); + return signatureBuffer; } @@ -135,9 +138,10 @@ size_t QOlmAccount::generateOneTimeKeys(size_t numberOfKeys) const auto result = olm_account_generate_one_time_keys( m_account, numberOfKeys, RandomBuffer(randomLength), randomLength); - if (result == olm_error()) { - throw lastError(); - } + if (result == olm_error()) + QOLM_INTERNAL_ERROR(qPrintable( + "Failed to generate one-time keys for account " + accountId())); + emit needsSave(); return result; } @@ -149,9 +153,10 @@ UnsignedOneTimeKeys QOlmAccount::oneTimeKeys() const if (olm_account_one_time_keys(m_account, oneTimeKeysBuffer.data(), oneTimeKeyLength) - == olm_error()) { - throw lastError(); - } + == olm_error()) + QOLM_INTERNAL_ERROR(qPrintable( + "Failed to obtain one-time keys for account" % accountId())); + const auto json = QJsonDocument::fromJson(oneTimeKeysBuffer).object(); UnsignedOneTimeKeys oneTimeKeys; fromJson(json, oneTimeKeys.keys); @@ -266,3 +271,5 @@ bool Quotient::ed25519VerifySignature(const QString& signingKey, return utility.ed25519Verify(signingKeyBuf, canonicalJson, signatureBuf) .value_or(false); } + +QString QOlmAccount::accountId() const { return m_userId % '/' % m_deviceId; } diff --git a/lib/e2ee/qolmaccount.h b/lib/e2ee/qolmaccount.h index 0fb9803f..a5faa82a 100644 --- a/lib/e2ee/qolmaccount.h +++ b/lib/e2ee/qolmaccount.h @@ -41,7 +41,7 @@ public: const PicklingMode& mode); //! Serialises an OlmAccount to encrypted Base64. - QOlmExpected<QByteArray> pickle(const PicklingMode &mode); + QByteArray pickle(const PicklingMode &mode); //! Returns the account's public identity keys already formatted as JSON IdentityKeys identityKeys() const; @@ -107,6 +107,8 @@ private: OlmAccount *m_account = nullptr; // owning QString m_userId; QString m_deviceId; + + QString accountId() const; }; QUOTIENT_API bool verifyIdentitySignature(const DeviceKeys& deviceKeys, diff --git a/lib/e2ee/qolminboundsession.cpp b/lib/e2ee/qolminboundsession.cpp index a05ddf62..1e3803f2 100644 --- a/lib/e2ee/qolminboundsession.cpp +++ b/lib/e2ee/qolminboundsession.cpp @@ -31,7 +31,8 @@ QOlmInboundGroupSession::~QOlmInboundGroupSession() //delete[](reinterpret_cast<uint8_t *>(m_groupSession)); } -std::unique_ptr<QOlmInboundGroupSession> QOlmInboundGroupSession::create(const QByteArray &key) +QOlmExpected<QOlmInboundGroupSessionPtr> QOlmInboundGroupSession::create( + const QByteArray& key) { const auto olmInboundGroupSession = olm_inbound_group_session(new uint8_t[olm_inbound_group_session_size()]); if (olm_init_inbound_group_session( @@ -39,13 +40,17 @@ std::unique_ptr<QOlmInboundGroupSession> QOlmInboundGroupSession::create(const Q reinterpret_cast<const uint8_t*>(key.constData()), key.size()) == olm_error()) { // FIXME: create QOlmInboundGroupSession earlier and use lastErrorCode() - throw olm_inbound_group_session_last_error_code(olmInboundGroupSession); + qWarning(E2EE) << "Failed to create an inbound group session:" + << olm_inbound_group_session_last_error( + olmInboundGroupSession); + return olm_inbound_group_session_last_error_code(olmInboundGroupSession); } return std::make_unique<QOlmInboundGroupSession>(olmInboundGroupSession); } -std::unique_ptr<QOlmInboundGroupSession> QOlmInboundGroupSession::import(const QByteArray &key) +QOlmExpected<QOlmInboundGroupSessionPtr> QOlmInboundGroupSession::import( + const QByteArray& key) { const auto olmInboundGroupSession = olm_inbound_group_session(new uint8_t[olm_inbound_group_session_size()]); @@ -54,7 +59,10 @@ std::unique_ptr<QOlmInboundGroupSession> QOlmInboundGroupSession::import(const Q reinterpret_cast<const uint8_t*>(key.data()), key.size()) == olm_error()) { // FIXME: create QOlmInboundGroupSession earlier and use lastError() - throw olm_inbound_group_session_last_error_code(olmInboundGroupSession); + qWarning(E2EE) << "Failed to import an inbound group session:" + << olm_inbound_group_session_last_error( + olmInboundGroupSession); + return olm_inbound_group_session_last_error_code(olmInboundGroupSession); } return std::make_unique<QOlmInboundGroupSession>(olmInboundGroupSession); @@ -69,7 +77,7 @@ QByteArray QOlmInboundGroupSession::pickle(const PicklingMode& mode) const key.length(), pickledBuf.data(), pickledBuf.length()) == olm_error()) { - throw lastError(); + QOLM_INTERNAL_ERROR("Failed to pickle the inbound group session"); } return pickledBuf; } @@ -84,6 +92,8 @@ QOlmExpected<QOlmInboundGroupSessionPtr> QOlmInboundGroupSession::unpickle( pickled.size()) == olm_error()) { // FIXME: create QOlmInboundGroupSession earlier and use lastError() + qWarning(E2EE) << "Failed to unpickle an inbound group session:" + << olm_inbound_group_session_last_error(groupSession); return olm_inbound_group_session_last_error_code(groupSession); } key.clear(); @@ -133,6 +143,8 @@ QOlmExpected<QByteArray> QOlmInboundGroupSession::exportSession( m_groupSession, reinterpret_cast<uint8_t*>(keyBuf.data()), keyLength, messageIndex) == olm_error()) { + QOLM_FAIL_OR_LOG(OLM_OUTPUT_BUFFER_TOO_SMALL, + "Failed to export the inbound group session"); return lastErrorCode(); } return keyBuf; @@ -150,9 +162,9 @@ QByteArray QOlmInboundGroupSession::sessionId() const if (olm_inbound_group_session_id( m_groupSession, reinterpret_cast<uint8_t*>(sessionIdBuf.data()), sessionIdBuf.length()) - == olm_error()) { - throw lastError(); - } + == olm_error()) + QOLM_INTERNAL_ERROR("Failed to obtain the group session id"); + return sessionIdBuf; } diff --git a/lib/e2ee/qolminboundsession.h b/lib/e2ee/qolminboundsession.h index 0b89349a..15979a05 100644 --- a/lib/e2ee/qolminboundsession.h +++ b/lib/e2ee/qolminboundsession.h @@ -17,9 +17,9 @@ class QUOTIENT_API QOlmInboundGroupSession public: ~QOlmInboundGroupSession(); //! Creates a new instance of `OlmInboundGroupSession`. - static std::unique_ptr<QOlmInboundGroupSession> create(const QByteArray& key); + static QOlmExpected<QOlmInboundGroupSessionPtr> create(const QByteArray& key); //! Import an inbound group session, from a previous export. - static std::unique_ptr<QOlmInboundGroupSession> import(const QByteArray& key); + static QOlmExpected<QOlmInboundGroupSessionPtr> import(const QByteArray& key); //! Serialises an `OlmInboundGroupSession` to encrypted Base64. QByteArray pickle(const PicklingMode& mode) const; //! Deserialises from encrypted Base64 that was previously obtained by pickling diff --git a/lib/e2ee/qolmoutboundsession.cpp b/lib/e2ee/qolmoutboundsession.cpp index 3d176274..18af5569 100644 --- a/lib/e2ee/qolmoutboundsession.cpp +++ b/lib/e2ee/qolmoutboundsession.cpp @@ -39,8 +39,10 @@ QOlmOutboundGroupSessionPtr QOlmOutboundGroupSession::create() RandomBuffer(randomLength).bytes(), randomLength) == olm_error()) { - // FIXME: create the session object earlier and use lastError() - throw olm_outbound_group_session_last_error_code(olmOutboundGroupSession); + // FIXME: create the session object earlier + QOLM_INTERNAL_ERROR_X("Failed to initialise an outbound group session", + olm_outbound_group_session_last_error( + olmOutboundGroupSession)); } // FIXME: is it used anywhere? @@ -52,7 +54,7 @@ QOlmOutboundGroupSessionPtr QOlmOutboundGroupSession::create() return std::make_unique<QOlmOutboundGroupSession>(olmOutboundGroupSession); } -QOlmExpected<QByteArray> QOlmOutboundGroupSession::pickle(const PicklingMode &mode) const +QByteArray QOlmOutboundGroupSession::pickle(const PicklingMode &mode) const { QByteArray pickledBuf( olm_pickle_outbound_group_session_length(m_groupSession), '\0'); @@ -61,7 +63,7 @@ QOlmExpected<QByteArray> QOlmOutboundGroupSession::pickle(const PicklingMode &mo key.length(), pickledBuf.data(), pickledBuf.length()) == olm_error()) - return lastErrorCode(); + QOLM_INTERNAL_ERROR("Failed to pickle the outbound group session"); key.clear(); return pickledBuf; @@ -77,6 +79,9 @@ QOlmExpected<QOlmOutboundGroupSessionPtr> QOlmOutboundGroupSession::unpickle( pickled.length()) == olm_error()) { // FIXME: create the session object earlier and use lastError() + qWarning(E2EE) << "Failed to unpickle an outbound group session:" + << olm_outbound_group_session_last_error( + olmOutboundGroupSession); return olm_outbound_group_session_last_error_code( olmOutboundGroupSession); } @@ -90,8 +95,7 @@ QOlmExpected<QOlmOutboundGroupSessionPtr> QOlmOutboundGroupSession::unpickle( return std::make_unique<QOlmOutboundGroupSession>(olmOutboundGroupSession); } -QOlmExpected<QByteArray> QOlmOutboundGroupSession::encrypt( - const QByteArray& plaintext) const +QByteArray QOlmOutboundGroupSession::encrypt(const QByteArray& plaintext) const { const auto messageMaxLength = olm_group_encrypt_message_length(m_groupSession, plaintext.length()); @@ -102,7 +106,7 @@ QOlmExpected<QByteArray> QOlmOutboundGroupSession::encrypt( reinterpret_cast<uint8_t*>(messageBuf.data()), messageBuf.length()) == olm_error()) - return lastErrorCode(); + QOLM_INTERNAL_ERROR("Failed to encrypt a message"); return messageBuf; } @@ -120,12 +124,12 @@ QByteArray QOlmOutboundGroupSession::sessionId() const m_groupSession, reinterpret_cast<uint8_t*>(idBuffer.data()), idBuffer.length()) == olm_error()) - throw lastError(); + QOLM_INTERNAL_ERROR("Failed to obtain group session id"); return idBuffer; } -QOlmExpected<QByteArray> QOlmOutboundGroupSession::sessionKey() const +QByteArray QOlmOutboundGroupSession::sessionKey() const { const auto keyMaxLength = olm_outbound_group_session_key_length(m_groupSession); QByteArray keyBuffer(keyMaxLength, '\0'); @@ -133,7 +137,7 @@ QOlmExpected<QByteArray> QOlmOutboundGroupSession::sessionKey() const m_groupSession, reinterpret_cast<uint8_t*>(keyBuffer.data()), keyMaxLength) == olm_error()) - return lastErrorCode(); + QOLM_INTERNAL_ERROR("Failed to obtain group session key"); return keyBuffer; } diff --git a/lib/e2ee/qolmoutboundsession.h b/lib/e2ee/qolmoutboundsession.h index e5e73ddf..d36fbf69 100644 --- a/lib/e2ee/qolmoutboundsession.h +++ b/lib/e2ee/qolmoutboundsession.h @@ -20,14 +20,14 @@ public: //! Throw OlmError on errors static QOlmOutboundGroupSessionPtr create(); //! Serialises a `QOlmOutboundGroupSession` to encrypted Base64. - QOlmExpected<QByteArray> pickle(const PicklingMode &mode) const; + QByteArray pickle(const PicklingMode &mode) const; //! Deserialises from encrypted Base64 that was previously obtained by //! pickling a `QOlmOutboundGroupSession`. static QOlmExpected<QOlmOutboundGroupSessionPtr> unpickle( QByteArray&& pickled, const PicklingMode& mode); //! Encrypts a plaintext message using the session. - QOlmExpected<QByteArray> encrypt(const QByteArray& plaintext) const; + QByteArray encrypt(const QByteArray& plaintext) const; //! Get the current message index for this session. //! @@ -42,7 +42,7 @@ public: //! //! Each message is sent with a different ratchet key. This function returns the //! ratchet key that will be used for the next message. - QOlmExpected<QByteArray> sessionKey() const; + QByteArray sessionKey() const; QOlmOutboundGroupSession(OlmOutboundGroupSession *groupSession); int messageCount() const; diff --git a/lib/e2ee/qolmsession.cpp b/lib/e2ee/qolmsession.cpp index 7c102a96..0d6edd21 100644 --- a/lib/e2ee/qolmsession.cpp +++ b/lib/e2ee/qolmsession.cpp @@ -90,23 +90,23 @@ QOlmExpected<QOlmSessionPtr> QOlmSession::createOutboundSession( == olm_error()) { // FIXME: the QOlmSession object should be created earlier const auto lastErr = olm_session_last_error_code(olmOutboundSession); - if (lastErr == OLM_NOT_ENOUGH_RANDOM) { - throw lastErr; - } + QOLM_FAIL_OR_LOG_X(lastErr == OLM_NOT_ENOUGH_RANDOM, + "Failed to create an outbound Olm session", + olm_session_last_error(olmOutboundSession)); return lastErr; } return std::make_unique<QOlmSession>(olmOutboundSession); } -QOlmExpected<QByteArray> QOlmSession::pickle(const PicklingMode &mode) const +QByteArray QOlmSession::pickle(const PicklingMode &mode) const { QByteArray pickledBuf(olm_pickle_session_length(m_session), '\0'); QByteArray key = toKey(mode); if (olm_pickle_session(m_session, key.data(), key.length(), pickledBuf.data(), pickledBuf.length()) == olm_error()) - return lastErrorCode(); + QOLM_INTERNAL_ERROR("Failed to pickle an Olm session"); key.clear(); return pickledBuf; @@ -121,7 +121,11 @@ QOlmExpected<QOlmSessionPtr> QOlmSession::unpickle(QByteArray&& pickled, pickled.data(), pickled.length()) == olm_error()) { // FIXME: the QOlmSession object should be created earlier - return olm_session_last_error_code(olmSession); + const auto errorCode = olm_session_last_error_code(olmSession); + QOLM_FAIL_OR_LOG_X(errorCode == OLM_OUTPUT_BUFFER_TOO_SMALL, + "Failed to unpickle an Olm session", + olm_session_last_error(olmSession)); + return errorCode; } key.clear(); @@ -140,7 +144,7 @@ QOlmMessage QOlmSession::encrypt(const QByteArray& plaintext) RandomBuffer(randomLength), randomLength, messageBuf.data(), messageBuf.length()) == olm_error()) { - throw lastError(); + QOLM_INTERNAL_ERROR("Failed to encrypt the message"); } return QOlmMessage(messageBuf, QOlmMessage::Type(messageType)); @@ -159,7 +163,9 @@ QOlmExpected<QByteArray> QOlmSession::decrypt(const QOlmMessage &message) const const auto plaintextMaxLen = olm_decrypt_max_plaintext_length( m_session, messageTypeValue, messageBuf.data(), messageBuf.length()); if (plaintextMaxLen == olm_error()) { - return lastError(); + qWarning(E2EE) << "Couldn't calculate decrypted message length:" + << lastError(); + return lastErrorCode(); } QByteArray plaintextBuf(plaintextMaxLen, '\0'); @@ -170,11 +176,9 @@ QOlmExpected<QByteArray> QOlmSession::decrypt(const QOlmMessage &message) const olm_decrypt(m_session, messageTypeValue, messageBuf2.data(), messageBuf2.length(), plaintextBuf.data(), plaintextMaxLen); if (plaintextResultLen == olm_error()) { - const auto lastErr = lastErrorCode(); - if (lastErr == OLM_OUTPUT_BUFFER_TOO_SMALL) { - throw lastErr; - } - return lastErr; + QOLM_FAIL_OR_LOG(OLM_OUTPUT_BUFFER_TOO_SMALL, + "Failed to decrypt the message"); + return lastErrorCode(); } plaintextBuf.truncate(plaintextResultLen); return plaintextBuf; @@ -183,10 +187,10 @@ QOlmExpected<QByteArray> QOlmSession::decrypt(const QOlmMessage &message) const QByteArray QOlmSession::sessionId() const { const auto idMaxLength = olm_session_id_length(m_session); - QByteArray idBuffer(idMaxLength, '0'); - if (olm_session_id(m_session, idBuffer.data(), idMaxLength) == olm_error()) { - throw lastError(); - } + QByteArray idBuffer(idMaxLength, '\0'); + if (olm_session_id(m_session, idBuffer.data(), idMaxLength) == olm_error()) + QOLM_INTERNAL_ERROR("Failed to obtain Olm session id"); + return idBuffer; } @@ -203,8 +207,8 @@ bool QOlmSession::matchesInboundSession(const QOlmMessage& preKeyMessage) const olm_matches_inbound_session(m_session, oneTimeKeyBuf.data(), oneTimeKeyBuf.length()); if (maybeMatches == olm_error()) - qWarning(E2EE) << "Error matching an inbound session:" - << olm_session_last_error(m_session); + qWarning(E2EE) << "Error matching an inbound session:" << lastError(); + return maybeMatches == 1; // Any errors are treated as non-match } @@ -218,8 +222,8 @@ bool QOlmSession::matchesInboundSessionFrom( oneTimeKeyMessageBuf.data(), oneTimeKeyMessageBuf.length()); if (maybeMatches == olm_error()) - qCWarning(E2EE) << "Error matching an inbound session:" - << olm_session_last_error(m_session); + qCWarning(E2EE) << "Error matching an inbound session:" << lastError(); + return maybeMatches == 1; } diff --git a/lib/e2ee/qolmsession.h b/lib/e2ee/qolmsession.h index 174e98ef..400fb854 100644 --- a/lib/e2ee/qolmsession.h +++ b/lib/e2ee/qolmsession.h @@ -30,7 +30,7 @@ public: const QByteArray& theirOneTimeKey); //! Serialises an `QOlmSession` to encrypted Base64. - QOlmExpected<QByteArray> pickle(const PicklingMode &mode) const; + QByteArray pickle(const PicklingMode &mode) const; //! Deserialises from encrypted Base64 previously made with pickle() static QOlmExpected<QOlmSessionPtr> unpickle(QByteArray&& pickled, diff --git a/lib/e2ee/qolmutils.h b/lib/e2ee/qolmutils.h index da9d2d18..17eee7a3 100644 --- a/lib/e2ee/qolmutils.h +++ b/lib/e2ee/qolmutils.h @@ -34,4 +34,22 @@ public: return RandomBuffer(bufferSize); } +#define QOLM_INTERNAL_ERROR_X(Message_, LastError_) \ + qFatal("%s, internal error: %s", Message_, LastError_) + +#define QOLM_INTERNAL_ERROR(Message_) \ + QOLM_INTERNAL_ERROR_X(Message_, lastError()) + +#define QOLM_FAIL_OR_LOG_X(InternalCondition_, Message_, LastErrorText_) \ + do { \ + const QString errorMsg{ (Message_) }; \ + if (InternalCondition_) \ + QOLM_INTERNAL_ERROR_X(qPrintable(errorMsg), (LastErrorText_)); \ + qWarning(E2EE).nospace() << errorMsg << ": " << (LastErrorText_); \ + } while (false) /* End of macro */ + +#define QOLM_FAIL_OR_LOG(InternalFailureValue_, Message_) \ + QOLM_FAIL_OR_LOG_X(lastErrorCode() == (InternalFailureValue_), (Message_), \ + lastError()) + } // namespace Quotient diff --git a/lib/room.cpp b/lib/room.cpp index 382d0243..5fdff036 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -357,7 +357,9 @@ public: return false; } - auto megolmSession = QOlmInboundGroupSession::create(sessionKey); + auto expectedMegolmSession = QOlmInboundGroupSession::create(sessionKey); + Q_ASSERT(expectedMegolmSession.has_value()); + auto&& megolmSession = *expectedMegolmSession; if (megolmSession->sessionId() != sessionId) { qCWarning(E2EE) << "Session ID mismatch in m.room_key event"; return false; @@ -442,12 +444,10 @@ public: connection->saveCurrentOutboundMegolmSession( id, *currentOutboundMegolmSession); - const auto sessionKey = currentOutboundMegolmSession->sessionKey(); - if(!sessionKey) { - qCWarning(E2EE) << "Failed to load key for new megolm session"; - return; - } - addInboundGroupSession(currentOutboundMegolmSession->sessionId(), *sessionKey, q->localUser()->id(), "SELF"_ls); + currentOutboundMegolmSession->sessionKey(); + addInboundGroupSession(currentOutboundMegolmSession->sessionId(), + currentOutboundMegolmSession->sessionKey(), + q->localUser()->id(), "SELF"_ls); } QMultiHash<QString, QString> getDevicesWithoutKey() const @@ -460,22 +460,6 @@ public: return connection->database()->devicesWithoutKey( id, devices, currentOutboundMegolmSession->sessionId()); } - - void sendMegolmSession(const QMultiHash<QString, QString>& devices) const { - // Save the session to this device - const auto sessionId = currentOutboundMegolmSession->sessionId(); - const auto sessionKey = currentOutboundMegolmSession->sessionKey(); - if(!sessionKey) { - qCWarning(E2EE) << "Error loading session key"; - return; - } - - // Send the session to other people - connection->sendSessionKeyToDevices( - id, sessionId, *sessionKey, devices, - currentOutboundMegolmSession->sessionMessageIndex()); - } - #endif // Quotient_E2EE_ENABLED private: @@ -2029,17 +2013,20 @@ QString Room::Private::doSendEvent(const RoomEvent* pEvent) if (!hasValidMegolmSession() || shouldRotateMegolmSession()) { createMegolmSession(); } - sendMegolmSession(getDevicesWithoutKey()); + // Send the session to other people + connection->sendSessionKeyToDevices( + id, currentOutboundMegolmSession->sessionId(), + currentOutboundMegolmSession->sessionKey(), getDevicesWithoutKey(), + currentOutboundMegolmSession->sessionMessageIndex()); const auto encrypted = currentOutboundMegolmSession->encrypt(QJsonDocument(pEvent->fullJson()).toJson()); currentOutboundMegolmSession->setMessageCount(currentOutboundMegolmSession->messageCount() + 1); connection->saveCurrentOutboundMegolmSession( id, *currentOutboundMegolmSession); - if(!encrypted) { - qWarning(E2EE) << "Error encrypting message" << encrypted.error(); - return {}; - } - encryptedEvent = makeEvent<EncryptedEvent>(*encrypted, q->connection()->olmAccount()->identityKeys().curve25519, q->connection()->deviceId(), currentOutboundMegolmSession->sessionId()); + encryptedEvent = makeEvent<EncryptedEvent>( + encrypted, q->connection()->olmAccount()->identityKeys().curve25519, + q->connection()->deviceId(), + currentOutboundMegolmSession->sessionId()); encryptedEvent->setTransactionId(connection->generateTxnId()); encryptedEvent->setRoomId(id); encryptedEvent->setSender(connection->userId()); |