|  | 
| 1 | 1 | #pragma once | 
| 2 | 2 | 
 | 
| 3 | 3 | #include <napi.h> | 
| 4 |  | -#include <span> | 
| 5 |  | -#include <vector> | 
| 6 | 4 | 
 | 
| 7 | 5 | #include <algorithm> | 
|  | 6 | +#include <span> | 
|  | 7 | +#include <vector> | 
| 8 | 8 | 
 | 
| 9 | 9 | #include "../utilities.hpp" | 
|  | 10 | +#include "session/attachments.hpp" | 
| 10 | 11 | #include "session/config/user_profile.hpp" | 
| 11 | 12 | #include "session/multi_encrypt.hpp" | 
| 12 | 13 | #include "session/random.hpp" | 
| @@ -35,7 +36,14 @@ class MultiEncryptWrapper : public Napi::ObjectWrap<MultiEncryptWrapper> { | 
| 35 | 36 |                                 "multiDecryptEd25519", | 
| 36 | 37 |                                 static_cast<napi_property_attributes>( | 
| 37 | 38 |                                         napi_writable | napi_configurable)), | 
| 38 |  | - | 
|  | 39 | +                        StaticMethod<&MultiEncryptWrapper::attachmentDecrypt>( | 
|  | 40 | +                                "attachmentDecrypt", | 
|  | 41 | +                                static_cast<napi_property_attributes>( | 
|  | 42 | +                                        napi_writable | napi_configurable)), | 
|  | 43 | +                        StaticMethod<&MultiEncryptWrapper::attachmentEncrypt>( | 
|  | 44 | +                                "attachmentEncrypt", | 
|  | 45 | +                                static_cast<napi_property_attributes>( | 
|  | 46 | +                                        napi_writable | napi_configurable)), | 
| 39 | 47 |                 }); | 
| 40 | 48 |     } | 
| 41 | 49 | 
 | 
| @@ -83,8 +91,10 @@ class MultiEncryptWrapper : public Napi::ObjectWrap<MultiEncryptWrapper> { | 
| 83 | 91 |             } | 
| 84 | 92 |             std::vector<unsigned char> random_nonce = session::random::random(24); | 
| 85 | 93 | 
 | 
| 86 |  | -            std::vector<std::span<const unsigned char>> messages_sv(messages.begin(), messages.end()); | 
| 87 |  | -            std::vector<std::span<const unsigned char>> recipients_sv(recipients.begin(), recipients.end()); | 
|  | 94 | +            std::vector<std::span<const unsigned char>> messages_sv( | 
|  | 95 | +                    messages.begin(), messages.end()); | 
|  | 96 | +            std::vector<std::span<const unsigned char>> recipients_sv( | 
|  | 97 | +                    recipients.begin(), recipients.end()); | 
| 88 | 98 | 
 | 
| 89 | 99 |             // Note: this function needs the first 2 args to be vector of sv explicitly | 
| 90 | 100 |             return session::encrypt_for_multiple_simple( | 
| @@ -121,6 +131,99 @@ class MultiEncryptWrapper : public Napi::ObjectWrap<MultiEncryptWrapper> { | 
| 121 | 131 |                     encoded, ed25519_secret_key, sender_ed25519_pubkey, domain); | 
| 122 | 132 |         }); | 
| 123 | 133 |     }; | 
|  | 134 | + | 
|  | 135 | +    static Napi::Value attachmentEncrypt(const Napi::CallbackInfo& info) { | 
|  | 136 | +        return wrapResult(info, [&] { | 
|  | 137 | +            assertInfoLength(info, 1); | 
|  | 138 | +            assertIsObject(info[0]); | 
|  | 139 | +            auto obj = info[0].As<Napi::Object>(); | 
|  | 140 | + | 
|  | 141 | +            if (obj.IsEmpty()) | 
|  | 142 | +                throw std::invalid_argument("attachmentEncrypt received empty"); | 
|  | 143 | + | 
|  | 144 | +            assertIsUInt8Array(obj.Get("seed"), "attachmentEncrypt.seed"); | 
|  | 145 | +            auto seed = toCppBuffer(obj.Get("seed"), "attachmentEncrypt.seed"); | 
|  | 146 | + | 
|  | 147 | +            assertIsUInt8Array(obj.Get("data"), "attachmentEncrypt.data"); | 
|  | 148 | +            auto data = toCppBuffer(obj.Get("data"), "attachmentEncrypt.data"); | 
|  | 149 | + | 
|  | 150 | +            assertIsString(obj.Get("domain")); | 
|  | 151 | +            auto domain = toCppString(obj.Get("domain"), "attachmentEncrypt.domain"); | 
|  | 152 | + | 
|  | 153 | +            assertIsBoolean(obj.Get("allowLarge")); | 
|  | 154 | + | 
|  | 155 | +            auto allow_large = toCppBoolean(obj.Get("allowLarge"), "attachmentEncrypt.allowLarge"); | 
|  | 156 | + | 
|  | 157 | +            if (domain != "attachment" && domain != "profilePic") { | 
|  | 158 | +                throw std::invalid_argument( | 
|  | 159 | +                        "attachmentEncrypt.domain must be either 'attachment' or 'profilePic'"); | 
|  | 160 | +            } | 
|  | 161 | + | 
|  | 162 | +            session::attachment::Domain attachment_domain = | 
|  | 163 | +                    domain == "attachment" ? session::attachment::Domain::ATTACHMENT | 
|  | 164 | +                                           : session::attachment::Domain::PROFILE_PIC; | 
|  | 165 | + | 
|  | 166 | +            std::vector<std::byte> seed_bytes( | 
|  | 167 | +                    reinterpret_cast<const std::byte*>(seed.data()), | 
|  | 168 | +                    reinterpret_cast<const std::byte*>(seed.data() + seed.size())); | 
|  | 169 | + | 
|  | 170 | +            std::vector<std::byte> data_bytes( | 
|  | 171 | +                    reinterpret_cast<const std::byte*>(data.data()), | 
|  | 172 | +                    reinterpret_cast<const std::byte*>(data.data() + data.size())); | 
|  | 173 | +            auto encrypted = session::attachment::encrypt( | 
|  | 174 | +                    seed_bytes, data_bytes, attachment_domain, allow_large); | 
|  | 175 | + | 
|  | 176 | +            auto ret = Napi::Object::New(info.Env()); | 
|  | 177 | +            ret.Set("encryptedData", toJs(info.Env(), encrypted.first)); | 
|  | 178 | +            ret.Set("encryptionKey", toJs(info.Env(), encrypted.second)); | 
|  | 179 | + | 
|  | 180 | +            return ret; | 
|  | 181 | +        }); | 
|  | 182 | +    }; | 
|  | 183 | + | 
|  | 184 | +    static Napi::Value attachmentDecrypt(const Napi::CallbackInfo& info) { | 
|  | 185 | +        return wrapResult(info, [&] { | 
|  | 186 | +            assertInfoLength(info, 1); | 
|  | 187 | +            assertIsObject(info[0]); | 
|  | 188 | +            auto obj = info[0].As<Napi::Object>(); | 
|  | 189 | + | 
|  | 190 | +            if (obj.IsEmpty()) | 
|  | 191 | +                throw std::invalid_argument("attachmentDecrypt received empty"); | 
|  | 192 | + | 
|  | 193 | +            assertIsUInt8Array(obj.Get("encryptedData"), "attachmentDecrypt.encryptedData"); | 
|  | 194 | +            auto encrypted_data = | 
|  | 195 | +                    toCppBuffer(obj.Get("encryptedData"), "attachmentDecrypt.encryptedData"); | 
|  | 196 | + | 
|  | 197 | +            assertIsUInt8Array(obj.Get("decryptionKey"), "attachmentDecrypt.decryptionKey"); | 
|  | 198 | +            auto decryption_key = | 
|  | 199 | +                    toCppBuffer(obj.Get("decryptionKey"), "attachmentDecrypt.decryptionKey"); | 
|  | 200 | + | 
|  | 201 | +            std::vector<std::byte> encrypted_data_bytes( | 
|  | 202 | +                    reinterpret_cast<const std::byte*>(encrypted_data.data()), | 
|  | 203 | +                    reinterpret_cast<const std::byte*>( | 
|  | 204 | +                            encrypted_data.data() + encrypted_data.size())); | 
|  | 205 | + | 
|  | 206 | +            std::vector<std::byte> decryption_key_bytes( | 
|  | 207 | +                    reinterpret_cast<const std::byte*>(decryption_key.data()), | 
|  | 208 | +                    reinterpret_cast<const std::byte*>( | 
|  | 209 | +                            decryption_key.data() + decryption_key.size())); | 
|  | 210 | + | 
|  | 211 | +            if (decryption_key_bytes.size() != session::attachment::ENCRYPT_KEY_SIZE) { | 
|  | 212 | +                throw std::invalid_argument("Key size mismatch"); | 
|  | 213 | +            } | 
|  | 214 | + | 
|  | 215 | +            std::span<const std::byte, session::attachment::ENCRYPT_KEY_SIZE> decryption_key_span( | 
|  | 216 | +                    decryption_key_bytes.data(), session::attachment::ENCRYPT_KEY_SIZE); | 
|  | 217 | + | 
|  | 218 | +            auto decrypted = | 
|  | 219 | +                    session::attachment::decrypt(encrypted_data_bytes, decryption_key_span); | 
|  | 220 | + | 
|  | 221 | +            auto ret = Napi::Object::New(info.Env()); | 
|  | 222 | +            ret.Set("decryptedData", toJs(info.Env(), decrypted)); | 
|  | 223 | + | 
|  | 224 | +            return ret; | 
|  | 225 | +        }); | 
|  | 226 | +    }; | 
| 124 | 227 | }; | 
| 125 | 228 | 
 | 
| 126 |  | -}  // namespace session::nodeapi | 
|  | 229 | +};  // namespace session::nodeapi | 
0 commit comments