core_crypto/transaction_context/
credential.rs

1use std::sync::Arc;
2
3use super::{Error, Result};
4use crate::{Credential, CredentialRef, MlsConversation, RecursiveError, transaction_context::TransactionContext};
5
6impl TransactionContext {
7    /// Add a credential to the database of this session without validating that its client ID matches the session
8    /// client id.
9    ///
10    /// This is rarely useful and should only be used when absolutely necessary. You'll know it if you need it.
11    ///
12    /// Prefer [`Self::add_credential`].
13    pub(crate) async fn add_credential_without_clientid_check(
14        &self,
15        mut credential: Credential,
16    ) -> Result<Arc<Credential>> {
17        let _credential_ref = credential
18            .save(&self.database().await?)
19            .await
20            .map_err(RecursiveError::mls_credential("saving credential"))?;
21
22        Ok(Arc::new(credential))
23    }
24    /// Add a credential to the database of this session.
25    pub async fn add_credential(&self, credential: Credential) -> Result<CredentialRef> {
26        let credential = self.add_credential_producing_arc(credential).await?;
27        Ok(CredentialRef::from_credential(&credential))
28    }
29
30    /// Add a credential to the database of this session.
31    ///
32    /// Returns the actual credential instance which was loaded from the DB.
33    /// This is a convenience for internal use and should _not_ be propagated across
34    /// the FFI boundary. Instead, use [`Self::add_credential`] to produce a [`CredentialRef`].
35    pub(crate) async fn add_credential_producing_arc(&self, credential: Credential) -> Result<Arc<Credential>> {
36        if *credential.client_id() != self.session().await?.id() {
37            return Err(Error::WrongCredential);
38        }
39
40        self.add_credential_without_clientid_check(credential).await
41    }
42
43    /// Remove a credential from the database of this session.
44    ///
45    /// First checks that the credential is not used in any conversation.
46    /// Removes both the credential itself and also any key packages which were generated from it.
47    pub async fn remove_credential(&self, credential_ref: &CredentialRef) -> Result<()> {
48        // setup
49        if *credential_ref.client_id() != self.session().await?.id() {
50            return Err(Error::WrongCredential);
51        }
52
53        let database = self.database().await?;
54
55        let credential = credential_ref
56            .load(&database)
57            .await
58            .map_err(RecursiveError::mls_credential_ref(
59                "loading all credentials from ref to remove from session identities",
60            ))?;
61
62        // in a perfect world, we'd pre-cache the mls credentials in a set structure of some sort for faster querying.
63        // unfortunately, `MlsCredential` is `!Hash` and `!Ord`, so both the standard sets are out.
64        // so whatever, linear scan over the credentials every time will have to do.
65
66        // ensure this credential is not in use by any conversation
67        for (conversation_id, conversation) in
68            MlsConversation::load_all(&database)
69                .await
70                .map_err(RecursiveError::mls_conversation(
71                    "loading all conversations to check if the credential to be removed is present",
72                ))?
73        {
74            let converation_credential = conversation
75                .own_mls_credential()
76                .map_err(RecursiveError::mls_conversation("geting conversation credential"))?;
77            if credential.mls_credential() == converation_credential {
78                return Err(Error::CredentialStillInUse(conversation_id));
79            }
80        }
81
82        // remove any key packages generated by this credential
83        self.remove_keypackages_for(credential_ref).await?;
84
85        // finally remove the credentials from the keystore so they won't be loaded on next mls_init
86        credential
87            .delete(&database)
88            .await
89            .map_err(RecursiveError::mls_credential("deleting credential from keystore"))
90            .map_err(Into::into)
91    }
92}