//! Certificate types use crate::{name::Name, serial_number::SerialNumber, time::Validity}; use alloc::vec::Vec; use const_oid::AssociatedOid; use core::{cmp::Ordering, fmt::Debug}; use der::asn1::BitString; use der::{Decode, Enumerated, Error, ErrorKind, Sequence, Tag, ValueOrd}; use spki::{AlgorithmIdentifierOwned, SubjectPublicKeyInfoOwned}; #[cfg(feature = "pem")] use der::{ pem::{self, PemLabel}, DecodePem, }; /// [`Profile`] allows the consumer of this crate to customize the behavior when parsing /// certificates. /// By default, parsing will be made in a rfc5280-compliant manner. pub trait Profile: PartialEq + Debug + Eq + Clone { /// Checks to run when parsing serial numbers fn check_serial_number(serial: &SerialNumber) -> der::Result<()> { // See the note in `SerialNumber::new`: we permit lengths of 21 bytes here, // since some X.509 implementations interpret the limit of 20 bytes to refer // to the pre-encoded value. if serial.inner.len() > SerialNumber::::MAX_DECODE_LEN { Err(Tag::Integer.value_error()) } else { Ok(()) } } } #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Debug, PartialEq, Eq, Clone)] /// Parse certificates with rfc5280-compliant checks pub struct Rfc5280; impl Profile for Rfc5280 {} #[cfg(feature = "hazmat")] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Debug, PartialEq, Eq, Clone)] /// Parse raw x509 certificate and disable all the checks pub struct Raw; #[cfg(feature = "hazmat")] impl Profile for Raw { fn check_serial_number(_serial: &SerialNumber) -> der::Result<()> { Ok(()) } } /// Certificate `Version` as defined in [RFC 5280 Section 4.1]. /// /// ```text /// Version ::= INTEGER { v1(0), v2(1), v3(2) } /// ``` /// /// [RFC 5280 Section 4.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1 #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Copy, PartialEq, Eq, Enumerated)] #[asn1(type = "INTEGER")] #[repr(u8)] pub enum Version { /// Version 1 (default) V1 = 0, /// Version 2 V2 = 1, /// Version 3 V3 = 2, } impl ValueOrd for Version { fn value_cmp(&self, other: &Self) -> der::Result { (*self as u8).value_cmp(&(*other as u8)) } } impl Default for Version { fn default() -> Self { Self::V1 } } /// X.509 `TbsCertificate` as defined in [RFC 5280 Section 4.1] pub type TbsCertificate = TbsCertificateInner; /// X.509 `TbsCertificate` as defined in [RFC 5280 Section 4.1] /// /// ASN.1 structure containing the names of the subject and issuer, a public /// key associated with the subject, a validity period, and other associated /// information. /// /// ```text /// TBSCertificate ::= SEQUENCE { /// version [0] EXPLICIT Version DEFAULT v1, /// serialNumber CertificateSerialNumber, /// signature AlgorithmIdentifier, /// issuer Name, /// validity Validity, /// subject Name, /// subjectPublicKeyInfo SubjectPublicKeyInfo, /// issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, /// -- If present, version MUST be v2 or v3 /// subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, /// -- If present, version MUST be v2 or v3 /// extensions [3] Extensions OPTIONAL /// -- If present, version MUST be v3 -- /// } /// ``` /// /// [RFC 5280 Section 4.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1 #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)] #[allow(missing_docs)] pub struct TbsCertificateInner { /// The certificate version /// /// Note that this value defaults to Version 1 per the RFC. However, /// fields such as `issuer_unique_id`, `subject_unique_id` and `extensions` /// require later versions. Care should be taken in order to ensure /// standards compliance. #[asn1(context_specific = "0", default = "Default::default")] pub version: Version, pub serial_number: SerialNumber

, pub signature: AlgorithmIdentifierOwned, pub issuer: Name, pub validity: Validity, pub subject: Name, pub subject_public_key_info: SubjectPublicKeyInfoOwned, #[asn1(context_specific = "1", tag_mode = "IMPLICIT", optional = "true")] pub issuer_unique_id: Option, #[asn1(context_specific = "2", tag_mode = "IMPLICIT", optional = "true")] pub subject_unique_id: Option, #[asn1(context_specific = "3", tag_mode = "EXPLICIT", optional = "true")] pub extensions: Option, } impl TbsCertificateInner

{ /// Decodes a single extension /// /// Returns an error if multiple of these extensions is present. Returns /// `Ok(None)` if the extension is not present. Returns a decoding error /// if decoding failed. Otherwise returns the extension. pub fn get<'a, T: Decode<'a> + AssociatedOid>(&'a self) -> Result, Error> { let mut iter = self.filter::().peekable(); match iter.next() { None => Ok(None), Some(item) => match iter.peek() { Some(..) => Err(ErrorKind::Failed.into()), None => Ok(Some(item?)), }, } } /// Filters extensions by an associated OID /// /// Returns a filtered iterator over all the extensions with the OID. pub fn filter<'a, T: Decode<'a> + AssociatedOid>( &'a self, ) -> impl 'a + Iterator> { 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())?))) } } /// X.509 certificates are defined in [RFC 5280 Section 4.1]. /// /// [RFC 5280 Section 4.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1 pub type Certificate = CertificateInner; /// X.509 certificates are defined in [RFC 5280 Section 4.1]. /// /// ```text /// Certificate ::= SEQUENCE { /// tbsCertificate TBSCertificate, /// signatureAlgorithm AlgorithmIdentifier, /// signature BIT STRING /// } /// ``` /// /// [RFC 5280 Section 4.1]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1 #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[derive(Clone, Debug, Eq, PartialEq, Sequence, ValueOrd)] #[allow(missing_docs)] pub struct CertificateInner { pub tbs_certificate: TbsCertificateInner

, pub signature_algorithm: AlgorithmIdentifierOwned, pub signature: BitString, } #[cfg(feature = "pem")] impl PemLabel for CertificateInner

{ const PEM_LABEL: &'static str = "CERTIFICATE"; } /// `PkiPath` as defined by X.509 and referenced by [RFC 6066]. /// /// This contains a series of certificates in validation order from the /// top-most certificate to the bottom-most certificate. This means that /// the first certificate signs the second certificate and so on. /// /// ```text /// PkiPath ::= SEQUENCE OF Certificate /// ``` /// /// [RFC 6066]: https://datatracker.ietf.org/doc/html/rfc6066#section-10.1 pub type PkiPath = Vec; #[cfg(feature = "pem")] impl CertificateInner

{ /// Parse a chain of pem-encoded certificates from a slice. /// /// Returns the list of certificates. pub fn load_pem_chain(mut input: &[u8]) -> Result, Error> { fn find_boundary(haystack: &[T], needle: &[T]) -> Option 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-----"[..]; // Strip the trailing whitespaces 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 < input.len() - 1 { 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) } }