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
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#[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 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 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 pub(crate) fn own_index(&self) -> LeafNodeIndex {
189 self.own_index
190 }
191
192 #[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 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 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 self.get_node(index.into())?.is_none() {
231 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 if self.get_node(parent_node.into())?.is_some() {
240 break;
241 }
242 }
243
244 empty_nodes.reverse();
246
247 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 let node_secret = match self.get_node(index.into())? {
256 Some(node) => &node.secret,
257 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 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 self.set_node(index.into(), None)
333 }
334
335 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 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 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 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 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 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 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 self.set_node(
492 left_index,
493 Some(SecretTreeNode {
494 secret: left_secret,
495 }),
496 )?;
497
498 self.set_node(
500 right_index,
501 Some(SecretTreeNode {
502 secret: right_secret,
503 }),
504 )?;
505
506 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}