use asn1_rs::{Any, Class, FromDer, Length, Result, Tag}; use colored::*; use nom::HexDisplay; // use oid_registry::{format_oid, Oid as DerOid, OidRegistry}; use std::cmp::min; use std::error::Error; use std::marker::PhantomData; use std::{env, fs}; struct Context<'a> { // oid_registry: OidRegistry<'a>, hex_max: usize, t: PhantomData<&'a ()>, } impl<'a> Default for Context<'a> { fn default() -> Self { // let oid_registry = OidRegistry::default().with_all_crypto().with_x509(); Context { // oid_registry, hex_max: 64, t: PhantomData, } } } #[macro_export] macro_rules! indent_println { ( $depth: expr, $fmt:expr ) => { println!(concat!("{:indent$}",$fmt), "", indent = 2*$depth) }; ( $depth: expr, $fmt:expr, $( $x:expr ),* ) => { println!(concat!("{:indent$}",$fmt), "", $($x),*, indent = 2*$depth) }; } #[allow(dead_code)] pub fn print_hex_dump(bytes: &[u8], max_len: usize) { let m = min(bytes.len(), max_len); print!("{}", &bytes[..m].to_hex(16)); if bytes.len() > max_len { println!("... "); } } fn main() -> std::result::Result<(), Box> { let ctx = Context::default(); for filename in env::args().skip(1) { eprintln!("File: {}", filename); let content = fs::read(&filename)?; // check for PEM file if filename.ends_with(".pem") || content.starts_with(b"----") { let pems = pem::parse_many(&content).expect("Parsing PEM failed"); if pems.is_empty() { eprintln!("{}", "No PEM section decoded".bright_red()); continue; } for (idx, pem) in pems.iter().enumerate() { eprintln!("Pem entry {} [{}]", idx, pem.tag().bright_blue()); print_der(pem.contents(), 1, &ctx); } } else { print_der(&content, 1, &ctx); } } Ok(()) } fn print_der(i: &[u8], depth: usize, ctx: &Context) { match Any::from_der(i) { Ok((rem, any)) => { print_der_any(any, depth, ctx); if !rem.is_empty() { let warning = format!("WARNING: {} extra bytes after object", rem.len()); indent_println!(depth, "{}", warning.bright_red()); print_hex_dump(rem, ctx.hex_max); } } Err(e) => { eprintln!("Error while parsing at depth {}: {:?}", depth, e); } } } fn print_der_result_any(r: Result, depth: usize, ctx: &Context) { match r { Ok(any) => print_der_any(any, depth, ctx), Err(e) => { eprintln!("Error while parsing at depth {}: {:?}", depth, e); } } } fn print_der_any(any: Any, depth: usize, ctx: &Context) { let class = match any.header.class() { Class::Universal => "UNIVERSAL".to_string().white(), c => c.to_string().cyan(), }; let hdr = format!( "[c:{} t:{}({}) l:{}]", class, any.header.tag().0, any.header.tag().to_string().white(), str_of_length(any.header.length()) ); indent_println!(depth, "{}", hdr); match any.header.class() { Class::Universal => (), Class::ContextSpecific | Class::Application => { // attempt to decode inner object (if EXPLICIT) match Any::from_der(any.data) { Ok((rem2, inner)) => { indent_println!( depth + 1, "{} (rem.len={})", format!("EXPLICIT [{}]", any.header.tag().0).green(), // any.header.tag.0, rem2.len() ); print_der_any(inner, depth + 2, ctx); } Err(_) => { // assume tagged IMPLICIT indent_println!( depth + 1, "{}", "could not decode (IMPLICIT tagging?)".bright_red() ); } } return; } _ => { indent_println!( depth + 1, "tagged: [{}] {}", any.header.tag().0, "*NOT SUPPORTED*".red() ); return; } } match any.header.tag() { Tag::BitString => { let b = any.bitstring().unwrap(); indent_println!(depth + 1, "BITSTRING"); print_hex_dump(b.as_ref(), ctx.hex_max); } Tag::Boolean => { let b = any.bool().unwrap(); indent_println!(depth + 1, "BOOLEAN: {}", b.to_string().green()); } Tag::EmbeddedPdv => { let e = any.embedded_pdv().unwrap(); indent_println!(depth + 1, "EMBEDDED PDV: {:?}", e); print_hex_dump(e.data_value, ctx.hex_max); } Tag::Enumerated => { let i = any.enumerated().unwrap(); indent_println!(depth + 1, "ENUMERATED: {}", i.0); } Tag::GeneralizedTime => { let s = any.generalizedtime().unwrap(); indent_println!(depth + 1, "GeneralizedTime: {}", s); } Tag::GeneralString => { let s = any.generalstring().unwrap(); indent_println!(depth + 1, "GeneralString: {}", s.as_ref()); } Tag::Ia5String => { let s = any.ia5string().unwrap(); indent_println!(depth + 1, "IA5String: {}", s.as_ref()); } Tag::Integer => { let i = any.integer().unwrap(); match i.as_i128() { Ok(i) => { indent_println!(depth + 1, "{}", i); } Err(_) => { print_hex_dump(i.as_ref(), ctx.hex_max); } } } Tag::Null => (), Tag::OctetString => { let b = any.octetstring().unwrap(); indent_println!(depth + 1, "OCTETSTRING"); print_hex_dump(b.as_ref(), ctx.hex_max); } Tag::Oid => { let oid = any.oid().unwrap(); // let der_oid = DerOid::new(oid.as_bytes().into()); // let s = format_oid(&der_oid, &ctx.oid_registry).cyan(); let s = oid.to_string().cyan(); indent_println!(depth + 1, "OID: {}", s); } Tag::PrintableString => { let s = any.printablestring().unwrap(); indent_println!(depth + 1, "PrintableString: {}", s.as_ref()); } Tag::RelativeOid => { let oid = any.oid().unwrap(); // let der_oid = DerOid::new(oid.as_bytes().into()); // let s = format_oid(&der_oid, &ctx.oid_registry).cyan(); let s = oid.to_string().cyan(); indent_println!(depth + 1, "RELATIVE-OID: {}", s); } Tag::Set => { let seq = any.set().unwrap(); for item in seq.der_iter::() { print_der_result_any(item, depth + 1, ctx); } } Tag::Sequence => { let seq = any.sequence().unwrap(); for item in seq.der_iter::() { print_der_result_any(item, depth + 1, ctx); } } Tag::UtcTime => { let s = any.utctime().unwrap(); indent_println!(depth + 1, "UtcTime: {}", s); } Tag::Utf8String => { let s = any.utf8string().unwrap(); indent_println!(depth + 1, "UTF-8: {}", s.as_ref()); } _ => unimplemented!("unsupported tag {}", any.header.tag()), } } fn str_of_length(l: Length) -> String { match l { Length::Definite(l) => l.to_string(), Length::Indefinite => "Indefinite".to_string(), } }