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::StoredKeyPackage,
7    traits::{DeletableBySearchKey, EntityDeleteBorrowed as _},
8};
9use openmls::prelude::{CryptoConfig, Lifetime};
10
11use super::{Error, Result, TransactionContext};
12use crate::{ConversationConfiguration, CredentialRef, Keypackage, KeypackageRef, KeystoreError, RecursiveError};
13
14/// Default lifetime of all generated Keypackages. Matches the limit defined in openmls
15/// Default lifetime of a generated key package.
16///
17/// Note that this sits exactly on the maximum lifetime range openmls will accept for a leaf node:
18/// `Lifetime::new` extends `not_before` a further hour into the past for clock skew, and
19/// `MAX_LEAF_NODE_LIFETIME_RANGE_SECONDS` is that margin plus this value. Raising this constant, or
20/// passing a longer lifetime to [`TransactionContext::generate_key_package`], produces key packages
21/// which `KeypackageExt::is_valid` reports as invalid from the moment they are created.
22pub const KEYPACKAGE_DEFAULT_LIFETIME: Duration = Duration::from_secs(60 * 60 * 24 * 28 * 3); // ~3 months
23
24impl TransactionContext {
25    /// Generate a [Keypackage] from the referenced credential.
26    ///
27    /// Makes no attempt to look up or prune existing keypackges.
28    ///
29    /// If `lifetime` is set, the keypackages will expire that span into the future.
30    /// If it is unset, [`KEYPACKAGE_DEFAULT_LIFETIME`]
31    /// is used.
32    ///
33    /// As a side effect, stores the keypackages and some related data in the keystore.
34    pub async fn generate_key_package(
35        &self,
36        credential_ref: &CredentialRef,
37        lifetime: Option<Duration>,
38    ) -> Result<Keypackage> {
39        let inner = self.inner()?;
40        let lifetime = Lifetime::new(lifetime.unwrap_or(KEYPACKAGE_DEFAULT_LIFETIME).as_secs());
41        let credential = credential_ref
42            .load(&inner.transaction)
43            .await
44            .map_err(RecursiveError::context("loading credential"))?;
45        let config = CryptoConfig {
46            ciphersuite: credential.cipher_suite.into(),
47            version: openmls::versions::ProtocolVersion::default(),
48        };
49
50        Keypackage::builder()
51            .leaf_node_capabilities(ConversationConfiguration::default_leaf_capabilities())
52            .key_package_lifetime(lifetime)
53            .build(
54                config,
55                &self.crypto_provider().await?,
56                &credential.signature_key_pair,
57                credential.to_mls_credential_with_key(),
58            )
59            .await
60            .map_err(Error::key_package_new())
61    }
62
63    /// Get all [`KeypackageRef`]s known to the keystore.
64    pub async fn get_key_package_refs(&self) -> Result<Vec<KeypackageRef>> {
65        let session = self.session().await?;
66        session
67            .get_keypackage_refs()
68            .await
69            .map_err(RecursiveError::context("getting all key package refs for transaction"))
70            .map_err(Into::into)
71    }
72
73    /// Remove one [`Keypackage`] from the database.
74    ///
75    /// Succeeds silently if the keypackage does not exist in the database.
76    pub fn remove_key_package(&self, kp_ref: &KeypackageRef) -> Result<()> {
77        let inner = self.inner()?;
78        let tx = inner.transaction();
79
80        StoredKeyPackage::delete_borrowed(tx, kp_ref.hash_ref())
81            .map_err(KeystoreError::wrap("removing key package from keystore"))?;
82
83        Ok(())
84    }
85
86    /// Remove all keypackages associated with this credential.
87    pub fn remove_key_packages_for(&self, credential_ref: &CredentialRef) -> Result<()> {
88        let inner = self.inner()?;
89        let tx = inner.transaction();
90
91        StoredKeyPackage::delete_all_matching(tx, &credential_ref.keystore_pk())
92            .map_err(KeystoreError::wrap("removing key packages for credential"))?;
93
94        Ok(())
95    }
96}