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(crate) mod proposal;
10pub(crate) mod session;
11
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>;
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 mls::Session,
28 test_utils::{x509::X509TestChain, *},
29 transaction_context::Error as TransactionError,
30 };
31
32 mod conversation_epoch {
33 use super::*;
34 use crate::mls::conversation::Conversation as _;
35
36 #[apply(all_cred_cipher)]
37 async fn can_get_newly_created_conversation_epoch(case: TestContext) {
38 let [session] = case.sessions().await;
39 let conversation = case.create_conversation([&session]).await;
40 let epoch = conversation.guard().await.epoch().await;
41 assert_eq!(epoch, 0);
42 }
43
44 #[apply(all_cred_cipher)]
45 async fn can_get_conversation_epoch(case: TestContext) {
46 let [alice, bob] = case.sessions().await;
47 Box::pin(async move {
48 let conversation = case.create_conversation([&alice, &bob]).await;
49 let epoch = conversation.guard().await.epoch().await;
50 assert_eq!(epoch, 1);
51 })
52 .await;
53 }
54
55 #[apply(all_cred_cipher)]
56 async fn conversation_not_found(case: TestContext) {
57 use crate::LeafError;
58 let [session] = case.sessions().await;
59 let id = conversation_id();
60 let err = session.transaction.conversation(&id).await.unwrap_err();
61 assert!(matches!(
62 err,
63 TransactionError::Leaf(LeafError::ConversationNotFound(i)) if i == id
64 ));
65 }
66 }
67
68 mod invariants {
69 use super::*;
70
71 #[apply(all_cred_cipher)]
72 async fn can_create_from_valid_configuration(mut case: TestContext) {
73 let db = case.create_persistent_db().await;
74 Box::pin(async move {
75 let new_client_result = Session::try_new(&db).await;
76 assert!(new_client_result.is_ok())
77 })
78 .await
79 }
80 }
81
82 #[apply(all_cred_cipher)]
83 async fn create_conversation_should_fail_when_already_exists(case: TestContext) {
84 use crate::LeafError;
85
86 let [alice] = case.sessions().await;
87 Box::pin(async move {
88 let conversation = case.create_conversation([&alice]).await;
89 let id = conversation.id().clone();
90
91 let repeat_create = alice
93 .transaction
94 .new_conversation(&id, case.credential_type, case.cfg.clone())
95 .await;
96 assert!(matches!(repeat_create.unwrap_err(), TransactionError::Leaf(LeafError::ConversationAlreadyExists(i)) if i == id));
97 })
98 .await;
99 }
100
101 #[apply(all_cred_cipher)]
102 async fn can_fetch_client_public_key(mut case: TestContext) {
103 let db = case.create_persistent_db().await;
104 Box::pin(async move {
105 let result = Session::try_new(&db).await;
106 println!("{result:?}");
107 assert!(result.is_ok());
108 })
109 .await
110 }
111
112 #[apply(all_cred_cipher)]
113 async fn can_2_phase_init_central(mut case: TestContext) {
114 let db = case.create_persistent_db().await;
115 Box::pin(async move {
116 use crate::{ClientId, Credential};
117
118 let x509_test_chain = X509TestChain::init_empty(case.signature_scheme());
119
120 let client = Session::try_new(&db).await.unwrap();
122 let cc = CoreCrypto::from(client);
123 let context = cc.new_transaction().await.unwrap();
124 x509_test_chain.register_with_central(&context).await;
125
126 assert!(!context.session().await.unwrap().is_ready().await);
127 let client_id = ClientId::from("alice");
129 let identifier = match case.credential_type {
130 CredentialType::Basic => ClientIdentifier::Basic(client_id.clone()),
131 CredentialType::X509 => {
132 CertificateBundle::rand_identifier(&client_id, &[x509_test_chain.find_local_intermediate_ca()])
133 }
134 };
135 context
136 .mls_init(identifier.clone(), &[case.ciphersuite()])
137 .await
138 .unwrap();
139
140 let credential =
141 Credential::from_identifier(&identifier, case.signature_scheme(), &cc.mls.crypto_provider).unwrap();
142 cc.add_credential(credential).await.unwrap();
143
144 assert!(context.session().await.unwrap().is_ready().await);
145 assert_eq!(
147 context
148 .get_or_create_client_keypackages(case.ciphersuite(), case.credential_type, 2)
149 .await
150 .unwrap()
151 .len(),
152 2
153 );
154 })
155 .await
156 }
157}