core_crypto/mls/session/
mod.rs

1pub(crate) mod config;
2pub(crate) mod e2e_identity;
3mod epoch_observer;
4mod error;
5mod history_observer;
6pub(crate) mod id;
7pub(crate) mod identifier;
8pub(crate) mod identities;
9pub(crate) mod key_package;
10pub(crate) mod user_id;
11
12use crate::{
13    CoreCrypto, KeystoreError, LeafError, MlsError, MlsTransport, RecursiveError,
14    group_store::GroupStore,
15    mls::{
16        self, HasSessionAndCrypto,
17        conversation::ImmutableConversation,
18        credential::{CredentialBundle, ext::CredentialExt},
19    },
20    prelude::{
21        CertificateBundle, ClientId, ConversationId, HistorySecret, MlsCiphersuite, MlsCredentialType,
22        config::ValidatedSessionConfig, identifier::ClientIdentifier, key_package::KEYPACKAGE_DEFAULT_LIFETIME,
23    },
24};
25use async_lock::RwLock;
26use core_crypto_keystore::{
27    Connection, CryptoKeystoreError,
28    connection::FetchFromDatabase,
29    entities::{EntityFindParams, MlsCredential, MlsSignatureKeyPair},
30};
31pub use epoch_observer::EpochObserver;
32pub(crate) use error::{Error, Result};
33pub use history_observer::HistoryObserver;
34use identities::Identities;
35use log::debug;
36use mls_crypto_provider::{CryptoKeystore, EntropySeed, MlsCryptoProvider};
37use openmls::prelude::{Credential, CredentialType};
38use openmls_basic_credential::SignatureKeyPair;
39use openmls_traits::{OpenMlsCryptoProvider, crypto::OpenMlsCrypto, types::SignatureScheme};
40use openmls_x509_credential::CertificateKeyPair;
41use std::collections::HashSet;
42use std::ops::Deref;
43use std::sync::Arc;
44use tls_codec::{Deserialize, Serialize};
45
46/// A MLS Session enables a user device to communicate via the MLS protocol.
47///
48/// This closely maps to the `Client` term in [RFC 9720], but we avoid that term to avoid ambiguity;
49/// `Client` is very overloaded with distinct meanings.
50///
51/// There is one `Session` per user per device. A session can contain many MLS groups/conversations.
52///
53/// It is cheap to clone a `Session` because everything heavy is wrapped inside an [Arc].
54///
55/// [RFC 9720]: https://www.rfc-editor.org/rfc/rfc9420.html
56#[derive(Clone, derive_more::Debug)]
57pub struct Session {
58    pub(crate) inner: Arc<RwLock<Option<SessionInner>>>,
59    pub(crate) crypto_provider: MlsCryptoProvider,
60    pub(crate) transport: Arc<RwLock<Option<Arc<dyn MlsTransport + 'static>>>>,
61    #[debug("EpochObserver")]
62    pub(crate) epoch_observer: Arc<RwLock<Option<Arc<dyn EpochObserver + 'static>>>>,
63    #[debug("HistoryObserver")]
64    pub(crate) history_observer: Arc<RwLock<Option<Arc<dyn HistoryObserver + 'static>>>>,
65}
66
67#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
68#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
69impl HasSessionAndCrypto for Session {
70    async fn session(&self) -> mls::Result<Session> {
71        Ok(self.clone())
72    }
73
74    async fn crypto_provider(&self) -> mls::Result<MlsCryptoProvider> {
75        Ok(self.crypto_provider.clone())
76    }
77}
78
79#[derive(Clone, Debug)]
80pub(crate) struct SessionInner {
81    id: ClientId,
82    pub(crate) identities: Identities,
83    keypackage_lifetime: std::time::Duration,
84}
85
86impl Session {
87    /// Creates a new [Session].
88    ///
89    /// ## Errors
90    ///
91    /// Failures in the initialization of the KeyStore can cause errors, such as IO, the same kind
92    /// of errors can happen when the groups are being restored from the KeyStore or even during
93    /// the client initialization (to fetch the identity signature).
94    ///
95    /// Other than that, `MlsError` can be caused by group deserialization or during the initialization of the credentials:
96    ///
97    /// * for x509 Credentials if the certificate chain length is lower than 2
98    /// * for Basic Credentials if the signature key cannot be generated, if the scheme is unsupported
99    ///   or the key generation fails
100    pub async fn try_new(
101        ValidatedSessionConfig {
102            db_connection_type,
103            database_key,
104            client_id,
105            external_entropy,
106            ciphersuites,
107            nb_key_packages,
108        }: ValidatedSessionConfig<'_>,
109    ) -> crate::mls::Result<Self> {
110        // Init backend (crypto + rand + keystore)
111        let key_store = CryptoKeystore::open(db_connection_type, &database_key)
112            .await
113            .map_err(MlsError::wrap("initializing keystore"))?;
114        let mls_backend = MlsCryptoProvider::builder()
115            .key_store(key_store)
116            .entropy_seed_opt(external_entropy)
117            .build();
118
119        // We create the core crypto instance first to enable creating a transaction from it and
120        // doing all subsequent actions inside a single transaction, though it forces us to clone
121        // a few Arcs and locks.
122        let session = Self {
123            crypto_provider: mls_backend.clone(),
124            inner: Default::default(),
125            transport: Arc::new(None.into()),
126            epoch_observer: Arc::new(None.into()),
127            history_observer: Arc::new(None.into()),
128        };
129
130        let cc = CoreCrypto::from(session);
131        let context = cc
132            .new_transaction()
133            .await
134            .map_err(RecursiveError::transaction("starting new transaction"))?;
135
136        if let Some(id) = client_id {
137            cc.mls
138                .init(
139                    ClientIdentifier::Basic(id),
140                    ciphersuites.as_slice(),
141                    &mls_backend,
142                    nb_key_packages,
143                )
144                .await
145                .map_err(RecursiveError::mls_client("initializing mls client"))?
146        }
147
148        context
149            .init_pki_env()
150            .await
151            .map_err(RecursiveError::transaction("initializing pki environment"))?;
152        context
153            .finish()
154            .await
155            .map_err(RecursiveError::transaction("finishing transaction"))?;
156
157        Ok(cc.mls)
158    }
159
160    /// Provide the implementation of functions to communicate with the delivery service
161    /// (see [MlsTransport]).
162    pub async fn provide_transport(&self, transport: Arc<dyn MlsTransport>) {
163        self.transport.write().await.replace(transport);
164    }
165
166    /// Initializes the client.
167    /// If the client's cryptographic material is already stored in the keystore, it loads it
168    /// Otherwise, it is being created.
169    ///
170    /// # Arguments
171    /// * `identifier` - client identifier ; either a [ClientId] or a x509 certificate chain
172    /// * `ciphersuites` - all ciphersuites this client is supposed to support
173    /// * `backend` - the KeyStore and crypto provider to read identities from
174    ///
175    /// # Errors
176    /// KeyStore and OpenMls errors can happen
177    pub async fn init(
178        &self,
179        identifier: ClientIdentifier,
180        ciphersuites: &[MlsCiphersuite],
181        backend: &MlsCryptoProvider,
182        nb_key_packages: usize,
183    ) -> Result<()> {
184        self.ensure_unready().await?;
185        let id = identifier.get_id()?;
186
187        let credentials = backend
188            .key_store()
189            .find_all::<MlsCredential>(EntityFindParams::default())
190            .await
191            .map_err(KeystoreError::wrap("finding all mls credentials"))?;
192
193        let credentials = credentials
194            .into_iter()
195            .filter(|mls_credential| mls_credential.id.as_slice() == id.as_slice())
196            .map(|mls_credential| -> Result<_> {
197                let credential = Credential::tls_deserialize(&mut mls_credential.credential.as_slice())
198                    .map_err(Error::tls_deserialize("mls credential"))?;
199                Ok((credential, mls_credential.created_at))
200            })
201            .collect::<Result<Vec<_>>>()?;
202
203        if credentials.is_empty() {
204            debug!(count = nb_key_packages, ciphersuites:? = ciphersuites; "Generating client");
205            self.generate(identifier, backend, ciphersuites, nb_key_packages)
206                .await?;
207        } else {
208            let signature_schemes = ciphersuites
209                .iter()
210                .map(|cs| cs.signature_algorithm())
211                .collect::<HashSet<_>>();
212            let load_result = self.load(backend, id.as_ref(), credentials, signature_schemes).await;
213            if let Err(Error::ClientSignatureNotFound) = load_result {
214                debug!(count = nb_key_packages, ciphersuites:? = ciphersuites; "Client signature not found. Generating client");
215                self.generate(identifier, backend, ciphersuites, nb_key_packages)
216                    .await?;
217            } else {
218                load_result?;
219            }
220        };
221
222        Ok(())
223    }
224
225    /// Resets the client to an uninitialized state.
226    #[cfg(test)]
227    pub(crate) async fn reset(&self) {
228        let mut inner_lock = self.inner.write().await;
229        *inner_lock = None;
230    }
231
232    pub(crate) async fn is_ready(&self) -> bool {
233        let inner_lock = self.inner.read().await;
234        inner_lock.is_some()
235    }
236
237    async fn ensure_unready(&self) -> Result<()> {
238        if self.is_ready().await {
239            Err(Error::UnexpectedlyReady)
240        } else {
241            Ok(())
242        }
243    }
244
245    async fn replace_inner(&self, new_inner: SessionInner) {
246        let mut inner_lock = self.inner.write().await;
247        *inner_lock = Some(new_inner);
248    }
249
250    /// Get an immutable view of an `MlsConversation`.
251    ///
252    /// Because it operates on the raw conversation type, this may be faster than
253    /// [crate::transaction_context::TransactionContext::conversation]. for transient and immutable
254    /// purposes. For long-lived or mutable purposes, prefer the other method.
255    pub async fn get_raw_conversation(&self, id: &ConversationId) -> Result<ImmutableConversation> {
256        let raw_conversation = GroupStore::fetch_from_keystore(id, &self.crypto_provider.keystore(), None)
257            .await
258            .map_err(RecursiveError::root("getting conversation by id"))?
259            .ok_or_else(|| LeafError::ConversationNotFound(id.clone()))?;
260        Ok(ImmutableConversation::new(raw_conversation, self.clone()))
261    }
262
263    /// Returns the client's most recent public signature key as a buffer.
264    /// Used to upload a public key to the server in order to verify client's messages signature.
265    ///
266    /// # Arguments
267    /// * `ciphersuite` - a callback to be called to perform authorization
268    /// * `credential_type` - of the credential to look for
269    pub async fn public_key(
270        &self,
271        ciphersuite: MlsCiphersuite,
272        credential_type: MlsCredentialType,
273    ) -> crate::mls::Result<Vec<u8>> {
274        let cb = self
275            .find_most_recent_credential_bundle(ciphersuite.signature_algorithm(), credential_type)
276            .await
277            .map_err(RecursiveError::mls_client("finding most recent credential bundle"))?;
278        Ok(cb.signature_key.to_public_vec())
279    }
280
281    pub(crate) fn new_basic_credential_bundle(
282        id: &ClientId,
283        sc: SignatureScheme,
284        backend: &MlsCryptoProvider,
285    ) -> Result<CredentialBundle> {
286        let (sk, pk) = backend
287            .crypto()
288            .signature_key_gen(sc)
289            .map_err(MlsError::wrap("generating a signature key"))?;
290
291        let signature_key = SignatureKeyPair::from_raw(sc, sk, pk);
292        let credential = Credential::new_basic(id.to_vec());
293        let cb = CredentialBundle {
294            credential,
295            signature_key,
296            created_at: 0,
297        };
298
299        Ok(cb)
300    }
301
302    pub(crate) fn new_x509_credential_bundle(cert: CertificateBundle) -> Result<CredentialBundle> {
303        let created_at = cert
304            .get_created_at()
305            .map_err(RecursiveError::mls_credential("getting credetntial created at"))?;
306        let (sk, ..) = cert.private_key.into_parts();
307        let chain = cert.certificate_chain;
308
309        let kp = CertificateKeyPair::new(sk, chain.clone()).map_err(MlsError::wrap("creating certificate key pair"))?;
310
311        let credential = Credential::new_x509(chain).map_err(MlsError::wrap("creating x509 credential"))?;
312
313        let cb = CredentialBundle {
314            credential,
315            signature_key: kp.0,
316            created_at,
317        };
318        Ok(cb)
319    }
320
321    /// Checks if a given conversation id exists locally
322    pub async fn conversation_exists(&self, id: &ConversationId) -> Result<bool> {
323        match self.get_raw_conversation(id).await {
324            Ok(_) => Ok(true),
325            Err(Error::Leaf(LeafError::ConversationNotFound(_))) => Ok(false),
326            Err(e) => Err(e),
327        }
328    }
329
330    /// Generates a random byte array of the specified size
331    pub fn random_bytes(&self, len: usize) -> crate::mls::Result<Vec<u8>> {
332        use openmls_traits::random::OpenMlsRand as _;
333        self.crypto_provider
334            .rand()
335            .random_vec(len)
336            .map_err(MlsError::wrap("generating random vector"))
337            .map_err(Into::into)
338    }
339
340    /// Reports whether the local KeyStore believes that it can currently close.
341    ///
342    /// Beware TOCTOU!
343    pub async fn can_close(&self) -> bool {
344        self.crypto_provider.can_close().await
345    }
346
347    /// Closes the connection with the local KeyStore
348    ///
349    /// # Errors
350    /// KeyStore errors, such as IO
351    pub async fn close(self) -> crate::mls::Result<()> {
352        self.crypto_provider
353            .close()
354            .await
355            .map_err(MlsError::wrap("closing connection with keystore"))
356            .map_err(Into::into)
357    }
358
359    /// see [mls_crypto_provider::MlsCryptoProvider::reseed]
360    pub async fn reseed(&self, seed: Option<EntropySeed>) -> crate::mls::Result<()> {
361        self.crypto_provider
362            .reseed(seed)
363            .map_err(MlsError::wrap("reseeding mls backend"))
364            .map_err(Into::into)
365    }
366
367    /// Generates a brand new client from scratch
368    pub(crate) async fn generate(
369        &self,
370        identifier: ClientIdentifier,
371        backend: &MlsCryptoProvider,
372        ciphersuites: &[MlsCiphersuite],
373        nb_key_package: usize,
374    ) -> Result<()> {
375        self.ensure_unready().await?;
376        let id = identifier.get_id()?;
377        let signature_schemes = ciphersuites
378            .iter()
379            .map(|cs| cs.signature_algorithm())
380            .collect::<HashSet<_>>();
381        self.replace_inner(SessionInner {
382            id: id.into_owned(),
383            identities: Identities::new(signature_schemes.len()),
384            keypackage_lifetime: KEYPACKAGE_DEFAULT_LIFETIME,
385        })
386        .await;
387
388        let identities = identifier.generate_credential_bundles(backend, signature_schemes)?;
389
390        for (sc, id, cb) in identities {
391            self.save_identity(&backend.keystore(), Some(&id), sc, cb).await?;
392        }
393
394        let guard = self.inner.read().await;
395        let SessionInner { identities, .. } = guard.as_ref().ok_or(Error::MlsNotInitialized)?;
396
397        if nb_key_package != 0 {
398            for ciphersuite in ciphersuites.iter().copied() {
399                let ciphersuite_signature_scheme = ciphersuite.signature_algorithm();
400                for credential_bundle in identities.iter().filter_map(|(signature_scheme, credential_bundle)| {
401                    (signature_scheme == ciphersuite_signature_scheme).then_some(credential_bundle)
402                }) {
403                    let credential_type = credential_bundle.credential.credential_type().into();
404                    self.request_key_packages(nb_key_package, ciphersuite, credential_type, backend)
405                        .await?;
406                }
407            }
408        }
409
410        Ok(())
411    }
412
413    /// Loads the client from the keystore.
414    pub(crate) async fn load(
415        &self,
416        backend: &MlsCryptoProvider,
417        id: &ClientId,
418        mut credentials: Vec<(Credential, u64)>,
419        signature_schemes: HashSet<SignatureScheme>,
420    ) -> Result<()> {
421        self.ensure_unready().await?;
422        let mut identities = Identities::new(signature_schemes.len());
423
424        // ensures we load credentials in chronological order
425        credentials.sort_by_key(|(_, timestamp)| *timestamp);
426
427        let stored_signature_keypairs = backend
428            .key_store()
429            .find_all::<MlsSignatureKeyPair>(EntityFindParams::default())
430            .await
431            .map_err(KeystoreError::wrap("finding all mls signature keypairs"))?;
432
433        for signature_scheme in signature_schemes {
434            let signature_keypair = stored_signature_keypairs
435                .iter()
436                .find(|skp| skp.signature_scheme == (signature_scheme as u16));
437
438            let signature_key = if let Some(kp) = signature_keypair {
439                SignatureKeyPair::tls_deserialize(&mut kp.keypair.as_slice())
440                    .map_err(Error::tls_deserialize("signature keypair"))?
441            } else {
442                let (private_key, public_key) = backend
443                    .crypto()
444                    .signature_key_gen(signature_scheme)
445                    .map_err(MlsError::wrap("generating signature key"))?;
446                let keypair = SignatureKeyPair::from_raw(signature_scheme, private_key, public_key.clone());
447                let raw_keypair = keypair
448                    .tls_serialize_detached()
449                    .map_err(Error::tls_serialize("raw keypair"))?;
450                let store_keypair =
451                    MlsSignatureKeyPair::new(signature_scheme, public_key, raw_keypair, id.as_slice().into());
452                backend
453                    .key_store()
454                    .save(store_keypair.clone())
455                    .await
456                    .map_err(KeystoreError::wrap("storing keypairs in keystore"))?;
457                SignatureKeyPair::tls_deserialize(&mut store_keypair.keypair.as_slice())
458                    .map_err(Error::tls_deserialize("signature keypair"))?
459            };
460
461            for (credential, created_at) in &credentials {
462                match credential.mls_credential() {
463                    openmls::prelude::MlsCredentialType::Basic(_) => {
464                        if id.as_slice() != credential.identity() {
465                            return Err(Error::WrongCredential);
466                        }
467                    }
468                    openmls::prelude::MlsCredentialType::X509(cert) => {
469                        let spk = cert
470                            .extract_public_key()
471                            .map_err(RecursiveError::mls_credential("extracting public key"))?
472                            .ok_or(LeafError::InternalMlsError)?;
473                        if signature_key.public() != spk {
474                            return Err(Error::WrongCredential);
475                        }
476                    }
477                };
478                let cb = CredentialBundle {
479                    credential: credential.clone(),
480                    signature_key: signature_key.clone(),
481                    created_at: *created_at,
482                };
483                identities.push_credential_bundle(signature_scheme, cb).await?;
484            }
485        }
486        self.replace_inner(SessionInner {
487            id: id.clone(),
488            identities,
489            keypackage_lifetime: KEYPACKAGE_DEFAULT_LIFETIME,
490        })
491        .await;
492        Ok(())
493    }
494
495    /// Restore from an external [`HistorySecret`].
496    pub(crate) async fn restore_from_history_secret(&self, history_secret: HistorySecret) -> Result<()> {
497        self.ensure_unready().await?;
498
499        // store the client id (with some other stuff)
500        self.replace_inner(SessionInner {
501            id: history_secret.client_id.clone(),
502            identities: Identities::new(0),
503            keypackage_lifetime: KEYPACKAGE_DEFAULT_LIFETIME,
504        })
505        .await;
506
507        // store the key package
508        history_secret
509            .key_package
510            .store(&self.crypto_provider)
511            .await
512            .map_err(MlsError::wrap("storing key package encapsulation"))?;
513
514        Ok(())
515    }
516
517    pub(crate) async fn save_identity(
518        &self,
519        keystore: &Connection,
520        id: Option<&ClientId>,
521        signature_scheme: SignatureScheme,
522        mut credential_bundle: CredentialBundle,
523    ) -> Result<CredentialBundle> {
524        let mut guard = self.inner.write().await;
525        let SessionInner {
526            id: existing_id,
527            identities,
528            ..
529        } = guard.as_mut().ok_or(Error::MlsNotInitialized)?;
530
531        let id = id.unwrap_or(existing_id);
532
533        let credential = credential_bundle
534            .credential
535            .tls_serialize_detached()
536            .map_err(Error::tls_serialize("credential bundle"))?;
537        let credential = MlsCredential {
538            id: id.clone().into(),
539            credential,
540            created_at: 0,
541        };
542
543        let credential = keystore
544            .save(credential)
545            .await
546            .map_err(KeystoreError::wrap("saving credential"))?;
547
548        let sign_kp = MlsSignatureKeyPair::new(
549            signature_scheme,
550            credential_bundle.signature_key.to_public_vec(),
551            credential_bundle
552                .signature_key
553                .tls_serialize_detached()
554                .map_err(Error::tls_serialize("signature keypair"))?,
555            id.clone().into(),
556        );
557        keystore.save(sign_kp).await.map_err(|e| match e {
558            CryptoKeystoreError::AlreadyExists => Error::CredentialBundleConflict,
559            _ => KeystoreError::wrap("saving mls signature key pair")(e).into(),
560        })?;
561
562        // set the creation date of the signature keypair which is the same for the CredentialBundle
563        credential_bundle.created_at = credential.created_at;
564
565        identities
566            .push_credential_bundle(signature_scheme, credential_bundle.clone())
567            .await?;
568
569        Ok(credential_bundle)
570    }
571
572    /// Retrieves the client's client id. This is free-form and not inspected.
573    pub async fn id(&self) -> Result<ClientId> {
574        match self.inner.read().await.deref() {
575            None => Err(Error::MlsNotInitialized),
576            Some(SessionInner { id, .. }) => Ok(id.clone()),
577        }
578    }
579
580    /// Returns whether this client is E2EI capable
581    pub async fn is_e2ei_capable(&self) -> bool {
582        match self.inner.read().await.deref() {
583            None => false,
584            Some(SessionInner { identities, .. }) => identities
585                .iter()
586                .any(|(_, cred)| cred.credential().credential_type() == CredentialType::X509),
587        }
588    }
589
590    pub(crate) async fn get_most_recent_or_create_credential_bundle(
591        &self,
592        backend: &MlsCryptoProvider,
593        sc: SignatureScheme,
594        ct: MlsCredentialType,
595    ) -> Result<Arc<CredentialBundle>> {
596        match ct {
597            MlsCredentialType::Basic => {
598                self.init_basic_credential_bundle_if_missing(backend, sc).await?;
599                self.find_most_recent_credential_bundle(sc, ct).await
600            }
601            MlsCredentialType::X509 => self
602                .find_most_recent_credential_bundle(sc, ct)
603                .await
604                .map_err(|e| match e {
605                    Error::CredentialNotFound(_) => LeafError::E2eiEnrollmentNotDone.into(),
606                    _ => e,
607                }),
608        }
609    }
610
611    pub(crate) async fn init_basic_credential_bundle_if_missing(
612        &self,
613        backend: &MlsCryptoProvider,
614        sc: SignatureScheme,
615    ) -> Result<()> {
616        let existing_cb = self
617            .find_most_recent_credential_bundle(sc, MlsCredentialType::Basic)
618            .await;
619        if matches!(existing_cb, Err(Error::CredentialNotFound(_))) {
620            let id = self.id().await?;
621            debug!(id:% = &id; "Initializing basic credential bundle");
622            let cb = Self::new_basic_credential_bundle(&id, sc, backend)?;
623            self.save_identity(&backend.keystore(), None, sc, cb).await?;
624        }
625        Ok(())
626    }
627
628    pub(crate) async fn save_new_x509_credential_bundle(
629        &self,
630        keystore: &Connection,
631        sc: SignatureScheme,
632        cb: CertificateBundle,
633    ) -> Result<CredentialBundle> {
634        let id = cb
635            .get_client_id()
636            .map_err(RecursiveError::mls_credential("getting client id"))?;
637        let cb = Self::new_x509_credential_bundle(cb)?;
638        self.save_identity(keystore, Some(&id), sc, cb).await
639    }
640}
641
642#[cfg(test)]
643mod tests {
644    use super::*;
645    use crate::test_utils::*;
646    use crate::transaction_context::test_utils::EntitiesCount;
647    use core_crypto_keystore::connection::{ConnectionType, DatabaseKey, FetchFromDatabase};
648    use core_crypto_keystore::entities::*;
649    use mls_crypto_provider::MlsCryptoProvider;
650
651    impl Session {
652        // test functions are not held to the same documentation standard as proper functions
653        #![allow(missing_docs)]
654
655        pub async fn random_generate(
656            &self,
657            case: &crate::test_utils::TestContext,
658            signer: Option<&crate::test_utils::x509::X509Certificate>,
659            provision: bool,
660        ) -> Result<()> {
661            self.reset().await;
662            let user_uuid = uuid::Uuid::new_v4();
663            let rnd_id = rand::random::<usize>();
664            let client_id = format!("{}:{rnd_id:x}@members.wire.com", user_uuid.hyphenated());
665            let identity = match case.credential_type {
666                MlsCredentialType::Basic => ClientIdentifier::Basic(client_id.as_str().into()),
667                MlsCredentialType::X509 => {
668                    let signer = signer.expect("Missing intermediate CA");
669                    CertificateBundle::rand_identifier(&client_id, &[signer])
670                }
671            };
672            let nb_key_package = if provision {
673                crate::prelude::INITIAL_KEYING_MATERIAL_COUNT
674            } else {
675                0
676            };
677            let backend = self.crypto_provider.clone();
678            self.generate(identity, &backend, &[case.ciphersuite()], nb_key_package)
679                .await?;
680            Ok(())
681        }
682
683        pub async fn find_keypackages(&self, backend: &MlsCryptoProvider) -> Result<Vec<openmls::prelude::KeyPackage>> {
684            use core_crypto_keystore::CryptoKeystoreMls as _;
685            let kps = backend
686                .key_store()
687                .mls_fetch_keypackages::<openmls::prelude::KeyPackage>(u32::MAX)
688                .await
689                .map_err(KeystoreError::wrap("fetching mls keypackages"))?;
690            Ok(kps)
691        }
692
693        pub(crate) async fn generate_one_keypackage(
694            &self,
695            backend: &MlsCryptoProvider,
696            cs: MlsCiphersuite,
697            ct: MlsCredentialType,
698        ) -> Result<openmls::prelude::KeyPackage> {
699            let cb = self
700                .find_most_recent_credential_bundle(cs.signature_algorithm(), ct)
701                .await?;
702            self.generate_one_keypackage_from_credential_bundle(backend, cs, &cb)
703                .await
704        }
705
706        /// Count the entities
707        pub async fn count_entities(&self) -> EntitiesCount {
708            let keystore = self.crypto_provider.keystore();
709            let credential = keystore.count::<MlsCredential>().await.unwrap();
710            let encryption_keypair = keystore.count::<MlsEncryptionKeyPair>().await.unwrap();
711            let epoch_encryption_keypair = keystore.count::<MlsEpochEncryptionKeyPair>().await.unwrap();
712            let enrollment = keystore.count::<E2eiEnrollment>().await.unwrap();
713            let group = keystore.count::<PersistedMlsGroup>().await.unwrap();
714            let hpke_private_key = keystore.count::<MlsHpkePrivateKey>().await.unwrap();
715            let key_package = keystore.count::<MlsKeyPackage>().await.unwrap();
716            let pending_group = keystore.count::<PersistedMlsPendingGroup>().await.unwrap();
717            let pending_messages = keystore.count::<MlsPendingMessage>().await.unwrap();
718            let psk_bundle = keystore.count::<MlsPskBundle>().await.unwrap();
719            let signature_keypair = keystore.count::<MlsSignatureKeyPair>().await.unwrap();
720            EntitiesCount {
721                credential,
722                encryption_keypair,
723                epoch_encryption_keypair,
724                enrollment,
725                group,
726                hpke_private_key,
727                key_package,
728                pending_group,
729                pending_messages,
730                psk_bundle,
731                signature_keypair,
732            }
733        }
734    }
735
736    #[apply(all_cred_cipher)]
737    async fn can_generate_session(case: TestContext) {
738        let [alice] = case.sessions().await;
739        let key = DatabaseKey::generate();
740        let key_store = CryptoKeystore::open(ConnectionType::InMemory, &key).await.unwrap();
741        let backend = MlsCryptoProvider::builder().key_store(key_store).build();
742        let x509_test_chain = if case.is_x509() {
743            let x509_test_chain = crate::test_utils::x509::X509TestChain::init_empty(case.signature_scheme());
744            x509_test_chain.register_with_provider(&backend).await;
745            Some(x509_test_chain)
746        } else {
747            None
748        };
749        backend.new_transaction().await.unwrap();
750        let session = alice.session().await;
751        session
752            .random_generate(
753                &case,
754                x509_test_chain.as_ref().map(|chain| chain.find_local_intermediate_ca()),
755                false,
756            )
757            .await
758            .unwrap();
759    }
760}