Skip to main content

openmls/tree/
secret_tree.rs

1use openmls_traits::crypto::OpenMlsCrypto;
2use openmls_traits::types::{Ciphersuite, CryptoError};
3use thiserror::Error;
4use tls_codec::{Error as TlsCodecError, TlsSerialize, TlsSize};
5
6use super::*;
7#[cfg(feature = "virtual-clients-draft")]
8use crate::tree::dual_use_ratchet::DualUseRatchet;
9use crate::{
10    binary_tree::{
11        array_representation::{
12            direct_path, left, right, root, ParentNodeIndex, TreeNodeIndex, TreeSize,
13        },
14        LeafNodeIndex,
15    },
16    framing::*,
17    schedule::*,
18    tree::sender_ratchet::*,
19};
20
21/// Secret tree error
22#[derive(Error, Debug, Eq, PartialEq, Clone)]
23pub enum SecretTreeError {
24    /// Generation is too old to be processed.
25    #[error("Generation is too old to be processed.")]
26    TooDistantInThePast,
27    /// Generation is too far in the future to be processed.
28    #[error("Generation is too far in the future to be processed.")]
29    TooDistantInTheFuture,
30    /// Index out of bounds
31    #[error("Index out of bounds")]
32    IndexOutOfBounds,
33    /// The requested secret was deleted to preserve forward secrecy.
34    #[error("The requested secret was deleted to preserve forward secrecy.")]
35    SecretReuseError,
36    /// Cannot create decryption secrets from own sender ratchet or encryption secrets from the sender ratchets of other members.
37    #[error("Cannot create decryption secrets from own sender ratchet or encryption secrets from the sender ratchets of other members.")]
38    RatchetTypeError,
39    /// Ratchet generation has reached `u32::MAX`.
40    #[error("Ratchet generation has reached `u32::MAX`.")]
41    RatchetTooLong,
42    /// An unrecoverable error has occurred due to a bug in the implementation.
43    #[error("An unrecoverable error has occurred due to a bug in the implementation.")]
44    LibraryError,
45    /// See [`TlsCodecError`] for more details.
46    #[error(transparent)]
47    CodecError(#[from] TlsCodecError),
48    /// See [`CryptoError`] for more details.
49    #[error(transparent)]
50    CryptoError(#[from] CryptoError),
51}
52
53#[derive(Debug, Copy, Clone)]
54pub(crate) enum SecretType {
55    HandshakeSecret,
56    ApplicationSecret,
57}
58
59impl From<&ContentType> for SecretType {
60    fn from(content_type: &ContentType) -> SecretType {
61        match content_type {
62            ContentType::Application => SecretType::ApplicationSecret,
63            ContentType::Commit => SecretType::HandshakeSecret,
64            ContentType::Proposal => SecretType::HandshakeSecret,
65        }
66    }
67}
68
69impl From<&PublicMessage> for SecretType {
70    fn from(public_message: &PublicMessage) -> SecretType {
71        SecretType::from(&public_message.content_type())
72    }
73}
74
75pub(crate) fn derive_child_secrets(
76    parent_secret: &Secret,
77    crypto: &impl OpenMlsCrypto,
78    ciphersuite: Ciphersuite,
79) -> Result<(Secret, Secret), CryptoError> {
80    let left_child = parent_secret.kdf_expand_label(
81        crypto,
82        ciphersuite,
83        "tree",
84        b"left",
85        ciphersuite.hash_length(),
86    )?;
87    let right_child = parent_secret.kdf_expand_label(
88        crypto,
89        ciphersuite,
90        "tree",
91        b"right",
92        ciphersuite.hash_length(),
93    )?;
94    Ok((left_child, right_child))
95}
96
97/// Derives secrets for inner nodes of a SecretTree. This function corresponds
98/// to the `DeriveTreeSecret` defined in Section 10.1 of the MLS specification.
99#[inline]
100pub(crate) fn derive_tree_secret(
101    ciphersuite: Ciphersuite,
102    secret: &Secret,
103    label: &str,
104    generation: u32,
105    length: usize,
106    crypto: &impl OpenMlsCrypto,
107) -> Result<Secret, SecretTreeError> {
108    log::debug!(
109        "Derive tree secret with label \"{label}\" in generation {generation} of length {length}"
110    );
111    log_crypto!(trace, "Input secret {:x?}", secret.as_slice());
112
113    let secret = secret.kdf_expand_label(
114        crypto,
115        ciphersuite,
116        label,
117        &generation.to_be_bytes(),
118        length,
119    )?;
120    log_crypto!(trace, "Derived secret {:x?}", secret.as_slice());
121    Ok(secret)
122}
123
124#[derive(Debug, TlsSerialize, TlsSize)]
125pub(crate) struct TreeContext {
126    pub(crate) node: u32,
127    pub(crate) generation: u32,
128}
129
130#[derive(Debug, Serialize, Deserialize, TlsSerialize, TlsSize)]
131#[cfg_attr(any(feature = "test-utils", test), derive(PartialEq, Clone))]
132pub(crate) struct SecretTreeNode {
133    pub(crate) secret: Secret,
134}
135
136#[derive(Serialize, Deserialize)]
137#[cfg_attr(any(feature = "test-utils", test), derive(PartialEq, Clone))]
138#[cfg_attr(any(feature = "crypto-debug", test), derive(Debug))]
139pub(crate) struct SecretTree {
140    own_index: LeafNodeIndex,
141    leaf_nodes: Vec<Option<SecretTreeNode>>,
142    parent_nodes: Vec<Option<SecretTreeNode>>,
143    handshake_sender_ratchets: Vec<Option<SenderRatchet>>,
144    application_sender_ratchets: Vec<Option<SenderRatchet>>,
145    size: TreeSize,
146}
147
148impl SecretTree {
149    /// Creates a new SecretTree based on an `encryption_secret` and group size
150    /// `size`. The inner nodes of the tree and the SenderRatchets only get
151    /// initialized when secrets are requested either through `secret()`
152    /// or `next_secret()`.
153    pub(crate) fn new(
154        encryption_secret: EncryptionSecret,
155        size: TreeSize,
156        own_index: LeafNodeIndex,
157    ) -> Self {
158        let leaf_count = size.leaf_count() as usize;
159        let leaf_nodes = std::iter::repeat_with(|| None).take(leaf_count).collect();
160        let parent_nodes = std::iter::repeat_with(|| None).take(leaf_count).collect();
161        let handshake_sender_ratchets = std::iter::repeat_with(|| None).take(leaf_count).collect();
162        let application_sender_ratchets =
163            std::iter::repeat_with(|| None).take(leaf_count).collect();
164
165        let mut secret_tree = SecretTree {
166            own_index,
167            leaf_nodes,
168            parent_nodes,
169            handshake_sender_ratchets,
170            application_sender_ratchets,
171            size,
172        };
173
174        // Set the encryption secret in the root node. We ignore the Result
175        // here, since the we rely on the tree math to be correct, i.e.
176        // root(size) < size.
177        let _ = secret_tree.set_node(
178            root(size),
179            Some(SecretTreeNode {
180                secret: encryption_secret.consume_secret(),
181            }),
182        );
183
184        secret_tree
185    }
186
187    /// The leaf index this tree was built for.
188    pub(crate) fn own_index(&self) -> LeafNodeIndex {
189        self.own_index
190    }
191
192    /// Get current generation for a specific SenderRatchet
193    #[cfg(test)]
194    pub(crate) fn generation(&self, index: LeafNodeIndex, secret_type: SecretType) -> u32 {
195        match self
196            .ratchet_opt(index, secret_type)
197            .expect("Index out of bounds.")
198        {
199            Some(sender_ratchet) => sender_ratchet.generation(),
200            None => 0,
201        }
202    }
203
204    /// Initializes a specific SenderRatchet pair for a given index by
205    /// calculating and deleting the appropriate values in the SecretTree
206    fn initialize_sender_ratchets(
207        &mut self,
208        ciphersuite: Ciphersuite,
209        crypto: &impl OpenMlsCrypto,
210        index: LeafNodeIndex,
211    ) -> Result<(), SecretTreeError> {
212        log::trace!("Initializing sender ratchets for {index:?} with {ciphersuite}");
213        if index.u32() >= self.size.leaf_count() {
214            log::error!("Index is larger than the tree size.");
215            return Err(SecretTreeError::IndexOutOfBounds);
216        }
217        // Check if SenderRatchets are already initialized
218        if self
219            .ratchet_opt(index, SecretType::HandshakeSecret)?
220            .is_some()
221            && self
222                .ratchet_opt(index, SecretType::ApplicationSecret)?
223                .is_some()
224        {
225            log::trace!("The sender ratchets are initialized already.");
226            return Ok(());
227        }
228
229        // If we don't have a secret in the leaf node, we derive it
230        if self.get_node(index.into())?.is_none() {
231            // Collect empty nodes in the direct path until a non-empty node is
232            // found
233            let mut empty_nodes: Vec<ParentNodeIndex> = Vec::new();
234            let direct_path = direct_path(index, self.size);
235            log::trace!("Direct path for node {index:?}: {direct_path:?}");
236            for parent_node in direct_path {
237                empty_nodes.push(parent_node);
238                // Stop if we find a non-empty node
239                if self.get_node(parent_node.into())?.is_some() {
240                    break;
241                }
242            }
243
244            // Invert direct path
245            empty_nodes.reverse();
246
247            // Derive the secrets down all the way to the leaf node
248            for n in empty_nodes {
249                log::trace!("Derive down for parent node {n:?}.");
250                self.derive_down(ciphersuite, crypto, n)?;
251            }
252        }
253
254        // Calculate node secret and initialize SenderRatchets
255        let node_secret = match self.get_node(index.into())? {
256            Some(node) => &node.secret,
257            // We just derived all necessary nodes so this should not happen
258            None => {
259                return Err(SecretTreeError::LibraryError);
260            }
261        };
262
263        log::trace!("Deriving leaf node secrets for leaf {index:?}");
264
265        let handshake_ratchet_secret = node_secret.kdf_expand_label(
266            crypto,
267            ciphersuite,
268            "handshake",
269            b"",
270            ciphersuite.hash_length(),
271        )?;
272        let application_ratchet_secret = node_secret.kdf_expand_label(
273            crypto,
274            ciphersuite,
275            "application",
276            b"",
277            ciphersuite.hash_length(),
278        )?;
279
280        log_crypto!(
281            trace,
282            "handshake ratchet secret {handshake_ratchet_secret:x?}"
283        );
284        log_crypto!(
285            trace,
286            "application ratchet secret {application_ratchet_secret:x?}"
287        );
288
289        // Initialize SenderRatchets. We differentiate between the own
290        // SenderRatchets and the SenderRatchets of other members. With the
291        // `virtual-clients-draft` feature, the own SenderRatchets are
292        // [`DualUseRatchet`]s, which can produce key material for both
293        // encryption and decryption.
294        let (handshake_sender_ratchet, application_sender_ratchet) = if index == self.own_index {
295            #[cfg(not(feature = "virtual-clients-draft"))]
296            {
297                (
298                    SenderRatchet::EncryptionRatchet(RatchetSecret::initial_ratchet_secret(
299                        handshake_ratchet_secret,
300                    )),
301                    SenderRatchet::EncryptionRatchet(RatchetSecret::initial_ratchet_secret(
302                        application_ratchet_secret,
303                    )),
304                )
305            }
306            #[cfg(feature = "virtual-clients-draft")]
307            {
308                (
309                    SenderRatchet::DualUse(DualUseRatchet::new(handshake_ratchet_secret)),
310                    SenderRatchet::DualUse(DualUseRatchet::new(application_ratchet_secret)),
311                )
312            }
313        } else {
314            (
315                SenderRatchet::DecryptionRatchet(DecryptionRatchet::new(handshake_ratchet_secret)),
316                SenderRatchet::DecryptionRatchet(DecryptionRatchet::new(
317                    application_ratchet_secret,
318                )),
319            )
320        };
321
322        *self
323            .handshake_sender_ratchets
324            .get_mut(index.usize())
325            .ok_or(SecretTreeError::IndexOutOfBounds)? = Some(handshake_sender_ratchet);
326        *self
327            .application_sender_ratchets
328            .get_mut(index.usize())
329            .ok_or(SecretTreeError::IndexOutOfBounds)? = Some(application_sender_ratchet);
330
331        // Delete leaf node
332        self.set_node(index.into(), None)
333    }
334
335    /// Return RatchetSecrets for a given index and generation. This should be
336    /// called when decrypting an PrivateMessage received from another member.
337    /// Returns an error if index or generation are out of bound.
338    pub(crate) fn secret_for_decryption(
339        &mut self,
340        ciphersuite: Ciphersuite,
341        crypto: &impl OpenMlsCrypto,
342        index: LeafNodeIndex,
343        secret_type: SecretType,
344        generation: u32,
345        configuration: &SenderRatchetConfiguration,
346    ) -> Result<RatchetKeyMaterial, SecretTreeError> {
347        log::debug!(
348            "Generating {secret_type:?} decryption secret for {index:?} in generation {generation} with {ciphersuite}",
349        );
350        // Check tree bounds
351        if index.u32() >= self.size.leaf_count() {
352            log::error!("Sender index is not in the tree.");
353            return Err(SecretTreeError::IndexOutOfBounds);
354        }
355        if self.ratchet_opt(index, secret_type)?.is_none() {
356            log::trace!("   initialize sender ratchets");
357            self.initialize_sender_ratchets(ciphersuite, crypto, index)?;
358        }
359        match self.ratchet_mut(index, secret_type)? {
360            SenderRatchet::EncryptionRatchet(_) => {
361                log::error!("This is the wrong ratchet type.");
362                Err(SecretTreeError::RatchetTypeError)
363            }
364            SenderRatchet::DecryptionRatchet(dec_ratchet) => {
365                log::trace!("   getting secret for decryption");
366                dec_ratchet.secret_for_decryption(ciphersuite, crypto, generation, configuration)
367            }
368            #[cfg(feature = "virtual-clients-draft")]
369            SenderRatchet::DualUse(dual_ratchet) => {
370                log::trace!("   getting secret for decryption (own dual-use ratchet)");
371                dual_ratchet.secret_for_decryption(ciphersuite, crypto, generation, configuration)
372            }
373        }
374    }
375
376    /// Return the next RatchetSecrets that should be used for encryption and
377    /// then increments the generation.
378    pub(crate) fn secret_for_encryption(
379        &mut self,
380        ciphersuite: Ciphersuite,
381        crypto: &impl OpenMlsCrypto,
382        index: LeafNodeIndex,
383        secret_type: SecretType,
384    ) -> Result<(u32, RatchetKeyMaterial), SecretTreeError> {
385        if self.ratchet_opt(index, secret_type)?.is_none() {
386            self.initialize_sender_ratchets(ciphersuite, crypto, index)?;
387        }
388        match self.ratchet_mut(index, secret_type)? {
389            SenderRatchet::DecryptionRatchet(_) => {
390                log::error!("Invalid ratchet type. Got decryption, expected encryption.");
391                Err(SecretTreeError::RatchetTypeError)
392            }
393            SenderRatchet::EncryptionRatchet(enc_ratchet) => {
394                enc_ratchet.ratchet_forward(crypto, ciphersuite)
395            }
396            #[cfg(feature = "virtual-clients-draft")]
397            SenderRatchet::DualUse(dual_ratchet) => {
398                dual_ratchet.secret_for_encryption(ciphersuite, crypto)
399            }
400        }
401    }
402
403    #[cfg(feature = "virtual-clients-draft")]
404    pub(crate) fn delete_own_secret_for_generation(
405        &mut self,
406        secret_type: SecretType,
407        generation: Generation,
408    ) -> Result<(), SecretTreeError> {
409        match self.ratchet_mut(self.own_index, secret_type)? {
410            SenderRatchet::DualUse(dual_ratchet) => {
411                dual_ratchet.delete_secret_for_generation(generation);
412                Ok(())
413            }
414            SenderRatchet::EncryptionRatchet(_) | SenderRatchet::DecryptionRatchet(_) => {
415                Err(SecretTreeError::RatchetTypeError)
416            }
417        }
418    }
419
420    /// Returns a mutable reference to a specific SenderRatchet. The
421    /// SenderRatchet needs to be initialized.
422    fn ratchet_mut(
423        &mut self,
424        index: LeafNodeIndex,
425        secret_type: SecretType,
426    ) -> Result<&mut SenderRatchet, SecretTreeError> {
427        let sender_ratchets = match secret_type {
428            SecretType::HandshakeSecret => &mut self.handshake_sender_ratchets,
429            SecretType::ApplicationSecret => &mut self.application_sender_ratchets,
430        };
431        sender_ratchets
432            .get_mut(index.usize())
433            .and_then(|r| r.as_mut())
434            .ok_or(SecretTreeError::IndexOutOfBounds)
435    }
436
437    /// Returns an optional reference to a specific SenderRatchet
438    fn ratchet_opt(
439        &self,
440        index: LeafNodeIndex,
441        secret_type: SecretType,
442    ) -> Result<Option<&SenderRatchet>, SecretTreeError> {
443        let sender_ratchets = match secret_type {
444            SecretType::HandshakeSecret => &self.handshake_sender_ratchets,
445            SecretType::ApplicationSecret => &self.application_sender_ratchets,
446        };
447        match sender_ratchets.get(index.usize()) {
448            Some(sender_ratchet_option) => Ok(sender_ratchet_option.as_ref()),
449            None => Err(SecretTreeError::IndexOutOfBounds),
450        }
451    }
452
453    /// Derives the secrets for the child nodes in a SecretTree and blanks the
454    /// parent node.
455    fn derive_down(
456        &mut self,
457        ciphersuite: Ciphersuite,
458        crypto: &impl OpenMlsCrypto,
459        index_in_tree: ParentNodeIndex,
460    ) -> Result<(), SecretTreeError> {
461        log::debug!(
462            "Deriving tree secret for parent node {} with {}",
463            index_in_tree.u32(),
464            ciphersuite
465        );
466        let node_secret = match &self.get_node(index_in_tree.into())? {
467            Some(node) => &node.secret,
468            // This function only gets called top to bottom, so this should not happen
469            None => {
470                return Err(SecretTreeError::LibraryError);
471            }
472        };
473        log_crypto!(trace, "Node secret: {:x?}", node_secret.as_slice());
474        let left_index = left(index_in_tree);
475        let right_index = right(index_in_tree);
476        let (left_secret, right_secret) = derive_child_secrets(node_secret, crypto, ciphersuite)?;
477        log_crypto!(
478            trace,
479            "Left node ({}) secret: {:x?}",
480            left_index.test_u32(),
481            left_secret.as_slice()
482        );
483        log_crypto!(
484            trace,
485            "Right node ({}) secret: {:x?}",
486            right_index.test_u32(),
487            right_secret.as_slice()
488        );
489
490        // Populate left child
491        self.set_node(
492            left_index,
493            Some(SecretTreeNode {
494                secret: left_secret,
495            }),
496        )?;
497
498        // Populate right child
499        self.set_node(
500            right_index,
501            Some(SecretTreeNode {
502                secret: right_secret,
503            }),
504        )?;
505
506        // Delete parent node
507        self.set_node(index_in_tree.into(), None)
508    }
509
510    fn get_node(&self, index: TreeNodeIndex) -> Result<Option<&SecretTreeNode>, SecretTreeError> {
511        match index {
512            TreeNodeIndex::Leaf(leaf_index) => Ok(self
513                .leaf_nodes
514                .get(leaf_index.usize())
515                .ok_or(SecretTreeError::IndexOutOfBounds)?
516                .as_ref()),
517            TreeNodeIndex::Parent(parent_index) => Ok(self
518                .parent_nodes
519                .get(parent_index.usize())
520                .ok_or(SecretTreeError::IndexOutOfBounds)?
521                .as_ref()),
522        }
523    }
524
525    fn set_node(
526        &mut self,
527        index: TreeNodeIndex,
528        node: Option<SecretTreeNode>,
529    ) -> Result<(), SecretTreeError> {
530        match index {
531            TreeNodeIndex::Leaf(leaf_index) => {
532                *self
533                    .leaf_nodes
534                    .get_mut(leaf_index.usize())
535                    .ok_or(SecretTreeError::IndexOutOfBounds)? = node;
536            }
537            TreeNodeIndex::Parent(parent_index) => {
538                *self
539                    .parent_nodes
540                    .get_mut(parent_index.usize())
541                    .ok_or(SecretTreeError::IndexOutOfBounds)? = node;
542            }
543        }
544        Ok(())
545    }
546}