Skip to content

Commit 43ee42f

Browse files
committed
fix: decrypt for 1o1 work as expected
1 parent d25efc8 commit 43ee42f

File tree

4 files changed

+293
-44
lines changed

4 files changed

+293
-44
lines changed

include/multi_encrypt/multi_encrypt.hpp

Lines changed: 203 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,65 @@ struct toJs_impl<session::Envelope> {
4949
}
5050
};
5151

52+
template <>
53+
struct toJs_impl<session::DecodedEnvelope> {
54+
Napi::Object operator()(const Napi::Env& env, const session::DecodedEnvelope decoded_envelope) {
55+
auto obj = Napi::Object::New(env);
56+
57+
obj.Set("envelope", toJs(env, decoded_envelope.envelope));
58+
obj.Set("contentPlaintextUnpadded", toJs(env, decoded_envelope.content_plaintext));
59+
obj.Set("sessionId",
60+
toJs(env, "05" + oxenc::to_hex(decoded_envelope.sender_x25519_pubkey)));
61+
obj.Set("decodedPro", decoded_envelope.pro ? toJs(env, decoded_envelope.pro) : env.Null());
62+
63+
return obj;
64+
}
65+
};
66+
67+
template <>
68+
struct toJs_impl<SESSION_PROTOCOL_PRO_FEATURES> {
69+
Napi::Object operator()(const Napi::Env& env, const SESSION_PROTOCOL_PRO_FEATURES bitset) {
70+
Napi::Array arr = Napi::Array::New(env);
71+
uint32_t index = 0;
72+
73+
if (bitset == SESSION_PROTOCOL_PRO_FEATURES_NIL) {
74+
return arr;
75+
}
76+
77+
if (bitset & (SESSION_PROTOCOL_PRO_FEATURES_10K_CHARACTER_LIMIT)) {
78+
arr[index] = Napi::String::New(env, "10K_CHARACTER_LIMIT");
79+
index++;
80+
}
81+
if (bitset & SESSION_PROTOCOL_PRO_FEATURES_PRO_BADGE) {
82+
arr[index++] = Napi::String::New(env, "PRO_BADGE");
83+
index++;
84+
}
85+
if (bitset & SESSION_PROTOCOL_PRO_FEATURES_ANIMATED_AVATAR) {
86+
arr[index++] = Napi::String::New(env, "ANIMATED_AVATAR");
87+
index++;
88+
}
89+
return arr;
90+
}
91+
};
92+
93+
template <>
94+
struct toJs_impl<session::DecodedPro> {
95+
Napi::Object operator()(const Napi::Env& env, const session::DecodedPro decoded_pro) {
96+
auto obj = Napi::Object::New(env);
97+
98+
obj["proStatus"] =
99+
toJs(env,
100+
decoded_pro.status == ProStatus::InvalidProBackendSig ? "InvalidProBackendSig"
101+
: decoded_pro.status == ProStatus::InvalidUserSig ? "InvalidUserSig"
102+
: decoded_pro.status == ProStatus::Valid ? "Valid"
103+
: "Expired");
104+
obj["proProof"] = toJs(env, decoded_pro.proof);
105+
obj["proFeatures"] = toJs(env, decoded_pro.features);
106+
107+
return obj;
108+
}
109+
};
110+
52111
inline std::vector<unsigned char> extractPlaintext(
53112
const Napi::Object& obj, const std::string identifier) {
54113

@@ -66,11 +125,11 @@ inline std::chrono::milliseconds extractSentTimestampMs(
66125
return sentTimestampMs;
67126
}
68127

69-
inline std::span<const unsigned char> extractSenderEd25519SeedAsSpan(
128+
inline std::vector<unsigned char> extractSenderEd25519SeedAsVector(
70129
const Napi::Object& obj, const std::string identifier) {
71130

72131
assertIsUInt8Array(
73-
obj.Get("senderEd25519Seed"), "extractSenderEd25519SeedAsSpan.senderEd25519Seed");
132+
obj.Get("senderEd25519Seed"), "extractSenderEd25519SeedAsVector.senderEd25519Seed");
74133

75134
auto senderEd25519Seed = toCppBuffer(obj.Get("senderEd25519Seed"), identifier);
76135
assert_length(senderEd25519Seed, 32, identifier);
@@ -80,7 +139,7 @@ inline std::span<const unsigned char> extractSenderEd25519SeedAsSpan(
80139

81140
inline session::array_uc33 extractRecipientPubkeyAsArray(
82141
const Napi::Object& obj, const std::string identifier) {
83-
assertIsString(obj.Get("recipientPubkey"));
142+
assertIsString(obj.Get("recipientPubkey"), identifier);
84143
auto recipientPubkeyHex = toCppString(obj.Get("recipientPubkey"), identifier);
85144
assert_length(recipientPubkeyHex, 66, identifier);
86145

@@ -89,7 +148,7 @@ inline session::array_uc33 extractRecipientPubkeyAsArray(
89148

90149
inline session::array_uc32 extractCommunityPubkeyAsArray(
91150
const Napi::Object& obj, const std::string identifier) {
92-
assertIsString(obj.Get("communityPubkey"));
151+
assertIsString(obj.Get("communityPubkey"), identifier);
93152
auto communityPubkeyHex = toCppString(obj.Get("communityPubkey"), identifier);
94153
assert_length(communityPubkeyHex, 64, identifier);
95154

@@ -98,7 +157,7 @@ inline session::array_uc32 extractCommunityPubkeyAsArray(
98157

99158
inline session::array_uc33 extractGroupEd25519PubkeyAsArray(
100159
const Napi::Object& obj, const std::string identifier) {
101-
assertIsString(obj.Get("groupEd25519Pubkey"));
160+
assertIsString(obj.Get("groupEd25519Pubkey"), identifier);
102161
std::string groupEd25519PubkeyHex = toCppString(obj.Get("groupEd25519Pubkey"), identifier);
103162

104163
assert_length(groupEd25519PubkeyHex, 66, identifier);
@@ -109,7 +168,7 @@ inline session::array_uc33 extractGroupEd25519PubkeyAsArray(
109168

110169
inline cleared_uc32 extractGroupEncKeyAsArray(
111170
const Napi::Object& obj, const std::string identifier) {
112-
assertIsString(obj.Get("groupEncKey"));
171+
assertIsString(obj.Get("groupEncKey"), identifier);
113172

114173
auto groupEncKeyHex = toCppString(obj.Get("groupEncKey"), identifier);
115174
assert_length(groupEncKeyHex, 64, identifier);
@@ -122,16 +181,16 @@ inline cleared_uc32 extractGroupEncKeyAsArray(
122181
return result;
123182
}
124183

125-
inline std::optional<std::span<const unsigned char>> extractProRotatingEd25519PrivKeyAsSpan(
184+
inline std::optional<std::vector<unsigned char>> extractProRotatingEd25519PrivKeyAsSpan(
126185
const Napi::Object& obj, const std::string identifier) {
127-
assertIsStringOrNull(obj.Get("proRotatingEd25519PrivKey"));
186+
assertIsStringOrNull(obj.Get("proRotatingEd25519PrivKey"), identifier);
128187
auto proRotatingEd25519PrivKeyHex =
129188
maybeNonemptyString(obj.Get("proRotatingEd25519PrivKey"), identifier);
130189

131190
if (proRotatingEd25519PrivKeyHex.has_value() && proRotatingEd25519PrivKeyHex.value().size()) {
132191
assert_length(*proRotatingEd25519PrivKeyHex, 64, identifier);
133192

134-
auto ret = from_hex_to_span(*proRotatingEd25519PrivKeyHex);
193+
auto ret = from_hex_to_vector(*proRotatingEd25519PrivKeyHex);
135194

136195
return ret;
137196
}
@@ -155,6 +214,21 @@ inline uint32_t extractServerId(const Napi::Object& obj, const std::string ident
155214
return serverId;
156215
}
157216

217+
inline std::string extractMessageHash(const Napi::Object& obj, const std::string identifier) {
218+
assertIsString(obj.Get("messageHash"), identifier);
219+
auto messageHash = toCppString(obj.Get("messageHash"), identifier);
220+
221+
return messageHash;
222+
}
223+
224+
inline std::vector<unsigned char> extractEnvelopePayload(
225+
const Napi::Object& obj, const std::string identifier) {
226+
assertIsUInt8Array(obj.Get("envelopePayload"), identifier);
227+
auto envelopePayload = toCppBuffer(obj.Get("envelopePayload"), identifier);
228+
229+
return envelopePayload;
230+
}
231+
158232
inline std::chrono::sys_time<std::chrono::milliseconds> extractNowSysMs(
159233
const Napi::Object& obj, const std::string identifier) {
160234
assertIsNumber(obj.Get("nowMs"), identifier);
@@ -165,7 +239,7 @@ inline std::chrono::sys_time<std::chrono::milliseconds> extractNowSysMs(
165239

166240
inline session::array_uc32 extractProBackendPubkeyHex(
167241
const Napi::Object& obj, const std::string identifier) {
168-
assertIsString(obj.Get("proBackendPubkeyHex"));
242+
assertIsString(obj.Get("proBackendPubkeyHex"), identifier);
169243

170244
auto proBackendPubkeyHex = toCppString(obj.Get("proBackendPubkeyHex"), identifier);
171245
assert_length(proBackendPubkeyHex, 64, identifier);
@@ -175,6 +249,18 @@ inline session::array_uc32 extractProBackendPubkeyHex(
175249
return arr;
176250
}
177251

252+
inline session::array_uc32 extractEd25519PrivateKeyHex(
253+
const Napi::Object& obj, const std::string identifier) {
254+
assertIsString(obj.Get("ed25519PrivateKeyHex"), identifier);
255+
256+
auto ed25519PrivateKeyHex = toCppString(obj.Get("ed25519PrivateKeyHex"), identifier);
257+
assert_length(ed25519PrivateKeyHex, 64, identifier);
258+
259+
auto arr = from_hex_to_array<32>(ed25519PrivateKeyHex);
260+
261+
return arr;
262+
}
263+
178264
class MultiEncryptWrapper : public Napi::ObjectWrap<MultiEncryptWrapper> {
179265
public:
180266
MultiEncryptWrapper(const Napi::CallbackInfo& info) :
@@ -227,15 +313,18 @@ class MultiEncryptWrapper : public Napi::ObjectWrap<MultiEncryptWrapper> {
227313
napi_writable | napi_configurable)),
228314

229315
// Destination decrypt
230-
// StaticMethod<&MultiEncryptWrapper::encryptFor1o1>(
231-
// "encryptFor1o1",
232-
// static_cast<napi_property_attributes>(
233-
// napi_writable | napi_configurable)),
234-
235316
StaticMethod<&MultiEncryptWrapper::decryptForCommunity>(
236317
"decryptForCommunity",
237318
static_cast<napi_property_attributes>(
238319
napi_writable | napi_configurable)),
320+
StaticMethod<&MultiEncryptWrapper::decryptFor1o1>(
321+
"decryptFor1o1",
322+
static_cast<napi_property_attributes>(
323+
napi_writable | napi_configurable)),
324+
// StaticMethod<&MultiEncryptWrapper::decryptForGroup>(
325+
// "decryptForGroup",
326+
// static_cast<napi_property_attributes>(
327+
// napi_writable | napi_configurable)),
239328
});
240329
}
241330

@@ -253,7 +342,7 @@ class MultiEncryptWrapper : public Napi::ObjectWrap<MultiEncryptWrapper> {
253342
auto ed25519SecretKey =
254343
toCppBuffer(obj.Get("ed25519SecretKey"), "multiEncrypt.ed25519SecretKey");
255344

256-
assertIsString(obj.Get("domain"));
345+
assertIsString(obj.Get("domain"), "multiEncrypt.domain");
257346
auto domain = toCppString(obj.Get("domain"), "multiEncrypt.domain");
258347

259348
// handle the messages conversion
@@ -316,7 +405,7 @@ class MultiEncryptWrapper : public Napi::ObjectWrap<MultiEncryptWrapper> {
316405
auto sender_ed25519_pubkey = toCppBuffer(
317406
obj.Get("senderEd25519Pubkey"), "multiDecryptEd25519.senderEd25519Pubkey");
318407

319-
assertIsString(obj.Get("domain"));
408+
assertIsString(obj.Get("domain"), "multiDecryptEd25519.domain");
320409
auto domain = toCppString(obj.Get("domain"), "multiDecryptEd25519.domain");
321410

322411
return session::decrypt_for_multiple_simple_ed25519(
@@ -345,7 +434,7 @@ class MultiEncryptWrapper : public Napi::ObjectWrap<MultiEncryptWrapper> {
345434
assertIsUInt8Array(obj.Get("data"), "attachmentEncrypt.data");
346435
auto data = toCppBuffer(obj.Get("data"), "attachmentEncrypt.data");
347436

348-
assertIsString(obj.Get("domain"));
437+
assertIsString(obj.Get("domain"), "attachmentEncrypt.domain");
349438
auto domain = toCppString(obj.Get("domain"), "attachmentEncrypt.domain");
350439

351440
assertIsBoolean(obj.Get("allowLarge"));
@@ -460,7 +549,7 @@ class MultiEncryptWrapper : public Napi::ObjectWrap<MultiEncryptWrapper> {
460549

461550
ready_to_send[i] = session::encode_for_1o1(
462551
extractPlaintext(obj, "encryptFor1o1.obj.plaintext"),
463-
extractSenderEd25519SeedAsSpan(obj, "encryptFor1o1.obj.senderEd25519Seed"),
552+
extractSenderEd25519SeedAsVector(obj, "encryptFor1o1.obj.senderEd25519Seed"),
464553
extractSentTimestampMs(obj, "encryptFor1o1.obj.sentTimestampMs"),
465554
extractRecipientPubkeyAsArray(obj, "encryptFor1o1.obj.recipientPubkey"),
466555
extractProRotatingEd25519PrivKeyAsSpan(
@@ -507,7 +596,7 @@ class MultiEncryptWrapper : public Napi::ObjectWrap<MultiEncryptWrapper> {
507596

508597
ready_to_send[i] = session::encode_for_community_inbox(
509598
extractPlaintext(obj, "encryptForCommunityInbox.obj.plaintext"),
510-
extractSenderEd25519SeedAsSpan(
599+
extractSenderEd25519SeedAsVector(
511600
obj, "encryptForCommunityInbox.obj.senderEd25519Seed"),
512601
extractSentTimestampMs(obj, "encryptForCommunityInbox.obj.sentTimestampMs"),
513602
extractRecipientPubkeyAsArray(
@@ -595,7 +684,7 @@ class MultiEncryptWrapper : public Napi::ObjectWrap<MultiEncryptWrapper> {
595684
auto obj = itemValue.As<Napi::Object>();
596685

597686
auto plaintext = extractPlaintext(obj, "encryptForGroup.obj.plaintext");
598-
auto senderEd25519Seed = extractSenderEd25519SeedAsSpan(
687+
auto senderEd25519Seed = extractSenderEd25519SeedAsVector(
599688
obj, "encryptForGroup.obj.senderEd25519Seed");
600689

601690
auto sentTimestampMs =
@@ -702,21 +791,105 @@ class MultiEncryptWrapper : public Napi::ObjectWrap<MultiEncryptWrapper> {
702791
std::span(d.content_plaintext)
703792
.subspan(0, d.content_plaintext_unpadded_size);
704793

794+
to_insert.Set(
795+
"envelope", d.envelope ? toJs(info.Env(), *d.envelope) : info.Env().Null());
705796
to_insert.Set(
706797
"contentPlaintextUnpadded", toJs(info.Env(), content_plaintext_unpadded));
707798
to_insert.Set("serverId", toJs(info.Env(), decryptedServerIds[i]));
799+
708800
to_insert.Set(
709-
"envelope", d.envelope ? toJs(info.Env(), *d.envelope) : info.Env().Null());
801+
"proSigHex",
802+
d.pro_sig ? toJs(info.Env(), oxenc::to_hex(*d.pro_sig))
803+
: info.Env().Null());
804+
to_insert.Set("decodedPro", d.pro ? toJs(info.Env(), d.pro) : info.Env().Null());
710805

711-
if (d.pro_sig)
712-
to_insert.Set("proSigHex", toJs(info.Env(), oxenc::to_hex(*d.pro_sig)));
713-
else
714-
to_insert.Set("proSigHex", info.Env().Null());
806+
ret.Set(i, to_insert);
807+
i++;
808+
}
809+
810+
return ret;
811+
});
812+
};
813+
814+
static Napi::Value decryptFor1o1(const Napi::CallbackInfo& info) {
815+
return wrapResult(info, [&] {
816+
// we expect two arguments that match:
817+
// first: [{
818+
// "envelopePayload": Uint8Array,
819+
// "messageHash": string,
820+
// }],
821+
// second: {
822+
// "nowMs": number,
823+
// "proBackendPubkeyHex": Hexstring,
824+
// "ed25519PrivateKeyHex": Hexstring,
825+
// }
826+
//
827+
828+
assertInfoLength(info, 2);
829+
assertIsArray(info[0], "decryptFor1o1 info[0]");
830+
assertIsObject(info[1]);
831+
832+
auto first = info[0].As<Napi::Array>();
833+
834+
if (first.IsEmpty())
835+
throw std::invalid_argument("decryptFor1o1 first received empty");
836+
837+
auto second = info[1].As<Napi::Object>();
838+
839+
if (second.IsEmpty())
840+
throw std::invalid_argument("decryptFor1o1 second received empty");
841+
842+
auto nowMs = extractNowSysMs(second, "decryptFor1o1.second.nowMs");
843+
auto proBackendPubkeyHex =
844+
extractProBackendPubkeyHex(second, "decryptFor1o1.second.proBackendPubkeyHex");
845+
846+
std::vector<DecodedEnvelope> decrypted;
847+
std::vector<std::string> decryptedMessageHashes;
848+
849+
DecodeEnvelopeKey keys{};
850+
auto keySpan = extractEd25519PrivateKeyHex(
851+
second, "decryptFor1o1.second.ed25519PrivateKeyHex");
852+
853+
std::vector<std::span<const unsigned char>> keySpans;
854+
keySpans.emplace_back(keySpan.data(), keySpan.size());
855+
keys.decrypt_keys = keySpans;
856+
857+
for (uint32_t i = 0; i < first.Length(); i++) {
858+
auto itemValue = first.Get(i);
859+
if (!itemValue.IsObject()) {
860+
throw std::invalid_argument(
861+
"decryptFor1o1 itemValue is not an "
862+
"object");
863+
}
864+
auto obj = itemValue.As<Napi::Object>();
865+
866+
try {
867+
std::string messageHash =
868+
extractMessageHash(obj, "decryptFor1o1.obj.messageHash");
869+
870+
auto envelopePayload =
871+
extractEnvelopePayload(obj, "decryptFor1o1.obj.envelopePayload");
872+
decrypted.push_back(
873+
session::decode_envelope(
874+
keys, envelopePayload, nowMs, proBackendPubkeyHex));
875+
decryptedMessageHashes.push_back(messageHash);
876+
} catch (const std::exception& e) {
877+
log::warning(
878+
cat,
879+
"decryptFor1o1: Failed to decrypt "
880+
"message at index {}",
881+
i);
882+
}
883+
}
884+
885+
auto ret = Napi::Array::New(info.Env(), decrypted.size());
886+
uint32_t i = 0;
887+
888+
for (auto& d : decrypted) {
889+
auto to_insert = Napi::Object::New(info.Env());
715890

716-
if (!d.pro.has_value())
717-
to_insert.Set("proProof", info.Env().Null());
718-
else
719-
to_insert.Set("proProof", toJs(info.Env(), d.pro->proof));
891+
to_insert.Set("decodedEnvelope", toJs(info.Env(), d));
892+
to_insert.Set("messageHash", toJs(info.Env(), decryptedMessageHashes[i]));
720893

721894
ret.Set(i, to_insert);
722895
i++;

0 commit comments

Comments
 (0)