Add Debug trait for X509 and other types.

This currently leaves out at least two useful things:
 - The detailed SubjectPublicKeyInfo, e.g. the modulus of RSA keys.
 - Extensions.
This commit is contained in:
Jacob Hoffman-Andrews 2020-05-29 21:34:20 -07:00
parent 7446f9fa68
commit 6482f419b8
5 changed files with 162 additions and 11 deletions

View File

@ -67,12 +67,16 @@ foreign_type_and_impl_send_sync! {
impl fmt::Display for Asn1GeneralizedTimeRef { impl fmt::Display for Asn1GeneralizedTimeRef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
unsafe { unsafe {
let mem_bio = MemBio::new()?; match MemBio::new() {
cvt(ffi::ASN1_GENERALIZEDTIME_print( Err(_) => f.write_str(""),
mem_bio.as_ptr(), Ok(mem_bio) => match cvt(ffi::ASN1_GENERALIZEDTIME_print(
self.as_ptr(), mem_bio.as_ptr(),
))?; self.as_ptr(),
write!(f, "{}", str::from_utf8_unchecked(mem_bio.get_buf())) )) {
Err(_) => f.write_str(""),
Ok(_) => f.write_str(str::from_utf8_unchecked(mem_bio.get_buf())),
},
}
} }
} }
} }
@ -207,13 +211,23 @@ impl<'a> PartialOrd<Asn1Time> for &'a Asn1TimeRef {
impl fmt::Display for Asn1TimeRef { impl fmt::Display for Asn1TimeRef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
unsafe { unsafe {
let mem_bio = MemBio::new()?; match MemBio::new() {
cvt(ffi::ASN1_TIME_print(mem_bio.as_ptr(), self.as_ptr()))?; Err(_) => f.write_str("error"),
write!(f, "{}", str::from_utf8_unchecked(mem_bio.get_buf())) Ok(mem_bio) => match cvt(ffi::ASN1_TIME_print(mem_bio.as_ptr(), self.as_ptr())) {
Err(_) => f.write_str("error"),
Ok(_) => f.write_str(str::from_utf8_unchecked(mem_bio.get_buf())),
},
}
} }
} }
} }
impl fmt::Debug for Asn1TimeRef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(&self.to_string())
}
}
impl Asn1Time { impl Asn1Time {
fn new() -> Result<Asn1Time, ErrorStack> { fn new() -> Result<Asn1Time, ErrorStack> {
ffi::init(); ffi::init();
@ -389,6 +403,15 @@ impl Asn1StringRef {
} }
} }
impl fmt::Debug for Asn1StringRef {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self.as_utf8() {
Ok(openssl_string) => openssl_string.fmt(fmt),
Err(_) => fmt.write_str("error"),
}
}
}
foreign_type_and_impl_send_sync! { foreign_type_and_impl_send_sync! {
type CType = ffi::ASN1_INTEGER; type CType = ffi::ASN1_INTEGER;
fn drop = ffi::ASN1_INTEGER_free; fn drop = ffi::ASN1_INTEGER_free;
@ -527,12 +550,20 @@ impl fmt::Display for Asn1ObjectRef {
self.as_ptr(), self.as_ptr(),
0, 0,
); );
let s = str::from_utf8(&buf[..len as usize]).map_err(|_| fmt::Error)?; match str::from_utf8(&buf[..len as usize]) {
fmt.write_str(s) Err(_) => fmt.write_str("error"),
Ok(s) => fmt.write_str(s),
}
} }
} }
} }
impl fmt::Debug for Asn1ObjectRef {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.write_str(self.to_string().as_str())
}
}
cfg_if! { cfg_if! {
if #[cfg(any(ossl110, libressl273))] { if #[cfg(any(ossl110, libressl273))] {
use ffi::ASN1_STRING_get0_data; use ffi::ASN1_STRING_get0_data;

View File

@ -49,6 +49,7 @@ use ffi;
use foreign_types::{ForeignType, ForeignTypeRef}; use foreign_types::{ForeignType, ForeignTypeRef};
use libc::{c_int, c_long}; use libc::{c_int, c_long};
use std::ffi::CString; use std::ffi::CString;
use std::fmt;
use std::mem; use std::mem;
use std::ptr; use std::ptr;
@ -286,6 +287,27 @@ where
} }
} }
impl<T> fmt::Debug for PKey<T> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let alg = match self.id() {
Id::RSA => "RSA",
Id::HMAC => "HMAC",
Id::DSA => "DSA",
Id::DH => "DH",
Id::EC => "EC",
#[cfg(ossl111)]
Id::ED25519 => "Ed25519",
#[cfg(ossl111)]
Id::ED448 => "Ed448",
_ => "unknown",
};
fmt.debug_struct("public_key")
.field("algorithm", &alg)
.finish()
// TODO: Print details for each specific type of key
}
}
impl<T> Clone for PKey<T> { impl<T> Clone for PKey<T> {
fn clone(&self) -> PKey<T> { fn clone(&self) -> PKey<T> {
PKeyRef::to_owned(self) PKeyRef::to_owned(self)

View File

@ -3,6 +3,7 @@ use foreign_types::{ForeignType, ForeignTypeRef, Opaque};
use libc::c_int; use libc::c_int;
use std::borrow::Borrow; use std::borrow::Borrow;
use std::convert::AsRef; use std::convert::AsRef;
use std::fmt;
use std::iter; use std::iter;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::mem; use std::mem;
@ -43,6 +44,15 @@ pub struct Stack<T: Stackable>(*mut T::StackType);
unsafe impl<T: Stackable + Send> Send for Stack<T> {} unsafe impl<T: Stackable + Send> Send for Stack<T> {}
unsafe impl<T: Stackable + Sync> Sync for Stack<T> {} unsafe impl<T: Stackable + Sync> Sync for Stack<T> {}
impl<T> fmt::Debug for Stack<T>
where
T: Stackable,
T::Ref: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_list().entries(self).finish()
}
}
impl<T: Stackable> Drop for Stack<T> { impl<T: Stackable> Drop for Stack<T> {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { unsafe {

View File

@ -671,6 +671,35 @@ impl Clone for X509 {
} }
} }
impl fmt::Debug for X509 {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
let serial = match &self.serial_number().to_bn() {
Ok(bn) => match bn.to_hex_str() {
Ok(hex) => hex.to_string(),
Err(_) => "".to_string(),
},
Err(_) => "".to_string(),
};
let mut debug_struct = formatter.debug_struct("X509");
debug_struct.field("serial_number", &serial);
debug_struct.field("signature_algorithm", &self.signature_algorithm().object());
debug_struct.field("issuer", &self.issuer_name());
debug_struct.field("subject", &self.subject_name());
if let Some(subject_alt_names) = &self.subject_alt_names() {
debug_struct.field("subject_alt_names", subject_alt_names);
}
debug_struct.field("not_before", &self.not_before());
debug_struct.field("not_after", &self.not_after());
if let Ok(public_key) = &self.public_key() {
debug_struct.field("public_key", public_key);
};
// TODO: Print extensions once they are supported on the X509 struct.
debug_struct.finish()
}
}
impl AsRef<X509Ref> for X509Ref { impl AsRef<X509Ref> for X509Ref {
fn as_ref(&self) -> &X509Ref { fn as_ref(&self) -> &X509Ref {
self self
@ -867,6 +896,12 @@ impl X509NameRef {
} }
} }
impl fmt::Debug for X509NameRef {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.debug_list().entries(self.entries()).finish()
}
}
/// A type to destructure and examine an `X509Name`. /// A type to destructure and examine an `X509Name`.
pub struct X509NameEntries<'a> { pub struct X509NameEntries<'a> {
name: &'a X509NameRef, name: &'a X509NameRef,
@ -942,6 +977,12 @@ impl X509NameEntryRef {
} }
} }
impl fmt::Debug for X509NameEntryRef {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_fmt(format_args!("{:?} = {:?}", self.object(), self.data()))
}
}
/// A builder used to construct an `X509Req`. /// A builder used to construct an `X509Req`.
pub struct X509ReqBuilder(X509Req); pub struct X509ReqBuilder(X509Req);
@ -1298,6 +1339,25 @@ impl GeneralNameRef {
} }
} }
impl fmt::Debug for GeneralNameRef {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
if let Some(email) = self.email() {
return formatter.write_str(email);
}
if let Some(dnsname) = self.dnsname() {
return formatter.write_str(dnsname);
}
if let Some(uri) = self.uri() {
return formatter.write_str(uri);
}
if let Some(ipaddress) = self.ipaddress() {
let result = String::from_utf8_lossy(ipaddress);
return formatter.write_str(&result);
}
Ok(())
}
}
impl Stackable for GeneralName { impl Stackable for GeneralName {
type StackType = ffi::stack_st_GENERAL_NAME; type StackType = ffi::stack_st_GENERAL_NAME;
} }

View File

@ -31,6 +31,34 @@ fn test_cert_loading() {
assert_eq!(hash_vec, &*fingerprint); assert_eq!(hash_vec, &*fingerprint);
} }
#[test]
fn test_debug() {
let cert = include_bytes!("../../test/cert.pem");
let cert = X509::from_pem(cert).unwrap();
let debugged = format!("{:#?}", cert);
let expected = r#"X509 {
serial_number: "8771F7BDEE982FA5",
signature_algorithm: sha256WithRSAEncryption,
issuer: [
countryName = "AU",
stateOrProvinceName = "Some-State",
organizationName = "Internet Widgits Pty Ltd",
],
subject: [
countryName = "AU",
stateOrProvinceName = "Some-State",
organizationName = "Internet Widgits Pty Ltd",
commonName = "foobar.com",
],
not_before: Aug 14 17:00:03 2016 GMT,
not_after: Aug 12 17:00:03 2026 GMT,
public_key: public_key {
algorithm: "RSA",
},
}"#;
assert_eq!(expected, debugged);
}
#[test] #[test]
fn test_cert_issue_validity() { fn test_cert_issue_validity() {
let cert = include_bytes!("../../test/cert.pem"); let cert = include_bytes!("../../test/cert.pem");