Skip to content

Commit

Permalink
Expose the amount of data needed to complete an operation
Browse files Browse the repository at this point in the history
  • Loading branch information
alex committed Feb 10, 2024
1 parent dc30645 commit f3cc379
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 36 deletions.
121 changes: 90 additions & 31 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::types::{Asn1Readable, Tlv};
use crate::Tag;
use core::fmt;

#[derive(Debug, PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum ParseErrorKind {
/// Something about the value was invalid.
InvalidValue,
Expand All @@ -15,8 +15,14 @@ pub enum ParseErrorKind {
InvalidLength,
/// An unexpected tag was encountered.
UnexpectedTag { actual: Tag },
/// There was not enough data available to complete parsing.
ShortData,
/// There was not enough data available to complete parsing. Includes the
/// amount of data needed to advance the parse.
///
/// Note that provided `needed` additional bytes of data does not ensure
/// that `parse` will succeed -- it is the amount of data required to
/// satisfy the `read` operation that failed, and there may be subsequent
/// `read` operations that require additional data.
ShortData { needed: usize },
/// An internal computation would have overflowed.
IntegerOverflow,
/// There was extraneous data in the input.
Expand Down Expand Up @@ -57,6 +63,10 @@ impl ParseError {
}
}

pub fn kind(&self) -> ParseErrorKind {
self.kind
}

#[doc(hidden)]
#[must_use]
pub fn add_location(mut self, loc: ParseLocation) -> Self {
Expand Down Expand Up @@ -123,7 +133,13 @@ impl fmt::Display for ParseError {
ParseErrorKind::UnexpectedTag { actual } => {
write!(f, "unexpected tag (got {:?})", actual)
}
ParseErrorKind::ShortData => write!(f, "short data"),
ParseErrorKind::ShortData { needed } => {
write!(
f,
"short data (needed at least {} additional bytes)",
needed
)
}
ParseErrorKind::IntegerOverflow => write!(f, "integer overflow"),
ParseErrorKind::ExtraData => write!(f, "extra data"),
ParseErrorKind::InvalidSetOrdering => write!(f, "SET value was ordered incorrectly"),
Expand Down Expand Up @@ -202,7 +218,9 @@ impl<'a> Parser<'a> {
#[inline]
fn read_bytes(&mut self, length: usize) -> ParseResult<&'a [u8]> {
if length > self.data.len() {
return Err(ParseError::new(ParseErrorKind::ShortData));
return Err(ParseError::new(ParseErrorKind::ShortData {
needed: length - self.data.len(),
}));
}
let (result, data) = self.data.split_at(length);
self.data = data;
Expand Down Expand Up @@ -401,9 +419,9 @@ mod tests {
"ASN.1 parsing error: DEFINED BY with unknown value"
),
(
ParseError::new(ParseErrorKind::ShortData)
ParseError::new(ParseErrorKind::ShortData{needed: 7})
.add_location(ParseLocation::Field("Abc::123")),
"ASN.1 parsing error: short data",
"ASN.1 parsing error: short data (needed at least 7 additional bytes)",
),
(
ParseError::new(ParseErrorKind::UnexpectedTag {
Expand Down Expand Up @@ -465,7 +483,9 @@ mod tests {
&[
(Ok(8), b"\x02\x01\x08"),
(
Err(E::P(ParseError::new(ParseErrorKind::ShortData))),
Err(E::P(ParseError::new(ParseErrorKind::ShortData {
needed: 1,
}))),
b"\x02\x01",
),
(Err(E::X(7)), b"\x02\x01\x07"),
Expand Down Expand Up @@ -493,11 +513,17 @@ mod tests {
b"\x04\x03abc",
),
(
Err(ParseError::new(ParseErrorKind::ShortData)),
Err(ParseError::new(ParseErrorKind::ShortData { needed: 2 })),
b"\x04\x03a",
),
(Err(ParseError::new(ParseErrorKind::ShortData)), b"\x04"),
(Err(ParseError::new(ParseErrorKind::ShortData)), b""),
(
Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
b"\x04",
),
(
Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
b"",
),
// Long form tags
(
Ok(Tlv {
Expand Down Expand Up @@ -618,8 +644,8 @@ mod tests {
Err(ParseError::new(ParseErrorKind::InvalidLength)),
b"\x04\x89\x01\x01\x01\x01\x01\x01\x01\x01\x01"
),
(Err(ParseError::new(ParseErrorKind::ShortData)), b"\x04\x03\x01\x02"),
(Err(ParseError::new(ParseErrorKind::ShortData)), b"\x04\x82\xff\xff\xff\xff\xff\xff"),
(Err(ParseError::new(ParseErrorKind::ShortData{needed: 1})), b"\x04\x03\x01\x02"),
(Err(ParseError::new(ParseErrorKind::ShortData{needed: 65531})), b"\x04\x82\xff\xff\xff\xff\xff\xff"),
// 3 byte length form with leading 0.
(Err(ParseError::new(ParseErrorKind::InvalidLength)), b"\x04\x83\x00\xff\xff"),
// 4 byte length form with leading 0.
Expand Down Expand Up @@ -668,11 +694,17 @@ mod tests {
b"\x03\x00",
),
(
Err(ParseError::new(ParseErrorKind::ShortData)),
Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
b"\x02\x02\x00",
),
(Err(ParseError::new(ParseErrorKind::ShortData)), b""),
(Err(ParseError::new(ParseErrorKind::ShortData)), b"\x02"),
(
Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
b"",
),
(
Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
b"\x02",
),
(
Err(ParseError::new(ParseErrorKind::IntegerOverflow)),
b"\x02\x09\x02\x00\x00\x00\x00\x00\x00\x00\x00",
Expand Down Expand Up @@ -732,11 +764,17 @@ mod tests {
b"\x03\x00",
),
(
Err(ParseError::new(ParseErrorKind::ShortData)),
Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
b"\x02\x02\x00",
),
(Err(ParseError::new(ParseErrorKind::ShortData)), b""),
(Err(ParseError::new(ParseErrorKind::ShortData)), b"\x02"),
(
Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
b"",
),
(
Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
b"\x02",
),
(
Err(ParseError::new(ParseErrorKind::IntegerOverflow)),
b"\x02\x09\x02\x00\x00\x00\x00\x00\x00\x00\x00",
Expand Down Expand Up @@ -1449,7 +1487,7 @@ mod tests {
b"\x30\x06\x02\x01\x01\x02\x01\x02",
),
(
Err(ParseError::new(ParseErrorKind::ShortData)),
Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
b"\x30\x04\x02\x01\x01",
),
(
Expand All @@ -1465,7 +1503,7 @@ mod tests {
&[
(Ok((1, 2)), b"\x30\x06\x02\x01\x01\x02\x01\x02"),
(
Err(ParseError::new(ParseErrorKind::ShortData)),
Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
b"\x30\x03\x02\x01\x01",
),
(
Expand All @@ -1490,7 +1528,7 @@ mod tests {
),
(Ok(vec![]), b"\x30\x00"),
(
Err(ParseError::new(ParseErrorKind::ShortData)),
Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
b"\x30\x02\x02\x01",
),
],
Expand All @@ -1516,7 +1554,7 @@ mod tests {
),
(Ok(vec![]), b"\x30\x00"),
(
Err(ParseError::new(ParseErrorKind::ShortData)
Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })
.add_location(ParseLocation::Index(0))),
b"\x30\x02\x02\x01",
),
Expand All @@ -1540,7 +1578,7 @@ mod tests {
b"\x31\x06\x02\x01\x03\x02\x01\x01",
),
(
Err(ParseError::new(ParseErrorKind::ShortData)
Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })
.add_location(ParseLocation::Index(0))),
b"\x31\x01\x02",
),
Expand All @@ -1565,8 +1603,14 @@ mod tests {
(Ok((None, Some(18))), b"\x02\x01\x12"),
(Ok((Some(true), Some(18))), b"\x01\x01\xff\x02\x01\x12"),
(Ok((None, None)), b""),
(Err(ParseError::new(ParseErrorKind::ShortData)), b"\x01"),
(Err(ParseError::new(ParseErrorKind::ShortData)), b"\x02"),
(
Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
b"\x01",
),
(
Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
b"\x02",
),
],
|p| {
Ok((
Expand All @@ -1586,7 +1630,10 @@ mod tests {
b"\x04\x03abc",
),
(Ok(None), b""),
(Err(ParseError::new(ParseErrorKind::ShortData)), b"\x04"),
(
Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
b"\x04",
),
]);

assert_parses::<Option<Choice2<u64, bool>>>(&[
Expand All @@ -1607,7 +1654,10 @@ mod tests {
})),
b"\x03\x00",
),
(Err(ParseError::new(ParseErrorKind::ShortData)), b""),
(
Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
b"",
),
]);
}

Expand All @@ -1622,7 +1672,10 @@ mod tests {
})),
b"\x03\x00",
),
(Err(ParseError::new(ParseErrorKind::ShortData)), b""),
(
Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
b"",
),
]);
}

Expand All @@ -1638,7 +1691,10 @@ mod tests {
})),
b"\x03\x00",
),
(Err(ParseError::new(ParseErrorKind::ShortData)), b""),
(
Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
b"",
),
]);
}

Expand Down Expand Up @@ -1675,7 +1731,10 @@ mod tests {
})),
b"\x02\x01\xff",
),
(Err(ParseError::new(ParseErrorKind::ShortData)), b""),
(
Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
b"",
),
]);
}

Expand Down
2 changes: 1 addition & 1 deletion src/tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ impl Tag {
pub fn from_bytes(mut data: &[u8]) -> ParseResult<(Tag, &[u8])> {
let tag = match data.first() {
Some(&b) => b,
None => return Err(ParseError::new(ParseErrorKind::ShortData)),
None => return Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
};
data = &data[1..];
let mut value = u32::from(tag & 0x1f);
Expand Down
12 changes: 8 additions & 4 deletions tests/derive_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -465,8 +465,10 @@ fn test_required_implicit() {
assert_roundtrips::<RequiredImplicit>(&[
(Ok(RequiredImplicit { value: 8 }), b"\x30\x03\x80\x01\x08"),
(
Err(asn1::ParseError::new(asn1::ParseErrorKind::ShortData)
.add_location(asn1::ParseLocation::Field("RequiredImplicit::value"))),
Err(
asn1::ParseError::new(asn1::ParseErrorKind::ShortData { needed: 1 })
.add_location(asn1::ParseLocation::Field("RequiredImplicit::value")),
),
b"\x30\x00",
),
(
Expand All @@ -493,8 +495,10 @@ fn test_required_explicit() {
b"\x30\x05\xa0\x03\x02\x01\x08",
),
(
Err(asn1::ParseError::new(asn1::ParseErrorKind::ShortData)
.add_location(asn1::ParseLocation::Field("RequiredExplicit::value"))),
Err(
asn1::ParseError::new(asn1::ParseErrorKind::ShortData { needed: 1 })
.add_location(asn1::ParseLocation::Field("RequiredExplicit::value")),
),
b"\x30\x00",
),
(
Expand Down

0 comments on commit f3cc379

Please sign in to comment.