core_crypto_ffi/core_crypto_context/
mls.rs

1use core_crypto::{
2    RecursiveError,
3    mls::conversation::Conversation as _,
4    prelude::{ClientIdentifier, KeyPackageIn, KeyPackageRef, MlsConversationConfiguration, VerifiableGroupInfo},
5    transaction_context::Error as TransactionError,
6};
7use tls_codec::{Deserialize as _, Serialize as _};
8#[cfg(target_family = "wasm")]
9use wasm_bindgen::prelude::*;
10
11use crate::{
12    Ciphersuite, Ciphersuites, ClientId, ConversationConfiguration, ConversationId, CoreCryptoContext, CoreCryptoError,
13    CoreCryptoResult, CredentialType, CustomConfiguration, DecryptedMessage, NewCrlDistributionPoints, WelcomeBundle,
14    conversation_id_vec,
15};
16
17#[cfg(not(target_family = "wasm"))]
18type KeyPackages = Vec<Vec<u8>>;
19
20#[cfg(target_family = "wasm")]
21type KeyPackages = super::array_of_byte_array::ArrayOfByteArray;
22
23#[cfg_attr(target_family = "wasm", wasm_bindgen)]
24#[cfg_attr(not(target_family = "wasm"), uniffi::export)]
25impl CoreCryptoContext {
26    /// See [core_crypto::context::CentralContext::mls_init]
27    pub async fn mls_init(
28        &self,
29        client_id: ClientId,
30        ciphersuites: Ciphersuites,
31        nb_key_package: Option<u32>,
32    ) -> CoreCryptoResult<()> {
33        let nb_key_package = nb_key_package
34            .map(usize::try_from)
35            .transpose()
36            .map_err(CoreCryptoError::generic())?;
37        self.inner
38            .mls_init(
39                ClientIdentifier::Basic(client_id.0),
40                (&ciphersuites).into(),
41                nb_key_package,
42            )
43            .await?;
44        Ok(())
45    }
46
47    /// See [core_crypto::context::CentralContext::mls_generate_keypairs]
48    pub async fn mls_generate_keypairs(&self, ciphersuites: Ciphersuites) -> CoreCryptoResult<Vec<ClientId>> {
49        Ok(self
50            .inner
51            .mls_generate_keypairs((&ciphersuites).into())
52            .await
53            .map(|cids| cids.into_iter().map(ClientId).collect())?)
54    }
55
56    /// See [core_crypto::context::CentralContext::mls_init_with_client_id]
57    pub async fn mls_init_with_client_id(
58        &self,
59        client_id: ClientId,
60        tmp_client_ids: Vec<ClientId>,
61        ciphersuites: Ciphersuites,
62    ) -> CoreCryptoResult<()> {
63        Ok(self
64            .inner
65            .mls_init_with_client_id(
66                client_id.0,
67                tmp_client_ids.into_iter().map(|cid| cid.0).collect(),
68                (&ciphersuites).into(),
69            )
70            .await?)
71    }
72
73    /// See [core_crypto::mls::MlsCentral::client_public_key]
74    pub async fn client_public_key(
75        &self,
76        ciphersuite: Ciphersuite,
77        credential_type: CredentialType,
78    ) -> CoreCryptoResult<Vec<u8>> {
79        Ok(self
80            .inner
81            .client_public_key(ciphersuite.into(), credential_type.into())
82            .await?)
83    }
84
85    /// See [core_crypto::mls::conversation::ConversationGuard::epoch]
86    pub async fn conversation_epoch(&self, conversation_id: &ConversationId) -> CoreCryptoResult<u64> {
87        let conversation_id = conversation_id_vec!(conversation_id);
88        let conversation = self.inner.conversation(&conversation_id).await?;
89        Ok(conversation.epoch().await)
90    }
91
92    /// See [core_crypto::mls::conversation::ConversationGuard::ciphersuite]
93    pub async fn conversation_ciphersuite(&self, conversation_id: &ConversationId) -> CoreCryptoResult<Ciphersuite> {
94        let conversation_id = conversation_id_vec!(conversation_id);
95        let cs = self.inner.conversation(&conversation_id).await?.ciphersuite().await;
96        Ok(Ciphersuite::from(core_crypto::prelude::CiphersuiteName::from(cs)))
97    }
98
99    /// See [core_crypto::mls::MlsCentral::conversation_exists]
100    pub async fn conversation_exists(&self, conversation_id: &ConversationId) -> CoreCryptoResult<bool> {
101        let conversation_id = conversation_id_vec!(conversation_id);
102        self.inner
103            .conversation_exists(&conversation_id)
104            .await
105            .map_err(Into::into)
106    }
107
108    /// See [core_crypto::mls::conversation::ImmutableConversation::get_client_ids]
109    pub async fn get_client_ids(&self, conversation_id: &ConversationId) -> CoreCryptoResult<Vec<ClientId>> {
110        let conversation_id = conversation_id_vec!(conversation_id);
111        let conversation = self.inner.conversation(&conversation_id).await?;
112        let client_ids = conversation.get_client_ids().await.into_iter().map(ClientId).collect();
113        Ok(client_ids)
114    }
115
116    /// See [core_crypto::mls::conversation::ImmutableConversation::export_secret_key]
117    pub async fn export_secret_key(
118        &self,
119        conversation_id: &ConversationId,
120        key_length: u32,
121    ) -> CoreCryptoResult<Vec<u8>> {
122        let conversation_id = conversation_id_vec!(conversation_id);
123        let conversation = self.inner.conversation(&conversation_id).await?;
124        conversation
125            .export_secret_key(key_length as usize)
126            .await
127            .map_err(Into::into)
128    }
129
130    /// See [core_crypto::mls::conversation::ImmutableConversation::get_external_sender]
131    pub async fn get_external_sender(&self, conversation_id: &ConversationId) -> CoreCryptoResult<Vec<u8>> {
132        let conversation_id = conversation_id_vec!(conversation_id);
133        let conversation = self.inner.conversation(&conversation_id).await?;
134        conversation.get_external_sender().await.map_err(Into::into)
135    }
136
137    /// See [core_crypto::context::CentralContext::get_or_create_client_keypackages]
138    pub async fn client_keypackages(
139        &self,
140        ciphersuite: Ciphersuite,
141        credential_type: CredentialType,
142        amount_requested: u32,
143    ) -> CoreCryptoResult<KeyPackages> {
144        let kps = self
145            .inner
146            .get_or_create_client_keypackages(ciphersuite.into(), credential_type.into(), amount_requested as usize)
147            .await
148            .map_err(RecursiveError::mls_client("getting or creating client keypackages"))?;
149
150        kps.into_iter()
151            .map(|kp| {
152                kp.tls_serialize_detached()
153                    .map_err(core_crypto::mls::conversation::Error::tls_serialize("keypackage"))
154                    .map_err(RecursiveError::mls_conversation("serializing keypackage"))
155                    .map_err(Into::into)
156            })
157            .collect::<CoreCryptoResult<KeyPackages>>()
158    }
159
160    /// See [core_crypto::context::CentralContext::client_valid_key_packages_count]
161    pub async fn client_valid_keypackages_count(
162        &self,
163        ciphersuite: Ciphersuite,
164        credential_type: CredentialType,
165    ) -> CoreCryptoResult<u64> {
166        let count = self
167            .inner
168            .client_valid_key_packages_count(ciphersuite.into(), credential_type.into())
169            .await
170            .map_err(RecursiveError::mls_client("counting client valid keypackages"))?;
171
172        Ok(count.try_into().unwrap_or(0))
173    }
174
175    /// See [core_crypto::context::CentralContext::delete_keypackages]
176    pub async fn delete_keypackages(&self, refs: KeyPackages) -> CoreCryptoResult<()> {
177        #[cfg(target_family = "wasm")]
178        let refs = refs.into_inner();
179        let refs = refs
180            .into_iter()
181            .map(|r| KeyPackageRef::from_slice(&r))
182            .collect::<Vec<_>>();
183
184        self.inner
185            .delete_keypackages(&refs[..])
186            .await
187            .map_err(RecursiveError::mls_client("deleting keypackages"))?;
188        Ok(())
189    }
190
191    /// See [core_crypto::context::CentralContext::new_conversation]
192    pub async fn create_conversation(
193        &self,
194        conversation_id: &ConversationId,
195        creator_credential_type: CredentialType,
196        config: ConversationConfiguration,
197    ) -> CoreCryptoResult<()> {
198        let conversation_id = conversation_id_vec!(conversation_id);
199
200        let mut lower_cfg = MlsConversationConfiguration {
201            custom: config.custom.into(),
202            ciphersuite: config.ciphersuite.map(Into::into).unwrap_or_default(),
203            ..Default::default()
204        };
205
206        self.inner
207            .set_raw_external_senders(&mut lower_cfg, config.external_senders)
208            .await?;
209
210        self.inner
211            .new_conversation(&conversation_id, creator_credential_type.into(), lower_cfg)
212            .await?;
213        Ok(())
214    }
215
216    /// See [core_crypto::context::CentralContext::process_raw_welcome_message]
217    pub async fn process_welcome_message(
218        &self,
219        welcome_message: Vec<u8>,
220        custom_configuration: CustomConfiguration,
221    ) -> CoreCryptoResult<WelcomeBundle> {
222        let result = self
223            .inner
224            .process_raw_welcome_message(welcome_message, custom_configuration.into())
225            .await?
226            .into();
227        Ok(result)
228    }
229
230    /// See [core_crypto::mls::conversation::conversation_guard::ConversationGuard::add_members]
231    pub async fn add_clients_to_conversation(
232        &self,
233        conversation_id: &ConversationId,
234        key_packages: KeyPackages,
235    ) -> CoreCryptoResult<NewCrlDistributionPoints> {
236        let conversation_id = conversation_id_vec!(conversation_id);
237
238        #[cfg(target_family = "wasm")]
239        let key_packages = key_packages.into_inner();
240        let key_packages = key_packages
241            .into_iter()
242            .map(|kp| {
243                KeyPackageIn::tls_deserialize(&mut kp.as_slice())
244                    .map_err(core_crypto::mls::conversation::Error::tls_deserialize("keypackage"))
245                    .map_err(RecursiveError::mls_conversation("adding members to conversation"))
246                    .map_err(Into::into)
247            })
248            .collect::<CoreCryptoResult<Vec<_>>>()?;
249
250        let mut conversation = self.inner.conversation(&conversation_id).await?;
251        let distribution_points: Option<Vec<_>> = conversation.add_members(key_packages).await?.into();
252        Ok(distribution_points.into())
253    }
254
255    /// See [core_crypto::context::CentralContext::remove_members_from_conversation]
256    pub async fn remove_clients_from_conversation(
257        &self,
258        conversation_id: &ConversationId,
259        clients: Vec<ClientId>,
260    ) -> CoreCryptoResult<()> {
261        let clients: Vec<core_crypto::prelude::ClientId> = clients.into_iter().map(|c| c.0).collect();
262        let conversation_id = conversation_id_vec!(conversation_id);
263        let mut conversation = self.inner.conversation(&conversation_id).await?;
264        conversation.remove_members(&clients).await.map_err(Into::into)
265    }
266
267    /// See [core_crypto::mls::conversation::ConversationGuard::mark_as_child_of]
268    pub async fn mark_conversation_as_child_of(
269        &self,
270        child_id: &ConversationId,
271        parent_id: &ConversationId,
272    ) -> CoreCryptoResult<()> {
273        let parent_id = conversation_id_vec!(parent_id);
274        let child_id = conversation_id_vec!(child_id);
275        let mut conversation = self.inner.conversation(&child_id).await?;
276        conversation.mark_as_child_of(&parent_id).await.map_err(Into::into)
277    }
278
279    /// See [core_crypto::context::CentralContext::update_keying_material]
280    pub async fn update_keying_material(&self, conversation_id: &ConversationId) -> CoreCryptoResult<()> {
281        let conversation_id = conversation_id_vec!(conversation_id);
282        let mut conversation = self.inner.conversation(&conversation_id).await?;
283        conversation.update_key_material().await.map_err(Into::into)
284    }
285
286    /// See [core_crypto::mls::conversation::conversation_guard::ConversationGuard::commit_pending_proposals]
287    pub async fn commit_pending_proposals(&self, conversation_id: &ConversationId) -> CoreCryptoResult<()> {
288        let conversation_id = conversation_id_vec!(conversation_id);
289        let mut conversation = self.inner.conversation(&conversation_id).await?;
290        conversation.commit_pending_proposals().await.map_err(Into::into)
291    }
292
293    /// see [core_crypto::context::CentralContext::wipe_conversation]
294    pub async fn wipe_conversation(&self, conversation_id: &ConversationId) -> CoreCryptoResult<()> {
295        let conversation_id = conversation_id_vec!(conversation_id);
296        let mut conversation = self.inner.conversation(&conversation_id).await?;
297        conversation.wipe().await.map_err(Into::into)
298    }
299
300    /// See [core_crypto::mls::conversation::conversation_guard::ConversationGuard::decrypt_message]
301    pub async fn decrypt_message(
302        &self,
303        conversation_id: &ConversationId,
304        payload: Vec<u8>,
305    ) -> CoreCryptoResult<DecryptedMessage> {
306        let conversation_id = conversation_id_vec!(conversation_id);
307        let conversation_result = self.inner.conversation(&conversation_id).await;
308        let decrypted_message = match conversation_result {
309            Err(TransactionError::PendingConversation(mut pending)) => {
310                pending.try_process_own_join_commit(&payload).await?
311            }
312            Ok(mut conversation) => conversation.decrypt_message(&payload).await?,
313            Err(e) => Err(e)?,
314        };
315
316        decrypted_message.try_into()
317    }
318
319    /// See [core_crypto::mls::conversation::conversation_guard::ConversationGuard::encrypt_message]
320    pub async fn encrypt_message(
321        &self,
322        conversation_id: &ConversationId,
323        message: Vec<u8>,
324    ) -> CoreCryptoResult<Vec<u8>> {
325        let conversation_id = conversation_id_vec!(conversation_id);
326        let mut conversation = self.inner.conversation(&conversation_id).await?;
327        conversation.encrypt_message(message).await.map_err(Into::into)
328    }
329
330    /// See [core_crypto::context::CentralContext::join_by_external_commit]
331    pub async fn join_by_external_commit(
332        &self,
333        group_info: Vec<u8>,
334        custom_configuration: CustomConfiguration,
335        credential_type: CredentialType,
336    ) -> CoreCryptoResult<WelcomeBundle> {
337        let group_info = VerifiableGroupInfo::tls_deserialize(&mut group_info.as_slice())
338            .map_err(core_crypto::mls::conversation::Error::tls_deserialize(
339                "verifiable group info",
340            ))
341            .map_err(RecursiveError::mls_conversation("joining by external commmit"))?;
342        let welcome_bundle = self
343            .inner
344            .join_by_external_commit(group_info, custom_configuration.into(), credential_type.into())
345            .await?;
346        Ok(welcome_bundle.into())
347    }
348}