core_crypto/mls/
key_package.rs

1//! MLS Keypackage helpers.
2//!
3//! Turns out that Openmls already defines both `KeyPackage` and `KeyPackageRef`, so there's not a ton of point
4//! redefining either of those structs here. That would just lead to a bunch of redefinition.
5//!
6//! On the other hand, the `KeyPackage` API isn't strictly ideal, so we improve on it here.
7
8use std::sync::LazyLock;
9
10use mls_crypto_provider::RustCrypto;
11pub use openmls::prelude::KeyPackage as Keypackage;
12use openmls::prelude::{KeyPackageRef as KpHashRef, Lifetime, SignatureScheme};
13
14use crate::{Ciphersuite, CredentialType, MlsError};
15
16static CRYPTO: LazyLock<RustCrypto> = LazyLock::new(RustCrypto::default);
17
18/// Extensions on the `KeyPackage` type for nicer usage patterns.
19pub trait KeypackageExt {
20    /// Make a "fat" [`KeypackageRef`] from this [`Keypackage`].
21    ///
22    /// This is a fairly inexpensive operation.
23    fn make_ref(&self) -> Result<KeypackageRef, MlsError>;
24
25    /// Returns the ciphersuite associated this this key package.
26    fn ciphersuite(&self) -> Ciphersuite;
27
28    /// Returns the credential type associated with this key package.
29    fn credential_type(&self) -> CredentialType;
30
31    /// Determines whether a keypackage is valid in the sense of the former `client_valid_keypackages_count` method.
32    ///
33    /// In practice, this just checks whether its lifetime (if present) has expired or not.
34    fn is_valid(&self) -> bool;
35}
36
37impl KeypackageExt for Keypackage {
38    fn make_ref(&self) -> Result<KeypackageRef, MlsError> {
39        let hash_ref = self
40            .hash_ref(&*CRYPTO)
41            .map_err(MlsError::wrap("computing keypackage hash ref"))?;
42
43        let ciphersuite = <Self as KeypackageExt>::ciphersuite(self);
44        let credential_type = self.credential_type();
45        let lifetime = self.leaf_node().life_time().cloned();
46
47        Ok(KeypackageRef {
48            hash_ref,
49            ciphersuite,
50            credential_type,
51            lifetime,
52        })
53    }
54
55    fn ciphersuite(&self) -> Ciphersuite {
56        <Keypackage>::ciphersuite(self).into()
57    }
58
59    fn credential_type(&self) -> CredentialType {
60        self.leaf_node()
61            .credential()
62            .credential_type()
63            .try_into()
64            .expect("we should only ever have a key package from a credential that this instance of CC understands")
65    }
66
67    fn is_valid(&self) -> bool {
68        self.leaf_node()
69            .life_time()
70            .is_none_or(|lifetime| lifetime.has_acceptable_range() && lifetime.is_valid())
71    }
72}
73
74/// A "fat" reference to a [`Keypackage`].
75///
76/// Contains the relevant hash, and also information about its ciphersuite, credential type, and lifetime.
77#[derive(Debug, Clone, PartialEq, Eq)]
78pub struct KeypackageRef {
79    hash_ref: KpHashRef,
80    ciphersuite: Ciphersuite,
81    credential_type: CredentialType,
82    lifetime: Option<Lifetime>,
83}
84
85impl KeypackageRef {
86    /// Get the bytes of the hash reference to a [`Keypackage`]
87    pub fn hash_ref(&self) -> &[u8] {
88        self.hash_ref.as_slice()
89    }
90
91    /// Get the ciphersuite associated with this key package ref.
92    pub fn ciphersuite(&self) -> Ciphersuite {
93        self.ciphersuite
94    }
95
96    /// Get the signature scheme associated wtih this key package ref.
97    pub fn signature_scheme(&self) -> SignatureScheme {
98        self.ciphersuite.signature_algorithm()
99    }
100
101    /// Get the credential type associated with this key package ref.
102    pub fn credential_type(&self) -> CredentialType {
103        self.credential_type
104    }
105
106    /// Get the lifetime associated with this key package ref.
107    pub fn lifetime(&self) -> Option<&Lifetime> {
108        self.lifetime.as_ref()
109    }
110
111    /// Determines whether this keypackage is valid in the sense of the former `client_valid_keypackages_count` method.
112    ///
113    /// In practice, this just checks whether its lifetime (if present) has expired or not.
114    pub fn is_valid(&self) -> bool {
115        self.lifetime()
116            .is_none_or(|lifetime| lifetime.has_acceptable_range() && lifetime.is_valid())
117    }
118}
119
120impl KeypackageExt for KeypackageRef {
121    fn make_ref(&self) -> Result<KeypackageRef, MlsError> {
122        Ok(self.clone())
123    }
124
125    fn ciphersuite(&self) -> Ciphersuite {
126        self.ciphersuite
127    }
128
129    fn credential_type(&self) -> CredentialType {
130        self.credential_type
131    }
132
133    fn is_valid(&self) -> bool {
134        <Self>::is_valid(self)
135    }
136}