core_crypto/mls/conversation/
wipe.rs1use crate::context::CentralContext;
2use crate::prelude::{ConversationId, CryptoResult, MlsConversation, MlsError};
3use core_crypto_keystore::CryptoKeystoreMls;
4use mls_crypto_provider::MlsCryptoProvider;
5use openmls_traits::OpenMlsCryptoProvider;
6
7impl CentralContext {
8 #[cfg_attr(test, crate::dispotent)]
13 pub async fn wipe_conversation(&self, id: &ConversationId) -> CryptoResult<()> {
14 let provider = self.mls_provider().await?;
15 self.get_conversation(id)
16 .await?
17 .write()
18 .await
19 .wipe_associated_entities(&provider)
20 .await?;
21 provider.key_store().mls_group_delete(id).await?;
22 let _ = self.mls_groups().await?.remove(id);
23 Ok(())
24 }
25}
26
27impl MlsConversation {
28 async fn wipe_associated_entities(&mut self, backend: &MlsCryptoProvider) -> CryptoResult<()> {
29 let _ = self.group.delete_previous_epoch_keypairs(backend).await;
32
33 let pending_proposals = self.group.pending_proposals().cloned().collect::<Vec<_>>();
34 for proposal in pending_proposals {
35 self.group
37 .remove_pending_proposal(backend.key_store(), proposal.proposal_reference())
38 .await
39 .map_err(MlsError::from)?;
40 }
41
42 Ok(())
43 }
44}
45
46#[cfg(test)]
47mod tests {
48 use wasm_bindgen_test::*;
49
50 use crate::{prelude::CryptoError, test_utils::*};
51
52 wasm_bindgen_test_configure!(run_in_browser);
53
54 #[apply(all_cred_cipher)]
55 #[wasm_bindgen_test]
56 async fn can_wipe_group(case: TestCase) {
57 run_test_with_central(case.clone(), move |[central]| {
58 Box::pin(async move {
59 let id = conversation_id();
60 central
61 .context
62 .new_conversation(&id, case.credential_type, case.cfg.clone())
63 .await
64 .unwrap();
65 assert!(central.get_conversation_unchecked(&id).await.group.is_active());
66
67 central.context.wipe_conversation(&id).await.unwrap();
68 assert!(!central.context.conversation_exists(&id).await.unwrap());
69 })
70 })
71 .await;
72 }
73
74 #[apply(all_cred_cipher)]
75 #[wasm_bindgen_test]
76 async fn cannot_wipe_group_non_existent(case: TestCase) {
77 run_test_with_central(case.clone(), move |[central]| {
78 Box::pin(async move {
79 let id = conversation_id();
80 let err = central.context.wipe_conversation(&id).await.unwrap_err();
81 assert!(matches!(err, CryptoError::ConversationNotFound(conv_id) if conv_id == id));
82 })
83 })
84 .await;
85 }
86
87 #[apply(all_cred_cipher)]
89 #[wasm_bindgen_test]
90 async fn should_cascade_deletion(case: TestCase) {
91 run_test_with_client_ids(case.clone(), ["alice"], move |[cc]| {
92 Box::pin(async move {
93 let id = conversation_id();
94 cc.context
95 .new_conversation(&id, case.credential_type, case.cfg.clone())
96 .await
97 .unwrap();
98 let initial_count = cc.context.count_entities().await;
99
100 cc.context.new_update_proposal(&id).await.unwrap();
101 let post_proposal_count = cc.context.count_entities().await;
102 assert_eq!(
103 post_proposal_count.encryption_keypair,
104 initial_count.encryption_keypair + 1
105 );
106
107 cc.context.wipe_conversation(&id).await.unwrap();
108
109 let final_count = cc.context.count_entities().await;
110 assert_eq!(final_count.group, 0);
111 assert_eq!(final_count.encryption_keypair, final_count.key_package);
112 assert_eq!(final_count.epoch_encryption_keypair, 0);
113 })
114 })
115 .await
116 }
117}