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, PeekedDualUseGeneration};
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#[derive(Error, Debug, Eq, PartialEq, Clone)]
23pub enum SecretTreeError {
24 #[error("Generation is too old to be processed.")]
26 TooDistantInThePast,
27 #[error("Generation is too far in the future to be processed.")]
29 TooDistantInTheFuture,
30 #[error("Index out of bounds")]
32 IndexOutOfBounds,
33 #[error("The requested secret was deleted to preserve forward secrecy.")]
35 SecretReuseError,
36 #[error("Cannot create decryption secrets from own sender ratchet or encryption secrets from the sender ratchets of other members.")]
38 RatchetTypeError,
39 #[error("Ratchet generation has reached `u32::MAX`.")]
41 RatchetTooLong,
42 #[error("An unrecoverable error has occurred due to a bug in the implementation.")]
44 LibraryError,
45 #[error(transparent)]
47 CodecError(#[from] TlsCodecError),
48 #[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#[cfg_attr(test, derive(Debug, PartialEq))]
77pub(crate) enum DecryptionSecret {
78 Available(RatchetKeyMaterial),
79 #[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) struct PendingRatchetUpdate {
99 index: LeafNodeIndex,
100 secret_type: SecretType,
101 peeked: Box<PeekedRatchetGeneration>,
102}
103
104enum PeekedRatchetGeneration {
105 Decryption(PeekedGeneration),
106 #[cfg(feature = "virtual-clients-draft")]
107 DualUse(PeekedDualUseGeneration),
108}
109
110impl std::fmt::Debug for PendingRatchetUpdate {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 f.debug_struct("PendingRatchetUpdate")
113 .field("index", &self.index)
114 .field("secret_type", &self.secret_type)
115 .finish_non_exhaustive()
116 }
117}
118
119pub(crate) fn derive_child_secrets(
120 parent_secret: &Secret,
121 crypto: &impl OpenMlsCrypto,
122 ciphersuite: Ciphersuite,
123) -> Result<(Secret, Secret), CryptoError> {
124 let left_child = parent_secret.kdf_expand_label(
125 crypto,
126 ciphersuite,
127 "tree",
128 b"left",
129 ciphersuite.hash_length(),
130 )?;
131 let right_child = parent_secret.kdf_expand_label(
132 crypto,
133 ciphersuite,
134 "tree",
135 b"right",
136 ciphersuite.hash_length(),
137 )?;
138 Ok((left_child, right_child))
139}
140
141#[inline]
144pub(crate) fn derive_tree_secret(
145 ciphersuite: Ciphersuite,
146 secret: &Secret,
147 label: &str,
148 generation: u32,
149 length: usize,
150 crypto: &impl OpenMlsCrypto,
151) -> Result<Secret, SecretTreeError> {
152 log::debug!(
153 "Derive tree secret with label \"{label}\" in generation {generation} of length {length}"
154 );
155 log_crypto!(trace, "Input secret {:x?}", secret.as_slice());
156
157 let secret = secret.kdf_expand_label(
158 crypto,
159 ciphersuite,
160 label,
161 &generation.to_be_bytes(),
162 length,
163 )?;
164 log_crypto!(trace, "Derived secret {:x?}", secret.as_slice());
165 Ok(secret)
166}
167
168#[derive(Debug, TlsSerialize, TlsSize)]
169pub(crate) struct TreeContext {
170 pub(crate) node: u32,
171 pub(crate) generation: u32,
172}
173
174#[derive(Debug, Serialize, Deserialize, TlsSerialize, TlsSize)]
175#[cfg_attr(any(feature = "test-utils", test), derive(PartialEq, Clone))]
176pub(crate) struct SecretTreeNode {
177 pub(crate) secret: Secret,
178}
179
180#[derive(Serialize, Deserialize)]
181#[cfg_attr(any(feature = "test-utils", test), derive(PartialEq, Clone))]
182#[cfg_attr(any(feature = "crypto-debug", test), derive(Debug))]
183pub(crate) struct SecretTree {
184 own_index: LeafNodeIndex,
185 leaf_nodes: Vec<Option<SecretTreeNode>>,
186 parent_nodes: Vec<Option<SecretTreeNode>>,
187 handshake_sender_ratchets: Vec<Option<SenderRatchet>>,
188 application_sender_ratchets: Vec<Option<SenderRatchet>>,
189 size: TreeSize,
190}
191
192impl SecretTree {
193 pub(crate) fn new(
198 encryption_secret: EncryptionSecret,
199 size: TreeSize,
200 own_index: LeafNodeIndex,
201 ) -> Self {
202 let leaf_count = size.leaf_count() as usize;
203 let leaf_nodes = std::iter::repeat_with(|| None).take(leaf_count).collect();
204 let parent_nodes = std::iter::repeat_with(|| None).take(leaf_count).collect();
205 let handshake_sender_ratchets = std::iter::repeat_with(|| None).take(leaf_count).collect();
206 let application_sender_ratchets =
207 std::iter::repeat_with(|| None).take(leaf_count).collect();
208
209 let mut secret_tree = SecretTree {
210 own_index,
211 leaf_nodes,
212 parent_nodes,
213 handshake_sender_ratchets,
214 application_sender_ratchets,
215 size,
216 };
217
218 let _ = secret_tree.set_node(
222 root(size),
223 Some(SecretTreeNode {
224 secret: encryption_secret.consume_secret(),
225 }),
226 );
227
228 secret_tree
229 }
230
231 pub(crate) fn own_index(&self) -> LeafNodeIndex {
233 self.own_index
234 }
235
236 #[cfg(test)]
238 pub(crate) fn generation(&self, index: LeafNodeIndex, secret_type: SecretType) -> u32 {
239 match self
240 .ratchet_opt(index, secret_type)
241 .expect("Index out of bounds.")
242 {
243 Some(sender_ratchet) => sender_ratchet.generation(),
244 None => 0,
245 }
246 }
247
248 #[cfg(test)]
249 pub(crate) fn sender_ratchet(
250 &self,
251 index: LeafNodeIndex,
252 secret_type: SecretType,
253 ) -> Option<&SenderRatchet> {
254 self.ratchet_opt(index, secret_type).ok().flatten()
255 }
256
257 fn initialize_sender_ratchets(
260 &mut self,
261 ciphersuite: Ciphersuite,
262 crypto: &impl OpenMlsCrypto,
263 index: LeafNodeIndex,
264 ) -> Result<(), SecretTreeError> {
265 log::trace!("Initializing sender ratchets for {index:?} with {ciphersuite}");
266 if index.u32() >= self.size.leaf_count() {
267 log::error!("Index is larger than the tree size.");
268 return Err(SecretTreeError::IndexOutOfBounds);
269 }
270 if self
272 .ratchet_opt(index, SecretType::HandshakeSecret)?
273 .is_some()
274 && self
275 .ratchet_opt(index, SecretType::ApplicationSecret)?
276 .is_some()
277 {
278 log::trace!("The sender ratchets are initialized already.");
279 return Ok(());
280 }
281
282 if self.get_node(index.into())?.is_none() {
284 let mut empty_nodes: Vec<ParentNodeIndex> = Vec::new();
287 let direct_path = direct_path(index, self.size);
288 log::trace!("Direct path for node {index:?}: {direct_path:?}");
289 for parent_node in direct_path {
290 empty_nodes.push(parent_node);
291 if self.get_node(parent_node.into())?.is_some() {
293 break;
294 }
295 }
296
297 empty_nodes.reverse();
299
300 for n in empty_nodes {
302 log::trace!("Derive down for parent node {n:?}.");
303 self.derive_down(ciphersuite, crypto, n)?;
304 }
305 }
306
307 let node_secret = match self.get_node(index.into())? {
309 Some(node) => &node.secret,
310 None => {
312 return Err(SecretTreeError::LibraryError);
313 }
314 };
315
316 log::trace!("Deriving leaf node secrets for leaf {index:?}");
317
318 let handshake_ratchet_secret = node_secret.kdf_expand_label(
319 crypto,
320 ciphersuite,
321 "handshake",
322 b"",
323 ciphersuite.hash_length(),
324 )?;
325 let application_ratchet_secret = node_secret.kdf_expand_label(
326 crypto,
327 ciphersuite,
328 "application",
329 b"",
330 ciphersuite.hash_length(),
331 )?;
332
333 log_crypto!(
334 trace,
335 "handshake ratchet secret {handshake_ratchet_secret:x?}"
336 );
337 log_crypto!(
338 trace,
339 "application ratchet secret {application_ratchet_secret:x?}"
340 );
341
342 let (handshake_sender_ratchet, application_sender_ratchet) = if index == self.own_index {
348 #[cfg(not(feature = "virtual-clients-draft"))]
349 {
350 (
351 SenderRatchet::EncryptionRatchet(RatchetSecret::initial_ratchet_secret(
352 handshake_ratchet_secret,
353 )),
354 SenderRatchet::EncryptionRatchet(RatchetSecret::initial_ratchet_secret(
355 application_ratchet_secret,
356 )),
357 )
358 }
359 #[cfg(feature = "virtual-clients-draft")]
360 {
361 (
362 SenderRatchet::DualUse(DualUseRatchet::new(handshake_ratchet_secret)),
363 SenderRatchet::DualUse(DualUseRatchet::new(application_ratchet_secret)),
364 )
365 }
366 } else {
367 (
368 SenderRatchet::DecryptionRatchet(DecryptionRatchet::new(handshake_ratchet_secret)),
369 SenderRatchet::DecryptionRatchet(DecryptionRatchet::new(
370 application_ratchet_secret,
371 )),
372 )
373 };
374
375 *self
376 .handshake_sender_ratchets
377 .get_mut(index.usize())
378 .ok_or(SecretTreeError::IndexOutOfBounds)? = Some(handshake_sender_ratchet);
379 *self
380 .application_sender_ratchets
381 .get_mut(index.usize())
382 .ok_or(SecretTreeError::IndexOutOfBounds)? = Some(application_sender_ratchet);
383
384 self.set_node(index.into(), None)
386 }
387
388 pub(crate) fn prepare_decryption(
399 &mut self,
400 ciphersuite: Ciphersuite,
401 crypto: &impl OpenMlsCrypto,
402 index: LeafNodeIndex,
403 secret_type: SecretType,
404 generation: u32,
405 configuration: &SenderRatchetConfiguration,
406 ) -> Result<(DecryptionSecret, Option<PendingRatchetUpdate>), SecretTreeError> {
407 log::debug!(
408 "Generating {secret_type:?} decryption secret for {index:?} in generation {generation} with {ciphersuite}",
409 );
410 if index.u32() >= self.size.leaf_count() {
412 log::error!("Sender index is not in the tree.");
413 return Err(SecretTreeError::IndexOutOfBounds);
414 }
415 if self.ratchet_opt(index, secret_type)?.is_none() {
416 log::trace!(" initialize sender ratchets");
417 self.initialize_sender_ratchets(ciphersuite, crypto, index)?;
418 }
419 let (decryption_secret, peeked) = match self.ratchet_mut(index, secret_type)? {
420 SenderRatchet::EncryptionRatchet(_) => {
421 log::error!("This is the wrong ratchet type.");
422 return Err(SecretTreeError::RatchetTypeError);
423 }
424 SenderRatchet::DecryptionRatchet(dec_ratchet) => {
425 log::trace!(" getting secret for decryption");
426 let (ratchet_key_material, peeked) =
427 dec_ratchet.peek(ciphersuite, crypto, generation, configuration)?;
428 (
429 DecryptionSecret::Available(ratchet_key_material),
430 Some(PeekedRatchetGeneration::Decryption(peeked)),
431 )
432 }
433 #[cfg(feature = "virtual-clients-draft")]
434 SenderRatchet::DualUse(dual_ratchet) => {
435 log::trace!(" getting secret for decryption (own dual-use ratchet)");
436 let (decryption_secret, peeked) =
437 dual_ratchet.peek(ciphersuite, crypto, generation, configuration)?;
438 (
439 decryption_secret,
440 peeked.map(PeekedRatchetGeneration::DualUse),
441 )
442 }
443 };
444 let update = peeked.map(|peeked| PendingRatchetUpdate {
445 index,
446 secret_type,
447 peeked: Box::new(peeked),
448 });
449 Ok((decryption_secret, update))
450 }
451
452 pub(crate) fn apply_decryption_update(
454 &mut self,
455 update: PendingRatchetUpdate,
456 configuration: &SenderRatchetConfiguration,
457 ) -> Result<(), SecretTreeError> {
458 let ratchet = self.ratchet_mut(update.index, update.secret_type)?;
459 match (ratchet, *update.peeked) {
460 (
461 SenderRatchet::DecryptionRatchet(dec_ratchet),
462 PeekedRatchetGeneration::Decryption(peeked),
463 ) => dec_ratchet.consume(peeked, configuration),
464 #[cfg(feature = "virtual-clients-draft")]
465 (SenderRatchet::DualUse(dual_ratchet), PeekedRatchetGeneration::DualUse(peeked)) => {
466 dual_ratchet.consume(peeked, configuration)
467 }
468 _ => Err(SecretTreeError::RatchetTypeError),
469 }
470 }
471
472 #[cfg(any(feature = "test-utils", test))]
476 pub(crate) fn secret_for_decryption(
477 &mut self,
478 ciphersuite: Ciphersuite,
479 crypto: &impl OpenMlsCrypto,
480 index: LeafNodeIndex,
481 secret_type: SecretType,
482 generation: u32,
483 configuration: &SenderRatchetConfiguration,
484 ) -> Result<DecryptionSecret, SecretTreeError> {
485 let (decryption_secret, update) = self.prepare_decryption(
486 ciphersuite,
487 crypto,
488 index,
489 secret_type,
490 generation,
491 configuration,
492 )?;
493 if let Some(update) = update {
494 self.apply_decryption_update(update, configuration)?;
495 }
496 Ok(decryption_secret)
497 }
498
499 pub(crate) fn secret_for_encryption(
502 &mut self,
503 ciphersuite: Ciphersuite,
504 crypto: &impl OpenMlsCrypto,
505 index: LeafNodeIndex,
506 secret_type: SecretType,
507 ) -> Result<(u32, RatchetKeyMaterial), SecretTreeError> {
508 if self.ratchet_opt(index, secret_type)?.is_none() {
509 self.initialize_sender_ratchets(ciphersuite, crypto, index)?;
510 }
511 match self.ratchet_mut(index, secret_type)? {
512 SenderRatchet::DecryptionRatchet(_) => {
513 log::error!("Invalid ratchet type. Got decryption, expected encryption.");
514 Err(SecretTreeError::RatchetTypeError)
515 }
516 SenderRatchet::EncryptionRatchet(enc_ratchet) => {
517 enc_ratchet.ratchet_forward(crypto, ciphersuite)
518 }
519 #[cfg(feature = "virtual-clients-draft")]
520 SenderRatchet::DualUse(dual_ratchet) => {
521 dual_ratchet.secret_for_encryption(ciphersuite, crypto)
522 }
523 }
524 }
525
526 #[cfg(feature = "virtual-clients-draft")]
527 pub(crate) fn delete_own_secret_for_generation(
528 &mut self,
529 secret_type: SecretType,
530 generation: Generation,
531 ) -> Result<(), SecretTreeError> {
532 match self.ratchet_mut(self.own_index, secret_type)? {
533 SenderRatchet::DualUse(dual_ratchet) => {
534 dual_ratchet.delete_secret_for_generation(generation);
535 Ok(())
536 }
537 SenderRatchet::EncryptionRatchet(_) | SenderRatchet::DecryptionRatchet(_) => {
538 Err(SecretTreeError::RatchetTypeError)
539 }
540 }
541 }
542
543 fn ratchet_mut(
546 &mut self,
547 index: LeafNodeIndex,
548 secret_type: SecretType,
549 ) -> Result<&mut SenderRatchet, SecretTreeError> {
550 let sender_ratchets = match secret_type {
551 SecretType::HandshakeSecret => &mut self.handshake_sender_ratchets,
552 SecretType::ApplicationSecret => &mut self.application_sender_ratchets,
553 };
554 sender_ratchets
555 .get_mut(index.usize())
556 .and_then(|r| r.as_mut())
557 .ok_or(SecretTreeError::IndexOutOfBounds)
558 }
559
560 fn ratchet_opt(
562 &self,
563 index: LeafNodeIndex,
564 secret_type: SecretType,
565 ) -> Result<Option<&SenderRatchet>, SecretTreeError> {
566 let sender_ratchets = match secret_type {
567 SecretType::HandshakeSecret => &self.handshake_sender_ratchets,
568 SecretType::ApplicationSecret => &self.application_sender_ratchets,
569 };
570 match sender_ratchets.get(index.usize()) {
571 Some(sender_ratchet_option) => Ok(sender_ratchet_option.as_ref()),
572 None => Err(SecretTreeError::IndexOutOfBounds),
573 }
574 }
575
576 fn derive_down(
579 &mut self,
580 ciphersuite: Ciphersuite,
581 crypto: &impl OpenMlsCrypto,
582 index_in_tree: ParentNodeIndex,
583 ) -> Result<(), SecretTreeError> {
584 log::debug!(
585 "Deriving tree secret for parent node {} with {}",
586 index_in_tree.u32(),
587 ciphersuite
588 );
589 let node_secret = match &self.get_node(index_in_tree.into())? {
590 Some(node) => &node.secret,
591 None => {
593 return Err(SecretTreeError::LibraryError);
594 }
595 };
596 log_crypto!(trace, "Node secret: {:x?}", node_secret.as_slice());
597 let left_index = left(index_in_tree);
598 let right_index = right(index_in_tree);
599 let (left_secret, right_secret) = derive_child_secrets(node_secret, crypto, ciphersuite)?;
600 log_crypto!(
601 trace,
602 "Left node ({}) secret: {:x?}",
603 left_index.test_u32(),
604 left_secret.as_slice()
605 );
606 log_crypto!(
607 trace,
608 "Right node ({}) secret: {:x?}",
609 right_index.test_u32(),
610 right_secret.as_slice()
611 );
612
613 self.set_node(
615 left_index,
616 Some(SecretTreeNode {
617 secret: left_secret,
618 }),
619 )?;
620
621 self.set_node(
623 right_index,
624 Some(SecretTreeNode {
625 secret: right_secret,
626 }),
627 )?;
628
629 self.set_node(index_in_tree.into(), None)
631 }
632
633 fn get_node(&self, index: TreeNodeIndex) -> Result<Option<&SecretTreeNode>, SecretTreeError> {
634 match index {
635 TreeNodeIndex::Leaf(leaf_index) => Ok(self
636 .leaf_nodes
637 .get(leaf_index.usize())
638 .ok_or(SecretTreeError::IndexOutOfBounds)?
639 .as_ref()),
640 TreeNodeIndex::Parent(parent_index) => Ok(self
641 .parent_nodes
642 .get(parent_index.usize())
643 .ok_or(SecretTreeError::IndexOutOfBounds)?
644 .as_ref()),
645 }
646 }
647
648 fn set_node(
649 &mut self,
650 index: TreeNodeIndex,
651 node: Option<SecretTreeNode>,
652 ) -> Result<(), SecretTreeError> {
653 match index {
654 TreeNodeIndex::Leaf(leaf_index) => {
655 *self
656 .leaf_nodes
657 .get_mut(leaf_index.usize())
658 .ok_or(SecretTreeError::IndexOutOfBounds)? = node;
659 }
660 TreeNodeIndex::Parent(parent_index) => {
661 *self
662 .parent_nodes
663 .get_mut(parent_index.usize())
664 .ok_or(SecretTreeError::IndexOutOfBounds)? = node;
665 }
666 }
667 Ok(())
668 }
669}