diff options
-rw-r--r-- | lib/crypto/qolmaccount.cpp | 10 | ||||
-rw-r--r-- | lib/crypto/qolmaccount.h | 3 | ||||
-rw-r--r-- | lib/crypto/qolmsession.cpp | 21 | ||||
-rw-r--r-- | lib/crypto/qolmsession.h | 9 | ||||
-rw-r--r-- | lib/encryptionmanager.cpp | 95 |
5 files changed, 90 insertions, 48 deletions
diff --git a/lib/crypto/qolmaccount.cpp b/lib/crypto/qolmaccount.cpp index fc0fc1cf..76b0a263 100644 --- a/lib/crypto/qolmaccount.cpp +++ b/lib/crypto/qolmaccount.cpp @@ -197,6 +197,16 @@ QByteArray QOlmAccount::signOneTimeKey(const QString &key) const return sign(j.toJson()); } +std::optional<QOlmError> QOlmAccount::removeOneTimeKeys(const std::unique_ptr<QOlmSession> &session) const +{ + const auto error = olm_remove_one_time_keys(m_account, session->raw()); + + if (error == olm_error()) { + return lastError(m_account); + } + return std::nullopt; +} + OlmAccount *Quotient::QOlmAccount::data() { return m_account; diff --git a/lib/crypto/qolmaccount.h b/lib/crypto/qolmaccount.h index b33e3768..4398214a 100644 --- a/lib/crypto/qolmaccount.h +++ b/lib/crypto/qolmaccount.h @@ -68,6 +68,9 @@ public: SignedOneTimeKey signedOneTimeKey(const QByteArray &key, const QString &signature) const; + //! Remove the one time key used to create the supplied session. + [[nodiscard]] std::optional<QOlmError> removeOneTimeKeys(const std::unique_ptr<QOlmSession> &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. diff --git a/lib/crypto/qolmsession.cpp b/lib/crypto/qolmsession.cpp index cfe21650..b901a440 100644 --- a/lib/crypto/qolmsession.cpp +++ b/lib/crypto/qolmsession.cpp @@ -213,7 +213,7 @@ bool QOlmSession::hasReceivedMessage() const return olm_session_has_received_message(m_session); } -std::variant<bool, QOlmError> QOlmSession::matchesInboundSession(QOlmMessage &preKeyMessage) +std::variant<bool, QOlmError> QOlmSession::matchesInboundSession(const QOlmMessage &preKeyMessage) const { Q_ASSERT(preKeyMessage.type() == QOlmMessage::Type::PreKey); QByteArray oneTimeKeyBuf(preKeyMessage.data()); @@ -231,6 +231,25 @@ std::variant<bool, QOlmError> QOlmSession::matchesInboundSession(QOlmMessage &pr return QOlmError::Unknown; } } +std::variant<bool, QOlmError> 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 index 6e13801e..0fc59e9e 100644 --- a/lib/crypto/qolmsession.h +++ b/lib/crypto/qolmsession.h @@ -50,7 +50,10 @@ public: bool hasReceivedMessage() const; //! Checks if the 'prekey' message is for this in-bound session. - std::variant<bool, QOlmError> matchesInboundSession(QOlmMessage &preKeyMessage); + std::variant<bool, QOlmError> matchesInboundSession(const QOlmMessage &preKeyMessage) const; + + //! Checks if the 'prekey' message is for this in-bound session. + std::variant<bool, QOlmError> matchesInboundSessionFrom(const QString &theirIdentityKey, const QOlmMessage &preKeyMessage) const; friend bool operator<(const QOlmSession& lhs, const QOlmSession& rhs) { @@ -61,6 +64,10 @@ public: return *lhs < *rhs; } + OlmSession *raw() const + { + return m_session; + } QOlmSession(OlmSession* session); private: //! Helper function for creating new sessions and handling errors. diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp index 8081f788..449eb2a3 100644 --- a/lib/encryptionmanager.cpp +++ b/lib/encryptionmanager.cpp @@ -88,7 +88,7 @@ public: QHash<QString, int> targetOneTimeKeyCounts; // A map from senderKey to InboundSession - QMap<QString, QOlmSession*> sessions; // TODO: cache + QMap<QString, std::unique_ptr<QOlmSession>> sessions; // TODO: cache void updateDeviceKeys( const QHash<QString, QHash<QString, QueryKeysJob::DeviceInformation>>& deviceKeys) @@ -100,44 +100,45 @@ public: } } } - QString sessionDecrypt(QOlmMessage* message, const QString& senderKey) + QString sessionDecrypt(const QOlmMessage& message, const QString& senderKey) { - QString decrypted; - QList<QOlmSession*> senderSessions = sessions.values(senderKey); // Try to decrypt message body using one of the known sessions for that // device bool sessionsPassed = false; // new e2ee TODO: - /* - for (auto senderSession : senderSessions) { - if (senderSession == senderSessions.last()) { + for (auto &senderSession : sessions) { + if (senderSession == sessions.last()) { sessionsPassed = true; } - try { - decrypted = senderSession->decrypt(message); + + const auto decryptedResult = senderSession->decrypt(message); + if (std::holds_alternative<QString>(decryptedResult)) { qCDebug(E2EE) << "Success decrypting Olm event using existing session" - << senderSession->id(); - break; - } catch (QOlmError* e) { - if (message->type() == QOlmMessage::PreKey) { - if (senderSession->matches(&message, senderKey)) { - // We had a matching session for a pre-key message, but - // it didn't work. This means something is wrong, so we - // fail now. - qCDebug(E2EE) - << "Error decrypting pre-key message with existing " - "Olm session" - << senderSession->id() << "reason:" << e->what(); - return QString(); + << senderSession->sessionId(); + return std::get<QString>(decryptedResult); + } else { + const auto error = std::get<QOlmError>(decryptedResult); + if (message.type() == QOlmMessage::PreKey) { + const auto matches = senderSession->matchesInboundSessionFrom(senderKey, message); + if (auto hasMatch = std::get_if<bool>(&matches)) { + if (hasMatch) { + // We had a matching session for a pre-key message, but + // it didn't work. This means something is wrong, so we + // fail now. + qCDebug(E2EE) + << "Error decrypting pre-key message with existing " + "Olm session" + << senderSession->sessionId() << "reason:" << error; + return QString(); + } } } // Simply keep trying otherwise } } - */ - if (sessionsPassed || senderSessions.empty()) { - if (message->type() != QOlmMessage::PreKey) { + if (sessionsPassed || sessions.empty()) { + if (message.type() != QOlmMessage::PreKey) { // Not a pre-key message, we should have had a matching session if (!sessions.empty()) { qCDebug(E2EE) << "Error decrypting with existing sessions"; @@ -148,36 +149,39 @@ public: } // We have a pre-key message without any matching session, in this // case we should try to create one. - QOlmSession* newSession; qCDebug(E2EE) << "try to establish new InboundSession with" << senderKey; - QOlmMessage preKeyMessage = QOlmMessage(message->toCiphertext(),QOlmMessage::PreKey); + QOlmMessage preKeyMessage = QOlmMessage(message.toCiphertext(), QOlmMessage::PreKey); // new e2ee TODO: - /* - try { - newSession = new InboundSession(olmAccount.data(), - &preKeyMessage, - senderKey.toLatin1(), q); - } catch (OlmError* e) { + const auto sessionResult = olmAccount->createInboundSessionFrom(senderKey.toUtf8(), preKeyMessage); + + if (const auto error = std::get_if<QOlmError>(&sessionResult)) { qCDebug(E2EE) << "Error decrypting pre-key message when trying " "to establish a new session:" - << e->what(); + << error; return QString(); } - qCDebug(E2EE) << "Created new Olm session" << newSession->id(); - try { - decrypted = newSession->decrypt(message); - } catch (OlmError* e) { + + const auto newSession = std::get<std::unique_ptr<QOlmSession>>(sessionResult); + + qCDebug(E2EE) << "Created new Olm session" << newSession->sessionId(); + + const auto decryptedResult = newSession->decrypt(message); + if (const auto error = std::get_if<QOlmError>(&decryptedResult)) { qCDebug(E2EE) << "Error decrypting pre-key message with new session" - << e->what(); + << error; return QString(); } - olmAccount->removeOneTimeKeys(newSession); - */ - sessions.insert(senderKey, newSession); + if (auto error = olmAccount->removeOneTimeKeys(newSession)) { + qCDebug(E2EE) + << "Error removing one time keys" + << error.value(); + } + sessions.insert(senderKey, std::move(newSession)); + return std::get<QString>(decryptedResult); } - return decrypted; + return QString(); } }; @@ -330,11 +334,10 @@ QString EncryptionManager::sessionDecryptMessage( QByteArray body = personalCipherObject.value(BodyKeyL).toString().toLatin1(); if (type == 0) { QOlmMessage preKeyMessage = QOlmMessage(body, QOlmMessage::PreKey); - decrypted = d->sessionDecrypt(reinterpret_cast<QOlmMessage*>(&preKeyMessage), - senderKey); + decrypted = d->sessionDecrypt(preKeyMessage, senderKey); } else if (type == 1) { QOlmMessage message = QOlmMessage(body, QOlmMessage::PreKey); - decrypted = d->sessionDecrypt(&message, senderKey); + decrypted = d->sessionDecrypt(message, senderKey); } return decrypted; } |