1use super::{
2 BufferedDecryptedMessage, Ciphersuite, Ciphersuites, ClientId, CommitBundle, ConversationConfiguration,
3 ConversationInitBundle, CoreCrypto, CoreCryptoError, CoreCryptoResult, CustomConfiguration, DecryptedMessage,
4 MemberAddedMessages, MlsCredentialType, ProposalBundle, WelcomeBundle,
5};
6use async_lock::{Mutex, OnceCell};
7use core_crypto::context::CentralContext;
8use core_crypto::{
9 prelude::{
10 ClientIdentifier, ConversationId, KeyPackageIn, KeyPackageRef, MlsConversationConfiguration,
11 VerifiableGroupInfo,
12 },
13 CryptoError, CryptoResult, MlsError,
14};
15use std::future::Future;
16use std::sync::atomic::AtomicU16;
17use std::{ops::Deref, sync::Arc};
18use tls_codec::{Deserialize, Serialize};
19
20pub mod e2ei;
21pub mod proteus;
22
23#[derive(uniffi::Object)]
24pub struct CoreCryptoContext {
25 pub(super) context: Arc<CentralContext>,
26 proteus_last_error_code: AtomicU16,
27}
28
29impl Deref for CoreCryptoContext {
30 type Target = CentralContext;
31
32 fn deref(&self) -> &Self::Target {
33 self.context.as_ref()
34 }
35}
36
37#[uniffi::export(with_foreign)]
38#[async_trait::async_trait]
39pub trait CoreCryptoCommand: Send + Sync {
40 async fn execute(&self, context: Arc<CoreCryptoContext>) -> CoreCryptoResult<()>;
42}
43
44#[async_trait::async_trait]
45impl<F, Fut> CoreCryptoCommand for F
46where
47 F: Fn(Arc<CoreCryptoContext>) -> Fut + Send + Sync,
48 Fut: Future<Output = CoreCryptoResult<()>> + Send,
49{
50 async fn execute(&self, context: Arc<CoreCryptoContext>) -> CoreCryptoResult<()> {
51 self(context).await
52 }
53}
54
55pub struct TransactionHelper<T, F> {
86 func: Mutex<Option<F>>,
87 return_value: OnceCell<T>,
88}
89
90impl<T, F, Fut> TransactionHelper<T, F>
91where
92 F: FnOnce(Arc<CoreCryptoContext>) -> Fut + Send + Sync,
93 Fut: Future<Output = CoreCryptoResult<T>> + Send,
94 T: Send + Sync,
95{
96 pub fn new(func: F) -> Arc<Self> {
97 Arc::new(Self {
98 func: Mutex::new(Some(func)),
99 return_value: OnceCell::new(),
100 })
101 }
102
103 pub fn into_return_value(self: Arc<Self>) -> T {
119 Arc::into_inner(self)
120 .expect("there should exist exactly one strong ref right now")
121 .return_value
122 .into_inner()
123 .expect("return value should be initialized")
124 }
125
126 pub fn try_into_return_value(self: Arc<Self>) -> Option<T> {
132 Arc::into_inner(self)?.return_value.into_inner()
133 }
134}
135
136#[async_trait::async_trait]
137impl<T, F, Fut> CoreCryptoCommand for TransactionHelper<T, F>
138where
139 F: FnOnce(Arc<CoreCryptoContext>) -> Fut + Send + Sync,
140 Fut: Future<Output = CoreCryptoResult<T>> + Send,
141 T: Send + Sync,
142{
143 async fn execute(&self, context: Arc<CoreCryptoContext>) -> CoreCryptoResult<()> {
144 let func = self
145 .func
146 .lock()
147 .await
148 .take()
149 .expect("inner function must only be called once");
150 let return_value = func(context).await?;
151 let set_result = self.return_value.set(return_value).await;
152 if set_result.is_err() {
153 panic!("return value was previously set");
157 }
158 Ok(())
159 }
160}
161
162#[uniffi::export]
163impl CoreCrypto {
164 pub async fn transaction(&self, command: Arc<dyn CoreCryptoCommand>) -> CoreCryptoResult<()> {
177 let context = Arc::new(CoreCryptoContext {
178 context: Arc::new(self.central.new_transaction().await?),
179 proteus_last_error_code: AtomicU16::new(0),
180 });
181
182 let result = command.execute(context.clone()).await;
183 match result {
184 Ok(result) => {
185 context.context.finish().await?;
186 Ok(result)
187 }
188 Err(err) => {
189 context.context.abort().await?;
190 Err(err)
191 }
192 }
193 }
194}
195impl CoreCrypto {
196 pub(crate) async fn deprecated_transaction<F, Fut, R>(&self, callback: F) -> CoreCryptoResult<R>
198 where
199 F: FnOnce(CentralContext) -> Fut,
200 Fut: Future<Output = CryptoResult<R>>,
201 {
202 let context = self.central.new_transaction().await?;
203 let result = callback(context.clone()).await;
204 match result {
205 Ok(result) => {
206 context.finish().await?;
207 Ok(result)
208 }
209 Err(err) => {
210 context.abort().await?;
211 Err(err.into())
212 }
213 }
214 }
215}
216
217#[uniffi::export]
218impl CoreCryptoContext {
219 pub async fn set_data(&self, data: Vec<u8>) -> CoreCryptoResult<()> {
221 self.context.set_data(data).await.map_err(Into::into)
222 }
223
224 pub async fn get_data(&self) -> CoreCryptoResult<Option<Vec<u8>>> {
226 self.context.get_data().await.map_err(Into::into)
227 }
228
229 pub async fn mls_init(
231 &self,
232 client_id: ClientId,
233 ciphersuites: Ciphersuites,
234 nb_key_package: Option<u32>,
235 ) -> CoreCryptoResult<()> {
236 let nb_key_package = nb_key_package
237 .map(usize::try_from)
238 .transpose()
239 .map_err(CryptoError::from)?;
240 self.context
241 .mls_init(
242 ClientIdentifier::Basic(client_id.0),
243 (&ciphersuites).into(),
244 nb_key_package,
245 )
246 .await?;
247 Ok(())
248 }
249
250 pub async fn mls_generate_keypairs(&self, ciphersuites: Ciphersuites) -> CoreCryptoResult<Vec<ClientId>> {
252 Ok(self
253 .context
254 .mls_generate_keypairs((&ciphersuites).into())
255 .await
256 .map(|cids| cids.into_iter().map(ClientId).collect())?)
257 }
258
259 pub async fn mls_init_with_client_id(
261 &self,
262 client_id: ClientId,
263 tmp_client_ids: Vec<ClientId>,
264 ciphersuites: Ciphersuites,
265 ) -> CoreCryptoResult<()> {
266 Ok(self
267 .context
268 .mls_init_with_client_id(
269 client_id.0,
270 tmp_client_ids.into_iter().map(|cid| cid.0).collect(),
271 (&ciphersuites).into(),
272 )
273 .await?)
274 }
275
276 pub async fn client_public_key(
278 &self,
279 ciphersuite: Ciphersuite,
280 credential_type: MlsCredentialType,
281 ) -> CoreCryptoResult<Vec<u8>> {
282 Ok(self
283 .context
284 .client_public_key(ciphersuite.into(), credential_type.into())
285 .await?)
286 }
287
288 pub async fn conversation_epoch(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<u64> {
290 Ok(self.context.conversation_epoch(&conversation_id).await?)
291 }
292
293 pub async fn conversation_ciphersuite(&self, conversation_id: &ConversationId) -> CoreCryptoResult<Ciphersuite> {
295 let cs = self.context.conversation_ciphersuite(conversation_id).await?;
296 Ok(Ciphersuite::from(core_crypto::prelude::CiphersuiteName::from(cs)))
297 }
298
299 pub async fn conversation_exists(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<bool> {
301 Ok(self.context.conversation_exists(&conversation_id).await?)
302 }
303
304 pub async fn get_client_ids(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<Vec<ClientId>> {
306 Ok(self
307 .context
308 .get_client_ids(&conversation_id)
309 .await
310 .map(|cids| cids.into_iter().map(ClientId).collect())?)
311 }
312
313 pub async fn export_secret_key(&self, conversation_id: Vec<u8>, key_length: u32) -> CoreCryptoResult<Vec<u8>> {
315 Ok(self
316 .context
317 .export_secret_key(&conversation_id, key_length as usize)
318 .await?)
319 }
320
321 pub async fn get_external_sender(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<Vec<u8>> {
323 Ok(self.context.get_external_sender(&conversation_id).await?)
324 }
325
326 pub async fn client_keypackages(
328 &self,
329 ciphersuite: Ciphersuite,
330 credential_type: MlsCredentialType,
331 amount_requested: u32,
332 ) -> CoreCryptoResult<Vec<Vec<u8>>> {
333 let kps = self
334 .context
335 .get_or_create_client_keypackages(ciphersuite.into(), credential_type.into(), amount_requested as usize)
336 .await?;
337
338 kps.into_iter()
339 .map(|kp| {
340 Ok(kp
341 .tls_serialize_detached()
342 .map_err(MlsError::from)
343 .map_err(CryptoError::from)?)
344 })
345 .collect::<CoreCryptoResult<Vec<Vec<u8>>>>()
346 }
347
348 pub async fn client_valid_keypackages_count(
350 &self,
351 ciphersuite: Ciphersuite,
352 credential_type: MlsCredentialType,
353 ) -> CoreCryptoResult<u64> {
354 let count = self
355 .context
356 .client_valid_key_packages_count(ciphersuite.into(), credential_type.into())
357 .await?;
358
359 Ok(count.try_into().unwrap_or(0))
360 }
361
362 pub async fn delete_keypackages(&self, refs: Vec<Vec<u8>>) -> CoreCryptoResult<()> {
364 let refs = refs
365 .into_iter()
366 .map(|r| KeyPackageRef::from_slice(&r))
367 .collect::<Vec<_>>();
368
369 self.context.delete_keypackages(&refs[..]).await?;
370 Ok(())
371 }
372
373 pub async fn create_conversation(
375 &self,
376 conversation_id: Vec<u8>,
377 creator_credential_type: MlsCredentialType,
378 config: ConversationConfiguration,
379 ) -> CoreCryptoResult<()> {
380 let mut lower_cfg = MlsConversationConfiguration {
381 custom: config.custom.into(),
382 ciphersuite: config.ciphersuite.into(),
383 ..Default::default()
384 };
385
386 self.context
387 .set_raw_external_senders(&mut lower_cfg, config.external_senders)
388 .await?;
389
390 self.context
391 .new_conversation(&conversation_id, creator_credential_type.into(), lower_cfg)
392 .await?;
393 Ok(())
394 }
395
396 pub async fn process_welcome_message(
398 &self,
399 welcome_message: Vec<u8>,
400 custom_configuration: CustomConfiguration,
401 ) -> CoreCryptoResult<WelcomeBundle> {
402 let result = self
403 .context
404 .process_raw_welcome_message(welcome_message, custom_configuration.into())
405 .await?
406 .into();
407 Ok(result)
408 }
409
410 pub async fn add_clients_to_conversation(
412 &self,
413 conversation_id: Vec<u8>,
414 key_packages: Vec<Vec<u8>>,
415 ) -> CoreCryptoResult<MemberAddedMessages> {
416 let key_packages = key_packages
417 .into_iter()
418 .map(|kp| {
419 KeyPackageIn::tls_deserialize(&mut kp.as_slice())
420 .map_err(|e| CoreCryptoError::from(CryptoError::MlsError(e.into())))
421 })
422 .collect::<CoreCryptoResult<Vec<_>>>()?;
423
424 let result = self
425 .context
426 .add_members_to_conversation(&conversation_id, key_packages)
427 .await?
428 .try_into()?;
429 Ok(result)
430 }
431
432 pub async fn remove_clients_from_conversation(
434 &self,
435 conversation_id: Vec<u8>,
436 clients: Vec<ClientId>,
437 ) -> CoreCryptoResult<CommitBundle> {
438 let clients: Vec<core_crypto::prelude::ClientId> = clients.into_iter().map(|c| c.0).collect();
439 let result = self
440 .context
441 .remove_members_from_conversation(&conversation_id, &clients)
442 .await?
443 .try_into()?;
444
445 Ok(result)
446 }
447
448 pub async fn mark_conversation_as_child_of(&self, child_id: Vec<u8>, parent_id: Vec<u8>) -> CoreCryptoResult<()> {
450 self.context
451 .mark_conversation_as_child_of(&child_id, &parent_id)
452 .await?;
453 Ok(())
454 }
455
456 pub async fn update_keying_material(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<CommitBundle> {
458 self.context.update_keying_material(&conversation_id).await?.try_into()
459 }
460
461 pub async fn commit_pending_proposals(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<Option<CommitBundle>> {
463 self.context
464 .commit_pending_proposals(&conversation_id)
465 .await
466 .transpose()
467 .map(|r| r?.try_into())
468 .transpose()
469 }
470
471 pub async fn wipe_conversation(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<()> {
473 self.context.wipe_conversation(&conversation_id).await?;
474 Ok(())
475 }
476
477 pub async fn decrypt_message(
479 &self,
480 conversation_id: Vec<u8>,
481 payload: Vec<u8>,
482 ) -> CoreCryptoResult<DecryptedMessage> {
483 let raw_decrypted_message = self.context.decrypt_message(&conversation_id, payload).await?;
484
485 let decrypted_message: DecryptedMessage = raw_decrypted_message.try_into()?;
486
487 Ok(decrypted_message)
488 }
489
490 pub async fn encrypt_message(&self, conversation_id: Vec<u8>, message: Vec<u8>) -> CoreCryptoResult<Vec<u8>> {
492 Ok(self.context.encrypt_message(&conversation_id, message).await?)
493 }
494
495 pub async fn new_add_proposal(
497 &self,
498 conversation_id: Vec<u8>,
499 keypackage: Vec<u8>,
500 ) -> CoreCryptoResult<ProposalBundle> {
501 let kp = KeyPackageIn::tls_deserialize(&mut keypackage.as_slice())
502 .map_err(MlsError::from)
503 .map_err(CryptoError::from)?;
504 self.context
505 .new_add_proposal(&conversation_id, kp.into())
506 .await?
507 .try_into()
508 }
509
510 pub async fn new_update_proposal(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<ProposalBundle> {
512 self.context.new_update_proposal(&conversation_id).await?.try_into()
513 }
514
515 pub async fn new_remove_proposal(
517 &self,
518 conversation_id: Vec<u8>,
519 client_id: ClientId,
520 ) -> CoreCryptoResult<ProposalBundle> {
521 self.context
522 .new_remove_proposal(&conversation_id, client_id.0)
523 .await?
524 .try_into()
525 }
526
527 pub async fn new_external_add_proposal(
529 &self,
530 conversation_id: Vec<u8>,
531 epoch: u64,
532 ciphersuite: Ciphersuite,
533 credential_type: MlsCredentialType,
534 ) -> CoreCryptoResult<Vec<u8>> {
535 Ok(self
536 .context
537 .new_external_add_proposal(
538 conversation_id,
539 epoch.into(),
540 ciphersuite.into(),
541 credential_type.into(),
542 )
543 .await?
544 .to_bytes()
545 .map_err(MlsError::from)
546 .map_err(CryptoError::from)?)
547 }
548
549 pub async fn join_by_external_commit(
551 &self,
552 group_info: Vec<u8>,
553 custom_configuration: CustomConfiguration,
554 credential_type: MlsCredentialType,
555 ) -> CoreCryptoResult<ConversationInitBundle> {
556 let group_info = VerifiableGroupInfo::tls_deserialize(&mut group_info.as_slice())
557 .map_err(MlsError::from)
558 .map_err(CryptoError::from)?;
559 self.context
560 .join_by_external_commit(group_info, custom_configuration.into(), credential_type.into())
561 .await?
562 .try_into()
563 }
564
565 pub async fn merge_pending_group_from_external_commit(
567 &self,
568 conversation_id: Vec<u8>,
569 ) -> CoreCryptoResult<Option<Vec<BufferedDecryptedMessage>>> {
570 if let Some(decrypted_messages) = self
571 .context
572 .merge_pending_group_from_external_commit(&conversation_id)
573 .await?
574 {
575 let result = decrypted_messages
576 .into_iter()
577 .map(TryInto::try_into)
578 .collect::<CoreCryptoResult<Vec<_>>>()?;
579 return Ok(Some(result));
580 }
581
582 Ok(None)
583 }
584
585 pub async fn clear_pending_group_from_external_commit(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<()> {
587 self.context
588 .clear_pending_group_from_external_commit(&conversation_id)
589 .await?;
590 Ok(())
591 }
592
593 pub async fn commit_accepted(
595 &self,
596 conversation_id: Vec<u8>,
597 ) -> CoreCryptoResult<Option<Vec<BufferedDecryptedMessage>>> {
598 if let Some(decrypted_messages) = self.context.commit_accepted(&conversation_id).await? {
599 let result = decrypted_messages
600 .into_iter()
601 .map(TryInto::try_into)
602 .collect::<CoreCryptoResult<Vec<_>>>()?;
603 return Ok(Some(result));
604 }
605
606 Ok(None)
607 }
608
609 pub async fn clear_pending_proposal(
611 &self,
612 conversation_id: Vec<u8>,
613 proposal_ref: Vec<u8>,
614 ) -> CoreCryptoResult<()> {
615 self.context
616 .clear_pending_proposal(&conversation_id, proposal_ref.into())
617 .await?;
618 Ok(())
619 }
620
621 pub async fn clear_pending_commit(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<()> {
623 self.context.clear_pending_commit(&conversation_id).await?;
624 Ok(())
625 }
626}