der_parser/ber/
wrap_any.rs

1use alloc::string::String;
2
3use super::{BerObject, BerObjectContent, BitStringObject};
4use crate::ber::{ber_get_object_content, MAX_OBJECT_SIZE};
5use crate::error::{BerError, BerResult};
6use alloc::vec::Vec;
7use asn1_rs::*;
8use rusticata_macros::custom_check;
9
10/// Parse any BER object recursively, specifying the maximum recursion depth and expected tag
11///
12/// Raise an error if the maximum recursion depth was reached.
13pub fn parse_ber_any_with_tag_r(i: &[u8], tag: Tag, max_depth: usize) -> BerResult {
14    custom_check!(i, max_depth == 0, BerError::BerMaxDepth)?;
15    let (rem, any) = Any::from_ber(i)?;
16    any.header.assert_tag(tag)?;
17    let obj = try_berobject_from_any(any, max_depth)?;
18    Ok((rem, obj))
19}
20
21/// Parse any BER object recursively, specifying the maximum recursion depth
22///
23/// Raise an error if the maximum recursion depth was reached.
24pub fn parse_ber_any_r(i: &[u8], max_depth: usize) -> BerResult {
25    custom_check!(i, max_depth == 0, BerError::BerMaxDepth)?;
26    let (rem, any) = Any::from_ber(i)?;
27    let obj = try_berobject_from_any(any, max_depth)?;
28    Ok((rem, obj))
29}
30
31/// Parse any BER object (not recursive)
32pub fn parse_ber_any(i: &[u8]) -> BerResult<Any> {
33    Any::from_ber(i)
34}
35
36macro_rules! from_obj {
37    ($header:ident, $content:expr) => {
38        BerObject::from_header_and_content($header, $content)
39    };
40    (STRING $ty:ident, $any:ident, $header:ident) => {
41        from_obj!(STRING $ty, $ty, $any, $header)
42    };
43    // macro variant when enum variant is not the same as char type
44    (STRING $ty:ident, $variant:ident, $any:ident, $header:ident) => {{
45        custom_check!($any.data, $header.constructed(), BerError::Unsupported)?; // XXX valid in BER (8.21)
46        <$ty>::test_valid_charset($any.data)?;
47        let s = core::str::from_utf8($any.data)?;
48        Ok(BerObject::from_header_and_content(
49            $header,
50            BerObjectContent::$variant(s),
51        ))
52    }};
53}
54
55/// Read element content as Universal object, or Unknown
56// TODO implement the function for BerObjectContent (to replace ber_read_element_content_as)
57// note: we cannot implement TryFrom because of the `max_depth` argument
58pub(crate) fn try_read_berobjectcontent_as(
59    i: &[u8],
60    tag: Tag,
61    length: Length,
62    constructed: bool,
63    max_depth: usize,
64) -> BerResult<BerObjectContent> {
65    if let Length::Definite(l) = length {
66        custom_check!(i, l > MAX_OBJECT_SIZE, BerError::InvalidLength)?;
67        if i.len() < l {
68            return Err(Err::Incomplete(Needed::new(l)));
69        }
70    }
71    let header = Header::new(Class::Universal, constructed, tag, length);
72    let (rem, i) = ber_get_object_content(i, &header, max_depth)?;
73    let any = Any::new(header, i);
74    let object = try_berobject_from_any(any, max_depth)?;
75    Ok((rem, object.content))
76}
77
78// note: we cannot implement TryFrom because of the `max_depth` argument
79fn try_berobject_from_any(any: Any, max_depth: usize) -> Result<BerObject> {
80    custom_check!(any.data, max_depth == 0, BerError::BerMaxDepth)?;
81    let obj_from = BerObject::from_header_and_content;
82    let header = any.header.clone();
83    if any.class() != Class::Universal {
84        return Ok(obj_from(header, BerObjectContent::Unknown(any)));
85    }
86    match any.tag() {
87        Tag::BitString => {
88            if any.data.is_empty() {
89                return Err(BerError::BerValueError);
90            }
91            custom_check!(any.data, header.constructed(), BerError::Unsupported)?; // XXX valid in BER (8.6.3)
92            let ignored_bits = any.data[0];
93            let data = &any.data[1..];
94            Ok(obj_from(
95                header,
96                BerObjectContent::BitString(ignored_bits, BitStringObject { data }),
97            ))
98        }
99        Tag::BmpString => {
100            custom_check!(any.data, header.constructed(), BerError::Unsupported)?; // XXX valid in BER (8.6.3)
101            if any.data.len() % 2 != 0 {
102                return Err(BerError::Unsupported);
103            }
104            let v: Vec<u16> = any
105                .data
106                .chunks_exact(2)
107                .map(|a| u16::from_be_bytes([a[0], a[1]]))
108                .collect();
109            let _s = String::from_utf16(&v)?;
110            Ok(obj_from(header, BerObjectContent::BmpString(any.data)))
111        }
112        Tag::Boolean => {
113            let b = any.bool()?;
114            Ok(obj_from(header, BerObjectContent::Boolean(b)))
115        }
116        Tag::EndOfContent => Ok(obj_from(header, BerObjectContent::EndOfContent)),
117        Tag::Enumerated => {
118            let obj = any.enumerated()?;
119            Ok(obj_from(header, BerObjectContent::Enum(obj.0 as u64)))
120        }
121        Tag::GeneralizedTime => {
122            let time = any.generalizedtime()?;
123            Ok(obj_from(header, BerObjectContent::GeneralizedTime(time.0)))
124        }
125        Tag::GeneralString => from_obj!(STRING GeneralString, any, header),
126        Tag::GraphicString => from_obj!(STRING GraphicString, any, header),
127        Tag::Ia5String => from_obj!(STRING Ia5String, IA5String, any, header),
128        Tag::Integer => {
129            let obj = obj_from(header, BerObjectContent::Integer(any.data));
130            Ok(obj)
131        }
132        Tag::Null => Ok(obj_from(header, BerObjectContent::Null)),
133        Tag::NumericString => from_obj!(STRING NumericString, any, header),
134        Tag::ObjectDescriptor => from_obj!(STRING ObjectDescriptor, any, header),
135        Tag::OctetString => Ok(obj_from(header, BerObjectContent::OctetString(any.data))),
136        Tag::Oid => {
137            let oid = any.oid()?;
138            Ok(obj_from(header, BerObjectContent::OID(oid)))
139        }
140        Tag::PrintableString => from_obj!(STRING PrintableString, any, header),
141        Tag::RelativeOid => {
142            let oid = any.relative_oid()?;
143            Ok(obj_from(header, BerObjectContent::RelativeOID(oid)))
144        }
145        Tag::Sequence => {
146            header.assert_constructed()?;
147            let objects: Result<Vec<_>> = SequenceIterator::<Any, BerParser>::new(any.data)
148                .map(|item| {
149                    let item = item?;
150                    try_berobject_from_any(item, max_depth - 1)
151                })
152                .collect();
153            let objects = objects?;
154            Ok(obj_from(header, BerObjectContent::Sequence(objects)))
155        }
156        Tag::Set => {
157            header.assert_constructed()?;
158            let objects: Result<Vec<_>> = SetIterator::<Any, BerParser>::new(any.data)
159                .map(|item| {
160                    let item = item?;
161                    try_berobject_from_any(item, max_depth - 1)
162                })
163                .collect();
164            let objects = objects?;
165            Ok(obj_from(header, BerObjectContent::Set(objects)))
166        }
167        Tag::TeletexString => from_obj!(STRING TeletexString, T61String, any, header),
168        Tag::UtcTime => {
169            let time = any.utctime()?;
170            Ok(obj_from(header, BerObjectContent::UTCTime(time.0)))
171        }
172        Tag::UniversalString => {
173            custom_check!(any.data, header.constructed(), BerError::Unsupported)?; // XXX valid in BER (8.21)
174
175            // as detailed in asn1-rs, UniversalString allocates memory since the UCS-4 to UTF-8 conversion requires a memory allocation.
176            // so, the charset is not checked here
177            Ok(obj_from(
178                header,
179                BerObjectContent::UniversalString(any.data),
180            ))
181        }
182        Tag::Utf8String => from_obj!(STRING Utf8String, UTF8String, any, header),
183        Tag::VideotexString => from_obj!(STRING VideotexString, any, header),
184        Tag::VisibleString => from_obj!(STRING VisibleString, any, header),
185        _ => {
186            // Note for: Tag::EmbeddedPdv | Tag::External | Tag::RealType
187            // these types have no mapping in the BerObjectContent enum,
188            // so we use the Unknown type
189            Ok(obj_from(header, BerObjectContent::Unknown(any)))
190        }
191    }
192}
193
194#[cfg(test)]
195mod tests {
196    use crate::ber::{BerObject, BerObjectContent, MAX_RECURSION};
197    use crate::error::BerError;
198    use hex_literal::hex;
199    use test_case::test_case;
200
201    use super::parse_ber_any_r;
202
203    #[test_case(&hex!("01 01 00") => matches Ok(BerObject{header:_, content:BerObjectContent::Boolean(false)}) ; "val false")]
204    #[test_case(&hex!("01 01 ff") => matches Ok(BerObject{header:_, content:BerObjectContent::Boolean(true)}) ; "val true")]
205    #[test_case(&hex!("01 01 7f") => matches Ok(BerObject{header:_, content:BerObjectContent::Boolean(true)}) ; "true not ff")]
206    #[test_case(&hex!("02 02 00 ff") => matches Ok(BerObject{header:_, content:BerObjectContent::Integer(_)}) ; "u32-255")]
207    #[test_case(&hex!("02 02 01 23") => matches Ok(BerObject{header:_, content:BerObjectContent::Integer(_)}) ; "u32-0x123")]
208    #[test_case(&hex!("02 04 ff ff ff ff") => matches Ok(BerObject{header:_, content:BerObjectContent::Integer(_)}) ; "u32-long-neg")]
209    #[test_case(&hex!("0c 04 31 32 33 34") => matches Ok(BerObject{header:_, content:BerObjectContent::UTF8String("1234")}) ; "utf8: numeric")]
210    #[test_case(&hex!("0d 04 c2 7b 03 02") => matches Ok(BerObject{header:_, content:BerObjectContent::RelativeOID(_)}) ; "relative OID")]
211    #[test_case(&hex!("12 04 31 32 33 34") => matches Ok(BerObject{header:_, content:BerObjectContent::NumericString("1234")}) ; "numeric string")]
212    #[test_case(&hex!("12 04 01 02 03 04") => matches Err(BerError::StringInvalidCharset) ; "numeric string err")]
213    #[test_case(&hex!("13 04 31 32 33 34") => matches Ok(BerObject{header:_, content:BerObjectContent::PrintableString("1234")}) ; "printable string")]
214    #[test_case(&hex!("13 04 01 02 03 04") => matches Err(BerError::StringInvalidCharset) ; "printable string err")]
215    #[test_case(&hex!("16 04 31 32 33 34") => matches Ok(BerObject{header:_, content:BerObjectContent::IA5String("1234")}) ; "ia5: numeric")]
216    #[test_case(&hex!("1a 04 31 32 33 34") => matches Ok(BerObject{header:_, content:BerObjectContent::VisibleString("1234")}) ; "visible: numeric")]
217    #[test_case(&hex!("1e 08 00 55 00 73 00 65 00 72") => matches Ok(BerObject{header:_, content:BerObjectContent::BmpString(b"\x00U\x00s\x00e\x00r")}) ; "bmp")]
218    #[test_case(&hex!("30 80 04 03 56 78 90 00 00") => matches Ok(BerObject{header:_, content:BerObjectContent::Sequence(_)}) ; "indefinite length")]
219    #[test_case(&hex!("c0 03 01 00 01") => matches Ok(BerObject{header:_, content:BerObjectContent::Unknown(_)}) ; "private")]
220    fn ber_from_any(i: &[u8]) -> Result<BerObject, BerError> {
221        let (rem, res) = parse_ber_any_r(i, MAX_RECURSION)?;
222        assert!(rem.is_empty());
223        // dbg!(&res);
224        Ok(res)
225    }
226}