Repository: google/OpenSK
File: libraries/cbor/src/reader.rs (Rust)

Code before:
    cbor_array_vec, cbor_bytes_lit, cbor_map_collection, cbor_tagged, cbor_text, cbor_unsigned,
};
use alloc::str;
use alloc::vec::Vec;

/// Possible errors from a deserialization operation.
#[derive(Debug, PartialEq)]
pub enum DecoderError {
    UnsupportedMajorType,
    UnknownAdditionalInfo,
    IncompleteCborData,
    TooMuchNesting,
    InvalidUtf8,
    ExtranousData,
    OutOfOrderKey,
    NonMinimalCborEncoding,
    UnsupportedSimpleValue,
    UnsupportedFloatingPointValue,
    OutOfRangeIntegerValue,
}

/// Deserialize CBOR binary data to produce a single [`Value`], expecting that there is no additional data.
pub fn read(encoded_cbor: &[u8]) -> Result<Value, DecoderError> {
    read_nested(encoded_cbor, Reader::MAX_NESTING_DEPTH)
}

/// Deserialize CBOR binary data to produce a single [`Value`], expecting that there is no additional data.  Nested
/// structures up to the provided `max_nest` are supported.
pub fn read_nested(encoded_cbor: &[u8], max_nest: i8) -> Result<Value, DecoderError> {
    let mut reader = Reader::new(encoded_cbor);
    let value = reader.decode_complete_data_item(max_nest)?;
    if !reader.remaining_cbor.is_empty() {
        return Err(DecoderError::ExtranousData);
    }
    Ok(value)
}

struct Reader<'a> {
    remaining_cbor: &'a [u8],
}

impl<'a> Reader<'a> {
    const MAX_NESTING_DEPTH: i8 = 4;

    pub fn new(cbor: &'a [u8]) -> Reader<'a> {
        Reader {
            remaining_cbor: cbor,
        }
    }

    pub fn decode_complete_data_item(

Review comment (refactor):
Since we are splitting, `MAX_NESTING_DEPTH` in `Reader` and `Writer` should probably be a constant in `ctap2`, as it is not part of CBOR. It means that the current `read` with one parameter should be moved to the application. If we do that, one would arguably also want it to return a `Ctap2StatusCode` as error, and then do some refactoring to always just `?`. Let me do that in a next PR, so we just have to agree on the interface we want for the library.

I don't think there is a general default for nesting depth in CBOR. Maybe we still want a default of infinity or something very big for convenience. So IMO it could be:
- `read_nested` and `read` with a big default max or the depth becomes an `Option`
- a variety of `read_VARIANT` functions for each variant of CBOR that we want to support, if such a list exists, like `read_ctap`, `read_canonical`, `read_crazy_variant`

This comment applies to `writer` as well.

Code after:
    cbor_array_vec, cbor_bytes_lit, cbor_map_collection, cbor_tagged, cbor_text, cbor_unsigned,
};
use alloc::str;
use alloc::vec::Vec;

/// Possible errors from a deserialization operation.
#[derive(Debug, PartialEq)]
pub enum DecoderError {
    UnsupportedMajorType,
    UnknownAdditionalInfo,
    IncompleteCborData,
    TooMuchNesting,
    InvalidUtf8,
    ExtraneousData,
    OutOfOrderKey,
    NonMinimalCborEncoding,
    UnsupportedSimpleValue,
    UnsupportedFloatingPointValue,
    OutOfRangeIntegerValue,
}

/// 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(max_nest)?;
    if !reader.remaining_cbor.is_empty() {
        return Err(DecoderError::ExtraneousData);
    }
    Ok(value)
}

struct Reader<'a> {
    remaining_cbor: &'a [u8],
}

impl<'a> Reader<'a> {
    pub fn new(cbor: &'a [u8]) -> Reader<'a> {
        Reader {
            remaining_cbor: cbor,
        }
    }

    pub fn decode_complete_data_item(