aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAlexey Rusakov <Kitsune-Ral@users.sf.net>2022-06-24 15:10:33 +0200
committerAlexey Rusakov <Kitsune-Ral@users.sf.net>2022-06-24 15:10:33 +0200
commit6ae41d68dcdb91e5ec4a3ea48a151daaa0765765 (patch)
treef715eba9e690e8faa7de729b82ee82d9e0b30962 /lib
parent9f7a65b04c246de4c27b205ece778ede1ad7df7e (diff)
downloadlibquotient-6ae41d68dcdb91e5ec4a3ea48a151daaa0765765.tar.gz
libquotient-6ae41d68dcdb91e5ec4a3ea48a151daaa0765765.zip
Rework SignedOneTimeKey as a QJsonObject wrapper
Since this object has to be verified against a signature it also carries there's a rather specific procedure described in The Spec for that. That procedure basically assumes handling the signed one-time key object as a JSON object, not as a C++ object. And originally Quotient E2EE code was exactly like that (obtaining the right QJsonObject from the job result and handling it as specced) but then one enthusiastic developer (me) decided it's better to use a proper C++ structure - breaking the verification logic along the way. After a couple attempts to fix it, here we are again: SignedOneTimeKey is a proper QJsonObject, and even provides a method returning its JSON in the form prepared for verification (according to the spec).
Diffstat (limited to 'lib')
-rw-r--r--lib/connection.cpp10
-rw-r--r--lib/e2ee/e2ee.h62
-rw-r--r--lib/e2ee/qolmaccount.cpp12
3 files changed, 48 insertions, 36 deletions
diff --git a/lib/connection.cpp b/lib/connection.cpp
index 13a35684..690b3f6a 100644
--- a/lib/connection.cpp
+++ b/lib/connection.cpp
@@ -2303,14 +2303,10 @@ bool Connection::Private::createOlmSession(const QString& targetUserId,
// Verify contents of signedOneTimeKey - for that, drop `signatures` and
// `unsigned` and then verify the object against the respective signature
const auto signature =
- signedOneTimeKey
- ->signatures[targetUserId]["ed25519:"_ls % targetDeviceId]
- .toLatin1();
- const auto payloadObject =
- toJson(SignedOneTimeKey { signedOneTimeKey->key, {} });
+ signedOneTimeKey->signature(targetUserId, targetDeviceId);
if (!verifier.ed25519Verify(
edKeyForUserDevice(targetUserId, targetDeviceId).toLatin1(),
- QJsonDocument(payloadObject).toJson(QJsonDocument::Compact),
+ signedOneTimeKey->toJsonForVerification(),
signature)) {
qWarning(E2EE) << "Failed to verify one-time-key signature for" << targetUserId
<< targetDeviceId << ". Skipping this device.";
@@ -2320,7 +2316,7 @@ bool Connection::Private::createOlmSession(const QString& targetUserId,
curveKeyForUserDevice(targetUserId, targetDeviceId);
auto session =
QOlmSession::createOutboundSession(olmAccount.get(), recipientCurveKey,
- signedOneTimeKey->key);
+ signedOneTimeKey->key());
if (!session) {
qCWarning(E2EE) << "Failed to create olm session for "
<< recipientCurveKey << session.error();
diff --git a/lib/e2ee/e2ee.h b/lib/e2ee/e2ee.h
index 17c87f53..7b9b5820 100644
--- a/lib/e2ee/e2ee.h
+++ b/lib/e2ee/e2ee.h
@@ -10,8 +10,10 @@
#include "qolmerrors.h"
#include <QtCore/QMetaType>
-#include <variant>
+#include <QtCore/QStringBuilder>
+
#include <array>
+#include <variant>
namespace Quotient {
@@ -79,35 +81,53 @@ struct UnsignedOneTimeKeys
QHash<QString, QString> curve25519() const { return keys[Curve25519Key]; }
};
-//! Struct representing the signed one-time keys.
-class SignedOneTimeKey
-{
+class SignedOneTimeKey {
public:
- //! Required. The unpadded Base64-encoded 32-byte Curve25519 public key.
- QString key;
+ explicit SignedOneTimeKey(const QString& unsignedKey, const QString& userId,
+ const QString& deviceId, const QString& signature)
+ : payload { { "key"_ls, unsignedKey },
+ { "signatures"_ls,
+ QJsonObject {
+ { userId, QJsonObject { { "ed25519:"_ls % deviceId,
+ signature } } } } } }
+ {}
+ explicit SignedOneTimeKey(const QJsonObject& jo = {})
+ : payload(jo)
+ {}
- //! Required. Signatures of the key object.
- //! The signature is calculated using the process described at Signing JSON.
- QHash<QString, QHash<QString, QString>> signatures;
+ //! Unpadded Base64-encoded 32-byte Curve25519 public key
+ QString key() const { return payload["key"_ls].toString(); }
- bool fallback = false;
-};
+ //! \brief Signatures of the key object
+ //!
+ //! The signature is calculated using the process described at
+ //! https://spec.matrix.org/v1.3/appendices/#signing-json
+ auto signatures() const
+ {
+ return fromJson<QHash<QString, QHash<QString, QString>>>(
+ payload["signatures"_ls]);
+ }
-template <>
-struct JsonObjectConverter<SignedOneTimeKey> {
- static void fillFrom(const QJsonObject& jo, SignedOneTimeKey& result)
+ QByteArray signature(QStringView userId, QStringView deviceId) const
{
- fromJson(jo.value("key"_ls), result.key);
- fromJson(jo.value("signatures"_ls), result.signatures);
- fromJson(jo.value("fallback"_ls), result.fallback);
+ return payload["signatures"_ls][userId]["ed25519:"_ls % deviceId]
+ .toString()
+ .toLatin1();
}
- static void dumpTo(QJsonObject &jo, const SignedOneTimeKey &result)
+ //! Whether the key is a fallback key
+ bool isFallback() const { return payload["fallback"_ls].toBool(); }
+ auto toJson() const { return payload; }
+ auto toJsonForVerification() const
{
- addParam<>(jo, "key"_ls, result.key);
- addParam<IfNotEmpty>(jo, "signatures"_ls, result.signatures);
- addParam<IfNotEmpty>(jo, "fallback"_ls, result.fallback);
+ auto json = payload;
+ json.remove("signatures"_ls);
+ json.remove("unsigned"_ls);
+ return QJsonDocument(json).toJson(QJsonDocument::Compact);
}
+
+private:
+ QJsonObject payload;
};
using OneTimeKeys = QHash<QString, std::variant<QString, SignedOneTimeKey>>;
diff --git a/lib/e2ee/qolmaccount.cpp b/lib/e2ee/qolmaccount.cpp
index 241ae750..c3714363 100644
--- a/lib/e2ee/qolmaccount.cpp
+++ b/lib/e2ee/qolmaccount.cpp
@@ -162,17 +162,13 @@ OneTimeKeys QOlmAccount::signOneTimeKeys(const UnsignedOneTimeKeys &keys) const
OneTimeKeys signedOneTimeKeys;
for (const auto& curveKeys = keys.curve25519();
const auto& [keyId, key] : asKeyValueRange(curveKeys))
- signedOneTimeKeys["signed_curve25519:" % keyId] =
- signedOneTimeKey(key.toUtf8(), sign(QJsonObject{{"key", key}}));
+ signedOneTimeKeys.insert("signed_curve25519:" % keyId,
+ SignedOneTimeKey {
+ key, m_userId, m_deviceId,
+ sign(QJsonObject { { "key", key } }) });
return signedOneTimeKeys;
}
-SignedOneTimeKey QOlmAccount::signedOneTimeKey(const QByteArray& key,
- const QString& signature) const
-{
- return { key, { { m_userId, { { "ed25519:" + m_deviceId, signature } } } } };
-}
-
std::optional<QOlmError> QOlmAccount::removeOneTimeKeys(
const QOlmSession& session)
{