[network/ruqola] src/core: Add method for decrypted message
Laurent Montel <[email protected]>
| Newsgroups | gmane.comp.kde.cvs |
|---|---|
| Message-ID | <[email protected]> |
Git commit 0dd98c1c895865dce4d403326d9e7a5c096fc1ce by Laurent Montel.
Committed on 04/08/2026 at 06:51.
Pushed by mlaurent into branch 'master'.
Add method for decrypted message
M +27 -0 src/core/autotests/messageencryptedtest.cpp
M +1 -0 src/core/autotests/messageencryptedtest.h
M +39 -5 src/core/messages/messageencrypted.cpp
M +2 -0 src/core/messages/messageencrypted.h
https://invent.kde.org/network/ruqola/-/commit/0dd98c1c895865dce4d403326d9e7a5c096fc1ce
diff --git a/src/core/autotests/messageencryptedtest.cpp b/src/core/autotests/messageencryptedtest.cpp
index 1c8d0f8081..01df01d78e 100644
--- a/src/core/autotests/messageencryptedtest.cpp
+++ b/src/core/autotests/messageencryptedtest.cpp
@@ -4,8 +4,10 @@
SPDX-License-Identifier: LGPL-2.0-or-later
*/
#include "messageencryptedtest.h"
+#include "config-ruqola.h"
#include "messages/messageencrypted.h"
#include <QTest>
+
QTEST_GUILESS_MAIN(MessageEncryptedTest)
MessageEncryptedTest::MessageEncryptedTest(QObject *parent)
@@ -23,4 +25,29 @@ void MessageEncryptedTest::shouldHaveDefaultValues()
QVERIFY(!w.isValid());
}
+void MessageEncryptedTest::shouldDecryptV2Payload()
+{
+ // Test vector generated with AES-256-GCM, key='k'*32, iv="0123456789ab", plaintext={"msg":"hello e2e"}
+ const QByteArray sessionKey(32, 'k');
+ const QByteArray iv("0123456789ab");
+ const QByteArray plainText("{\"msg\":\"hello e2e\"}");
+ const QByteArray encryptedPayload = QByteArray::fromBase64("ej0KsqEKP7tIhPFauxLZfLCDiI6PY2Ex68Kv4kt2sCFIA24=");
+ QVERIFY(!encryptedPayload.isEmpty());
+
+ MessageEncrypted encrypted;
+ encrypted.setAlgorithm("rc.v2.aes-sha2");
+ encrypted.setKeyId("23e2720d-b3e0-4753-85ff-bad2caeb867b");
+ encrypted.setIv(iv.toBase64());
+ encrypted.setCiphertext(QString::fromLatin1(encryptedPayload.toBase64()));
+
+#if USE_E2E_SUPPORT
+ QCOMPARE(encrypted.decrypt(sessionKey), plainText);
+
+ const QByteArray wrongSessionKey(32, 'x');
+ QVERIFY(encrypted.decrypt(wrongSessionKey).isEmpty());
+#else
+ QVERIFY(encrypted.decrypt(sessionKey).isEmpty());
+#endif
+}
+
#include "moc_messageencryptedtest.cpp"
diff --git a/src/core/autotests/messageencryptedtest.h b/src/core/autotests/messageencryptedtest.h
index ba550396dc..794c411550 100644
--- a/src/core/autotests/messageencryptedtest.h
+++ b/src/core/autotests/messageencryptedtest.h
@@ -15,4 +15,5 @@ public:
~MessageEncryptedTest() override = default;
private Q_SLOTS:
void shouldHaveDefaultValues();
+ void shouldDecryptV2Payload();
};
diff --git a/src/core/messages/messageencrypted.cpp b/src/core/messages/messageencrypted.cpp
index 57a7ee407e..09a7effb67 100644
--- a/src/core/messages/messageencrypted.cpp
+++ b/src/core/messages/messageencrypted.cpp
@@ -5,6 +5,8 @@
*/
#include "messageencrypted.h"
+#include "config-ruqola.h"
+#include "encryption/encryptionutils.h"
#include "ruqola_encryption_debug.h"
#include <QJsonObject>
@@ -76,6 +78,38 @@ void MessageEncrypted::setIv(const QByteArray &newIv)
mIv = newIv;
}
+QByteArray MessageEncrypted::decrypt(const QByteArray &sessionKey) const
+{
+#if USE_E2E_SUPPORT
+ if (sessionKey.isEmpty()) {
+ qCWarning(RUQOLA_ENCRYPTION_LOG) << "MessageEncrypted::decrypt: session key is empty";
+ return {};
+ }
+
+ if (mAlgorithm != "rc.v2.aes-sha2") {
+ qCWarning(RUQOLA_ENCRYPTION_LOG) << "MessageEncrypted::decrypt: unsupported algorithm" << mAlgorithm;
+ return {};
+ }
+
+ const QByteArray decodedIv = QByteArray::fromBase64(mIv);
+ if (decodedIv.isEmpty()) {
+ qCWarning(RUQOLA_ENCRYPTION_LOG) << "MessageEncrypted::decrypt: iv is invalid";
+ return {};
+ }
+
+ const QByteArray decodedCiphertext = QByteArray::fromBase64(mCiphertext.toLatin1());
+ if (decodedCiphertext.isEmpty()) {
+ qCWarning(RUQOLA_ENCRYPTION_LOG) << "MessageEncrypted::decrypt: ciphertext is invalid";
+ return {};
+ }
+
+ return EncryptionUtils::decryptAES_GCM_256(decodedCiphertext, sessionKey, decodedIv);
+#else
+ Q_UNUSED(sessionKey)
+ return {};
+#endif
+}
+
bool MessageEncrypted::operator==(const MessageEncrypted &other) const
{
return mAlgorithm == other.algorithm() && mCiphertext == other.ciphertext() && mKeyId == other.keyId() && mIv == other.iv();
@@ -89,13 +123,13 @@ void MessageEncrypted::parse(const QJsonObject &o)
mKeyId = o["kid"_L1].toString().toLatin1();
}
-QJsonObject MessageEncrypted::serialize(const MessageEncrypted &encrypted)
+QJsonObject MessageEncrypted::serialize(const MessageEncrypted &message)
{
QJsonObject o;
- o["algorithm"_L1] = QString::fromLatin1(encrypted.algorithm());
- o["ciphertext"_L1] = encrypted.ciphertext();
- o["iv"_L1] = QString::fromLatin1(encrypted.iv());
- o["kid"_L1] = QString::fromLatin1(encrypted.keyId());
+ o["algorithm"_L1] = QString::fromLatin1(message.algorithm());
+ o["ciphertext"_L1] = message.ciphertext();
+ o["iv"_L1] = QString::fromLatin1(message.iv());
+ o["kid"_L1] = QString::fromLatin1(message.keyId());
return o;
}
diff --git a/src/core/messages/messageencrypted.h b/src/core/messages/messageencrypted.h
index 10ac881fdf..7da22e0253 100644
--- a/src/core/messages/messageencrypted.h
+++ b/src/core/messages/messageencrypted.h
@@ -38,6 +38,8 @@ public:
[[nodiscard]] QByteArray iv() const;
void setIv(const QByteArray &newIv);
+ [[nodiscard]] QByteArray decrypt(const QByteArray &sessionKey) const;
+
private:
QByteArray mAlgorithm;
QByteArray mKeyId;