core_crypto/mls/conversation/
wipe.rs

1use super::Result;
2use crate::{
3    MlsError,
4    context::CentralContext,
5    prelude::{ConversationId, MlsConversation},
6};
7use mls_crypto_provider::MlsCryptoProvider;
8use openmls_traits::OpenMlsCryptoProvider;
9
10impl CentralContext {
11    /// Destroys a group locally
12    ///
13    /// # Errors
14    /// KeyStore errors, such as IO
15    #[cfg_attr(test, crate::dispotent)]
16    pub async fn wipe_conversation(&self, id: &ConversationId) -> Result<()> {
17        self.conversation(id).await?.wipe().await
18    }
19}
20
21impl MlsConversation {
22    pub(crate) async fn wipe_associated_entities(&mut self, backend: &MlsCryptoProvider) -> Result<()> {
23        // the own client may or may not have generated an epoch keypair in the previous epoch
24        // Since it is a terminal operation, ignoring the error is fine here.
25        let _ = self.group.delete_previous_epoch_keypairs(backend).await;
26
27        let pending_proposals = self.group.pending_proposals().cloned().collect::<Vec<_>>();
28        for proposal in pending_proposals {
29            // Update proposals rekey the own leaf node. Hence the associated encryption keypair has to be cleared
30            self.group
31                .remove_pending_proposal(backend.key_store(), proposal.proposal_reference())
32                .await
33                .map_err(MlsError::wrap("removing pending proposal"))?;
34        }
35
36        Ok(())
37    }
38}
39
40#[cfg(test)]
41mod tests {
42    use wasm_bindgen_test::*;
43
44    use super::super::error::Error;
45    use crate::test_utils::*;
46
47    wasm_bindgen_test_configure!(run_in_browser);
48
49    #[apply(all_cred_cipher)]
50    #[wasm_bindgen_test]
51    async fn can_wipe_group(case: TestCase) {
52        run_test_with_central(case.clone(), move |[central]| {
53            Box::pin(async move {
54                let id = conversation_id();
55                central
56                    .context
57                    .new_conversation(&id, case.credential_type, case.cfg.clone())
58                    .await
59                    .unwrap();
60                assert!(central.get_conversation_unchecked(&id).await.group.is_active());
61
62                central.context.wipe_conversation(&id).await.unwrap();
63                assert!(!central.context.conversation_exists(&id).await.unwrap());
64            })
65        })
66        .await;
67    }
68
69    #[apply(all_cred_cipher)]
70    #[wasm_bindgen_test]
71    async fn cannot_wipe_group_non_existent(case: TestCase) {
72        use crate::LeafError;
73
74        run_test_with_central(case.clone(), move |[central]| {
75            Box::pin(async move {
76                let id = conversation_id();
77                let err = central.context.wipe_conversation(&id).await.unwrap_err();
78                assert!(matches!(err, Error::Leaf(LeafError::ConversationNotFound(conv_id)) if conv_id == id));
79            })
80        })
81        .await;
82    }
83
84    // should delete anything related to this conversation
85    #[apply(all_cred_cipher)]
86    #[wasm_bindgen_test]
87    async fn should_cascade_deletion(case: TestCase) {
88        run_test_with_client_ids(case.clone(), ["alice"], move |[cc]| {
89            Box::pin(async move {
90                let id = conversation_id();
91                cc.context
92                    .new_conversation(&id, case.credential_type, case.cfg.clone())
93                    .await
94                    .unwrap();
95                let initial_count = cc.context.count_entities().await;
96
97                cc.context.new_update_proposal(&id).await.unwrap();
98                let post_proposal_count = cc.context.count_entities().await;
99                assert_eq!(
100                    post_proposal_count.encryption_keypair,
101                    initial_count.encryption_keypair + 1
102                );
103
104                cc.context.wipe_conversation(&id).await.unwrap();
105
106                let final_count = cc.context.count_entities().await;
107                assert_eq!(final_count.group, 0);
108                assert_eq!(final_count.encryption_keypair, final_count.key_package);
109                assert_eq!(final_count.epoch_encryption_keypair, 0);
110            })
111        })
112        .await
113    }
114}