summaryrefslogtreecommitdiffstats
path: root/third_party/rust/moz_cbor/src/decoder.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /third_party/rust/moz_cbor/src/decoder.rs
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/moz_cbor/src/decoder.rs')
-rw-r--r--third_party/rust/moz_cbor/src/decoder.rs165
1 files changed, 165 insertions, 0 deletions
diff --git a/third_party/rust/moz_cbor/src/decoder.rs b/third_party/rust/moz_cbor/src/decoder.rs
new file mode 100644
index 0000000000..f93bb13f1c
--- /dev/null
+++ b/third_party/rust/moz_cbor/src/decoder.rs
@@ -0,0 +1,165 @@
+use std::collections::BTreeMap;
+use std::io::{Cursor, Read, Seek, SeekFrom};
+use {CborError, CborType};
+
+// We limit the length of any cbor byte array to 128MiB. This is a somewhat
+// arbitrary limit that should work on all platforms and is large enough for
+// any benign data.
+pub const MAX_ARRAY_SIZE: usize = 134_217_728;
+
+// Prevent stack exhaustion by limiting the nested depth of CBOR data.
+const MAX_NESTED_DEPTH: usize = 256;
+
+/// Struct holding a cursor and additional information for decoding.
+#[derive(Debug)]
+struct DecoderCursor<'a> {
+ cursor: Cursor<&'a [u8]>,
+ depth: usize,
+}
+
+/// Apply this mask (with &) to get the value part of the initial byte of a CBOR item.
+const INITIAL_VALUE_MASK: u64 = 0b0001_1111;
+
+impl<'a> DecoderCursor<'a> {
+ /// Read and return the given number of bytes from the cursor. Advances the cursor.
+ fn read_bytes(&mut self, len: usize) -> Result<Vec<u8>, CborError> {
+ if len > MAX_ARRAY_SIZE {
+ return Err(CborError::InputTooLarge);
+ }
+ let mut buf: Vec<u8> = vec![0; len];
+ if self.cursor.read_exact(&mut buf).is_err() {
+ Err(CborError::TruncatedInput)
+ } else {
+ Ok(buf)
+ }
+ }
+
+ /// Convert num bytes to a u64
+ fn read_uint_from_bytes(&mut self, num: usize) -> Result<u64, CborError> {
+ let x = self.read_bytes(num)?;
+ let mut result: u64 = 0;
+ for i in (0..num).rev() {
+ result += u64::from(x[num - 1 - i]) << (i * 8);
+ }
+ Ok(result)
+ }
+
+ /// Read an integer and return it as u64.
+ fn read_int(&mut self) -> Result<u64, CborError> {
+ let first_value = self.read_uint_from_bytes(1)? & INITIAL_VALUE_MASK;
+ match first_value {
+ 0..=23 => Ok(first_value),
+ 24 => self.read_uint_from_bytes(1),
+ 25 => self.read_uint_from_bytes(2),
+ 26 => self.read_uint_from_bytes(4),
+ 27 => self.read_uint_from_bytes(8),
+ _ => Err(CborError::MalformedInput),
+ }
+ }
+
+ fn read_negative_int(&mut self) -> Result<CborType, CborError> {
+ let uint = self.read_int()?;
+ if uint > i64::max_value() as u64 {
+ return Err(CborError::InputValueOutOfRange);
+ }
+ let result: i64 = -1 - uint as i64;
+ Ok(CborType::SignedInteger(result))
+ }
+
+ /// Read an array of data items and return it.
+ fn read_array(&mut self) -> Result<CborType, CborError> {
+ // Create a new array.
+ let mut array: Vec<CborType> = Vec::new();
+ // Read the length of the array.
+ let num_items = self.read_int()?;
+ // Decode each of the num_items data items.
+ for _ in 0..num_items {
+ let new_item = self.decode_item()?;
+ array.push(new_item);
+ }
+ Ok(CborType::Array(array))
+ }
+
+ /// Read a byte string and return it.
+ fn read_byte_string(&mut self) -> Result<CborType, CborError> {
+ let length = self.read_int()?;
+ if length > MAX_ARRAY_SIZE as u64 {
+ return Err(CborError::InputTooLarge);
+ }
+ let byte_string = self.read_bytes(length as usize)?;
+ Ok(CborType::Bytes(byte_string))
+ }
+
+ /// Read a map.
+ fn read_map(&mut self) -> Result<CborType, CborError> {
+ let num_items = self.read_int()?;
+ // Create a new array.
+ let mut map: BTreeMap<CborType, CborType> = BTreeMap::new();
+ // Decode each of the num_items (key, data item) pairs.
+ for _ in 0..num_items {
+ let key_val = self.decode_item()?;
+ let item_value = self.decode_item()?;
+ if map.insert(key_val, item_value).is_some() {
+ return Err(CborError::DuplicateMapKey);
+ }
+ }
+ Ok(CborType::Map(map))
+ }
+
+ fn read_null(&mut self) -> Result<CborType, CborError> {
+ let value = self.read_uint_from_bytes(1)? & INITIAL_VALUE_MASK;
+ if value != 22 {
+ return Err(CborError::UnsupportedType);
+ }
+ Ok(CborType::Null)
+ }
+
+ /// Peeks at the next byte in the cursor, but does not change the position.
+ fn peek_byte(&mut self) -> Result<u8, CborError> {
+ let x = self.read_bytes(1)?;
+ if self.cursor.seek(SeekFrom::Current(-1)).is_err() {
+ return Err(CborError::LibraryError);
+ };
+ Ok(x[0])
+ }
+
+ /// Decodes the next CBOR item.
+ pub fn decode_item(&mut self) -> Result<CborType, CborError> {
+ if self.depth > MAX_NESTED_DEPTH {
+ return Err(CborError::MalformedInput);
+ }
+ self.depth += 1;
+ let major_type = self.peek_byte()? >> 5;
+ let result = match major_type {
+ 0 => {
+ let value = self.read_int()?;
+ Ok(CborType::Integer(value))
+ }
+ 1 => self.read_negative_int(),
+ 2 => self.read_byte_string(),
+ 4 => self.read_array(),
+ 5 => self.read_map(),
+ 6 => {
+ let tag = self.read_int()?;
+ let item = self.decode_item()?;
+ Ok(CborType::Tag(tag, Box::new(item)))
+ }
+ 7 => self.read_null(),
+ _ => Err(CborError::UnsupportedType),
+ };
+ self.depth -= 1;
+ result
+ }
+}
+
+/// Read the CBOR structure in bytes and return it as a `CborType`. To prevent stack exhaustion, the
+/// maximum nested depth of CBOR objects (for example, an array of an array of an array...) is 256.
+/// If the input data describes a CBOR structure that exceeds this limit, an error will be returned.
+pub fn decode(bytes: &[u8]) -> Result<CborType, CborError> {
+ let mut decoder_cursor = DecoderCursor {
+ cursor: Cursor::new(bytes),
+ depth: 0,
+ };
+ decoder_cursor.decode_item()
+ // TODO: check cursor at end?
+}