core_crypto_ffi/core_crypto/
mod.rs

1mod client;
2pub(crate) mod command;
3pub(crate) mod conversation;
4pub(crate) mod e2ei;
5pub(crate) mod epoch_observer;
6pub(crate) mod logger;
7pub(crate) mod mls_transport;
8mod proteus;
9mod randomness;
10
11use core_crypto::prelude::{MlsClientConfiguration, Session};
12#[cfg(target_family = "wasm")]
13use wasm_bindgen::prelude::*;
14
15use crate::{Ciphersuites, CoreCryptoError, CoreCryptoResult, DatabaseKey, client_id::AsCoreCryptoClientId as _};
16
17/// In Wasm, boxed slices are the natural way to communicate an immutable byte slice
18#[cfg(target_family = "wasm")]
19pub(crate) type EntropySeed = Box<[u8]>;
20
21/// In uniffi, a vector is the natural way to communicate a byte slice
22#[cfg(not(target_family = "wasm"))]
23pub(crate) type EntropySeed = Vec<u8>;
24
25#[cfg(target_family = "wasm")]
26pub(crate) fn entropy_seed_map(e: EntropySeed) -> Vec<u8> {
27    e.into()
28}
29
30#[cfg(not(target_family = "wasm"))]
31pub(crate) fn entropy_seed_map(e: EntropySeed) -> Vec<u8> {
32    e
33}
34
35#[cfg(not(target_family = "wasm"))]
36type ClientId = crate::ClientId;
37
38#[cfg(target_family = "wasm")]
39type ClientId = crate::FfiClientId;
40
41#[derive(Debug)]
42#[cfg_attr(target_family = "wasm", wasm_bindgen)]
43#[cfg_attr(not(target_family = "wasm"), derive(uniffi::Object))]
44pub struct CoreCrypto {
45    pub(crate) inner: core_crypto::CoreCrypto,
46}
47
48/// Free function to construct a new `CoreCrypto` instance.
49///
50/// This is necessary because in uniffi async constructors are not supported.
51///
52/// See [core_crypto::mls::Client::try_new]
53#[cfg(not(target_family = "wasm"))]
54#[uniffi::export]
55pub async fn core_crypto_new(
56    path: String,
57    key: DatabaseKey,
58    client_id: ClientId,
59    ciphersuites: Ciphersuites,
60    entropy_seed: Option<EntropySeed>,
61    nb_key_package: Option<u32>,
62) -> CoreCryptoResult<CoreCrypto> {
63    CoreCrypto::new(
64        path,
65        key,
66        Some(client_id),
67        Some(ciphersuites),
68        entropy_seed,
69        nb_key_package,
70    )
71    .await
72}
73
74/// Free function to construct a new `CoreCrypto` instance.
75///
76/// Similar to [`core_crypto_new`] but defers MLS initialization. It can be initialized later
77/// with [CoreCryptoContext::mls_init].
78#[cfg(not(target_family = "wasm"))]
79#[uniffi::export]
80pub async fn core_crypto_deferred_init(
81    path: String,
82    key: DatabaseKey,
83    entropy_seed: Option<EntropySeed>,
84) -> CoreCryptoResult<CoreCrypto> {
85    let entropy_seed = entropy_seed.map(entropy_seed_map);
86    let configuration = MlsClientConfiguration::try_new(path, key.into(), None, Vec::new(), entropy_seed, None)?;
87    CoreCrypto::from_config(configuration).await
88}
89
90impl CoreCrypto {
91    pub async fn new(
92        path: String,
93        key: DatabaseKey,
94        client_id: Option<ClientId>,
95        ciphersuites: Option<Ciphersuites>,
96        entropy_seed: Option<EntropySeed>,
97        nb_key_package: Option<u32>,
98    ) -> CoreCryptoResult<Self> {
99        let nb_key_package = nb_key_package
100            .map(usize::try_from)
101            .transpose()
102            .map_err(CoreCryptoError::generic())?;
103        let entropy_seed = entropy_seed.map(entropy_seed_map);
104        let configuration = MlsClientConfiguration::try_new(
105            path,
106            key.into(),
107            client_id.map(|cid| cid.as_cc_client_id()),
108            (&ciphersuites.unwrap_or_default()).into(),
109            entropy_seed,
110            nb_key_package,
111        )?;
112        Self::from_config(configuration).await
113    }
114
115    async fn from_config(configuration: MlsClientConfiguration) -> CoreCryptoResult<Self> {
116        #[cfg(target_family = "wasm")]
117        console_error_panic_hook::set_once();
118
119        let client = Session::try_new(configuration).await?;
120        let inner = core_crypto::CoreCrypto::from(client);
121
122        Ok(Self { inner })
123    }
124}
125
126#[cfg(target_family = "wasm")]
127#[wasm_bindgen]
128impl CoreCrypto {
129    pub async fn async_new(
130        path: String,
131        key: DatabaseKey,
132        client_id: Option<ClientId>,
133        ciphersuites: Option<Ciphersuites>,
134        entropy_seed: Option<EntropySeed>,
135        nb_key_package: Option<u32>,
136    ) -> CoreCryptoResult<Self> {
137        Self::new(path, key, client_id, ciphersuites, entropy_seed, nb_key_package).await
138    }
139
140    pub async fn deferred_init(
141        path: String,
142        key: DatabaseKey,
143        entropy_seed: Option<Box<[u8]>>,
144    ) -> CoreCryptoResult<CoreCrypto> {
145        let entropy_seed = entropy_seed.map(|s| s.to_vec());
146        let configuration = MlsClientConfiguration::try_new(path, key.into(), None, vec![], entropy_seed, None)
147            .map_err(CoreCryptoError::from)?;
148
149        Self::from_config(configuration).await
150    }
151
152    /// see [core_crypto::mls::Client::close]
153    // Note that this is implemented only for Wasm; Uniffi already generates a `close` method which suffices.
154    pub async fn close(self) -> CoreCryptoResult<()> {
155        self.inner.take().close().await.map_err(Into::into)
156    }
157}
158
159#[cfg_attr(target_family = "wasm", wasm_bindgen)]
160#[cfg_attr(not(target_family = "wasm"), uniffi::export)]
161impl CoreCrypto {
162    /// see [core_crypto::mls::MlsCentral::can_close]
163    pub async fn can_close(&self) -> bool {
164        self.inner.can_close().await
165    }
166}