Skip to content

feat: add multi part msg support #25

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libsession-util
Submodule libsession-util updated 69 files
+7 −6 .drone.jsonnet
+3 −0 .gitmodules
+1 −1 CMakeLists.txt
+3 −0 docs/api/.gitignore
+27 −17 docs/api/Makefile
+56 −0 docs/api/README.md
+14 −9 docs/api/api-to-markdown.py
+18 −16 docs/api/docs/config_merge_logic.md
+1 −1 docs/api/docs/index.md
+10 −0 docs/api/docs/javascripts/katex.js
+17 −0 docs/api/docs/stylesheets/extra.css
+1 −80 docs/api/make-docs.sh
+73 −0 docs/api/mkdocs.yml
+38 −0 docs/api/requirements.txt
+0 −10 docs/api/static/sidebar.md
+1 −2 external/CMakeLists.txt
+1 −1 external/oxen-libquic
+1 −0 external/oxen-logging
+17 −4 include/session/config.hpp
+54 −19 include/session/config/base.h
+167 −39 include/session/config/base.hpp
+3 −2 include/session/config/convo_info_volatile.hpp
+7 −1 include/session/config/encrypt.hpp
+2 −2 include/session/config/groups/info.h
+1 −1 include/session/config/groups/info.hpp
+5 −5 include/session/config/groups/keys.h
+5 −3 include/session/config/groups/keys.hpp
+20 −0 include/session/config/groups/members.hpp
+33 −1 include/session/hash.hpp
+4 −4 include/session/multi_encrypt.h
+3 −2 include/session/random.h
+48 −0 include/session/session_encrypt.h
+26 −0 include/session/session_encrypt.hpp
+4 −4 include/session/session_network.h
+7 −7 include/session/session_network.hpp
+2 −0 include/session/types.hpp
+9 −2 include/session/util.hpp
+2 −2 src/CMakeLists.txt
+533 −92 src/config/base.cpp
+1 −1 src/config/convo_info_volatile.cpp
+10 −14 src/config/encrypt.cpp
+4 −4 src/config/groups/keys.cpp
+13 −10 src/hash.cpp
+4 −4 src/multi_encrypt.cpp
+1 −1 src/onionreq/builder.cpp
+110 −1 src/session_encrypt.cpp
+71 −63 src/session_network.cpp
+3 −1 tests/CMakeLists.txt
+93 −0 tests/case_logger.cpp
+3 −1 tests/main.cpp
+3 −9 tests/swarm-auth-test.cpp
+2 −7 tests/test_blinding.cpp
+17 −15 tests/test_bugs.cpp
+0 −3 tests/test_compression.cpp
+403 −23 tests/test_config_contacts.cpp
+26 −17 tests/test_config_convo_info_volatile.cpp
+40 −34 tests/test_config_user_groups.cpp
+31 −24 tests/test_config_userprofile.cpp
+16 −14 tests/test_configdata.cpp
+0 −2 tests/test_encrypt.cpp
+34 −34 tests/test_group_info.cpp
+120 −95 tests/test_group_keys.cpp
+28 −31 tests/test_group_members.cpp
+3 −9 tests/test_multi_encrypt.cpp
+1 −1 tests/test_onionreq.cpp
+16 −3 tests/test_session_encrypt.cpp
+283 −113 tests/test_session_network.cpp
+11 −66 tests/utils.hpp
+2 −2 utils/ci/drone-docs-upload.sh
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"main": "index.js",
"name": "libsession_util_nodejs",
"description": "Wrappers for the Session Util Library",
"version": "0.4.34",
"version": "0.5.0",
"license": "GPL-3.0",
"author": {
"name": "Oxen Project",
Expand Down
11 changes: 11 additions & 0 deletions src/addon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@

Napi::Object InitAll(Napi::Env env, Napi::Object exports) {
using namespace session::nodeapi;

session::add_logger([env](std::string_view msg) {
std::string toLog = std::string("libsession-util: ") + std::string(msg) + "\n";

Napi::Function consoleLog =
env.Global().Get("console").As<Napi::Object>().Get("log").As<Napi::Function>();
consoleLog.Call({Napi::String::New(env, toLog)});
});

// session::logger_set_level_default(session::LogLevel::debug);

ConstantsWrapper::Init(env, exports);

// Group wrappers init
Expand Down
27 changes: 16 additions & 11 deletions src/base_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,12 @@ Napi::Value ConfigBaseImpl::needsPush(const Napi::CallbackInfo& info) {
return wrapResult(info, [&] { return get_config<ConfigBase>().needs_push(); });
}

Napi::Value ConfigBaseImpl::currentHashes(const Napi::CallbackInfo& info) {
return wrapResult(info, [&] { return (get_config<ConfigBase>().current_hashes()); });
Napi::Value ConfigBaseImpl::activeHashes(const Napi::CallbackInfo& info) {
return wrapResult(info, [&] {
std::unordered_set<std::string> hashes = get_config<ConfigBase>().active_hashes();
std::vector<std::string> hashesVec(hashes.begin(), hashes.end());
return hashesVec;
});
}

Napi::Value ConfigBaseImpl::push(const Napi::CallbackInfo& info) {
Expand Down Expand Up @@ -44,21 +48,21 @@ Napi::Value ConfigBaseImpl::makeDump(const Napi::CallbackInfo& info) {
}

void ConfigBaseImpl::confirmPushed(const Napi::CallbackInfo& info) {
return wrapResult(info, [&]() {
assertInfoLength(info, 2);
assertIsNumber(info[0], "confirmPushed");
assertIsString(info[1]);
return wrapExceptions(info, [&]() {
assertInfoLength(info, 1);
assertIsObject(info[0]);
auto obj = info[0].As<Napi::Object>();
auto confirmed_pushed_entry = confirm_pushed_entry_from_JS(info.Env(), obj);

get_config<ConfigBase>().confirm_pushed(
toCppInteger(info[0], "confirmPushed", false),
toCppString(info[1], "confirmPushed"));
std::get<0>(confirmed_pushed_entry), std::get<1>(confirmed_pushed_entry));
});
}

Napi::Value ConfigBaseImpl::merge(const Napi::CallbackInfo& info) {
return wrapResult(info, [&]() {
assertInfoLength(info, 1);
assertIsArray(info[0]);
assertIsArray(info[0], "ConfigBaseImpl::merge");
Napi::Array asArray = info[0].As<Napi::Array>();

std::vector<std::pair<std::string, std::vector<unsigned char>>> conf_strs;
Expand All @@ -75,8 +79,9 @@ Napi::Value ConfigBaseImpl::merge(const Napi::CallbackInfo& info) {
toCppString(itemObject.Get("hash"), "base.merge"),
toCppBuffer(itemObject.Get("data"), "base.merge"));
}

return get_config<ConfigBase>().merge(conf_strs);
std::unordered_set<std::string> merged = get_config<ConfigBase>().merge(conf_strs);
std::vector<std::string> mergedVec(merged.begin(), merged.end());
return mergedVec;
});
}

Expand Down
24 changes: 4 additions & 20 deletions src/base_config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

#include <cassert>
#include <memory>
#include <oxen/log.hpp>
#include <span>
#include <stdexcept>
#include <unordered_set>
Expand Down Expand Up @@ -33,7 +34,7 @@ class ConfigBaseImpl {
// These are exposed as read-only accessors rather than methods:
Napi::Value needsDump(const Napi::CallbackInfo& info);
Napi::Value needsPush(const Napi::CallbackInfo& info);
Napi::Value currentHashes(const Napi::CallbackInfo& info);
Napi::Value activeHashes(const Napi::CallbackInfo& info);

Napi::Value push(const Napi::CallbackInfo& info);
Napi::Value dump(const Napi::CallbackInfo& info);
Expand All @@ -53,7 +54,7 @@ class ConfigBaseImpl {

properties.push_back(T::InstanceMethod("needsDump", &T::needsDump));
properties.push_back(T::InstanceMethod("needsPush", &T::needsPush));
properties.push_back(T::InstanceMethod("currentHashes", &T::currentHashes));
properties.push_back(T::InstanceMethod("activeHashes", &T::activeHashes));
properties.push_back(T::InstanceMethod("push", &T::push));
properties.push_back(T::InstanceMethod("dump", &T::dump));
properties.push_back(T::InstanceMethod("makeDump", &T::makeDump));
Expand Down Expand Up @@ -101,24 +102,7 @@ class ConfigBaseImpl {
if (!second.IsEmpty() && !second.IsNull() && !second.IsUndefined())
dump = toCppBuffer(second, class_name + ".new");

// return std::make_shared<Config>(secretKey, dump);
std::shared_ptr<Config> config = std::make_shared<Config>(secretKey, dump);

Napi::Env env = info.Env();

session::add_logger([env, class_name](auto msg) {
std::string toLog =
"libsession-util:" + std::string(class_name) + ": " + std::string(msg) + "\n";

Napi::Function consoleLog = env.Global()
.Get("console")
.As<Napi::Object>()
.Get("log")
.As<Napi::Function>();
consoleLog.Call({Napi::String::New(env, toLog)});
});

return config;
return std::make_shared<Config>(secretKey, dump);
});
}

Expand Down
65 changes: 25 additions & 40 deletions src/groups/meta_group_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ void MetaGroupWrapper::Init(Napi::Env env, Napi::Object exports) {
InstanceMethod("keysNeedsRekey", &MetaGroupWrapper::keysNeedsRekey),
InstanceMethod("keyRekey", &MetaGroupWrapper::keyRekey),
InstanceMethod("keyGetAll", &MetaGroupWrapper::keyGetAll),
InstanceMethod("currentHashes", &MetaGroupWrapper::currentHashes),
InstanceMethod("activeHashes", &MetaGroupWrapper::activeHashes),
InstanceMethod("loadKeyMessage", &MetaGroupWrapper::loadKeyMessage),
InstanceMethod("keyGetCurrentGen", &MetaGroupWrapper::keyGetCurrentGen),
InstanceMethod("encryptMessages", &MetaGroupWrapper::encryptMessages),
Expand Down Expand Up @@ -244,35 +244,21 @@ void MetaGroupWrapper::metaConfirmPushed(const Napi::CallbackInfo& info) {
auto groupMember = obj.Get("groupMember");

if (!groupInfo.IsNull() && !groupInfo.IsUndefined()) {
assertIsArray(groupInfo);
auto groupInfoArr = groupInfo.As<Napi::Array>();
if (groupInfoArr.Length() != 2) {
throw std::invalid_argument("groupInfoArr length was not 2");
}
assertIsObject(groupInfo);
auto groupInfoObj = groupInfo.As<Napi::Object>();
auto groupInfoConfirmed = confirm_pushed_entry_from_JS(info.Env(), groupInfoObj);

auto seqno = maybeNonemptyInt(
groupInfoArr.Get("0"), "MetaGroupWrapper::metaConfirmPushed groupInfo seqno");
auto hash = maybeNonemptyString(
groupInfoArr.Get("1"), "MetaGroupWrapper::metaConfirmPushed groupInfo hash");
if (seqno && hash)
this->meta_group->info->confirm_pushed(*seqno, *hash);
this->meta_group->info->confirm_pushed(
std::get<0>(groupInfoConfirmed), std::get<1>(groupInfoConfirmed));
}

if (!groupMember.IsNull() && !groupMember.IsUndefined()) {
assertIsArray(groupMember);
auto groupMemberArr = groupMember.As<Napi::Array>();
if (groupMemberArr.Length() != 2) {
throw std::invalid_argument("groupMemberArr length was not 2");
}
assertIsObject(groupMember);
auto groupMemberObj = groupMember.As<Napi::Object>();
auto groupMemberConfirmed = confirm_pushed_entry_from_JS(info.Env(), groupMemberObj);

auto seqno = maybeNonemptyInt(
groupMemberArr.Get("0"),
"MetaGroupWrapper::metaConfirmPushed groupMemberArr seqno");
auto hash = maybeNonemptyString(
groupMemberArr.Get("1"),
"MetaGroupWrapper::metaConfirmPushed groupMemberArr hash");
if (seqno && hash)
this->meta_group->members->confirm_pushed(*seqno, *hash);
this->meta_group->members->confirm_pushed(
std::get<0>(groupMemberConfirmed), std::get<1>(groupMemberConfirmed));
}
});
};
Expand All @@ -283,17 +269,17 @@ Napi::Value MetaGroupWrapper::metaMerge(const Napi::CallbackInfo& info) {
auto arg = info[0];
assertIsObject(arg);
auto obj = arg.As<Napi::Object>();

auto groupInfo = obj.Get("groupInfo");
auto groupMember = obj.Get("groupMember");
auto groupKeys = obj.Get("groupKeys");

auto count_merged = 0;


// Note: we need to process keys first as they might allow us the incoming info+members
// details
if (!groupKeys.IsNull() && !groupKeys.IsUndefined()) {
assertIsArray(groupKeys);
assertIsArray(groupKeys, "metaMerge groupKeys");
auto asArr = groupKeys.As<Napi::Array>();

for (uint32_t i = 0; i < asArr.Length(); i++) {
Expand All @@ -318,13 +304,13 @@ Napi::Value MetaGroupWrapper::metaMerge(const Napi::CallbackInfo& info) {
timestamp_ms,
*(this->meta_group->info),
*(this->meta_group->members));
count_merged++; // load_key_message doesn't necessarely merge something as not
count_merged++; // load_key_message doesn't necessarily merge something as not
// all keys are for us.
}
}

if (!groupInfo.IsNull() && !groupInfo.IsUndefined()) {
assertIsArray(groupInfo);
assertIsArray(groupInfo, "metaMerge groupInfo");
auto asArr = groupInfo.As<Napi::Array>();

std::vector<std::pair<std::string, std::vector<unsigned char>>> conf_strs;
Expand All @@ -349,9 +335,8 @@ Napi::Value MetaGroupWrapper::metaMerge(const Napi::CallbackInfo& info) {
count_merged += info_merged.size();
}
}

if (!groupMember.IsNull() && !groupMember.IsUndefined()) {
assertIsArray(groupMember);
assertIsArray(groupMember, "metaMerge groupMember");
auto asArr = groupMember.As<Napi::Array>();

std::vector<std::pair<std::string, std::vector<unsigned char>>> conf_strs;
Expand All @@ -376,7 +361,6 @@ Napi::Value MetaGroupWrapper::metaMerge(const Napi::CallbackInfo& info) {
count_merged += member_merged.size();
}
}

if (this->meta_group->keys->needs_rekey()) {
this->meta_group->keys->rekey(*(this->meta_group->info), *(this->meta_group->members));
}
Expand Down Expand Up @@ -693,7 +677,7 @@ void MetaGroupWrapper::membersMarkPendingRemoval(const Napi::CallbackInfo& info)
auto toUpdateJSValue = info[0];
auto withMessageJSValue = info[1];

assertIsArray(toUpdateJSValue);
assertIsArray(toUpdateJSValue, "membersMarkPendingRemoval");
assertIsBoolean(withMessageJSValue);
bool withMessages = toCppBoolean(withMessageJSValue, "membersMarkPendingRemoval");

Expand All @@ -714,7 +698,7 @@ Napi::Value MetaGroupWrapper::memberEraseAndRekey(const Napi::CallbackInfo& info
assertInfoLength(info, 1);
auto toRemoveJSValue = info[0];

assertIsArray(toRemoveJSValue);
assertIsArray(toRemoveJSValue, "memberEraseAndRekey");

auto toRemoveJS = toRemoveJSValue.As<Napi::Array>();
auto rekeyed = false;
Expand Down Expand Up @@ -771,23 +755,24 @@ Napi::Value MetaGroupWrapper::keyGetCurrentGen(const Napi::CallbackInfo& info) {
});
}

Napi::Value MetaGroupWrapper::currentHashes(const Napi::CallbackInfo& info) {
Napi::Value MetaGroupWrapper::activeHashes(const Napi::CallbackInfo& info) {
return wrapResult(info, [&] {
auto keysHashes = meta_group->keys->current_hashes();
auto infoHashes = meta_group->info->current_hashes();
auto memberHashes = meta_group->members->current_hashes();
auto keysHashes = meta_group->keys->active_hashes();
auto infoHashes = meta_group->info->active_hashes();
auto memberHashes = meta_group->members->active_hashes();
std::vector<std::string> merged;
std::copy(std::begin(keysHashes), std::end(keysHashes), std::back_inserter(merged));
std::copy(std::begin(infoHashes), std::end(infoHashes), std::back_inserter(merged));
std::copy(std::begin(memberHashes), std::end(memberHashes), std::back_inserter(merged));

return merged;
});
}

Napi::Value MetaGroupWrapper::encryptMessages(const Napi::CallbackInfo& info) {
return wrapResult(info, [&] {
assertInfoLength(info, 1);
assertIsArray(info[0]);
assertIsArray(info[0], "encryptMessages");

auto plaintextsJS = info[0].As<Napi::Array>();
uint32_t arrayLength = plaintextsJS.Length();
Expand Down Expand Up @@ -880,7 +865,7 @@ Napi::Value MetaGroupWrapper::generateSupplementKeys(const Napi::CallbackInfo& i
return wrapResult(info, [&] {
assertInfoLength(info, 1);
auto membersJSValue = info[0];
assertIsArray(membersJSValue);
assertIsArray(membersJSValue, "generateSupplementKeys");

auto membersJS = membersJSValue.As<Napi::Array>();
uint32_t arrayLength = membersJS.Length();
Expand Down
2 changes: 1 addition & 1 deletion src/groups/meta_group_wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class MetaGroupWrapper : public Napi::ObjectWrap<MetaGroupWrapper> {
Napi::Value keyGetAll(const Napi::CallbackInfo& info);
Napi::Value loadKeyMessage(const Napi::CallbackInfo& info);
Napi::Value keyGetCurrentGen(const Napi::CallbackInfo& info);
Napi::Value currentHashes(const Napi::CallbackInfo& info);
Napi::Value activeHashes(const Napi::CallbackInfo& info);
Napi::Value encryptMessages(const Napi::CallbackInfo& info);
Napi::Value decryptMessage(const Napi::CallbackInfo& info);
Napi::Value makeSwarmSubAccount(const Napi::CallbackInfo& info);
Expand Down
4 changes: 2 additions & 2 deletions src/multi_encrypt/multi_encrypt.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class MultiEncryptWrapper : public Napi::ObjectWrap<MultiEncryptWrapper> {

// handle the messages conversion
auto messagesJSValue = obj.Get("messages");
assertIsArray(messagesJSValue);
assertIsArray(messagesJSValue, "multiEncrypt");
auto messagesJS = messagesJSValue.As<Napi::Array>();
std::vector<std::vector<unsigned char>> messages;
messages.reserve(messagesJS.Length());
Expand All @@ -71,7 +71,7 @@ class MultiEncryptWrapper : public Napi::ObjectWrap<MultiEncryptWrapper> {

// handle the recipients conversion
auto recipientsJSValue = obj.Get("recipients");
assertIsArray(recipientsJSValue);
assertIsArray(recipientsJSValue, "multiEncrypt");
auto recipientsJS = recipientsJSValue.As<Napi::Array>();
std::vector<std::vector<unsigned char>> recipients;
recipients.reserve(recipientsJS.Length());
Expand Down
2 changes: 1 addition & 1 deletion src/user_groups_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ void UserGroupsWrapper::setLegacyGroup(const Napi::CallbackInfo& info) {
true)};

auto membersJSValue = obj.Get("members");
assertIsArray(membersJSValue);
assertIsArray(membersJSValue, "setLegacyGroup.membersJSValue");
auto membersJS = membersJSValue.As<Napi::Array>();
uint32_t arrayLength = membersJS.Length();
std::vector<std::pair<std::string, bool>> membersToAddOrUpdate;
Expand Down
24 changes: 22 additions & 2 deletions src/utilities.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ void assertIsNumber(const Napi::Value& val, const std::string& identifier) {
std::string("Wrong arguments: expected number" + identifier).c_str());
}

void assertIsArray(const Napi::Value& val) {
checkOrThrow(val.IsArray(), "Wrong arguments: expected array");
void assertIsArray(const Napi::Value& val, const std::string& identifier) {
checkOrThrow(
val.IsArray(), std::string("Wrong arguments: expected array:" + identifier).c_str());
}

void assertIsObject(const Napi::Value& val) {
Expand Down Expand Up @@ -230,4 +231,23 @@ Napi::Object decrypt_result_to_JS(
return obj;
}

confirm_pushed_entry_t confirm_pushed_entry_from_JS(const Napi::Env& env, const Napi::Object& obj) {
auto seqnoJsValue = obj.Get("seqno");
assertIsNumber(seqnoJsValue, "confirm_pushed_entry_from_JS.seqno");
int64_t seqno = toCppInteger(seqnoJsValue, "confirm_pushed_entry_from_JS.seqno", false);
auto hashesJsValue = obj.Get("hashes");
assertIsArray(hashesJsValue, "confirm_pushed_entry_from_JS.hashes");

auto hashesJs = hashesJsValue.As<Napi::Array>();
std::unordered_set<std::string> hashes;
for (uint32_t i = 0; i < hashesJs.Length(); i++) {
auto hashValue = hashesJs.Get(i);
assertIsString(hashValue);
std::string hash = toCppString(hashValue, "confirm_pushed_entry_from_JS.hashes.hash");
hashes.insert(hash);
}
confirm_pushed_entry_t confirmed_pushed_entry{seqno, hashes};
return confirmed_pushed_entry;
}

} // namespace session::nodeapi
Loading