core_crypto/transaction_context/credential/
mod.rs

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