240 lines
7.8 KiB
Rust
240 lines
7.8 KiB
Rust
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 Default for Context<'_> {
|
|
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());
|
|
// 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::<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(),
|
|
}
|
|
}
|