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::{
100        errors::InvalidExtensionError, Extension, ExtensionType, Extensions, LastResortExtension,
101    },
102    storage::OpenMlsProvider,
103    treesync::{
104        node::{
105            encryption_keys::{EncryptionKeyPair, EncryptionPrivateKey},
106            leaf_node::{Capabilities, LeafNodeSource, NewLeafNodeParams, TreeInfoTbs},
107        },
108        LeafNode,
109    },
110    versions::ProtocolVersion,
111};
112use openmls_traits::{
113    crypto::OpenMlsCrypto, signatures::Signer, storage::StorageProvider, types::Ciphersuite,
114};
115use serde::{Deserialize, Serialize};
116use tls_codec::{
117    Serialize as TlsSerializeTrait, TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize,
118};
119
120// Private
121use errors::*;
122
123// Public
124pub mod errors;
125pub mod key_package_in;
126
127mod lifetime;
128
129// Tests
130#[cfg(test)]
131pub(crate) mod tests;
132
133// Public types
134pub use key_package_in::KeyPackageIn;
135pub use lifetime::Lifetime;
136
137/// The unsigned payload of a key package.
138/// Any modification must happen on this unsigned struct. Use `sign` to get a
139/// signed key package.
140///
141/// ```text
142/// struct {
143///     ProtocolVersion version;
144///     CipherSuite cipher_suite;
145///     HPKEPublicKey init_key;
146///     LeafNode leaf_node;
147///     Extension extensions<V>;
148/// } KeyPackageTBS;
149/// ```
150#[derive(Debug, Clone, PartialEq, TlsSize, TlsSerialize, Serialize, Deserialize)]
151struct KeyPackageTbs {
152    protocol_version: ProtocolVersion,
153    ciphersuite: Ciphersuite,
154    init_key: InitKey,
155    leaf_node: LeafNode,
156    extensions: Extensions,
157}
158
159impl Signable for KeyPackageTbs {
160    type SignedOutput = KeyPackage;
161
162    fn unsigned_payload(&self) -> Result<Vec<u8>, tls_codec::Error> {
163        self.tls_serialize_detached()
164    }
165
166    fn label(&self) -> &str {
167        SIGNATURE_KEY_PACKAGE_LABEL
168    }
169}
170
171impl From<KeyPackage> for KeyPackageTbs {
172    fn from(kp: KeyPackage) -> Self {
173        kp.payload
174    }
175}
176
177/// The key package struct.
178#[derive(Debug, Clone, Serialize, Deserialize, TlsSize, TlsSerialize)]
179pub struct KeyPackage {
180    payload: KeyPackageTbs,
181    signature: Signature,
182}
183
184impl PartialEq for KeyPackage {
185    fn eq(&self, other: &Self) -> bool {
186        // We ignore the signature in the comparison. The same key package
187        // may have different, valid signatures.
188        self.payload == other.payload
189    }
190}
191
192impl SignedStruct<KeyPackageTbs> for KeyPackage {
193    fn from_payload(payload: KeyPackageTbs, signature: Signature) -> Self {
194        Self { payload, signature }
195    }
196}
197
198const SIGNATURE_KEY_PACKAGE_LABEL: &str = "KeyPackageTBS";
199
200/// Helper struct containing the results of building a new [`KeyPackage`].
201pub(crate) struct KeyPackageCreationResult {
202    pub key_package: KeyPackage,
203    pub encryption_keypair: EncryptionKeyPair,
204    pub init_private_key: HpkePrivateKey,
205}
206
207/// Init key for HPKE.
208#[derive(
209    Debug,
210    Clone,
211    PartialEq,
212    TlsSize,
213    TlsSerialize,
214    Serialize,
215    Deserialize,
216    TlsDeserialize,
217    TlsDeserializeBytes,
218)]
219pub struct InitKey {
220    key: HpkePublicKey,
221}
222
223impl InitKey {
224    /// Return the internal [`HpkePublicKey`].
225    pub fn key(&self) -> &HpkePublicKey {
226        &self.key
227    }
228
229    /// Return the internal [`HpkePublicKey`] as a slice.
230    pub fn as_slice(&self) -> &[u8] {
231        self.key.as_slice()
232    }
233}
234
235impl From<Vec<u8>> for InitKey {
236    fn from(key: Vec<u8>) -> Self {
237        Self {
238            key: HpkePublicKey::from(key),
239        }
240    }
241}
242
243impl From<HpkePublicKey> for InitKey {
244    fn from(key: HpkePublicKey) -> Self {
245        Self { key }
246    }
247}
248
249// Public `KeyPackage` functions.
250impl KeyPackage {
251    /// Create a key package builder.
252    ///
253    /// This is provided for convenience. You can also use [`KeyPackageBuilder::new`].
254    pub fn builder() -> KeyPackageBuilder {
255        KeyPackageBuilder::new()
256    }
257
258    #[allow(clippy::too_many_arguments)]
259    /// Create a new key package for the given `ciphersuite` and `identity`.
260    pub(crate) fn create(
261        ciphersuite: Ciphersuite,
262        provider: &impl OpenMlsProvider,
263        signer: &impl Signer,
264        credential_with_key: CredentialWithKey,
265        lifetime: Lifetime,
266        extensions: Extensions,
267        leaf_node_capabilities: Capabilities,
268        leaf_node_extensions: Extensions,
269    ) -> Result<KeyPackageCreationResult, KeyPackageNewError> {
270        if ciphersuite.signature_algorithm() != signer.signature_scheme() {
271            return Err(KeyPackageNewError::CiphersuiteSignatureSchemeMismatch);
272        }
273
274        // Create a new HPKE key pair
275        let ikm = Secret::random(ciphersuite, provider.rand())
276            .map_err(LibraryError::unexpected_crypto_error)?;
277        let init_key = provider
278            .crypto()
279            .derive_hpke_keypair(ciphersuite.hpke_config(), ikm.as_slice())
280            .map_err(|e| {
281                KeyPackageNewError::LibraryError(LibraryError::unexpected_crypto_error(e))
282            })?;
283        let (key_package, encryption_keypair) = Self::new_from_keys(
284            ciphersuite,
285            provider,
286            signer,
287            credential_with_key,
288            lifetime,
289            extensions,
290            leaf_node_capabilities,
291            leaf_node_extensions,
292            init_key.public.into(),
293        )?;
294
295        Ok(KeyPackageCreationResult {
296            key_package,
297            encryption_keypair,
298            init_private_key: init_key.private,
299        })
300    }
301
302    /// Create a new key package for the given `ciphersuite` and `identity`.
303    ///
304    /// The HPKE init key must have been generated before and the private part
305    /// has to be stored in the key store.
306    ///
307    /// This function returns the new [`KeyPackage`] as well as the
308    /// encryption key ([`HpkeKeyPair`]) of the leaf node.
309    ///
310    /// The caller is responsible for storing the new values.
311    #[allow(clippy::too_many_arguments)]
312    fn new_from_keys(
313        ciphersuite: Ciphersuite,
314        provider: &impl OpenMlsProvider,
315        signer: &impl Signer,
316        credential_with_key: CredentialWithKey,
317        lifetime: Lifetime,
318        extensions: Extensions,
319        capabilities: Capabilities,
320        leaf_node_extensions: Extensions,
321        init_key: InitKey,
322    ) -> Result<(Self, EncryptionKeyPair), KeyPackageNewError> {
323        // We don't need the private key here. It's stored in the key store for
324        // use later when creating a group with this key package.
325
326        let new_leaf_node_params = NewLeafNodeParams {
327            ciphersuite,
328            leaf_node_source: LeafNodeSource::KeyPackage(lifetime),
329            credential_with_key,
330            capabilities,
331            extensions: leaf_node_extensions,
332            tree_info_tbs: TreeInfoTbs::KeyPackage,
333        };
334
335        let (leaf_node, encryption_key_pair) =
336            LeafNode::new(provider, signer, new_leaf_node_params)?;
337
338        let key_package_tbs = KeyPackageTbs {
339            protocol_version: ProtocolVersion::default(),
340            ciphersuite,
341            init_key,
342            leaf_node,
343            extensions,
344        };
345
346        let key_package = key_package_tbs.sign(signer)?;
347
348        Ok((key_package, encryption_key_pair))
349    }
350
351    /// Get a reference to the extensions of this key package.
352    pub fn extensions(&self) -> &Extensions {
353        &self.payload.extensions
354    }
355
356    /// Check whether the this key package supports all the required extensions
357    /// in the provided list.
358    pub fn check_extension_support(
359        &self,
360        required_extensions: &[ExtensionType],
361    ) -> Result<(), KeyPackageExtensionSupportError> {
362        for required_extension in required_extensions.iter() {
363            if !self.extensions().contains(*required_extension) {
364                return Err(KeyPackageExtensionSupportError::UnsupportedExtension);
365            }
366        }
367
368        Ok(())
369    }
370
371    /// Compute the [`KeyPackageRef`] of this [`KeyPackage`].
372    /// The [`KeyPackageRef`] is used to identify a new member that should get
373    /// added to a group.
374    pub fn hash_ref(&self, crypto: &impl OpenMlsCrypto) -> Result<KeyPackageRef, LibraryError> {
375        make_key_package_ref(
376            &self
377                .tls_serialize_detached()
378                .map_err(LibraryError::missing_bound_check)?,
379            self.payload.ciphersuite,
380            crypto,
381        )
382        .map_err(LibraryError::unexpected_crypto_error)
383    }
384
385    /// Get the [`Ciphersuite`].
386    pub fn ciphersuite(&self) -> Ciphersuite {
387        self.payload.ciphersuite
388    }
389
390    /// Get the [`LeafNode`] reference.
391    pub fn leaf_node(&self) -> &LeafNode {
392        &self.payload.leaf_node
393    }
394
395    /// Get the public HPKE init key of this key package.
396    pub fn hpke_init_key(&self) -> &InitKey {
397        &self.payload.init_key
398    }
399
400    /// Check if this KeyPackage is a last resort key package.
401    pub fn last_resort(&self) -> bool {
402        self.payload.extensions.contains(ExtensionType::LastResort)
403    }
404
405    /// Get the lifetime of the KeyPackage
406    pub fn life_time(&self) -> &Lifetime {
407        // Leaf nodes contain a lifetime if an only if they are inside a KeyPackage. Since we are
408        // in a KeyPackage, this can never be None and unwrap is safe.
409        // TODO: get rid of the unwrap, see https://github.com/openmls/openmls/issues/1663.
410        self.payload.leaf_node.life_time().unwrap()
411    }
412}
413
414/// Crate visible `KeyPackage` functions.
415impl KeyPackage {
416    /// Get the `ProtocolVersion`.
417    pub(crate) fn protocol_version(&self) -> ProtocolVersion {
418        self.payload.protocol_version
419    }
420}
421
422/// Builder that helps creating (and configuring) a [`KeyPackage`].
423#[derive(Default, Debug, Clone, Serialize, Deserialize)]
424pub struct KeyPackageBuilder {
425    key_package_lifetime: Option<Lifetime>,
426    key_package_extensions: Option<Extensions>,
427    leaf_node_capabilities: Option<Capabilities>,
428    leaf_node_extensions: Option<Extensions>,
429    last_resort: bool,
430}
431
432impl KeyPackageBuilder {
433    /// Create a key package builder.
434    pub fn new() -> Self {
435        Self {
436            key_package_lifetime: None,
437            key_package_extensions: None,
438            leaf_node_capabilities: None,
439            leaf_node_extensions: None,
440            last_resort: false,
441        }
442    }
443
444    /// Set the key package lifetime.
445    pub fn key_package_lifetime(mut self, lifetime: Lifetime) -> Self {
446        self.key_package_lifetime.replace(lifetime);
447        self
448    }
449
450    /// Set the key package extensions.
451    pub fn key_package_extensions(mut self, extensions: Extensions) -> Self {
452        self.key_package_extensions.replace(extensions);
453        self
454    }
455
456    /// Mark the key package as a last-resort key package via a [`LastResortExtension`].
457    pub fn mark_as_last_resort(mut self) -> Self {
458        self.last_resort = true;
459        self
460    }
461
462    /// Set the leaf node capabilities.
463    pub fn leaf_node_capabilities(mut self, capabilities: Capabilities) -> Self {
464        self.leaf_node_capabilities.replace(capabilities);
465        self
466    }
467
468    /// Set the leaf node extensions.
469    ///
470    /// Returns an error if one or more of the extensions is invalid in leaf nodes.
471    pub fn leaf_node_extensions(
472        mut self,
473        extensions: Extensions,
474    ) -> Result<Self, InvalidExtensionError> {
475        // https://validation.openmls.tech/#valn1601
476        extensions.validate_extension_types_for_leaf_node()?;
477        self.leaf_node_extensions.replace(extensions);
478
479        Ok(self)
480    }
481
482    /// Ensure that a last-resort extension is present in the key package if the
483    /// `last_resort` flag is set.
484    fn ensure_last_resort(&mut self) {
485        if self.last_resort {
486            let last_resort_extension = Extension::LastResort(LastResortExtension::default());
487            if let Some(extensions) = self.key_package_extensions.as_mut() {
488                extensions.add_or_replace(last_resort_extension);
489            } else {
490                self.key_package_extensions = Some(Extensions::single(last_resort_extension));
491            }
492        }
493    }
494
495    #[cfg(test)]
496    pub(crate) fn build_without_storage(
497        mut self,
498        ciphersuite: Ciphersuite,
499        provider: &impl OpenMlsProvider,
500        signer: &impl Signer,
501        credential_with_key: CredentialWithKey,
502    ) -> Result<KeyPackageCreationResult, KeyPackageNewError> {
503        self.ensure_last_resort();
504        KeyPackage::create(
505            ciphersuite,
506            provider,
507            signer,
508            credential_with_key,
509            self.key_package_lifetime.unwrap_or_default(),
510            self.key_package_extensions.unwrap_or_default(),
511            self.leaf_node_capabilities.unwrap_or_default(),
512            self.leaf_node_extensions.unwrap_or_default(),
513        )
514    }
515
516    /// Finalize and build the key package.
517    pub fn build(
518        mut self,
519        ciphersuite: Ciphersuite,
520        provider: &impl OpenMlsProvider,
521        signer: &impl Signer,
522        credential_with_key: CredentialWithKey,
523    ) -> Result<KeyPackageBundle, KeyPackageNewError> {
524        self.ensure_last_resort();
525        let KeyPackageCreationResult {
526            key_package,
527            encryption_keypair,
528            init_private_key,
529        } = KeyPackage::create(
530            ciphersuite,
531            provider,
532            signer,
533            credential_with_key,
534            self.key_package_lifetime.unwrap_or_default(),
535            self.key_package_extensions.unwrap_or_default(),
536            self.leaf_node_capabilities.unwrap_or_default(),
537            self.leaf_node_extensions.unwrap_or_default(),
538        )?;
539
540        // Store the key package in the key store with the hash reference as id
541        // for retrieval when parsing welcome messages.
542        let full_kp = KeyPackageBundle {
543            key_package,
544            private_init_key: init_private_key,
545            private_encryption_key: encryption_keypair.private_key().clone(),
546        };
547        provider
548            .storage()
549            .write_key_package(&full_kp.key_package.hash_ref(provider.crypto())?, &full_kp)
550            .map_err(|_| KeyPackageNewError::StorageError)?;
551
552        Ok(full_kp)
553    }
554}
555
556/// A [`KeyPackageBundle`] contains a [`KeyPackage`] and the init and encryption
557/// private key.
558///
559/// This is stored to ensure the private key is handled together with the key
560/// package.
561#[derive(Debug, Clone, Serialize, Deserialize)]
562pub struct KeyPackageBundle {
563    pub(crate) key_package: KeyPackage,
564    pub(crate) private_init_key: HpkePrivateKey,
565    pub(crate) private_encryption_key: EncryptionPrivateKey,
566}
567
568// Public `KeyPackageBundle` functions.
569impl KeyPackageBundle {
570    /// Get a reference to the public part of this bundle, i.e. the [`KeyPackage`].
571    pub fn key_package(&self) -> &KeyPackage {
572        &self.key_package
573    }
574
575    /// Get a reference to the private init key.
576    pub fn init_private_key(&self) -> &HpkePrivateKey {
577        &self.private_init_key
578    }
579
580    /// Get the encryption key pair.
581    pub(crate) fn encryption_key_pair(&self) -> EncryptionKeyPair {
582        EncryptionKeyPair::from((
583            self.key_package.leaf_node().encryption_key().clone(),
584            self.private_encryption_key.clone(),
585        ))
586    }
587}
588
589#[cfg(any(test, feature = "test-utils"))]
590impl KeyPackageBundle {
591    /// Generate a new key package bundle with the private key.
592    pub fn new(
593        key_package: KeyPackage,
594        private_init_key: HpkePrivateKey,
595        private_encryption_key: EncryptionPrivateKey,
596    ) -> Self {
597        Self {
598            key_package,
599            private_init_key,
600            private_encryption_key,
601        }
602    }
603
604    /// Get a reference to the private encryption key.
605    pub fn encryption_private_key(&self) -> &HpkePrivateKey {
606        self.private_encryption_key.key()
607    }
608}
609
610#[cfg(test)]
611impl KeyPackageBundle {
612    pub(crate) fn generate(
613        provider: &impl OpenMlsProvider,
614        signer: &impl Signer,
615        ciphersuite: Ciphersuite,
616        credential_with_key: CredentialWithKey,
617    ) -> Self {
618        KeyPackage::builder()
619            .build(ciphersuite, provider, signer, credential_with_key)
620            .unwrap()
621    }
622}