core_crypto/mls/
mod.rs

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