Skip to main content

core_crypto/transaction_context/credential/
mod.rs

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