pub mod oid {
pub use x509_parser::oid_registry::*;
pub use x509_parser::objects::*;
}
pub mod bigint {
pub use x509_parser::der_parser::num_bigint::*;
}
pub mod x509 {
pub use x509_parser::certificate::*;
pub use x509_parser::cri_attributes::*;
pub use x509_parser::error::*;
pub use x509_parser::extensions::*;
pub use x509_parser::revocation_list::*;
pub use x509_parser::time::*;
pub use x509_parser::x509::*;
pub use x509_parser::der_parser::der;
pub use x509_parser::der_parser::ber;
pub use x509_parser::traits::*;
}
use std::fmt;
use std::ops::Deref;
use std::num::NonZeroUsize;
use ref_cast::RefCast;
use x509_parser::nom;
use x509::{ParsedExtension, X509Name, X509Certificate, TbsCertificate, X509Error, FromDer};
use oid::OID_X509_EXT_SUBJECT_ALT_NAME as SUBJECT_ALT_NAME;
use crate::listener::CertificateData;
pub type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Debug, PartialEq)]
pub struct Certificate<'a> {
x509: X509Certificate<'a>,
data: &'a CertificateData,
}
#[repr(transparent)]
#[derive(Debug, PartialEq, RefCast)]
pub struct Name<'a>(X509Name<'a>);
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum Error {
Empty,
NoSubject,
NonCriticalSubjectAlt,
Parse(X509Error),
Incomplete(Option<NonZeroUsize>),
Trailing(usize),
}
impl<'a> Certificate<'a> {
fn parse_one(raw: &[u8]) -> Result<X509Certificate<'_>> {
let (left, x509) = X509Certificate::from_der(raw)?;
if !left.is_empty() {
return Err(Error::Trailing(left.len()));
}
if x509.subject().as_raw().is_empty() {
if let Some(ext) = x509.extensions().iter().find(|e| e.oid == SUBJECT_ALT_NAME) {
if !matches!(ext.parsed_extension(), ParsedExtension::SubjectAlternativeName(..)) {
return Err(Error::NoSubject);
} else if !ext.critical {
return Err(Error::NonCriticalSubjectAlt);
}
} else {
return Err(Error::NoSubject);
}
}
Ok(x509)
}
#[inline(always)]
fn inner(&self) -> &TbsCertificate<'a> {
&self.x509.tbs_certificate
}
#[doc(hidden)]
pub fn parse(chain: &[CertificateData]) -> Result<Certificate<'_>> {
let data = chain.first().ok_or_else(|| Error::Empty)?;
let x509 = Certificate::parse_one(&data.0)?;
Ok(Certificate { x509, data })
}
pub fn serial(&self) -> &bigint::BigUint {
&self.inner().serial
}
pub fn version(&self) -> u32 {
self.inner().version.0
}
pub fn subject(&self) -> &Name<'a> {
Name::ref_cast(&self.inner().subject)
}
pub fn issuer(&self) -> &Name<'a> {
Name::ref_cast(&self.inner().issuer)
}
pub fn extensions(&self) -> &[x509::X509Extension<'a>] {
&self.inner().extensions()
}
pub fn has_serial(&self, number: &str) -> Option<bool> {
let uint: bigint::BigUint = number.parse().ok()?;
Some(&uint == self.serial())
}
pub fn as_bytes(&self) -> &'a [u8] {
&self.data.0
}
}
impl<'a> Deref for Certificate<'a> {
type Target = TbsCertificate<'a>;
fn deref(&self) -> &Self::Target {
self.inner()
}
}
impl<'a> Name<'a> {
pub fn common_name(&self) -> Option<&'a str> {
self.common_names().next()
}
pub fn common_names(&self) -> impl Iterator<Item = &'a str> + '_ {
self.iter_by_oid(&oid::OID_X509_COMMON_NAME).filter_map(|n| n.as_str().ok())
}
pub fn email(&self) -> Option<&'a str> {
self.emails().next()
}
pub fn emails(&self) -> impl Iterator<Item = &'a str> + '_ {
self.iter_by_oid(&oid::OID_PKCS9_EMAIL_ADDRESS).filter_map(|n| n.as_str().ok())
}
pub fn is_empty(&self) -> bool {
self.0.as_raw().is_empty()
}
}
impl<'a> Deref for Name<'a> {
type Target = X509Name<'a>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl fmt::Display for Name<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Parse(e) => write!(f, "parse error: {}", e),
Error::Incomplete(_) => write!(f, "incomplete certificate data"),
Error::Trailing(n) => write!(f, "found {} trailing bytes", n),
Error::Empty => write!(f, "empty certificate chain"),
Error::NoSubject => write!(f, "empty subject without subjectAlt"),
Error::NonCriticalSubjectAlt => write!(f, "empty subject without critical subjectAlt"),
}
}
}
impl From<nom::Err<X509Error>> for Error {
fn from(e: nom::Err<X509Error>) -> Self {
match e {
nom::Err::Incomplete(nom::Needed::Unknown) => Error::Incomplete(None),
nom::Err::Incomplete(nom::Needed::Size(n)) => Error::Incomplete(Some(n)),
nom::Err::Error(e) | nom::Err::Failure(e) => Error::Parse(e),
}
}
}
impl std::error::Error for Error {
}