mod chunk;
pub mod format;
mod head;
mod value;
pub use format::CborFmt;
pub use head::{
BreakFmt, CborHead, CborHeadFmt, CborHeadValue, CborInitial, CborInitialFmt, MajorType, BREAK,
};
pub use value::{CborArray, CborBytes, CborFloat, CborMap, CborText, CborValue, CborValueSpec};
pub const MAX_RECURSION_DEPTH: usize = 30;
#[cfg(test)]
mod tests {
use super::*;
use crate::core::exec::{ByteLen, Parser, Prepare, SerializerExt};
use alloc::borrow::ToOwned;
const GENERAL: bool = false;
const DETERMINISTIC: bool = true;
#[test]
fn rfc8949_major_types_conformance() {
let format = CborFmt::<GENERAL>;
assert_eq!(format.parse(&&[0x00][..]), Ok((1, CborValue::Integer(0))));
assert_eq!(format.parse(&&[0x17][..]), Ok((1, CborValue::Integer(23))));
assert_eq!(format.parse(&&[0x18, 0x18][..]), Ok((2, CborValue::Integer(24))));
assert_eq!(format.parse(&&[0x19, 0x01, 0x00][..]), Ok((3, CborValue::Integer(256))));
assert_eq!(
format.parse(&&[0x1b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff][..]),
Ok((9, CborValue::Integer(u64::MAX as i128)))
);
assert_eq!(format.parse(&&[0x20][..]), Ok((1, CborValue::Integer(-1))));
assert_eq!(format.parse(&&[0x37][..]), Ok((1, CborValue::Integer(-24))));
assert_eq!(format.parse(&&[0x38, 0x18][..]), Ok((2, CborValue::Integer(-25))));
assert_eq!(format.parse(&&[0x39, 0x03, 0xe7][..]), Ok((3, CborValue::Integer(-1000))));
assert_eq!(
format.parse(&&[0x3b, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff][..]),
Ok((9, CborValue::Integer(-1i128 - u64::MAX as i128)))
);
let raw_bytes = [0x44, 0x01, 0x02, 0x03, 0x04];
let (consumed, parsed) = format.parse(&&raw_bytes[..]).unwrap();
assert_eq!(consumed, 5);
match parsed {
CborValue::Bytes(CborBytes::Definite(slice)) => {
assert_eq!(slice, &[1, 2, 3, 4]);
assert_eq!(slice.as_ptr(), raw_bytes[1..].as_ptr());
}
_ => panic!("expected definite byte string"),
}
let raw_text = [0x63, 0xe6, 0xb0, 0xb4]; let (consumed, parsed) = format.parse(&&raw_text[..]).unwrap();
assert_eq!(consumed, 4);
match parsed {
CborValue::Text(CborText::Definite(s)) => {
assert_eq!(s, "æ°´");
assert_eq!(s.as_ptr(), raw_text[1..].as_ptr());
}
_ => panic!("expected definite text string"),
}
assert_eq!(format.parse(&&[0x80][..]), Ok((1, CborValue::Array(vec![]))));
assert_eq!(
format.parse(&&[0x82, 0x01, 0x02][..]),
Ok((3, CborValue::Array(vec![CborValue::Integer(1), CborValue::Integer(2)])))
);
assert_eq!(format.parse(&&[0xa0][..]), Ok((1, CborValue::Map(vec![]))));
assert_eq!(
format.parse(&&[0xa1, 0x61, b'a', 0x01][..]),
Ok((
4,
CborValue::Map(vec![(
CborValue::Text(CborText::Definite("a")),
CborValue::Integer(1)
)])
))
);
assert_eq!(
format.parse(&&[0xc0, 0x60][..]),
Ok((2, CborValue::Tag(0, alloc::boxed::Box::new(CborValue::Text(CborText::Definite(""))))))
);
assert_eq!(format.parse(&&[0xf4][..]), Ok((1, CborValue::Bool(false))));
assert_eq!(format.parse(&&[0xf5][..]), Ok((1, CborValue::Bool(true))));
assert_eq!(format.parse(&&[0xf6][..]), Ok((1, CborValue::Null)));
assert_eq!(format.parse(&&[0xf7][..]), Ok((1, CborValue::Undefined)));
assert_eq!(format.parse(&&[0xf0][..]), Ok((1, CborValue::Simple(16))));
assert_eq!(format.parse(&&[0xf8, 0x20][..]), Ok((2, CborValue::Simple(32))));
assert_eq!(format.parse(&&[0xf9, 0x3c, 0x00][..]), Ok((3, CborValue::Float(CborFloat::F16(0x3c00)))));
assert_eq!(format.parse(&&[0xfa, 0x47, 0xc3, 0x50, 0x00][..]), Ok((5, CborValue::Float(CborFloat::F32(0x47c35000)))));
assert_eq!(format.parse(&&[0xfb, 0x3f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00][..]), Ok((9, CborValue::Float(CborFloat::F64(0x3ff8000000000000)))));
}
#[test]
fn indefinite_streaming_and_chunk_security() {
let format = CborFmt::<GENERAL>;
let byte_chunks = [0x5f, 0x42, 0x01, 0x02, 0x41, 0x03, 0xff];
assert_eq!(
format.parse(&&byte_chunks[..]),
Ok((byte_chunks.len(), CborValue::Bytes(CborBytes::Indefinite(vec![1, 2, 3]))))
);
let text_chunks = [0x7f, 0x62, b'h', b'i', 0x61, b'!', 0xff];
assert_eq!(
format.parse(&&text_chunks[..]),
Ok((text_chunks.len(), CborValue::Text(CborText::Indefinite("hi!".to_owned()))))
);
let array = [0x9f, 0x01, 0x02, 0xff];
assert_eq!(
format.parse(&&array[..]),
Ok((array.len(), CborValue::Array(vec![CborValue::Integer(1), CborValue::Integer(2)])))
);
let map = [0xbf, 0x61, b'k', 0x01, 0xff];
assert_eq!(
format.parse(&&map[..]),
Ok((
map.len(),
CborValue::Map(vec![(
CborValue::Text(CborText::Definite("k")),
CborValue::Integer(1)
)])
))
);
assert!(format.parse(&&[0x5f, 0x5f, 0x41, 0x01, 0xff, 0xff][..]).is_err());
assert!(format.parse(&&[0x5f, 0x61, b'x', 0xff][..]).is_err());
assert!(format.parse(&&[0x7f, 0x41, 0x01, 0xff][..]).is_err());
assert!(format.parse(&&[0x5f, 0x01, 0xff][..]).is_err());
let split_utf8 = [0x7f, 0x61, 0xc2, 0x61, 0xa2, 0xff];
assert!(format.parse(&&split_utf8[..]).is_err());
assert!(format.parse(&&[0xbf, 0x01, 0xff][..]).is_err());
assert!(format.parse(&&[0xff][..]).is_err());
assert!(format.parse(&&[0x81, 0xff][..]).is_err());
}
#[test]
fn deterministic_dcbor_vs_general_cbor() {
let det = CborFmt::<DETERMINISTIC>;
let gen = CborFmt::<GENERAL>;
let non_minimal_uint = [0x18, 0x17];
assert!(det.parse(&&non_minimal_uint[..]).is_err());
assert_eq!(gen.parse(&&non_minimal_uint[..]), Ok((2, CborValue::Integer(23))));
let non_minimal_neg = [0x38, 0x17];
assert!(det.parse(&&non_minimal_neg[..]).is_err());
assert_eq!(gen.parse(&&non_minimal_neg[..]), Ok((2, CborValue::Integer(-24))));
let non_minimal_bstr_len = [0x58, 0x01, 0xaa];
assert!(det.parse(&&non_minimal_bstr_len[..]).is_err());
assert!(gen.parse(&&non_minimal_bstr_len[..]).is_ok());
let non_minimal_tag = [0xd8, 0x01, 0x00];
assert!(det.parse(&&non_minimal_tag[..]).is_err());
assert!(gen.parse(&&non_minimal_tag[..]).is_ok());
assert!(det.parse(&&[0x5f, 0x41, 0x01, 0xff][..]).is_err());
assert!(det.parse(&&[0x7f, 0x61, b'a', 0xff][..]).is_err());
assert!(det.parse(&&[0x9f, 0x01, 0xff][..]).is_err());
assert!(det.parse(&&[0xbf, 0x01, 0x02, 0xff][..]).is_err());
}
#[test]
fn truncation_and_malformed_input_robustness() {
let format = CborFmt::<GENERAL>;
assert!(format.parse(&&[0x18][..]).is_err());
assert!(format.parse(&&[0x19, 0x01][..]).is_err());
assert!(format.parse(&&[0x1a, 0x00, 0x01][..]).is_err());
assert!(format.parse(&&[0x1b, 0x00, 0x00, 0x00, 0x01][..]).is_err());
assert!(format.parse(&&[0x45, 0x01, 0x02, 0x03][..]).is_err()); assert!(format.parse(&&[0x65, b'a', b'b'][..]).is_err());
assert!(format.parse(&&[0x83, 0x01, 0x02][..]).is_err()); assert!(format.parse(&&[0xa1, 0x01][..]).is_err());
assert!(format.parse(&&[0x9f, 0x01, 0x02][..]).is_err());
assert!(format.parse(&&[0x5f, 0x41, 0x01][..]).is_err());
assert!(format.parse(&&[0x1c][..]).is_err());
assert!(format.parse(&&[0x1d][..]).is_err());
assert!(format.parse(&&[0x1e][..]).is_err());
}
#[test]
fn recursion_depth_limit_defense() {
let deeply_nested = [0x81, 0x81, 0x81, 0x81, 0x01];
let format_deep = CborFmt::<GENERAL, 6>;
assert!(format_deep.parse(&&deeply_nested[..]).is_ok());
let format_shallow = CborFmt::<GENERAL, 3>;
assert!(format_shallow.parse(&&deeply_nested[..]).is_err());
}
#[test]
fn serialization_and_roundtrip_invariants() {
let format = CborFmt::<DETERMINISTIC, 8>;
let value = CborValue::Map(vec![
(
CborValue::Integer(1),
CborValue::Array(vec![CborValue::Bool(true), CborValue::Null]),
),
(
CborValue::Text(CborText::Definite("tag")),
CborValue::Tag(42, alloc::boxed::Box::new(CborValue::Integer(100))),
),
]);
let len = format.prepare(&value).unwrap();
assert_eq!(format.length(&value), len);
let mut buffer = vec![0u8; len];
format.serialize(&value, buffer.as_mut_slice());
assert_eq!(format.parse(&&buffer[..]), Ok((len, value)));
let gen_format = CborFmt::<GENERAL, 8>;
let indefinite_bytes = CborValue::Bytes(CborBytes::Indefinite(vec![1, 2, 3]));
let len = gen_format.prepare(&indefinite_bytes).unwrap();
let mut buffer = vec![0u8; len];
gen_format.serialize(&indefinite_bytes, buffer.as_mut_slice());
assert_eq!(buffer, [0x43, 0x01, 0x02, 0x03]);
}
}