blob: 29ee8970809e967a60bf9ee3b72c858f04db1605 [file] [log] [blame] [edit]
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!("... <continued>");
}
}
fn main() -> std::result::Result<(), Box<dyn Error>> {
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<Any>, 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());
indent_println!(
depth + 1,
"OID: {}",
format_oid(&der_oid, &ctx.oid_registry).cyan()
);
}
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());
indent_println!(
depth + 1,
"RELATIVE-OID: {}",
format_oid(&der_oid, &ctx.oid_registry).cyan()
);
}
Tag::Set => {
let seq = any.set().unwrap();
for item in seq.der_iter::<Any, asn1_rs::Error>() {
print_der_result_any(item, depth + 1, ctx);
}
}
Tag::Sequence => {
let seq = any.sequence().unwrap();
for item in seq.der_iter::<Any, asn1_rs::Error>() {
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(),
}
}