Skip to main content

openmls/treesync/node/
leaf_node.rs

1//! This module contains the [`LeafNode`] struct and its implementation.
2use std::collections::HashSet;
3
4use openmls_traits::{
5    crypto::OpenMlsCrypto, random::OpenMlsRand, signatures::Signer, types::Ciphersuite,
6};
7use serde::{Deserialize, Serialize};
8use thiserror::Error;
9use tls_codec::{
10    Serialize as TlsSerializeTrait, TlsDeserialize, TlsDeserializeBytes, TlsSerialize, TlsSize,
11    VLBytes,
12};
13
14use super::encryption_keys::{EncryptionKey, EncryptionKeyPair};
15use crate::{
16    binary_tree::array_representation::LeafNodeIndex,
17    ciphersuite::{
18        signable::{Signable, SignedStruct, Verifiable, VerifiedStruct},
19        Signature, SignaturePublicKey,
20    },
21    credentials::{Credential, CredentialType, CredentialWithKey},
22    error::LibraryError,
23    extensions::{ExtensionType, Extensions},
24    group::GroupId,
25    key_packages::{KeyPackage, Lifetime},
26    prelude::KeyPackageBundle,
27    storage::OpenMlsProvider,
28};
29
30use crate::treesync::errors::LeafNodeValidationError;
31
32mod capabilities;
33mod codec;
34
35pub use capabilities::*;
36
37pub(crate) struct NewLeafNodeParams {
38    pub(crate) ciphersuite: Ciphersuite,
39    pub(crate) credential_with_key: CredentialWithKey,
40    pub(crate) leaf_node_source: LeafNodeSource,
41    pub(crate) capabilities: Capabilities,
42    pub(crate) extensions: Extensions<LeafNode>,
43    pub(crate) tree_info_tbs: TreeInfoTbs,
44}
45
46/// Set of LeafNode parameters that are used when regenerating a LeafNodes
47/// during an update operation.
48#[derive(Debug, PartialEq, Clone)]
49pub(crate) struct UpdateLeafNodeParams {
50    pub(crate) credential_with_key: CredentialWithKey,
51    pub(crate) capabilities: Capabilities,
52    pub(crate) extensions: Extensions<LeafNode>,
53}
54
55impl UpdateLeafNodeParams {
56    #[cfg(test)]
57    pub(crate) fn derive(leaf_node: &LeafNode) -> Self {
58        Self {
59            credential_with_key: CredentialWithKey {
60                credential: leaf_node.payload.credential.clone(),
61                signature_key: leaf_node.payload.signature_key.clone(),
62            },
63            capabilities: leaf_node.payload.capabilities.clone(),
64            extensions: leaf_node.payload.extensions.clone(),
65        }
66    }
67}
68
69/// Parameters for a leaf node that can be chosen by the application.
70#[derive(Debug, PartialEq, Clone, Default)]
71pub struct LeafNodeParameters {
72    credential_with_key: Option<CredentialWithKey>,
73    capabilities: Option<Capabilities>,
74    extensions: Option<Extensions<LeafNode>>,
75}
76
77impl LeafNodeParameters {
78    /// Create a new [`LeafNodeParametersBuilder`].
79    pub fn builder() -> LeafNodeParametersBuilder {
80        LeafNodeParametersBuilder::default()
81    }
82
83    /// Returns the credential with key.
84    pub fn credential_with_key(&self) -> Option<&CredentialWithKey> {
85        self.credential_with_key.as_ref()
86    }
87
88    /// Returns the capabilities.
89    pub fn capabilities(&self) -> Option<&Capabilities> {
90        self.capabilities.as_ref()
91    }
92
93    /// Returns the extensions.
94    pub fn extensions(&self) -> Option<&Extensions<LeafNode>> {
95        self.extensions.as_ref()
96    }
97
98    pub(crate) fn is_empty(&self) -> bool {
99        self.credential_with_key.is_none()
100            && self.capabilities.is_none()
101            && self.extensions.is_none()
102    }
103
104    pub(crate) fn set_credential_with_key(&mut self, credential_with_key: CredentialWithKey) {
105        self.credential_with_key = Some(credential_with_key);
106    }
107
108    #[cfg(feature = "virtual-clients-draft")]
109    pub(crate) fn set_extensions(&mut self, extensions: Extensions<LeafNode>) {
110        self.extensions = Some(extensions);
111    }
112}
113
114/// Builder for [`LeafNodeParameters`].
115#[derive(Debug, Default)]
116pub struct LeafNodeParametersBuilder {
117    credential_with_key: Option<CredentialWithKey>,
118    capabilities: Option<Capabilities>,
119    extensions: Option<Extensions<LeafNode>>,
120}
121
122impl LeafNodeParametersBuilder {
123    /// Set the credential with key.
124    pub fn with_credential_with_key(mut self, credential_with_key: CredentialWithKey) -> Self {
125        self.credential_with_key = Some(credential_with_key);
126        self
127    }
128
129    /// Set the capabilities.
130    pub fn with_capabilities(mut self, capabilities: Capabilities) -> Self {
131        self.capabilities = Some(capabilities);
132        self
133    }
134
135    /// Set the extensions.
136    ///
137    /// Returns an error if one or more of the extensions is invalid in leaf nodes.
138    pub fn with_extensions(mut self, extensions: Extensions<LeafNode>) -> Self {
139        self.extensions = Some(extensions);
140        self
141    }
142
143    /// Build the [`LeafNodeParameters`].
144    pub fn build(self) -> LeafNodeParameters {
145        LeafNodeParameters {
146            credential_with_key: self.credential_with_key,
147            capabilities: self.capabilities,
148            extensions: self.extensions,
149        }
150    }
151}
152
153/// This struct implements the MLS leaf node.
154///
155/// ```c
156/// // draft-ietf-mls-protocol-17
157/// struct {
158///     HPKEPublicKey encryption_key;
159///     SignaturePublicKey signature_key;
160///     Credential credential;
161///     Capabilities capabilities;
162///
163///     LeafNodeSource leaf_node_source;
164///     select (LeafNode.leaf_node_source) {
165///         case key_package:
166///             Lifetime lifetime;
167///
168///         case update:
169///             struct{};
170///
171///         case commit:
172///             opaque parent_hash<V>;
173///     };
174///
175///     Extension extensions<V>;
176///     /* SignWithLabel(., "LeafNodeTBS", LeafNodeTBS) */
177///     opaque signature<V>;
178/// } LeafNode;
179/// ```
180#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, TlsSerialize, TlsSize)]
181pub struct LeafNode {
182    payload: LeafNodePayload,
183    signature: Signature,
184}
185
186impl LeafNode {
187    /// Create a new [`LeafNode`].
188    /// This first creates a `LeadNodeTbs` and returns the result of signing
189    /// it.
190    ///
191    /// This function generates a fresh HPKE key pair for the leaf node and
192    /// returns the HPKE key pair along with the new leaf node.
193    /// The caller is responsible for storing the private key.
194    pub(crate) fn new(
195        provider: &impl OpenMlsProvider,
196        signer: &impl Signer,
197        new_leaf_node_params: NewLeafNodeParams,
198    ) -> Result<(Self, EncryptionKeyPair), LibraryError> {
199        let NewLeafNodeParams {
200            ciphersuite,
201            credential_with_key,
202            leaf_node_source,
203            capabilities,
204            extensions,
205            tree_info_tbs,
206        } = new_leaf_node_params;
207
208        // Create a new encryption key pair.
209        let encryption_key_pair =
210            EncryptionKeyPair::random(provider.rand(), provider.crypto(), ciphersuite)?;
211
212        let leaf_node = Self::new_with_key(
213            encryption_key_pair.public_key().clone(),
214            credential_with_key,
215            leaf_node_source,
216            capabilities,
217            extensions,
218            tree_info_tbs,
219            signer,
220        )?;
221
222        Ok((leaf_node, encryption_key_pair))
223    }
224
225    /// Create a new [`LeafNode`] from a caller-provided encryption key pair.
226    ///
227    /// Mirrors [`LeafNode::new`] but uses `encryption_key_pair` instead of
228    /// generating a fresh one. This is the virtual-clients KeyPackage build
229    /// hook: the encryption key is derived from the per-operation secret so a
230    /// sibling can reproduce it.
231    #[cfg(feature = "virtual-clients-draft")]
232    pub(crate) fn new_with_encryption_key_pair(
233        signer: &impl Signer,
234        new_leaf_node_params: NewLeafNodeParams,
235        encryption_key_pair: EncryptionKeyPair,
236    ) -> Result<(Self, EncryptionKeyPair), LibraryError> {
237        let NewLeafNodeParams {
238            ciphersuite: _,
239            credential_with_key,
240            leaf_node_source,
241            capabilities,
242            extensions,
243            tree_info_tbs,
244        } = new_leaf_node_params;
245
246        let leaf_node = Self::new_with_key(
247            encryption_key_pair.public_key().clone(),
248            credential_with_key,
249            leaf_node_source,
250            capabilities,
251            extensions,
252            tree_info_tbs,
253            signer,
254        )?;
255
256        Ok((leaf_node, encryption_key_pair))
257    }
258
259    /// Creates a new placeholder [`LeafNode`] that is used to build external
260    /// commits.
261    ///
262    /// Note: This is not a valid leaf node and it must be rekeyed and signed
263    /// before it can be used.
264    pub(crate) fn new_placeholder() -> Self {
265        let payload = LeafNodePayload {
266            encryption_key: EncryptionKey::from(Vec::new()),
267            signature_key: Vec::new().into(),
268            credential: Credential::new(CredentialType::Basic, Vec::new()),
269            capabilities: Capabilities::default(),
270            leaf_node_source: LeafNodeSource::Update,
271            extensions: Extensions::default(),
272        };
273
274        Self {
275            payload,
276            signature: Vec::new().into(),
277        }
278    }
279
280    /// Create a new leaf node with a given HPKE encryption key pair.
281    /// The key pair must be stored in the key store by the caller.
282    fn new_with_key(
283        encryption_key: EncryptionKey,
284        credential_with_key: CredentialWithKey,
285        leaf_node_source: LeafNodeSource,
286        capabilities: Capabilities,
287        extensions: Extensions<LeafNode>,
288        tree_info_tbs: TreeInfoTbs,
289        signer: &impl Signer,
290    ) -> Result<Self, LibraryError> {
291        let leaf_node_tbs = LeafNodeTbs::new(
292            encryption_key,
293            credential_with_key,
294            capabilities,
295            leaf_node_source,
296            extensions,
297            tree_info_tbs,
298        );
299
300        leaf_node_tbs
301            .sign(signer)
302            .map_err(|_| LibraryError::custom("Signing failed"))
303    }
304
305    /// New [`LeafNode`] with a parent hash.
306    ///
307    /// With the `virtual-clients-draft` feature, an
308    /// `encryption_key_pair_override` may be supplied. If `Some`, it is used
309    /// as the leaf's encryption keypair instead of generating a fresh one.
310    /// This is the hook for the virtual-clients-draft sender.
311    #[allow(clippy::too_many_arguments)]
312    pub(in crate::treesync) fn new_with_parent_hash(
313        rand: &impl OpenMlsRand,
314        crypto: &impl OpenMlsCrypto,
315        ciphersuite: Ciphersuite,
316        parent_hash: &[u8],
317        leaf_node_params: UpdateLeafNodeParams,
318        group_id: GroupId,
319        leaf_index: LeafNodeIndex,
320        signer: &impl Signer,
321        #[cfg(feature = "virtual-clients-draft")] encryption_key_pair_override: Option<
322            EncryptionKeyPair,
323        >,
324    ) -> Result<(Self, EncryptionKeyPair), LibraryError> {
325        #[cfg(feature = "virtual-clients-draft")]
326        let encryption_key_pair = match encryption_key_pair_override {
327            Some(kp) => kp,
328            None => EncryptionKeyPair::random(rand, crypto, ciphersuite)?,
329        };
330        #[cfg(not(feature = "virtual-clients-draft"))]
331        let encryption_key_pair = EncryptionKeyPair::random(rand, crypto, ciphersuite)?;
332
333        let leaf_node_tbs = LeafNodeTbs::new(
334            encryption_key_pair.public_key().clone(),
335            leaf_node_params.credential_with_key,
336            leaf_node_params.capabilities,
337            LeafNodeSource::Commit(parent_hash.into()),
338            leaf_node_params.extensions,
339            TreeInfoTbs::Commit(TreePosition {
340                group_id,
341                leaf_index,
342            }),
343        );
344
345        // Sign the leaf node
346        let leaf_node = leaf_node_tbs
347            .sign(signer)
348            .map_err(|_| LibraryError::custom("Signing failed"))?;
349
350        Ok((leaf_node, encryption_key_pair))
351    }
352
353    /// Generate a fresh leaf node.
354    ///
355    /// This includes generating a new encryption key pair that is stored in the
356    /// key store.
357    ///
358    /// This function can be used when generating an update. In most other cases
359    /// a leaf node should be generated as part of a new [`KeyPackage`].
360    #[cfg(all(test, feature = "generate-kats"))]
361    pub(crate) fn generate_update<Provider: OpenMlsProvider>(
362        ciphersuite: Ciphersuite,
363        credential_with_key: CredentialWithKey,
364        capabilities: Capabilities,
365        extensions: Extensions<LeafNode>,
366        tree_info_tbs: TreeInfoTbs,
367        provider: &Provider,
368        signer: &impl Signer,
369    ) -> Result<Self, LeafNodeGenerationError<Provider::StorageError>> {
370        // Note that this function is supposed to be used in the public API only
371        // because it is interacting with the key store.
372
373        let new_leaf_node_params = NewLeafNodeParams {
374            ciphersuite,
375            credential_with_key,
376            leaf_node_source: LeafNodeSource::Update,
377            capabilities,
378            extensions,
379            tree_info_tbs,
380        };
381
382        let (leaf_node, encryption_key_pair) = Self::new(provider, signer, new_leaf_node_params)?;
383
384        // Store the encryption key pair in the key store.
385        encryption_key_pair
386            .write(provider.storage())
387            .map_err(LeafNodeGenerationError::StorageError)?;
388
389        Ok(leaf_node)
390    }
391
392    /// Update a leaf node.
393    ///
394    /// This function generates a new encryption key pair that is stored in the
395    /// key store and also returned.
396    ///
397    /// This function can be used when generating an update. In most other cases
398    /// a leaf node should be generated as part of a new [`KeyPackage`].
399    pub(crate) fn update<Provider: OpenMlsProvider>(
400        &mut self,
401        ciphersuite: Ciphersuite,
402        provider: &Provider,
403        signer: &impl Signer,
404        group_id: GroupId,
405        leaf_index: LeafNodeIndex,
406        leaf_node_parmeters: LeafNodeParameters,
407    ) -> Result<EncryptionKeyPair, LeafNodeUpdateError<Provider::StorageError>> {
408        let tree_info = TreeInfoTbs::Update(TreePosition::new(group_id, leaf_index));
409        let mut leaf_node_tbs = LeafNodeTbs::from(self.clone(), tree_info);
410
411        // Update credential
412        if let Some(credential_with_key) = leaf_node_parmeters.credential_with_key {
413            leaf_node_tbs.payload.credential = credential_with_key.credential;
414            leaf_node_tbs.payload.signature_key = credential_with_key.signature_key;
415        }
416
417        // Update extensions
418        if let Some(extensions) = leaf_node_parmeters.extensions {
419            leaf_node_tbs.payload.extensions = extensions;
420        }
421
422        // Update capabilities
423        if let Some(capabilities) = leaf_node_parmeters.capabilities {
424            leaf_node_tbs.payload.capabilities = capabilities;
425        }
426
427        // Create a new encryption key pair
428        let encryption_key_pair =
429            EncryptionKeyPair::random(provider.rand(), provider.crypto(), ciphersuite)?;
430        leaf_node_tbs.payload.encryption_key = encryption_key_pair.public_key().clone();
431
432        // Store the encryption key pair in the key store.
433        encryption_key_pair
434            .write(provider.storage())
435            .map_err(LeafNodeUpdateError::Storage)?;
436
437        // Set the leaf node source to update
438        leaf_node_tbs.payload.leaf_node_source = LeafNodeSource::Update;
439
440        // Sign the leaf node
441        let leaf_node = leaf_node_tbs.sign(signer)?;
442        self.payload = leaf_node.payload;
443        self.signature = leaf_node.signature;
444
445        Ok(encryption_key_pair)
446    }
447
448    /// Returns the `encryption_key`.
449    pub fn encryption_key(&self) -> &EncryptionKey {
450        &self.payload.encryption_key
451    }
452
453    /// Returns the `signature_key` as byte slice.
454    pub fn signature_key(&self) -> &SignaturePublicKey {
455        &self.payload.signature_key
456    }
457
458    /// Returns the `credential`.
459    pub fn credential(&self) -> &Credential {
460        &self.payload.credential
461    }
462
463    /// Returns the `parent_hash` as byte slice or `None`.
464    pub fn parent_hash(&self) -> Option<&[u8]> {
465        match &self.payload.leaf_node_source {
466            LeafNodeSource::Commit(ph) => Some(ph.as_slice()),
467            _ => None,
468        }
469    }
470
471    /// Returns the [`Lifetime`] if present.
472    /// `None` otherwise.
473    pub(crate) fn life_time(&self) -> Option<&Lifetime> {
474        if let LeafNodeSource::KeyPackage(life_time) = &self.payload.leaf_node_source {
475            Some(life_time)
476        } else {
477            None
478        }
479    }
480
481    /// Returns a reference to the [`Signature`] of this leaf.
482    pub fn signature(&self) -> &Signature {
483        &self.signature
484    }
485
486    /// Return a reference to [`Capabilities`].
487    pub fn capabilities(&self) -> &Capabilities {
488        &self.payload.capabilities
489    }
490
491    /// Return a reference to the leaf node source.
492    pub fn leaf_node_source(&self) -> &LeafNodeSource {
493        &self.payload.leaf_node_source
494    }
495
496    /// Return a reference to the leaf node extensions.
497    pub fn extensions(&self) -> &Extensions<LeafNode> {
498        &self.payload.extensions
499    }
500
501    /// Returns `true` if the [`ExtensionType`] is supported by this leaf node.
502    pub(crate) fn supports_extension(&self, extension_type: &ExtensionType) -> bool {
503        extension_type.is_default()
504            || self
505                .payload
506                .capabilities
507                .extensions
508                .contains(extension_type)
509    }
510
511    /// Check whether the this leaf node supports all the required extensions
512    /// in the provided list.
513    pub(crate) fn check_extension_support(
514        &self,
515        extensions: &[ExtensionType],
516    ) -> Result<(), LeafNodeValidationError> {
517        let mut required = extensions.iter().filter(|e| !e.is_default()).peekable();
518
519        // Skip building the lookup if there are no non-default extensions.
520        if required.peek().is_none() {
521            return Ok(());
522        }
523
524        let supported: HashSet<ExtensionType> = self
525            .payload
526            .capabilities
527            .extensions
528            .iter()
529            .copied()
530            .collect();
531
532        if let Some(unsupported) = required.find(|e| !supported.contains(e)) {
533            log::error!(
534                "Leaf node does not support required extension {:?}\n
535                    Supported extensions: {:?}",
536                unsupported,
537                self.payload.capabilities.extensions
538            );
539            return Err(LeafNodeValidationError::UnsupportedExtensions);
540        }
541
542        Ok(())
543    }
544
545    /// Perform all checks that can be done without further context:
546    /// - the used extensions are not known to be invalid in leaf nodes
547    /// - the types of the used extensions are covered by the capabilities
548    /// - the type of the credential is covered by the capabilities
549    pub(crate) fn validate_locally(&self) -> Result<(), LeafNodeValidationError> {
550        // Check that no extension is invalid when used in leaf nodes.
551        // https://validation.openmls.tech/#valn1601
552        // NOTE: This check is conducted manually for now, instead of using the method
553        // Extensions::validate_extension_types_for_leaf_node(),
554        // in order to collect the invalid extension types for the log message below.
555        // However, it could be better to instead return the list of invalid extension types
556        // as part of Extensions::validate_extension_types_for_leaf_node(),
557        // as part of the error message.
558        let invalid_extension_types = self
559            .extensions()
560            .iter()
561            .filter(|ext| !ext.extension_type().is_valid_in_leaf_node())
562            .collect::<Vec<_>>();
563        if !invalid_extension_types.is_empty() {
564            log::error!("Invalid extension used in leaf node: {invalid_extension_types:?}");
565            return Err(LeafNodeValidationError::UnsupportedExtensions);
566        }
567
568        // Check that all extensions are contained in the capabilities.
569        if !self.capabilities().contains_extensions(self.extensions()) {
570            log::error!(
571                "Leaf node does not support all extensions it uses\n
572                Supported extensions: {:?}\n
573                Used extensions: {:?}",
574                self.payload.capabilities.extensions,
575                self.extensions()
576            );
577            return Err(LeafNodeValidationError::UnsupportedExtensions);
578        }
579
580        // Check that the capabilities contain the leaf node's credential type.
581        // (https://validation.openmls.tech/#valn0113)
582        if !self
583            .capabilities()
584            .contains_credential(self.credential().credential_type())
585        {
586            return Err(LeafNodeValidationError::UnsupportedCredentials);
587        }
588
589        Ok(())
590    }
591}
592
593/// The payload of a [`LeafNode`]
594///
595/// ```text
596/// struct {
597///     HPKEPublicKey encryption_key;
598///     SignaturePublicKey signature_key;
599///     Credential credential;
600///     Capabilities capabilities;
601///
602///     LeafNodeSource leaf_node_source;
603///     select (LeafNode.leaf_node_source) {
604///         case key_package:
605///             Lifetime lifetime;
606///
607///         case update:
608///             struct{};
609///
610///         case commit:
611///             opaque parent_hash<V>;
612///     };
613///
614///     Extension extensions<V>;
615///     ...
616/// } LeafNode;
617/// ```
618#[derive(
619    Debug,
620    Clone,
621    PartialEq,
622    Eq,
623    Serialize,
624    Deserialize,
625    TlsSerialize,
626    TlsDeserialize,
627    TlsDeserializeBytes,
628    TlsSize,
629)]
630struct LeafNodePayload {
631    encryption_key: EncryptionKey,
632    signature_key: SignaturePublicKey,
633    credential: Credential,
634    capabilities: Capabilities,
635    leaf_node_source: LeafNodeSource,
636    extensions: Extensions<LeafNode>,
637}
638
639/// The source of the `LeafNode`.
640#[derive(
641    Debug,
642    Clone,
643    PartialEq,
644    Eq,
645    Serialize,
646    Deserialize,
647    TlsSerialize,
648    TlsDeserialize,
649    TlsDeserializeBytes,
650    TlsSize,
651)]
652#[repr(u8)]
653pub enum LeafNodeSource {
654    /// The leaf node was added to the group as part of a key package.
655    #[tls_codec(discriminant = 1)]
656    KeyPackage(Lifetime),
657    /// The leaf node was added through an Update proposal.
658    Update,
659    /// The leaf node was added via a Commit.
660    Commit(ParentHash),
661}
662
663pub type ParentHash = VLBytes;
664
665/// To-be-signed leaf node.
666///
667/// ```c
668/// // draft-ietf-mls-protocol-17
669/// struct {
670///     HPKEPublicKey encryption_key;
671///     SignaturePublicKey signature_key;
672///     Credential credential;
673///     Capabilities capabilities;
674///
675///     LeafNodeSource leaf_node_source;
676///     select (LeafNodeTBS.leaf_node_source) {
677///         case key_package:
678///             Lifetime lifetime;
679///
680///         case update:
681///             struct{};
682///
683///         case commit:
684///             opaque parent_hash<V>;
685///     };
686///
687///     Extension extensions<V>;
688///
689///     // ... continued in [`TreeInfo`] ...
690/// } LeafNodeTBS;
691/// ```
692#[derive(Debug, TlsSerialize, TlsSize)]
693pub struct LeafNodeTbs {
694    payload: LeafNodePayload,
695    tree_info_tbs: TreeInfoTbs,
696}
697
698impl LeafNodeTbs {
699    /// Build a [`LeafNodeTbs`] from a [`LeafNode`] and a [`TreeInfo`]
700    /// to update a leaf node.
701    pub(crate) fn from(leaf_node: LeafNode, tree_info_tbs: TreeInfoTbs) -> Self {
702        Self {
703            payload: leaf_node.payload,
704            tree_info_tbs,
705        }
706    }
707
708    /// Build a new [`LeafNodeTbs`] from a [`KeyPackage`] and [`Credential`].
709    /// To get the [`LeafNode`] call [`LeafNode::sign`].
710    pub(crate) fn new(
711        encryption_key: EncryptionKey,
712        credential_with_key: CredentialWithKey,
713        capabilities: Capabilities,
714        leaf_node_source: LeafNodeSource,
715        extensions: Extensions<LeafNode>,
716        tree_info_tbs: TreeInfoTbs,
717    ) -> Self {
718        let payload = LeafNodePayload {
719            encryption_key,
720            signature_key: credential_with_key.signature_key,
721            credential: credential_with_key.credential,
722            capabilities,
723            leaf_node_source,
724            extensions,
725        };
726
727        LeafNodeTbs {
728            payload,
729            tree_info_tbs,
730        }
731    }
732}
733
734/// Helper struct that holds additional information required to sign a leaf node.
735///
736/// ```c
737/// // draft-ietf-mls-protocol-17
738/// struct {
739///     // ... continued from [`LeafNodeTbs`] ...
740///
741///     select (LeafNodeTBS.leaf_node_source) {
742///         case key_package:
743///             struct{};
744///
745///         case update:
746///             opaque group_id<V>;
747///             uint32 leaf_index;
748///
749///         case commit:
750///             opaque group_id<V>;
751///             uint32 leaf_index;
752///     };
753/// } LeafNodeTBS;
754/// ```
755#[derive(Debug)]
756pub(crate) enum TreeInfoTbs {
757    KeyPackage,
758    Update(TreePosition),
759    Commit(TreePosition),
760}
761
762#[derive(Debug, Clone, PartialEq, Eq, TlsSerialize, TlsSize)]
763pub(crate) struct TreePosition {
764    group_id: GroupId,
765    leaf_index: LeafNodeIndex,
766}
767
768impl TreePosition {
769    pub(crate) fn new(group_id: GroupId, leaf_index: LeafNodeIndex) -> Self {
770        Self {
771            group_id,
772            leaf_index,
773        }
774    }
775
776    #[cfg(feature = "test-utils")]
777    pub(crate) fn into_parts(self) -> (GroupId, LeafNodeIndex) {
778        (self.group_id, self.leaf_index)
779    }
780}
781
782const LEAF_NODE_SIGNATURE_LABEL: &str = "LeafNodeTBS";
783
784#[derive(
785    Debug,
786    Clone,
787    PartialEq,
788    Eq,
789    Serialize,
790    Deserialize,
791    TlsSerialize,
792    TlsDeserialize,
793    TlsDeserializeBytes,
794    TlsSize,
795)]
796pub struct LeafNodeIn {
797    payload: LeafNodePayload,
798    signature: Signature,
799}
800
801impl LeafNodeIn {
802    pub(crate) fn into_verifiable_leaf_node(self) -> VerifiableLeafNode {
803        match self.payload.leaf_node_source {
804            LeafNodeSource::KeyPackage(_) => {
805                let verifiable = VerifiableKeyPackageLeafNode {
806                    payload: self.payload,
807                    signature: self.signature,
808                };
809                VerifiableLeafNode::KeyPackage(verifiable)
810            }
811            LeafNodeSource::Update => {
812                let verifiable = VerifiableUpdateLeafNode {
813                    payload: self.payload,
814                    signature: self.signature,
815                    tree_position: None,
816                };
817                VerifiableLeafNode::Update(verifiable)
818            }
819            LeafNodeSource::Commit(_) => {
820                let verifiable = VerifiableCommitLeafNode {
821                    payload: self.payload,
822                    signature: self.signature,
823                    tree_position: None,
824                };
825                VerifiableLeafNode::Commit(verifiable)
826            }
827        }
828    }
829
830    /// Returns the `encryption_key` as byte slice.
831    pub fn encryption_key(&self) -> &EncryptionKey {
832        &self.payload.encryption_key
833    }
834
835    /// Returns the `signature_key` as byte slice.
836    pub fn signature_key(&self) -> &SignaturePublicKey {
837        &self.payload.signature_key
838    }
839
840    /// Returns the `signature_key` as byte slice.
841    pub fn credential(&self) -> &Credential {
842        &self.payload.credential
843    }
844
845    /// Assume that signature is valid and return the corresponding [`LeafNode`].
846    ///
847    /// # Safety
848    ///
849    /// The caller must guarantee that the leaf node is verified.
850    #[cfg(feature = "unchecked-conversions")]
851    pub fn into_unchecked(self) -> LeafNode {
852        LeafNode {
853            payload: self.payload,
854            signature: self.signature,
855        }
856    }
857}
858
859impl From<LeafNode> for LeafNodeIn {
860    fn from(leaf_node: LeafNode) -> Self {
861        Self {
862            payload: leaf_node.payload,
863            signature: leaf_node.signature,
864        }
865    }
866}
867
868#[cfg(any(feature = "test-utils", test))]
869impl From<LeafNodeIn> for LeafNode {
870    fn from(deserialized: LeafNodeIn) -> Self {
871        Self {
872            payload: deserialized.payload,
873            signature: deserialized.signature,
874        }
875    }
876}
877
878impl From<KeyPackage> for LeafNode {
879    fn from(key_package: KeyPackage) -> Self {
880        key_package.leaf_node().clone()
881    }
882}
883
884impl From<KeyPackageBundle> for LeafNode {
885    fn from(key_package: KeyPackageBundle) -> Self {
886        key_package.key_package().leaf_node().clone()
887    }
888}
889
890#[derive(Debug, Clone, PartialEq, Eq)]
891pub(crate) enum VerifiableLeafNode {
892    KeyPackage(VerifiableKeyPackageLeafNode),
893    Update(VerifiableUpdateLeafNode),
894    Commit(VerifiableCommitLeafNode),
895}
896
897impl VerifiableLeafNode {
898    pub(crate) fn signature_key(&self) -> &SignaturePublicKey {
899        match self {
900            VerifiableLeafNode::KeyPackage(v) => v.signature_key(),
901            VerifiableLeafNode::Update(v) => v.signature_key(),
902            VerifiableLeafNode::Commit(v) => v.signature_key(),
903        }
904    }
905}
906
907#[derive(Debug, Clone, PartialEq, Eq)]
908pub(crate) struct VerifiableKeyPackageLeafNode {
909    payload: LeafNodePayload,
910    signature: Signature,
911}
912
913impl VerifiableKeyPackageLeafNode {
914    pub(crate) fn signature_key(&self) -> &SignaturePublicKey {
915        &self.payload.signature_key
916    }
917}
918
919// https://validation.openmls.tech/#valn0102
920impl Verifiable for VerifiableKeyPackageLeafNode {
921    type VerifiedStruct = LeafNode;
922
923    fn unsigned_payload(&self) -> Result<Vec<u8>, tls_codec::Error> {
924        self.payload.tls_serialize_detached()
925    }
926
927    fn signature(&self) -> &Signature {
928        &self.signature
929    }
930
931    fn label(&self) -> &str {
932        LEAF_NODE_SIGNATURE_LABEL
933    }
934
935    fn verify(
936        self,
937        crypto: &impl openmls_traits::crypto::OpenMlsCrypto,
938        pk: &crate::ciphersuite::OpenMlsSignaturePublicKey,
939    ) -> Result<Self::VerifiedStruct, crate::ciphersuite::signable::SignatureError> {
940        self.verify_no_out(crypto, pk)?;
941        Ok(LeafNode {
942            payload: self.payload,
943            signature: self.signature,
944        })
945    }
946}
947
948impl VerifiedStruct for LeafNode {}
949
950#[derive(Debug, Clone, PartialEq, Eq)]
951pub(crate) struct VerifiableUpdateLeafNode {
952    payload: LeafNodePayload,
953    signature: Signature,
954    tree_position: Option<TreePosition>,
955}
956
957impl VerifiableUpdateLeafNode {
958    pub(crate) fn add_tree_position(&mut self, tree_info: TreePosition) {
959        self.tree_position = Some(tree_info);
960    }
961
962    pub(crate) fn signature_key(&self) -> &SignaturePublicKey {
963        &self.payload.signature_key
964    }
965}
966
967impl Verifiable for VerifiableUpdateLeafNode {
968    type VerifiedStruct = LeafNode;
969
970    fn unsigned_payload(&self) -> Result<Vec<u8>, tls_codec::Error> {
971        let tree_info_tbs = match &self.tree_position {
972            Some(tree_position) => TreeInfoTbs::Commit(tree_position.clone()),
973            None => return Err(tls_codec::Error::InvalidInput),
974        };
975        let leaf_node_tbs = LeafNodeTbs {
976            payload: self.payload.clone(),
977            tree_info_tbs,
978        };
979        leaf_node_tbs.tls_serialize_detached()
980    }
981
982    fn signature(&self) -> &Signature {
983        &self.signature
984    }
985
986    fn label(&self) -> &str {
987        LEAF_NODE_SIGNATURE_LABEL
988    }
989
990    fn verify(
991        self,
992        crypto: &impl openmls_traits::crypto::OpenMlsCrypto,
993        pk: &crate::ciphersuite::OpenMlsSignaturePublicKey,
994    ) -> Result<Self::VerifiedStruct, crate::ciphersuite::signable::SignatureError> {
995        self.verify_no_out(crypto, pk)?;
996        Ok(LeafNode {
997            payload: self.payload,
998            signature: self.signature,
999        })
1000    }
1001}
1002
1003#[derive(Debug, Clone, PartialEq, Eq)]
1004pub(crate) struct VerifiableCommitLeafNode {
1005    payload: LeafNodePayload,
1006    signature: Signature,
1007    tree_position: Option<TreePosition>,
1008}
1009
1010impl VerifiableCommitLeafNode {
1011    pub(crate) fn add_tree_position(&mut self, tree_info: TreePosition) {
1012        self.tree_position = Some(tree_info);
1013    }
1014
1015    pub(crate) fn signature_key(&self) -> &SignaturePublicKey {
1016        &self.payload.signature_key
1017    }
1018}
1019
1020impl Verifiable for VerifiableCommitLeafNode {
1021    type VerifiedStruct = LeafNode;
1022
1023    fn unsigned_payload(&self) -> Result<Vec<u8>, tls_codec::Error> {
1024        let tree_info_tbs = match &self.tree_position {
1025            Some(tree_position) => TreeInfoTbs::Commit(tree_position.clone()),
1026            None => return Err(tls_codec::Error::InvalidInput),
1027        };
1028        let leaf_node_tbs = LeafNodeTbs {
1029            payload: self.payload.clone(),
1030            tree_info_tbs,
1031        };
1032
1033        leaf_node_tbs.tls_serialize_detached()
1034    }
1035
1036    fn signature(&self) -> &Signature {
1037        &self.signature
1038    }
1039
1040    fn label(&self) -> &str {
1041        LEAF_NODE_SIGNATURE_LABEL
1042    }
1043
1044    fn verify(
1045        self,
1046        crypto: &impl openmls_traits::crypto::OpenMlsCrypto,
1047        pk: &crate::ciphersuite::OpenMlsSignaturePublicKey,
1048    ) -> Result<Self::VerifiedStruct, crate::ciphersuite::signable::SignatureError> {
1049        self.verify_no_out(crypto, pk)?;
1050        Ok(LeafNode {
1051            payload: self.payload,
1052            signature: self.signature,
1053        })
1054    }
1055}
1056
1057impl Signable for LeafNodeTbs {
1058    type SignedOutput = LeafNode;
1059
1060    fn unsigned_payload(&self) -> Result<Vec<u8>, tls_codec::Error> {
1061        self.tls_serialize_detached()
1062    }
1063
1064    fn label(&self) -> &str {
1065        LEAF_NODE_SIGNATURE_LABEL
1066    }
1067}
1068
1069impl SignedStruct<LeafNodeTbs> for LeafNode {
1070    fn from_payload(tbs: LeafNodeTbs, signature: Signature, _serialized_payload: Vec<u8>) -> Self {
1071        Self {
1072            payload: tbs.payload,
1073            signature,
1074        }
1075    }
1076}
1077
1078#[cfg(all(test, feature = "generate-kats"))]
1079#[derive(Error, Debug, PartialEq, Clone)]
1080pub enum LeafNodeGenerationError<StorageError> {
1081    /// See [`LibraryError`] for more details.
1082    #[error(transparent)]
1083    LibraryError(#[from] LibraryError),
1084
1085    /// Error storing leaf private key in storage.
1086    #[error("Error storing leaf private key.")]
1087    StorageError(StorageError),
1088}
1089
1090/// Leaf Node Update Error
1091#[derive(Error, Debug, PartialEq, Clone)]
1092pub enum LeafNodeUpdateError<StorageError> {
1093    /// See [`LibraryError`] for more details.
1094    #[error(transparent)]
1095    LibraryError(#[from] LibraryError),
1096
1097    /// Error storing leaf private key in storage.
1098    #[error("Error storing leaf private key.")]
1099    Storage(StorageError),
1100
1101    /// Signature error.
1102    #[error(transparent)]
1103    Signature(#[from] crate::ciphersuite::signable::SignatureError),
1104}