core_crypto_ffi/generic/context/
mod.rs1use super::{
2 Ciphersuite, Ciphersuites, ClientId, ConversationConfiguration, CoreCrypto, CoreCryptoError, CoreCryptoResult,
3 CustomConfiguration, DecryptedMessage, MlsCredentialType, WelcomeBundle,
4};
5use crate::NewCrlDistributionPoints;
6use async_lock::{Mutex, OnceCell};
7use core_crypto::mls::conversation::Conversation as _;
8use core_crypto::mls::conversation::Error as ConversationError;
9use core_crypto::{
10 RecursiveError,
11 context::CentralContext,
12 prelude::{
13 ClientIdentifier, ConversationId, KeyPackageIn, KeyPackageRef, MlsConversationConfiguration,
14 VerifiableGroupInfo,
15 },
16};
17use std::{future::Future, 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}
27
28impl Deref for CoreCryptoContext {
29 type Target = CentralContext;
30
31 fn deref(&self) -> &Self::Target {
32 self.context.as_ref()
33 }
34}
35
36#[uniffi::export(with_foreign)]
37#[async_trait::async_trait]
38pub trait CoreCryptoCommand: Send + Sync {
39 async fn execute(&self, context: Arc<CoreCryptoContext>) -> CoreCryptoResult<()>;
41}
42
43#[async_trait::async_trait]
44impl<F, Fut> CoreCryptoCommand for F
45where
46 F: Fn(Arc<CoreCryptoContext>) -> Fut + Send + Sync,
47 Fut: Future<Output = CoreCryptoResult<()>> + Send,
48{
49 async fn execute(&self, context: Arc<CoreCryptoContext>) -> CoreCryptoResult<()> {
50 self(context).await
51 }
52}
53
54pub struct TransactionHelper<T, F> {
85 func: Mutex<Option<F>>,
86 return_value: OnceCell<T>,
87}
88
89impl<T, F, Fut> TransactionHelper<T, F>
90where
91 F: FnOnce(Arc<CoreCryptoContext>) -> Fut + Send + Sync,
92 Fut: Future<Output = CoreCryptoResult<T>> + Send,
93 T: Send + Sync,
94{
95 pub fn new(func: F) -> Arc<Self> {
96 Arc::new(Self {
97 func: Mutex::new(Some(func)),
98 return_value: OnceCell::new(),
99 })
100 }
101
102 pub fn into_return_value(self: Arc<Self>) -> T {
118 Arc::into_inner(self)
119 .expect("there should exist exactly one strong ref right now")
120 .return_value
121 .into_inner()
122 .expect("return value should be initialized")
123 }
124
125 pub fn try_into_return_value(self: Arc<Self>) -> Option<T> {
131 Arc::into_inner(self)?.return_value.into_inner()
132 }
133}
134
135#[async_trait::async_trait]
136impl<T, F, Fut> CoreCryptoCommand for TransactionHelper<T, F>
137where
138 F: FnOnce(Arc<CoreCryptoContext>) -> Fut + Send + Sync,
139 Fut: Future<Output = CoreCryptoResult<T>> + Send,
140 T: Send + Sync,
141{
142 async fn execute(&self, context: Arc<CoreCryptoContext>) -> CoreCryptoResult<()> {
143 let func = self
144 .func
145 .lock()
146 .await
147 .take()
148 .expect("inner function must only be called once");
149 let return_value = func(context).await?;
150 let set_result = self.return_value.set(return_value).await;
151 if set_result.is_err() {
152 panic!("return value was previously set");
156 }
157 Ok(())
158 }
159}
160
161#[uniffi::export]
162impl CoreCrypto {
163 pub async fn transaction(&self, command: Arc<dyn CoreCryptoCommand>) -> CoreCryptoResult<()> {
176 let context = Arc::new(CoreCryptoContext {
177 context: Arc::new(self.central.new_transaction().await?),
178 });
179
180 let result = command.execute(context.clone()).await;
181 match result {
182 Ok(result) => {
183 context.context.finish().await?;
184 Ok(result)
185 }
186 Err(err) => {
187 context.context.abort().await?;
188 Err(err)
189 }
190 }
191 }
192}
193
194#[uniffi::export]
195impl CoreCryptoContext {
196 pub async fn set_data(&self, data: Vec<u8>) -> CoreCryptoResult<()> {
198 self.context.set_data(data).await.map_err(Into::into)
199 }
200
201 pub async fn get_data(&self) -> CoreCryptoResult<Option<Vec<u8>>> {
203 self.context.get_data().await.map_err(Into::into)
204 }
205
206 pub async fn mls_init(
208 &self,
209 client_id: ClientId,
210 ciphersuites: Ciphersuites,
211 nb_key_package: Option<u32>,
212 ) -> CoreCryptoResult<()> {
213 let nb_key_package = nb_key_package
214 .map(usize::try_from)
215 .transpose()
216 .map_err(CoreCryptoError::generic())?;
217 self.context
218 .mls_init(
219 ClientIdentifier::Basic(client_id.0),
220 (&ciphersuites).into(),
221 nb_key_package,
222 )
223 .await?;
224 Ok(())
225 }
226
227 pub async fn mls_generate_keypairs(&self, ciphersuites: Ciphersuites) -> CoreCryptoResult<Vec<ClientId>> {
229 Ok(self
230 .context
231 .mls_generate_keypairs((&ciphersuites).into())
232 .await
233 .map(|cids| cids.into_iter().map(ClientId).collect())?)
234 }
235
236 pub async fn mls_init_with_client_id(
238 &self,
239 client_id: ClientId,
240 tmp_client_ids: Vec<ClientId>,
241 ciphersuites: Ciphersuites,
242 ) -> CoreCryptoResult<()> {
243 Ok(self
244 .context
245 .mls_init_with_client_id(
246 client_id.0,
247 tmp_client_ids.into_iter().map(|cid| cid.0).collect(),
248 (&ciphersuites).into(),
249 )
250 .await?)
251 }
252
253 pub async fn client_public_key(
255 &self,
256 ciphersuite: Ciphersuite,
257 credential_type: MlsCredentialType,
258 ) -> CoreCryptoResult<Vec<u8>> {
259 Ok(self
260 .context
261 .client_public_key(ciphersuite.into(), credential_type.into())
262 .await?)
263 }
264
265 pub async fn conversation_epoch(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<u64> {
267 let conversation = self.context.conversation(&conversation_id).await?;
268 Ok(conversation.epoch().await)
269 }
270
271 pub async fn conversation_ciphersuite(&self, conversation_id: &ConversationId) -> CoreCryptoResult<Ciphersuite> {
273 let cs = self.context.conversation(conversation_id).await?.ciphersuite().await;
274 Ok(Ciphersuite::from(core_crypto::prelude::CiphersuiteName::from(cs)))
275 }
276
277 pub async fn conversation_exists(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<bool> {
279 Ok(self.context.conversation_exists(&conversation_id).await?)
280 }
281
282 pub async fn get_client_ids(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<Vec<ClientId>> {
284 let client_ids = self
285 .context
286 .conversation(&conversation_id)
287 .await?
288 .get_client_ids()
289 .await
290 .into_iter()
291 .map(ClientId)
292 .collect();
293 Ok(client_ids)
294 }
295
296 pub async fn export_secret_key(&self, conversation_id: Vec<u8>, key_length: u32) -> CoreCryptoResult<Vec<u8>> {
298 self.context
299 .conversation(&conversation_id)
300 .await?
301 .export_secret_key(key_length as usize)
302 .await
303 .map_err(Into::into)
304 }
305
306 pub async fn get_external_sender(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<Vec<u8>> {
308 self.context
309 .conversation(&conversation_id)
310 .await?
311 .get_external_sender()
312 .await
313 .map_err(Into::into)
314 }
315
316 pub async fn client_keypackages(
318 &self,
319 ciphersuite: Ciphersuite,
320 credential_type: MlsCredentialType,
321 amount_requested: u32,
322 ) -> CoreCryptoResult<Vec<Vec<u8>>> {
323 let kps = self
324 .context
325 .get_or_create_client_keypackages(ciphersuite.into(), credential_type.into(), amount_requested as usize)
326 .await
327 .map_err(RecursiveError::mls_client("getting or creating client keypackages"))?;
328
329 kps.into_iter()
330 .map(|kp| {
331 kp.tls_serialize_detached()
332 .map_err(core_crypto::mls::conversation::Error::tls_serialize("keypackage"))
333 .map_err(RecursiveError::mls_conversation("serializing keypackage"))
334 .map_err(Into::into)
335 })
336 .collect::<CoreCryptoResult<Vec<Vec<u8>>>>()
337 }
338
339 pub async fn client_valid_keypackages_count(
341 &self,
342 ciphersuite: Ciphersuite,
343 credential_type: MlsCredentialType,
344 ) -> CoreCryptoResult<u64> {
345 let count = self
346 .context
347 .client_valid_key_packages_count(ciphersuite.into(), credential_type.into())
348 .await
349 .map_err(RecursiveError::mls_client("counting client valid keypackages"))?;
350
351 Ok(count.try_into().unwrap_or(0))
352 }
353
354 pub async fn delete_keypackages(&self, refs: Vec<Vec<u8>>) -> CoreCryptoResult<()> {
356 let refs = refs
357 .into_iter()
358 .map(|r| KeyPackageRef::from_slice(&r))
359 .collect::<Vec<_>>();
360
361 self.context
362 .delete_keypackages(&refs[..])
363 .await
364 .map_err(RecursiveError::mls_client("deleting keypackages"))?;
365 Ok(())
366 }
367
368 pub async fn create_conversation(
370 &self,
371 conversation_id: Vec<u8>,
372 creator_credential_type: MlsCredentialType,
373 config: ConversationConfiguration,
374 ) -> CoreCryptoResult<()> {
375 let mut lower_cfg = MlsConversationConfiguration {
376 custom: config.custom.into(),
377 ciphersuite: config.ciphersuite.into(),
378 ..Default::default()
379 };
380
381 self.context
382 .set_raw_external_senders(&mut lower_cfg, config.external_senders)
383 .await?;
384
385 self.context
386 .new_conversation(&conversation_id, creator_credential_type.into(), lower_cfg)
387 .await?;
388 Ok(())
389 }
390
391 pub async fn process_welcome_message(
393 &self,
394 welcome_message: Vec<u8>,
395 custom_configuration: CustomConfiguration,
396 ) -> CoreCryptoResult<WelcomeBundle> {
397 let result = self
398 .context
399 .process_raw_welcome_message(welcome_message, custom_configuration.into())
400 .await?
401 .into();
402 Ok(result)
403 }
404
405 pub async fn add_clients_to_conversation(
407 &self,
408 conversation_id: Vec<u8>,
409 key_packages: Vec<Vec<u8>>,
410 ) -> CoreCryptoResult<NewCrlDistributionPoints> {
411 let key_packages = key_packages
412 .into_iter()
413 .map(|kp| {
414 KeyPackageIn::tls_deserialize(&mut kp.as_slice())
415 .map_err(core_crypto::mls::conversation::Error::tls_deserialize("keypackage"))
416 .map_err(RecursiveError::mls_conversation("adding members to conversation"))
417 .map_err(Into::into)
418 })
419 .collect::<CoreCryptoResult<Vec<_>>>()?;
420
421 let distribution_points: Option<Vec<_>> = self
422 .context
423 .conversation(&conversation_id)
424 .await?
425 .add_members(key_packages)
426 .await?
427 .into();
428 Ok(distribution_points.into())
429 }
430
431 pub async fn remove_clients_from_conversation(
433 &self,
434 conversation_id: Vec<u8>,
435 clients: Vec<ClientId>,
436 ) -> CoreCryptoResult<()> {
437 let clients: Vec<core_crypto::prelude::ClientId> = clients.into_iter().map(|c| c.0).collect();
438 self.context
439 .conversation(&conversation_id)
440 .await?
441 .remove_members(&clients)
442 .await
443 .map_err(Into::into)
444 }
445
446 pub async fn mark_conversation_as_child_of(&self, child_id: Vec<u8>, parent_id: Vec<u8>) -> CoreCryptoResult<()> {
448 self.context
449 .conversation(&child_id)
450 .await?
451 .mark_as_child_of(&parent_id)
452 .await
453 .map_err(Into::into)
454 }
455
456 pub async fn update_keying_material(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<()> {
458 self.context
459 .conversation(&conversation_id)
460 .await?
461 .update_key_material()
462 .await
463 .map_err(Into::into)
464 }
465
466 pub async fn commit_pending_proposals(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<()> {
468 self.context
469 .conversation(&conversation_id)
470 .await?
471 .commit_pending_proposals()
472 .await
473 .map_err(Into::into)
474 }
475
476 pub async fn wipe_conversation(&self, conversation_id: Vec<u8>) -> CoreCryptoResult<()> {
478 self.context.wipe_conversation(&conversation_id).await?;
479 Ok(())
480 }
481
482 pub async fn decrypt_message(
484 &self,
485 conversation_id: Vec<u8>,
486 payload: Vec<u8>,
487 ) -> CoreCryptoResult<DecryptedMessage> {
488 let result = self
489 .context
490 .conversation(&conversation_id)
491 .await?
492 .decrypt_message(&payload)
493 .await;
494 let decrypted_message = if let Err(ConversationError::PendingConversation(mut pending)) = result {
495 pending.try_process_own_join_commit(&payload).await
496 } else {
497 result
498 }?;
499
500 decrypted_message.try_into()
501 }
502
503 pub async fn encrypt_message(&self, conversation_id: Vec<u8>, message: Vec<u8>) -> CoreCryptoResult<Vec<u8>> {
505 self.context
506 .conversation(&conversation_id)
507 .await?
508 .encrypt_message(message)
509 .await
510 .map_err(Into::into)
511 }
512
513 pub async fn join_by_external_commit(
515 &self,
516 group_info: Vec<u8>,
517 custom_configuration: CustomConfiguration,
518 credential_type: MlsCredentialType,
519 ) -> CoreCryptoResult<WelcomeBundle> {
520 let group_info = VerifiableGroupInfo::tls_deserialize(&mut group_info.as_slice())
521 .map_err(core_crypto::mls::conversation::Error::tls_deserialize(
522 "verifiable group info",
523 ))
524 .map_err(RecursiveError::mls_conversation("joining by external commmit"))?;
525 Ok(self
526 .context
527 .join_by_external_commit(group_info, custom_configuration.into(), credential_type.into())
528 .await?
529 .into())
530 }
531}