[network/ruqola] src/core: Generate unique id for room session

Laurent Montel <[email protected]> Wed, 5 Aug 2026 06:52:38 +0000 (UTC)
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit 5c904d0c8b80b9e0ab8e25dbe8e122e374b2ab7f by Laurent Montel.
Committed on 05/08/2026 at 06:45.
Pushed by mlaurent into branch 'master'.

Generate unique id for room session

M  +20   -0    src/core/autotests/encryptionutilstest.cpp
M  +1    -0    src/core/autotests/encryptionutilstest.h
M  +18   -0    src/core/encryption/encryptionutils.cpp
M  +1    -0    src/core/encryption/encryptionutils.h

https://invent.kde.org/network/ruqola/-/commit/5c904d0c8b80b9e0ab8e25dbe8e122e374b2ab7f

diff --git a/src/core/autotests/encryptionutilstest.cpp b/src/core/autotests/encryptionutilstest.cpp
index 55157bdccd..3a8f79b930 100644
--- a/src/core/autotests/encryptionutilstest.cpp
+++ b/src/core/autotests/encryptionutilstest.cpp
@@ -80,4 +80,24 @@ void EncryptionUtilsTest::shouldGenerateRandomPassword()
     QVERIFY(password1 != password2);
 }
 
+void EncryptionUtilsTest::shouldGenerateRoomKeyId()
+{
+    // Must produce a non-empty UUID string.
+    const QString keyId1 = EncryptionUtils::generateRoomKeyId();
+    QVERIFY(!keyId1.isEmpty());
+
+    // UUID without braces is 36 chars: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+    QCOMPARE(keyId1.size(), 36);
+
+    // Must look like a UUID (contains four hyphens at the right positions).
+    QCOMPARE(keyId1[8], QChar(u'-'));
+    QCOMPARE(keyId1[13], QChar(u'-'));
+    QCOMPARE(keyId1[18], QChar(u'-'));
+    QCOMPARE(keyId1[23], QChar(u'-'));
+
+    // Each call must produce a unique value.
+    const QString keyId2 = EncryptionUtils::generateRoomKeyId();
+    QVERIFY(keyId1 != keyId2);
+}
+
 #include "moc_encryptionutilstest.cpp"
diff --git a/src/core/autotests/encryptionutilstest.h b/src/core/autotests/encryptionutilstest.h
index 978d13736b..0301dbadc6 100644
--- a/src/core/autotests/encryptionutilstest.h
+++ b/src/core/autotests/encryptionutilstest.h
@@ -23,4 +23,5 @@ private Q_SLOTS:
     void shouldJoinVectorAndEcryptedData_data();
     void shouldJoinVectorAndEcryptedData();
     void shouldGenerateRandomPassword();
+    void shouldGenerateRoomKeyId();
 };
diff --git a/src/core/encryption/encryptionutils.cpp b/src/core/encryption/encryptionutils.cpp
index 2f0b74e03f..299e6751a7 100644
--- a/src/core/encryption/encryptionutils.cpp
+++ b/src/core/encryption/encryptionutils.cpp
@@ -15,6 +15,7 @@
 #include <QJsonObject>
 #include <QJsonParseError>
 #include <QRandomGenerator>
+#include <QUuid>
 
 using namespace Qt::Literals::StringLiterals;
 
@@ -306,6 +307,23 @@ QByteArray EncryptionUtils::generateSessionKey()
     return generateRandomIV(16);
 }
 
+/**
+ * @brief Generates a room-specific key identifier (keyId).
+ *
+ * Matches Rocket.Chat's e2e.room implementation:
+ *   this.keyID = crypto.randomUUID()
+ *
+ * The keyId is sent to the server via e2e.setRoomKeyID and is prepended to
+ * every encrypted session key shared with room participants. During decryption
+ * the keyId is used to look up the correct room key (current or from oldRoomKeys).
+ *
+ * @return A UUID string without braces, e.g. "550e8400-e29b-41d4-a716-446655440000".
+ */
+QString EncryptionUtils::generateRoomKeyId()
+{
+    return QUuid::createUuid().toString(QUuid::WithoutBraces);
+}
+
 /**
  * @brief Converts public key from QByteArray to RSA.
  * @param QByteArray &pem
diff --git a/src/core/encryption/encryptionutils.h b/src/core/encryption/encryptionutils.h
index 995f5d7b88..d9330be4c1 100644
--- a/src/core/encryption/encryptionutils.h
+++ b/src/core/encryption/encryptionutils.h
@@ -57,5 +57,6 @@ struct RSAKeyPair {
 [[nodiscard]] LIBRUQOLACORE_EXPORT QVector<uint8_t> toArrayBuffer(const QByteArray &ba);
 [[nodiscard]] LIBRUQOLACORE_EXPORT QByteArray importRawKey(const QByteArray &keyData, const QByteArray &salt, int iterations);
 [[nodiscard]] LIBRUQOLACORE_EXPORT QString generateRandomPassword();
+[[nodiscard]] LIBRUQOLACORE_EXPORT QString generateRoomKeyId();
 };
 Q_DECLARE_TYPEINFO(EncryptionUtils::EncryptionInfo, Q_RELOCATABLE_TYPE);