Skip to main content

cellos_supervisor/sni_proxy/h2/hpack/
integer.rs

1//! HPACK integer encoding (RFC 7541 §5.1).
2//!
3//! Pure-byte parsing: takes a buffer + the prefix bit-width and returns the
4//! integer + remaining slice. Bounded iteration to defend against an
5//! adversary who emits an unbounded chain of continuation octets.
6
7use super::super::error::H2ParseError;
8
9/// Decode an HPACK integer with `prefix_bits` of the first octet allocated
10/// to the integer prefix. Returns the integer and the slice immediately
11/// after it.
12pub fn decode_integer(buf: &[u8], prefix_bits: u32) -> Result<(u64, &[u8]), H2ParseError> {
13    if buf.is_empty() {
14        return Err(H2ParseError::MalformedHeaders);
15    }
16    let mask: u8 = ((1u16 << prefix_bits) - 1) as u8;
17    let prefix = buf[0] & mask;
18    if prefix < mask {
19        return Ok((prefix as u64, &buf[1..]));
20    }
21    // Multi-octet form. Continuation bytes use the bottom 7 bits as data;
22    // the top bit signals "more". Cap the iterations to bound CPU.
23    let mut value: u64 = mask as u64;
24    let mut shift: u32 = 0;
25    let mut idx: usize = 1;
26    loop {
27        if idx >= buf.len() {
28            return Err(H2ParseError::MalformedHeaders);
29        }
30        let b = buf[idx];
31        idx += 1;
32        let chunk = (b & 0x7F) as u64;
33        let shifted = chunk
34            .checked_shl(shift)
35            .ok_or(H2ParseError::MalformedHeaders)?;
36        value = value
37            .checked_add(shifted)
38            .ok_or(H2ParseError::MalformedHeaders)?;
39        if b & 0x80 == 0 {
40            return Ok((value, &buf[idx..]));
41        }
42        shift = shift.checked_add(7).ok_or(H2ParseError::MalformedHeaders)?;
43        if shift >= 64 {
44            return Err(H2ParseError::MalformedHeaders);
45        }
46    }
47}
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52
53    #[test]
54    fn decodes_short_form_under_prefix_max() {
55        // 7-bit prefix, value 10 < 127 → single octet.
56        let (v, rest) = decode_integer(&[0x0A, 0xAA], 7).unwrap();
57        assert_eq!(v, 10);
58        assert_eq!(rest, &[0xAA]);
59    }
60
61    #[test]
62    fn decodes_multi_octet_form() {
63        // 7-bit prefix saturated (127) + one continuation octet 0x05 → 132.
64        let (v, _) = decode_integer(&[0x7F, 0x05], 7).unwrap();
65        assert_eq!(v, 127 + 5);
66    }
67
68    #[test]
69    fn rejects_unbounded_continuation() {
70        // 9 continuation octets all with high bit set → shift overflow.
71        let buf: Vec<u8> = std::iter::once(0x7F)
72            .chain(std::iter::repeat_n(0xFF, 16))
73            .collect();
74        assert!(decode_integer(&buf, 7).is_err());
75    }
76}