openmls/key_packages/
mod.rs

1//! # Key Packages
2//!
3//! Key packages are pre-published public keys that carry information about a
4//! user, allowing for the asynchronous addition of clients to an MLS group.
5//!
6//! A key package object specifies:
7//!
8//! - A **protocol version** and ciphersuite that the client supports
9//! - A **public key** that others can use for key agreement
10//! - A **credential** authenticating the client's application-layer identity
11//! - A list of **extensions** for the key package (see
12//!   [Extensions](`mod@crate::extensions`) for details)
13//!
14//! Key packages are meant to be used only once and SHOULD NOT be reused,
15//! except as a last resort—i.e., when no other key package is available.
16//! Clients MAY generate and publish multiple key packages to support multiple
17//! ciphersuites.
18//!
19//! The HPKE init key MUST be a public key for the asymmetric encryption scheme
20//! defined by the ciphersuite. It MUST be unique among key packages created by
21//! the client. The entire structure is signed using the client's signature key.
22//! A key package object with an invalid signature field is considered malformed.
23//!
24//! ## Creating key package bundles
25//!
26//! Key package bundles are key packages that include their private key. A key
27//! package bundle can be created as follows:
28//!
29//! ```
30//! use openmls::{prelude::{*, tls_codec::*}};
31//! use openmls_rust_crypto::OpenMlsRustCrypto;
32//! use openmls_basic_credential::SignatureKeyPair;
33//!
34//! let ciphersuite = Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519;
35//! let provider = OpenMlsRustCrypto::default();
36//!
37//! let credential = BasicCredential::new("identity".into());
38//! let signer =
39//!     SignatureKeyPair::new(ciphersuite.signature_algorithm())
40//!         .expect("Error generating a signature key pair.");
41//! let credential_with_key = CredentialWithKey {
42//!     credential: credential.into(),
43//!     signature_key: signer.public().into(),
44//! };
45//! let key_package = KeyPackage::builder()
46//!     .build(
47//!         ciphersuite,
48//!         &provider,
49//!         &signer,
50//!         credential_with_key,
51//!     )
52//!     .unwrap();
53//! ```
54//!
55//! See [`KeyPackage`] for more details and other ways to create key packages.
56//!
57//! ## Loading key packages
58//!
59//! When getting key packages from another user the serialized bytes are parsed
60//! as follows;
61//!
62//! ```
63//! use openmls::prelude::{*, tls_codec::*};
64//! use openmls::test_utils::hex_to_bytes;
65//! use openmls_rust_crypto::OpenMlsRustCrypto;
66//!
67//! let provider = OpenMlsRustCrypto::default();
68//! let ciphersuite = Ciphersuite::MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519;
69//!
70//! let key_package_bytes = hex_to_bytes(
71//!         "0001000120D4F26FCA6EF6B1CA2FDD8DCAA501730FB003323AD8C781490B94782771\
72//!         B22216208E9BF17CE632EC753A9BFC624F275AA745ACD7316A5CF18B8E39CE71A80EE\
73//!         137205639176E415B378BA54B9E7C678FFAA860676CEFEDFA0DD3FF692F20AC7E2632\
74//!         0001086964656E74697479020001060001000200030200010C0001000200030004000\
75//!         50007020001010000000064A1986E00000000776DA97E004040A71F1B7A5F78D15C3F\
76//!         B215D811BADB0BBBD78B582D42E5C3672085699DCA5F90DAA57BB74A3A973789E7006\
77//!         887FCE85F0E64C19C1F26C28B5752B3C3312FF3040040407B5A96167512061E78414F\
78//!         E3F29B89FF2A954CB8E0A6E976EA039E0A1A0AB91B80664BDDC62BC8CBE64BC9242C4\
79//!         CDC33F56A10E425A384AED029C23E1D467C0E");
80//!
81//! let key_package_in = KeyPackageIn::tls_deserialize(&mut key_package_bytes.as_slice())
82//!     .expect("Could not deserialize KeyPackage");
83//!
84//! let key_package = key_package_in
85//!     .validate(provider.crypto(), ProtocolVersion::Mls10)
86//!     .expect("Invalid KeyPackage");
87//! ```
88//!
89//! See [`KeyPackage`] for more details on how to use key packages.
90
91use crate::{
92    ciphersuite::{
93        hash_ref::{make_key_package_ref, KeyPackageRef},
94        signable::*,
95        *,
96    },
97    credentials::*,
98    error::LibraryError,
99    extensions::{Extension, ExtensionType, Extensions, LastResortExtension},
100    storage::OpenMlsProvider,
101    treesync::{
102        node::{
103            encryption_keys::{EncryptionKeyPair, EncryptionPrivateKey},
104            leaf_node::{Capabilities, LeafNodeSource, NewLeafNodeParams, TreeInfoTbs},
105        },
106        LeafNode,
107    },
108    versions::ProtocolVersion,
109};
110use openmls_traits::{
111    crypto::OpenMlsCrypto, signatures::Signer, storage::StorageProvider, types::Ciphersuite,
112};
113use serde::{Deserialize, Serialize};
114use tls_codec::{
115    Serialize as TlsSerializeTrait, TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize,
116};
117
118// Private
119use errors::*;
120
121// Public
122pub mod errors;
123pub mod key_package_in;
124
125mod lifetime;
126
127// Tests
128#[cfg(test)]
129pub(crate) mod tests;
130
131// Public types
132pub use key_package_in::KeyPackageIn;
133pub use lifetime::Lifetime;
134
135/// The unsigned payload of a key package.
136/// Any modification must happen on this unsigned struct. Use `sign` to get a
137/// signed key package.
138///
139/// ```text
140/// struct {
141///     ProtocolVersion version;
142///     CipherSuite cipher_suite;
143///     HPKEPublicKey init_key;
144///     LeafNode leaf_node;
145///     Extension extensions<V>;
146/// } KeyPackageTBS;
147/// ```
148#[derive(Debug, Clone, PartialEq, TlsSize, TlsSerialize, Serialize, Deserialize)]
149struct KeyPackageTbs {
150    protocol_version: ProtocolVersion,
151    ciphersuite: Ciphersuite,
152    init_key: InitKey,
153    leaf_node: LeafNode,
154    extensions: Extensions,
155}
156
157impl Signable for KeyPackageTbs {
158    type SignedOutput = KeyPackage;
159
160    fn unsigned_payload(&self) -> Result<Vec<u8>, tls_codec::Error> {
161        self.tls_serialize_detached()
162    }
163
164    fn label(&self) -> &str {
165        SIGNATURE_KEY_PACKAGE_LABEL
166    }
167}
168
169impl From<KeyPackage> for KeyPackageTbs {
170    fn from(kp: KeyPackage) -> Self {
171        kp.payload
172    }
173}
174
175/// The key package struct.
176#[derive(Debug, Clone, Serialize, Deserialize, TlsSize, TlsSerialize)]
177pub struct KeyPackage {
178    payload: KeyPackageTbs,
179    signature: Signature,
180}
181
182impl PartialEq for KeyPackage {
183    fn eq(&self, other: &Self) -> bool {
184        // We ignore the signature in the comparison. The same key package
185        // may have different, valid signatures.
186        self.payload == other.payload
187    }
188}
189
190impl SignedStruct<KeyPackageTbs> for KeyPackage {
191    fn from_payload(payload: KeyPackageTbs, signature: Signature) -> Self {
192        Self { payload, signature }
193    }
194}
195
196const SIGNATURE_KEY_PACKAGE_LABEL: &str = "KeyPackageTBS";
197
198/// Helper struct containing the results of building a new [`KeyPackage`].
199pub(crate) struct KeyPackageCreationResult {
200    pub key_package: KeyPackage,
201    pub encryption_keypair: EncryptionKeyPair,
202    pub init_private_key: HpkePrivateKey,
203}
204
205/// Init key for HPKE.
206#[derive(
207    Debug,
208    Clone,
209    PartialEq,
210    TlsSize,
211    TlsSerialize,
212    Serialize,
213    Deserialize,
214    TlsDeserialize,
215    TlsDeserializeBytes,
216)]
217pub struct InitKey {
218    key: HpkePublicKey,
219}
220
221impl InitKey {
222    /// Return the internal [`HpkePublicKey`].
223    pub fn key(&self) -> &HpkePublicKey {
224        &self.key
225    }
226
227    /// Return the internal [`HpkePublicKey`] as a slice.
228    pub fn as_slice(&self) -> &[u8] {
229        self.key.as_slice()
230    }
231}
232
233impl From<Vec<u8>> for InitKey {
234    fn from(key: Vec<u8>) -> Self {
235        Self {
236            key: HpkePublicKey::from(key),
237        }
238    }
239}
240
241impl From<HpkePublicKey> for InitKey {
242    fn from(key: HpkePublicKey) -> Self {
243        Self { key }
244    }
245}
246
247// Public `KeyPackage` functions.
248impl KeyPackage {
249    /// Create a key package builder.
250    ///
251    /// This is provided for convenience. You can also use [`KeyPackageBuilder::new`].
252    pub fn builder() -> KeyPackageBuilder {
253        KeyPackageBuilder::new()
254    }
255
256    #[allow(clippy::too_many_arguments)]
257    /// Create a new key package for the given `ciphersuite` and `identity`.
258    pub(crate) fn create(
259        ciphersuite: Ciphersuite,
260        provider: &impl OpenMlsProvider,
261        signer: &impl Signer,
262        credential_with_key: CredentialWithKey,
263        lifetime: Lifetime,
264        extensions: Extensions,
265        leaf_node_capabilities: Capabilities,
266        leaf_node_extensions: Extensions,
267    ) -> Result<KeyPackageCreationResult, KeyPackageNewError> {
268        if ciphersuite.signature_algorithm() != signer.signature_scheme() {
269            return Err(KeyPackageNewError::CiphersuiteSignatureSchemeMismatch);
270        }
271
272        // Create a new HPKE key pair
273        let ikm = Secret::random(ciphersuite, provider.rand())
274            .map_err(LibraryError::unexpected_crypto_error)?;
275        let init_key = provider
276            .crypto()
277            .derive_hpke_keypair(ciphersuite.hpke_config(), ikm.as_slice())
278            .map_err(|e| {
279                KeyPackageNewError::LibraryError(LibraryError::unexpected_crypto_error(e))
280            })?;
281        let (key_package, encryption_keypair) = Self::new_from_keys(
282            ciphersuite,
283            provider,
284            signer,
285            credential_with_key,
286            lifetime,
287            extensions,
288            leaf_node_capabilities,
289            leaf_node_extensions,
290            init_key.public.into(),
291        )?;
292
293        Ok(KeyPackageCreationResult {
294            key_package,
295            encryption_keypair,
296            init_private_key: init_key.private,
297        })
298    }
299
300    /// Create a new key package for the given `ciphersuite` and `identity`.
301    ///
302    /// The HPKE init key must have been generated before and the private part
303    /// has to be stored in the key store.
304    ///
305    /// This function returns the new [`KeyPackage`] as well as the
306    /// encryption key ([`HpkeKeyPair`]) of the leaf node.
307    ///
308    /// The caller is responsible for storing the new values.
309    #[allow(clippy::too_many_arguments)]
310    fn new_from_keys(
311        ciphersuite: Ciphersuite,
312        provider: &impl OpenMlsProvider,
313        signer: &impl Signer,
314        credential_with_key: CredentialWithKey,
315        lifetime: Lifetime,
316        extensions: Extensions,
317        capabilities: Capabilities,
318        leaf_node_extensions: Extensions,
319        init_key: InitKey,
320    ) -> Result<(Self, EncryptionKeyPair), KeyPackageNewError> {
321        // We don't need the private key here. It's stored in the key store for
322        // use later when creating a group with this key package.
323
324        let new_leaf_node_params = NewLeafNodeParams {
325            ciphersuite,
326            leaf_node_source: LeafNodeSource::KeyPackage(lifetime),
327            credential_with_key,
328            capabilities,
329            extensions: leaf_node_extensions,
330            tree_info_tbs: TreeInfoTbs::KeyPackage,
331        };
332
333        let (leaf_node, encryption_key_pair) =
334            LeafNode::new(provider, signer, new_leaf_node_params)?;
335
336        let key_package_tbs = KeyPackageTbs {
337            protocol_version: ProtocolVersion::default(),
338            ciphersuite,
339            init_key,
340            leaf_node,
341            extensions,
342        };
343
344        let key_package = key_package_tbs.sign(signer)?;
345
346        Ok((key_package, encryption_key_pair))
347    }
348
349    /// Get a reference to the extensions of this key package.
350    pub fn extensions(&self) -> &Extensions {
351        &self.payload.extensions
352    }
353
354    /// Check whether the this key package supports all the required extensions
355    /// in the provided list.
356    pub fn check_extension_support(
357        &self,
358        required_extensions: &[ExtensionType],
359    ) -> Result<(), KeyPackageExtensionSupportError> {
360        for required_extension in required_extensions.iter() {
361            if !self.extensions().contains(*required_extension) {
362                return Err(KeyPackageExtensionSupportError::UnsupportedExtension);
363            }
364        }
365
366        Ok(())
367    }
368
369    /// Compute the [`KeyPackageRef`] of this [`KeyPackage`].
370    /// The [`KeyPackageRef`] is used to identify a new member that should get
371    /// added to a group.
372    pub fn hash_ref(&self, crypto: &impl OpenMlsCrypto) -> Result<KeyPackageRef, LibraryError> {
373        make_key_package_ref(
374            &self
375                .tls_serialize_detached()
376                .map_err(LibraryError::missing_bound_check)?,
377            self.payload.ciphersuite,
378            crypto,
379        )
380        .map_err(LibraryError::unexpected_crypto_error)
381    }
382
383    /// Get the [`Ciphersuite`].
384    pub fn ciphersuite(&self) -> Ciphersuite {
385        self.payload.ciphersuite
386    }
387
388    /// Get the [`LeafNode`] reference.
389    pub fn leaf_node(&self) -> &LeafNode {
390        &self.payload.leaf_node
391    }
392
393    /// Get the public HPKE init key of this key package.
394    pub fn hpke_init_key(&self) -> &InitKey {
395        &self.payload.init_key
396    }
397
398    /// Check if this KeyPackage is a last resort key package.
399    pub fn last_resort(&self) -> bool {
400        self.payload.extensions.contains(ExtensionType::LastResort)
401    }
402
403    /// Get the lifetime of the KeyPackage
404    pub fn life_time(&self) -> &Lifetime {
405        // Leaf nodes contain a lifetime if an only if they are inside a KeyPackage. Since we are
406        // in a KeyPackage, this can never be None and unwrap is safe.
407        // TODO: get rid of the unwrap, see https://github.com/openmls/openmls/issues/1663.
408        self.payload.leaf_node.life_time().unwrap()
409    }
410}
411
412/// Crate visible `KeyPackage` functions.
413impl KeyPackage {
414    /// Get the `ProtocolVersion`.
415    pub(crate) fn protocol_version(&self) -> ProtocolVersion {
416        self.payload.protocol_version
417    }
418}
419
420/// Builder that helps creating (and configuring) a [`KeyPackage`].
421#[derive(Default, Debug, Clone, Serialize, Deserialize)]
422pub struct KeyPackageBuilder {
423    key_package_lifetime: Option<Lifetime>,
424    key_package_extensions: Option<Extensions>,
425    leaf_node_capabilities: Option<Capabilities>,
426    leaf_node_extensions: Option<Extensions>,
427    last_resort: bool,
428}
429
430impl KeyPackageBuilder {
431    /// Create a key package builder.
432    pub fn new() -> Self {
433        Self {
434            key_package_lifetime: None,
435            key_package_extensions: None,
436            leaf_node_capabilities: None,
437            leaf_node_extensions: None,
438            last_resort: false,
439        }
440    }
441
442    /// Set the key package lifetime.
443    pub fn key_package_lifetime(mut self, lifetime: Lifetime) -> Self {
444        self.key_package_lifetime.replace(lifetime);
445        self
446    }
447
448    /// Set the key package extensions.
449    pub fn key_package_extensions(mut self, extensions: Extensions) -> Self {
450        self.key_package_extensions.replace(extensions);
451        self
452    }
453
454    /// Mark the key package as a last-resort key package via a [`LastResortExtension`].
455    pub fn mark_as_last_resort(mut self) -> Self {
456        self.last_resort = true;
457        self
458    }
459
460    /// Set the leaf node capabilities.
461    pub fn leaf_node_capabilities(mut self, capabilities: Capabilities) -> Self {
462        self.leaf_node_capabilities.replace(capabilities);
463        self
464    }
465
466    /// Set the leaf node extensions.
467    pub fn leaf_node_extensions(mut self, extensions: Extensions) -> Self {
468        self.leaf_node_extensions.replace(extensions);
469        self
470    }
471
472    /// Ensure that a last-resort extension is present in the key package if the
473    /// `last_resort` flag is set.
474    fn ensure_last_resort(&mut self) {
475        if self.last_resort {
476            let last_resort_extension = Extension::LastResort(LastResortExtension::default());
477            if let Some(extensions) = self.key_package_extensions.as_mut() {
478                extensions.add_or_replace(last_resort_extension);
479            } else {
480                self.key_package_extensions = Some(Extensions::single(last_resort_extension));
481            }
482        }
483    }
484
485    #[cfg(test)]
486    pub(crate) fn build_without_storage(
487        mut self,
488        ciphersuite: Ciphersuite,
489        provider: &impl OpenMlsProvider,
490        signer: &impl Signer,
491        credential_with_key: CredentialWithKey,
492    ) -> Result<KeyPackageCreationResult, KeyPackageNewError> {
493        self.ensure_last_resort();
494        KeyPackage::create(
495            ciphersuite,
496            provider,
497            signer,
498            credential_with_key,
499            self.key_package_lifetime.unwrap_or_default(),
500            self.key_package_extensions.unwrap_or_default(),
501            self.leaf_node_capabilities.unwrap_or_default(),
502            self.leaf_node_extensions.unwrap_or_default(),
503        )
504    }
505
506    /// Finalize and build the key package.
507    pub fn build(
508        mut self,
509        ciphersuite: Ciphersuite,
510        provider: &impl OpenMlsProvider,
511        signer: &impl Signer,
512        credential_with_key: CredentialWithKey,
513    ) -> Result<KeyPackageBundle, KeyPackageNewError> {
514        self.ensure_last_resort();
515        let KeyPackageCreationResult {
516            key_package,
517            encryption_keypair,
518            init_private_key,
519        } = KeyPackage::create(
520            ciphersuite,
521            provider,
522            signer,
523            credential_with_key,
524            self.key_package_lifetime.unwrap_or_default(),
525            self.key_package_extensions.unwrap_or_default(),
526            self.leaf_node_capabilities.unwrap_or_default(),
527            self.leaf_node_extensions.unwrap_or_default(),
528        )?;
529
530        // Store the key package in the key store with the hash reference as id
531        // for retrieval when parsing welcome messages.
532        let full_kp = KeyPackageBundle {
533            key_package,
534            private_init_key: init_private_key,
535            private_encryption_key: encryption_keypair.private_key().clone(),
536        };
537        provider
538            .storage()
539            .write_key_package(&full_kp.key_package.hash_ref(provider.crypto())?, &full_kp)
540            .map_err(|_| KeyPackageNewError::StorageError)?;
541
542        Ok(full_kp)
543    }
544}
545
546/// A [`KeyPackageBundle`] contains a [`KeyPackage`] and the init and encryption
547/// private key.
548///
549/// This is stored to ensure the private key is handled together with the key
550/// package.
551#[derive(Debug, Clone, Serialize, Deserialize)]
552pub struct KeyPackageBundle {
553    pub(crate) key_package: KeyPackage,
554    pub(crate) private_init_key: HpkePrivateKey,
555    pub(crate) private_encryption_key: EncryptionPrivateKey,
556}
557
558// Public `KeyPackageBundle` functions.
559impl KeyPackageBundle {
560    /// Get a reference to the public part of this bundle, i.e. the [`KeyPackage`].
561    pub fn key_package(&self) -> &KeyPackage {
562        &self.key_package
563    }
564
565    /// Get a reference to the private init key.
566    pub fn init_private_key(&self) -> &HpkePrivateKey {
567        &self.private_init_key
568    }
569
570    /// Get the encryption key pair.
571    pub(crate) fn encryption_key_pair(&self) -> EncryptionKeyPair {
572        EncryptionKeyPair::from((
573            self.key_package.leaf_node().encryption_key().clone(),
574            self.private_encryption_key.clone(),
575        ))
576    }
577}
578
579#[cfg(any(test, feature = "test-utils"))]
580impl KeyPackageBundle {
581    /// Generate a new key package bundle with the private key.
582    pub fn new(
583        key_package: KeyPackage,
584        private_init_key: HpkePrivateKey,
585        private_encryption_key: EncryptionPrivateKey,
586    ) -> Self {
587        Self {
588            key_package,
589            private_init_key,
590            private_encryption_key,
591        }
592    }
593
594    /// Get a reference to the private encryption key.
595    pub fn encryption_private_key(&self) -> &HpkePrivateKey {
596        self.private_encryption_key.key()
597    }
598}
599
600#[cfg(test)]
601impl KeyPackageBundle {
602    pub(crate) fn generate(
603        provider: &impl OpenMlsProvider,
604        signer: &impl Signer,
605        ciphersuite: Ciphersuite,
606        credential_with_key: CredentialWithKey,
607    ) -> Self {
608        KeyPackage::builder()
609            .build(ciphersuite, provider, signer, credential_with_key)
610            .unwrap()
611    }
612}