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