asn1_rs/
from_ber.rs

1use core::convert::{TryFrom, TryInto};
2use core::fmt::Display;
3
4use nom::error::ParseError;
5use nom::Input as _;
6use nom::{Err, IResult};
7
8use crate::ber::{GetObjectContent, MAX_RECURSION};
9use crate::debug::macros::log_error;
10use crate::debug::trace_input;
11use crate::{Any, BerError, BerMode, DynTagged, Error, Header, Input, ParseResult};
12
13/// Base trait for BER object parsers
14///
15/// # Notes
16///
17/// *This trait might become deprecated soon! Instead of this one, implement trait [`BerParser`].*
18///
19/// Library authors should usually not directly implement this trait, but should prefer implementing the
20/// [`TryFrom<Any>`] trait,
21/// which offers greater flexibility and provides an equivalent `FromBer` implementation for free.
22///
23/// # Examples
24///
25/// ```
26/// use asn1_rs::{Any, Result, Tag};
27/// use std::convert::TryFrom;
28///
29/// // The type to be decoded
30/// #[derive(Clone, Copy, Debug, PartialEq, Eq)]
31/// pub struct MyType(pub u32);
32///
33/// impl<'a> TryFrom<Any<'a>> for MyType {
34///     type Error = asn1_rs::Error;
35///
36///     fn try_from(any: Any<'a>) -> Result<MyType> {
37///         any.tag().assert_eq(Tag::Integer)?;
38///         // for this fictive example, the type contains the number of characters
39///         let n = any.data.len() as u32;
40///         Ok(MyType(n))
41///     }
42/// }
43///
44/// // The above code provides a `FromBer` implementation for free.
45///
46/// // Example of parsing code:
47/// use asn1_rs::FromBer;
48///
49/// let input = &[2, 1, 2];
50/// // Objects can be parsed using `from_ber`, which returns the remaining bytes
51/// // and the parsed object:
52/// let (rem, my_type) = MyType::from_ber(input).expect("parsing failed");
53/// ```
54pub trait FromBer<'a, E = Error>: Sized {
55    /// Attempt to parse input bytes into a BER object
56    fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, E>;
57}
58
59impl<'a, T, E> FromBer<'a, E> for T
60where
61    T: TryFrom<Any<'a>, Error = E>,
62    E: From<Error>,
63{
64    fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, T, E> {
65        let (i, any) = Any::from_ber(bytes).map_err(Err::convert)?;
66        match any.try_into().map_err(Err::Error) {
67            Ok(result) => Ok((i, result)),
68            Err(err) => {
69                log_error!(
70                    "≠ Conversion from Any to {} failed",
71                    core::any::type_name::<T>()
72                );
73                Err(err)
74            }
75        }
76    }
77}
78
79/// Base trait for BER object parsers
80///
81/// Implementers should provide a definition for the following:
82/// - method [`from_ber_content`](BerParser::from_ber_content): Parse BER content, given a header and data
83/// - trait [`DynTagged`]
84///
85/// This trait can be automatically derived from a `struct` using the [`BerParserSequence`](crate::BerParserSequence)
86/// or [`BerParserSet`](crate::BerParserSet) custom derive attributes.
87pub trait BerParser<'i>
88where
89    Self: Sized,
90    Self: DynTagged,
91{
92    /// The Error type for parsing errors.
93    type Error: Display + ParseError<Input<'i>> + From<BerError<Input<'i>>>;
94
95    /// Attempt to parse a new BER object from data.
96    ///
97    /// Header tag must match expected tag
98    fn parse_ber(input: Input<'i>) -> IResult<Input<'i>, Self, Self::Error> {
99        trace_input("BerParser::parse_ber", |input| {
100            let (rem, header) = Header::parse_ber(input.clone()).map_err(Err::convert)?;
101            if !Self::accept_tag(header.tag) {
102                return Err(Err::Error(
103                    // FIXME: expected Tag is `None`, so the error will not be helpful
104                    BerError::unexpected_tag(input, None, header.tag).into(),
105                ));
106            }
107            let (rem, data) =
108                BerMode::get_object_content(&header, rem, MAX_RECURSION).map_err(Err::convert)?;
109            let (_, obj) = trace_input("BerParser::from_ber_content", |i| {
110                // wrap from_ber_content function to display better errors, if any
111                Self::from_ber_content(&header, i)
112            })(data)
113            .map_err(Err::convert)?;
114            Ok((rem, obj))
115        })(input)
116    }
117
118    /// Parse a new BER object from header and data.
119    ///
120    /// `input` length is guaranteed to match `header` length (definite or indefinite)
121    ///
122    /// Note: in this method, implementers should *not* check header tag (which can be
123    /// different from the usual object tag when using IMPLICIT tagging, for ex.).
124    fn from_ber_content(
125        header: &'_ Header<'i>,
126        input: Input<'i>,
127    ) -> IResult<Input<'i>, Self, Self::Error>;
128
129    fn parse_ber_optional(input: Input<'i>) -> IResult<Input<'i>, Option<Self>, Self::Error> {
130        if input.input_len() == 0 {
131            return Ok((input, None));
132        }
133        let (rem, header) = Header::parse_ber(input.clone()).map_err(Err::convert)?;
134        if !Self::accept_tag(header.tag) {
135            return Ok((input, None));
136        }
137        let (rem, data) =
138            BerMode::get_object_content(&header, rem, MAX_RECURSION).map_err(Err::convert)?;
139        let (_, obj) = Self::from_ber_content(&header, data).map_err(Err::convert)?;
140        Ok((rem, Some(obj)))
141    }
142
143    /// Parse object header (validating tag) and return header and content as `Input`
144    fn parse_ber_as_input(
145        input: Input<'i>,
146    ) -> IResult<Input<'i>, (Header<'i>, Input<'i>), Self::Error> {
147        trace_input("BerParser::parse_ber_as_input", |input| {
148            let (rem, header) = Header::parse_ber(input.clone()).map_err(Err::convert)?;
149            if !Self::accept_tag(header.tag) {
150                return Err(Err::Error(
151                    // FIXME: expected Tag is `None`, so the error will not be helpful
152                    BerError::unexpected_tag(input, None, header.tag).into(),
153                ));
154            }
155            let (rem, data) =
156                BerMode::get_object_content(&header, rem, MAX_RECURSION).map_err(Err::convert)?;
157
158            Ok((rem, (header, data)))
159        })(input)
160    }
161}
162
163// NOTE: function useful during transition to Input. Remove this after
164pub(crate) fn wrap_ber_parser<'i, F, T>(mut f: F) -> impl FnMut(&'i [u8]) -> ParseResult<'i, T>
165where
166    F: FnMut(Input<'i>) -> IResult<Input<'i>, T, BerError<Input<'i>>>,
167{
168    move |i: &[u8]| {
169        let input = Input::from_slice(i);
170        match f(input) {
171            Ok((rem, res)) => Ok((rem.into_bytes(), res)),
172            Err(e) => Err(Err::convert(e)),
173        }
174    }
175}