Repository: alex/rust-asn1 (language: rust)

Issue:
extracting TLV to get DER string length
In streaming APIs or other situations where the full length is not known in advance it may be useful to have a way to peek at the TLV so that the full length can be known in adnvance and later only a correctly sized slice is passed to the parsing routines.


Discussion / hints:

Fix patch:
diff --git a/src/lib.rs b/src/lib.rs
index 2ec0d82..e3dcd7b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -152,7 +152,7 @@ mod writer;
 pub use crate::bit_string::{BitString, OwnedBitString};
 pub use crate::object_identifier::ObjectIdentifier;
 pub use crate::parser::{
-    parse, parse_single, ParseError, ParseErrorKind, ParseLocation, ParseResult, Parser,
+    parse, parse_single, strip_tlv, ParseError, ParseErrorKind, ParseLocation, ParseResult, Parser,
 };
 pub use crate::tag::Tag;
 pub use crate::types::{
diff --git a/src/parser.rs b/src/parser.rs
index 1cb34fd..c01d366 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -175,6 +175,24 @@ pub fn parse_single<'a, T: Asn1Readable<'a>>(data: &'a [u8]) -> ParseResult<T> {
     parse(data, Parser::read_element::<T>)
 }
 
+/// Attempts to parse the `Tlv` at the start of `data` (allows trailing data).
+/// If successful, the `Tlv` and the trailing data after it are returned, if
+/// unsuccessful a `ParseError` is returned.
+///
+/// This can be useful where you have a file or stream format that relies on
+/// ASN.1 TLVs for framing.
+///
+/// When parsing a stream, if an error is returned, if its `kind` is
+/// `ParseErrorKind::ShortData`, this indicates that `data` did not contain
+/// sufficient data to parse an entire `Tlv`, and thus adding more data may
+/// resolve this. All other errors are "fatal" and cannot be resolved with
+/// additional data.
+pub fn strip_tlv(data: &[u8]) -> ParseResult<(Tlv<'_>, &[u8])> {
+    let mut p = Parser::new(data);
+    let tlv = p.read_element::<Tlv<'_>>()?;
+    Ok((tlv, p.data))
+}
+
 /// Encapsulates an ongoing parse. For almost all use-cases the correct
 /// entry-point is [`parse`] or [`parse_single`].
 pub struct Parser<'a> {
@@ -446,6 +464,57 @@ mod tests {
         assert_eq!(e.kind(), ParseErrorKind::EncodedDefault);
     }
 
+    #[test]
+    fn test_strip_tlv() {
+        for (der_bytes, expected) in [
+            (
+                b"" as &[u8],
+                Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
+            ),
+            (
+                b"\x04",
+                Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
+            ),
+            (
+                b"\x04\x82",
+                Err(ParseError::new(ParseErrorKind::ShortData { needed: 2 })),
+            ),
+            (
+                b"\x04\x03",
+                Err(ParseError::new(ParseErrorKind::ShortData { needed: 3 })),
+            ),
+            (
+                b"\x04\x03ab",
+                Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })),
+            ),
+            (
+                b"\x04\x03abc",
+                Ok((
+                    Tlv {
+                        tag: Tag::primitive(0x04),
+                        data: b"abc",
+                        full_data: b"\x04\x03abc",
+                    },
+                    b"" as &[u8],
+                )),
+            ),
+            (
+                b"\x04\x03abc\x00\x00\x00",
+                Ok((
+                    Tlv {
+                        tag: Tag::primitive(0x04),
+                        data: b"abc",
+                        full_data: b"\x04\x03abc",
+                    },
+                    b"\x00\x00\x00",
+                )),
+            ),
+        ] {
+            let result = crate::strip_tlv(der_bytes);
+            assert_eq!(result, expected);
+        }
+    }
+
     fn assert_parses_cb<
         'a,
         T: fmt::Debug + PartialEq,


Test patch: