diff options
Diffstat (limited to 'third_party/rust/moz_cbor/src/decoder.rs')
-rw-r--r-- | third_party/rust/moz_cbor/src/decoder.rs | 165 |
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? +} |