| //! X.509 Certificate object definitions and operations |
| |
| use crate::error::{X509Error, X509Result}; |
| use crate::extensions::*; |
| use crate::time::ASN1Time; |
| use crate::traits::FromDer; |
| #[cfg(feature = "validate")] |
| use crate::validate::Validate; |
| use crate::x509::{ |
| parse_serial, parse_signature_value, AlgorithmIdentifier, SubjectPublicKeyInfo, X509Name, |
| X509Version, |
| }; |
| |
| use der_parser::ber::{parse_ber_optional, BerTag, BitStringObject}; |
| use der_parser::der::*; |
| use der_parser::error::*; |
| use der_parser::num_bigint::BigUint; |
| use der_parser::oid::Oid; |
| use der_parser::*; |
| use nom::{Offset, Parser}; |
| use oid_registry::*; |
| use std::collections::HashMap; |
| #[cfg(feature = "validate")] |
| use std::collections::HashSet; |
| |
| /// An X.509 v3 Certificate. |
| /// |
| /// X.509 v3 certificates are defined in [RFC5280](https://tools.ietf.org/html/rfc5280), section |
| /// 4.1. This object uses the same structure for content, so for ex the subject can be accessed |
| /// using the path `x509.tbs_certificate.subject`. |
| /// |
| /// `X509Certificate` also contains convenience methods to access the most common fields (subject, |
| /// issuer, etc.). |
| /// |
| /// A `X509Certificate` is a zero-copy view over a buffer, so the lifetime is the same as the |
| /// buffer containing the binary representation. |
| /// |
| /// ```rust |
| /// # use x509_parser::certificate::X509Certificate; |
| /// # use x509_parser::traits::FromDer; |
| /// # |
| /// # static DER: &'static [u8] = include_bytes!("../assets/IGC_A.der"); |
| /// # |
| /// fn display_x509_info(x509: &X509Certificate<'_>) { |
| /// let subject = x509.subject(); |
| /// let issuer = x509.issuer(); |
| /// println!("X.509 Subject: {}", subject); |
| /// println!("X.509 Issuer: {}", issuer); |
| /// println!("X.509 serial: {}", x509.tbs_certificate.raw_serial_as_string()); |
| /// } |
| /// # |
| /// # fn main() { |
| /// # let res = X509Certificate::from_der(DER); |
| /// # match res { |
| /// # Ok((_rem, x509)) => { |
| /// # display_x509_info(&x509); |
| /// # }, |
| /// # _ => panic!("x509 parsing failed: {:?}", res), |
| /// # } |
| /// # } |
| /// ``` |
| #[derive(Clone, Debug, PartialEq)] |
| pub struct X509Certificate<'a> { |
| pub tbs_certificate: TbsCertificate<'a>, |
| pub signature_algorithm: AlgorithmIdentifier<'a>, |
| pub signature_value: BitStringObject<'a>, |
| } |
| |
| impl<'a> X509Certificate<'a> { |
| /// Get the version of the encoded certificate |
| pub fn version(&self) -> X509Version { |
| self.tbs_certificate.version |
| } |
| |
| /// Get the certificate subject. |
| #[inline] |
| pub fn subject(&self) -> &X509Name { |
| &self.tbs_certificate.subject |
| } |
| |
| /// Get the certificate issuer. |
| #[inline] |
| pub fn issuer(&self) -> &X509Name { |
| &self.tbs_certificate.issuer |
| } |
| |
| /// Get the certificate validity. |
| #[inline] |
| pub fn validity(&self) -> &Validity { |
| &self.tbs_certificate.validity |
| } |
| |
| /// Get the certificate public key information. |
| #[inline] |
| pub fn public_key(&self) -> &SubjectPublicKeyInfo { |
| &self.tbs_certificate.subject_pki |
| } |
| |
| /// Get the certificate extensions. |
| #[inline] |
| pub fn extensions(&self) -> &[X509Extension] { |
| &self.tbs_certificate.extensions |
| } |
| |
| /// Verify the cryptographic signature of this certificate |
| /// |
| /// `public_key` is the public key of the **signer**. For a self-signed certificate, |
| /// (for ex. a public root certificate authority), this is the key from the certificate, |
| /// so you can use `None`. |
| /// |
| /// For a leaf certificate, this is the public key of the certificate that signed it. |
| /// It is usually an intermediate authority. |
| #[cfg(feature = "verify")] |
| #[cfg_attr(docsrs, doc(cfg(feature = "verify")))] |
| pub fn verify_signature( |
| &self, |
| public_key: Option<&SubjectPublicKeyInfo>, |
| ) -> Result<(), X509Error> { |
| use ring::signature; |
| let spki = public_key.unwrap_or_else(|| self.public_key()); |
| let signature_alg = &self.signature_algorithm.algorithm; |
| // identify verification algorithm |
| let verification_alg: &dyn signature::VerificationAlgorithm = |
| if *signature_alg == OID_PKCS1_SHA1WITHRSA { |
| &signature::RSA_PKCS1_1024_8192_SHA1_FOR_LEGACY_USE_ONLY |
| } else if *signature_alg == OID_PKCS1_SHA256WITHRSA { |
| &signature::RSA_PKCS1_2048_8192_SHA256 |
| } else if *signature_alg == OID_PKCS1_SHA384WITHRSA { |
| &signature::RSA_PKCS1_2048_8192_SHA384 |
| } else if *signature_alg == OID_PKCS1_SHA512WITHRSA { |
| &signature::RSA_PKCS1_2048_8192_SHA512 |
| } else if *signature_alg == OID_SIG_ECDSA_WITH_SHA256 { |
| &signature::ECDSA_P256_SHA256_ASN1 |
| } else if *signature_alg == OID_SIG_ECDSA_WITH_SHA384 { |
| &signature::ECDSA_P384_SHA384_ASN1 |
| } else if *signature_alg == OID_SIG_ED25519 { |
| &signature::ED25519 |
| } else { |
| return Err(X509Error::SignatureUnsupportedAlgorithm); |
| }; |
| // get public key |
| let key = signature::UnparsedPublicKey::new(verification_alg, spki.subject_public_key.data); |
| // verify signature |
| let sig = self.signature_value.data; |
| key.verify(self.tbs_certificate.raw, sig) |
| .or(Err(X509Error::SignatureVerificationError)) |
| } |
| } |
| |
| impl<'a> FromDer<'a> for X509Certificate<'a> { |
| /// Parse a DER-encoded X.509 Certificate, and return the remaining of the input and the built |
| /// object. |
| /// |
| /// The returned object uses zero-copy, and so has the same lifetime as the input. |
| /// |
| /// Note that only parsing is done, not validation. |
| /// |
| /// <pre> |
| /// Certificate ::= SEQUENCE { |
| /// tbsCertificate TBSCertificate, |
| /// signatureAlgorithm AlgorithmIdentifier, |
| /// signatureValue BIT STRING } |
| /// </pre> |
| /// |
| /// # Example |
| /// |
| /// To parse a certificate and print the subject and issuer: |
| /// |
| /// ```rust |
| /// # use x509_parser::parse_x509_certificate; |
| /// # |
| /// # static DER: &'static [u8] = include_bytes!("../assets/IGC_A.der"); |
| /// # |
| /// # fn main() { |
| /// let res = parse_x509_certificate(DER); |
| /// match res { |
| /// Ok((_rem, x509)) => { |
| /// let subject = x509.subject(); |
| /// let issuer = x509.issuer(); |
| /// println!("X.509 Subject: {}", subject); |
| /// println!("X.509 Issuer: {}", issuer); |
| /// }, |
| /// _ => panic!("x509 parsing failed: {:?}", res), |
| /// } |
| /// # } |
| /// ``` |
| fn from_der(i: &'a [u8]) -> X509Result<Self> { |
| // run parser with default options |
| X509CertificateParser::new().parse(i) |
| } |
| } |
| |
| /// X.509 Certificate parser |
| /// |
| /// This object is a parser builder, and allows specifying parsing options. |
| /// Currently, the only option is to control deep parsing of X.509v3 extensions: |
| /// a parser can decide to skip deep-parsing to be faster (the structure of extensions is still |
| /// parsed, and the contents can be parsed later using the [`from_der`](FromDer::from_der) |
| /// method from individual extension objects). |
| /// |
| /// This object uses the `nom::Parser` trait, which must be imported. |
| /// |
| /// # Example |
| /// |
| /// To parse a certificate without parsing extensions: |
| /// |
| /// ```rust |
| /// use x509_parser::certificate::X509CertificateParser; |
| /// use x509_parser::nom::Parser; |
| /// |
| /// # static DER: &'static [u8] = include_bytes!("../assets/IGC_A.der"); |
| /// # |
| /// # fn main() { |
| /// // create a parser that will not parse extensions |
| /// let mut parser = X509CertificateParser::new() |
| /// .with_deep_parse_extensions(false); |
| /// let res = parser.parse(DER); |
| /// match res { |
| /// Ok((_rem, x509)) => { |
| /// let subject = x509.subject(); |
| /// let issuer = x509.issuer(); |
| /// println!("X.509 Subject: {}", subject); |
| /// println!("X.509 Issuer: {}", issuer); |
| /// }, |
| /// _ => panic!("x509 parsing failed: {:?}", res), |
| /// } |
| /// # } |
| /// ``` |
| #[derive(Clone, Copy, Debug, PartialEq)] |
| pub struct X509CertificateParser { |
| deep_parse_extensions: bool, |
| // strict: bool, |
| } |
| |
| impl X509CertificateParser { |
| #[inline] |
| pub const fn new() -> Self { |
| X509CertificateParser { |
| deep_parse_extensions: true, |
| } |
| } |
| |
| #[inline] |
| pub const fn with_deep_parse_extensions(self, deep_parse_extensions: bool) -> Self { |
| X509CertificateParser { |
| deep_parse_extensions, |
| } |
| } |
| } |
| |
| impl<'a> Parser<&'a [u8], X509Certificate<'a>, X509Error> for X509CertificateParser { |
| fn parse(&mut self, input: &'a [u8]) -> IResult<&'a [u8], X509Certificate<'a>, X509Error> { |
| parse_der_sequence_defined_g(|i, _| { |
| // pass options to TbsCertificate parser |
| let mut tbs_parser = |
| TbsCertificateParser::new().with_deep_parse_extensions(self.deep_parse_extensions); |
| let (i, tbs_certificate) = tbs_parser.parse(i)?; |
| let (i, signature_algorithm) = AlgorithmIdentifier::from_der(i)?; |
| let (i, signature_value) = parse_signature_value(i)?; |
| let cert = X509Certificate { |
| tbs_certificate, |
| signature_algorithm, |
| signature_value, |
| }; |
| Ok((i, cert)) |
| })(input) |
| } |
| } |
| |
| #[cfg(feature = "validate")] |
| #[cfg_attr(docsrs, doc(cfg(feature = "validate")))] |
| impl Validate for X509Certificate<'_> { |
| fn validate<W, E>(&self, warn: W, err: E) -> bool |
| where |
| W: FnMut(&str), |
| E: FnMut(&str), |
| { |
| let mut res = true; |
| res |= self.tbs_certificate.validate(warn, err); |
| res |
| } |
| } |
| |
| /// The sequence `TBSCertificate` contains information associated with the |
| /// subject of the certificate and the CA that issued it. |
| /// |
| /// RFC5280 definition: |
| /// |
| /// <pre> |
| /// 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] EXPLICIT Extensions OPTIONAL |
| /// -- If present, version MUST be v3 |
| /// } |
| /// </pre> |
| #[derive(Clone, Debug, PartialEq)] |
| pub struct TbsCertificate<'a> { |
| pub version: X509Version, |
| pub serial: BigUint, |
| pub signature: AlgorithmIdentifier<'a>, |
| pub issuer: X509Name<'a>, |
| pub validity: Validity, |
| pub subject: X509Name<'a>, |
| pub subject_pki: SubjectPublicKeyInfo<'a>, |
| pub issuer_uid: Option<UniqueIdentifier<'a>>, |
| pub subject_uid: Option<UniqueIdentifier<'a>>, |
| extensions: Vec<X509Extension<'a>>, |
| pub(crate) raw: &'a [u8], |
| pub(crate) raw_serial: &'a [u8], |
| } |
| |
| impl<'a> TbsCertificate<'a> { |
| /// Returns the certificate extensions |
| #[inline] |
| pub fn extensions(&self) -> &[X509Extension<'a>] { |
| &self.extensions |
| } |
| |
| /// Returns an iterator over the certificate extensions |
| #[inline] |
| pub fn iter_extensions(&self) -> impl Iterator<Item = &X509Extension<'a>> { |
| self.extensions.iter() |
| } |
| |
| /// Searches for an extension with the given `Oid`. |
| /// |
| /// Note: if there are several extensions with the same `Oid`, the first one is returned. |
| pub fn find_extension(&self, oid: &Oid) -> Option<&X509Extension<'a>> { |
| self.extensions.iter().find(|&ext| ext.oid == *oid) |
| } |
| |
| /// Builds and returns a map of extensions. |
| /// |
| /// If an extension is present twice, this will fail and return `DuplicateExtensions`. |
| pub fn extensions_map(&self) -> Result<HashMap<Oid, &X509Extension<'a>>, X509Error> { |
| self.extensions |
| .iter() |
| .try_fold(HashMap::new(), |mut m, ext| { |
| if m.contains_key(&ext.oid) { |
| return Err(X509Error::DuplicateExtensions); |
| } |
| m.insert(ext.oid.clone(), ext); |
| Ok(m) |
| }) |
| } |
| |
| pub fn basic_constraints(&self) -> Option<(bool, &BasicConstraints)> { |
| self.find_extension(&OID_X509_EXT_BASIC_CONSTRAINTS) |
| .and_then(|ext| match ext.parsed_extension { |
| ParsedExtension::BasicConstraints(ref bc) => Some((ext.critical, bc)), |
| _ => None, |
| }) |
| } |
| |
| pub fn key_usage(&self) -> Option<(bool, &KeyUsage)> { |
| self.find_extension(&OID_X509_EXT_KEY_USAGE) |
| .and_then(|ext| match ext.parsed_extension { |
| ParsedExtension::KeyUsage(ref ku) => Some((ext.critical, ku)), |
| _ => None, |
| }) |
| } |
| |
| pub fn extended_key_usage(&self) -> Option<(bool, &ExtendedKeyUsage<'a>)> { |
| self.find_extension(&OID_X509_EXT_EXTENDED_KEY_USAGE) |
| .and_then(|ext| match ext.parsed_extension { |
| ParsedExtension::ExtendedKeyUsage(ref eku) => Some((ext.critical, eku)), |
| _ => None, |
| }) |
| } |
| |
| pub fn policy_constraints(&self) -> Option<(bool, &PolicyConstraints)> { |
| self.find_extension(&OID_X509_EXT_POLICY_CONSTRAINTS) |
| .and_then(|ext| match ext.parsed_extension { |
| ParsedExtension::PolicyConstraints(ref pc) => Some((ext.critical, pc)), |
| _ => None, |
| }) |
| } |
| |
| pub fn inhibit_anypolicy(&self) -> Option<(bool, &InhibitAnyPolicy)> { |
| self.find_extension(&OID_X509_EXT_INHIBITANT_ANY_POLICY) |
| .and_then(|ext| match ext.parsed_extension { |
| ParsedExtension::InhibitAnyPolicy(ref iap) => Some((ext.critical, iap)), |
| _ => None, |
| }) |
| } |
| |
| pub fn policy_mappings(&self) -> Option<(bool, &PolicyMappings<'a>)> { |
| self.find_extension(&OID_X509_EXT_POLICY_MAPPINGS) |
| .and_then(|ext| match ext.parsed_extension { |
| ParsedExtension::PolicyMappings(ref pm) => Some((ext.critical, pm)), |
| _ => None, |
| }) |
| } |
| |
| pub fn subject_alternative_name(&self) -> Option<(bool, &SubjectAlternativeName<'a>)> { |
| self.find_extension(&OID_X509_EXT_SUBJECT_ALT_NAME) |
| .and_then(|ext| match ext.parsed_extension { |
| ParsedExtension::SubjectAlternativeName(ref san) => Some((ext.critical, san)), |
| _ => None, |
| }) |
| } |
| |
| pub fn name_constraints(&self) -> Option<(bool, &NameConstraints<'a>)> { |
| self.find_extension(&OID_X509_EXT_NAME_CONSTRAINTS) |
| .and_then(|ext| match ext.parsed_extension { |
| ParsedExtension::NameConstraints(ref nc) => Some((ext.critical, nc)), |
| _ => None, |
| }) |
| } |
| |
| /// Returns true if certificate has `basicConstraints CA:true` |
| pub fn is_ca(&self) -> bool { |
| self.basic_constraints() |
| .map(|(_, bc)| bc.ca) |
| .unwrap_or(false) |
| } |
| |
| /// Get the raw bytes of the certificate serial number |
| pub fn raw_serial(&self) -> &'a [u8] { |
| self.raw_serial |
| } |
| |
| /// Get a formatted string of the certificate serial number, separated by ':' |
| pub fn raw_serial_as_string(&self) -> String { |
| let mut s = self |
| .raw_serial |
| .iter() |
| .fold(String::with_capacity(3 * self.raw_serial.len()), |a, b| { |
| a + &format!("{:02x}:", b) |
| }); |
| s.pop(); |
| s |
| } |
| } |
| |
| impl<'a> AsRef<[u8]> for TbsCertificate<'a> { |
| #[inline] |
| fn as_ref(&self) -> &[u8] { |
| self.raw |
| } |
| } |
| |
| impl<'a> FromDer<'a> for TbsCertificate<'a> { |
| /// Parse a DER-encoded TbsCertificate object |
| /// |
| /// <pre> |
| /// TBSCertificate ::= SEQUENCE { |
| /// version [0] 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 -- } |
| /// </pre> |
| fn from_der(i: &'a [u8]) -> X509Result<TbsCertificate<'a>> { |
| let start_i = i; |
| parse_der_sequence_defined_g(move |i, _| { |
| let (i, version) = X509Version::from_der(i)?; |
| let (i, serial) = parse_serial(i)?; |
| let (i, signature) = AlgorithmIdentifier::from_der(i)?; |
| let (i, issuer) = X509Name::from_der(i)?; |
| let (i, validity) = Validity::from_der(i)?; |
| let (i, subject) = X509Name::from_der(i)?; |
| let (i, subject_pki) = SubjectPublicKeyInfo::from_der(i)?; |
| let (i, issuer_uid) = UniqueIdentifier::from_der_issuer(i)?; |
| let (i, subject_uid) = UniqueIdentifier::from_der_subject(i)?; |
| let (i, extensions) = parse_extensions(i, BerTag(3))?; |
| let len = start_i.offset(i); |
| let tbs = TbsCertificate { |
| version, |
| serial: serial.1, |
| signature, |
| issuer, |
| validity, |
| subject, |
| subject_pki, |
| issuer_uid, |
| subject_uid, |
| extensions, |
| |
| raw: &start_i[..len], |
| raw_serial: serial.0, |
| }; |
| Ok((i, tbs)) |
| })(i) |
| } |
| } |
| |
| /// `TbsCertificate` parser builder |
| #[derive(Clone, Copy, Debug, PartialEq)] |
| pub struct TbsCertificateParser { |
| deep_parse_extensions: bool, |
| } |
| |
| impl TbsCertificateParser { |
| #[inline] |
| pub const fn new() -> Self { |
| TbsCertificateParser { |
| deep_parse_extensions: true, |
| } |
| } |
| |
| #[inline] |
| pub const fn with_deep_parse_extensions(self, deep_parse_extensions: bool) -> Self { |
| TbsCertificateParser { |
| deep_parse_extensions, |
| } |
| } |
| } |
| |
| impl<'a> Parser<&'a [u8], TbsCertificate<'a>, X509Error> for TbsCertificateParser { |
| fn parse(&mut self, input: &'a [u8]) -> IResult<&'a [u8], TbsCertificate<'a>, X509Error> { |
| let start_i = input; |
| parse_der_sequence_defined_g(move |i, _| { |
| let (i, version) = X509Version::from_der(i)?; |
| let (i, serial) = parse_serial(i)?; |
| let (i, signature) = AlgorithmIdentifier::from_der(i)?; |
| let (i, issuer) = X509Name::from_der(i)?; |
| let (i, validity) = Validity::from_der(i)?; |
| let (i, subject) = X509Name::from_der(i)?; |
| let (i, subject_pki) = SubjectPublicKeyInfo::from_der(i)?; |
| let (i, issuer_uid) = UniqueIdentifier::from_der_issuer(i)?; |
| let (i, subject_uid) = UniqueIdentifier::from_der_subject(i)?; |
| let (i, extensions) = if self.deep_parse_extensions { |
| parse_extensions(i, BerTag(3))? |
| } else { |
| parse_extensions_envelope(i, BerTag(3))? |
| }; |
| let len = start_i.offset(i); |
| let tbs = TbsCertificate { |
| version, |
| serial: serial.1, |
| signature, |
| issuer, |
| validity, |
| subject, |
| subject_pki, |
| issuer_uid, |
| subject_uid, |
| extensions, |
| |
| raw: &start_i[..len], |
| raw_serial: serial.0, |
| }; |
| Ok((i, tbs)) |
| })(input) |
| } |
| } |
| |
| #[cfg(feature = "validate")] |
| #[cfg_attr(docsrs, doc(cfg(feature = "validate")))] |
| impl Validate for TbsCertificate<'_> { |
| fn validate<W, E>(&self, mut warn: W, mut err: E) -> bool |
| where |
| W: FnMut(&str), |
| E: FnMut(&str), |
| { |
| let mut res = true; |
| // version must be 0, 1 or 2 |
| if self.version.0 >= 3 { |
| err("Invalid version"); |
| res = false; |
| } |
| // extensions require v3 |
| if !self.extensions().is_empty() && self.version != X509Version::V3 { |
| err("Extensions present but version is not 3"); |
| res = false; |
| } |
| let b = self.raw_serial(); |
| if b.is_empty() { |
| err("Serial is empty"); |
| res = false; |
| } else { |
| // check MSB of serial |
| if b[0] & 0x80 != 0 { |
| warn("Serial number is negative"); |
| } |
| // check leading zeroes in serial |
| if b.len() > 1 && b[0] == 0 && b[1] & 0x80 == 0 { |
| warn("Leading zeroes in serial number"); |
| } |
| } |
| // subject/issuer: verify charsets |
| // - wildcards in PrintableString |
| // - non-IA5 in IA5String |
| for attr in self.subject.iter_attributes() { |
| match attr.attr_value().content { |
| DerObjectContent::PrintableString(s) | DerObjectContent::IA5String(s) => { |
| if !s.as_bytes().iter().all(u8::is_ascii) { |
| warn(&format!( |
| "Invalid charset in 'Subject', component {}", |
| attr.attr_type() |
| )); |
| } |
| } |
| _ => (), |
| } |
| } |
| // check for parse errors or unsupported extensions |
| for ext in self.extensions() { |
| if let ParsedExtension::UnsupportedExtension { .. } = &ext.parsed_extension { |
| warn(&format!("Unsupported extension {}", ext.oid)); |
| } |
| if let ParsedExtension::ParseError { error } = &ext.parsed_extension { |
| err(&format!("Parse error in extension {}: {}", ext.oid, error)); |
| res = false; |
| } |
| } |
| // check for duplicate extensions |
| let mut m = HashSet::new(); |
| for ext in self.extensions() { |
| if m.contains(&ext.oid) { |
| err(&format!("Duplicate extension {}", ext.oid)); |
| res = false; |
| } else { |
| m.insert(ext.oid.clone()); |
| } |
| // specific extension checks |
| // SAN |
| if let ParsedExtension::SubjectAlternativeName(san) = ext.parsed_extension() { |
| for name in &san.general_names { |
| match name { |
| GeneralName::DNSName(ref s) | GeneralName::RFC822Name(ref s) => { |
| // should be an ia5string |
| if !s.as_bytes().iter().all(u8::is_ascii) { |
| warn(&format!("Invalid charset in 'SAN' entry '{}'", s)); |
| } |
| } |
| _ => (), |
| } |
| } |
| } |
| } |
| res |
| } |
| } |
| |
| #[derive(Clone, Debug, PartialEq)] |
| pub struct Validity { |
| pub not_before: ASN1Time, |
| pub not_after: ASN1Time, |
| } |
| |
| impl Validity { |
| /// The time left before the certificate expires. |
| /// |
| /// If the certificate is not currently valid, then `None` is |
| /// returned. Otherwise, the `Duration` until the certificate |
| /// expires is returned. |
| pub fn time_to_expiration(&self) -> Option<std::time::Duration> { |
| let now = ASN1Time::now(); |
| if !self.is_valid_at(now) { |
| return None; |
| } |
| // Note that the duration below is guaranteed to be positive, |
| // since we just checked that now < na |
| self.not_after - now |
| } |
| |
| /// Check the certificate time validity for the provided date/time |
| #[inline] |
| pub fn is_valid_at(&self, time: ASN1Time) -> bool { |
| time >= self.not_before && time <= self.not_after |
| } |
| |
| /// Check the certificate time validity |
| #[inline] |
| pub fn is_valid(&self) -> bool { |
| self.is_valid_at(ASN1Time::now()) |
| } |
| } |
| |
| impl<'a> FromDer<'a> for Validity { |
| fn from_der(i: &[u8]) -> X509Result<Self> { |
| parse_der_sequence_defined_g(|i, _| { |
| let (i, not_before) = ASN1Time::from_der(i)?; |
| let (i, not_after) = ASN1Time::from_der(i)?; |
| let v = Validity { |
| not_before, |
| not_after, |
| }; |
| Ok((i, v)) |
| })(i) |
| } |
| } |
| |
| #[derive(Clone, Debug, PartialEq)] |
| pub struct UniqueIdentifier<'a>(pub BitStringObject<'a>); |
| |
| impl<'a> UniqueIdentifier<'a> { |
| // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL |
| fn from_der_issuer(i: &'a [u8]) -> X509Result<Option<Self>> { |
| Self::parse(i, 1).map_err(|_| X509Error::InvalidIssuerUID.into()) |
| } |
| |
| // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL |
| fn from_der_subject(i: &[u8]) -> X509Result<Option<UniqueIdentifier>> { |
| Self::parse(i, 2).map_err(|_| X509Error::InvalidSubjectUID.into()) |
| } |
| |
| // Parse a [tag] UniqueIdentifier OPTIONAL |
| // |
| // UniqueIdentifier ::= BIT STRING |
| fn parse(i: &[u8], tag: u32) -> BerResult<Option<UniqueIdentifier>> { |
| let (rem, obj) = parse_ber_optional(parse_der_tagged_implicit( |
| tag, |
| parse_der_content(DerTag::BitString), |
| ))(i)?; |
| let unique_id = match obj.content { |
| DerObjectContent::Optional(None) => Ok(None), |
| DerObjectContent::Optional(Some(o)) => match o.content { |
| DerObjectContent::BitString(_, b) => Ok(Some(UniqueIdentifier(b.to_owned()))), |
| _ => Err(BerError::BerTypeError), |
| }, |
| _ => Err(BerError::BerTypeError), |
| }?; |
| Ok((rem, unique_id)) |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| #[test] |
| fn check_validity_expiration() { |
| let mut v = Validity { |
| not_before: ASN1Time::now(), |
| not_after: ASN1Time::now(), |
| }; |
| assert_eq!(v.time_to_expiration(), None); |
| v.not_after = (v.not_after + std::time::Duration::new(60, 0)).unwrap(); |
| assert!(v.time_to_expiration().is_some()); |
| assert!(v.time_to_expiration().unwrap() <= std::time::Duration::from_secs(60)); |
| // The following assumes this timing won't take 10 seconds... I |
| // think that is safe. |
| assert!(v.time_to_expiration().unwrap() > std::time::Duration::from_secs(50)); |
| } |
| } |