cbor: allow user to control nesting (#329)
* cbor: allow user to control nesting
- Make the default read/write entrypoints allow infinite nesting.
- Add {read,write}_nested() entrypoints that allow the crate user to
control the depth of nesting that's allowed.
- Along the way, convert the write[_nested] variants to return a
`Result<(), EncoderError>` rather than a bool. This exposes
more failure information (and forces the caller to take notice
of those tailures), and allows use of the ? operator.
* fixup: transmute error
Co-authored-by: kaczmarczyck <43844792+kaczmarczyck@users.noreply.github.com>
This commit is contained in:
@@ -37,11 +37,18 @@ pub enum DecoderError {
|
||||
OutOfRangeIntegerValue,
|
||||
}
|
||||
|
||||
/// Deserialize CBOR binary data to produce a single [`Value`], expecting that there
|
||||
/// is no additional data.
|
||||
/// Deserialize CBOR binary data to produce a single [`Value`], expecting that there is no additional data.
|
||||
/// Supports arbitrarily nested CBOR (so the [`DecoderError::TooMuchNesting`] error is never emitted).
|
||||
pub fn read(encoded_cbor: &[u8]) -> Result<Value, DecoderError> {
|
||||
read_nested(encoded_cbor, None)
|
||||
}
|
||||
|
||||
/// Deserialize CBOR binary data to produce a single [`Value`], expecting that there is no additional data. If
|
||||
/// `max_nest` is `Some(max)`, then nested structures are only supported up to the given limit (returning
|
||||
/// [`DecoderError::TooMuchNesting`] if the limit is hit).
|
||||
pub fn read_nested(encoded_cbor: &[u8], max_nest: Option<i8>) -> Result<Value, DecoderError> {
|
||||
let mut reader = Reader::new(encoded_cbor);
|
||||
let value = reader.decode_complete_data_item(Reader::MAX_NESTING_DEPTH)?;
|
||||
let value = reader.decode_complete_data_item(max_nest)?;
|
||||
if !reader.remaining_cbor.is_empty() {
|
||||
return Err(DecoderError::ExtraneousData);
|
||||
}
|
||||
@@ -53,8 +60,6 @@ struct Reader<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Reader<'a> {
|
||||
const MAX_NESTING_DEPTH: i8 = 4;
|
||||
|
||||
pub fn new(cbor: &'a [u8]) -> Reader<'a> {
|
||||
Reader {
|
||||
remaining_cbor: cbor,
|
||||
@@ -63,9 +68,9 @@ impl<'a> Reader<'a> {
|
||||
|
||||
pub fn decode_complete_data_item(
|
||||
&mut self,
|
||||
remaining_depth: i8,
|
||||
remaining_depth: Option<i8>,
|
||||
) -> Result<Value, DecoderError> {
|
||||
if remaining_depth < 0 {
|
||||
if remaining_depth.map_or(false, |d| d < 0) {
|
||||
return Err(DecoderError::TooMuchNesting);
|
||||
}
|
||||
|
||||
@@ -162,12 +167,12 @@ impl<'a> Reader<'a> {
|
||||
fn read_array_content(
|
||||
&mut self,
|
||||
size_value: u64,
|
||||
remaining_depth: i8,
|
||||
remaining_depth: Option<i8>,
|
||||
) -> Result<Value, DecoderError> {
|
||||
// Don't set the capacity already, it is an unsanitized input.
|
||||
let mut value_array = Vec::new();
|
||||
for _ in 0..size_value {
|
||||
value_array.push(self.decode_complete_data_item(remaining_depth - 1)?);
|
||||
value_array.push(self.decode_complete_data_item(remaining_depth.map(|d| d - 1))?);
|
||||
}
|
||||
Ok(cbor_array_vec!(value_array))
|
||||
}
|
||||
@@ -175,17 +180,20 @@ impl<'a> Reader<'a> {
|
||||
fn read_map_content(
|
||||
&mut self,
|
||||
size_value: u64,
|
||||
remaining_depth: i8,
|
||||
remaining_depth: Option<i8>,
|
||||
) -> Result<Value, DecoderError> {
|
||||
let mut value_map = Vec::<(Value, Value)>::new();
|
||||
for _ in 0..size_value {
|
||||
let key = self.decode_complete_data_item(remaining_depth - 1)?;
|
||||
let key = self.decode_complete_data_item(remaining_depth.map(|d| d - 1))?;
|
||||
if let Some(last_item) = value_map.last() {
|
||||
if last_item.0 >= key {
|
||||
return Err(DecoderError::OutOfOrderKey);
|
||||
}
|
||||
}
|
||||
value_map.push((key, self.decode_complete_data_item(remaining_depth - 1)?));
|
||||
value_map.push((
|
||||
key,
|
||||
self.decode_complete_data_item(remaining_depth.map(|d| d - 1))?,
|
||||
));
|
||||
}
|
||||
Ok(cbor_map_collection!(value_map))
|
||||
}
|
||||
@@ -193,9 +201,9 @@ impl<'a> Reader<'a> {
|
||||
fn read_tagged_content(
|
||||
&mut self,
|
||||
tag_value: u64,
|
||||
remaining_depth: i8,
|
||||
remaining_depth: Option<i8>,
|
||||
) -> Result<Value, DecoderError> {
|
||||
let inner_value = self.decode_complete_data_item(remaining_depth - 1)?;
|
||||
let inner_value = self.decode_complete_data_item(remaining_depth.map(|d| d - 1))?;
|
||||
Ok(cbor_tagged!(tag_value, inner_value))
|
||||
}
|
||||
|
||||
@@ -679,7 +687,7 @@ mod test {
|
||||
];
|
||||
for cbor in cases {
|
||||
let mut reader = Reader::new(&cbor);
|
||||
assert!(reader.decode_complete_data_item(0).is_ok());
|
||||
assert!(reader.decode_complete_data_item(Some(0)).is_ok());
|
||||
}
|
||||
let map_cbor = vec![
|
||||
0xa2, // map of 2 pairs
|
||||
@@ -690,11 +698,11 @@ mod test {
|
||||
];
|
||||
let mut reader = Reader::new(&map_cbor);
|
||||
assert_eq!(
|
||||
reader.decode_complete_data_item(1),
|
||||
reader.decode_complete_data_item(Some(1)),
|
||||
Err(DecoderError::TooMuchNesting)
|
||||
);
|
||||
reader = Reader::new(&map_cbor);
|
||||
assert!(reader.decode_complete_data_item(2).is_ok());
|
||||
assert!(reader.decode_complete_data_item(Some(2)).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -130,9 +130,9 @@ impl Ord for Value {
|
||||
(v1, v2) => {
|
||||
// This case could handle all of the above as well. Checking individually is faster.
|
||||
let mut encoding1 = Vec::new();
|
||||
write(v1.clone(), &mut encoding1);
|
||||
let _ = write(v1.clone(), &mut encoding1);
|
||||
let mut encoding2 = Vec::new();
|
||||
write(v2.clone(), &mut encoding2);
|
||||
let _ = write(v2.clone(), &mut encoding2);
|
||||
encoding1.cmp(&encoding2)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,11 +17,29 @@
|
||||
use super::values::{Constants, Value};
|
||||
use alloc::vec::Vec;
|
||||
|
||||
/// Possible errors from a serialization operation.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum EncoderError {
|
||||
TooMuchNesting,
|
||||
DuplicateMapKey,
|
||||
}
|
||||
|
||||
/// Convert a [`Value`] to serialized CBOR data, consuming it along the way and appending to the provided vector.
|
||||
/// Returns a `bool` indicating whether conversion succeeded.
|
||||
pub fn write(value: Value, encoded_cbor: &mut Vec<u8>) -> bool {
|
||||
/// Supports arbitrarily nested CBOR (so the [`EncoderError::TooMuchNesting`] error is never emitted).
|
||||
pub fn write(value: Value, encoded_cbor: &mut Vec<u8>) -> Result<(), EncoderError> {
|
||||
write_nested(value, encoded_cbor, None)
|
||||
}
|
||||
|
||||
/// Convert a [`Value`] to serialized CBOR data, consuming it along the way and appending to the provided vector. If
|
||||
/// `max_nest` is `Some(max)`, then nested structures are only supported up to the given limit (returning
|
||||
/// [`DecoderError::TooMuchNesting`] if the limit is hit).
|
||||
pub fn write_nested(
|
||||
value: Value,
|
||||
encoded_cbor: &mut Vec<u8>,
|
||||
max_nest: Option<i8>,
|
||||
) -> Result<(), EncoderError> {
|
||||
let mut writer = Writer::new(encoded_cbor);
|
||||
writer.encode_cbor(value, Writer::MAX_NESTING_DEPTH)
|
||||
writer.encode_cbor(value, max_nest)
|
||||
}
|
||||
|
||||
struct Writer<'a> {
|
||||
@@ -29,15 +47,17 @@ struct Writer<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Writer<'a> {
|
||||
const MAX_NESTING_DEPTH: i8 = 4;
|
||||
|
||||
pub fn new(encoded_cbor: &mut Vec<u8>) -> Writer {
|
||||
Writer { encoded_cbor }
|
||||
}
|
||||
|
||||
fn encode_cbor(&mut self, value: Value, remaining_depth: i8) -> bool {
|
||||
if remaining_depth < 0 {
|
||||
return false;
|
||||
fn encode_cbor(
|
||||
&mut self,
|
||||
value: Value,
|
||||
remaining_depth: Option<i8>,
|
||||
) -> Result<(), EncoderError> {
|
||||
if remaining_depth.map_or(false, |d| d < 0) {
|
||||
return Err(EncoderError::TooMuchNesting);
|
||||
}
|
||||
let type_label = value.type_label();
|
||||
match value {
|
||||
@@ -54,9 +74,7 @@ impl<'a> Writer<'a> {
|
||||
Value::Array(array) => {
|
||||
self.start_item(type_label, array.len() as u64);
|
||||
for el in array {
|
||||
if !self.encode_cbor(el, remaining_depth - 1) {
|
||||
return false;
|
||||
}
|
||||
self.encode_cbor(el, remaining_depth.map(|d| d - 1))?;
|
||||
}
|
||||
}
|
||||
Value::Map(mut map) => {
|
||||
@@ -64,27 +82,21 @@ impl<'a> Writer<'a> {
|
||||
let map_len = map.len();
|
||||
map.dedup_by(|a, b| a.0.eq(&b.0));
|
||||
if map_len != map.len() {
|
||||
return false;
|
||||
return Err(EncoderError::DuplicateMapKey);
|
||||
}
|
||||
self.start_item(type_label, map_len as u64);
|
||||
for (k, v) in map {
|
||||
if !self.encode_cbor(k, remaining_depth - 1) {
|
||||
return false;
|
||||
}
|
||||
if !self.encode_cbor(v, remaining_depth - 1) {
|
||||
return false;
|
||||
}
|
||||
self.encode_cbor(k, remaining_depth.map(|d| d - 1))?;
|
||||
self.encode_cbor(v, remaining_depth.map(|d| d - 1))?;
|
||||
}
|
||||
}
|
||||
Value::Tag(tag, inner_value) => {
|
||||
self.start_item(type_label, tag);
|
||||
if !self.encode_cbor(*inner_value, remaining_depth - 1) {
|
||||
return false;
|
||||
}
|
||||
self.encode_cbor(*inner_value, remaining_depth.map(|d| d - 1))?;
|
||||
}
|
||||
Value::Simple(simple_value) => self.start_item(type_label, simple_value as u64),
|
||||
}
|
||||
true
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn start_item(&mut self, type_label: u8, size: u64) {
|
||||
@@ -115,7 +127,7 @@ mod test {
|
||||
|
||||
fn write_return(value: Value) -> Option<Vec<u8>> {
|
||||
let mut encoded_cbor = Vec::new();
|
||||
if write(value, &mut encoded_cbor) {
|
||||
if write(value, &mut encoded_cbor).is_ok() {
|
||||
Some(encoded_cbor)
|
||||
} else {
|
||||
None
|
||||
@@ -459,12 +471,12 @@ mod test {
|
||||
for (value, level) in positive_cases {
|
||||
let mut buf = Vec::new();
|
||||
let mut writer = Writer::new(&mut buf);
|
||||
assert!(writer.encode_cbor(value, level));
|
||||
assert!(writer.encode_cbor(value, Some(level)).is_ok());
|
||||
}
|
||||
for (value, level) in negative_cases {
|
||||
let mut buf = Vec::new();
|
||||
let mut writer = Writer::new(&mut buf);
|
||||
assert!(!writer.encode_cbor(value, level));
|
||||
assert!(!writer.encode_cbor(value, Some(level)).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -480,9 +492,10 @@ mod test {
|
||||
|
||||
let mut buf = Vec::new();
|
||||
let mut writer = Writer::new(&mut buf);
|
||||
assert!(writer.encode_cbor(cbor_map.clone(), 2));
|
||||
assert!(writer.encode_cbor(cbor_map.clone(), Some(2)).is_ok());
|
||||
assert!(writer.encode_cbor(cbor_map.clone(), None).is_ok());
|
||||
writer = Writer::new(&mut buf);
|
||||
assert!(!writer.encode_cbor(cbor_map, 1));
|
||||
assert!(writer.encode_cbor(cbor_map, Some(1)).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -502,9 +515,9 @@ mod test {
|
||||
|
||||
let mut buf = Vec::new();
|
||||
let mut writer = Writer::new(&mut buf);
|
||||
assert!(writer.encode_cbor(cbor_array.clone(), 3));
|
||||
assert!(writer.encode_cbor(cbor_array.clone(), Some(3)).is_ok());
|
||||
writer = Writer::new(&mut buf);
|
||||
assert!(!writer.encode_cbor(cbor_array, 2));
|
||||
assert!(writer.encode_cbor(cbor_array, Some(2)).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -530,8 +543,9 @@ mod test {
|
||||
|
||||
let mut buf = Vec::new();
|
||||
let mut writer = Writer::new(&mut buf);
|
||||
assert!(writer.encode_cbor(cbor_map.clone(), 5));
|
||||
assert!(writer.encode_cbor(cbor_map.clone(), Some(5)).is_ok());
|
||||
assert!(writer.encode_cbor(cbor_map.clone(), None).is_ok());
|
||||
writer = Writer::new(&mut buf);
|
||||
assert!(!writer.encode_cbor(cbor_map, 4));
|
||||
assert!(writer.encode_cbor(cbor_map, Some(4)).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user