Skip to content

Commit f5384c3

Browse files
committed
feat: add 1o1 encrypt
1 parent 70cc278 commit f5384c3

File tree

5 files changed

+139
-6
lines changed

5 files changed

+139
-6
lines changed

include/multi_encrypt/multi_encrypt.hpp

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22

33
#include <napi.h>
4+
#include <oxenc/hex.h>
45

56
#include <algorithm>
67
#include <span>
@@ -14,6 +15,7 @@
1415

1516
namespace session::nodeapi {
1617

18+
1719
class MultiEncryptWrapper : public Napi::ObjectWrap<MultiEncryptWrapper> {
1820
public:
1921
MultiEncryptWrapper(const Napi::CallbackInfo& info) :
@@ -36,6 +38,7 @@ class MultiEncryptWrapper : public Napi::ObjectWrap<MultiEncryptWrapper> {
3638
"multiDecryptEd25519",
3739
static_cast<napi_property_attributes>(
3840
napi_writable | napi_configurable)),
41+
// Attachments encrypt/decrypt
3942
StaticMethod<&MultiEncryptWrapper::attachmentDecrypt>(
4043
"attachmentDecrypt",
4144
static_cast<napi_property_attributes>(
@@ -44,6 +47,25 @@ class MultiEncryptWrapper : public Napi::ObjectWrap<MultiEncryptWrapper> {
4447
"attachmentEncrypt",
4548
static_cast<napi_property_attributes>(
4649
napi_writable | napi_configurable)),
50+
51+
// Destination encrypt
52+
StaticMethod<&MultiEncryptWrapper::encryptFor1o1>(
53+
"encryptFor1o1",
54+
static_cast<napi_property_attributes>(
55+
napi_writable | napi_configurable)),
56+
57+
// StaticMethod<&MultiEncryptWrapper::encryptForCommunity>(
58+
// "encryptForCommunity",
59+
// static_cast<napi_property_attributes>(
60+
// napi_writable | napi_configurable)),
61+
// StaticMethod<&MultiEncryptWrapper::encryptForCommunityInbox>(
62+
// "encryptForCommunityInbox",
63+
// static_cast<napi_property_attributes>(
64+
// napi_writable | napi_configurable)),
65+
// StaticMethod<&MultiEncryptWrapper::encryptForGroup>(
66+
// "encryptForGroup",
67+
// static_cast<napi_property_attributes>(
68+
// napi_writable | napi_configurable)),
4769
});
4870
}
4971

@@ -181,6 +203,69 @@ class MultiEncryptWrapper : public Napi::ObjectWrap<MultiEncryptWrapper> {
181203
});
182204
};
183205

206+
static Napi::Value encryptFor1o1(const Napi::CallbackInfo& info) {
207+
return wrapResult(info, [&] {
208+
// we expect an single argument which is an array of objects with the following
209+
// properties:
210+
// {
211+
// "plaintext": Uint8Array,
212+
// "sentTimestampMs": Number,
213+
// "ed25519Privkey": Hexstring,
214+
// "recipientPubkey": Hexstring,
215+
// "proRotatingEd25519Privkey": Hexstring | null,
216+
// }
217+
//
218+
219+
assertInfoLength(info, 1);
220+
assertIsArray(info[0], "encryptFor1o1 info[0]");
221+
222+
auto array = info[0].As<Napi::Array>();
223+
224+
if (array.IsEmpty())
225+
throw std::invalid_argument("encryptFor1o1 received empty");
226+
227+
std::vector<std::vector<uint8_t>> ready_to_send(array.Length());
228+
for (uint32_t i = 0; i < array.Length(); i++) {
229+
auto itemValue = array.Get(i);
230+
if (!itemValue.IsObject()) {
231+
throw std::invalid_argument("encryptFor1o1 itemValue is not an object");
232+
}
233+
auto obj = itemValue.As<Napi::Object>();
234+
235+
assertIsUInt8Array(obj.Get("plaintext"), "encryptFor1o1.obj.message");
236+
auto plaintext = toCppBuffer(obj.Get("plaintext"), "encryptFor1o1.obj.message");
237+
238+
assertIsNumber(obj.Get("sentTimestampMs"), "encryptFor1o1.obj.sentTimestampMs");
239+
auto sentTimestampMs =
240+
toCppMs(obj.Get("sentTimestampMs"), "encryptFor1o1.obj.sentTimestampMs");
241+
242+
assertIsString(obj.Get("ed25519Privkey"));
243+
auto ed25519PrivkeyHex =
244+
toCppString(obj.Get("ed25519Privkey"), "encryptFor1o1.obj.ed25519Privkey");
245+
246+
assertIsString(obj.Get("recipientPubkey"));
247+
auto recipientPubkeyHex = toCppString(
248+
obj.Get("recipientPubkey"), "encryptFor1o1.obj.recipientPubkey");
249+
250+
assertIsStringOrNull(obj.Get("proRotatingEd25519Privkey"));
251+
auto proRotatingEd25519PrivkeyHex = maybeNonemptyString(
252+
obj.Get("proRotatingEd25519Privkey"),
253+
"encryptFor1o1.obj.proRotatingEd25519Privkey");
254+
ready_to_send[i] = session::encode_for_1o1(
255+
plaintext,
256+
from_hex_to_span(ed25519PrivkeyHex),
257+
sentTimestampMs,
258+
from_hex_to_array<33>(recipientPubkeyHex),
259+
from_hex_to_span(proRotatingEd25519PrivkeyHex.value_or("")));
260+
}
261+
262+
auto ret = Napi::Object::New(info.Env());
263+
ret.Set("encryptedData", toJs(info.Env(), ready_to_send));
264+
265+
return ret;
266+
});
267+
};
268+
184269
static Napi::Value attachmentDecrypt(const Napi::CallbackInfo& info) {
185270
return wrapResult(info, [&] {
186271
assertInfoLength(info, 1);

include/utilities.hpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ std::optional<std::chrono::sys_seconds> maybeNonemptySysSeconds(
6464
Napi::Value x, const std::string& identifier);
6565

6666
std::chrono::sys_seconds toCppSysSeconds(Napi::Value x, const std::string& identifier);
67+
std::chrono::milliseconds toCppMs(Napi::Value x, const std::string& identifier);
6768

6869
bool toCppBoolean(Napi::Value x, const std::string& identifier);
6970

@@ -150,10 +151,7 @@ template <>
150151
struct toJs_impl<std::vector<std::byte>> {
151152
auto operator()(const Napi::Env& env, std::vector<std::byte> b) const {
152153
return Napi::Buffer<uint8_t>::Copy(
153-
env,
154-
reinterpret_cast<const unsigned char*>(b.data()),
155-
b.size()
156-
);
154+
env, reinterpret_cast<const unsigned char*>(b.data()), b.size());
157155
}
158156
};
159157

@@ -353,4 +351,12 @@ Napi::Object decrypt_result_to_JS(
353351

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

354+
std::span<const uint8_t> from_hex_to_span(std::string_view x);
355+
356+
template <std::size_t N>
357+
std::array<uint8_t, N> spanToArray(std::span<const unsigned char> span);
358+
359+
template <std::size_t N>
360+
std::array<uint8_t, N> from_hex_to_array(std::string_view x);
361+
356362
} // namespace session::nodeapi

libsession-util

src/utilities.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,16 @@ std::chrono::sys_seconds toCppSysSeconds(Napi::Value x, const std::string& ident
163163
throw std::invalid_argument{"toCppSysSeconds with invalid type, called from " + identifier};
164164
}
165165

166+
std::chrono::milliseconds toCppMs(Napi::Value x, const std::string& identifier) {
167+
168+
if (x.IsNumber()) {
169+
auto num = x.As<Napi::Number>().Int64Value();
170+
return std::chrono::milliseconds{num};
171+
}
172+
173+
throw std::invalid_argument{"toCppMs with invalid type, called from " + identifier};
174+
}
175+
166176
std::optional<session::config::profile_pic> maybeNonemptyProfilePic(
167177
Napi::Value x, const std::string& identifier) {
168178
if (x.IsNull() || x.IsUndefined())
@@ -317,4 +327,24 @@ confirm_pushed_entry_t confirm_pushed_entry_from_JS(const Napi::Env& env, const
317327
confirm_pushed_entry_t confirmed_pushed_entry{seqno, hashes};
318328
return confirmed_pushed_entry;
319329
}
330+
331+
std::span<const uint8_t> from_hex_to_span(std::string_view x) {
332+
return session::to_span(oxenc::from_hex(x));
333+
}
334+
335+
template <std::size_t N>
336+
std::array<uint8_t, N> spanToArray(std::span<const unsigned char> span) {
337+
if (span.size() != N) {
338+
throw std::invalid_argument("Span size does not match array size");
339+
}
340+
341+
std::array<uint8_t, N> result;
342+
std::ranges::copy(span, result.begin());
343+
return result;
344+
}
345+
346+
template <std::size_t N>
347+
std::array<uint8_t, N> from_hex_to_array(std::string_view x) {
348+
return spanToArray<N>(session::to_span(oxenc::from_hex(x)));
349+
}
320350
} // namespace session::nodeapi

types/multi_encrypt/multi_encrypt.d.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ declare module 'libsession_util_nodejs' {
3636
attachmentDecrypt: (opts: { encryptedData: Uint8Array; decryptionKey: Uint8Array }) => {
3737
decryptedData: Uint8Array;
3838
};
39+
40+
encryptFor1o1: (
41+
opts: Array<{
42+
plaintext: Uint8Array;
43+
sentTimestampMs: number;
44+
ed25519Privkey: Uint8Array;
45+
recipientPubkey: Uint8Array;
46+
proRotatingEd25519Privkey?: Uint8Array;
47+
}>
48+
) => Array<Uint8Array>;
3949
};
4050

4151
export type MultiEncryptActionsCalls = MakeWrapperActionCalls<MultiEncryptWrapper>;
@@ -48,6 +58,7 @@ declare module 'libsession_util_nodejs' {
4858
public static multiDecryptEd25519: MultiEncryptWrapper['multiDecryptEd25519'];
4959
public static attachmentDecrypt: MultiEncryptWrapper['attachmentDecrypt'];
5060
public static attachmentEncrypt: MultiEncryptWrapper['attachmentEncrypt'];
61+
public static encryptFor1o1: MultiEncryptWrapper['encryptFor1o1'];
5162
}
5263

5364
/**
@@ -59,5 +70,6 @@ declare module 'libsession_util_nodejs' {
5970
| MakeActionCall<MultiEncryptWrapper, 'multiEncrypt'>
6071
| MakeActionCall<MultiEncryptWrapper, 'multiDecryptEd25519'>
6172
| MakeActionCall<MultiEncryptWrapper, 'attachmentDecrypt'>
62-
| MakeActionCall<MultiEncryptWrapper, 'attachmentEncrypt'>;
73+
| MakeActionCall<MultiEncryptWrapper, 'attachmentEncrypt'>
74+
| MakeActionCall<MultiEncryptWrapper, 'encryptFor1o1'>;
6375
}

0 commit comments

Comments
 (0)