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#[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) 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#[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 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 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 pub(crate) fn own_index(&self) -> LeafNodeIndex {
210 self.own_index
211 }
212
213 #[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 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 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 self.get_node(index.into())?.is_none() {
252 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 if self.get_node(parent_node.into())?.is_some() {
261 break;
262 }
263 }
264
265 empty_nodes.reverse();
267
268 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 let node_secret = match self.get_node(index.into())? {
277 Some(node) => &node.secret,
278 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 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 self.set_node(index.into(), None)
354 }
355
356 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 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 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 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 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 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 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 self.set_node(
516 left_index,
517 Some(SecretTreeNode {
518 secret: left_secret,
519 }),
520 )?;
521
522 self.set_node(
524 right_index,
525 Some(SecretTreeNode {
526 secret: right_secret,
527 }),
528 )?;
529
530 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}