aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--lib/olm/message.cpp35
-rw-r--r--lib/olm/message.h46
-rw-r--r--lib/olm/qolmaccount.cpp5
-rw-r--r--lib/olm/qolmaccount.h4
-rw-r--r--lib/olm/qolminboundsession.h1
-rw-r--r--lib/olm/qolmoutboundsession.h1
-rw-r--r--lib/olm/session.cpp155
-rw-r--r--lib/olm/session.h46
9 files changed, 295 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a359ae07..476b7d81 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -184,6 +184,8 @@ list(APPEND lib_SRCS
lib/olm/qolmoutboundsession.cpp
lib/olm/utils.cpp
lib/olm/errors.cpp
+ lib/olm/session.cpp
+ lib/olm/message.cpp
)
# Configure API files generation
diff --git a/lib/olm/message.cpp b/lib/olm/message.cpp
new file mode 100644
index 00000000..0998a66b
--- /dev/null
+++ b/lib/olm/message.cpp
@@ -0,0 +1,35 @@
+// SPDX-FileCopyrightText: 2021 Alexey Andreyev <aa13q@ya.ru>
+//
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#ifdef Quotient_E2EE_ENABLED
+#include "olm/message.h"
+
+using namespace Quotient;
+
+Message::Message(const QByteArray &ciphertext, Message::Type type)
+ : QByteArray(std::move(ciphertext)), _messageType(type)
+{
+ Q_ASSERT_X(!ciphertext.isEmpty(), "olm message", "Ciphertext is empty");
+}
+
+Message::Message(QByteArray ciphertext) : QByteArray(std::move(ciphertext))
+{
+ Q_ASSERT_X(!ciphertext.isEmpty(), "olm message", "Ciphertext is empty");
+}
+
+Message::Type Message::type() const
+{
+ return _messageType;
+}
+
+QByteArray Message::toCiphertext() const
+{
+ return QByteArray(*this);
+}
+
+
+#endif // Quotient_E2EE_ENABLED
+
+
+
diff --git a/lib/olm/message.h b/lib/olm/message.h
new file mode 100644
index 00000000..6c8ab485
--- /dev/null
+++ b/lib/olm/message.h
@@ -0,0 +1,46 @@
+// SPDX-FileCopyrightText: 2021 Alexey Andreyev <aa13q@ya.ru>
+//
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#pragma once
+
+#ifdef Quotient_E2EE_ENABLED
+
+#include <QtCore/QObject>
+#include <QtCore/QByteArray>
+
+namespace Quotient {
+
+/*! \brief A wrapper around an olm encrypted message
+ *
+ * This class encapsulates a Matrix olm encrypted message,
+ * passed in either of 2 forms: a general message or a pre-key message.
+ *
+ * The class provides functions to get a type and the ciphertext.
+ */
+class Message : private QByteArray {
+ Q_GADGET
+public:
+ enum Type {
+ General,
+ PreKey,
+ };
+ Q_ENUM(Type)
+
+ Message() = default;
+ explicit Message(const QByteArray& ciphertext, Type type = General);
+ explicit Message(QByteArray ciphertext);
+
+ static Message fromCiphertext(QByteArray ciphertext);
+
+ Q_INVOKABLE Type type() const;
+ Q_INVOKABLE QByteArray toCiphertext() const;
+
+private:
+ Type _messageType = General;
+};
+
+
+} //namespace Quotient
+
+#endif // Quotient_E2EE_ENABLED
diff --git a/lib/olm/qolmaccount.cpp b/lib/olm/qolmaccount.cpp
index 8872f66e..9530d675 100644
--- a/lib/olm/qolmaccount.cpp
+++ b/lib/olm/qolmaccount.cpp
@@ -192,4 +192,9 @@ QByteArray QOlmAccount::signOneTimeKey(const QString &key) const
return sign(j.toJson());
}
+OlmAccount *Quotient::QOlmAccount::data()
+{
+ return m_account;
+}
+
#endif
diff --git a/lib/olm/qolmaccount.h b/lib/olm/qolmaccount.h
index 3b55212d..3260ca71 100644
--- a/lib/olm/qolmaccount.h
+++ b/lib/olm/qolmaccount.h
@@ -63,6 +63,10 @@ public:
QByteArray signOneTimeKey(const QString &key) const;
SignedOneTimeKey signedOneTimeKey(const QByteArray &key, const QString &signature) const;
+ OlmAccount *data();
+
+ // HACK do not use directly
+ QOlmAccount(OlmAccount *account);
private:
OlmAccount *m_account = nullptr;
QString m_userId;
diff --git a/lib/olm/qolminboundsession.h b/lib/olm/qolminboundsession.h
index eb698868..739a8411 100644
--- a/lib/olm/qolminboundsession.h
+++ b/lib/olm/qolminboundsession.h
@@ -46,5 +46,6 @@ private:
};
using QOlmInboundGroupSessionPtr = std::unique_ptr<QOlmInboundGroupSession>;
+using OlmInboundGroupSessionPtr = std::unique_ptr<OlmInboundGroupSession>;
} // namespace Quotient
#endif
diff --git a/lib/olm/qolmoutboundsession.h b/lib/olm/qolmoutboundsession.h
index a642f581..70c4d27f 100644
--- a/lib/olm/qolmoutboundsession.h
+++ b/lib/olm/qolmoutboundsession.h
@@ -49,5 +49,6 @@ private:
};
using QOlmOutboundGroupSessionPtr = std::unique_ptr<QOlmOutboundGroupSession>;
+using OlmOutboundGroupSessionPtr = std::unique_ptr<OlmOutboundGroupSession>;
}
#endif
diff --git a/lib/olm/session.cpp b/lib/olm/session.cpp
new file mode 100644
index 00000000..a2a7d28a
--- /dev/null
+++ b/lib/olm/session.cpp
@@ -0,0 +1,155 @@
+// SPDX-FileCopyrightText: 2021 Alexey Andreyev <aa13q@ya.ru>
+//
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#ifdef Quotient_E2EE_ENABLED
+#include "olm/session.h"
+#include "olm/utils.h"
+#include "logging.h"
+
+using namespace Quotient;
+
+OlmError lastError(OlmSession* session) {
+ const std::string error_raw = olm_session_last_error(session);
+
+ return fromString(error_raw);
+}
+
+Quotient::QOlmSession::~QOlmSession()
+{
+ olm_clear_session(m_session);
+}
+
+OlmSession* QOlmSession::create()
+{
+ return olm_session(new uint8_t[olm_session_size()]);
+}
+
+std::unique_ptr<QOlmSession> QOlmSession::createInbound(QOlmAccount &account, const Message &preKeyMessage, bool from, const QString &theirIdentityKey)
+{
+ if (preKeyMessage.type() != Message::PreKey) {
+ qCDebug(E2EE) << "The message is not a pre-key";
+ throw BadMessageFormat;
+ }
+
+ const auto olmSession = create();
+
+ QByteArray oneTimeKeyMessageBuf = preKeyMessage.toCiphertext();
+ QByteArray theirIdentityKeyBuf = theirIdentityKey.toUtf8();
+ size_t error = 0;
+ if (from) {
+ error = olm_create_inbound_session_from(olmSession, account.data(), theirIdentityKeyBuf.data(), theirIdentityKeyBuf.length(), oneTimeKeyMessageBuf.data(), oneTimeKeyMessageBuf.length());
+ } else {
+ error = olm_create_inbound_session(olmSession, account.data(), oneTimeKeyMessageBuf.data(), oneTimeKeyMessageBuf.length());
+ }
+
+ if (error == olm_error()) {
+ throw lastError(olmSession);
+ }
+
+ return std::make_unique<QOlmSession>(olmSession);
+}
+
+std::unique_ptr<QOlmSession> QOlmSession::createInboundSession(QOlmAccount& account, const Message &preKeyMessage)
+{
+ return createInbound(account, preKeyMessage);
+}
+
+std::unique_ptr<QOlmSession> QOlmSession::createInboundSessionFrom(QOlmAccount &account, const QString &theirIdentityKey, const Message &preKeyMessage)
+{
+ return createInbound(account, preKeyMessage, true, theirIdentityKey);
+}
+
+std::unique_ptr<QOlmSession> QOlmSession::createOutboundSession(QOlmAccount &account, const QString &theirIdentityKey, const QString &theirOneTimeKey)
+{
+ auto *olmOutboundSession = create();
+ const auto randomLen = olm_create_outbound_session_random_length(olmOutboundSession);
+ QByteArray randomBuf = getRandom(randomLen);
+
+ QByteArray theirIdentityKeyBuf = theirIdentityKey.toUtf8();
+ QByteArray theirOneTimeKeyBuf = theirOneTimeKey.toUtf8();
+ const auto error = olm_create_outbound_session(olmOutboundSession,
+ account.data(),
+ theirIdentityKeyBuf.data(), theirIdentityKeyBuf.length(),
+ theirOneTimeKeyBuf.data(), theirOneTimeKeyBuf.length(),
+ reinterpret_cast<uint8_t *>(randomBuf.data()), randomBuf.length());
+
+ if (error == olm_error()) {
+ throw lastError(olmOutboundSession);
+ }
+
+ randomBuf.clear();
+ return std::make_unique<QOlmSession>(olmOutboundSession);
+}
+
+std::variant<QByteArray, OlmError> QOlmSession::pickle(const PicklingMode &mode)
+{
+ QByteArray pickledBuf(olm_pickle_session_length(m_session), '0');
+ QByteArray key = toKey(mode);
+ const auto error = olm_pickle_session(m_session, key.data(), key.length(),
+ pickledBuf.data(), pickledBuf.length());
+
+ if (error == olm_error()) {
+ return lastError(m_session);
+ }
+
+ key.clear();
+
+ return pickledBuf;
+}
+
+std::variant<std::unique_ptr<QOlmSession>, OlmError> QOlmSession::unpickle(QByteArray &pickled, const PicklingMode &mode)
+{
+ QByteArray pickledBuf = pickled;
+ auto *olmSession = create();
+ QByteArray key = toKey(mode);
+ const auto error = olm_unpickle_session(olmSession, key.data(), key.length(),
+ pickled.data(), pickled.length());
+ if (error == olm_error()) {
+ return lastError(olmSession);
+ }
+
+ key.clear();
+ return std::make_unique<QOlmSession>(olmSession);
+}
+
+std::variant<Message, OlmError> QOlmSession::encrypt(const QString &plaintext)
+{
+ QByteArray plaintextBuf = plaintext.toUtf8();
+ const auto messageMaxLen = olm_encrypt_message_length(m_session, plaintextBuf.length());
+ QByteArray messageBuf(messageMaxLen, '0');
+ const auto randomLen = olm_encrypt_random_length(m_session);
+ QByteArray randomBuf = getRandom(randomLen);
+ const auto error = olm_encrypt(m_session,
+ reinterpret_cast<uint8_t *>(plaintextBuf.data()), plaintextBuf.length(),
+ reinterpret_cast<uint8_t *>(randomBuf.data()), randomBuf.length(),
+ reinterpret_cast<uint8_t *>(messageBuf.data()), messageBuf.length());
+
+ if (error == olm_error()) {
+ return lastError(m_session);
+ }
+
+ return Message::fromCiphertext(messageBuf);
+}
+
+QByteArray QOlmSession::sessionId() const
+{
+ const auto idMaxLength = olm_session_id_length(m_session);
+ QByteArray idBuffer(idMaxLength, '0');
+ const auto error = olm_session_id(m_session, reinterpret_cast<uint8_t *>(idBuffer.data()),
+ idBuffer.length());
+ if (error == olm_error()) {
+ throw lastError(m_session);
+ }
+ return idBuffer;
+}
+
+QOlmSession::QOlmSession(OlmSession *session): m_session(session)
+{
+
+}
+
+#endif // Quotient_E2EE_ENABLED
+
+
+
diff --git a/lib/olm/session.h b/lib/olm/session.h
new file mode 100644
index 00000000..76c1df29
--- /dev/null
+++ b/lib/olm/session.h
@@ -0,0 +1,46 @@
+// SPDX-FileCopyrightText: 2021 Alexey Andreyev <aa13q@ya.ru>
+//
+// SPDX-License-Identifier: LGPL-2.1-or-later
+
+#pragma once
+
+#ifdef Quotient_E2EE_ENABLED
+
+#include "olm/e2ee.h"
+#include "olm/message.h"
+#include "olm/errors.h"
+#include "olm/qolmaccount.h"
+
+namespace Quotient {
+
+//! Either an outbound or inbound session for secure communication.
+class QOlmSession
+{
+public:
+ ~QOlmSession();
+ //! Creates an inbound session for sending/receiving messages from a received 'prekey' message.
+ static std::unique_ptr<QOlmSession> createInboundSession(QOlmAccount& account, const Message& preKeyMessage);
+ static std::unique_ptr<QOlmSession> createInboundSessionFrom(QOlmAccount& account, const QString& theirIdentityKey, const Message& preKeyMessage);
+ static std::unique_ptr<QOlmSession> createOutboundSession(QOlmAccount& account, const QString& theirIdentityKey, const QString& theirOneTimeKey);
+ //! Serialises an `QOlmSession` to encrypted Base64.
+ std::variant<QByteArray, OlmError> pickle(const PicklingMode &mode);
+ //! Deserialises from encrypted Base64 that was previously obtained by pickling a `QOlmSession`.
+ static std::variant<std::unique_ptr<QOlmSession>, OlmError> unpickle(QByteArray &pickled, const PicklingMode &mode);
+ //! Encrypts a plaintext message using the session.
+ std::variant<Message, OlmError> encrypt(const QString &plaintext);
+ // TODO: WiP
+
+ //! Get a base64-encoded identifier for this session.
+ QByteArray sessionId() const;
+
+ QOlmSession(OlmSession* session);
+private:
+ //! Helper function for creating new sessions and handling errors.
+ static OlmSession* create();
+ static std::unique_ptr<QOlmSession> createInbound(QOlmAccount& account, const Message& preKeyMessage, bool from = false, const QString& theirIdentityKey = "");
+ OlmSession* m_session;
+};
+
+} //namespace Quotient
+
+#endif // Quotient_E2EE_ENABLED