Page MenuHomePhorge

No OneTemporary

Size
17 KB
Referenced Files
None
Subscribers
None
diff --git a/src/crypto/inbound-group-session.cpp b/src/crypto/inbound-group-session.cpp
index 4d124e4..02759e0 100644
--- a/src/crypto/inbound-group-session.cpp
+++ b/src/crypto/inbound-group-session.cpp
@@ -1,170 +1,172 @@
/*
* This file is part of libkazv.
* 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
{
InboundGroupSessionPrivate::InboundGroupSessionPrivate()
: session(std::nullopt)
{
}
InboundGroupSessionPrivate::InboundGroupSessionPrivate(std::string sessionKey, std::string ed25519Key)
: InboundGroupSessionPrivate()
{
valid = false;
this->ed25519Key = ed25519Key;
auto keyRust = checkVodozemacError([&]() { return vodozemac::megolm::session_key_from_base64(rust::Str(sessionKey)); });
if (!keyRust) {
return;
}
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());
+ if (that.valid) {
+ valid = unpickle(that.pickle());
+ }
decryptedEvents = that.decryptedEvents;
}
std::string InboundGroupSessionPrivate::pickle() const
{
auto pickleData = this->session.value()->pickle(
VODOZEMAC_PICKLE_KEY);
return static_cast<std::string>(pickleData);
}
bool InboundGroupSessionPrivate::unpickle(std::string pickleData)
{
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)
{
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())); });
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.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) {
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/crypto/outbound-group-session.cpp b/src/crypto/outbound-group-session.cpp
index 1e855a0..8d6b046 100644
--- a/src/crypto/outbound-group-session.cpp
+++ b/src/crypto/outbound-group-session.cpp
@@ -1,186 +1,188 @@
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2021-2024 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include "outbound-group-session-p.hpp"
#include "crypto-util-p.hpp"
#include <debug.hpp>
#include "time-util.hpp"
namespace Kazv
{
OutboundGroupSessionPrivate::OutboundGroupSessionPrivate()
: session(std::nullopt)
{
}
OutboundGroupSessionPrivate::OutboundGroupSessionPrivate(
RandomTag,
[[maybe_unused]] RandomData random,
Timestamp creationTime)
: session(std::nullopt)
, creationTime(creationTime)
{
session = checkVodozemacError([&]() {
return vodozemac::megolm::new_group_session();
});
if (session.has_value()) {
valid = true;
initialSessionKey = sessionKey();
}
}
OutboundGroupSessionPrivate::OutboundGroupSessionPrivate(const OutboundGroupSessionPrivate &that)
: session(std::nullopt)
, creationTime(that.creationTime)
, initialSessionKey(that.initialSessionKey)
{
- valid = unpickle(that.pickle());
+ if (that.valid) {
+ valid = unpickle(that.pickle());
+ }
}
std::string OutboundGroupSessionPrivate::pickle() const
{
auto pickleData = session.value()->pickle(VODOZEMAC_PICKLE_KEY);
return static_cast<std::string>(pickleData);
}
bool OutboundGroupSessionPrivate::unpickle(std::string pickleData)
{
session = checkVodozemacError([&]() {
return vodozemac::megolm::group_session_from_pickle(pickleData, VODOZEMAC_PICKLE_KEY);
});
return session.has_value();
}
bool OutboundGroupSessionPrivate::unpickleFromLibolm(std::string pickleData)
{
session = checkVodozemacError([&]() {
return vodozemac::megolm::group_session_from_libolm_pickle(pickleData, rust::Slice<const unsigned char>(OLM_PICKLE_KEY.data(), OLM_PICKLE_KEY.size()));
});
return session.has_value();
}
std::size_t OutboundGroupSession::constructRandomSize()
{
return 0;
}
OutboundGroupSession::OutboundGroupSession()
: m_d(new OutboundGroupSessionPrivate)
{
}
OutboundGroupSession::OutboundGroupSession(RandomTag, RandomData random, Timestamp creationTime)
: m_d(new OutboundGroupSessionPrivate(RandomTag{}, std::move(random), std::move(creationTime)))
{
}
OutboundGroupSession::~OutboundGroupSession() = default;
OutboundGroupSession::OutboundGroupSession(const OutboundGroupSession &that)
: m_d(new OutboundGroupSessionPrivate(*that.m_d))
{
}
OutboundGroupSession::OutboundGroupSession(OutboundGroupSession &&that)
: m_d(std::move(that.m_d))
{
}
OutboundGroupSession &OutboundGroupSession::operator=(const OutboundGroupSession &that)
{
m_d.reset(new OutboundGroupSessionPrivate(*that.m_d));
return *this;
}
OutboundGroupSession &OutboundGroupSession::operator=(OutboundGroupSession &&that)
{
m_d = std::move(that.m_d);
return *this;
}
bool OutboundGroupSession::valid() const
{
return m_d && m_d->valid;
}
std::string OutboundGroupSession::encrypt(std::string plainText)
{
auto res = checkVodozemacError([&]() {
return m_d->session.value()->encrypt(rust::Str(plainText));
});
if (!res.has_value()) {
return std::string();
}
return static_cast<std::string>(res.value()->to_base64());
}
std::string OutboundGroupSessionPrivate::sessionKey()
{
auto key = session.value()->session_key()->to_base64();
return static_cast<std::string>(key);
}
std::string OutboundGroupSession::sessionKey()
{
return m_d->sessionKey();
}
std::string OutboundGroupSession::initialSessionKey() const
{
return m_d->initialSessionKey;
}
std::string OutboundGroupSession::sessionId()
{
auto id = m_d->session.value()->session_id();
return static_cast<std::string>(id);
}
int OutboundGroupSession::messageIndex()
{
return m_d->session.value()->message_index();
}
Timestamp OutboundGroupSession::creationTimeMs() const
{
return m_d->creationTime;
}
void to_json(nlohmann::json &j, const OutboundGroupSession &s)
{
j = nlohmann::json::object();
j["version"] = 1;
j["valid"] = s.m_d->valid;
j["creationTime"] = s.m_d->creationTime;
j["initialSessionKey"] = s.m_d->initialSessionKey;
if (s.m_d->valid) {
j["session"] = s.m_d->pickle();
}
}
void from_json(const nlohmann::json &j, OutboundGroupSession &s)
{
s.m_d->valid = j.at("valid");
s.m_d->creationTime = j.at("creationTime");
s.m_d->initialSessionKey = j.at("initialSessionKey");
if (s.m_d->valid) {
if (j.contains("version") && j["version"] == 1) {
s.m_d->valid = s.m_d->unpickle(j.at("session"));
} else {
s.m_d->valid = s.m_d->unpickleFromLibolm(j.at("session"));
}
}
}
}
diff --git a/src/tests/crypto/inbound-group-session-test.cpp b/src/tests/crypto/inbound-group-session-test.cpp
index e6b751c..a4b5db7 100644
--- a/src/tests/crypto/inbound-group-session-test.cpp
+++ b/src/tests/crypto/inbound-group-session-test.cpp
@@ -1,76 +1,84 @@
/*
* 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());
}
}
+
+TEST_CASE("invalid InboundGroupSession is copyable")
+{
+ InboundGroupSession session("AAAAAA", "ed25519Key");
+ REQUIRE(!session.valid());
+ auto session2 = session;
+ REQUIRE(!session.valid());
+}
diff --git a/src/tests/crypto/outbound-group-session-test.cpp b/src/tests/crypto/outbound-group-session-test.cpp
index 2408ac7..9421e3d 100644
--- a/src/tests/crypto/outbound-group-session-test.cpp
+++ b/src/tests/crypto/outbound-group-session-test.cpp
@@ -1,59 +1,67 @@
/*
* 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 <outbound-group-session.hpp>
#include <crypto.hpp>
#include "crypto-test-resource.hpp"
using namespace Kazv;
static const auto resource = cryptoDumpResource();
TEST_CASE("OutboundGroupSession conversion from libolm to vodozemac")
{
auto sessionJson = resource["a"]["outboundGroupSessions"]["!foo:example.com"];
auto session = sessionJson.template get<OutboundGroupSession>();
REQUIRE(session.valid());
REQUIRE(session.initialSessionKey() == sessionJson["initialSessionKey"]);
}
TEST_CASE("OutboundGroupSession serialization roundtrip")
{
auto session = OutboundGroupSession(RandomTag{}, genRandomData(OutboundGroupSession::constructRandomSize()), 0);
json j = session;
auto session2 = j.template get<OutboundGroupSession>();
REQUIRE(session2.valid());
REQUIRE(session.initialSessionKey() == session2.initialSessionKey());
REQUIRE(session.sessionId() == session2.sessionId());
}
TEST_CASE("OutboundGroupSession::from_json error handling")
{
auto sessionJson = resource["a"]["outboundGroupSessions"]["!foo:example.com"];
sessionJson["session"] = "AAAAAAAAAA";
auto session = sessionJson.template get<OutboundGroupSession>();
REQUIRE(!session.valid());
}
TEST_CASE("OutboundGroupSession ctor")
{
auto session = OutboundGroupSession();
REQUIRE(!session.valid());
}
TEST_CASE("OutboundGroupSession::encrypt error handling")
{
// invalid utf8 will cause rust::Str to throw
// https://stackoverflow.com/questions/1301402/example-invalid-utf8-string
std::string plainText = "\xc3\x28";
auto session = OutboundGroupSession(RandomTag{}, genRandomData(OutboundGroupSession::constructRandomSize()), 0);
auto originalIndex = session.messageIndex();
auto res = session.encrypt(plainText);
REQUIRE(res.empty());
REQUIRE(originalIndex == session.messageIndex());
}
+
+TEST_CASE("invalid OutboundGroupSession is copyable")
+{
+ OutboundGroupSession session;
+ REQUIRE(!session.valid());
+ auto session2 = session;
+ REQUIRE(!session.valid());
+}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Sep 19, 7:20 AM (1 d, 15 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1768275
Default Alt Text
(17 KB)

Event Timeline