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