1 //
2 // Copyright 2021 Signal Messenger, LLC.
3 // SPDX-License-Identifier: AGPL-3.0-only
4 //
5 
6 use super::*;
7 
8 use async_trait::async_trait;
9 use signal_neon_futures::*;
10 use std::cell::RefCell;
11 use std::sync::Arc;
12 use uuid::Uuid;
13 
14 pub struct NodePreKeyStore {
15     js_channel: Channel,
16     store_object: Arc<Root<JsObject>>,
17 }
18 
19 impl NodePreKeyStore {
new(cx: &mut FunctionContext, store: Handle<JsObject>) -> Self20     pub(crate) fn new(cx: &mut FunctionContext, store: Handle<JsObject>) -> Self {
21         Self {
22             js_channel: cx.channel(),
23             store_object: Arc::new(store.root(cx)),
24         }
25     }
26 
do_get_pre_key(&self, id: u32) -> Result<PreKeyRecord, String>27     async fn do_get_pre_key(&self, id: u32) -> Result<PreKeyRecord, String> {
28         let store_object_shared = self.store_object.clone();
29         JsFuture::get_promise(&self.js_channel, move |cx| {
30             let store_object = store_object_shared.to_inner(cx);
31             let id = id.convert_into(cx)?;
32             let result = call_method(cx, store_object, "_getPreKey", vec![id.upcast()])?;
33             let result = result.downcast_or_throw(cx)?;
34             store_object_shared.finalize(cx);
35             Ok(result)
36         })
37         .then(|cx, result| match result {
38             Ok(value) => match value.downcast::<DefaultJsBox<PreKeyRecord>, _>(cx) {
39                 Ok(obj) => Ok((***obj).clone()),
40                 Err(_) => Err("result must be an object".to_owned()),
41             },
42             Err(error) => Err(error
43                 .to_string(cx)
44                 .expect("can convert to string")
45                 .value(cx)),
46         })
47         .await
48     }
49 
do_save_pre_key(&self, id: u32, record: PreKeyRecord) -> Result<(), String>50     async fn do_save_pre_key(&self, id: u32, record: PreKeyRecord) -> Result<(), String> {
51         let store_object_shared = self.store_object.clone();
52         JsFuture::get_promise(&self.js_channel, move |cx| {
53             let store_object = store_object_shared.to_inner(cx);
54             let id: Handle<JsNumber> = id.convert_into(cx)?;
55             let record: Handle<JsValue> = record.convert_into(cx)?;
56             let result = call_method(cx, store_object, "_savePreKey", vec![id.upcast(), record])?
57                 .downcast_or_throw(cx)?;
58             store_object_shared.finalize(cx);
59             Ok(result)
60         })
61         .then(|cx, result| match result {
62             Ok(value) => match value.downcast::<JsUndefined, _>(cx) {
63                 Ok(_) => Ok(()),
64                 Err(_) => Err("unexpected result from _savePreKey".into()),
65             },
66             Err(error) => Err(error
67                 .to_string(cx)
68                 .expect("can convert to string")
69                 .value(cx)),
70         })
71         .await
72     }
73 
do_remove_pre_key(&self, id: u32) -> Result<(), String>74     async fn do_remove_pre_key(&self, id: u32) -> Result<(), String> {
75         let store_object_shared = self.store_object.clone();
76         JsFuture::get_promise(&self.js_channel, move |cx| {
77             let store_object = store_object_shared.to_inner(cx);
78             let id: Handle<JsNumber> = id.convert_into(cx)?;
79             let result = call_method(cx, store_object, "_removePreKey", vec![id.upcast()])?
80                 .downcast_or_throw(cx)?;
81             store_object_shared.finalize(cx);
82             Ok(result)
83         })
84         .then(|cx, result| match result {
85             Ok(value) => match value.downcast::<JsUndefined, _>(cx) {
86                 Ok(_) => Ok(()),
87                 Err(_) => Err("unexpected result from _removePreKey".into()),
88             },
89             Err(error) => Err(error
90                 .to_string(cx)
91                 .expect("can convert to string")
92                 .value(cx)),
93         })
94         .await
95     }
96 }
97 
98 impl Finalize for NodePreKeyStore {
finalize<'b, C: neon::prelude::Context<'b>>(self, cx: &mut C)99     fn finalize<'b, C: neon::prelude::Context<'b>>(self, cx: &mut C) {
100         self.store_object.finalize(cx)
101     }
102 }
103 
104 #[async_trait(?Send)]
105 impl PreKeyStore for NodePreKeyStore {
get_pre_key( &self, pre_key_id: u32, _ctx: libsignal_protocol::Context, ) -> Result<PreKeyRecord, SignalProtocolError>106     async fn get_pre_key(
107         &self,
108         pre_key_id: u32,
109         _ctx: libsignal_protocol::Context,
110     ) -> Result<PreKeyRecord, SignalProtocolError> {
111         self.do_get_pre_key(pre_key_id)
112             .await
113             .map_err(|s| js_error_to_rust("getPreKey", s))
114     }
115 
save_pre_key( &mut self, pre_key_id: u32, record: &PreKeyRecord, _ctx: libsignal_protocol::Context, ) -> Result<(), SignalProtocolError>116     async fn save_pre_key(
117         &mut self,
118         pre_key_id: u32,
119         record: &PreKeyRecord,
120         _ctx: libsignal_protocol::Context,
121     ) -> Result<(), SignalProtocolError> {
122         self.do_save_pre_key(pre_key_id, record.clone())
123             .await
124             .map_err(|s| js_error_to_rust("savePreKey", s))
125     }
126 
remove_pre_key( &mut self, pre_key_id: u32, _ctx: libsignal_protocol::Context, ) -> Result<(), SignalProtocolError>127     async fn remove_pre_key(
128         &mut self,
129         pre_key_id: u32,
130         _ctx: libsignal_protocol::Context,
131     ) -> Result<(), SignalProtocolError> {
132         self.do_remove_pre_key(pre_key_id)
133             .await
134             .map_err(|s| js_error_to_rust("removePreKey", s))
135     }
136 }
137 
138 pub struct NodeSignedPreKeyStore {
139     js_channel: Channel,
140     store_object: Arc<Root<JsObject>>,
141 }
142 
143 impl NodeSignedPreKeyStore {
new(cx: &mut FunctionContext, store: Handle<JsObject>) -> Self144     pub(crate) fn new(cx: &mut FunctionContext, store: Handle<JsObject>) -> Self {
145         Self {
146             js_channel: cx.channel(),
147             store_object: Arc::new(store.root(cx)),
148         }
149     }
150 
do_get_signed_pre_key(&self, id: u32) -> Result<SignedPreKeyRecord, String>151     async fn do_get_signed_pre_key(&self, id: u32) -> Result<SignedPreKeyRecord, String> {
152         let store_object_shared = self.store_object.clone();
153         JsFuture::get_promise(&self.js_channel, move |cx| {
154             let store_object = store_object_shared.to_inner(cx);
155             let id = id.convert_into(cx)?;
156             let result = call_method(cx, store_object, "_getSignedPreKey", vec![id.upcast()])?;
157             let result = result.downcast_or_throw(cx)?;
158             store_object_shared.finalize(cx);
159             Ok(result)
160         })
161         .then(|cx, result| match result {
162             Ok(value) => match value.downcast::<DefaultJsBox<SignedPreKeyRecord>, _>(cx) {
163                 Ok(obj) => Ok((***obj).clone()),
164                 Err(_) => Err("result must be an object".to_owned()),
165             },
166             Err(error) => Err(error
167                 .to_string(cx)
168                 .expect("can convert to string")
169                 .value(cx)),
170         })
171         .await
172     }
173 
do_save_signed_pre_key( &self, id: u32, record: SignedPreKeyRecord, ) -> Result<(), String>174     async fn do_save_signed_pre_key(
175         &self,
176         id: u32,
177         record: SignedPreKeyRecord,
178     ) -> Result<(), String> {
179         let store_object_shared = self.store_object.clone();
180         JsFuture::get_promise(&self.js_channel, move |cx| {
181             let store_object = store_object_shared.to_inner(cx);
182             let id: Handle<JsNumber> = id.convert_into(cx)?;
183             let record: Handle<JsValue> = record.convert_into(cx)?;
184             let result = call_method(
185                 cx,
186                 store_object,
187                 "_saveSignedPreKey",
188                 vec![id.upcast(), record],
189             )?
190             .downcast_or_throw(cx)?;
191             store_object_shared.finalize(cx);
192             Ok(result)
193         })
194         .then(|cx, result| match result {
195             Ok(value) => match value.downcast::<JsUndefined, _>(cx) {
196                 Ok(_) => Ok(()),
197                 Err(_) => Err("unexpected result from _saveSignedPreKey".into()),
198             },
199             Err(error) => Err(error
200                 .to_string(cx)
201                 .expect("can convert to string")
202                 .value(cx)),
203         })
204         .await
205     }
206 }
207 
208 impl Finalize for NodeSignedPreKeyStore {
finalize<'b, C: neon::prelude::Context<'b>>(self, cx: &mut C)209     fn finalize<'b, C: neon::prelude::Context<'b>>(self, cx: &mut C) {
210         self.store_object.finalize(cx)
211     }
212 }
213 
214 #[async_trait(?Send)]
215 impl SignedPreKeyStore for NodeSignedPreKeyStore {
get_signed_pre_key( &self, signed_pre_key_id: u32, _ctx: libsignal_protocol::Context, ) -> Result<SignedPreKeyRecord, SignalProtocolError>216     async fn get_signed_pre_key(
217         &self,
218         signed_pre_key_id: u32,
219         _ctx: libsignal_protocol::Context,
220     ) -> Result<SignedPreKeyRecord, SignalProtocolError> {
221         self.do_get_signed_pre_key(signed_pre_key_id)
222             .await
223             .map_err(|s| js_error_to_rust("getSignedPreKey", s))
224     }
225 
save_signed_pre_key( &mut self, signed_pre_key_id: u32, record: &SignedPreKeyRecord, _ctx: libsignal_protocol::Context, ) -> Result<(), SignalProtocolError>226     async fn save_signed_pre_key(
227         &mut self,
228         signed_pre_key_id: u32,
229         record: &SignedPreKeyRecord,
230         _ctx: libsignal_protocol::Context,
231     ) -> Result<(), SignalProtocolError> {
232         self.do_save_signed_pre_key(signed_pre_key_id, record.clone())
233             .await
234             .map_err(|s| js_error_to_rust("saveSignedPreKey", s))
235     }
236 }
237 
238 pub struct NodeSessionStore {
239     js_channel: Channel,
240     store_object: Arc<Root<JsObject>>,
241 }
242 
243 impl NodeSessionStore {
new(cx: &mut FunctionContext, store: Handle<JsObject>) -> Self244     pub(crate) fn new(cx: &mut FunctionContext, store: Handle<JsObject>) -> Self {
245         Self {
246             js_channel: cx.channel(),
247             store_object: Arc::new(store.root(cx)),
248         }
249     }
250 
do_get_session(&self, name: ProtocolAddress) -> Result<Option<SessionRecord>, String>251     async fn do_get_session(&self, name: ProtocolAddress) -> Result<Option<SessionRecord>, String> {
252         let store_object_shared = self.store_object.clone();
253         JsFuture::get_promise(&self.js_channel, move |cx| {
254             let store_object = store_object_shared.to_inner(cx);
255             let name: Handle<JsValue> = name.convert_into(cx)?;
256             let result = call_method(cx, store_object, "_getSession", vec![name])?;
257             let result = result.downcast_or_throw(cx)?;
258             store_object_shared.finalize(cx);
259             Ok(result)
260         })
261         .then(|cx, result| match result {
262             Ok(value) => match value.downcast::<DefaultJsBox<RefCell<SessionRecord>>, _>(cx) {
263                 Ok(obj) => Ok(Some((***obj).borrow().clone())),
264                 Err(_) => {
265                     if value.is_a::<JsNull, _>(cx) || value.is_a::<JsUndefined, _>(cx) {
266                         Ok(None)
267                     } else {
268                         Err("_getSession returned unexpected type".into())
269                     }
270                 }
271             },
272             Err(error) => Err(error
273                 .to_string(cx)
274                 .expect("can convert to string")
275                 .value(cx)),
276         })
277         .await
278     }
279 
do_save_session( &self, name: ProtocolAddress, record: SessionRecord, ) -> Result<(), String>280     async fn do_save_session(
281         &self,
282         name: ProtocolAddress,
283         record: SessionRecord,
284     ) -> Result<(), String> {
285         let store_object_shared = self.store_object.clone();
286         JsFuture::get_promise(&self.js_channel, move |cx| {
287             let store_object = store_object_shared.to_inner(cx);
288             let name = name.convert_into(cx)?;
289             let record = record.convert_into(cx)?;
290             let result = call_method(
291                 cx,
292                 store_object,
293                 "_saveSession",
294                 vec![name, record.upcast()],
295             )?
296             .downcast_or_throw(cx)?;
297             store_object_shared.finalize(cx);
298             Ok(result)
299         })
300         .then(|cx, result| match result {
301             Ok(value) => match value.downcast::<JsUndefined, _>(cx) {
302                 Ok(_) => Ok(()),
303                 Err(_) => Err("unexpected result from _saveSession".into()),
304             },
305             Err(error) => Err(error
306                 .to_string(cx)
307                 .expect("can convert to string")
308                 .value(cx)),
309         })
310         .await
311     }
312 }
313 
314 impl Finalize for NodeSessionStore {
finalize<'b, C: neon::prelude::Context<'b>>(self, cx: &mut C)315     fn finalize<'b, C: neon::prelude::Context<'b>>(self, cx: &mut C) {
316         self.store_object.finalize(cx)
317     }
318 }
319 
320 #[async_trait(?Send)]
321 impl SessionStore for NodeSessionStore {
load_session( &self, name: &ProtocolAddress, _ctx: libsignal_protocol::Context, ) -> Result<Option<SessionRecord>, SignalProtocolError>322     async fn load_session(
323         &self,
324         name: &ProtocolAddress,
325         _ctx: libsignal_protocol::Context,
326     ) -> Result<Option<SessionRecord>, SignalProtocolError> {
327         self.do_get_session(name.clone())
328             .await
329             .map_err(|s| js_error_to_rust("getSession", s))
330     }
331 
store_session( &mut self, name: &ProtocolAddress, record: &SessionRecord, _ctx: libsignal_protocol::Context, ) -> Result<(), SignalProtocolError>332     async fn store_session(
333         &mut self,
334         name: &ProtocolAddress,
335         record: &SessionRecord,
336         _ctx: libsignal_protocol::Context,
337     ) -> Result<(), SignalProtocolError> {
338         self.do_save_session(name.clone(), record.clone())
339             .await
340             .map_err(|s| js_error_to_rust("saveSession", s))
341     }
342 }
343 
344 pub struct NodeIdentityKeyStore {
345     js_channel: Channel,
346     store_object: Arc<Root<JsObject>>,
347 }
348 
349 impl NodeIdentityKeyStore {
new(cx: &mut FunctionContext, store: Handle<JsObject>) -> Self350     pub(crate) fn new(cx: &mut FunctionContext, store: Handle<JsObject>) -> Self {
351         Self {
352             js_channel: cx.channel(),
353             store_object: Arc::new(store.root(cx)),
354         }
355     }
356 
do_get_identity_key(&self) -> Result<PrivateKey, String>357     async fn do_get_identity_key(&self) -> Result<PrivateKey, String> {
358         let store_object_shared = self.store_object.clone();
359         JsFuture::get_promise(&self.js_channel, move |cx| {
360             let store_object = store_object_shared.to_inner(cx);
361             let result = call_method(cx, store_object, "_getIdentityKey", vec![])?;
362             let result = result.downcast_or_throw(cx)?;
363             store_object_shared.finalize(cx);
364             Ok(result)
365         })
366         .then(|cx, result| match result {
367             Ok(value) => match value.downcast::<DefaultJsBox<PrivateKey>, _>(cx) {
368                 Ok(obj) => Ok(***obj),
369                 Err(_) => Err("result must be an object".to_owned()),
370             },
371             Err(error) => Err(error
372                 .to_string(cx)
373                 .expect("can convert to string")
374                 .value(cx)),
375         })
376         .await
377     }
378 
do_get_local_registration_id(&self) -> Result<u32, String>379     async fn do_get_local_registration_id(&self) -> Result<u32, String> {
380         let store_object_shared = self.store_object.clone();
381         JsFuture::get_promise(&self.js_channel, move |cx| {
382             let store_object = store_object_shared.to_inner(cx);
383             let result = call_method(cx, store_object, "_getLocalRegistrationId", vec![])?
384                 .downcast_or_throw(cx)?;
385             store_object_shared.finalize(cx);
386             Ok(result)
387         })
388         .then(|cx, result| match result {
389             Ok(value) => match value.downcast::<JsNumber, _>(cx) {
390                 Ok(b) => Ok(b.value(cx) as u32),
391                 Err(_) => Err("unexpected result from _getLocalRegistrationId".into()),
392             },
393             Err(error) => Err(error
394                 .to_string(cx)
395                 .expect("can convert to string")
396                 .value(cx)),
397         })
398         .await
399     }
400 
do_get_identity(&self, name: ProtocolAddress) -> Result<Option<PublicKey>, String>401     async fn do_get_identity(&self, name: ProtocolAddress) -> Result<Option<PublicKey>, String> {
402         let store_object_shared = self.store_object.clone();
403         JsFuture::get_promise(&self.js_channel, move |cx| {
404             let store_object = store_object_shared.to_inner(cx);
405             let name: Handle<JsValue> = name.convert_into(cx)?;
406             let result = call_method(cx, store_object, "_getIdentity", vec![name])?;
407             let result = result.downcast_or_throw(cx)?;
408             store_object_shared.finalize(cx);
409             Ok(result)
410         })
411         .then(|cx, result| match result {
412             Ok(value) => match value.downcast::<DefaultJsBox<PublicKey>, _>(cx) {
413                 Ok(obj) => Ok(Some(***obj)),
414                 Err(_) => {
415                     if value.is_a::<JsNull, _>(cx) {
416                         Ok(None)
417                     } else {
418                         Err("result must be an object".to_owned())
419                     }
420                 }
421             },
422             Err(error) => Err(error
423                 .to_string(cx)
424                 .expect("can convert to string")
425                 .value(cx)),
426         })
427         .await
428     }
429 
do_save_identity( &self, name: ProtocolAddress, key: PublicKey, ) -> Result<bool, String>430     async fn do_save_identity(
431         &self,
432         name: ProtocolAddress,
433         key: PublicKey,
434     ) -> Result<bool, String> {
435         let store_object_shared = self.store_object.clone();
436         JsFuture::get_promise(&self.js_channel, move |cx| {
437             let store_object = store_object_shared.to_inner(cx);
438             let name: Handle<JsValue> = name.convert_into(cx)?;
439             let key: Handle<JsValue> = key.convert_into(cx)?;
440             let result = call_method(cx, store_object, "_saveIdentity", vec![name, key])?
441                 .downcast_or_throw(cx)?;
442             store_object_shared.finalize(cx);
443             Ok(result)
444         })
445         .then(|cx, result| match result {
446             Ok(value) => match value.downcast::<JsBoolean, _>(cx) {
447                 Ok(b) => Ok(b.value(cx)),
448                 Err(_) => Err("unexpected result from _saveIdentity".into()),
449             },
450             Err(error) => Err(error
451                 .to_string(cx)
452                 .expect("can convert to string")
453                 .value(cx)),
454         })
455         .await
456     }
457 
do_is_trusted( &self, name: ProtocolAddress, key: PublicKey, direction: Direction, ) -> Result<bool, String>458     async fn do_is_trusted(
459         &self,
460         name: ProtocolAddress,
461         key: PublicKey,
462         direction: Direction,
463     ) -> Result<bool, String> {
464         let store_object_shared = self.store_object.clone();
465         JsFuture::get_promise(&self.js_channel, move |cx| {
466             let store_object = store_object_shared.to_inner(cx);
467             let name: Handle<JsValue> = name.convert_into(cx)?;
468             let key: Handle<JsValue> = key.convert_into(cx)?;
469             let sending = direction == Direction::Sending;
470 
471             let sending = sending.convert_into(cx)?;
472             let result = call_method(
473                 cx,
474                 store_object,
475                 "_isTrustedIdentity",
476                 vec![name, key, sending.upcast()],
477             )?
478             .downcast_or_throw(cx)?;
479             store_object_shared.finalize(cx);
480             Ok(result)
481         })
482         .then(|cx, result| match result {
483             Ok(value) => match value.downcast::<JsBoolean, _>(cx) {
484                 Ok(b) => Ok(b.value(cx)),
485                 Err(_) => Err("unexpected result from _isTrustedIdentity".into()),
486             },
487             Err(error) => Err(error
488                 .to_string(cx)
489                 .expect("can convert to string")
490                 .value(cx)),
491         })
492         .await
493     }
494 }
495 
496 impl Finalize for NodeIdentityKeyStore {
finalize<'b, C: neon::prelude::Context<'b>>(self, cx: &mut C)497     fn finalize<'b, C: neon::prelude::Context<'b>>(self, cx: &mut C) {
498         self.store_object.finalize(cx)
499     }
500 }
501 
502 #[async_trait(?Send)]
503 impl IdentityKeyStore for NodeIdentityKeyStore {
get_identity_key_pair( &self, _ctx: libsignal_protocol::Context, ) -> Result<IdentityKeyPair, SignalProtocolError>504     async fn get_identity_key_pair(
505         &self,
506         _ctx: libsignal_protocol::Context,
507     ) -> Result<IdentityKeyPair, SignalProtocolError> {
508         let pk = self
509             .do_get_identity_key()
510             .await
511             .map_err(|s| js_error_to_rust("getIdentityPrivateKey", s))?;
512 
513         IdentityKeyPair::try_from(pk)
514     }
515 
get_local_registration_id( &self, _ctx: libsignal_protocol::Context, ) -> Result<u32, SignalProtocolError>516     async fn get_local_registration_id(
517         &self,
518         _ctx: libsignal_protocol::Context,
519     ) -> Result<u32, SignalProtocolError> {
520         self.do_get_local_registration_id()
521             .await
522             .map_err(|s| js_error_to_rust("getLocalRegistrationId", s))
523     }
524 
get_identity( &self, address: &ProtocolAddress, _ctx: libsignal_protocol::Context, ) -> Result<Option<IdentityKey>, SignalProtocolError>525     async fn get_identity(
526         &self,
527         address: &ProtocolAddress,
528         _ctx: libsignal_protocol::Context,
529     ) -> Result<Option<IdentityKey>, SignalProtocolError> {
530         Ok(self
531             .do_get_identity(address.clone())
532             .await
533             .map_err(|s| js_error_to_rust("getIdentity", s))?
534             .map(IdentityKey::new))
535     }
536 
save_identity( &mut self, address: &ProtocolAddress, identity: &IdentityKey, _ctx: libsignal_protocol::Context, ) -> Result<bool, SignalProtocolError>537     async fn save_identity(
538         &mut self,
539         address: &ProtocolAddress,
540         identity: &IdentityKey,
541         _ctx: libsignal_protocol::Context,
542     ) -> Result<bool, SignalProtocolError> {
543         self.do_save_identity(address.clone(), *identity.public_key())
544             .await
545             .map_err(|s| js_error_to_rust("saveIdentity", s))
546     }
547 
is_trusted_identity( &self, address: &ProtocolAddress, identity: &IdentityKey, direction: libsignal_protocol::Direction, _ctx: libsignal_protocol::Context, ) -> Result<bool, SignalProtocolError>548     async fn is_trusted_identity(
549         &self,
550         address: &ProtocolAddress,
551         identity: &IdentityKey,
552         direction: libsignal_protocol::Direction,
553         _ctx: libsignal_protocol::Context,
554     ) -> Result<bool, SignalProtocolError> {
555         self.do_is_trusted(address.clone(), *identity.public_key(), direction)
556             .await
557             .map_err(|s| js_error_to_rust("isTrustedIdentity", s))
558     }
559 }
560 
561 pub struct NodeSenderKeyStore {
562     js_channel: Channel,
563     store_object: Arc<Root<JsObject>>,
564 }
565 
566 impl NodeSenderKeyStore {
new(cx: &mut FunctionContext, store: Handle<JsObject>) -> Self567     pub(crate) fn new(cx: &mut FunctionContext, store: Handle<JsObject>) -> Self {
568         Self {
569             js_channel: cx.channel(),
570             store_object: Arc::new(store.root(cx)),
571         }
572     }
573 
do_get_sender_key( &self, sender: ProtocolAddress, distribution_id: Uuid, ) -> Result<Option<SenderKeyRecord>, String>574     async fn do_get_sender_key(
575         &self,
576         sender: ProtocolAddress,
577         distribution_id: Uuid,
578     ) -> Result<Option<SenderKeyRecord>, String> {
579         let store_object_shared = self.store_object.clone();
580         JsFuture::get_promise(&self.js_channel, move |cx| {
581             let store_object = store_object_shared.to_inner(cx);
582             let sender: Handle<JsValue> = sender.convert_into(cx)?;
583             let distribution_id: Handle<JsValue> = distribution_id.convert_into(cx)?.upcast();
584             let result = call_method(
585                 cx,
586                 store_object,
587                 "_getSenderKey",
588                 vec![sender, distribution_id],
589             )?;
590             let result = result.downcast_or_throw(cx)?;
591             store_object_shared.finalize(cx);
592             Ok(result)
593         })
594         .then(|cx, result| match result {
595             Ok(value) => match value.downcast::<DefaultJsBox<SenderKeyRecord>, _>(cx) {
596                 Ok(obj) => Ok(Some((***obj).clone())),
597                 Err(_) => {
598                     if value.is_a::<JsNull, _>(cx) {
599                         Ok(None)
600                     } else {
601                         Err("result must be an object".to_owned())
602                     }
603                 }
604             },
605             Err(error) => Err(error
606                 .to_string(cx)
607                 .expect("can convert to string")
608                 .value(cx)),
609         })
610         .await
611     }
612 
do_save_sender_key( &self, sender: ProtocolAddress, distribution_id: Uuid, record: SenderKeyRecord, ) -> Result<(), String>613     async fn do_save_sender_key(
614         &self,
615         sender: ProtocolAddress,
616         distribution_id: Uuid,
617         record: SenderKeyRecord,
618     ) -> Result<(), String> {
619         let store_object_shared = self.store_object.clone();
620         JsFuture::get_promise(&self.js_channel, move |cx| {
621             let store_object = store_object_shared.to_inner(cx);
622             let sender: Handle<JsValue> = sender.convert_into(cx)?;
623             let distribution_id: Handle<JsValue> = distribution_id.convert_into(cx)?.upcast();
624             let record: Handle<JsValue> = record.convert_into(cx)?;
625             let result = call_method(
626                 cx,
627                 store_object,
628                 "_saveSenderKey",
629                 vec![sender, distribution_id, record],
630             )?
631             .downcast_or_throw(cx)?;
632             store_object_shared.finalize(cx);
633             Ok(result)
634         })
635         .then(|cx, result| match result {
636             Ok(value) => match value.downcast::<JsUndefined, _>(cx) {
637                 Ok(_) => Ok(()),
638                 Err(_) => Err("unexpected result from _saveSenderKey".into()),
639             },
640             Err(error) => Err(error
641                 .to_string(cx)
642                 .expect("can convert to string")
643                 .value(cx)),
644         })
645         .await
646     }
647 }
648 
649 impl Finalize for NodeSenderKeyStore {
finalize<'a, C: Context<'a>>(self, cx: &mut C)650     fn finalize<'a, C: Context<'a>>(self, cx: &mut C) {
651         self.store_object.finalize(cx)
652     }
653 }
654 
655 #[async_trait(?Send)]
656 impl SenderKeyStore for NodeSenderKeyStore {
load_sender_key( &mut self, sender: &ProtocolAddress, distribution_id: Uuid, _ctx: libsignal_protocol::Context, ) -> Result<Option<SenderKeyRecord>, SignalProtocolError>657     async fn load_sender_key(
658         &mut self,
659         sender: &ProtocolAddress,
660         distribution_id: Uuid,
661         _ctx: libsignal_protocol::Context,
662     ) -> Result<Option<SenderKeyRecord>, SignalProtocolError> {
663         self.do_get_sender_key(sender.clone(), distribution_id)
664             .await
665             .map_err(|s| js_error_to_rust("getSenderKey", s))
666     }
667 
store_sender_key( &mut self, sender: &ProtocolAddress, distribution_id: Uuid, record: &SenderKeyRecord, _ctx: libsignal_protocol::Context, ) -> Result<(), SignalProtocolError>668     async fn store_sender_key(
669         &mut self,
670         sender: &ProtocolAddress,
671         distribution_id: Uuid,
672         record: &SenderKeyRecord,
673         _ctx: libsignal_protocol::Context,
674     ) -> Result<(), SignalProtocolError> {
675         self.do_save_sender_key(sender.clone(), distribution_id, record.clone())
676             .await
677             .map_err(|s| js_error_to_rust("saveSenderKey", s))
678     }
679 }
680