Skip to main content

core_crypto/mls/
mod.rs

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