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