Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85832796
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Authored By
Unknown
Size
20 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/base/maybe.hpp b/src/base/maybe.hpp
index 32591d2..9683560 100644
--- a/src/base/maybe.hpp
+++ b/src/base/maybe.hpp
@@ -1,36 +1,36 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2021 Tusooa Zhu <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include "libkazv-config.hpp"
#include <string>
#include <optional>
namespace Kazv
{
class NotBut
{
std::string m_reason;
public:
explicit NotBut(std::string r) : m_reason(r) {}
std::string reason() const { return m_reason; }
};
template<class T>
class Maybe : public std::optional<T>
{
std::string m_reason;
using BaseT = std::optional<T>;
public:
- Maybe(T x) : BaseT(x) {}
+ Maybe(T x) : BaseT(std::move(x)) {}
Maybe(NotBut r) : BaseT(), m_reason(r.reason()) {}
std::string reason() const { return m_reason; }
};
using MaybeString = Maybe<std::string>;
}
diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt
index f39de5f..cf7ed08 100644
--- a/src/crypto/CMakeLists.txt
+++ b/src/crypto/CMakeLists.txt
@@ -1,26 +1,27 @@
set(kazvcrypto_SRCS
crypto.cpp
session.cpp
inbound-group-session.cpp
outbound-group-session.cpp
aes-256-ctr.cpp
base64.cpp
sha256.cpp
)
add_library(kazvcrypto ${kazvcrypto_SRCS})
add_library(libkazv::kazvcrypto ALIAS kazvcrypto)
set_target_properties(kazvcrypto PROPERTIES VERSION ${libkazv_VERSION_STRING} SOVERSION ${libkazv_SOVERSION})
target_link_libraries(kazvcrypto PUBLIC kazvbase Olm::Olm ${CRYPTOPP_TARGET_NAME})
+target_link_libraries(kazvcrypto PRIVATE vodozemac::vodozemac)
target_include_directories(kazvcrypto PRIVATE .)
target_include_directories(kazvcrypto
INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:include/kazv/crypto>
)
install(TARGETS kazvcrypto EXPORT libkazvTargets LIBRARY)
diff --git a/src/crypto/crypto-util-p.hpp b/src/crypto/crypto-util-p.hpp
new file mode 100644
index 0000000..00a0666
--- /dev/null
+++ b/src/crypto/crypto-util-p.hpp
@@ -0,0 +1,28 @@
+/*
+ * This file is part of libkazv.
+ * SPDX-FileCopyrightText: 2024 tusooa <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#pragma once
+#include <libkazv-config.hpp>
+#include <array>
+#include <vector>
+#include <cstdint>
+#include <maybe.hpp>
+
+namespace Kazv
+{
+ static const std::array<std::uint8_t, 32> VODOZEMAC_PICKLE_KEY = {};
+ static const std::vector<std::uint8_t> OLM_PICKLE_KEY = {'x', 'x', 'x'};
+
+ template<class Func>
+ static auto checkVodozemacError(Func &&func) -> Maybe<std::invoke_result_t<Func>>
+ {
+ try {
+ return std::forward<Func>(func)();
+ } catch (const std::exception &e) {
+ return NotBut(e.what());
+ }
+ }
+}
diff --git a/src/crypto/inbound-group-session-p.hpp b/src/crypto/inbound-group-session-p.hpp
index 699a073..ecdd1b6 100644
--- a/src/crypto/inbound-group-session-p.hpp
+++ b/src/crypto/inbound-group-session-p.hpp
@@ -1,71 +1,71 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2021 Tusooa Zhu <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include <libkazv-config.hpp>
#include "inbound-group-session.hpp"
-#include <olm/olm.h>
+#include <vodozemac.h>
#include <immer/map.hpp>
namespace Kazv
{
struct KeyOfDecryptedEvent
{
std::string eventId;
Timestamp originServerTs;
};
inline void to_json(nlohmann::json &j, const KeyOfDecryptedEvent &k)
{
j = nlohmann::json::object();
j["eventId"] = k.eventId;
j["originServerTs"] = k.originServerTs;
}
inline void from_json(const nlohmann::json &j, KeyOfDecryptedEvent &k)
{
k.eventId = j.at("eventId");
k.originServerTs = j.at("originServerTs");
}
inline bool operator==(KeyOfDecryptedEvent a, KeyOfDecryptedEvent b)
{
return a.eventId == b.eventId
&& a.originServerTs == b.originServerTs;
}
inline bool operator!=(KeyOfDecryptedEvent a, KeyOfDecryptedEvent b)
{
return !(a == b);
}
struct InboundGroupSessionPrivate
{
InboundGroupSessionPrivate();
InboundGroupSessionPrivate(std::string sessionKey, std::string ed25519Key);
InboundGroupSessionPrivate(const InboundGroupSessionPrivate &that);
~InboundGroupSessionPrivate() = default;
- ByteArray sessionData;
- OlmInboundGroupSession *session;
+ std::optional<rust::Box<vodozemac::megolm::InboundGroupSession>> session;
std::string ed25519Key;
bool valid{false};
immer::map<std::uint32_t /* index */, KeyOfDecryptedEvent> decryptedEvents;
std::size_t checkError(std::size_t code) const;
std::string error() const;
std::string pickle() const;
bool unpickle(std::string pickleData);
+ bool unpickleFromLibolm(std::string pickleData);
};
}
diff --git a/src/crypto/inbound-group-session.cpp b/src/crypto/inbound-group-session.cpp
index 092070e..4d124e4 100644
--- a/src/crypto/inbound-group-session.cpp
+++ b/src/crypto/inbound-group-session.cpp
@@ -1,187 +1,170 @@
/*
* This file is part of libkazv.
- * SPDX-FileCopyrightText: 2021 Tusooa Zhu <tusooa@kazv.moe>
+ * SPDX-FileCopyrightText: 2021-2024 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
-
#include "inbound-group-session-p.hpp"
+#include "crypto-util-p.hpp"
#include <types.hpp>
#include <debug.hpp>
namespace Kazv
{
- std::size_t InboundGroupSessionPrivate::checkError(std::size_t code) const
- {
- if (code == olm_error()) {
- kzo.crypto.warn() << "Olm inbound group session error: "
- << olm_inbound_group_session_last_error(session) << std::endl;
- }
- return code;
- }
-
- std::string InboundGroupSessionPrivate::error() const
- {
- return olm_inbound_group_session_last_error(session);
- }
-
-
InboundGroupSessionPrivate::InboundGroupSessionPrivate()
- : sessionData(olm_inbound_group_session_size(), '\0')
- , session(olm_inbound_group_session(sessionData.data()))
+ : session(std::nullopt)
{
}
InboundGroupSessionPrivate::InboundGroupSessionPrivate(std::string sessionKey, std::string ed25519Key)
: InboundGroupSessionPrivate()
{
+ valid = false;
this->ed25519Key = ed25519Key;
- auto keyBuf = ByteArray(sessionKey.begin(), sessionKey.end());
+ auto keyRust = checkVodozemacError([&]() { return vodozemac::megolm::session_key_from_base64(rust::Str(sessionKey)); });
+ if (!keyRust) {
+ return;
+ }
- auto res = checkError(olm_init_inbound_group_session(session, keyBuf.data(), keyBuf.size()));
- if (res != olm_error()) {
+ this->session = checkVodozemacError([&]() { return vodozemac::megolm::new_inbound_group_session(*(keyRust.value())); });
+ if (this->session.has_value()) {
valid = true;
}
}
InboundGroupSessionPrivate::InboundGroupSessionPrivate(const InboundGroupSessionPrivate &that)
: InboundGroupSessionPrivate()
{
ed25519Key = that.ed25519Key;
valid = unpickle(that.pickle());
decryptedEvents = that.decryptedEvents;
}
std::string InboundGroupSessionPrivate::pickle() const
{
- auto pickleData = std::string(olm_pickle_inbound_group_session_length(session), '\0');
- auto key = ByteArray(3, 'x');
- checkError(olm_pickle_inbound_group_session(session,
- key.data(), key.size(),
- pickleData.data(), pickleData.size()));
- return pickleData;
+ auto pickleData = this->session.value()->pickle(
+ VODOZEMAC_PICKLE_KEY);
+ return static_cast<std::string>(pickleData);
}
bool InboundGroupSessionPrivate::unpickle(std::string pickleData)
{
- auto key = ByteArray(3, 'x');
- auto res = checkError(olm_unpickle_inbound_group_session(
- session,
- key.data(), key.size(),
- pickleData.data(), pickleData.size()));
-
- return res != olm_error();
+ this->session = checkVodozemacError([&]() {
+ return vodozemac::megolm::inbound_group_session_from_pickle(pickleData, VODOZEMAC_PICKLE_KEY);
+ });
+ return this->session.has_value();
}
+ bool InboundGroupSessionPrivate::unpickleFromLibolm(std::string pickleData)
+ {
+ this->session = checkVodozemacError([&]() {
+ return vodozemac::megolm::inbound_group_session_from_libolm_pickle(pickleData, rust::Slice<const unsigned char>(OLM_PICKLE_KEY.data(), OLM_PICKLE_KEY.size()));
+ });
+ return this->session.has_value();
+ }
InboundGroupSession::InboundGroupSession()
: m_d(new InboundGroupSessionPrivate)
{
}
InboundGroupSession::InboundGroupSession(std::string sessionKey, std::string ed25519Key)
: m_d(new InboundGroupSessionPrivate(std::move(sessionKey), std::move(ed25519Key)))
{
}
InboundGroupSession::~InboundGroupSession() = default;
InboundGroupSession::InboundGroupSession(const InboundGroupSession &that)
: m_d(new InboundGroupSessionPrivate(*that.m_d))
{
}
InboundGroupSession::InboundGroupSession(InboundGroupSession &&that)
: m_d(std::move(that.m_d))
{
}
InboundGroupSession &InboundGroupSession::operator=(const InboundGroupSession &that)
{
m_d.reset(new InboundGroupSessionPrivate(*that.m_d));
return *this;
}
InboundGroupSession &InboundGroupSession::operator=(InboundGroupSession &&that)
{
m_d = std::move(that.m_d);
return *this;
}
bool InboundGroupSession::valid() const
{
return m_d && m_d->valid;
}
MaybeString InboundGroupSession::decrypt(std::string message, std::string eventId, std::int_fast64_t originServerTs)
{
- ByteArray msgBuffer(message.begin(), message.end());
- ByteArray msgBuffer2 = msgBuffer;
-
- auto size = m_d->checkError(olm_group_decrypt_max_plaintext_length(
- m_d->session,
- msgBuffer.data(), msgBuffer.size()));
- if (size == olm_error()) {
- return NotBut(m_d->error());
+ auto messageRust = checkVodozemacError([&]() { return vodozemac::megolm::megolm_message_from_base64(rust::Str(message)); });
+ if (!messageRust.has_value()) {
+ return NotBut(messageRust.reason());
}
+ auto decrypted = checkVodozemacError([&]() { return m_d->session.value()->decrypt(*(messageRust.value())); });
- auto plainText = ByteArray(size, '\0');
- std::uint32_t messageIndex;
-
- auto actualSize = m_d->checkError(olm_group_decrypt(
- m_d->session,
- msgBuffer2.data(), msgBuffer2.size(),
- plainText.data(), plainText.size(),
- &messageIndex));
-
- if (actualSize == olm_error()) {
- return NotBut(m_d->error());
+ if (!decrypted.has_value()) {
+ return NotBut(decrypted.reason());
}
+ auto [plainText, messageIndex] = *decrypted;
+
// Check for possible replay attack
auto keyForThisMsg = KeyOfDecryptedEvent{eventId, originServerTs};
if (! m_d->decryptedEvents.find(messageIndex)) {
m_d->decryptedEvents = std::move(m_d->decryptedEvents)
.set(messageIndex, keyForThisMsg);
} else { // already decrypted in the past
auto key = m_d->decryptedEvents.at(messageIndex);
if (key != keyForThisMsg) {
return NotBut("This message has been decrypted in the past, but eventId or originServerTs does not match");
}
}
- return std::string(plainText.begin(), plainText.begin() + actualSize);
+ return std::string(plainText.begin(), plainText.end());
}
std::string InboundGroupSession::ed25519Key() const
{
return m_d->ed25519Key;
}
void to_json(nlohmann::json &j, const InboundGroupSession &s)
{
j = nlohmann::json::object();
+ j["version"] = 1;
j["ed25519Key"] = s.m_d->ed25519Key;
j["valid"] = s.m_d->valid;
j["decryptedEvents"] = s.m_d->decryptedEvents;
if (s.m_d->valid) {
j["session"] = s.m_d->pickle();
}
}
void from_json(const nlohmann::json &j, InboundGroupSession &s)
{
s.m_d->ed25519Key = j.at("ed25519Key");
s.m_d->valid = j.at("valid");
s.m_d->decryptedEvents = j.at("decryptedEvents");
if (s.m_d->valid) {
- s.m_d->valid = s.m_d->unpickle(j.at("session"));
+ if (j.contains("version") && j["version"] == 1) { // vodozemac format
+ s.m_d->valid = s.m_d->unpickle(j.at("session"));
+ } else { // libolm format
+ s.m_d->valid = s.m_d->unpickleFromLibolm(j.at("session"));
+ }
}
}
}
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 5082a94..4a1b923 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -1,118 +1,119 @@
include(CTest)
set(KAZVTEST_RESPATH ${CMAKE_CURRENT_SOURCE_DIR}/resources)
configure_file(kazvtest-respath.hpp.in kazvtest-respath.hpp)
function(libkazv_add_tests)
set(options "")
set(oneValueArgs "")
set(multiValueArgs EXTRA_LINK_LIBRARIES EXTRA_INCLUDE_DIRECTORIES)
cmake_parse_arguments(PARSE_ARGV 0 libkazv_add_tests "${options}" "${oneValueArgs}" "${multiValueArgs}")
foreach(test_source ${libkazv_add_tests_UNPARSED_ARGUMENTS})
string(REGEX REPLACE "\\.cpp$" "" test_executable "${test_source}")
string(REGEX REPLACE "/|\\\\" "--" test_executable "${test_executable}")
message(STATUS "Test ${test_executable} added")
add_executable("${test_executable}" "${test_source}")
target_link_libraries("${test_executable}"
PRIVATE Catch2::Catch2WithMain
Threads::Threads
${libkazv_add_tests_EXTRA_LINK_LIBRARIES}
)
target_include_directories(
"${test_executable}"
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/..
${libkazv_add_tests_EXTRA_INCLUDE_DIRECTORIES}
)
target_compile_definitions("${test_executable}" PRIVATE CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS)
add_test(NAME "${test_executable}" COMMAND "${test_executable}" "--allow-running-no-tests" "~[needs-internet]")
endforeach()
endfunction()
libkazv_add_tests(
event-test.cpp
cursorutiltest.cpp
base/serialization-test.cpp
base/types-test.cpp
base/immer-utils-test.cpp
base/json-utils-test.cpp
EXTRA_LINK_LIBRARIES kazvbase
)
add_library(client-test-lib SHARED client/client-test-util.cpp)
target_link_libraries(client-test-lib PUBLIC kazvjob kazvclient)
libkazv_add_tests(
client/discovery-test.cpp
client/sync-test.cpp
client/content-test.cpp
client/paginate-test.cpp
client/util-test.cpp
client/serialization-test.cpp
client/encrypted-file-test.cpp
client/sdk-test.cpp
client/thread-safety-test.cpp
client/room-test.cpp
client/random-generator-test.cpp
client/profile-test.cpp
client/kick-test.cpp
client/ban-test.cpp
client/join-test.cpp
client/keys-test.cpp
client/device-ops-test.cpp
client/send-test.cpp
client/encryption-test.cpp
client/redact-test.cpp
client/tagging-test.cpp
client/account-data-test.cpp
client/room/room-actions-test.cpp
client/room/local-echo-test.cpp
client/room/event-relationships-test.cpp
client/room/member-membership-test.cpp
client/push-rules-desc-test.cpp
client/notification-handler-test.cpp
client/validator-test.cpp
client/power-levels-desc-test.cpp
client/client-test.cpp
client/create-room-test.cpp
client/device-list-tracker-benchmark-test.cpp
client/room/read-receipt-test.cpp
client/room/undecrypted-events-test.cpp
client/encryption-benchmark-test.cpp
client/logout-test.cpp
client/room/pinned-events-test.cpp
EXTRA_LINK_LIBRARIES kazvclient kazveventemitter kazvjob client-test-lib kazvtestfixtures
EXTRA_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/client
)
libkazv_add_tests(
basejobtest.cpp
kazvjobtest.cpp
file-desc-test.cpp
EXTRA_LINK_LIBRARIES kazvbase kazvjob
)
libkazv_add_tests(
promise-test.cpp
EXTRA_LINK_LIBRARIES kazvbase kazvjob kazvstore
)
libkazv_add_tests(
event-emitter-test.cpp
EXTRA_LINK_LIBRARIES kazvbase kazveventemitter
)
libkazv_add_tests(
crypto-test.cpp
crypto/deterministic-test.cpp
+ crypto/inbound-group-session-test.cpp
EXTRA_LINK_LIBRARIES kazvcrypto
)
libkazv_add_tests(
store-test.cpp
EXTRA_LINK_LIBRARIES kazvstore kazvjob
)
diff --git a/src/tests/crypto/crypto-test-resource.hpp b/src/tests/crypto/crypto-test-resource.hpp
new file mode 100644
index 0000000..ecfaaf9
--- /dev/null
+++ b/src/tests/crypto/crypto-test-resource.hpp
@@ -0,0 +1,28 @@
+/*
+ * This file is part of libkazv.
+ * SPDX-FileCopyrightText: 2024 tusooa <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#pragma once
+#include <libkazv-config.hpp>
+#include <fstream>
+#include <streambuf>
+#include <nlohmann/json.hpp>
+#include "kazvtest-respath.hpp"
+
+inline const std::string cryptoDumpPath = resPath + "/libolm-crypto-dump";
+
+inline nlohmann::json getCryptoDumpResource()
+{
+ std::ifstream s(cryptoDumpPath);
+ std::string str((std::istreambuf_iterator<char>(s)),
+ std::istreambuf_iterator<char>());
+ return nlohmann::json::parse(str);
+}
+
+inline nlohmann::json cryptoDumpResource()
+{
+ static nlohmann::json res = getCryptoDumpResource();
+ return res;
+};
diff --git a/src/tests/crypto/inbound-group-session-test.cpp b/src/tests/crypto/inbound-group-session-test.cpp
new file mode 100644
index 0000000..e6b751c
--- /dev/null
+++ b/src/tests/crypto/inbound-group-session-test.cpp
@@ -0,0 +1,76 @@
+/*
+ * This file is part of libkazv.
+ * SPDX-FileCopyrightText: 2024 tusooa <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libkazv-config.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <inbound-group-session.hpp>
+#include <outbound-group-session.hpp>
+#include <crypto.hpp>
+#include "crypto-test-resource.hpp"
+
+using namespace Kazv;
+
+static const auto resource = cryptoDumpResource();
+
+TEST_CASE("InboundGroupSession conversion from libolm to vodozemac")
+{
+ auto sessionJson = resource["a"]["inboundGroupSessions"][0][1];
+ auto session = sessionJson.template get<InboundGroupSession>();
+ REQUIRE(session.valid());
+ REQUIRE(session.ed25519Key() == sessionJson["ed25519Key"]);
+ auto encrypted = resource["megolmEncrypted"];
+ auto plainText = resource["megolmPlainText"];
+ auto a = Crypto();
+ a.loadJson(resource["a"]);
+ auto decrypted = a.decrypt(encrypted);
+ REQUIRE(decrypted.has_value());
+ auto decryptedJson = json::parse(decrypted.value());
+ REQUIRE(decryptedJson == plainText);
+}
+
+TEST_CASE("InboundGroupSession::from_json error handling")
+{
+ auto sessionJson = resource["a"]["inboundGroupSessions"][0][1];
+ sessionJson["session"] = "AAAAAAAAAA";
+ auto session = sessionJson.template get<InboundGroupSession>();
+ REQUIRE(!session.valid());
+}
+
+TEST_CASE("InboundGroupSession::decrypt error handling")
+{
+ auto sessionJson = resource["a"]["inboundGroupSessions"][0][1];
+ auto session = sessionJson.template get<InboundGroupSession>();
+ WHEN("message not decryptable") {
+ auto res = session.decrypt("AAAAAA", "$1", 1234);
+ REQUIRE(!res);
+ }
+
+ WHEN("message is not valid base64") {
+ auto res = session.decrypt("喵喵喵", "$1", 1234);
+ REQUIRE(!res);
+ }
+
+ WHEN("message is before the index") {
+ auto ogs = OutboundGroupSession(RandomTag{}, genRandomData(OutboundGroupSession::constructRandomSize()), 0);
+ auto encrypted1 = ogs.encrypt("text");
+ auto igs = InboundGroupSession(ogs.sessionKey(), "placeholder");
+ auto res = igs.decrypt(encrypted1, "$1", 1234);
+ REQUIRE(!res.has_value());
+ }
+}
+
+TEST_CASE("InboundGroupSession constructor error handling")
+{
+ WHEN("key not valid") {
+ auto session = InboundGroupSession("AAAAAA", "ed25519Key");
+ REQUIRE(!session.valid());
+ }
+
+ WHEN("key is not valid base64") {
+ auto session = InboundGroupSession("喵喵喵", "ed25519Key");
+ REQUIRE(!session.valid());
+ }
+}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Mon, Nov 2, 9:09 AM (8 h, 20 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1804109
Default Alt Text
(20 KB)
Attached To
Mode
rL libkazv
Attached
Detach File
Event Timeline