core_crypto/transaction_context/conversation/
welcome.rs1use std::borrow::BorrowMut as _;
4
5use openmls::prelude::{MlsMessageIn, MlsMessageInBody};
6
7use super::{Error, Result, TransactionContext};
8use crate::{ConversationId, MlsConversation, MlsConversationConfiguration, RecursiveError};
9
10impl TransactionContext {
11 #[cfg_attr(test, crate::dispotent)]
24 pub async fn process_welcome_message(&self, welcome: impl Into<MlsMessageIn>) -> Result<ConversationId> {
25 let database = &self.database().await?;
26 let MlsMessageInBody::Welcome(welcome) = welcome.into().extract() else {
27 return Err(Error::CallerError(
28 "the message provided to process_welcome_message was not a welcome message",
29 ));
30 };
31 let cs = welcome.ciphersuite().into();
32 let configuration = MlsConversationConfiguration {
33 ciphersuite: cs,
34 ..Default::default()
35 };
36 let mls_provider = self
37 .mls_provider()
38 .await
39 .map_err(RecursiveError::transaction("getting mls provider"))?;
40 let mut mls_groups = self
41 .mls_groups()
42 .await
43 .map_err(RecursiveError::transaction("getting mls groups"))?;
44 let conversation = MlsConversation::from_welcome_message(
45 welcome,
46 configuration,
47 &mls_provider,
48 database,
49 mls_groups.borrow_mut(),
50 )
51 .await
52 .map_err(RecursiveError::mls_conversation("creating conversation from welcome"))?;
53
54 let id = conversation.id.clone();
55 mls_groups.insert(&id, conversation);
56
57 Ok(id)
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use super::*;
64 use crate::test_utils::*;
65
66 #[apply(all_cred_cipher)]
67 async fn joining_from_welcome_should_prune_local_key_material(case: TestContext) {
68 let [alice, bob] = case.sessions().await;
69 Box::pin(async move {
70 let commit_guard = case.create_conversation([&alice]).await.invite([&bob]).await;
73
74 let prev_count = bob.transaction.count_entities().await;
76 commit_guard.notify_members().await;
78
79 let next_count = bob.transaction.count_entities().await;
82 assert_eq!(next_count.key_package, prev_count.key_package - 1);
83 assert_eq!(next_count.hpke_private_key, prev_count.hpke_private_key - 1);
84 assert_eq!(next_count.encryption_keypair, prev_count.encryption_keypair - 1);
85 })
86 .await;
87 }
88
89 #[apply(all_cred_cipher)]
90 async fn process_welcome_should_fail_when_already_exists(case: TestContext) {
91 use crate::LeafError;
92
93 let [alice, bob] = case.sessions().await;
94 Box::pin(async move {
95 let credential_ref = &bob.initial_credential;
96 let commit = case.create_conversation([&alice]).await.invite([&bob]).await;
97 let conversation = commit.conversation();
98 let id = conversation.id().clone();
99 bob
101 .transaction
102 .new_conversation(&id, credential_ref, case.cfg.clone())
103 .await
104 .unwrap();
105
106 let welcome = conversation.transport().await.latest_welcome_message().await;
107 let join_welcome = bob
108 .transaction
109 .process_welcome_message(welcome)
110 .await;
111 assert!(
112 matches!(join_welcome.unwrap_err(),
113 Error::Recursive(crate::RecursiveError::MlsConversation { source, .. })
114 if matches!(*source, crate::mls::conversation::Error::Leaf(LeafError::ConversationAlreadyExists(ref i)) if i == &id
115 )
116 )
117 );
118 })
119 .await;
120 }
121}