use crate::{AlgorithmIdentifier, SubjectPublicKeyInfo};
use crate::{ext, name::Name, serial_number::SerialNumber, time::Validity};
use alloc::vec::Vec;
use const_oid::AssociatedOid;
use core::{cmp::Ordering, fmt::Debug};
use der::{Decode, Enumerated, ErrorKind, Sequence, Tag, ValueOrd, asn1::BitString};
#[cfg(feature = "pem")]
use der::{
DecodePem,
pem::{self, PemLabel},
};
#[cfg(feature = "digest")]
use {
der::Encode,
digest::{Digest, Output},
spki::DigestWriter,
};
use crate::time::Time;
pub trait Profile: PartialEq + Debug + Eq + Ord + Clone + Copy + Default + 'static {
fn check_serial_number(serial: &SerialNumber<Self>) -> der::Result<()> {
if serial.inner.len() > SerialNumber::<Self>::MAX_DECODE_LEN {
Err(Tag::Integer.value_error().into())
} else {
Ok(())
}
}
fn time_encoding(mut time: Time) -> der::Result<Time> {
time.rfc5280_adjust_utc_time()?;
Ok(time)
}
}
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Default)]
pub struct Rfc5280;
impl Profile for Rfc5280 {}
#[cfg(feature = "hazmat")]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Default)]
pub struct Raw;
#[cfg(feature = "hazmat")]
impl Profile for Raw {
fn check_serial_number(_serial: &SerialNumber<Self>) -> der::Result<()> {
Ok(())
}
fn time_encoding(time: Time) -> der::Result<Time> {
Ok(time)
}
}
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, Copy, PartialEq, Eq, Enumerated)]
#[asn1(type = "INTEGER")]
#[repr(u8)]
#[derive(Default)]
pub enum Version {
#[default]
V1 = 0,
V2 = 1,
V3 = 2,
}
impl ValueOrd for Version {
fn value_cmp(&self, other: &Self) -> der::Result<Ordering> {
(*self as u8).value_cmp(&(*other as u8))
}
}
pub type TbsCertificate = TbsCertificateInner<Rfc5280>;
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
#[allow(missing_docs)]
pub struct TbsCertificateInner<P: Profile = Rfc5280> {
#[asn1(context_specific = "0", default = "Default::default")]
pub(crate) version: Version,
pub(crate) serial_number: SerialNumber<P>,
pub(crate) signature: AlgorithmIdentifier,
pub(crate) issuer: Name,
pub(crate) validity: Validity<P>,
pub(crate) subject: Name,
pub(crate) subject_public_key_info: SubjectPublicKeyInfo,
#[asn1(context_specific = "1", tag_mode = "IMPLICIT", optional = "true")]
pub(crate) issuer_unique_id: Option<BitString>,
#[asn1(context_specific = "2", tag_mode = "IMPLICIT", optional = "true")]
pub(crate) subject_unique_id: Option<BitString>,
#[asn1(context_specific = "3", tag_mode = "EXPLICIT", optional = "true")]
pub(crate) extensions: Option<ext::Extensions>,
}
impl<P: Profile> TbsCertificateInner<P> {
pub fn version(&self) -> Version {
self.version
}
pub fn serial_number(&self) -> &SerialNumber<P> {
&self.serial_number
}
pub fn signature(&self) -> &AlgorithmIdentifier {
&self.signature
}
pub fn issuer(&self) -> &Name {
&self.issuer
}
pub fn validity(&self) -> &Validity<P> {
&self.validity
}
pub fn subject(&self) -> &Name {
&self.subject
}
pub fn subject_public_key_info(&self) -> &SubjectPublicKeyInfo {
&self.subject_public_key_info
}
pub fn issuer_unique_id(&self) -> &Option<BitString> {
&self.issuer_unique_id
}
pub fn subject_unique_id(&self) -> &Option<BitString> {
&self.subject_unique_id
}
pub fn extensions(&self) -> Option<&ext::Extensions> {
self.extensions.as_ref()
}
pub fn get_extension<'a, T: Decode<'a> + AssociatedOid>(
&'a self,
) -> Result<Option<(bool, T)>, <T as Decode<'a>>::Error> {
let mut iter = self.filter_extensions::<T>().peekable();
match iter.next() {
None => Ok(None),
Some(item) => match iter.peek() {
Some(..) => Err(der::Error::from(ErrorKind::Failed).into()),
None => Ok(Some(item?)),
},
}
}
pub fn filter_extensions<'a, T: Decode<'a> + AssociatedOid>(
&'a self,
) -> impl 'a + Iterator<Item = Result<(bool, T), <T as Decode<'a>>::Error>> {
self.extensions
.as_deref()
.unwrap_or(&[])
.iter()
.filter(|e| e.extn_id == T::OID)
.map(|e| Ok((e.critical, T::from_der(e.extn_value.as_bytes())?)))
}
}
pub type Certificate = CertificateInner<Rfc5280>;
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)]
#[allow(missing_docs)]
pub struct CertificateInner<P: Profile = Rfc5280> {
pub(crate) tbs_certificate: TbsCertificateInner<P>,
pub(crate) signature_algorithm: AlgorithmIdentifier,
pub(crate) signature: BitString,
}
impl<P: Profile> CertificateInner<P> {
pub fn tbs_certificate(&self) -> &TbsCertificateInner<P> {
&self.tbs_certificate
}
pub fn signature_algorithm(&self) -> &AlgorithmIdentifier {
&self.signature_algorithm
}
pub fn signature(&self) -> &BitString {
&self.signature
}
}
#[cfg(feature = "pem")]
impl<P: Profile> PemLabel for CertificateInner<P> {
const PEM_LABEL: &'static str = "CERTIFICATE";
}
pub type PkiPath = Vec<Certificate>;
#[cfg(feature = "pem")]
impl<P: Profile> CertificateInner<P> {
pub fn load_pem_chain(mut input: &[u8]) -> Result<Vec<Self>, der::Error> {
fn find_boundary<T>(haystack: &[T], needle: &[T]) -> Option<usize>
where
for<'a> &'a [T]: PartialEq,
{
haystack
.windows(needle.len())
.position(|window| window == needle)
}
let mut certs = Vec::new();
let mut position: usize = 0;
let end_boundary = &b"-----END CERTIFICATE-----"[..];
loop {
if input.is_empty() {
break;
}
let last_pos = input.len() - 1;
match input.get(last_pos) {
Some(b'\r') | Some(b'\n') => {
input = &input[..last_pos];
}
_ => break,
}
}
while position + 1 < input.len() {
let rest = &input[position..];
let end_pos = find_boundary(rest, end_boundary)
.ok_or(pem::Error::PostEncapsulationBoundary)?
+ end_boundary.len();
let cert_buf = &rest[..end_pos];
let cert = Self::from_pem(cert_buf)?;
certs.push(cert);
position += end_pos;
}
Ok(certs)
}
}
#[cfg(feature = "digest")]
impl<P> CertificateInner<P>
where
P: Profile,
{
pub fn hash<D>(&self) -> der::Result<Output<D>>
where
D: Digest,
{
let mut digest = D::new();
self.encode(&mut DigestWriter(&mut digest))?;
Ok(digest.finalize())
}
}