Skip to main content

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, OpenMlsError, 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, OpenMlsError>;
19
20    /// Returns the ciphersuite associated this this key package.
21    fn cipher_suite(&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 unexpired.
27    ///
28    /// A key package with no lifetime is always valid. Otherwise it must both be unexpired *and*
29    /// have an acceptable lifetime range: openmls rejects a leaf node whose
30    /// `not_after - not_before` exceeds ~3 months (`MAX_LEAF_NODE_LIFETIME_RANGE_SECONDS`, which is
31    /// its default lifetime plus a 1h clock-skew margin). So a key package generated with a
32    /// lifetime longer than that is never valid, not merely expired.
33    fn is_valid(&self) -> bool;
34}
35
36impl KeypackageExt for Keypackage {
37    fn make_ref(&self) -> Result<KeypackageRef, OpenMlsError> {
38        let hash_ref = self
39            .hash_ref(CRYPTO.as_ref())
40            .map_err(OpenMlsError::wrap("computing keypackage hash ref"))?;
41
42        let cipher_suite = <Self as KeypackageExt>::cipher_suite(self);
43        let credential_type = self.credential_type();
44        let lifetime = self.leaf_node().life_time().cloned();
45
46        Ok(KeypackageRef {
47            hash_ref,
48            cipher_suite,
49            credential_type,
50            lifetime,
51        })
52    }
53
54    fn cipher_suite(&self) -> CipherSuite {
55        <Keypackage>::ciphersuite(self).into()
56    }
57
58    fn credential_type(&self) -> CredentialType {
59        self.leaf_node()
60            .credential()
61            .credential_type()
62            .try_into()
63            .expect("we should only ever have a key package from a credential that this instance of CC understands")
64    }
65
66    fn is_valid(&self) -> bool {
67        self.leaf_node()
68            .life_time()
69            .is_none_or(|lifetime| lifetime.has_acceptable_range() && lifetime.is_valid())
70    }
71}
72
73/// A "fat" reference to a [`Keypackage`].
74///
75/// Contains the relevant hash, and also information about its cipher suite, credential type, and lifetime.
76#[derive(Debug, Clone, PartialEq, Eq)]
77pub struct KeypackageRef {
78    hash_ref: KpHashRef,
79    cipher_suite: CipherSuite,
80    credential_type: CredentialType,
81    lifetime: Option<Lifetime>,
82}
83
84impl KeypackageRef {
85    /// Get the bytes of the hash reference to a [`Keypackage`]
86    pub fn hash_ref(&self) -> &[u8] {
87        self.hash_ref.as_slice()
88    }
89
90    /// Get the cipher suite associated with this key package ref.
91    pub fn cipher_suite(&self) -> CipherSuite {
92        self.cipher_suite
93    }
94
95    /// Get the signature scheme associated wtih this key package ref.
96    pub fn signature_scheme(&self) -> SignatureScheme {
97        self.cipher_suite.signature_algorithm()
98    }
99
100    /// Get the credential type associated with this key package ref.
101    pub fn credential_type(&self) -> CredentialType {
102        self.credential_type
103    }
104
105    /// Get the lifetime associated with this key package ref.
106    pub fn lifetime(&self) -> Option<&Lifetime> {
107        self.lifetime.as_ref()
108    }
109
110    /// Determines whether this keypackage is unexpired.
111    ///
112    /// A key package with no lifetime is always valid. Otherwise it must both be unexpired *and*
113    /// have an acceptable lifetime range: openmls rejects a leaf node whose
114    /// `not_after - not_before` exceeds ~3 months (`MAX_LEAF_NODE_LIFETIME_RANGE_SECONDS`, which is
115    /// its default lifetime plus a 1h clock-skew margin). So a key package generated with a
116    /// lifetime longer than that is never valid, not merely expired.
117    pub fn is_valid(&self) -> bool {
118        self.lifetime()
119            .is_none_or(|lifetime| lifetime.has_acceptable_range() && lifetime.is_valid())
120    }
121}
122
123impl KeypackageExt for KeypackageRef {
124    fn make_ref(&self) -> Result<KeypackageRef, OpenMlsError> {
125        Ok(self.clone())
126    }
127
128    fn cipher_suite(&self) -> CipherSuite {
129        self.cipher_suite
130    }
131
132    fn credential_type(&self) -> CredentialType {
133        self.credential_type
134    }
135
136    fn is_valid(&self) -> bool {
137        <Self>::is_valid(self)
138    }
139}