diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/olm/e2ee.h | 11 | ||||
-rw-r--r-- | lib/olm/qolmaccount.cpp | 83 | ||||
-rw-r--r-- | lib/olm/qolmaccount.h | 35 | ||||
-rw-r--r-- | lib/olm/qolmsession.cpp | 29 | ||||
-rw-r--r-- | lib/olm/qolmsession.h | 49 |
5 files changed, 174 insertions, 33 deletions
diff --git a/lib/olm/e2ee.h b/lib/olm/e2ee.h index 1dee0e42..74f876e4 100644 --- a/lib/olm/e2ee.h +++ b/lib/olm/e2ee.h @@ -66,6 +66,17 @@ struct OneTimeKeys std::optional<QMap<QString, QString>> get(QString keyType) const; }; +//! Struct representing the signed one-time keys. +struct SignedOneTimeKey +{ + //! Required. The unpadded Base64-encoded 32-byte Curve25519 public key. + QString key; + + //! Required. Signatures of the key object. + //! The signature is calculated using the process described at Signing JSON. + QMap<QString, QMap<QString, QString>> signatures; +}; + bool operator==(const IdentityKeys& lhs, const IdentityKeys& rhs); } // namespace Quotient diff --git a/lib/olm/qolmaccount.cpp b/lib/olm/qolmaccount.cpp index 742d7d18..8872f66e 100644 --- a/lib/olm/qolmaccount.cpp +++ b/lib/olm/qolmaccount.cpp @@ -44,36 +44,37 @@ QByteArray getRandom(size_t bufferSize) return buffer; } -QOlmAccount::QOlmAccount(OlmAccount *account) - : m_account(account) -{} +QOlmAccount::QOlmAccount(const QString &userId, const QString &deviceId) + : m_userId(userId) + , m_deviceId(deviceId) +{ +} QOlmAccount::~QOlmAccount() { olm_clear_account(m_account); + delete[](reinterpret_cast<uint8_t *>(m_account)); } -std::optional<QOlmAccount> QOlmAccount::create() +void QOlmAccount::createNewAccount() { - auto account = olm_account(new uint8_t[olm_account_size()]); - size_t randomSize = olm_create_account_random_length(account); + m_account = olm_account(new uint8_t[olm_account_size()]); + size_t randomSize = olm_create_account_random_length(m_account); QByteArray randomData = getRandom(randomSize); - const auto error = olm_create_account(account, randomData.data(), randomSize); + const auto error = olm_create_account(m_account, randomData.data(), randomSize); if (error == olm_error()) { - return std::nullopt; + throw lastError(m_account); } - return std::make_optional<QOlmAccount>(account); } -std::variant<QOlmAccount, OlmError> QOlmAccount::unpickle(QByteArray &pickled, const PicklingMode &mode) +void QOlmAccount::unpickle(QByteArray &pickled, const PicklingMode &mode) { - auto account = olm_account(new uint8_t[olm_account_size()]); + m_account = olm_account(new uint8_t[olm_account_size()]); const QByteArray key = toKey(mode); - const auto error = olm_unpickle_account(account, key.data(), key.length(), pickled.data(), pickled.size()); + const auto error = olm_unpickle_account(m_account, key.data(), key.length(), pickled.data(), pickled.size()); if (error == olm_error()) { - return lastError(account); + throw lastError(m_account); } - return QOlmAccount(account); } std::variant<QByteArray, OlmError> QOlmAccount::pickle(const PicklingMode &mode) @@ -89,13 +90,13 @@ std::variant<QByteArray, OlmError> QOlmAccount::pickle(const PicklingMode &mode) return pickleBuffer; } -std::variant<IdentityKeys, OlmError> QOlmAccount::identityKeys() +IdentityKeys QOlmAccount::identityKeys() const { const size_t keyLength = olm_account_identity_keys_length(m_account); QByteArray keyBuffer(keyLength, '0'); const auto error = olm_account_identity_keys(m_account, keyBuffer.data(), keyLength); if (error == olm_error()) { - return lastError(m_account); + throw lastError(m_account); } const QJsonObject key = QJsonDocument::fromJson(keyBuffer).object(); return IdentityKeys { @@ -104,7 +105,7 @@ std::variant<IdentityKeys, OlmError> QOlmAccount::identityKeys() }; } -std::variant<QString, OlmError> QOlmAccount::sign(const QString &message) const +QByteArray QOlmAccount::sign(const QByteArray &message) const { const size_t signatureLength = olm_account_signature_length(m_account); QByteArray signatureBuffer(signatureLength, '0'); @@ -112,9 +113,19 @@ std::variant<QString, OlmError> QOlmAccount::sign(const QString &message) const signatureBuffer.data(), signatureLength); if (error == olm_error()) { - return lastError(m_account); + throw lastError(m_account); } - return QString::fromUtf8(signatureBuffer); + return signatureBuffer; +} + +QByteArray QOlmAccount::signIdentityKeys() const +{ + const auto keys = identityKeys(); + const QJsonObject j{ {Curve25519Key, QString(keys.curve25519)}, {Ed25519Key, QString(keys.ed25519)} }; + QJsonDocument doc; + doc.setObject(j); + return sign(doc.toJson()); + } size_t QOlmAccount::maxNumberOfOneTimeKeys() const @@ -122,26 +133,25 @@ size_t QOlmAccount::maxNumberOfOneTimeKeys() const return olm_account_max_number_of_one_time_keys(m_account); } -std::optional<OlmError> QOlmAccount::generateOneTimeKeys(size_t numberOfKeys) const +void QOlmAccount::generateOneTimeKeys(size_t numberOfKeys) const { const size_t randomLen = olm_account_generate_one_time_keys_random_length(m_account, numberOfKeys); QByteArray randomBuffer = getRandom(randomLen); const auto error = olm_account_generate_one_time_keys(m_account, numberOfKeys, randomBuffer.data(), randomLen); if (error == olm_error()) { - return lastError(m_account); + throw lastError(m_account); } - return std::nullopt; } -std::variant<OneTimeKeys, OlmError> QOlmAccount::oneTimeKeys() const +OneTimeKeys QOlmAccount::oneTimeKeys() const { const size_t oneTimeKeyLength = olm_account_one_time_keys_length(m_account); QByteArray oneTimeKeysBuffer(oneTimeKeyLength, '0'); const auto error = olm_account_one_time_keys(m_account, oneTimeKeysBuffer.data(), oneTimeKeyLength); if (error == olm_error()) { - return lastError(m_account); + throw lastError(m_account); } const auto json = QJsonDocument::fromJson(oneTimeKeysBuffer).object(); OneTimeKeys oneTimeKeys; @@ -157,4 +167,29 @@ std::variant<OneTimeKeys, OlmError> QOlmAccount::oneTimeKeys() const return oneTimeKeys; } +QMap<QString, SignedOneTimeKey> QOlmAccount::signOneTimeKeys(const OneTimeKeys &keys) const +{ + QMap<QString, SignedOneTimeKey> signedOneTimeKeys; + for (const auto &keyid : keys.curve25519().keys()) { + const auto oneTimeKey = keys.curve25519()[keyid]; + QByteArray sign = signOneTimeKey(oneTimeKey); + signedOneTimeKeys["signed_curve25519:" + keyid] = signedOneTimeKey(oneTimeKey.toUtf8(), sign); + } + return signedOneTimeKeys; +} + +SignedOneTimeKey QOlmAccount::signedOneTimeKey(const QByteArray &key, const QString &signature) const +{ + SignedOneTimeKey sign{}; + sign.key = key; + sign.signatures = {{m_userId, {{"ed25519:" + m_deviceId, signature}}}}; + return sign; +} + +QByteArray QOlmAccount::signOneTimeKey(const QString &key) const +{ + QJsonDocument j(QJsonObject{{"key", key}}); + return sign(j.toJson()); +} + #endif diff --git a/lib/olm/qolmaccount.h b/lib/olm/qolmaccount.h index c478c781..3b55212d 100644 --- a/lib/olm/qolmaccount.h +++ b/lib/olm/qolmaccount.h @@ -20,36 +20,53 @@ namespace Quotient { class QOlmAccount { public: + QOlmAccount(const QString &userId, const QString &deviceId); ~QOlmAccount(); //! Creates a new instance of OlmAccount. During the instantiation //! the Ed25519 fingerprint key pair and the Curve25519 identity key //! pair are generated. For more information see <a //! href="https://matrix.org/docs/guides/e2e_implementation.html#keys-used-in-end-to-end-encryption">here</a>. - static std::optional<QOlmAccount> create(); - static std::variant<QOlmAccount, OlmError> unpickle(QByteArray &picked, const PicklingMode &mode); + //! This needs to be called before any other action or use unpickle() instead. + void createNewAccount(); + + //! Deserialises from encrypted Base64 that was previously obtained by pickling a `QOlmAccount`. + //! This needs to be called before any other action or use createNewAccount() instead. + void unpickle(QByteArray &picked, const PicklingMode &mode); //! Serialises an OlmAccount to encrypted Base64. std::variant<QByteArray, OlmError> pickle(const PicklingMode &mode); - std::variant<IdentityKeys, OlmError> identityKeys(); + + //! Returns the account's public identity keys already formatted as JSON + IdentityKeys identityKeys() const; //! Returns the signature of the supplied message. - std::variant<QString, OlmError> sign(const QString &message) const; + QByteArray sign(const QByteArray &message) const; + + //! Sign identity keys. + QByteArray signIdentityKeys() const; //! Maximum number of one time keys that this OlmAccount can //! currently hold. size_t maxNumberOfOneTimeKeys() const; //! Generates the supplied number of one time keys. - std::optional<OlmError> generateOneTimeKeys(size_t numberOfKeys) const; + void generateOneTimeKeys(size_t numberOfKeys) const; //! Gets the OlmAccount's one time keys formatted as JSON. - std::variant<OneTimeKeys, OlmError> oneTimeKeys() const; + OneTimeKeys oneTimeKeys() const; + + //! Sign all time key. + QMap<QString, SignedOneTimeKey> signOneTimeKeys(const OneTimeKeys &keys) const; + + //! Sign one time key. + QByteArray signOneTimeKey(const QString &key) const; - // HACK do not use directly - QOlmAccount(OlmAccount *account); + SignedOneTimeKey signedOneTimeKey(const QByteArray &key, const QString &signature) const; private: - OlmAccount *m_account; + OlmAccount *m_account = nullptr; + QString m_userId; + QString m_deviceId; }; } // namespace Quotient diff --git a/lib/olm/qolmsession.cpp b/lib/olm/qolmsession.cpp new file mode 100644 index 00000000..32a108a8 --- /dev/null +++ b/lib/olm/qolmsession.cpp @@ -0,0 +1,29 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org> +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "olm/qolmsession.h" + +using namespace Quotient; + +std::optional<OlmMessage> fromTypeAndCipthertext(size_t messageType, const QByteArray &ciphertext) +{ + if (messageType == OLM_MESSAGE_TYPE_PRE_KEY) { + return PreKeyMessage { ciphertext }; + } else if (messageType == OLM_MESSAGE_TYPE_MESSAGE) { + return Message { ciphertext }; + } + return std::nullopt; +} + +std::pair<OlmMessageType, QByteArray> toPair(const OlmMessage &message) +{ + return std::visit([](auto &arg) { + using T = std::decay_t<decltype(arg)>; + if constexpr (std::is_same_v<T, Message>) { + return std::make_pair<OlmMessageType, QByteArray>(MessageType, QByteArray(arg.message)); + } else if constexpr (std::is_same_v<T, PreKeyMessage>) { + return std::make_pair<OlmMessageType, QByteArray>(PreKeyType, QByteArray(arg.message)); + } + }, message); +} diff --git a/lib/olm/qolmsession.h b/lib/olm/qolmsession.h new file mode 100644 index 00000000..08f47331 --- /dev/null +++ b/lib/olm/qolmsession.h @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org> +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include "olm/e2ee.h" +#include "olm/olm.h" +#include "olm/errors.h" +#include <variant> + +namespace Quotient { + +//! An encrypted Olm message. +struct Message { + QByteArray message; +}; + +//! A encrypted Olm pre-key message. +//! +//! This message, unlike a normal Message, can be used to create new Olm sessions. +struct PreKeyMessage +{ + QByteArray message; +}; + +enum OlmMessageType +{ + PreKeyType, + MessageType, +}; + +using OlmMessage = std::variant<Message, PreKeyMessage>; + +std::optional<OlmMessage> fromTypeAndCipthertext(size_t messageType, const QByteArray &ciphertext); + +std::pair<OlmMessageType, QByteArray> toPair(const OlmMessage &message); + +//class QOlmSession +//{ +// /// Creates an inbound session for sending/receiving messages from a received 'prekey' message. +// static std::variant<std::unique_ptr<QOlmSession>, OlmError> createInboundSession(const QOlmAccount &account, +// PreKeyMessage &message); +// +////private: +// //static std::variant<std::unique_ptr<QOlmSession>, OlmError> createSessionWith(std::function<std::variant<size_t(OlmSession *)>> func); +//} + +} |