Skip to main content

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