Skip to content

Commit

Permalink
Add Visit and VisitMut patterns to iterate objects and update printer…
Browse files Browse the repository at this point in the history
… to use it
  • Loading branch information
chifflier committed Feb 7, 2022
1 parent 74d3e27 commit 76f7334
Show file tree
Hide file tree
Showing 5 changed files with 568 additions and 45 deletions.
68 changes: 67 additions & 1 deletion examples/dump-der.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,71 @@ use der_parser::der::*;
use std::env;
use std::io;

use nom::HexDisplay;

struct BerPrinter {}

impl BerPrinter {
/// try to parse contents, constructed or not
///
/// Do not raise errors if object is not constructed
fn try_print_encapsulated(&mut self, data: &[u8], depth: usize, ber: &BerObject) {
let mut i = data;
let mut first_object = true;
while !i.is_empty() {
match parse_ber_any_r(i, MAX_RECURSION) {
Ok((rem, inner)) => {
if first_object {
println!("{:1$}encapsulates {{", " ", depth * 2);
first_object = false;
}
self.run_at(&inner, depth + 1);
i = rem;
}
Err(e) => {
if ber.is_constructed() {
// object was constructed, so should have been parsed correctly
eprintln!(
"Error while parsing constructed object at depth {}: {}",
depth, e
);
eprintln!("tried to parse\n{}", data.to_hex(16));
} else {
// does not look like encapsulated data
i = &[];
}
break;
}
}
}
if !first_object {
println!("{:1$}}}", " ", depth * 2);
}
if !i.is_empty() {
println!("WARNING: {} remaining bytes at depth {}", i.len(), depth);
}
}
}

impl<'a> Visit<'a> for BerPrinter {
fn visit_ber(&mut self, ber: &'a BerObject<'a>, depth: usize) {
// create a printer without the recursive flag, recursion is handled by the
// visitor pattern
let pp = PrettyBer::new(ber, vec![PrettyPrinterFlag::ShowHeader], depth * 2, 2);
println!("{:?}", pp);
match ber.content {
BerObjectContent::Unknown(ref any) => {
self.try_print_encapsulated(any.data, depth, ber);
}
// Bitstring and OctetString also can encapsulate objects
BerObjectContent::OctetString(data) => {
self.try_print_encapsulated(data, depth, ber);
}
_ => (),
}
}
}

pub fn main() -> io::Result<()> {
let mut parse_as_ber = false;
for file_name in env::args().skip(1) {
Expand All @@ -23,7 +88,8 @@ pub fn main() -> io::Result<()> {
} else {
parse_der(&data).expect("could not parse DER data")
};
println!("{:?}", obj.as_pretty(0, 2));
let mut printer = BerPrinter {};
printer.run_at(&obj, 1);
if !rem.is_empty() {
println!("WARNING: extra bytes after BER/DER object:\n{:x?}", rem);
}
Expand Down
4 changes: 4 additions & 0 deletions src/ber/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ mod print;
#[cfg(feature = "serialize")]
mod serialize;
mod tagged;
mod visit;
mod visit_mut;
mod wrap_any;

pub use crate::ber::ber::*;
Expand All @@ -63,6 +65,8 @@ pub use crate::ber::print::*;
#[cfg(feature = "serialize")]
pub use crate::ber::serialize::*;
pub use crate::ber::tagged::*;
pub use crate::ber::visit::*;
pub use crate::ber::visit_mut::*;
pub use crate::ber::wrap_any::*;

pub mod compat;
Expand Down
143 changes: 99 additions & 44 deletions src/ber/print.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
use crate::ber::BitStringObject;
use crate::ber::{BerObject, BerObjectContent};
use alloc::string::String;
use alloc::string::{String, ToString};
use alloc::vec;
use alloc::vec::Vec;
use asn1_rs::Tag;
use asn1_rs::{Class, Header, Length, Tag};
use core::fmt;
use core::iter::FromIterator;
use core::str;
use debug::HexSlice;

use rusticata_macros::debug;

#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum PrettyPrinterFlag {
Recursive,
ShowHeader,
}

/// Pretty-print BER object
///
/// This method is recursive by default. To prevent that, unset the `Recursive` flag.
pub struct PrettyBer<'a> {
obj: &'a BerObject<'a>,
indent: usize,
Expand All @@ -24,23 +30,39 @@ pub struct PrettyBer<'a> {

impl<'a> BerObject<'a> {
pub fn as_pretty(&'a self, indent: usize, increment: usize) -> PrettyBer<'a> {
PrettyBer {
obj: self,
PrettyBer::new(self, vec![PrettyPrinterFlag::Recursive], indent, increment)
}
}

impl<'a> PrettyBer<'a> {
pub const fn new(
obj: &'a BerObject<'a>,
flags: Vec<PrettyPrinterFlag>,
indent: usize,
increment: usize,
) -> Self {
Self {
obj,
indent,
inc: increment,

flags: Vec::new(),
flags,
}
}
}

impl<'a> PrettyBer<'a> {
pub fn set_flag(&mut self, flag: PrettyPrinterFlag) {
if !self.flags.contains(&flag) {
self.flags.push(flag);
}
}

pub fn unset_flag(&mut self, flag: PrettyPrinterFlag) {
self.flags.retain(|&f| f != flag);
}

pub fn is_flag_set(&self, flag: PrettyPrinterFlag) -> bool {
self.flags.contains(&flag)
}

pub fn next_indent<'b>(&self, obj: &'b BerObject) -> PrettyBer<'b> {
PrettyBer {
obj,
Expand All @@ -49,6 +71,32 @@ impl<'a> PrettyBer<'a> {
flags: self.flags.to_vec(),
}
}

#[inline]
fn is_recursive(&self) -> bool {
self.is_flag_set(PrettyPrinterFlag::Recursive)
}
}

fn dbg_header(header: &Header, f: &mut fmt::Formatter) -> fmt::Result {
let s_constructed = if header.is_constructed() { "+" } else { "" };
let l = match header.length() {
Length::Definite(sz) => sz.to_string(),
Length::Indefinite => "Indefinite".to_string(),
};
match header.class() {
Class::Universal => {
write!(f, "[{}]{} {}", header.tag(), s_constructed, l)?;
}
Class::ContextSpecific => {
write!(f, "[{}]{} {}", header.tag().0, s_constructed, l)?;
}

class => {
write!(f, "[{} {}]{} {}", class, header.tag().0, s_constructed, l)?;
}
}
Ok(())
}

impl<'a> fmt::Debug for PrettyBer<'a> {
Expand All @@ -58,7 +106,8 @@ impl<'a> fmt::Debug for PrettyBer<'a> {
write!(f, "{:1$}", " ", self.indent)?;
};
if self.flags.contains(&PrettyPrinterFlag::ShowHeader) {
write!(f, "[c:{:?}, s:{}, t:{}] ", self.obj.header.class(), self.obj.header.constructed(), self.obj.header.tag())?;
dbg_header(&self.obj.header, f)?;
write!(f, " ")?;
};
fn print_utf32_string_with_type(f: &mut fmt::Formatter, s: &[u8], ty: &str) -> fmt::Result {
let chars: Option<Vec<char>> = s
Expand All @@ -72,34 +121,34 @@ impl<'a> fmt::Debug for PrettyBer<'a> {
}
}
match self.obj.content {
BerObjectContent::EndOfContent => writeln!(f, "EndOfContent"),
BerObjectContent::Boolean(b) => writeln!(f, "Boolean({:?})", b),
BerObjectContent::Integer(i) => writeln!(f, "Integer({:?})", debug::HexSlice(i)),
BerObjectContent::Enum(i) => writeln!(f, "Enum({})", i),
BerObjectContent::OID(ref v) => writeln!(f, "OID({:?})", v),
BerObjectContent::RelativeOID(ref v) => writeln!(f, "RelativeOID({:?})", v),
BerObjectContent::Null => writeln!(f, "Null"),
BerObjectContent::OctetString(v) => writeln!(f, "OctetString({:?})", debug::HexSlice(v)),
BerObjectContent::EndOfContent => write!(f, "EndOfContent"),
BerObjectContent::Boolean(b) => write!(f, "Boolean({:?})", b),
BerObjectContent::Integer(i) => write!(f, "Integer({:?})", HexSlice(i)),
BerObjectContent::Enum(i) => write!(f, "Enum({})", i),
BerObjectContent::OID(ref v) => write!(f, "OID({:?})", v),
BerObjectContent::RelativeOID(ref v) => write!(f, "RelativeOID({:?})", v),
BerObjectContent::Null => write!(f, "Null"),
BerObjectContent::OctetString(v) => write!(f, "OctetString({:?})", HexSlice(v)),
BerObjectContent::BitString(u,BitStringObject{data:v})
=> writeln!(f, "BitString({},{:?})", u, debug::HexSlice(v)),
BerObjectContent::GeneralizedTime(ref time) => writeln!(f, "GeneralizedTime(\"{}\")", time),
BerObjectContent::UTCTime(ref time) => writeln!(f, "UTCTime(\"{}\")", time),
BerObjectContent::VisibleString(s) => writeln!(f, "VisibleString(\"{}\")", s),
BerObjectContent::GeneralString(s) => writeln!(f, "GeneralString(\"{}\")", s),
BerObjectContent::GraphicString(s) => writeln!(f, "GraphicString(\"{}\")", s),
BerObjectContent::PrintableString(s) => writeln!(f, "PrintableString(\"{}\")", s),
BerObjectContent::NumericString(s) => writeln!(f, "NumericString(\"{}\")", s),
BerObjectContent::UTF8String(s) => writeln!(f, "UTF8String(\"{}\")", s),
BerObjectContent::IA5String(s) => writeln!(f, "IA5String(\"{}\")", s),
BerObjectContent::T61String(s) => writeln!(f, "T61String({})", s),
BerObjectContent::VideotexString(s) => writeln!(f, "VideotexString({})", s),
BerObjectContent::ObjectDescriptor(s) => writeln!(f, "ObjectDescriptor(\"{}\")", s),
BerObjectContent::BmpString(s) => writeln!(f, "BmpString(\"{}\")", s),
=> write!(f, "BitString({},{:?})", u, HexSlice(v)),
BerObjectContent::GeneralizedTime(ref time) => write!(f, "GeneralizedTime(\"{}\")", time),
BerObjectContent::UTCTime(ref time) => write!(f, "UTCTime(\"{}\")", time),
BerObjectContent::VisibleString(s) => write!(f, "VisibleString(\"{}\")", s),
BerObjectContent::GeneralString(s) => write!(f, "GeneralString(\"{}\")", s),
BerObjectContent::GraphicString(s) => write!(f, "GraphicString(\"{}\")", s),
BerObjectContent::PrintableString(s) => write!(f, "PrintableString(\"{}\")", s),
BerObjectContent::NumericString(s) => write!(f, "NumericString(\"{}\")", s),
BerObjectContent::UTF8String(s) => write!(f, "UTF8String(\"{}\")", s),
BerObjectContent::IA5String(s) => write!(f, "IA5String(\"{}\")", s),
BerObjectContent::T61String(s) => write!(f, "T61String({})", s),
BerObjectContent::VideotexString(s) => write!(f, "VideotexString({})", s),
BerObjectContent::ObjectDescriptor(s) => write!(f, "ObjectDescriptor(\"{}\")", s),
BerObjectContent::BmpString(s) => write!(f, "BmpString(\"{}\")", s),
BerObjectContent::UniversalString(s) => print_utf32_string_with_type(f, s, "UniversalString"),
BerObjectContent::Optional(ref o) => {
match o {
Some(obj) => writeln!(f, "OPTION {:?}", obj),
None => writeln!(f, "NONE"),
Some(obj) => write!(f, "OPTION {:?}", obj),
None => write!(f, "NONE"),
}
}
BerObjectContent::Tagged(class, tag, ref obj) => {
Expand All @@ -108,23 +157,29 @@ impl<'a> fmt::Debug for PrettyBer<'a> {
if self.indent > 0 {
write!(f, "{:1$}", " ", self.indent)?;
};
writeln!(f, "}}")?;
write!(f, "}}")?;
Ok(())
},
BerObjectContent::Set(ref v) |
BerObjectContent::Sequence(ref v) => {
let ty = if self.obj.header.tag() == Tag::Sequence { "Sequence" } else { "Set" };
writeln!(f, "{}[", ty)?;
for o in v {
write!(f, "{:?}", self.next_indent(o))?;
};
if self.indent > 0 {
write!(f, "{:1$}", " ", self.indent)?;
};
writeln!(f, "]")?;
if self.is_recursive() {
writeln!(f, "{}[", ty)?;
for o in v {
write!(f, "{:?}", self.next_indent(o))?;
};
if self.indent > 0 {
write!(f, "{:1$}", " ", self.indent)?;
};
write!(f, "]")?;
} else {
write!(f, "{}", ty)?;
}
Ok(())
},
BerObjectContent::Unknown(ref any) => writeln!(f, "Unknown([{} {}] {:x?})", any.class(), any.tag().0, debug::HexSlice(any.data)),
BerObjectContent::Unknown(ref any) => {
write!(f, "Unknown {:x?}", HexSlice(any.data))
},
}
}
}
Expand Down
Loading

0 comments on commit 76f7334

Please sign in to comment.