aboutsummaryrefslogtreecommitdiff
path: root/lib/encryptionmanager.cpp
diff options
context:
space:
mode:
authorTobias Fella <fella@posteo.de>2021-06-12 23:04:17 +0200
committerTobias Fella <fella@posteo.de>2021-12-01 21:56:59 +0100
commit703b3f89ef54d9d40c9117788d0920b6b745bd62 (patch)
tree800e2e706930a0c2512e4517e16246a2d30d79f9 /lib/encryptionmanager.cpp
parenteaf23c0f6bb97fdf16631296ab7016447a99c4f2 (diff)
downloadlibquotient-703b3f89ef54d9d40c9117788d0920b6b745bd62.tar.gz
libquotient-703b3f89ef54d9d40c9117788d0920b6b745bd62.zip
Implement (meg)olm key caching, megolm decrypting, EncryptedEvent
decryption, handling of encrypted redactions and replies
Diffstat (limited to 'lib/encryptionmanager.cpp')
-rw-r--r--lib/encryptionmanager.cpp72
1 files changed, 71 insertions, 1 deletions
diff --git a/lib/encryptionmanager.cpp b/lib/encryptionmanager.cpp
index 48e6701c..d36d5a7a 100644
--- a/lib/encryptionmanager.cpp
+++ b/lib/encryptionmanager.cpp
@@ -31,7 +31,6 @@ using std::move;
class EncryptionManager::Private {
public:
explicit Private()
- : q(nullptr)
{
}
~Private() = default;
@@ -51,6 +50,73 @@ public:
}
}
}
+ void loadSessions() {
+ QFile file { static_cast<Connection *>(q->parent())->stateCacheDir().filePath("olmsessions.json") };
+ if(!file.exists() || !file.open(QIODevice::ReadOnly)) {
+ qCDebug(E2EE) << "No sessions cache exists.";
+ return;
+ }
+ auto data = file.readAll();
+ const auto json = data.startsWith('{')
+ ? QJsonDocument::fromJson(data).object()
+#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
+ : QCborValue::fromCbor(data).toJsonValue().toObject()
+#else
+ : QJsonDocument::fromBinaryData(data).object()
+#endif
+ ;
+ if (json.isEmpty()) {
+ qCWarning(MAIN) << "Sessions cache is empty";
+ return;
+ }
+ for(const auto &senderKey : json["sessions"].toObject().keys()) {
+ auto pickle = json["sessions"].toObject()[senderKey].toString();
+ auto sessionResult = QOlmSession::unpickle(pickle.toLatin1(), Unencrypted{});
+ if(std::holds_alternative<QOlmError>(sessionResult)) {
+ qCWarning(E2EE) << "Failed to unpickle olm session";
+ continue;
+ }
+ sessions[senderKey] = std::move(std::get<std::unique_ptr<QOlmSession>>(sessionResult));
+ }
+ }
+ void saveSessions() {
+ QFile outFile { static_cast<Connection *>(q->parent())->stateCacheDir().filePath("olmsessions.json") };
+ if (!outFile.open(QFile::WriteOnly)) {
+ qCWarning(E2EE) << "Error opening" << outFile.fileName() << ":"
+ << outFile.errorString();
+ qCWarning(E2EE) << "Failed to write olm sessions";
+ return;
+ }
+
+ QJsonObject rootObj {
+ { QStringLiteral("cache_version"),
+ QJsonObject {
+ { QStringLiteral("major"), 1 },
+ { QStringLiteral("minor"), 0 } } }
+ };
+ {
+ QJsonObject sessionsJson;
+ for (const auto &session : sessions) {
+ auto pickleResult = session.second->pickle(Unencrypted{});
+ if(std::holds_alternative<QOlmError>(pickleResult)) {
+ qCWarning(E2EE) << "Failed to pickle session";
+ continue;
+ }
+ sessionsJson[session.first] = QString(std::get<QByteArray>(pickleResult));
+ }
+ rootObj.insert(QStringLiteral("sessions"), sessionsJson);
+ }
+
+ #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
+ const auto data = QJsonDocument(rootObj).toJson(QJsonDocument::Compact);
+ #else
+ QJsonDocument json { rootObj };
+ const auto data = json.toJson(QJsonDocument::Compact);
+ #endif
+
+ outFile.write(data.data(), data.size());
+ qCDebug(E2EE) << "Sessions saved to" << outFile.fileName();
+ }
QString sessionDecryptPrekey(const QOlmMessage& message, const QString &senderKey, std::unique_ptr<QOlmAccount>& olmAccount)
{
Q_ASSERT(message.type() == QOlmMessage::PreKey);
@@ -60,6 +126,7 @@ public:
qCDebug(E2EE) << "Found inbound session";
const auto result = session.second->decrypt(message);
if(std::holds_alternative<QString>(result)) {
+ saveSessions();
return std::get<QString>(result);
} else {
qCDebug(E2EE) << "Failed to decrypt prekey message";
@@ -79,6 +146,7 @@ public:
const auto result = newSession->decrypt(message);
sessions[senderKey] = std::move(newSession);
if(std::holds_alternative<QString>(result)) {
+ saveSessions();
return std::get<QString>(result);
} else {
qCDebug(E2EE) << "Failed to decrypt prekey message with new session";
@@ -91,6 +159,7 @@ public:
for(auto& session : sessions) {
const auto result = session.second->decrypt(message);
if(std::holds_alternative<QString>(result)) {
+ saveSessions();
return std::get<QString>(result);
}
}
@@ -104,6 +173,7 @@ EncryptionManager::EncryptionManager(QObject* parent)
, d(std::make_unique<Private>())
{
d->q = this;
+ d->loadSessions();
}
EncryptionManager::~EncryptionManager() = default;