Page MenuHomePhorge

No OneTemporary

Authored By
Unknown
Size
20 KB
Referenced Files
None
Subscribers
None
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

Mime Type
text/x-diff
Expires
Mon, Nov 2, 9:09 AM (4 h, 44 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1804109
Default Alt Text
(20 KB)

Event Timeline