diff options
-rw-r--r-- | autotests/CMakeLists.txt | 1 | ||||
-rw-r--r-- | autotests/testolmaccount.cpp | 10 | ||||
-rw-r--r-- | autotests/testolmutility.cpp | 131 | ||||
-rw-r--r-- | autotests/testolmutility.h | 15 | ||||
-rw-r--r-- | lib/crypto/qolmaccount.cpp | 36 | ||||
-rw-r--r-- | lib/crypto/qolmaccount.h | 8 | ||||
-rw-r--r-- | lib/crypto/qolmutility.cpp | 23 | ||||
-rw-r--r-- | lib/crypto/qolmutility.h | 2 |
8 files changed, 196 insertions, 30 deletions
diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index 6afdf8cc..0354172b 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -16,4 +16,5 @@ if(${PROJECT_NAME}_ENABLE_E2EE) quotient_add_test(NAME testolmaccount) quotient_add_test(NAME testgroupsession) quotient_add_test(NAME testolmsession) + quotient_add_test(NAME testolmutility) endif() diff --git a/autotests/testolmaccount.cpp b/autotests/testolmaccount.cpp index 5cb88a99..8d979e0b 100644 --- a/autotests/testolmaccount.cpp +++ b/autotests/testolmaccount.cpp @@ -309,11 +309,10 @@ void TestOlmAccount::claimKeys() deviceKeys[bob->userId()] = QStringList(); auto job = alice->callApi<QueryKeysJob>(deviceKeys); connect(job, &BaseJob::result, this, [bob, alice, aliceOlm, job, this] { + qDebug() << job->jsonData(); auto bobDevices = job->deviceKeys()[bob->userId()]; QVERIFY(bobDevices.size() > 0); - auto devices = {bob->deviceId()}; - // Retrieve the identity key for the current device. auto bobEd25519 = bobDevices[bob->deviceId()].keys["ed25519:" + bob->deviceId()]; @@ -324,10 +323,9 @@ void TestOlmAccount::claimKeys() QVERIFY(verifyIdentitySignature(currentDevice, bob->deviceId(), bob->userId())); QHash<QString, QHash<QString, QString>> oneTimeKeys; - for (const auto &d : devices) { - oneTimeKeys[bob->userId()] = QHash<QString, QString>(); - oneTimeKeys[bob->userId()][d] = SignedCurve25519Key; - } + oneTimeKeys[bob->userId()] = QHash<QString, QString>(); + oneTimeKeys[bob->userId()][bob->deviceId()] = SignedCurve25519Key; + auto job = alice->callApi<ClaimKeysJob>(oneTimeKeys); connect(job, &BaseJob::result, this, [aliceOlm, bob, bobEd25519, job] { const auto userId = bob->userId(); diff --git a/autotests/testolmutility.cpp b/autotests/testolmutility.cpp new file mode 100644 index 00000000..cb92a0df --- /dev/null +++ b/autotests/testolmutility.cpp @@ -0,0 +1,131 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org> +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "testolmutility.h" +#include "crypto/qolmaccount.h" +#include "crypto/qolmutility.h" + +using namespace Quotient; + +void TestOlmUtility::canonicalJSON() +{ + // Examples taken from + // https://matrix.org/docs/spec/appendices.html#canonical-json + auto data = QJsonDocument::fromJson(QByteArrayLiteral(R"({ + "auth": { + "success": true, + "mxid": "@john.doe:example.com", + "profile": { + "display_name": "John Doe", + "three_pids": [{ + "medium": "email", + "address": "john.doe@example.org" + }, { + "medium": "msisdn", + "address": "123456789" + }] + }}})")); + + QCOMPARE(data.toJson(QJsonDocument::Compact), + "{\"auth\":{\"mxid\":\"@john.doe:example.com\",\"profile\":{\"display_name\":\"John " + "Doe\",\"three_pids\":[{\"address\":\"john.doe@example.org\",\"medium\":\"email\"},{" + "\"address\":\"123456789\",\"medium\":\"msisdn\"}]},\"success\":true}}"); + + auto data0 = QJsonDocument::fromJson(QByteArrayLiteral(R"({"b":"2","a":"1"})")); + QCOMPARE(data0.toJson(QJsonDocument::Compact), "{\"a\":\"1\",\"b\":\"2\"}"); + + auto data1 = QJsonDocument::fromJson(QByteArrayLiteral(R"({ "本": 2, "日": 1 })")); + QCOMPARE(data1.toJson(QJsonDocument::Compact), "{\"日\":1,\"本\":2}"); + + auto data2 = QJsonDocument::fromJson(QByteArrayLiteral(R"({"a": "\u65E5"})")); + QCOMPARE(data2.toJson(QJsonDocument::Compact), "{\"a\":\"日\"}"); + + auto data3 = QJsonDocument::fromJson(QByteArrayLiteral(R"({ "a": null })")); + QCOMPARE(data3.toJson(QJsonDocument::Compact), "{\"a\":null}"); +} + +void TestOlmUtility::verifySignedOneTimeKey() +{ + auto aliceOlm = std::make_shared<QOlmAccount>("alice:matrix.org", "aliceDevice"); + aliceOlm->createNewAccount(); + aliceOlm->generateOneTimeKeys(1); + auto keys = aliceOlm->oneTimeKeys(); + + auto firstKey = keys.curve25519().keyValueBegin()->second; + auto msgObj = QJsonObject({{"key", firstKey}}); + auto sig = aliceOlm->sign(msgObj); + + auto msg = QJsonDocument(msgObj).toJson(QJsonDocument::Compact); + + auto utilityBuf = new uint8_t[olm_utility_size()]; + auto utility = olm_utility(utilityBuf); + + qDebug() << "1" << aliceOlm->identityKeys().ed25519 << msg << QString::fromUtf8(sig); + + QByteArray signatureBuf1(sig.length(), '0'); + std::copy(sig.begin(), sig.end(), signatureBuf1.begin()); + + auto res = olm_ed25519_verify(utility, + aliceOlm->identityKeys().ed25519.data(), + aliceOlm->identityKeys().ed25519.size(), + msg.data(), + msg.size(), + (void *)sig.data(), + sig.size()); + qDebug() << "2" << aliceOlm->identityKeys().ed25519 << msg << QString::fromUtf8(signatureBuf1); + + QCOMPARE(std::string(olm_utility_last_error(utility)), "SUCCESS"); + QCOMPARE(res, 0); + + delete[](reinterpret_cast<uint8_t *>(utility)); + + QOlmUtility utility2; + auto res2 = std::get<bool>(utility2.ed25519Verify(aliceOlm->identityKeys().ed25519, msg, signatureBuf1)); + + //QCOMPARE(std::string(olm_utility_last_error(utility)), "SUCCESS"); + QCOMPARE(res2, true); +} + +void TestOlmUtility::validUploadKeysRequest() +{ + const auto userId = QStringLiteral("@alice:matrix.org"); + const auto deviceId = QStringLiteral("FKALSOCCC"); + + auto alice = std::make_shared<QOlmAccount>(userId, deviceId); + alice->createNewAccount(); + alice->generateOneTimeKeys(1); + + auto idSig = alice->signIdentityKeys(); + + QJsonObject body + { + {"algorithms", QJsonArray{"m.olm.v1.curve25519-aes-sha2", "m.megolm.v1.aes-sha2"}}, + {"user_id", userId}, + {"device_id", deviceId}, + {"keys", + QJsonObject{ + {QStringLiteral("curve25519:") + deviceId, QString::fromUtf8(alice->identityKeys().curve25519)}, + {QStringLiteral("ed25519:") + deviceId, QString::fromUtf8(alice->identityKeys().ed25519)} + } + }, + {"signatures", + QJsonObject{ + {userId, + QJsonObject{ + {"ed25519:" + deviceId, QString::fromUtf8(idSig)} + } + } + } + } + }; + + DeviceKeys deviceKeys = alice->getDeviceKeys(); + QCOMPARE(QJsonDocument(toJson(deviceKeys)).toJson(QJsonDocument::Compact), + QJsonDocument(body).toJson(QJsonDocument::Compact)); + + QVERIFY(verifyIdentitySignature(fromJson<DeviceKeys>(body), deviceId, userId)); + QVERIFY(verifyIdentitySignature(deviceKeys, deviceId, userId)); +} + +QTEST_MAIN(TestOlmUtility) diff --git a/autotests/testolmutility.h b/autotests/testolmutility.h new file mode 100644 index 00000000..b30249c8 --- /dev/null +++ b/autotests/testolmutility.h @@ -0,0 +1,15 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org> +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include <QtTest/QtTest> + +class TestOlmUtility : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void canonicalJSON(); + void verifySignedOneTimeKey(); + void validUploadKeysRequest(); +}; diff --git a/lib/crypto/qolmaccount.cpp b/lib/crypto/qolmaccount.cpp index 750d7318..e27bbee1 100644 --- a/lib/crypto/qolmaccount.cpp +++ b/lib/crypto/qolmaccount.cpp @@ -110,10 +110,10 @@ IdentityKeys QOlmAccount::identityKeys() const QByteArray QOlmAccount::sign(const QByteArray &message) const { - const size_t signatureLength = olm_account_signature_length(m_account); - QByteArray signatureBuffer(signatureLength, '0'); + QByteArray signatureBuffer(olm_account_signature_length(m_account), '0'); + const auto error = olm_account_sign(m_account, message.data(), message.length(), - signatureBuffer.data(), signatureLength); + signatureBuffer.data(), signatureBuffer.length()); if (error == olm_error()) { throw lastError(m_account); @@ -216,9 +216,8 @@ OlmAccount *Quotient::QOlmAccount::data() return m_account; } -UploadKeysJob *QOlmAccount::createUploadKeyRequest(const OneTimeKeys &oneTimeKeys) +DeviceKeys QOlmAccount::getDeviceKeys() const { - DeviceKeys deviceKeys; deviceKeys.userId = m_userId; deviceKeys.deviceId = m_deviceId; @@ -231,6 +230,13 @@ UploadKeysJob *QOlmAccount::createUploadKeyRequest(const OneTimeKeys &oneTimeKey const auto sign = signIdentityKeys(); deviceKeys.signatures[m_userId]["ed25519:" + m_deviceId] = sign; + return deviceKeys; +} + +UploadKeysJob *QOlmAccount::createUploadKeyRequest(const OneTimeKeys &oneTimeKeys) +{ + auto deviceKeys = getDeviceKeys(); + if (oneTimeKeys.curve25519().isEmpty()) { return new UploadKeysJob(deviceKeys); } @@ -272,36 +278,34 @@ bool Quotient::verifyIdentitySignature(const DeviceKeys &deviceKeys, const auto signingKey = deviceKeys.keys[signKeyId]; const auto signature = deviceKeys.signatures[userId][signKeyId]; - if (signature.isEmpty()) { + qDebug() << "signature empty"; return false; } return ed25519VerifySignature(signingKey, toJson(deviceKeys), signature); } -bool Quotient::ed25519VerifySignature(QString signingKey, - QJsonObject obj, - QString signature) +bool Quotient::ed25519VerifySignature(const QString &signingKey, + const QJsonObject &obj, + const QString &signature) { if (signature.isEmpty()) { return false; } + QJsonObject obj1 = obj; - obj.remove("unsigned"); - obj.remove("signatures"); - - QJsonDocument doc; - doc.setObject(obj); - auto canonicalJson = doc.toJson(QJsonDocument::Compact); + obj1.remove("unsigned"); + obj1.remove("signatures"); - qDebug() << canonicalJson; + auto canonicalJson = QJsonDocument(obj1).toJson(QJsonDocument::Compact); QByteArray signingKeyBuf = signingKey.toUtf8(); QOlmUtility utility; auto signatureBuf = signature.toUtf8(); auto result = utility.ed25519Verify(signingKeyBuf, canonicalJson, signatureBuf); if (std::holds_alternative<QOlmError>(result)) { + qDebug() << "error:" << std::get<QOlmError>(result); return false; } diff --git a/lib/crypto/qolmaccount.h b/lib/crypto/qolmaccount.h index 09ef623a..de78a8af 100644 --- a/lib/crypto/qolmaccount.h +++ b/lib/crypto/qolmaccount.h @@ -72,6 +72,8 @@ public: UploadKeysJob *createUploadKeyRequest(const OneTimeKeys &oneTimeKeys); + DeviceKeys getDeviceKeys() const; + //! Remove the one time key used to create the supplied session. [[nodiscard]] std::optional<QOlmError> removeOneTimeKeys(const std::unique_ptr<QOlmSession> &session) const; @@ -104,9 +106,9 @@ bool verifyIdentitySignature(const DeviceKeys &deviceKeys, const QString &userId); //! checks if the signature is signed by the signing_key -bool ed25519VerifySignature(QString signingKey, - QJsonObject obj, - QString signature); +bool ed25519VerifySignature(const QString &signingKey, + const QJsonObject &obj, + const QString &signature); } // namespace Quotient diff --git a/lib/crypto/qolmutility.cpp b/lib/crypto/qolmutility.cpp index 3c6a14c7..ad78a226 100644 --- a/lib/crypto/qolmutility.cpp +++ b/lib/crypto/qolmutility.cpp @@ -5,6 +5,7 @@ #ifdef Quotient_E2EE_ENABLED #include "crypto/qolmutility.h" #include "olm/olm.h" +#include <QDebug> using namespace Quotient; @@ -19,10 +20,12 @@ QOlmUtility::QOlmUtility() { auto utility = new uint8_t[olm_utility_size()]; m_utility = olm_utility(utility); + qDebug() << "created"; } QOlmUtility::~QOlmUtility() { + qDebug() << "deleted"; olm_clear_utility(m_utility); delete[](reinterpret_cast<uint8_t *>(m_utility)); } @@ -43,15 +46,27 @@ QString QOlmUtility::sha256Utf8Msg(const QString &message) const } std::variant<bool, QOlmError> QOlmUtility::ed25519Verify(const QByteArray &key, - const QByteArray &message, QByteArray &signature) + const QByteArray &message, const QByteArray &signature) { - const auto error = olm_ed25519_verify(m_utility, key.data(), key.length(), - message.data(), message.length(), signature.data(), signature.length()); + QByteArray signatureBuf(signature.length(), '0'); + std::copy(signature.begin(), signature.end(), signatureBuf.begin()); + qDebug() << "3" << key << message << signature; + + const auto ret = olm_ed25519_verify(m_utility, key.data(), key.size(), + message.data(), message.size(), (void *)signatureBuf.data(), signatureBuf.size()); + + const auto error = ret; if (error == olm_error()) { + qDebug() << QString(olm_utility_last_error(m_utility)); return lastError(m_utility); } - return error == 0; + + if (ret != 0) { + qDebug() << "ed25519Verify" << ret; + return false; + } + return true; } diff --git a/lib/crypto/qolmutility.h b/lib/crypto/qolmutility.h index 16c330eb..3de09ab4 100644 --- a/lib/crypto/qolmutility.h +++ b/lib/crypto/qolmutility.h @@ -36,7 +36,7 @@ public: //! \param message QByteArray The message that was signed. //! \param signature QByteArray The signature of the message. std::variant<bool, QOlmError> ed25519Verify(const QByteArray &key, - const QByteArray &message, QByteArray &signature); + const QByteArray &message, const QByteArray &signature); private: |