core_crypto/transaction_context/conversation/
welcome.rs1use std::borrow::BorrowMut as _;
4
5use openmls::prelude::{MlsMessageIn, MlsMessageInBody};
6use tls_codec::Deserialize as _;
7
8use super::{Error, Result, TransactionContext};
9use crate::{
10 MlsConversation, MlsConversationConfiguration, MlsCustomConfiguration, RecursiveError, WelcomeBundle,
11 mls::credential::crl::{extract_crl_uris_from_group, get_new_crl_distribution_points},
12};
13
14impl TransactionContext {
15 #[cfg_attr(test, crate::dispotent)]
28 pub async fn process_raw_welcome_message(
29 &self,
30 welcome: &[u8],
31 custom_cfg: MlsCustomConfiguration,
32 ) -> Result<WelcomeBundle> {
33 let mut cursor = std::io::Cursor::new(welcome);
34 let welcome =
35 MlsMessageIn::tls_deserialize(&mut cursor).map_err(Error::tls_deserialize("mls message in (welcome)"))?;
36 self.process_welcome_message(welcome, custom_cfg).await
37 }
38
39 #[cfg_attr(test, crate::dispotent)]
53 pub async fn process_welcome_message(
54 &self,
55 welcome: MlsMessageIn,
56 custom_cfg: MlsCustomConfiguration,
57 ) -> Result<WelcomeBundle> {
58 let MlsMessageInBody::Welcome(welcome) = welcome.extract() else {
59 return Err(Error::CallerError(
60 "the message provided to process_welcome_message was not a welcome message",
61 ));
62 };
63 let cs = welcome.ciphersuite().into();
64 let configuration = MlsConversationConfiguration {
65 ciphersuite: cs,
66 custom: custom_cfg,
67 ..Default::default()
68 };
69 let mls_provider = self
70 .mls_provider()
71 .await
72 .map_err(RecursiveError::transaction("getting mls provider"))?;
73 let mut mls_groups = self
74 .mls_groups()
75 .await
76 .map_err(RecursiveError::transaction("getting mls groups"))?;
77 let conversation =
78 MlsConversation::from_welcome_message(welcome, configuration, &mls_provider, mls_groups.borrow_mut())
79 .await
80 .map_err(RecursiveError::mls_conversation("creating conversation from welcome"))?;
81
82 let crl_new_distribution_points = get_new_crl_distribution_points(
84 &mls_provider,
85 extract_crl_uris_from_group(&conversation.group)
86 .map_err(RecursiveError::mls_credential("extracting crl uris from group"))?,
87 )
88 .await
89 .map_err(RecursiveError::mls_credential("getting new crl distribution points"))?;
90
91 let id = conversation.id.clone();
92 mls_groups.insert(&id, conversation);
93
94 Ok(WelcomeBundle {
95 id,
96 crl_new_distribution_points,
97 })
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use crate::test_utils::*;
105
106 #[apply(all_cred_cipher)]
107 async fn joining_from_welcome_should_prune_local_key_material(case: TestContext) {
108 let [alice, bob] = case.sessions().await;
109 Box::pin(async move {
110 let commit_guard = case.create_conversation([&alice]).await.invite([&bob]).await;
113
114 let prev_count = bob.transaction.count_entities().await;
116 commit_guard.notify_members().await;
118
119 let next_count = bob.transaction.count_entities().await;
122 assert_eq!(next_count.key_package, prev_count.key_package - 1);
123 assert_eq!(next_count.hpke_private_key, prev_count.hpke_private_key - 1);
124 assert_eq!(next_count.encryption_keypair, prev_count.encryption_keypair - 1);
125 })
126 .await;
127 }
128
129 #[apply(all_cred_cipher)]
130 async fn process_welcome_should_fail_when_already_exists(case: TestContext) {
131 use crate::LeafError;
132
133 let [alice, bob] = case.sessions().await;
134 Box::pin(async move {
135 let commit = case.create_conversation([&alice]).await.invite([&bob]).await;
136 let conversation = commit.conversation();
137 let id = conversation.id().clone();
138 bob
140 .transaction
141 .new_conversation(&id, case.credential_type, case.cfg.clone())
142 .await
143 .unwrap();
144
145 let welcome = conversation.transport().await.latest_welcome_message().await;
146 let join_welcome = bob
147 .transaction
148 .process_welcome_message(welcome.into(), case.custom_cfg())
149 .await;
150 assert!(
151 matches!(join_welcome.unwrap_err(),
152 Error::Recursive(crate::RecursiveError::MlsConversation { source, .. })
153 if matches!(*source, crate::mls::conversation::Error::Leaf(LeafError::ConversationAlreadyExists(ref i)) if i == &id
154 )
155 )
156 );
157 })
158 .await;
159 }
160}