Change the read()/write() methods to use a nesting limit of 127
internally, to avoid the possibility of heavily nested inputs exhausting
the stack.
Library users that still want to skip nesting checks can still get at
this functionality by using `{read,write}_nested(..., None)`.
553 lines
18 KiB
Rust
553 lines
18 KiB
Rust
// Copyright 2019 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
//! Functionality for serializing CBOR values into bytes.
|
|
|
|
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.
|
|
/// Maximum level of nesting supported is 127; more deeply nested structures will fail with
|
|
/// [`EncoderError::TooMuchNesting`].
|
|
pub fn write(value: Value, encoded_cbor: &mut Vec<u8>) -> Result<(), EncoderError> {
|
|
write_nested(value, encoded_cbor, Some(i8::MAX))
|
|
}
|
|
|
|
/// 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, max_nest)
|
|
}
|
|
|
|
struct Writer<'a> {
|
|
encoded_cbor: &'a mut Vec<u8>,
|
|
}
|
|
|
|
impl<'a> Writer<'a> {
|
|
pub fn new(encoded_cbor: &mut Vec<u8>) -> Writer {
|
|
Writer { encoded_cbor }
|
|
}
|
|
|
|
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 {
|
|
Value::Unsigned(unsigned) => self.start_item(type_label, unsigned),
|
|
Value::Negative(negative) => self.start_item(type_label, -(negative + 1) as u64),
|
|
Value::ByteString(byte_string) => {
|
|
self.start_item(type_label, byte_string.len() as u64);
|
|
self.encoded_cbor.extend(byte_string);
|
|
}
|
|
Value::TextString(text_string) => {
|
|
self.start_item(type_label, text_string.len() as u64);
|
|
self.encoded_cbor.extend(text_string.into_bytes());
|
|
}
|
|
Value::Array(array) => {
|
|
self.start_item(type_label, array.len() as u64);
|
|
for el in array {
|
|
self.encode_cbor(el, remaining_depth.map(|d| d - 1))?;
|
|
}
|
|
}
|
|
Value::Map(mut map) => {
|
|
map.sort_by(|a, b| a.0.cmp(&b.0));
|
|
let map_len = map.len();
|
|
map.dedup_by(|a, b| a.0.eq(&b.0));
|
|
if map_len != map.len() {
|
|
return Err(EncoderError::DuplicateMapKey);
|
|
}
|
|
self.start_item(type_label, map_len as u64);
|
|
for (k, v) in map {
|
|
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);
|
|
self.encode_cbor(*inner_value, remaining_depth.map(|d| d - 1))?;
|
|
}
|
|
Value::Simple(simple_value) => self.start_item(type_label, simple_value as u64),
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn start_item(&mut self, type_label: u8, size: u64) {
|
|
let (mut first_byte, shift) = match size {
|
|
0..=23 => (size as u8, 0),
|
|
24..=0xFF => (Constants::ADDITIONAL_INFORMATION_1_BYTE, 1),
|
|
0x100..=0xFFFF => (Constants::ADDITIONAL_INFORMATION_2_BYTES, 2),
|
|
0x10000..=0xFFFF_FFFF => (Constants::ADDITIONAL_INFORMATION_4_BYTES, 4),
|
|
_ => (Constants::ADDITIONAL_INFORMATION_8_BYTES, 8),
|
|
};
|
|
first_byte |= type_label << Constants::MAJOR_TYPE_BIT_SHIFT;
|
|
self.encoded_cbor.push(first_byte);
|
|
|
|
for i in (0..shift).rev() {
|
|
self.encoded_cbor.push((size >> (i * 8)) as u8);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
use crate::{
|
|
cbor_array, cbor_array_vec, cbor_bytes, cbor_false, cbor_int, cbor_map, cbor_null,
|
|
cbor_tagged, cbor_text, cbor_true, cbor_undefined,
|
|
};
|
|
use alloc::vec;
|
|
|
|
fn write_return(value: Value) -> Option<Vec<u8>> {
|
|
let mut encoded_cbor = Vec::new();
|
|
if write(value, &mut encoded_cbor).is_ok() {
|
|
Some(encoded_cbor)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_write_unsigned() {
|
|
let cases = vec![
|
|
(0, vec![0x00]),
|
|
(1, vec![0x01]),
|
|
(10, vec![0x0A]),
|
|
(23, vec![0x17]),
|
|
(24, vec![0x18, 0x18]),
|
|
(25, vec![0x18, 0x19]),
|
|
(100, vec![0x18, 0x64]),
|
|
(1000, vec![0x19, 0x03, 0xE8]),
|
|
(1000000, vec![0x1A, 0x00, 0x0F, 0x42, 0x40]),
|
|
(0xFFFFFFFF, vec![0x1A, 0xFF, 0xFF, 0xFF, 0xFF]),
|
|
(
|
|
0x100000000,
|
|
vec![0x1B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00],
|
|
),
|
|
(
|
|
core::i64::MAX,
|
|
vec![0x1B, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
|
|
),
|
|
];
|
|
for (unsigned, correct_cbor) in cases {
|
|
assert_eq!(write_return(cbor_int!(unsigned)), Some(correct_cbor));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_write_negative() {
|
|
let cases = vec![
|
|
(-1, vec![0x20]),
|
|
(-10, vec![0x29]),
|
|
(-23, vec![0x36]),
|
|
(-24, vec![0x37]),
|
|
(-25, vec![0x38, 0x18]),
|
|
(-100, vec![0x38, 0x63]),
|
|
(-1000, vec![0x39, 0x03, 0xE7]),
|
|
(-4294967296, vec![0x3A, 0xFF, 0xFF, 0xFF, 0xFF]),
|
|
(
|
|
-4294967297,
|
|
vec![0x3B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00],
|
|
),
|
|
(
|
|
core::i64::MIN,
|
|
vec![0x3B, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
|
|
),
|
|
];
|
|
for (negative, correct_cbor) in cases {
|
|
assert_eq!(write_return(cbor_int!(negative)), Some(correct_cbor));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_write_byte_string() {
|
|
let cases = vec![
|
|
(vec![], vec![0x40]),
|
|
(
|
|
vec![0x01, 0x02, 0x03, 0x04],
|
|
vec![0x44, 0x01, 0x02, 0x03, 0x04],
|
|
),
|
|
];
|
|
for (byte_string, correct_cbor) in cases {
|
|
assert_eq!(write_return(cbor_bytes!(byte_string)), Some(correct_cbor));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_write_text_string() {
|
|
let unicode_3byte = vec![0xE6, 0xB0, 0xB4];
|
|
let cases = vec![
|
|
("", vec![0x60]),
|
|
("a", vec![0x61, 0x61]),
|
|
("IETF", vec![0x64, 0x49, 0x45, 0x54, 0x46]),
|
|
("\"\\", vec![0x62, 0x22, 0x5C]),
|
|
("ü", vec![0x62, 0xC3, 0xBC]),
|
|
(
|
|
core::str::from_utf8(&unicode_3byte).unwrap(),
|
|
vec![0x63, 0xE6, 0xB0, 0xB4],
|
|
),
|
|
("𐅑", vec![0x64, 0xF0, 0x90, 0x85, 0x91]),
|
|
];
|
|
for (text_string, correct_cbor) in cases {
|
|
assert_eq!(write_return(cbor_text!(text_string)), Some(correct_cbor));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_write_array() {
|
|
let value_vec: Vec<_> = (1..26).collect();
|
|
let expected_cbor = vec![
|
|
0x98, 0x19, // array of 25 elements
|
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
|
|
0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x18, 0x18, 0x19,
|
|
];
|
|
assert_eq!(
|
|
write_return(cbor_array_vec!(value_vec)),
|
|
Some(expected_cbor)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_write_map() {
|
|
let value_map = cbor_map! {
|
|
0 => "a",
|
|
23 => "b",
|
|
24 => "c",
|
|
core::u8::MAX as i64 => "d",
|
|
256 => "e",
|
|
core::u16::MAX as i64 => "f",
|
|
65536 => "g",
|
|
core::u32::MAX as i64 => "h",
|
|
4294967296_i64 => "i",
|
|
core::i64::MAX => "j",
|
|
-1 => "k",
|
|
-24 => "l",
|
|
-25 => "m",
|
|
-256 => "n",
|
|
-257 => "o",
|
|
-65537 => "p",
|
|
-4294967296_i64 => "q",
|
|
-4294967297_i64 => "r",
|
|
core::i64::MIN => "s",
|
|
b"a" => 2,
|
|
b"bar" => 3,
|
|
b"foo" => 4,
|
|
"" => ".",
|
|
"e" => "E",
|
|
"aa" => "AA",
|
|
};
|
|
let expected_cbor = vec![
|
|
0xb8, 0x19, // map of 25 pairs:
|
|
0x00, // key 0
|
|
0x61, 0x61, // value "a"
|
|
0x17, // key 23
|
|
0x61, 0x62, // value "b"
|
|
0x18, 0x18, // key 24
|
|
0x61, 0x63, // value "c"
|
|
0x18, 0xFF, // key 255
|
|
0x61, 0x64, // value "d"
|
|
0x19, 0x01, 0x00, // key 256
|
|
0x61, 0x65, // value "e"
|
|
0x19, 0xFF, 0xFF, // key 65535
|
|
0x61, 0x66, // value "f"
|
|
0x1A, 0x00, 0x01, 0x00, 0x00, // key 65536
|
|
0x61, 0x67, // value "g"
|
|
0x1A, 0xFF, 0xFF, 0xFF, 0xFF, // key 4294967295
|
|
0x61, 0x68, // value "h"
|
|
// key 4294967296
|
|
0x1B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x61, 0x69, // value "i"
|
|
// key INT64_MAX
|
|
0x1b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x61, 0x6a, // value "j"
|
|
0x20, // key -1
|
|
0x61, 0x6b, // value "k"
|
|
0x37, // key -24
|
|
0x61, 0x6c, // value "l"
|
|
0x38, 0x18, // key -25
|
|
0x61, 0x6d, // value "m"
|
|
0x38, 0xFF, // key -256
|
|
0x61, 0x6e, // value "n"
|
|
0x39, 0x01, 0x00, // key -257
|
|
0x61, 0x6f, // value "o"
|
|
0x3A, 0x00, 0x01, 0x00, 0x00, // key -65537
|
|
0x61, 0x70, // value "p"
|
|
0x3A, 0xFF, 0xFF, 0xFF, 0xFF, // key -4294967296
|
|
0x61, 0x71, // value "q"
|
|
// key -4294967297
|
|
0x3B, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x61, 0x72, // value "r"
|
|
// key INT64_MIN
|
|
0x3b, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x61, 0x73, // value "s"
|
|
0x41, b'a', // byte string "a"
|
|
0x02, 0x43, b'b', b'a', b'r', // byte string "bar"
|
|
0x03, 0x43, b'f', b'o', b'o', // byte string "foo"
|
|
0x04, 0x60, // key ""
|
|
0x61, 0x2e, // value "."
|
|
0x61, 0x65, // key "e"
|
|
0x61, 0x45, // value "E"
|
|
0x62, 0x61, 0x61, // key "aa"
|
|
0x62, 0x41, 0x41, // value "AA"
|
|
];
|
|
assert_eq!(write_return(value_map), Some(expected_cbor));
|
|
}
|
|
|
|
#[test]
|
|
fn test_write_map_sorted() {
|
|
let sorted_map = cbor_map! {
|
|
0 => "a",
|
|
1 => "b",
|
|
-1 => "c",
|
|
-2 => "d",
|
|
b"a" => "e",
|
|
b"b" => "f",
|
|
"" => "g",
|
|
"c" => "h",
|
|
};
|
|
let unsorted_map = cbor_map! {
|
|
1 => "b",
|
|
-2 => "d",
|
|
b"b" => "f",
|
|
"c" => "h",
|
|
"" => "g",
|
|
b"a" => "e",
|
|
-1 => "c",
|
|
0 => "a",
|
|
};
|
|
assert_eq!(write_return(sorted_map), write_return(unsorted_map));
|
|
}
|
|
|
|
#[test]
|
|
fn test_write_map_duplicates() {
|
|
let duplicate0 = cbor_map! {
|
|
0 => "a",
|
|
-1 => "c",
|
|
b"a" => "e",
|
|
"c" => "g",
|
|
0 => "b",
|
|
};
|
|
assert_eq!(write_return(duplicate0), None);
|
|
let duplicate1 = cbor_map! {
|
|
0 => "a",
|
|
-1 => "c",
|
|
b"a" => "e",
|
|
"c" => "g",
|
|
-1 => "d",
|
|
};
|
|
assert_eq!(write_return(duplicate1), None);
|
|
let duplicate2 = cbor_map! {
|
|
0 => "a",
|
|
-1 => "c",
|
|
b"a" => "e",
|
|
"c" => "g",
|
|
b"a" => "f",
|
|
};
|
|
assert_eq!(write_return(duplicate2), None);
|
|
let duplicate3 = cbor_map! {
|
|
0 => "a",
|
|
-1 => "c",
|
|
b"a" => "e",
|
|
"c" => "g",
|
|
"c" => "h",
|
|
};
|
|
assert_eq!(write_return(duplicate3), None);
|
|
}
|
|
|
|
#[test]
|
|
fn test_write_map_with_array() {
|
|
let value_map = cbor_map! {
|
|
"a" => 1,
|
|
"b" => cbor_array![2, 3],
|
|
};
|
|
let expected_cbor = vec![
|
|
0xa2, // map of 2 pairs
|
|
0x61, 0x61, // "a"
|
|
0x01, 0x61, 0x62, // "b"
|
|
0x82, // array with 2 elements
|
|
0x02, 0x03,
|
|
];
|
|
assert_eq!(write_return(value_map), Some(expected_cbor));
|
|
}
|
|
|
|
#[test]
|
|
fn test_write_nested_map() {
|
|
let value_map = cbor_map! {
|
|
"a" => 1,
|
|
"b" => cbor_map! {
|
|
"c" => 2,
|
|
"d" => 3,
|
|
},
|
|
};
|
|
let expected_cbor = vec![
|
|
0xa2, // map of 2 pairs
|
|
0x61, 0x61, // "a"
|
|
0x01, 0x61, 0x62, // "b"
|
|
0xa2, // map of 2 pairs
|
|
0x61, 0x63, // "c"
|
|
0x02, 0x61, 0x64, // "d"
|
|
0x03,
|
|
];
|
|
assert_eq!(write_return(value_map), Some(expected_cbor));
|
|
}
|
|
|
|
#[test]
|
|
fn test_write_tagged() {
|
|
let cases = vec![
|
|
(cbor_tagged!(6, cbor_int!(0x42)), vec![0xc6, 0x18, 0x42]),
|
|
(cbor_tagged!(1, cbor_true!()), vec![0xc1, 0xf5]),
|
|
(
|
|
cbor_tagged!(
|
|
1000,
|
|
cbor_map! {
|
|
"a" => 1,
|
|
"b" => cbor_array![2, 3],
|
|
}
|
|
),
|
|
vec![
|
|
0xd9, 0x03, 0xe8, 0xa2, // map of 2 pairs
|
|
0x61, 0x61, // "a"
|
|
0x01, 0x61, 0x62, // "b"
|
|
0x82, // array with 2 elements
|
|
0x02, 0x03,
|
|
],
|
|
),
|
|
];
|
|
for (value, correct_cbor) in cases {
|
|
assert_eq!(write_return(value), Some(correct_cbor));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_write_simple() {
|
|
let cases = vec![
|
|
(cbor_false!(), vec![0xF4]),
|
|
(cbor_true!(), vec![0xF5]),
|
|
(cbor_null!(), vec![0xF6]),
|
|
(cbor_undefined!(), vec![0xF7]),
|
|
];
|
|
for (value, correct_cbor) in cases {
|
|
assert_eq!(write_return(value), Some(correct_cbor));
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_write_single_levels() {
|
|
let simple_array: Value = cbor_array![2];
|
|
let simple_map: Value = cbor_map! {"b" => 3};
|
|
let positive_cases = vec![
|
|
(cbor_int!(1), 0),
|
|
(cbor_bytes!(vec![0x01, 0x02, 0x03, 0x04]), 0),
|
|
(cbor_text!("a"), 0),
|
|
(cbor_array![], 0),
|
|
(cbor_map! {}, 0),
|
|
(simple_array.clone(), 1),
|
|
(simple_map.clone(), 1),
|
|
];
|
|
let negative_cases = vec![(simple_array.clone(), 0), (simple_map.clone(), 0)];
|
|
for (value, level) in positive_cases {
|
|
let mut buf = Vec::new();
|
|
let mut writer = Writer::new(&mut buf);
|
|
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, Some(level)).is_ok());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_write_nested_map_levels() {
|
|
let cbor_map: Value = cbor_map! {
|
|
"a" => 1,
|
|
"b" => cbor_map! {
|
|
"c" => 2,
|
|
"d" => 3,
|
|
},
|
|
};
|
|
|
|
let mut buf = Vec::new();
|
|
let mut writer = Writer::new(&mut buf);
|
|
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, Some(1)).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_write_unbalanced_nested_containers() {
|
|
let cbor_array: Value = cbor_array![
|
|
1,
|
|
2,
|
|
3,
|
|
cbor_map! {
|
|
"a" => 1,
|
|
"b" => cbor_map! {
|
|
"c" => 2,
|
|
"d" => 3,
|
|
},
|
|
},
|
|
];
|
|
|
|
let mut buf = Vec::new();
|
|
let mut writer = Writer::new(&mut buf);
|
|
assert!(writer.encode_cbor(cbor_array.clone(), Some(3)).is_ok());
|
|
writer = Writer::new(&mut buf);
|
|
assert!(writer.encode_cbor(cbor_array, Some(2)).is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_write_overly_nested() {
|
|
let cbor_map: Value = cbor_map! {
|
|
"a" => 1,
|
|
"b" => cbor_map! {
|
|
"c" => 2,
|
|
"d" => 3,
|
|
"h" => cbor_map! {
|
|
"e" => 4,
|
|
"f" => 5,
|
|
"g" => cbor_array![
|
|
6,
|
|
7,
|
|
cbor_array![
|
|
8
|
|
]
|
|
],
|
|
},
|
|
},
|
|
};
|
|
|
|
let mut buf = Vec::new();
|
|
let mut writer = Writer::new(&mut buf);
|
|
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, Some(4)).is_err());
|
|
}
|
|
}
|