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