use openmls::treesync::RatchetTreeIn;
use tls_codec::{Deserialize, DeserializeBytes};
fn push_varint(len: usize, out: &mut Vec<u8>) {
if len <= 0x3f {
out.push(len as u8);
} else if len <= 0x3f_ff {
out.extend_from_slice(&((len as u16) | 0x4000).to_be_bytes());
} else if len <= 0x3f_ff_ff_ff {
out.extend_from_slice(&((len as u32) | 0x8000_0000).to_be_bytes());
} else {
panic!("length beyond 30 bits");
}
}
fn leaf_bytes(extensions_content: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(32 + extensions_content.len());
out.push(0x00); out.push(0x00); out.extend_from_slice(&[0x00, 0x01, 0x00]); out.extend_from_slice(&[0x00; 5]); out.push(0x01); out.extend_from_slice(&[0u8; 16]); push_varint(extensions_content.len(), &mut out);
out.extend_from_slice(extensions_content);
out.push(0x00); out
}
fn single_leaf_tree(leaf: &[u8]) -> Vec<u8> {
let mut content = Vec::with_capacity(2 + leaf.len());
content.push(0x01); content.push(0x01); content.extend_from_slice(leaf);
let mut out = Vec::with_capacity(4 + content.len());
push_varint(content.len(), &mut out);
out.extend_from_slice(&content);
out
}
fn ratchet_tree_extension(tree: &[u8]) -> Vec<u8> {
let mut out = Vec::with_capacity(3 + tree.len());
out.extend_from_slice(&[0x00, 0x02]); push_varint(tree.len(), &mut out);
out.extend_from_slice(tree);
out
}
fn nested_tree(depth: usize) -> Vec<u8> {
let mut tree = single_leaf_tree(&leaf_bytes(&[]));
for _ in 0..depth {
let extension = ratchet_tree_extension(&tree);
tree = single_leaf_tree(&leaf_bytes(&extension));
}
tree
}
#[test]
fn depth_zero_tree_decodes() {
let bytes = nested_tree(0);
RatchetTreeIn::tls_deserialize_exact(&bytes).expect("plain single-leaf tree must decode");
}
#[test]
fn shallow_nested_tree_returns_decode_error() {
for depth in [1_usize, 2, 8] {
let bytes = nested_tree(depth);
assert!(
RatchetTreeIn::tls_deserialize_exact(&bytes).is_err(),
"nested ratchet_tree (depth {depth}) must be rejected"
);
}
}
#[test]
fn deep_nested_tree_drives_decoder_recursion() {
let bytes = nested_tree(2_000);
assert!(
RatchetTreeIn::tls_deserialize_exact(&bytes).is_err(),
"deeply nested ratchet_tree must be a decoding error, not unbounded recursion"
);
}
#[test]
fn deep_nested_tree_via_raw_extension_entry_is_bounded() {
let tree = nested_tree(2_000);
let bytes = ratchet_tree_extension(&tree);
let _ = openmls::extensions::Extension::tls_deserialize_bytes(&bytes);
}