Skip to main content

core_crypto/mls/session/
key_package.rs

1use std::sync::Arc;
2
3use core_crypto_keystore::{entities::StoredKeyPackage, traits::FetchFromDatabase};
4
5use super::Result;
6use crate::{Keypackage, KeypackageRef, KeystoreError, Session, mls::key_package::KeypackageExt};
7
8pub(crate) fn from_stored(stored_keypackage: Arc<StoredKeyPackage>) -> Result<Keypackage> {
9    core_crypto_keystore::deser::<Keypackage>(&stored_keypackage.key_package)
10        .map_err(KeystoreError::wrap("deserializing keypackage"))
11        .map_err(Into::into)
12}
13
14impl Session {
15    /// Get all [`Keypackage`]s in the database.
16    pub(crate) async fn get_key_packages(&self) -> Result<Vec<Keypackage>> {
17        let stored_keypackages = self
18            .database
19            .load_all::<StoredKeyPackage>()
20            .await
21            .map_err(KeystoreError::wrap("finding all keypackages"))?;
22
23        let keypackages = stored_keypackages
24            .iter().cloned()
25            .map(from_stored)
26            // if any ref from loading all fails to load now, skip it
27            // strictly we could panic, but this is safer--maybe someone removed it concurrently
28            .filter_map(|kp| kp.ok())
29            .collect();
30
31        Ok(keypackages)
32    }
33
34    /// Get all [`KeypackageRef`]s in the database.
35    pub async fn get_keypackage_refs(&self) -> Result<Vec<KeypackageRef>> {
36        self.get_key_packages()
37            .await?
38            .iter()
39            .map(|keypackage| keypackage.make_ref().map_err(Into::into))
40            .collect()
41    }
42
43    /// Load one [`Keypackage`] from its [`KeypackageRef`]
44    pub(crate) async fn load_key_package(&self, kp_ref: &KeypackageRef) -> Result<Option<Keypackage>> {
45        self.database
46            .get_borrowed::<StoredKeyPackage>(kp_ref.hash_ref())
47            .await
48            .map_err(KeystoreError::wrap("loading keypackage from database"))?
49            .map(from_stored)
50            .transpose()
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use std::time::Duration;
57
58    use openmls::prelude::{KeyPackageIn, ProtocolVersion};
59    use openmls_traits::types::VerifiableCiphersuite;
60
61    use crate::{ConversationConfiguration, mls::key_package::KeypackageExt as _, test_utils::*};
62
63    #[apply(all_cred_cipher)]
64    async fn can_assess_keypackage_expiration(case: TestContext) {
65        let [session] = case.sessions().await;
66
67        // 90-day standard expiration
68        let kp_std_exp = session.new_keypackage(&case).await;
69        assert!(kp_std_exp.is_valid());
70
71        // 1-second expiration
72        let kp_1s_exp = session
73            .new_keypackage_with_lifetime(&case, Some(Duration::from_secs(1)))
74            .await;
75
76        // Sleep 2 seconds to make sure we make the kp expire
77        smol::Timer::after(std::time::Duration::from_secs(2)).await;
78        assert!(!kp_1s_exp.is_valid());
79    }
80
81    #[apply(all_cred_cipher)]
82    async fn new_keypackage_has_correct_extensions(case: TestContext) {
83        let [cc] = case.sessions().await;
84        Box::pin(async move {
85            let kp = cc.new_keypackage(&case).await;
86
87            // make sure it's valid
88            let _ = KeyPackageIn::from(kp.clone())
89                .standalone_validate(
90                    &cc.transaction.crypto_provider().await.unwrap(),
91                    ProtocolVersion::Mls10,
92                    true,
93                )
94                .await
95                .unwrap();
96
97            // see https://www.rfc-editor.org/rfc/rfc9420.html#section-10-10
98            assert!(kp.extensions().is_empty());
99
100            assert_eq!(kp.leaf_node().capabilities().versions(), &[ProtocolVersion::Mls10]);
101            assert_eq!(
102                kp.leaf_node().capabilities().ciphersuites().to_vec(),
103                ConversationConfiguration::DEFAULT_SUPPORTED_CIPHERSUITES
104                    .iter()
105                    .map(|c| VerifiableCiphersuite::from(*c))
106                    .collect::<Vec<_>>()
107            );
108            assert!(kp.leaf_node().capabilities().proposals().is_empty());
109            assert!(kp.leaf_node().capabilities().extensions().is_empty());
110            assert_eq!(
111                kp.leaf_node().capabilities().credentials(),
112                ConversationConfiguration::DEFAULT_SUPPORTED_CREDENTIALS
113            );
114        })
115        .await
116    }
117
118    #[apply(all_cred_cipher)]
119    async fn can_store_and_load_key_packages(case: TestContext) {
120        let [cc] = case.sessions().await;
121
122        // generate a keypackage; automatically saves it
123        let kp = cc.new_keypackage(&case).await;
124
125        let all_keypackages = cc.session.read().await.get_key_packages().await.unwrap();
126        assert_eq!(all_keypackages[0], kp);
127
128        let kp_ref = kp.make_ref().unwrap();
129        let by_ref = cc
130            .session
131            .read()
132            .await
133            .load_key_package(&kp_ref)
134            .await
135            .unwrap()
136            .unwrap();
137        assert_eq!(kp, by_ref);
138    }
139}