Skip to content

Commit 42f426f

Browse files
keytrans: Validate tree_size and pos before tree math
Co-authored-by: Steve Weis <sweis@anthropic.com>
1 parent 36ab938 commit 42f426f

File tree

3 files changed

+73
-0
lines changed

3 files changed

+73
-0
lines changed

rust/keytrans/src/implicit.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,4 +163,26 @@ mod test {
163163
let _ = root(start, n);
164164
});
165165
}
166+
167+
// These functions require `start < n`. Callers in `verify.rs` validate
168+
// `tree_size` and `pos` against `MAX_TREE_SIZE` and each other before
169+
// reaching this module.
170+
171+
#[test]
172+
#[should_panic(expected = "leaf node has no children")]
173+
fn precondition_frontier_panics_on_zero_n() {
174+
let _ = frontier(0, 0);
175+
}
176+
177+
#[test]
178+
#[should_panic(expected = "leaf node has no children")]
179+
fn precondition_root_panics_when_start_equals_n() {
180+
let _ = root(1, 1);
181+
}
182+
183+
#[test]
184+
#[should_panic(expected = "leaf node has no children")]
185+
fn precondition_root_panics_when_start_exceeds_n() {
186+
let _ = root(10, 5);
187+
}
166188
}

rust/keytrans/src/log.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,29 @@ mod math {
206206
assert_eq!(batch_copath(&[0, 2, 3, 4], 8), vec![2, 10, 13]);
207207
assert_eq!(batch_copath(&[0, 2, 3], 8), vec![2, 11]);
208208
}
209+
210+
// `node_width(n) = 2*(n-1)+1` overflows for `n > 2^63`. Callers in
211+
// `verify.rs` bound `tree_size` by `MAX_TREE_SIZE` (`2^62`) before
212+
// reaching this module.
213+
214+
#[test]
215+
#[should_panic(expected = "attempt to multiply with overflow")]
216+
fn precondition_node_width_panics_above_2_63() {
217+
let _ = node_width((1u64 << 63) + 1);
218+
}
219+
220+
#[test]
221+
#[should_panic(expected = "attempt to multiply with overflow")]
222+
fn precondition_root_panics_above_2_63() {
223+
let _ = root((1u64 << 63) + 1);
224+
}
225+
226+
#[test]
227+
fn node_width_safe_at_max_tree_size() {
228+
// MAX_TREE_SIZE = 2^62; node_width(2^62) = 2^63 - 1 fits in u64.
229+
let _ = node_width(1u64 << 62);
230+
let _ = root(1u64 << 62);
231+
}
209232
}
210233
}
211234

rust/keytrans/src/verify.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ const ALLOWED_AUDITOR_TIMESTAMP_RANGE: &TimestampRange = &TimestampRange {
3838
};
3939
const ENTRIES_MAX_BEHIND: u64 = 10_000_000;
4040

41+
/// Upper bound on `tree_size` accepted from the server.
42+
///
43+
/// Tree math in [`crate::implicit`] and [`crate::log`] performs arithmetic
44+
/// like `2*(n-1)+1` that would overflow for `n > 2^63`. Bounding `tree_size`
45+
/// at `2^62` keeps all such arithmetic safely within `u64`.
46+
const MAX_TREE_SIZE: u64 = 1u64 << 62;
47+
4148
#[derive(Clone, Debug, displaydoc::Display)]
4249
pub enum Error {
4350
/// Required field '{0}' not found
@@ -466,6 +473,19 @@ fn verify_search_internal(
466473
};
467474
let search_proof = get_proto_field(&search, "search")?;
468475

476+
// Validate server-controlled tree parameters before any tree math, which
477+
// would otherwise panic on out-of-range values.
478+
if tree_size == 0 || tree_size > MAX_TREE_SIZE {
479+
return Err(Error::VerificationFailed(
480+
"tree_size out of range".to_string(),
481+
));
482+
}
483+
if search_proof.pos >= tree_size {
484+
return Err(Error::VerificationFailed(
485+
"search proof pos must be less than tree_size".to_string(),
486+
));
487+
}
488+
469489
let guide = ProofGuide::new(version, search_proof.pos, tree_size);
470490

471491
let mut i = 0;
@@ -601,6 +621,14 @@ pub fn verify_monitor<'a>(
601621
let tree_head = get_proto_field(&full_tree_head.tree_head, "tree_head")?;
602622
let tree_size = tree_head.tree_size;
603623

624+
// Validate server-controlled tree_size before any tree math, which would
625+
// otherwise panic on out-of-range values.
626+
if tree_size == 0 || tree_size > MAX_TREE_SIZE {
627+
return Err(Error::VerificationFailed(
628+
"tree_size out of range".to_string(),
629+
));
630+
}
631+
604632
let MonitorContext {
605633
last_tree_head,
606634
last_distinguished_tree_head,

0 commit comments

Comments
 (0)