core_crypto/transaction_context/
key_package.rs

1//! This module contains all transactional behavior related to key packages
2
3use std::time::Duration;
4
5use core_crypto_keystore::entities::{StoredEncryptionKeyPair, StoredHpkePrivateKey, StoredKeypackage};
6use openmls::prelude::{CryptoConfig, Lifetime};
7
8use super::{Error, Result, TransactionContext};
9use crate::{
10    CredentialRef, Keypackage, KeypackageRef, KeystoreError, MlsConversationConfiguration, RecursiveError,
11    mls::key_package::KeypackageExt as _,
12};
13
14/// Default lifetime of all generated Keypackages. Matches the limit defined in openmls
15pub const KEYPACKAGE_DEFAULT_LIFETIME: Duration = Duration::from_secs(60 * 60 * 24 * 28 * 3); // ~3 months
16
17impl TransactionContext {
18    /// Generate a [Keypackage] from the referenced credential.
19    ///
20    /// Makes no attempt to look up or prune existing keypackges.
21    ///
22    /// If `lifetime` is set, the keypackages will expire that span into the future.
23    /// If it is unset, [`KEYPACKAGE_DEFAULT_LIFETIME`]
24    /// is used.
25    ///
26    /// As a side effect, stores the keypackages and some related data in the keystore.
27    pub async fn generate_keypackage(
28        &self,
29        credential_ref: &CredentialRef,
30        lifetime: Option<Duration>,
31    ) -> Result<Keypackage> {
32        let lifetime = Lifetime::new(lifetime.unwrap_or(KEYPACKAGE_DEFAULT_LIFETIME).as_secs());
33        let database = &self.database().await?;
34        let credential = credential_ref
35            .load(database)
36            .await
37            .map_err(RecursiveError::mls_credential_ref("loading credential"))?;
38        let config = CryptoConfig {
39            ciphersuite: credential.ciphersuite.into(),
40            version: openmls::versions::ProtocolVersion::default(),
41        };
42
43        Keypackage::builder()
44            .leaf_node_capabilities(MlsConversationConfiguration::default_leaf_capabilities())
45            .key_package_lifetime(lifetime)
46            .build(
47                config,
48                &self.mls_provider().await?,
49                &credential.signature_key_pair,
50                credential.to_mls_credential_with_key(),
51            )
52            .await
53            .map_err(Error::keypackage_new())
54    }
55
56    /// Get all [`KeypackageRef`]s known to the keystore.
57    pub async fn get_keypackage_refs(&self) -> Result<Vec<KeypackageRef>> {
58        let session = self.session().await?;
59        session
60            .get_keypackage_refs()
61            .await
62            .map_err(RecursiveError::mls_client(
63                "getting all key package refs for transaction",
64            ))
65            .map_err(Into::into)
66    }
67
68    /// Remove one [`Keypackage`] from the database.
69    ///
70    /// Succeeds silently if the keypackage does not exist in the database.
71    ///
72    /// Implementation note: this must first load and deserialize the keypackage,
73    /// then remove items from three distinct tables.
74    pub async fn remove_keypackage(&self, kp_ref: &KeypackageRef) -> Result<()> {
75        let Some(kp) = self
76            .session()
77            .await?
78            .load_keypackage(kp_ref)
79            .await
80            .map_err(RecursiveError::mls_client("loading key packages on session"))?
81        else {
82            return Ok(());
83        };
84
85        let db = self.database().await?;
86        db.remove_borrowed::<StoredKeypackage>(kp_ref.hash_ref())
87            .await
88            .map_err(KeystoreError::wrap("removing key package from keystore"))?;
89        db.remove_borrowed::<StoredHpkePrivateKey>(kp.hpke_init_key().as_slice())
90            .await
91            .map_err(KeystoreError::wrap("removing private key from keystore"))?;
92        db.remove_borrowed::<StoredEncryptionKeyPair>(kp.leaf_node().encryption_key().as_slice())
93            .await
94            .map_err(KeystoreError::wrap("removing encryption keypair from keystore"))?;
95
96        Ok(())
97    }
98
99    /// Remove all keypackages associated with this credential.
100    ///
101    /// This is fairly expensive as it must first load all keypackages, then delete those matching the credential.
102    ///
103    /// Implementation note: once it makes it as far as having a list of keypackages, does _not_ short-circuit
104    /// if removing one returns an error. In that case, only the first produced error is returned.
105    /// This helps ensure that as many keypackages for the given credential ref are removed as possible.
106    pub async fn remove_keypackages_for(&self, credential_ref: &CredentialRef) -> Result<()> {
107        let database = &self.database().await?;
108        let credential = credential_ref
109            .load(database)
110            .await
111            .map_err(RecursiveError::mls_credential_ref("loading credential"))?;
112        let signature_public_key = credential.signature_key_pair.public();
113
114        let mut first_err = None;
115        macro_rules! try_retain_err {
116            ($e:expr) => {
117                match $e {
118                    Err(err) => {
119                        if first_err.is_none() {
120                            first_err = Some(Error::from(err));
121                        }
122                        continue;
123                    }
124                    Ok(val) => val,
125                }
126            };
127        }
128
129        let session = self.session().await?;
130        for keypackage in session
131            .get_keypackages()
132            .await
133            .map_err(RecursiveError::mls_client("loading key packages"))?
134            .into_iter()
135            .filter(|keypackage| keypackage.leaf_node().signature_key().as_slice() == signature_public_key)
136        {
137            let kp_ref = try_retain_err!(keypackage.make_ref());
138            try_retain_err!(self.remove_keypackage(&kp_ref).await);
139        }
140
141        match first_err {
142            None => Ok(()),
143            Some(err) => Err(err),
144        }
145    }
146}