core_crypto/mls/
mod.rs

1use crate::{ClientId, MlsConversation, Session, mls_provider::MlsCryptoProvider};
2
3pub(crate) mod ciphersuite;
4pub mod conversation;
5pub mod credential;
6mod error;
7pub mod key_package;
8pub(crate) mod proposal;
9pub(crate) mod session;
10
11use core_crypto_keystore::Database;
12pub use error::{Error, Result};
13pub use session::{EpochObserver, HistoryObserver};
14
15#[cfg_attr(target_family = "wasm", async_trait::async_trait(?Send))]
16#[cfg_attr(not(target_family = "wasm"), async_trait::async_trait)]
17pub(crate) trait HasSessionAndCrypto: Send {
18    async fn session(&self) -> Result<Session<Database>>;
19    async fn crypto_provider(&self) -> Result<MlsCryptoProvider>;
20}
21
22#[cfg(test)]
23mod tests {
24
25    use crate::{
26        CertificateBundle, ClientIdentifier, CoreCrypto, CredentialType,
27        test_utils::{x509::X509TestChain, *},
28        transaction_context::Error as TransactionError,
29    };
30
31    mod conversation_epoch {
32        use super::*;
33        use crate::mls::conversation::Conversation as _;
34
35        #[apply(all_cred_cipher)]
36        async fn can_get_newly_created_conversation_epoch(case: TestContext) {
37            let [session] = case.sessions().await;
38            let conversation = case.create_conversation([&session]).await;
39            let epoch = conversation.guard().await.epoch().await;
40            assert_eq!(epoch, 0);
41        }
42
43        #[apply(all_cred_cipher)]
44        async fn can_get_conversation_epoch(case: TestContext) {
45            let [alice, bob] = case.sessions().await;
46            Box::pin(async move {
47                let conversation = case.create_conversation([&alice, &bob]).await;
48                let epoch = conversation.guard().await.epoch().await;
49                assert_eq!(epoch, 1);
50            })
51            .await;
52        }
53
54        #[apply(all_cred_cipher)]
55        async fn conversation_not_found(case: TestContext) {
56            use crate::LeafError;
57            let [session] = case.sessions().await;
58            let id = conversation_id();
59            let err = session.transaction.conversation(&id).await.unwrap_err();
60            assert!(matches!(
61                err,
62                TransactionError::Leaf(LeafError::ConversationNotFound(i)) if i == id
63            ));
64        }
65    }
66
67    #[apply(all_cred_cipher)]
68    async fn create_conversation_should_fail_when_already_exists(case: TestContext) {
69        use crate::LeafError;
70
71        let [alice] = case.sessions().await;
72        Box::pin(async move {
73            let conversation = case.create_conversation([&alice]).await;
74            let id = conversation.id().clone();
75            let credentials =alice.session().await.find_credentials(Default::default()).await.expect("finding credentials");
76            let credential = credentials.first().expect("first credential");
77
78                // creating a conversation should first verify that the conversation does not already exist ; only then create it
79                let repeat_create = alice
80                    .transaction
81                    .new_conversation(&id, credential, case.cfg.clone())
82                    .await;
83                assert!(matches!(repeat_create.unwrap_err(), TransactionError::Leaf(LeafError::ConversationAlreadyExists(i)) if i == id));
84            })
85        .await;
86    }
87
88    #[apply(all_cred_cipher)]
89    async fn can_2_phase_init_central(mut case: TestContext) {
90        let db = case.create_persistent_db().await;
91        Box::pin(async move {
92            use std::sync::Arc;
93
94            use wire_e2e_identity::pki_env::PkiEnvironment;
95
96            use crate::{ClientId, Credential, test_utils::DummyPkiEnvironmentHooks};
97
98            let x509_test_chain = X509TestChain::init_empty(case.signature_scheme());
99
100            // phase 1: init without initialized mls_client
101            let cc = CoreCrypto::new(db.clone());
102            let context = cc.new_transaction().await.unwrap();
103
104            let hooks = Arc::new(DummyPkiEnvironmentHooks);
105            let pki_env = PkiEnvironment::new(hooks, db).await.expect("creating pki environment");
106            cc.set_pki_environment(Some(pki_env))
107                .await
108                .expect("setting pki environment");
109
110            x509_test_chain.register_with_central(&context).await;
111
112            // phase 2: init mls_client
113            let session_id = ClientId::from("alice");
114            let identifier = match case.credential_type {
115                CredentialType::Basic => ClientIdentifier::Basic(session_id),
116                CredentialType::X509 => {
117                    CertificateBundle::rand_identifier(&session_id, &[x509_test_chain.find_local_intermediate_ca()])
118                }
119            };
120            let session_id = identifier
121                .get_id()
122                .expect("get session_id from identifier")
123                .into_owned();
124            context
125                .mls_init(
126                    session_id.clone(),
127                    Arc::new(CoreCryptoTransportSuccessProvider::default()),
128                )
129                .await
130                .unwrap();
131
132            let credential = Credential::from_identifier(&identifier, case.ciphersuite()).unwrap();
133            let credential_ref = context.add_credential(credential).await.unwrap();
134
135            // expect mls_client to work
136            assert!(context.generate_keypackage(&credential_ref, None).await.is_ok());
137        })
138        .await
139    }
140}