Skip to main content

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::{
6    entities::{StoredEncryptionKeyPair, StoredHpkePrivateKey, StoredKeypackage},
7    traits::FetchFromDatabase as _,
8};
9use openmls::prelude::{CryptoConfig, Lifetime};
10
11use super::{Error, Result, TransactionContext};
12use crate::{
13    ConversationConfiguration, CredentialRef, Keypackage, KeypackageRef, KeystoreError, RecursiveError,
14    mls::key_package::KeypackageExt as _,
15};
16
17/// Default lifetime of all generated Keypackages. Matches the limit defined in openmls
18pub const KEYPACKAGE_DEFAULT_LIFETIME: Duration = Duration::from_secs(60 * 60 * 24 * 28 * 3); // ~3 months
19
20impl TransactionContext {
21    /// Generate a [Keypackage] from the referenced credential.
22    ///
23    /// Makes no attempt to look up or prune existing keypackges.
24    ///
25    /// If `lifetime` is set, the keypackages will expire that span into the future.
26    /// If it is unset, [`KEYPACKAGE_DEFAULT_LIFETIME`]
27    /// is used.
28    ///
29    /// As a side effect, stores the keypackages and some related data in the keystore.
30    pub async fn generate_key_package(
31        &self,
32        credential_ref: &CredentialRef,
33        lifetime: Option<Duration>,
34    ) -> Result<Keypackage> {
35        let inner = self.inner().await?;
36        let lifetime = Lifetime::new(lifetime.unwrap_or(KEYPACKAGE_DEFAULT_LIFETIME).as_secs());
37        let credential = credential_ref
38            .load(&inner.transaction)
39            .await
40            .map_err(RecursiveError::mls_credential_ref("loading credential"))?;
41        let config = CryptoConfig {
42            ciphersuite: credential.cipher_suite.into(),
43            version: openmls::versions::ProtocolVersion::default(),
44        };
45
46        Keypackage::builder()
47            .leaf_node_capabilities(ConversationConfiguration::default_leaf_capabilities())
48            .key_package_lifetime(lifetime)
49            .build(
50                config,
51                &self.crypto_provider().await?,
52                &credential.signature_key_pair,
53                credential.to_mls_credential_with_key(),
54            )
55            .await
56            .map_err(Error::key_package_new())
57    }
58
59    /// Get all [`KeypackageRef`]s known to the keystore.
60    pub async fn get_key_package_refs(&self) -> Result<Vec<KeypackageRef>> {
61        let session = self.session().await?;
62        session
63            .get_keypackage_refs()
64            .await
65            .map_err(RecursiveError::mls_client(
66                "getting all key package refs for transaction",
67            ))
68            .map_err(Into::into)
69    }
70
71    /// Remove one [`Keypackage`] from the database.
72    ///
73    /// Succeeds silently if the keypackage does not exist in the database.
74    ///
75    /// Implementation note: this must first load and deserialize the keypackage,
76    /// then remove items from three distinct tables.
77    pub async fn remove_key_package(&self, kp_ref: &KeypackageRef) -> Result<()> {
78        let Some(kp) = self
79            .session()
80            .await?
81            .load_key_package(kp_ref)
82            .await
83            .map_err(RecursiveError::mls_client("loading key packages on session"))?
84        else {
85            return Ok(());
86        };
87
88        let inner = self.inner().await?;
89        inner
90            .transaction
91            .remove_borrowed::<StoredKeypackage>(kp_ref.hash_ref())
92            .await
93            .map_err(KeystoreError::wrap("removing key package from keystore"))?;
94        inner
95            .transaction
96            .remove_borrowed::<StoredHpkePrivateKey>(kp.hpke_init_key().as_slice())
97            .await
98            .map_err(KeystoreError::wrap("removing private key from keystore"))?;
99        inner
100            .transaction
101            .remove_borrowed::<StoredEncryptionKeyPair>(kp.leaf_node().encryption_key().as_slice())
102            .await
103            .map_err(KeystoreError::wrap("removing encryption keypair from keystore"))?;
104
105        Ok(())
106    }
107
108    /// Remove all keypackages associated with this credential.
109    ///
110    /// This is fairly expensive as it must first load all keypackages, then delete those matching the credential.
111    ///
112    /// Implementation note: once it makes it as far as having a list of keypackages, does _not_ short-circuit
113    /// if removing one returns an error. In that case, only the first produced error is returned.
114    /// This helps ensure that as many keypackages for the given credential ref are removed as possible.
115    pub async fn remove_key_packages_for(&self, credential_ref: &CredentialRef) -> Result<()> {
116        let inner = self.inner().await?;
117        let credential = credential_ref
118            .load(&inner.transaction)
119            .await
120            .map_err(RecursiveError::mls_credential_ref("loading credential"))?;
121        let signature_public_key = credential.signature_key_pair.public();
122
123        let mut first_err = None;
124        macro_rules! try_retain_err {
125            ($e:expr) => {
126                match $e {
127                    Err(err) => {
128                        if first_err.is_none() {
129                            first_err = Some(Error::from(err));
130                        }
131                        continue;
132                    }
133                    Ok(val) => val,
134                }
135            };
136        }
137
138        let session = self.session().await?;
139        for keypackage in session
140            .get_key_packages()
141            .await
142            .map_err(RecursiveError::mls_client("loading key packages"))?
143            .into_iter()
144            .filter(|keypackage| keypackage.leaf_node().signature_key().as_slice() == signature_public_key)
145        {
146            let kp_ref = try_retain_err!(keypackage.make_ref());
147            try_retain_err!(self.remove_key_package(&kp_ref).await);
148        }
149
150        match first_err {
151            None => Ok(()),
152            Some(err) => Err(err),
153        }
154    }
155
156    /// Restore a key package that was deleted in this transaction by removing it from the deleted list. This is
157    /// idempotent: if the key package doesn't exist in the deleted list, do nothing.
158    ///
159    /// NOTE: This will only work if the key package has been added in an earlier transaction, because otherwise,
160    /// removing its id from the deleted list wouldn't suffice: we'd need to replay its insertion.
161    pub(crate) async fn restore_key_package(&self, key_package_ref: &[u8]) -> Result<()> {
162        let inner = self.inner().await?;
163
164        inner
165            .transaction
166            .restore::<StoredKeypackage>(key_package_ref)
167            .await
168            .map_err(KeystoreError::wrap(
169                "restoring key package deleted in current transaction",
170            ))?;
171
172        let Some(key_package) = inner
173            .transaction
174            .get_borrowed::<StoredKeypackage>(key_package_ref)
175            .await
176            .map_err(KeystoreError::wrap("loading keypackage from database"))?
177            .map(|stored_keypackage| crate::mls::session::key_package::from_stored(&stored_keypackage))
178            .transpose()
179            .map_err(RecursiveError::mls_client("loading key package"))?
180        else {
181            return Ok(());
182        };
183
184        inner
185            .transaction
186            .restore::<StoredHpkePrivateKey>(key_package.hpke_init_key().as_slice())
187            .await
188            .map_err(KeystoreError::wrap("restoring private key from keystore"))?;
189        inner
190            .transaction
191            .restore::<StoredEncryptionKeyPair>(key_package.leaf_node().encryption_key().as_slice())
192            .await
193            .map_err(KeystoreError::wrap("restoring encryption keypair from keystore"))?;
194
195        Ok(())
196    }
197}