Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
43 changes: 16 additions & 27 deletions include/pro/pro.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <napi.h>
#include <oxenc/base64.h>
#include <oxenc/hex.h>

Expand Down Expand Up @@ -135,7 +136,7 @@ class ProWrapper : public Napi::ObjectWrap<ProWrapper> {
// we expect two arguments that match:
// first: {
// "utf16": string,
// "proFeatures": Array<ProFeature>,
// "proFeaturesBitset": bigint,
// }

assertInfoLength(info, 1);
Expand All @@ -147,27 +148,13 @@ class ProWrapper : public Napi::ObjectWrap<ProWrapper> {
if (first.IsEmpty())
throw std::invalid_argument("proFeaturesForMessage first received empty");

assertIsArray(first.Get("proFeatures"), "proFeaturesForMessage.proFeatures");
auto proFeaturesJS = first.Get("proFeatures").As<Napi::Array>();
std::vector<std::string> proFeatures;
proFeatures.reserve(proFeaturesJS.Length());
for (uint32_t i = 0; i < proFeaturesJS.Length(); i++) {
auto itemValue = proFeaturesJS.Get(i);
assertIsString(itemValue, "proFeaturesForMessage.proFeatures.itemValue");
std::string item =
toCppString(itemValue, "proFeaturesForMessage.proFeatures.itemValue");
proFeatures.push_back(item);
}

SESSION_PROTOCOL_PRO_EXTRA_FEATURES flags = 0;
for (std::string& feature : proFeatures) {
// Note: 10K_CHARACTER_LIMIT cannot be requested by the caller
if (feature == "PRO_BADGE") {
flags |= SESSION_PROTOCOL_PRO_EXTRA_FEATURES_PRO_BADGE;
} else if (feature == "ANIMATED_AVATAR") {
flags |= SESSION_PROTOCOL_PRO_EXTRA_FEATURES_ANIMATED_AVATAR;
}
}
assertIsBigint(
first.Get("proFeaturesBitset"), "proFeaturesForMessage.proFeaturesBitset");

auto lossless = true;
SESSION_PROTOCOL_PRO_FEATURES flags =
first.Get("proFeaturesBitset").As<Napi::BigInt>().Uint64Value(&lossless);

assertIsString(first.Get("utf16"), "proFeaturesForMessage.utf16");
std::u16string utf16 = first.Get("utf16").As<Napi::String>().Utf16Value();
auto pro_features_msg =
Expand All @@ -179,7 +166,7 @@ class ProWrapper : public Napi::ObjectWrap<ProWrapper> {
obj["error"] =
pro_features_msg.error.size() ? toJs(env, pro_features_msg.error) : env.Null();
obj["codepointCount"] = toJs(env, pro_features_msg.codepoint_count);
obj["proFeatures"] = proFeaturesToJs(env, pro_features_msg.features);
obj["proFeaturesBitset"] = proFeaturesToJsBitset(env, pro_features_msg.features);

return obj;
});
Expand Down Expand Up @@ -219,12 +206,13 @@ class ProWrapper : public Napi::ObjectWrap<ProWrapper> {
std::string rotating_privkey =
toCppString(rotating_privkey_js, "proProofRequestBody.rotatingPrivKeyHex");

assert_length(master_privkey, 64, "masterPrivKeyHex");
assert_length(rotating_privkey, 64, "rotatingPrivkey");

auto master_privkey_decoded = from_hex(master_privkey);
auto rotating_privkey_decoded = from_hex(rotating_privkey);

assert_length(master_privkey_decoded, 64, "masterPrivKeyHex");
assert_length(rotating_privkey_decoded, 64, "rotatingPrivkey");

std::string json = pro_backend::GetProProofRequest::build_to_json(
static_cast<uint8_t>(requestVersion.Int32Value()),
to_span(master_privkey_decoded),
Expand Down Expand Up @@ -355,11 +343,12 @@ class ProWrapper : public Napi::ObjectWrap<ProWrapper> {
auto master_privkey =
toCppString(master_privkey_js, "proStatusRequestBody.masterPrivKeyHex");

assert_length(master_privkey, 64, "proStatusRequestBody.masterPrivKeyHex");
auto master_privkey_decoded = from_hex(master_privkey);
assert_length(master_privkey_decoded, 64, "proStatusRequestBody.masterPrivKeyHex");

auto json = pro_backend::GetProStatusRequest::build_to_json(
static_cast<uint8_t>(requestVersion.Int32Value()),
to_span(from_hex(master_privkey)),
to_span(master_privkey_decoded),
unix_ts_ms,
withPaymentHistory);

Expand Down
2 changes: 1 addition & 1 deletion include/pro/types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ struct toJs_impl<session::DecodedPro> {
: decoded_pro.status == ProStatus::Valid ? "Valid"
: "Expired");
obj["proProof"] = toJs(env, decoded_pro.proof);
obj["proFeatures"] = proFeaturesToJs(env, decoded_pro.features);
obj["proFeaturesBitset"] = proFeaturesToJsBitset(env, decoded_pro.features);

return obj;
}
Expand Down
2 changes: 2 additions & 0 deletions include/user_config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,7 @@ class UserConfigWrapper : public ConfigBaseImpl, public Napi::ObjectWrap<UserCon
Napi::Value getProConfig(const Napi::CallbackInfo& info);
void setProConfig(const Napi::CallbackInfo& info);
Napi::Value generateProMasterKey(const Napi::CallbackInfo& info);
Napi::Value generateRotatingPrivKeyHex(const Napi::CallbackInfo& info);

};
}; // namespace session::nodeapi
22 changes: 12 additions & 10 deletions include/utilities.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ void assertInfoMinLength(const Napi::CallbackInfo& info, const int minLength);

void assertIsStringOrNull(const Napi::Value& value, const std::string& identifier = "");
void assertIsNumber(const Napi::Value& value, const std::string& identifier);
void assertIsBigint(const Napi::Value& val, const std::string& identifier);
void assertIsArray(const Napi::Value& value, const std::string& identifier);
void assertIsObject(const Napi::Value& value);
void assertIsUInt8ArrayOrNull(const Napi::Value& value);
Expand Down Expand Up @@ -394,7 +395,8 @@ Napi::Object decrypt_result_to_JS(

confirm_pushed_entry_t confirm_pushed_entry_from_JS(const Napi::Env& env, const Napi::Object& obj);

Napi::Object proFeaturesToJs(const Napi::Env& env, const SESSION_PROTOCOL_PRO_FEATURES bitset);
Napi::BigInt proFeaturesToJsBitset(
const Napi::Env& env, const SESSION_PROTOCOL_PRO_FEATURES bitset);

std::span<const uint8_t> from_hex_to_span(std::string_view x);

Expand All @@ -405,10 +407,11 @@ template <std::size_t N>
std::array<uint8_t, N> from_hex_to_array(std::string x) {
std::string as_hex = oxenc::from_hex(x);
if (as_hex.size() != N) {
throw std::invalid_argument(fmt::format(
"from_hex_to_array: Decoded hex size mismatch: expected {}, got {}",
N,
as_hex.size()));
throw std::invalid_argument(
fmt::format(
"from_hex_to_array: Decoded hex size mismatch: expected {}, got {}",
N,
as_hex.size()));
}

std::array<uint8_t, N> result;
Expand All @@ -424,16 +427,15 @@ std::vector<unsigned char> from_base64_to_vector(std::string_view x);
// Concept to match containers with a size() method
template <typename T>
concept HasSize = requires(T t) {
{
t.size()
} -> std::convertible_to<size_t>;
{t.size()}->std::convertible_to<size_t>;
};

template <HasSize T>
void assert_length(const T& x, size_t n, std::string_view base_identifier) {
if (x.size() != n) {
throw std::invalid_argument(fmt::format(
"assert_length: expected {}, got {} for {}", n, x.size(), base_identifier));
throw std::invalid_argument(
fmt::format(
"assert_length: expected {}, got {} for {}", n, x.size(), base_identifier));
}
}

Expand Down
19 changes: 10 additions & 9 deletions src/encrypt_decrypt/encrypt_decrypt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -636,8 +636,9 @@ Napi::Value MultiEncryptWrapper::decryptForCommunity(const Napi::CallbackInfo& i

auto contentOrEnvelope =
extractContentOrEnvelope(obj, "decryptForCommunity.obj.contentOrEnvelope");
decrypted.push_back(session::decode_for_community(
contentOrEnvelope, nowMs, proBackendPubkeyHex));
decrypted.push_back(
session::decode_for_community(
contentOrEnvelope, nowMs, proBackendPubkeyHex));
decryptedServerIds.push_back(serverId);

} catch (const std::exception& e) {
Expand All @@ -654,12 +655,10 @@ Napi::Value MultiEncryptWrapper::decryptForCommunity(const Napi::CallbackInfo& i

for (auto& d : decrypted) {
auto to_insert = Napi::Object::New(info.Env());
std::span<unsigned char> content_plaintext_unpadded =
std::span(d.content_plaintext).subspan(0, d.content_plaintext_unpadded_size);

to_insert.Set(
"envelope", d.envelope ? toJs(info.Env(), *d.envelope) : info.Env().Null());
to_insert.Set("contentPlaintextUnpadded", toJs(info.Env(), content_plaintext_unpadded));
to_insert.Set("contentPlaintextUnpadded", toJs(info.Env(), d.content_plaintext));
to_insert.Set("serverId", toJs(info.Env(), decryptedServerIds[i]));

to_insert.Set(
Expand Down Expand Up @@ -732,8 +731,9 @@ Napi::Value MultiEncryptWrapper::decryptFor1o1(const Napi::CallbackInfo& info) {

auto envelopePayload =
extractEnvelopePayload(obj, "decryptFor1o1.obj.envelopePayload");
decrypted.push_back(session::decode_envelope(
keys, envelopePayload, nowMs, proBackendPubkeyHex));
decrypted.push_back(
session::decode_envelope(
keys, envelopePayload, nowMs, proBackendPubkeyHex));
decryptedMessageHashes.push_back(messageHash);
} catch (const std::exception& e) {
log::warning(
Expand Down Expand Up @@ -831,8 +831,9 @@ Napi::Value MultiEncryptWrapper::decryptForGroup(const Napi::CallbackInfo& info)

auto envelopePayload =
extractEnvelopePayload(obj, "decryptForGroup.obj.envelopePayload");
decrypted.push_back(session::decode_envelope(
keys, envelopePayload, nowMs, proBackendPubkeyHex));
decrypted.push_back(
session::decode_envelope(
keys, envelopePayload, nowMs, proBackendPubkeyHex));
decryptedMessageHashes.push_back(messageHash);
} catch (const std::exception& e) {
log::warning(
Expand Down
31 changes: 25 additions & 6 deletions src/user_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ void UserConfigWrapper::Init(Napi::Env env, Napi::Object exports) {
InstanceMethod("setProConfig", &UserConfigWrapper::setProConfig),
InstanceMethod(
"generateProMasterKey", &UserConfigWrapper::generateProMasterKey),
InstanceMethod(
"generateRotatingPrivKeyHex",
&UserConfigWrapper::generateRotatingPrivKeyHex),
});
}

Expand Down Expand Up @@ -247,10 +250,11 @@ void UserConfigWrapper::setNoteToSelfExpiry(const Napi::CallbackInfo& info) {

Napi::Value UserConfigWrapper::getProConfig(const Napi::CallbackInfo& info) {
return wrapResult(info, [&] {
auto pro_config = config.get_pro_config();
if (pro_config) {
return toJs(info.Env(), *pro_config);
}
// TODO fixme once extra_data is implemented
// auto pro_config = config.get_pro_config();
// if (pro_config) {
// return toJs(info.Env(), *pro_config);
// }

return info.Env().Null();
});
Expand All @@ -264,8 +268,9 @@ void UserConfigWrapper::setProConfig(const Napi::CallbackInfo& info) {

session::config::ProConfig pro_config =
pro_config_from_object(pro_config_js.As<Napi::Object>());
// TODO fixme once extra_data is implemented

config.set_pro_config(pro_config);
// config.set_pro_config(pro_config);
});
}

Expand All @@ -283,7 +288,21 @@ Napi::Value UserConfigWrapper::generateProMasterKey(const Napi::CallbackInfo& in

auto pro_master_key_hex = session::ed25519::ed25519_pro_privkey_for_ed25519_seed(converted);
auto obj = Napi::Object::New(info.Env());
obj["proMasterKey"] = toJs(info.Env(), pro_master_key_hex);
obj["proMasterKeyHex"] = toJs(info.Env(), to_hex(pro_master_key_hex));

return obj;
});
}

Napi::Value UserConfigWrapper::generateRotatingPrivKeyHex(const Napi::CallbackInfo& info) {
return wrapResult(info, [&] {
assertInfoLength(info, 0);
auto result = session::ed25519::ed25519_key_pair();
auto [ed_pk, ed_sk] = result;

std::string rotating_privkey_hex = to_hex(ed_sk);
auto obj = Napi::Object::New(info.Env());
obj["rotatingPrivKeyHex"] = toJs(info.Env(), rotating_privkey_hex);

return obj;
});
Expand Down
38 changes: 13 additions & 25 deletions src/utilities.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "utilities.hpp"

#include <napi.h>
#include <oxenc/base64.h>
#include <oxenc/hex.h>

Expand All @@ -22,18 +23,24 @@ void assertInfoMinLength(const Napi::CallbackInfo& info, const int minLength) {
void assertIsStringOrNull(const Napi::Value& val, const std::string& identifier) {
checkOrThrow(
val.IsString() || val.IsNull(),
std::string("Wrong arguments: expected string or null" + identifier).c_str());
std::string("Wrong arguments: expected string or null: " + identifier).c_str());
}

void assertIsNumber(const Napi::Value& val, const std::string& identifier) {
checkOrThrow(
val.IsNumber() && !val.IsEmpty() && !val.IsNull() && !val.IsUndefined(),
std::string("Wrong arguments: expected number" + identifier).c_str());
std::string("Wrong arguments: expected number: " + identifier).c_str());
}

void assertIsBigint(const Napi::Value& val, const std::string& identifier) {
checkOrThrow(
val.IsBigInt() && !val.IsEmpty() && !val.IsNull() && !val.IsUndefined(),
std::string("Wrong arguments: expected bigint: " + identifier).c_str());
}

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

void assertIsObject(const Napi::Value& val) {
Expand Down Expand Up @@ -345,28 +352,9 @@ confirm_pushed_entry_t confirm_pushed_entry_from_JS(const Napi::Env& env, const
return confirmed_pushed_entry;
}

Napi::Object proFeaturesToJs(const Napi::Env& env, const SESSION_PROTOCOL_PRO_FEATURES bitset) {
Napi::Array arr = Napi::Array::New(env);
uint32_t index = 0;

if (bitset == SESSION_PROTOCOL_PRO_FEATURES_NIL) {
return arr;
}

if (bitset & (SESSION_PROTOCOL_PRO_FEATURES_10K_CHARACTER_LIMIT)) {
arr[index] = Napi::String::New(env, "10K_CHARACTER_LIMIT");
index++;
}
if (bitset & SESSION_PROTOCOL_PRO_FEATURES_PRO_BADGE) {
arr[index++] = Napi::String::New(env, "PRO_BADGE");
index++;
}
if (bitset & SESSION_PROTOCOL_PRO_FEATURES_ANIMATED_AVATAR) {
arr[index++] = Napi::String::New(env, "ANIMATED_AVATAR");
index++;
}

return arr;
Napi::BigInt proFeaturesToJsBitset(
const Napi::Env& env, const SESSION_PROTOCOL_PRO_FEATURES bitset) {
return Napi::BigInt::New(env, bitset);
}

std::span<const uint8_t> from_hex_to_span(std::string_view x) {
Expand Down
2 changes: 1 addition & 1 deletion types/multi_encrypt/multi_encrypt.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ declare module 'libsession_util_nodejs' {
};
type WithNowMs = { nowMs: number };

type DecodedPro = WithProFeatures & {
type DecodedPro = WithProFeaturesBitset & {
proStatus: ProStatus;
proProof: ProProof;
};
Expand Down
Loading
Loading