@@ -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+
52111inline 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
81140inline 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
90149inline 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
99158inline 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
110169inline 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+
158232inline 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
166240inline 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+
178264class 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