core_crypto/transaction_context/conversation/
welcome.rs1use openmls::prelude::{MlsMessageIn, MlsMessageInBody};
4
5use super::{Error, Result, TransactionContext};
6use crate::{ConversationConfiguration, ConversationId};
7
8impl TransactionContext {
9 #[cfg_attr(test, crate::dispotent)]
22 pub async fn process_welcome_message(&self, welcome: impl Into<MlsMessageIn>) -> Result<ConversationId> {
23 let MlsMessageInBody::Welcome(welcome) = welcome.into().extract() else {
24 return Err(Error::CallerError(
25 "the message provided to process_welcome_message was not a welcome message",
26 ));
27 };
28
29 let configuration = ConversationConfiguration {
30 cipher_suite: welcome.ciphersuite().into(),
31 ..Default::default()
32 };
33
34 let welcome_clone = welcome.clone();
35 let conversation_result = self
36 .persist_conversation_from_welcome_message(welcome_clone, configuration)
37 .await;
38
39 if conversation_result.is_err() {
40 for key_package_hash_ref in welcome.secrets().iter().map(|secret| secret.new_member().as_slice()) {
44 self.restore_key_package(key_package_hash_ref).await?;
45 }
46 }
47
48 let conversation = conversation_result?;
49 let id = conversation.id().to_owned();
50
51 Ok(id)
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use crate::test_utils::*;
58
59 #[apply(all_cred_cipher)]
60 async fn joining_from_welcome_should_prune_local_key_material(case: TestContext) {
61 let [alice, bob] = case.sessions().await;
62 Box::pin(async move {
63 let commit_guard = case.create_conversation([&alice]).await.invite([&bob]).await;
66
67 let prev_count = bob.transaction.count_entities().await;
69 commit_guard.notify_members().await;
71
72 let next_count = bob.transaction.count_entities().await;
75 assert_eq!(next_count.key_package, prev_count.key_package - 1);
76 assert_eq!(next_count.hpke_private_key, prev_count.hpke_private_key - 1);
77 assert_eq!(next_count.encryption_keypair, prev_count.encryption_keypair - 1);
78 })
79 .await;
80 }
81
82 #[apply(all_cred_cipher)]
83 async fn process_welcome_should_fail_when_already_exists(case: TestContext) {
84 use crate::LeafError;
85
86 let [alice, mut bob] = case.sessions().await;
87 Box::pin(async move {
88 let credential_ref = &bob.initial_credential;
89 let commit = case.create_conversation([&alice]).await.invite([&bob]).await;
90 let conversation = commit.conversation();
91 let id = conversation.id().clone();
92 bob
94 .transaction
95 .new_conversation(&id, credential_ref, case.cfg.clone())
96 .await
97 .unwrap();
98
99 let key_package_refs_before = bob.transaction.get_key_package_refs().await.unwrap();
101
102 let welcome = conversation.transport().await.latest_welcome_message().await;
103
104 bob.commit_transaction().await;
108
109 let join_welcome = bob
110 .transaction
111 .process_welcome_message(welcome)
112 .await;
113
114 let key_package_refs_after = bob.transaction.get_key_package_refs().await.unwrap();
116
117 assert!(!key_package_refs_before.is_empty());
118 assert_eq!(key_package_refs_before, key_package_refs_after);
119 assert!(innermost_source_matches!(join_welcome.unwrap_err(), LeafError::ConversationAlreadyExists(i) if i == &id));
120 })
121 .await;
122 }
123}