der_parser/ber/
print.rs

1use crate::ber::BitStringObject;
2use crate::ber::{BerObject, BerObjectContent};
3use alloc::string::{String, ToString};
4use alloc::vec;
5use alloc::vec::Vec;
6use asn1_rs::{Class, Header, Length, Tag};
7use core::fmt;
8use core::iter::FromIterator;
9use core::str;
10use debug::HexSlice;
11
12use rusticata_macros::debug;
13
14#[derive(Clone, Copy, Debug, PartialEq)]
15pub enum PrettyPrinterFlag {
16    Recursive,
17    ShowHeader,
18}
19
20/// Pretty-print BER object
21///
22/// This method is recursive by default. To prevent that, unset the `Recursive` flag.
23pub struct PrettyBer<'a> {
24    obj: &'a BerObject<'a>,
25    indent: usize,
26    inc: usize,
27
28    flags: Vec<PrettyPrinterFlag>,
29}
30
31impl<'a> BerObject<'a> {
32    pub fn as_pretty(&'a self, indent: usize, increment: usize) -> PrettyBer<'a> {
33        PrettyBer::new(self, vec![PrettyPrinterFlag::Recursive], indent, increment)
34    }
35}
36
37impl<'a> PrettyBer<'a> {
38    pub const fn new(
39        obj: &'a BerObject<'a>,
40        flags: Vec<PrettyPrinterFlag>,
41        indent: usize,
42        increment: usize,
43    ) -> Self {
44        Self {
45            obj,
46            indent,
47            inc: increment,
48            flags,
49        }
50    }
51
52    pub fn set_flag(&mut self, flag: PrettyPrinterFlag) {
53        if !self.flags.contains(&flag) {
54            self.flags.push(flag);
55        }
56    }
57
58    pub fn unset_flag(&mut self, flag: PrettyPrinterFlag) {
59        self.flags.retain(|&f| f != flag);
60    }
61
62    pub fn is_flag_set(&self, flag: PrettyPrinterFlag) -> bool {
63        self.flags.contains(&flag)
64    }
65
66    pub fn next_indent<'b>(&self, obj: &'b BerObject) -> PrettyBer<'b> {
67        PrettyBer {
68            obj,
69            indent: self.indent + self.inc,
70            inc: self.inc,
71            flags: self.flags.to_vec(),
72        }
73    }
74
75    #[inline]
76    fn is_recursive(&self) -> bool {
77        self.is_flag_set(PrettyPrinterFlag::Recursive)
78    }
79}
80
81fn dbg_header(header: &Header, f: &mut fmt::Formatter) -> fmt::Result {
82    let s_constructed = if header.is_constructed() { "+" } else { "" };
83    let l = match header.length() {
84        Length::Definite(sz) => sz.to_string(),
85        Length::Indefinite => "Indefinite".to_string(),
86    };
87    match header.class() {
88        Class::Universal => {
89            write!(f, "[{}]{} {}", header.tag(), s_constructed, l)?;
90        }
91        Class::ContextSpecific => {
92            write!(f, "[{}]{} {}", header.tag().0, s_constructed, l)?;
93        }
94
95        class => {
96            write!(f, "[{} {}]{} {}", class, header.tag().0, s_constructed, l)?;
97        }
98    }
99    Ok(())
100}
101
102impl fmt::Debug for PrettyBer<'_> {
103    #[rustfmt::skip]
104    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
105        if self.indent > 0 {
106            write!(f, "{:1$}", " ", self.indent)?;
107        };
108        if self.flags.contains(&PrettyPrinterFlag::ShowHeader) {
109            dbg_header(&self.obj.header, f)?;
110            write!(f, " ")?;
111        };
112        fn print_utf16_string_with_type(f: &mut fmt::Formatter, s: &[u8], ty: &str) -> fmt::Result {
113            let v: Vec<_> = s
114                .chunks_exact(2)
115                .map(|a| u16::from_be_bytes([a[0], a[1]]))
116                .collect();
117            let s = String::from_utf16(&v);
118
119            match s {
120                Ok(s)  => writeln!(f, "{}(\"{}\")", ty, s),
121                Err(_) => writeln!(f, "{}({:?}) <error decoding utf32 string>", ty, s),
122            }
123        }
124        fn print_utf32_string_with_type(f: &mut fmt::Formatter, s: &[u8], ty: &str) -> fmt::Result {
125            let chars: Option<Vec<char>> = s
126                .chunks_exact(4)
127                .map(|a| core::char::from_u32(u32::from_be_bytes([a[0], a[1], a[2], a[3]])))
128                .collect();
129
130            match chars {
131                Some(b)  => writeln!(f, "{}(\"{}\")", ty, String::from_iter(b)),
132                None => writeln!(f, "{}({:?}) <error decoding utf32 string>", ty, s),
133            }
134        }
135        match self.obj.content {
136            BerObjectContent::EndOfContent           => write!(f, "EndOfContent"),
137            BerObjectContent::Boolean(b)             => write!(f, "Boolean({:?})", b),
138            BerObjectContent::Integer(i)             => write!(f, "Integer({:?})", HexSlice(i)),
139            BerObjectContent::Enum(i)                => write!(f, "Enum({})", i),
140            BerObjectContent::OID(ref v)             => write!(f, "OID({:?})", v),
141            BerObjectContent::RelativeOID(ref v)     => write!(f, "RelativeOID({:?})", v),
142            BerObjectContent::Null                   => write!(f, "Null"),
143            BerObjectContent::OctetString(v)         => write!(f, "OctetString({:?})", HexSlice(v)),
144            BerObjectContent::BitString(u,BitStringObject{data:v})
145                                                     => write!(f, "BitString({},{:?})", u, HexSlice(v)),
146            BerObjectContent::GeneralizedTime(ref time)     => write!(f, "GeneralizedTime(\"{}\")", time),
147            BerObjectContent::UTCTime(ref time)             => write!(f, "UTCTime(\"{}\")", time),
148            BerObjectContent::VisibleString(s)       => write!(f, "VisibleString(\"{}\")", s),
149            BerObjectContent::GeneralString(s)       => write!(f, "GeneralString(\"{}\")", s),
150            BerObjectContent::GraphicString(s)       => write!(f, "GraphicString(\"{}\")", s),
151            BerObjectContent::PrintableString(s)     => write!(f, "PrintableString(\"{}\")", s),
152            BerObjectContent::NumericString(s)       => write!(f, "NumericString(\"{}\")", s),
153            BerObjectContent::UTF8String(s)          => write!(f, "UTF8String(\"{}\")", s),
154            BerObjectContent::IA5String(s)           => write!(f, "IA5String(\"{}\")", s),
155            BerObjectContent::T61String(s)           => write!(f, "T61String({})", s),
156            BerObjectContent::VideotexString(s)      => write!(f, "VideotexString({})", s),
157            BerObjectContent::ObjectDescriptor(s)    => write!(f, "ObjectDescriptor(\"{}\")", s),
158            BerObjectContent::BmpString(s)           => print_utf16_string_with_type(f, s, "BmpString"),
159            BerObjectContent::UniversalString(s)     => print_utf32_string_with_type(f, s, "UniversalString"),
160            BerObjectContent::Optional(ref o) => {
161                match o {
162                    Some(obj) => write!(f, "OPTION {:?}", obj),
163                    None => write!(f, "NONE"),
164                }
165            }
166            BerObjectContent::Tagged(class, tag, ref obj) => {
167                writeln!(f, "ContextSpecific [{} {}] {{", class, tag.0)?;
168                write!(f, "{:?}", self.next_indent(obj))?;
169                if self.indent > 0 {
170                    write!(f, "{:1$}", " ", self.indent)?;
171                };
172                write!(f, "}}")?;
173                Ok(())
174            },
175            BerObjectContent::Set(ref v) |
176            BerObjectContent::Sequence(ref v)        => {
177                let ty = if self.obj.header.tag() == Tag::Sequence { "Sequence" } else { "Set" };
178                if self.is_recursive() {
179                    writeln!(f, "{}[", ty)?;
180                    for o in v {
181                        write!(f, "{:?}", self.next_indent(o))?;
182                    };
183                    if self.indent > 0 {
184                        write!(f, "{:1$}", " ", self.indent)?;
185                    };
186                    write!(f, "]")?;
187                } else {
188                    write!(f, "{}", ty)?;
189                }
190                Ok(())
191            },
192            BerObjectContent::Unknown(ref any) => {
193                write!(f, "Unknown {:x?}", HexSlice(any.data))
194            },
195        }
196    }
197}
198
199#[cfg(test)]
200mod tests {
201    use super::PrettyPrinterFlag;
202    use crate::ber::*;
203
204    #[test]
205    fn test_pretty_print() {
206        let d = BerObject::from_obj(BerObjectContent::Sequence(vec![
207            BerObject::from_int_slice(b"\x01\x00\x01"),
208            BerObject::from_int_slice(b"\x01\x00\x01"),
209            BerObject::from_obj(BerObjectContent::Set(vec![
210                BerObject::from_int_slice(b"\x01"),
211                BerObject::from_int_slice(b"\x02"),
212            ])),
213        ]));
214
215        println!("{:?}", d.as_pretty(0, 2));
216
217        let mut pp = d.as_pretty(0, 4);
218        pp.set_flag(PrettyPrinterFlag::ShowHeader);
219        println!("{:?}", pp);
220    }
221}