diff options
Diffstat (limited to 'rust/vendor/x509-parser/src')
28 files changed, 6298 insertions, 0 deletions
diff --git a/rust/vendor/x509-parser/src/certificate.rs b/rust/vendor/x509-parser/src/certificate.rs new file mode 100644 index 0000000..3bd6b8e --- /dev/null +++ b/rust/vendor/x509-parser/src/certificate.rs @@ -0,0 +1,782 @@ +//! X.509 Certificate object definitions and operations + +use crate::error::{X509Error, X509Result}; +use crate::extensions::*; +use crate::time::ASN1Time; +use crate::utils::format_serial; +#[cfg(feature = "validate")] +use crate::validate::*; +use crate::x509::{ + parse_serial, parse_signature_value, AlgorithmIdentifier, SubjectPublicKeyInfo, X509Name, + X509Version, +}; + +#[cfg(feature = "verify")] +use crate::verify::verify_signature; +use asn1_rs::{BitString, FromDer, OptTaggedExplicit}; +use core::ops::Deref; +use der_parser::ber::Tag; +use der_parser::der::*; +use der_parser::error::*; +use der_parser::num_bigint::BigUint; +use der_parser::*; +use nom::{Offset, Parser}; +use oid_registry::Oid; +use oid_registry::*; +use std::collections::HashMap; +use time::Duration; + +/// 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.). These are provided using `Deref<Target = TbsCertificate>`, so documentation for +/// these methods can be found in the [`TbsCertificate`] object. +/// +/// 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::prelude::FromDer; +/// # use x509_parser::certificate::X509Certificate; +/// # +/// # 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: BitString<'a>, +} + +impl<'a> X509Certificate<'a> { + /// 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. + /// + /// Not all algorithms are supported, this function is limited to what `ring` supports. + #[cfg(feature = "verify")] + #[cfg_attr(docsrs, doc(cfg(feature = "verify")))] + pub fn verify_signature( + &self, + public_key: Option<&SubjectPublicKeyInfo>, + ) -> Result<(), X509Error> { + let spki = public_key.unwrap_or_else(|| self.public_key()); + verify_signature( + spki, + &self.signature_algorithm, + &self.signature_value, + self.tbs_certificate.raw, + ) + } +} + +impl<'a> Deref for X509Certificate<'a> { + type Target = TbsCertificate<'a>; + + fn deref(&self) -> &Self::Target { + &self.tbs_certificate + } +} + +impl<'a> FromDer<'a, X509Error> 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)] +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) + } +} + +#[allow(deprecated)] +#[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), + { + X509StructureValidator.validate(self, &mut CallbackLogger::new(warn, err)) + } +} + +/// 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> { + /// Get the version of the encoded certificate + pub fn version(&self) -> X509Version { + self.version + } + + /// Get the certificate subject. + #[inline] + pub fn subject(&self) -> &X509Name { + &self.subject + } + + /// Get the certificate issuer. + #[inline] + pub fn issuer(&self) -> &X509Name { + &self.issuer + } + + /// Get the certificate validity. + #[inline] + pub fn validity(&self) -> &Validity { + &self.validity + } + + /// Get the certificate public key information. + #[inline] + pub fn public_key(&self) -> &SubjectPublicKeyInfo { + &self.subject_pki + } + + /// 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`. + /// + /// Return `Ok(Some(extension))` if exactly one was found, `Ok(None)` if none was found, + /// or an error `DuplicateExtensions` if the extension is present twice or more. + #[inline] + pub fn get_extension_unique(&self, oid: &Oid) -> Result<Option<&X509Extension<'a>>, X509Error> { + get_extension_unique(&self.extensions, oid) + } + + /// Searches for an extension with the given `Oid`. + /// + /// ## Duplicate extensions + /// + /// Note: if there are several extensions with the same `Oid`, the first one is returned, masking other values. + /// + /// RFC5280 forbids having duplicate extensions, but does not specify how errors should be handled. + /// + /// **Because of this, the `find_extension` method is not safe and should not be used!** + /// The [`get_extension_unique`](Self::get_extension_unique) method checks for duplicate extensions and should be + /// preferred. + #[deprecated( + since = "0.13.0", + note = "Do not use this function (duplicate extensions are not checked), use `get_extension_unique`" + )] + 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) + }) + } + + /// Attempt to get the certificate Basic Constraints extension + /// + /// Return `Ok(Some(extension))` if exactly one was found, `Ok(None)` if none was found, + /// or an error if the extension is present twice or more. + pub fn basic_constraints( + &self, + ) -> Result<Option<BasicExtension<&BasicConstraints>>, X509Error> { + let r = self + .get_extension_unique(&OID_X509_EXT_BASIC_CONSTRAINTS)? + .and_then(|ext| match ext.parsed_extension { + ParsedExtension::BasicConstraints(ref bc) => { + Some(BasicExtension::new(ext.critical, bc)) + } + _ => None, + }); + Ok(r) + } + + /// Attempt to get the certificate Key Usage extension + /// + /// Return `Ok(Some(extension))` if exactly one was found, `Ok(None)` if none was found, + /// or an error if the extension is invalid, or is present twice or more. + pub fn key_usage(&self) -> Result<Option<BasicExtension<&KeyUsage>>, X509Error> { + self.get_extension_unique(&OID_X509_EXT_KEY_USAGE)? + .map_or(Ok(None), |ext| match ext.parsed_extension { + ParsedExtension::KeyUsage(ref value) => { + Ok(Some(BasicExtension::new(ext.critical, value))) + } + _ => Err(X509Error::InvalidExtensions), + }) + } + + /// Attempt to get the certificate Extended Key Usage extension + /// + /// Return `Ok(Some(extension))` if exactly one was found, `Ok(None)` if none was found, + /// or an error if the extension is invalid, or is present twice or more. + pub fn extended_key_usage( + &self, + ) -> Result<Option<BasicExtension<&ExtendedKeyUsage>>, X509Error> { + self.get_extension_unique(&OID_X509_EXT_EXTENDED_KEY_USAGE)? + .map_or(Ok(None), |ext| match ext.parsed_extension { + ParsedExtension::ExtendedKeyUsage(ref value) => { + Ok(Some(BasicExtension::new(ext.critical, value))) + } + _ => Err(X509Error::InvalidExtensions), + }) + } + + /// Attempt to get the certificate Policy Constraints extension + /// + /// Return `Ok(Some(extension))` if exactly one was found, `Ok(None)` if none was found, + /// or an error if the extension is invalid, or is present twice or more. + pub fn policy_constraints( + &self, + ) -> Result<Option<BasicExtension<&PolicyConstraints>>, X509Error> { + self.get_extension_unique(&OID_X509_EXT_POLICY_CONSTRAINTS)? + .map_or(Ok(None), |ext| match ext.parsed_extension { + ParsedExtension::PolicyConstraints(ref value) => { + Ok(Some(BasicExtension::new(ext.critical, value))) + } + _ => Err(X509Error::InvalidExtensions), + }) + } + + /// Attempt to get the certificate Policy Constraints extension + /// + /// Return `Ok(Some(extension))` if exactly one was found, `Ok(None)` if none was found, + /// or an error if the extension is invalid, or is present twice or more. + pub fn inhibit_anypolicy( + &self, + ) -> Result<Option<BasicExtension<&InhibitAnyPolicy>>, X509Error> { + self.get_extension_unique(&OID_X509_EXT_INHIBITANT_ANY_POLICY)? + .map_or(Ok(None), |ext| match ext.parsed_extension { + ParsedExtension::InhibitAnyPolicy(ref value) => { + Ok(Some(BasicExtension::new(ext.critical, value))) + } + _ => Err(X509Error::InvalidExtensions), + }) + } + + /// Attempt to get the certificate Policy Mappings extension + /// + /// Return `Ok(Some(extension))` if exactly one was found, `Ok(None)` if none was found, + /// or an error if the extension is invalid, or is present twice or more. + pub fn policy_mappings(&self) -> Result<Option<BasicExtension<&PolicyMappings>>, X509Error> { + self.get_extension_unique(&OID_X509_EXT_POLICY_MAPPINGS)? + .map_or(Ok(None), |ext| match ext.parsed_extension { + ParsedExtension::PolicyMappings(ref value) => { + Ok(Some(BasicExtension::new(ext.critical, value))) + } + _ => Err(X509Error::InvalidExtensions), + }) + } + + /// Attempt to get the certificate Subject Alternative Name extension + /// + /// Return `Ok(Some(extension))` if exactly one was found, `Ok(None)` if none was found, + /// or an error if the extension is invalid, or is present twice or more. + pub fn subject_alternative_name( + &self, + ) -> Result<Option<BasicExtension<&SubjectAlternativeName>>, X509Error> { + self.get_extension_unique(&OID_X509_EXT_SUBJECT_ALT_NAME)? + .map_or(Ok(None), |ext| match ext.parsed_extension { + ParsedExtension::SubjectAlternativeName(ref value) => { + Ok(Some(BasicExtension::new(ext.critical, value))) + } + _ => Err(X509Error::InvalidExtensions), + }) + } + + /// Attempt to get the certificate Name Constraints extension + /// + /// Return `Ok(Some(extension))` if exactly one was found, `Ok(None)` if none was found, + /// or an error if the extension is invalid, or is present twice or more. + pub fn name_constraints(&self) -> Result<Option<BasicExtension<&NameConstraints>>, X509Error> { + self.get_extension_unique(&OID_X509_EXT_NAME_CONSTRAINTS)? + .map_or(Ok(None), |ext| match ext.parsed_extension { + ParsedExtension::NameConstraints(ref value) => { + Ok(Some(BasicExtension::new(ext.critical, value))) + } + _ => Err(X509Error::InvalidExtensions), + }) + } + + /// Returns true if certificate has `basicConstraints CA:true` + pub fn is_ca(&self) -> bool { + self.basic_constraints() + .unwrap_or(None) + .map(|ext| ext.value.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 { + format_serial(self.raw_serial) + } +} + +/// Searches for an extension with the given `Oid`. +/// +/// Note: if there are several extensions with the same `Oid`, an error `DuplicateExtensions` is returned. +fn get_extension_unique<'a, 'b>( + extensions: &'a [X509Extension<'b>], + oid: &Oid, +) -> Result<Option<&'a X509Extension<'b>>, X509Error> { + let mut res = None; + for ext in extensions { + if ext.oid == *oid { + if res.is_some() { + return Err(X509Error::DuplicateExtensions); + } + res = Some(ext); + } + } + Ok(res) +} + +impl<'a> AsRef<[u8]> for TbsCertificate<'a> { + #[inline] + fn as_ref(&self) -> &[u8] { + self.raw + } +} + +impl<'a> FromDer<'a, X509Error> 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_tagged_0(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, Tag(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)] +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_tagged_0(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, Tag(3))? + } else { + parse_extensions_envelope(i, Tag(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) + } +} + +#[allow(deprecated)] +#[cfg(feature = "validate")] +#[cfg_attr(docsrs, doc(cfg(feature = "validate")))] +impl Validate for TbsCertificate<'_> { + fn validate<W, E>(&self, warn: W, err: E) -> bool + where + W: FnMut(&str), + E: FnMut(&str), + { + TbsCertificateStructureValidator.validate(self, &mut CallbackLogger::new(warn, err)) + } +} + +/// Basic extension structure, used in search results +#[derive(Debug, PartialEq, Eq)] +pub struct BasicExtension<T> { + pub critical: bool, + pub value: T, +} + +impl<T> BasicExtension<T> { + pub const fn new(critical: bool, value: T) -> Self { + Self { critical, value } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +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<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, X509Error> 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, Eq)] +pub struct UniqueIdentifier<'a>(pub BitString<'a>); + +impl<'a> UniqueIdentifier<'a> { + // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL + fn from_der_issuer(i: &'a [u8]) -> X509Result<Option<Self>> { + Self::parse::<1>(i).map_err(|_| X509Error::InvalidIssuerUID.into()) + } + + // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL + fn from_der_subject(i: &[u8]) -> X509Result<Option<UniqueIdentifier>> { + Self::parse::<2>(i).map_err(|_| X509Error::InvalidSubjectUID.into()) + } + + // Parse a [tag] UniqueIdentifier OPTIONAL + // + // UniqueIdentifier ::= BIT STRING + fn parse<const TAG: u32>(i: &[u8]) -> BerResult<Option<UniqueIdentifier>> { + let (rem, unique_id) = OptTaggedExplicit::<BitString, Error, TAG>::from_der(i)?; + let unique_id = unique_id.map(|u| UniqueIdentifier(u.into_inner())); + 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 + Duration::new(60, 0)).unwrap(); + assert!(v.time_to_expiration().is_some()); + assert!(v.time_to_expiration().unwrap() <= Duration::new(60, 0)); + // The following assumes this timing won't take 10 seconds... I + // think that is safe. + assert!(v.time_to_expiration().unwrap() > Duration::new(50, 0)); + } + + #[test] + fn extension_duplication() { + let extensions = vec![ + X509Extension::new(oid! {1.2}, true, &[], ParsedExtension::Unparsed), + X509Extension::new(oid! {1.3}, true, &[], ParsedExtension::Unparsed), + X509Extension::new(oid! {1.2}, true, &[], ParsedExtension::Unparsed), + X509Extension::new(oid! {1.4}, true, &[], ParsedExtension::Unparsed), + X509Extension::new(oid! {1.4}, true, &[], ParsedExtension::Unparsed), + ]; + + let r2 = get_extension_unique(&extensions, &oid! {1.2}); + assert!(r2.is_err()); + let r3 = get_extension_unique(&extensions, &oid! {1.3}); + assert!(r3.is_ok()); + let r4 = get_extension_unique(&extensions, &oid! {1.4}); + assert!(r4.is_err()); + } +} diff --git a/rust/vendor/x509-parser/src/certification_request.rs b/rust/vendor/x509-parser/src/certification_request.rs new file mode 100644 index 0000000..e56d05a --- /dev/null +++ b/rust/vendor/x509-parser/src/certification_request.rs @@ -0,0 +1,166 @@ +use crate::cri_attributes::*; +use crate::error::{X509Error, X509Result}; +use crate::extensions::*; +use crate::x509::{ + parse_signature_value, AlgorithmIdentifier, SubjectPublicKeyInfo, X509Name, X509Version, +}; + +#[cfg(feature = "verify")] +use crate::verify::verify_signature; +use asn1_rs::{BitString, FromDer}; +use der_parser::der::*; +use der_parser::oid::Oid; +use der_parser::*; +use nom::Offset; +use std::collections::HashMap; + +/// Certification Signing Request (CSR) +#[derive(Debug, PartialEq)] +pub struct X509CertificationRequest<'a> { + pub certification_request_info: X509CertificationRequestInfo<'a>, + pub signature_algorithm: AlgorithmIdentifier<'a>, + pub signature_value: BitString<'a>, +} + +impl<'a> X509CertificationRequest<'a> { + pub fn requested_extensions(&self) -> Option<impl Iterator<Item = &ParsedExtension>> { + self.certification_request_info + .iter_attributes() + .find_map(|attr| { + if let ParsedCriAttribute::ExtensionRequest(requested) = &attr.parsed_attribute { + Some(requested.extensions.iter().map(|ext| &ext.parsed_extension)) + } else { + None + } + }) + } + + /// Verify the cryptographic signature of this certification request + /// + /// Uses the public key contained in the CSR, which must be the one of the entity + /// requesting the certification for this verification to succeed. + #[cfg(feature = "verify")] + pub fn verify_signature(&self) -> Result<(), X509Error> { + let spki = &self.certification_request_info.subject_pki; + verify_signature( + spki, + &self.signature_algorithm, + &self.signature_value, + self.certification_request_info.raw, + ) + } +} + +/// <pre> +/// CertificationRequest ::= SEQUENCE { +/// certificationRequestInfo CertificationRequestInfo, +/// signatureAlgorithm AlgorithmIdentifier{{ SignatureAlgorithms }}, +/// signature BIT STRING +/// } +/// </pre> +impl<'a> FromDer<'a, X509Error> for X509CertificationRequest<'a> { + fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { + parse_der_sequence_defined_g(|i, _| { + let (i, certification_request_info) = X509CertificationRequestInfo::from_der(i)?; + let (i, signature_algorithm) = AlgorithmIdentifier::from_der(i)?; + let (i, signature_value) = parse_signature_value(i)?; + let cert = X509CertificationRequest { + certification_request_info, + signature_algorithm, + signature_value, + }; + Ok((i, cert)) + })(i) + } +} + +/// Certification Request Info structure +/// +/// Certification request information is defined by the following ASN.1 structure: +/// +/// <pre> +/// CertificationRequestInfo ::= SEQUENCE { +/// version INTEGER { v1(0) } (v1,...), +/// subject Name, +/// subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }}, +/// attributes [0] Attributes{{ CRIAttributes }} +/// } +/// </pre> +/// +/// version is the version number; subject is the distinguished name of the certificate +/// subject; subject_pki contains information about the public key being certified, and +/// attributes is a collection of attributes providing additional information about the +/// subject of the certificate. +#[derive(Debug, PartialEq)] +pub struct X509CertificationRequestInfo<'a> { + pub version: X509Version, + pub subject: X509Name<'a>, + pub subject_pki: SubjectPublicKeyInfo<'a>, + attributes: Vec<X509CriAttribute<'a>>, + pub raw: &'a [u8], +} + +impl<'a> X509CertificationRequestInfo<'a> { + /// Get the CRL entry extensions. + #[inline] + pub fn attributes(&self) -> &[X509CriAttribute] { + &self.attributes + } + + /// Returns an iterator over the CRL entry extensions + #[inline] + pub fn iter_attributes(&self) -> impl Iterator<Item = &X509CriAttribute> { + self.attributes.iter() + } + + /// Searches for a CRL entry extension with the given `Oid`. + /// + /// Note: if there are several extensions with the same `Oid`, the first one is returned. + pub fn find_attribute(&self, oid: &Oid) -> Option<&X509CriAttribute> { + self.attributes.iter().find(|&ext| ext.oid == *oid) + } + + /// Builds and returns a map of CRL entry extensions. + /// + /// If an extension is present twice, this will fail and return `DuplicateExtensions`. + pub fn attributes_map(&self) -> Result<HashMap<Oid, &X509CriAttribute>, X509Error> { + self.attributes + .iter() + .try_fold(HashMap::new(), |mut m, ext| { + if m.contains_key(&ext.oid) { + return Err(X509Error::DuplicateAttributes); + } + m.insert(ext.oid.clone(), ext); + Ok(m) + }) + } +} + +/// <pre> +/// CertificationRequestInfo ::= SEQUENCE { +/// version INTEGER { v1(0) } (v1,...), +/// subject Name, +/// subjectPKInfo SubjectPublicKeyInfo{{ PKInfoAlgorithms }}, +/// attributes [0] Attributes{{ CRIAttributes }} +/// } +/// </pre> +impl<'a> FromDer<'a, X509Error> for X509CertificationRequestInfo<'a> { + fn from_der(i: &'a [u8]) -> X509Result<Self> { + let start_i = i; + parse_der_sequence_defined_g(move |i, _| { + let (i, version) = X509Version::from_der(i)?; + let (i, subject) = X509Name::from_der(i)?; + let (i, subject_pki) = SubjectPublicKeyInfo::from_der(i)?; + let (i, attributes) = parse_cri_attributes(i)?; + let len = start_i.offset(i); + let tbs = X509CertificationRequestInfo { + version, + subject, + subject_pki, + attributes, + raw: &start_i[..len], + }; + Ok((i, tbs)) + })(i) + } +} diff --git a/rust/vendor/x509-parser/src/cri_attributes.rs b/rust/vendor/x509-parser/src/cri_attributes.rs new file mode 100644 index 0000000..1092200 --- /dev/null +++ b/rust/vendor/x509-parser/src/cri_attributes.rs @@ -0,0 +1,179 @@ +use crate::{ + error::{X509Error, X509Result}, + extensions::X509Extension, +}; + +use asn1_rs::{Error, FromDer, Header, Oid, Sequence, Tag}; +use nom::combinator::{all_consuming, complete}; +use nom::multi::many0; +use nom::Err; +use oid_registry::*; +use std::collections::HashMap; + +/// Attributes for Certification Request +#[derive(Clone, Debug, PartialEq)] +pub struct X509CriAttribute<'a> { + pub oid: Oid<'a>, + pub value: &'a [u8], + pub(crate) parsed_attribute: ParsedCriAttribute<'a>, +} + +impl<'a> FromDer<'a, X509Error> for X509CriAttribute<'a> { + fn from_der(i: &'a [u8]) -> X509Result<X509CriAttribute> { + Sequence::from_ber_and_then(i, |i| { + let (i, oid) = Oid::from_der(i)?; + let value_start = i; + let (i, hdr) = Header::from_der(i)?; + if hdr.tag() != Tag::Set { + return Err(Err::Error(Error::BerTypeError)); + }; + + let (i, parsed_attribute) = crate::cri_attributes::parser::parse_attribute(i, &oid) + .map_err(|_| Err::Error(Error::BerValueError))?; + let attribute = X509CriAttribute { + oid, + value: &value_start[..value_start.len() - i.len()], + parsed_attribute, + }; + Ok((i, attribute)) + }) + .map_err(|_| X509Error::InvalidAttributes.into()) + } +} + +impl<'a> X509CriAttribute<'a> { + /// Return the attribute type or `UnsupportedAttribute` if the attribute is unknown. + #[inline] + pub fn parsed_attribute(&self) -> &ParsedCriAttribute<'a> { + &self.parsed_attribute + } +} + +/// Section 3.1 of rfc 5272 +#[derive(Clone, Debug, PartialEq)] +pub struct ExtensionRequest<'a> { + pub extensions: Vec<X509Extension<'a>>, +} + +impl<'a> FromDer<'a, X509Error> for ExtensionRequest<'a> { + fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { + parser::parse_extension_request(i).map_err(Err::convert) + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ChallengePassword(pub String); + +/// Attributes for Certification Request +#[derive(Clone, Debug, PartialEq)] +pub enum ParsedCriAttribute<'a> { + ChallengePassword(ChallengePassword), + ExtensionRequest(ExtensionRequest<'a>), + UnsupportedAttribute, +} + +pub(crate) mod parser { + use crate::cri_attributes::*; + use der_parser::der::{ + parse_der_bmpstring, parse_der_printablestring, parse_der_t61string, + parse_der_universalstring, parse_der_utf8string, + }; + use lazy_static::lazy_static; + use nom::branch::alt; + use nom::combinator::map; + + type AttrParser = fn(&[u8]) -> X509Result<ParsedCriAttribute>; + + lazy_static! { + static ref ATTRIBUTE_PARSERS: HashMap<Oid<'static>, AttrParser> = { + macro_rules! add { + ($m:ident, $oid:ident, $p:ident) => { + $m.insert($oid, $p as AttrParser); + }; + } + + let mut m = HashMap::new(); + add!(m, OID_PKCS9_EXTENSION_REQUEST, parse_extension_request_attr); + add!( + m, + OID_PKCS9_CHALLENGE_PASSWORD, + parse_challenge_password_attr + ); + m + }; + } + + // look into the parser map if the extension is known, and parse it + // otherwise, leave it as UnsupportedExtension + pub(crate) fn parse_attribute<'a>( + i: &'a [u8], + oid: &Oid, + ) -> X509Result<'a, ParsedCriAttribute<'a>> { + if let Some(parser) = ATTRIBUTE_PARSERS.get(oid) { + parser(i) + } else { + Ok((i, ParsedCriAttribute::UnsupportedAttribute)) + } + } + + pub(super) fn parse_extension_request(i: &[u8]) -> X509Result<ExtensionRequest> { + crate::extensions::parse_extension_sequence(i) + .map(|(i, extensions)| (i, ExtensionRequest { extensions })) + } + + fn parse_extension_request_attr(i: &[u8]) -> X509Result<ParsedCriAttribute> { + map( + parse_extension_request, + ParsedCriAttribute::ExtensionRequest, + )(i) + } + + // RFC 2985, 5.4.1 Challenge password + // challengePassword ATTRIBUTE ::= { + // WITH SYNTAX DirectoryString {pkcs-9-ub-challengePassword} + // EQUALITY MATCHING RULE caseExactMatch + // SINGLE VALUE TRUE + // ID pkcs-9-at-challengePassword + // } + // RFC 5280, 4.1.2.4. Issuer + // DirectoryString ::= CHOICE { + // teletexString TeletexString (SIZE (1..MAX)), + // printableString PrintableString (SIZE (1..MAX)), + // universalString UniversalString (SIZE (1..MAX)), + // utf8String UTF8String (SIZE (1..MAX)), + // bmpString BMPString (SIZE (1..MAX)) + // } + pub(super) fn parse_challenge_password(i: &[u8]) -> X509Result<ChallengePassword> { + let (rem, obj) = match alt(( + parse_der_utf8string, + parse_der_printablestring, + parse_der_universalstring, + parse_der_bmpstring, + parse_der_t61string, // == teletexString + ))(i) + { + Ok((rem, obj)) => (rem, obj), + Err(_) => return Err(Err::Error(X509Error::InvalidAttributes)), + }; + match obj.content.as_str() { + Ok(s) => Ok((rem, ChallengePassword(s.to_string()))), + Err(_) => Err(Err::Error(X509Error::InvalidAttributes)), + } + } + + fn parse_challenge_password_attr(i: &[u8]) -> X509Result<ParsedCriAttribute> { + map( + parse_challenge_password, + ParsedCriAttribute::ChallengePassword, + )(i) + } +} + +pub(crate) fn parse_cri_attributes(i: &[u8]) -> X509Result<Vec<X509CriAttribute>> { + let (i, hdr) = Header::from_der(i).map_err(|_| Err::Error(X509Error::InvalidAttributes))?; + if hdr.is_contextspecific() && hdr.tag().0 == 0 { + all_consuming(many0(complete(X509CriAttribute::from_der)))(i) + } else { + Err(Err::Error(X509Error::InvalidAttributes)) + } +} diff --git a/rust/vendor/x509-parser/src/error.rs b/rust/vendor/x509-parser/src/error.rs new file mode 100644 index 0000000..85def3f --- /dev/null +++ b/rust/vendor/x509-parser/src/error.rs @@ -0,0 +1,123 @@ +//! X.509 errors + +use der_parser::error::BerError; +use nom::error::{ErrorKind, ParseError}; +use nom::IResult; + +/// An error that can occur while converting an OID to a Nid. +#[derive(Debug, PartialEq, Eq)] +pub struct NidError; + +/// Holds the result of parsing functions (X.509) +/// +/// Note that this type is also a `Result`, so usual functions (`map`, `unwrap` etc.) are available. +pub type X509Result<'a, T> = IResult<&'a [u8], T, X509Error>; + +/// An error that can occur while parsing or validating a certificate. +#[derive(Clone, Debug, PartialEq, thiserror::Error)] +pub enum X509Error { + #[error("generic error")] + Generic, + + #[error("invalid version")] + InvalidVersion, + #[error("invalid serial")] + InvalidSerial, + #[error("invalid algorithm identifier")] + InvalidAlgorithmIdentifier, + #[error("invalid X.509 name")] + InvalidX509Name, + #[error("invalid date")] + InvalidDate, + #[error("invalid X.509 Subject Public Key Info")] + InvalidSPKI, + #[error("invalid X.509 Subject Unique ID")] + InvalidSubjectUID, + #[error("invalid X.509 Issuer Unique ID")] + InvalidIssuerUID, + #[error("invalid extensions")] + InvalidExtensions, + #[error("invalid attributes")] + InvalidAttributes, + #[error("duplicate extensions")] + DuplicateExtensions, + #[error("duplicate attributes")] + DuplicateAttributes, + #[error("invalid Signature DER Value")] + InvalidSignatureValue, + #[error("invalid TBS certificate")] + InvalidTbsCertificate, + + // error types from CRL + #[error("invalid User certificate")] + InvalidUserCertificate, + + /// Top-level certificate structure is invalid + #[error("invalid certificate")] + InvalidCertificate, + + #[error("signature verification error")] + SignatureVerificationError, + #[error("signature unsupported algorithm")] + SignatureUnsupportedAlgorithm, + + #[error("invalid number")] + InvalidNumber, + + #[error("BER error: {0}")] + Der(#[from] BerError), + #[error("nom error: {0:?}")] + NomError(ErrorKind), +} + +impl From<nom::Err<BerError>> for X509Error { + fn from(e: nom::Err<BerError>) -> Self { + Self::Der(BerError::from(e)) + } +} + +impl From<nom::Err<X509Error>> for X509Error { + fn from(e: nom::Err<X509Error>) -> Self { + match e { + nom::Err::Error(e) | nom::Err::Failure(e) => e, + nom::Err::Incomplete(i) => Self::Der(BerError::Incomplete(i)), + } + } +} + +impl From<X509Error> for nom::Err<X509Error> { + fn from(e: X509Error) -> nom::Err<X509Error> { + nom::Err::Error(e) + } +} + +impl From<ErrorKind> for X509Error { + fn from(e: ErrorKind) -> X509Error { + X509Error::NomError(e) + } +} + +impl<I> ParseError<I> for X509Error { + fn from_error_kind(_input: I, kind: ErrorKind) -> Self { + X509Error::NomError(kind) + } + fn append(_input: I, kind: ErrorKind, _other: Self) -> Self { + X509Error::NomError(kind) + } +} + +/// An error that can occur while parsing or validating a certificate. +#[derive(Debug, thiserror::Error)] +pub enum PEMError { + #[error("base64 decode error")] + Base64DecodeError, + #[error("incomplete PEM")] + IncompletePEM, + #[error("invalid header")] + InvalidHeader, + #[error("missing header")] + MissingHeader, + + #[error("IO error: {0}")] + IOError(#[from] std::io::Error), +} diff --git a/rust/vendor/x509-parser/src/extensions/generalname.rs b/rust/vendor/x509-parser/src/extensions/generalname.rs new file mode 100644 index 0000000..37e6f18 --- /dev/null +++ b/rust/vendor/x509-parser/src/extensions/generalname.rs @@ -0,0 +1,115 @@ +use crate::error::{X509Error, X509Result}; +use crate::prelude::format_serial; +use crate::x509::X509Name; +use asn1_rs::{Any, CheckDerConstraints, Class, Error, FromDer, Oid, Sequence}; +use core::convert::TryFrom; +use nom::combinator::all_consuming; +use nom::{Err, IResult}; +use std::fmt; + +#[derive(Clone, Debug, PartialEq)] +/// Represents a GeneralName as defined in RFC5280. There +/// is no support X.400 addresses and EDIPartyName. +/// +/// String formats are not validated. +pub enum GeneralName<'a> { + OtherName(Oid<'a>, &'a [u8]), + /// More or less an e-mail, the format is not checked. + RFC822Name(&'a str), + /// A hostname, format is not checked. + DNSName(&'a str), + /// X400Address, + X400Address(Any<'a>), + /// RFC5280 defines several string types, we always try to parse as utf-8 + /// which is more or less a superset of the string types. + DirectoryName(X509Name<'a>), + /// EDIPartyName + EDIPartyName(Any<'a>), + /// An uniform resource identifier. The format is not checked. + URI(&'a str), + /// An ip address, provided as encoded. + IPAddress(&'a [u8]), + RegisteredID(Oid<'a>), +} + +impl<'a> TryFrom<Any<'a>> for GeneralName<'a> { + type Error = Error; + + fn try_from(any: Any<'a>) -> Result<Self, Self::Error> { + any.class().assert_eq(Class::ContextSpecific)?; + fn ia5str(any: Any) -> Result<&str, Err<Error>> { + // Relax constraints from RFC here: we are expecting an IA5String, but many certificates + // are using unicode characters + std::str::from_utf8(any.data).map_err(|_| nom::Err::Failure(Error::BerValueError)) + } + let name = match any.tag().0 { + 0 => { + // otherName SEQUENCE { OID, [0] explicit any defined by oid } + let (rest, oid) = Oid::from_der(any.data)?; + GeneralName::OtherName(oid, rest) + } + 1 => GeneralName::RFC822Name(ia5str(any)?), + 2 => GeneralName::DNSName(ia5str(any)?), + 3 => { + // XXX Not yet implemented + GeneralName::X400Address(any) + } + 4 => { + // directoryName, name + let (_, name) = all_consuming(X509Name::from_der)(any.data) + .or(Err(Error::Unsupported)) // XXX remove me + ?; + GeneralName::DirectoryName(name) + } + 5 => { + // XXX Not yet implemented + GeneralName::EDIPartyName(any) + } + 6 => GeneralName::URI(ia5str(any)?), + 7 => { + // IPAddress, OctetString + GeneralName::IPAddress(any.data) + } + 8 => { + let oid = Oid::new(any.data.into()); + GeneralName::RegisteredID(oid) + } + _ => return Err(Error::unexpected_tag(None, any.tag())), + }; + Ok(name) + } +} + +impl CheckDerConstraints for GeneralName<'_> { + fn check_constraints(any: &Any) -> asn1_rs::Result<()> { + Sequence::check_constraints(any) + } +} + +impl<'a> FromDer<'a, X509Error> for GeneralName<'a> { + fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { + parse_generalname(i).map_err(Err::convert) + } +} + +impl<'a> fmt::Display for GeneralName<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + GeneralName::OtherName(oid, _) => write!(f, "OtherName({}, [...])", oid), + GeneralName::RFC822Name(s) => write!(f, "RFC822Name({})", s), + GeneralName::DNSName(s) => write!(f, "DNSName({})", s), + GeneralName::X400Address(_) => write!(f, "X400Address(<unparsed>)"), + GeneralName::DirectoryName(dn) => write!(f, "DirectoryName({})", dn), + GeneralName::EDIPartyName(_) => write!(f, "EDIPartyName(<unparsed>)"), + GeneralName::URI(s) => write!(f, "URI({})", s), + GeneralName::IPAddress(b) => write!(f, "IPAddress({})", format_serial(b)), + GeneralName::RegisteredID(oid) => write!(f, "RegisteredID({})", oid), + } + } +} + +pub(crate) fn parse_generalname(i: &[u8]) -> IResult<&[u8], GeneralName, Error> { + let (rest, any) = Any::from_der(i)?; + let gn = GeneralName::try_from(any)?; + Ok((rest, gn)) +} diff --git a/rust/vendor/x509-parser/src/extensions/keyusage.rs b/rust/vendor/x509-parser/src/extensions/keyusage.rs new file mode 100644 index 0000000..81a07f1 --- /dev/null +++ b/rust/vendor/x509-parser/src/extensions/keyusage.rs @@ -0,0 +1,150 @@ +use crate::error::{X509Error, X509Result}; +use asn1_rs::FromDer; +use der_parser::der::*; +use der_parser::error::BerError; +use der_parser::{oid, oid::Oid}; +use nom::{Err, IResult}; +use std::fmt; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct KeyUsage { + pub flags: u16, +} + +impl KeyUsage { + pub fn digital_signature(&self) -> bool { + self.flags & 1 == 1 + } + pub fn non_repudiation(&self) -> bool { + (self.flags >> 1) & 1u16 == 1 + } + pub fn key_encipherment(&self) -> bool { + (self.flags >> 2) & 1u16 == 1 + } + pub fn data_encipherment(&self) -> bool { + (self.flags >> 3) & 1u16 == 1 + } + pub fn key_agreement(&self) -> bool { + (self.flags >> 4) & 1u16 == 1 + } + pub fn key_cert_sign(&self) -> bool { + (self.flags >> 5) & 1u16 == 1 + } + pub fn crl_sign(&self) -> bool { + (self.flags >> 6) & 1u16 == 1 + } + pub fn encipher_only(&self) -> bool { + (self.flags >> 7) & 1u16 == 1 + } + pub fn decipher_only(&self) -> bool { + (self.flags >> 8) & 1u16 == 1 + } +} + +// This list must have the same order as KeyUsage flags declaration (4.2.1.3) +const KEY_USAGE_FLAGS: &[&str] = &[ + "Digital Signature", + "Non Repudiation", + "Key Encipherment", + "Data Encipherment", + "Key Agreement", + "Key Cert Sign", + "CRL Sign", + "Encipher Only", + "Decipher Only", +]; + +impl fmt::Display for KeyUsage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut s = KEY_USAGE_FLAGS + .iter() + .enumerate() + .fold(String::new(), |acc, (idx, s)| { + if self.flags >> idx & 1 != 0 { + acc + s + ", " + } else { + acc + } + }); + s.pop(); + s.pop(); + f.write_str(&s) + } +} + +impl<'a> FromDer<'a, X509Error> for KeyUsage { + fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { + parse_keyusage(i).map_err(Err::convert) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ExtendedKeyUsage<'a> { + pub any: bool, + pub server_auth: bool, + pub client_auth: bool, + pub code_signing: bool, + pub email_protection: bool, + pub time_stamping: bool, + pub ocsp_signing: bool, + pub other: Vec<Oid<'a>>, +} + +impl<'a> FromDer<'a, X509Error> for ExtendedKeyUsage<'a> { + fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { + parse_extendedkeyusage(i).map_err(Err::convert) + } +} + +pub(crate) fn parse_keyusage(i: &[u8]) -> IResult<&[u8], KeyUsage, BerError> { + let (rest, obj) = parse_der_bitstring(i)?; + let bitstring = obj + .content + .as_bitstring() + .or(Err(Err::Error(BerError::BerTypeError)))?; + let flags = bitstring + .data + .iter() + .rev() + .fold(0, |acc, x| acc << 8 | (x.reverse_bits() as u16)); + Ok((rest, KeyUsage { flags })) +} + +pub(crate) fn parse_extendedkeyusage(i: &[u8]) -> IResult<&[u8], ExtendedKeyUsage, BerError> { + let (ret, seq) = <Vec<Oid>>::from_der(i)?; + let mut seen = std::collections::HashSet::new(); + let mut eku = ExtendedKeyUsage { + any: false, + server_auth: false, + client_auth: false, + code_signing: false, + email_protection: false, + time_stamping: false, + ocsp_signing: false, + other: Vec::new(), + }; + for oid in &seq { + if !seen.insert(oid.clone()) { + continue; + } + let asn1 = oid.as_bytes(); + if asn1 == oid!(raw 2.5.29.37.0) { + eku.any = true; + } else if asn1 == oid!(raw 1.3.6.1.5.5.7.3.1) { + eku.server_auth = true; + } else if asn1 == oid!(raw 1.3.6.1.5.5.7.3.2) { + eku.client_auth = true; + } else if asn1 == oid!(raw 1.3.6.1.5.5.7.3.3) { + eku.code_signing = true; + } else if asn1 == oid!(raw 1.3.6.1.5.5.7.3.4) { + eku.email_protection = true; + } else if asn1 == oid!(raw 1.3.6.1.5.5.7.3.8) { + eku.time_stamping = true; + } else if asn1 == oid!(raw 1.3.6.1.5.5.7.3.9) { + eku.ocsp_signing = true; + } else { + eku.other.push(oid.clone()); + } + } + Ok((ret, eku)) +} diff --git a/rust/vendor/x509-parser/src/extensions/mod.rs b/rust/vendor/x509-parser/src/extensions/mod.rs new file mode 100644 index 0000000..868dc77 --- /dev/null +++ b/rust/vendor/x509-parser/src/extensions/mod.rs @@ -0,0 +1,1484 @@ +//! X.509 Extensions objects and types + +use crate::error::{X509Error, X509Result}; +use crate::time::ASN1Time; +use crate::utils::format_serial; +use crate::x509::{ReasonCode, RelativeDistinguishedName}; + +use asn1_rs::FromDer; +use der_parser::ber::parse_ber_bool; +use der_parser::der::*; +use der_parser::error::{BerError, BerResult}; +use der_parser::num_bigint::BigUint; +use der_parser::oid::Oid; +use nom::combinator::{all_consuming, complete, cut, map, map_res, opt}; +use nom::multi::{many0, many1}; +use nom::{Err, IResult, Parser}; +use oid_registry::*; +use std::collections::HashMap; +use std::fmt::{self, LowerHex}; + +mod generalname; +mod keyusage; +mod nameconstraints; +mod policymappings; +mod sct; + +pub use generalname::*; +pub use keyusage::*; +pub use nameconstraints::*; +pub use policymappings::*; +pub use sct::*; + +/// X.509 version 3 extension +/// +/// X.509 extensions allow adding attributes to objects like certificates or revocation lists. +/// +/// Each extension in a certificate is designated as either critical or non-critical. A +/// certificate using system MUST reject the certificate if it encounters a critical extension it +/// does not recognize; however, a non-critical extension MAY be ignored if it is not recognized. +/// +/// Each extension includes an OID and an ASN.1 structure. When an extension appears in a +/// certificate, the OID appears as the field extnID and the corresponding ASN.1 encoded structure +/// is the value of the octet string extnValue. A certificate MUST NOT include more than one +/// instance of a particular extension. +/// +/// When parsing an extension, the global extension structure (described above) is parsed, +/// and the object is returned if it succeeds. +/// During this step, it also attempts to parse the content of the extension, if known. +/// The returned object has a +/// [`X509Extension::parsed_extension()`] method. The returned +/// enum is either a known extension, or the special value `ParsedExtension::UnsupportedExtension`. +/// +/// # Example +/// +/// ```rust +/// use x509_parser::prelude::FromDer; +/// use x509_parser::extensions::{X509Extension, ParsedExtension}; +/// +/// static DER: &[u8] = &[ +/// 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04, 0x16, 0x04, 0x14, 0xA3, 0x05, 0x2F, 0x18, +/// 0x60, 0x50, 0xC2, 0x89, 0x0A, 0xDD, 0x2B, 0x21, 0x4F, 0xFF, 0x8E, 0x4E, 0xA8, 0x30, 0x31, +/// 0x36 ]; +/// +/// # fn main() { +/// let res = X509Extension::from_der(DER); +/// match res { +/// Ok((_rem, ext)) => { +/// println!("Extension OID: {}", ext.oid); +/// println!(" Critical: {}", ext.critical); +/// let parsed_ext = ext.parsed_extension(); +/// assert!(!parsed_ext.unsupported()); +/// assert!(parsed_ext.error().is_none()); +/// if let ParsedExtension::SubjectKeyIdentifier(key_id) = parsed_ext { +/// assert!(key_id.0.len() > 0); +/// } else { +/// panic!("Extension has wrong type"); +/// } +/// }, +/// _ => panic!("x509 extension parsing failed: {:?}", res), +/// } +/// # } +/// ``` +#[derive(Clone, Debug, PartialEq)] +pub struct X509Extension<'a> { + /// OID describing the extension content + pub oid: Oid<'a>, + /// Boolean value describing the 'critical' attribute of the extension + /// + /// An extension includes the boolean critical, with a default value of FALSE. + pub critical: bool, + /// Raw content of the extension + pub value: &'a [u8], + pub(crate) parsed_extension: ParsedExtension<'a>, +} + +impl<'a> X509Extension<'a> { + /// Creates a new extension with the provided values. + #[inline] + pub const fn new( + oid: Oid<'a>, + critical: bool, + value: &'a [u8], + parsed_extension: ParsedExtension<'a>, + ) -> X509Extension<'a> { + X509Extension { + oid, + critical, + value, + parsed_extension, + } + } + + /// Return the extension type or `UnsupportedExtension` if the extension is not implemented. + #[inline] + pub fn parsed_extension(&self) -> &ParsedExtension<'a> { + &self.parsed_extension + } +} + +/// <pre> +/// Extension ::= SEQUENCE { +/// extnID OBJECT IDENTIFIER, +/// critical BOOLEAN DEFAULT FALSE, +/// extnValue OCTET STRING } +/// </pre> +impl<'a> FromDer<'a, X509Error> for X509Extension<'a> { + fn from_der(i: &'a [u8]) -> X509Result<Self> { + X509ExtensionParser::new().parse(i) + } +} + +/// `X509Extension` parser builder +#[derive(Clone, Copy, Debug)] +pub struct X509ExtensionParser { + deep_parse_extensions: bool, +} + +impl X509ExtensionParser { + #[inline] + pub const fn new() -> Self { + X509ExtensionParser { + deep_parse_extensions: true, + } + } + + #[inline] + pub const fn with_deep_parse_extensions(self, deep_parse_extensions: bool) -> Self { + X509ExtensionParser { + deep_parse_extensions, + } + } +} + +impl<'a> Parser<&'a [u8], X509Extension<'a>, X509Error> for X509ExtensionParser { + fn parse(&mut self, input: &'a [u8]) -> IResult<&'a [u8], X509Extension<'a>, X509Error> { + parse_der_sequence_defined_g(|i, _| { + let (i, oid) = Oid::from_der(i)?; + let (i, critical) = der_read_critical(i)?; + let (i, value) = <&[u8]>::from_der(i)?; + let (i, parsed_extension) = if self.deep_parse_extensions { + parser::parse_extension(i, value, &oid)? + } else { + (&[] as &[_], ParsedExtension::Unparsed) + }; + let ext = X509Extension { + oid, + critical, + value, + parsed_extension, + }; + Ok((i, ext)) + })(input) + .map_err(|_| X509Error::InvalidExtensions.into()) + } +} + +#[derive(Clone, Debug, PartialEq)] +pub enum ParsedExtension<'a> { + /// Crate parser does not support this extension (yet) + UnsupportedExtension { + oid: Oid<'a>, + }, + ParseError { + error: Err<BerError>, + }, + /// Section 4.2.1.1 of rfc 5280 + AuthorityKeyIdentifier(AuthorityKeyIdentifier<'a>), + /// Section 4.2.1.2 of rfc 5280 + SubjectKeyIdentifier(KeyIdentifier<'a>), + /// Section 4.2.1.3 of rfc 5280 + KeyUsage(KeyUsage), + /// Section 4.2.1.4 of rfc 5280 + CertificatePolicies(CertificatePolicies<'a>), + /// Section 4.2.1.5 of rfc 5280 + PolicyMappings(PolicyMappings<'a>), + /// Section 4.2.1.6 of rfc 5280 + SubjectAlternativeName(SubjectAlternativeName<'a>), + /// Section 4.2.1.7 of rfc 5280 + IssuerAlternativeName(IssuerAlternativeName<'a>), + /// Section 4.2.1.9 of rfc 5280 + BasicConstraints(BasicConstraints), + /// Section 4.2.1.10 of rfc 5280 + NameConstraints(NameConstraints<'a>), + /// Section 4.2.1.11 of rfc 5280 + PolicyConstraints(PolicyConstraints), + /// Section 4.2.1.12 of rfc 5280 + ExtendedKeyUsage(ExtendedKeyUsage<'a>), + /// Section 4.2.1.13 of rfc 5280 + CRLDistributionPoints(CRLDistributionPoints<'a>), + /// Section 4.2.1.14 of rfc 5280 + InhibitAnyPolicy(InhibitAnyPolicy), + /// Section 4.2.2.1 of rfc 5280 + AuthorityInfoAccess(AuthorityInfoAccess<'a>), + /// Netscape certificate type (subject is SSL client, an SSL server, or a CA) + NSCertType(NSCertType), + /// Netscape certificate comment + NsCertComment(&'a str), + /// Section 5.3.1 of rfc 5280 + CRLNumber(BigUint), + /// Section 5.3.1 of rfc 5280 + ReasonCode(ReasonCode), + /// Section 5.3.3 of rfc 5280 + InvalidityDate(ASN1Time), + /// rfc 6962 + SCT(Vec<SignedCertificateTimestamp<'a>>), + /// Unparsed extension (was not requested in parsing options) + Unparsed, +} + +impl<'a> ParsedExtension<'a> { + /// Return `true` if the extension is unsupported + pub fn unsupported(&self) -> bool { + matches!(self, &ParsedExtension::UnsupportedExtension { .. }) + } + + /// Return a reference on the parsing error if the extension parsing failed + pub fn error(&self) -> Option<&Err<BerError>> { + match self { + ParsedExtension::ParseError { error } => Some(error), + _ => None, + } + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct AuthorityKeyIdentifier<'a> { + pub key_identifier: Option<KeyIdentifier<'a>>, + pub authority_cert_issuer: Option<Vec<GeneralName<'a>>>, + pub authority_cert_serial: Option<&'a [u8]>, +} + +impl<'a> FromDer<'a, X509Error> for AuthorityKeyIdentifier<'a> { + fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { + parser::parse_authoritykeyidentifier(i).map_err(Err::convert) + } +} + +pub type CertificatePolicies<'a> = Vec<PolicyInformation<'a>>; + +// impl<'a> FromDer<'a> for CertificatePolicies<'a> { +// fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { +// parser::parse_certificatepolicies(i).map_err(Err::convert) +// } +// } + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PolicyInformation<'a> { + pub policy_id: Oid<'a>, + pub policy_qualifiers: Option<Vec<PolicyQualifierInfo<'a>>>, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PolicyQualifierInfo<'a> { + pub policy_qualifier_id: Oid<'a>, + pub qualifier: &'a [u8], +} + +/// Identifies whether the subject of the certificate is a CA, and the max validation depth. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct BasicConstraints { + pub ca: bool, + pub path_len_constraint: Option<u32>, +} + +impl<'a> FromDer<'a, X509Error> for BasicConstraints { + fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { + parser::parse_basicconstraints(i).map_err(Err::convert) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct KeyIdentifier<'a>(pub &'a [u8]); + +impl<'a> FromDer<'a, X509Error> for KeyIdentifier<'a> { + fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { + parser::parse_keyidentifier(i).map_err(Err::convert) + } +} + +impl<'a> LowerHex for KeyIdentifier<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let s = format_serial(self.0); + f.write_str(&s) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct NSCertType(u8); + +// The value is a bit-string, where the individual bit positions are defined as: +// +// bit-0 SSL client - this cert is certified for SSL client authentication use +// bit-1 SSL server - this cert is certified for SSL server authentication use +// bit-2 S/MIME - this cert is certified for use by clients (New in PR3) +// bit-3 Object Signing - this cert is certified for signing objects such as Java applets and plugins(New in PR3) +// bit-4 Reserved - this bit is reserved for future use +// bit-5 SSL CA - this cert is certified for issuing certs for SSL use +// bit-6 S/MIME CA - this cert is certified for issuing certs for S/MIME use (New in PR3) +// bit-7 Object Signing CA - this cert is certified for issuing certs for Object Signing (New in PR3) +impl NSCertType { + pub fn ssl_client(&self) -> bool { + self.0 & 0x1 == 1 + } + pub fn ssl_server(&self) -> bool { + (self.0 >> 1) & 1 == 1 + } + pub fn smime(&self) -> bool { + (self.0 >> 2) & 1 == 1 + } + pub fn object_signing(&self) -> bool { + (self.0 >> 3) & 1 == 1 + } + pub fn ssl_ca(&self) -> bool { + (self.0 >> 5) & 1 == 1 + } + pub fn smime_ca(&self) -> bool { + (self.0 >> 6) & 1 == 1 + } + pub fn object_signing_ca(&self) -> bool { + (self.0 >> 7) & 1 == 1 + } +} + +const NS_CERT_TYPE_FLAGS: &[&str] = &[ + "SSL CLient", + "SSL Server", + "S/MIME", + "Object Signing", + "Reserved", + "SSL CA", + "S/MIME CA", + "Object Signing CA", +]; + +impl fmt::Display for NSCertType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut s = String::new(); + let mut acc = self.0; + for flag_text in NS_CERT_TYPE_FLAGS { + if acc & 1 != 0 { + s = s + flag_text + ", "; + } + acc >>= 1; + } + s.pop(); + s.pop(); + f.write_str(&s) + } +} + +impl<'a> FromDer<'a, X509Error> for NSCertType { + fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { + parser::parse_nscerttype(i).map_err(Err::convert) + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct AuthorityInfoAccess<'a> { + pub accessdescs: Vec<AccessDescription<'a>>, +} + +impl<'a> AuthorityInfoAccess<'a> { + /// Returns an iterator over the Access Descriptors + pub fn iter(&self) -> impl Iterator<Item = &AccessDescription<'a>> { + self.accessdescs.iter() + } + + /// Returns a `HashMap` mapping `Oid` to the list of references to `GeneralNames` + /// + /// If several names match the same `Oid`, they are merged in the same entry. + pub fn as_hashmap(&self) -> HashMap<Oid<'a>, Vec<&GeneralName<'a>>> { + // create the hashmap and merge entries with same OID + let mut m: HashMap<Oid, Vec<&GeneralName>> = HashMap::new(); + for desc in &self.accessdescs { + let AccessDescription { + access_method: oid, + access_location: gn, + } = desc; + if let Some(general_names) = m.get_mut(oid) { + general_names.push(gn); + } else { + m.insert(oid.clone(), vec![gn]); + } + } + m + } + + /// Returns a `HashMap` mapping `Oid` to the list of `GeneralNames` (consuming the input) + /// + /// If several names match the same `Oid`, they are merged in the same entry. + pub fn into_hashmap(self) -> HashMap<Oid<'a>, Vec<GeneralName<'a>>> { + let mut aia_list = self.accessdescs; + // create the hashmap and merge entries with same OID + let mut m: HashMap<Oid, Vec<GeneralName>> = HashMap::new(); + for desc in aia_list.drain(..) { + let AccessDescription { + access_method: oid, + access_location: gn, + } = desc; + if let Some(general_names) = m.get_mut(&oid) { + general_names.push(gn); + } else { + m.insert(oid, vec![gn]); + } + } + m + } +} + +impl<'a> FromDer<'a, X509Error> for AuthorityInfoAccess<'a> { + fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { + parser::parse_authorityinfoaccess(i).map_err(Err::convert) + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct AccessDescription<'a> { + pub access_method: Oid<'a>, + pub access_location: GeneralName<'a>, +} + +impl<'a> AccessDescription<'a> { + pub const fn new(access_method: Oid<'a>, access_location: GeneralName<'a>) -> Self { + AccessDescription { + access_method, + access_location, + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct InhibitAnyPolicy { + pub skip_certs: u32, +} + +impl<'a> FromDer<'a, X509Error> for InhibitAnyPolicy { + fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { + map(parse_der_u32, |skip_certs| InhibitAnyPolicy { skip_certs })(i).map_err(Err::convert) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PolicyConstraints { + pub require_explicit_policy: Option<u32>, + pub inhibit_policy_mapping: Option<u32>, +} + +impl<'a> FromDer<'a, X509Error> for PolicyConstraints { + fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { + parser::parse_policyconstraints(i).map_err(Err::convert) + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct SubjectAlternativeName<'a> { + pub general_names: Vec<GeneralName<'a>>, +} + +impl<'a> FromDer<'a, X509Error> for SubjectAlternativeName<'a> { + fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { + parse_der_sequence_defined_g(|input, _| { + let (i, general_names) = + all_consuming(many0(complete(cut(GeneralName::from_der))))(input)?; + Ok((i, SubjectAlternativeName { general_names })) + })(i) + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct IssuerAlternativeName<'a> { + pub general_names: Vec<GeneralName<'a>>, +} + +impl<'a> FromDer<'a, X509Error> for IssuerAlternativeName<'a> { + fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { + parse_der_sequence_defined_g(|input, _| { + let (i, general_names) = + all_consuming(many0(complete(cut(GeneralName::from_der))))(input)?; + Ok((i, IssuerAlternativeName { general_names })) + })(i) + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct CRLDistributionPoints<'a> { + pub points: Vec<CRLDistributionPoint<'a>>, +} + +impl<'a> std::ops::Deref for CRLDistributionPoints<'a> { + type Target = Vec<CRLDistributionPoint<'a>>; + + fn deref(&self) -> &Self::Target { + &self.points + } +} + +impl<'a> FromDer<'a, X509Error> for CRLDistributionPoints<'a> { + fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { + parser::parse_crldistributionpoints(i).map_err(Err::convert) + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct CRLDistributionPoint<'a> { + pub distribution_point: Option<DistributionPointName<'a>>, + pub reasons: Option<ReasonFlags>, + pub crl_issuer: Option<Vec<GeneralName<'a>>>, +} + +#[derive(Clone, Debug, PartialEq)] +pub enum DistributionPointName<'a> { + FullName(Vec<GeneralName<'a>>), + NameRelativeToCRLIssuer(RelativeDistinguishedName<'a>), +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct ReasonFlags { + pub flags: u16, +} + +impl ReasonFlags { + pub fn key_compromise(&self) -> bool { + (self.flags >> 1) & 1 == 1 + } + pub fn ca_compromise(&self) -> bool { + (self.flags >> 2) & 1 == 1 + } + pub fn affilation_changed(&self) -> bool { + (self.flags >> 3) & 1 == 1 + } + pub fn superseded(&self) -> bool { + (self.flags >> 4) & 1 == 1 + } + pub fn cessation_of_operation(&self) -> bool { + (self.flags >> 5) & 1 == 1 + } + pub fn certificate_hold(&self) -> bool { + (self.flags >> 6) & 1 == 1 + } + pub fn privelege_withdrawn(&self) -> bool { + (self.flags >> 7) & 1 == 1 + } + pub fn aa_compromise(&self) -> bool { + (self.flags >> 8) & 1 == 1 + } +} + +const REASON_FLAGS: &[&str] = &[ + "Unused", + "Key Compromise", + "CA Compromise", + "Affiliation Changed", + "Superseded", + "Cessation Of Operation", + "Certificate Hold", + "Privilege Withdrawn", + "AA Compromise", +]; + +impl fmt::Display for ReasonFlags { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut s = String::new(); + let mut acc = self.flags; + for flag_text in REASON_FLAGS { + if acc & 1 != 0 { + s = s + flag_text + ", "; + } + acc >>= 1; + } + s.pop(); + s.pop(); + f.write_str(&s) + } +} + +pub(crate) mod parser { + use crate::extensions::*; + use crate::time::ASN1Time; + use asn1_rs::{GeneralizedTime, ParseResult}; + use der_parser::error::BerError; + use der_parser::{oid::Oid, *}; + use lazy_static::lazy_static; + use nom::combinator::{cut, map}; + use nom::{Err, IResult}; + + type ExtParser = fn(&[u8]) -> IResult<&[u8], ParsedExtension, BerError>; + + lazy_static! { + static ref EXTENSION_PARSERS: HashMap<Oid<'static>, ExtParser> = { + macro_rules! add { + ($m:ident, $oid:ident, $p:ident) => { + $m.insert($oid, $p as ExtParser); + }; + } + + let mut m = HashMap::new(); + add!( + m, + OID_X509_EXT_SUBJECT_KEY_IDENTIFIER, + parse_keyidentifier_ext + ); + add!(m, OID_X509_EXT_KEY_USAGE, parse_keyusage_ext); + add!( + m, + OID_X509_EXT_SUBJECT_ALT_NAME, + parse_subjectalternativename_ext + ); + add!( + m, + OID_X509_EXT_ISSUER_ALT_NAME, + parse_issueralternativename_ext + ); + add!( + m, + OID_X509_EXT_BASIC_CONSTRAINTS, + parse_basicconstraints_ext + ); + add!(m, OID_X509_EXT_NAME_CONSTRAINTS, parse_nameconstraints_ext); + add!( + m, + OID_X509_EXT_CERTIFICATE_POLICIES, + parse_certificatepolicies_ext + ); + add!(m, OID_X509_EXT_POLICY_MAPPINGS, parse_policymappings_ext); + add!( + m, + OID_X509_EXT_POLICY_CONSTRAINTS, + parse_policyconstraints_ext + ); + add!( + m, + OID_X509_EXT_EXTENDED_KEY_USAGE, + parse_extendedkeyusage_ext + ); + add!( + m, + OID_X509_EXT_CRL_DISTRIBUTION_POINTS, + parse_crldistributionpoints_ext + ); + add!( + m, + OID_X509_EXT_INHIBITANT_ANY_POLICY, + parse_inhibitanypolicy_ext + ); + add!( + m, + OID_PKIX_AUTHORITY_INFO_ACCESS, + parse_authorityinfoaccess_ext + ); + add!( + m, + OID_X509_EXT_AUTHORITY_KEY_IDENTIFIER, + parse_authoritykeyidentifier_ext + ); + add!(m, OID_CT_LIST_SCT, parse_sct_ext); + add!(m, OID_X509_EXT_CERT_TYPE, parse_nscerttype_ext); + add!(m, OID_X509_EXT_CERT_COMMENT, parse_nscomment_ext); + add!(m, OID_X509_EXT_CRL_NUMBER, parse_crl_number); + add!(m, OID_X509_EXT_REASON_CODE, parse_reason_code); + add!(m, OID_X509_EXT_INVALIDITY_DATE, parse_invalidity_date); + m + }; + } + + // look into the parser map if the extension is known, and parse it + // otherwise, leave it as UnsupportedExtension + fn parse_extension0<'a>( + orig_i: &'a [u8], + i: &'a [u8], + oid: &Oid, + ) -> IResult<&'a [u8], ParsedExtension<'a>, BerError> { + if let Some(parser) = EXTENSION_PARSERS.get(oid) { + match parser(i) { + Ok((_, ext)) => Ok((orig_i, ext)), + Err(error) => Ok((orig_i, ParsedExtension::ParseError { error })), + } + } else { + Ok(( + orig_i, + ParsedExtension::UnsupportedExtension { + oid: oid.to_owned(), + }, + )) + } + } + + pub(crate) fn parse_extension<'a>( + orig_i: &'a [u8], + i: &'a [u8], + oid: &Oid, + ) -> IResult<&'a [u8], ParsedExtension<'a>, BerError> { + parse_extension0(orig_i, i, oid) + } + + /// Parse a "Basic Constraints" extension + /// + /// <pre> + /// id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 } + /// BasicConstraints ::= SEQUENCE { + /// cA BOOLEAN DEFAULT FALSE, + /// pathLenConstraint INTEGER (0..MAX) OPTIONAL } + /// </pre> + /// + /// Note the maximum length of the `pathLenConstraint` field is limited to the size of a 32-bits + /// unsigned integer, and parsing will fail if value if larger. + pub(super) fn parse_basicconstraints(i: &[u8]) -> IResult<&[u8], BasicConstraints, BerError> { + let (rem, obj) = parse_der_sequence(i)?; + if let Ok(seq) = obj.as_sequence() { + let (ca, path_len_constraint) = match seq.len() { + 0 => (false, None), + 1 => { + if let Ok(b) = seq[0].as_bool() { + (b, None) + } else if let Ok(u) = seq[0].as_u32() { + (false, Some(u)) + } else { + return Err(nom::Err::Error(BerError::InvalidTag)); + } + } + 2 => { + let ca = seq[0] + .as_bool() + .or(Err(nom::Err::Error(BerError::InvalidLength)))?; + let pl = seq[1] + .as_u32() + .or(Err(nom::Err::Error(BerError::InvalidLength)))?; + (ca, Some(pl)) + } + _ => return Err(nom::Err::Error(BerError::InvalidLength)), + }; + Ok(( + rem, + BasicConstraints { + ca, + path_len_constraint, + }, + )) + } else { + Err(nom::Err::Error(BerError::InvalidLength)) + } + } + + fn parse_basicconstraints_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + map(parse_basicconstraints, ParsedExtension::BasicConstraints)(i) + } + + fn parse_nameconstraints_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + map(parse_nameconstraints, ParsedExtension::NameConstraints)(i) + } + + pub(super) fn parse_subjectalternativename_ext( + i: &[u8], + ) -> IResult<&[u8], ParsedExtension, BerError> { + parse_der_sequence_defined_g(|input, _| { + let (i, general_names) = all_consuming(many0(complete(cut(parse_generalname))))(input)?; + Ok(( + i, + ParsedExtension::SubjectAlternativeName(SubjectAlternativeName { general_names }), + )) + })(i) + } + + pub(super) fn parse_issueralternativename_ext( + i: &[u8], + ) -> IResult<&[u8], ParsedExtension, BerError> { + parse_der_sequence_defined_g(|input, _| { + let (i, general_names) = all_consuming(many0(complete(cut(parse_generalname))))(input)?; + Ok(( + i, + ParsedExtension::IssuerAlternativeName(IssuerAlternativeName { general_names }), + )) + })(i) + } + + pub(super) fn parse_policyconstraints(i: &[u8]) -> IResult<&[u8], PolicyConstraints, BerError> { + parse_der_sequence_defined_g(|input, _| { + let (i, require_explicit_policy) = opt(complete(map_res( + parse_der_tagged_implicit(0, parse_der_content(Tag::Integer)), + |x| x.as_u32(), + )))(input)?; + let (i, inhibit_policy_mapping) = all_consuming(opt(complete(map_res( + parse_der_tagged_implicit(1, parse_der_content(Tag::Integer)), + |x| x.as_u32(), + ))))(i)?; + let policy_constraint = PolicyConstraints { + require_explicit_policy, + inhibit_policy_mapping, + }; + Ok((i, policy_constraint)) + })(i) + } + + fn parse_policyconstraints_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + map(parse_policyconstraints, ParsedExtension::PolicyConstraints)(i) + } + + fn parse_policymappings_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + map(parse_policymappings, ParsedExtension::PolicyMappings)(i) + } + + fn parse_inhibitanypolicy_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + let (ret, skip_certs) = parse_der_u32(i)?; + Ok(( + ret, + ParsedExtension::InhibitAnyPolicy(InhibitAnyPolicy { skip_certs }), + )) + } + + fn parse_extendedkeyusage_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + map(parse_extendedkeyusage, ParsedExtension::ExtendedKeyUsage)(i) + } + + // DistributionPointName ::= CHOICE { + // fullName [0] GeneralNames, + // nameRelativeToCRLIssuer [1] RelativeDistinguishedName } + fn parse_distributionpointname(i: &[u8]) -> IResult<&[u8], DistributionPointName, BerError> { + let (rem, header) = der_read_element_header(i)?; + match header.tag().0 { + 0 => { + let (rem, names) = many1(complete(parse_generalname))(rem)?; + Ok((rem, DistributionPointName::FullName(names))) + } + 1 => { + let (rem, rdn) = RelativeDistinguishedName::from_der(rem) + .map_err(|_| BerError::BerValueError)?; + Ok((rem, DistributionPointName::NameRelativeToCRLIssuer(rdn))) + } + _ => Err(Err::Error(BerError::InvalidTag)), + } + } + + // ReasonFlags ::= BIT STRING { + // unused (0), + // keyCompromise (1), + // cACompromise (2), + // affiliationChanged (3), + // superseded (4), + // cessationOfOperation (5), + // certificateHold (6), + // privilegeWithdrawn (7), + // aACompromise (8) } + fn parse_tagged1_reasons(i: &[u8]) -> BerResult<ReasonFlags> { + let (rem, obj) = parse_der_tagged_implicit(1, parse_der_content(Tag::BitString))(i)?; + if let DerObjectContent::BitString(_, b) = obj.content { + let flags = b + .data + .iter() + .rev() + .fold(0, |acc, x| acc << 8 | (x.reverse_bits() as u16)); + Ok((rem, ReasonFlags { flags })) + } else { + Err(nom::Err::Failure(BerError::InvalidTag)) + } + } + + fn parse_crlissuer_content(i: &[u8]) -> BerResult<Vec<GeneralName>> { + many1(complete(parse_generalname))(i) + } + + // DistributionPoint ::= SEQUENCE { + // distributionPoint [0] DistributionPointName OPTIONAL, + // reasons [1] ReasonFlags OPTIONAL, + // cRLIssuer [2] GeneralNames OPTIONAL } + pub(super) fn parse_crldistributionpoint( + i: &[u8], + ) -> IResult<&[u8], CRLDistributionPoint, BerError> { + parse_der_sequence_defined_g(|content, _| { + let (rem, distribution_point) = + opt(complete(parse_der_tagged_explicit_g(0, |b, _| { + parse_distributionpointname(b) + })))(content)?; + let (rem, reasons) = opt(complete(parse_tagged1_reasons))(rem)?; + let (rem, crl_issuer) = opt(complete(parse_der_tagged_implicit_g(2, |i, _, _| { + parse_crlissuer_content(i) + })))(rem)?; + let crl_dp = CRLDistributionPoint { + distribution_point, + reasons, + crl_issuer, + }; + Ok((rem, crl_dp)) + })(i) + } + + pub(super) fn parse_crldistributionpoints( + i: &[u8], + ) -> IResult<&[u8], CRLDistributionPoints, BerError> { + let (ret, crldps) = parse_der_sequence_of_v(parse_crldistributionpoint)(i)?; + Ok((ret, CRLDistributionPoints { points: crldps })) + } + + fn parse_crldistributionpoints_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + map( + parse_crldistributionpoints, + ParsedExtension::CRLDistributionPoints, + )(i) + } + + // AuthorityInfoAccessSyntax ::= + // SEQUENCE SIZE (1..MAX) OF AccessDescription + // + // AccessDescription ::= SEQUENCE { + // accessMethod OBJECT IDENTIFIER, + // accessLocation GeneralName } + pub(super) fn parse_authorityinfoaccess( + i: &[u8], + ) -> IResult<&[u8], AuthorityInfoAccess, BerError> { + fn parse_aia(i: &[u8]) -> IResult<&[u8], AccessDescription, BerError> { + parse_der_sequence_defined_g(|content, _| { + // Read first element, an oid. + let (gn, oid) = Oid::from_der(content)?; + // Parse second element + let (rest, gn) = parse_generalname(gn)?; + Ok((rest, AccessDescription::new(oid, gn))) + })(i) + } + let (ret, accessdescs) = parse_der_sequence_of_v(parse_aia)(i)?; + Ok((ret, AuthorityInfoAccess { accessdescs })) + } + + fn parse_authorityinfoaccess_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + map( + parse_authorityinfoaccess, + ParsedExtension::AuthorityInfoAccess, + )(i) + } + + fn parse_aki_content<'a>( + i: &'a [u8], + _hdr: Header<'_>, + ) -> IResult<&'a [u8], AuthorityKeyIdentifier<'a>, BerError> { + let (i, key_identifier) = opt(complete(parse_der_tagged_implicit_g(0, |d, _, _| { + Ok((&[], KeyIdentifier(d))) + })))(i)?; + let (i, authority_cert_issuer) = + opt(complete(parse_der_tagged_implicit_g(1, |d, _, _| { + many0(complete(parse_generalname))(d) + })))(i)?; + let (i, authority_cert_serial) = opt(complete(parse_der_tagged_implicit( + 2, + parse_der_content(Tag::Integer), + )))(i)?; + let authority_cert_serial = authority_cert_serial.and_then(|o| o.as_slice().ok()); + let aki = AuthorityKeyIdentifier { + key_identifier, + authority_cert_issuer, + authority_cert_serial, + }; + Ok((i, aki)) + } + + // RFC 5280 section 4.2.1.1: Authority Key Identifier + pub(super) fn parse_authoritykeyidentifier( + i: &[u8], + ) -> IResult<&[u8], AuthorityKeyIdentifier, BerError> { + let (rem, aki) = parse_der_sequence_defined_g(parse_aki_content)(i)?; + Ok((rem, aki)) + } + + fn parse_authoritykeyidentifier_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + map( + parse_authoritykeyidentifier, + ParsedExtension::AuthorityKeyIdentifier, + )(i) + } + + pub(super) fn parse_keyidentifier(i: &[u8]) -> IResult<&[u8], KeyIdentifier, BerError> { + let (rest, id) = <&[u8]>::from_der(i)?; + let ki = KeyIdentifier(id); + Ok((rest, ki)) + } + + fn parse_keyidentifier_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + map(parse_keyidentifier, ParsedExtension::SubjectKeyIdentifier)(i) + } + + fn parse_keyusage_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + map(parse_keyusage, ParsedExtension::KeyUsage)(i) + } + + pub(super) fn parse_nscerttype(i: &[u8]) -> IResult<&[u8], NSCertType, BerError> { + let (rest, obj) = parse_der_bitstring(i)?; + let bitstring = obj + .content + .as_bitstring() + .or(Err(Err::Error(BerError::BerTypeError)))?; + // bitstring should be 1 byte long + if bitstring.data.len() != 1 { + return Err(Err::Error(BerError::BerValueError)); + } + let flags = bitstring.data[0].reverse_bits(); + Ok((rest, NSCertType(flags))) + } + + fn parse_nscerttype_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + map(parse_nscerttype, ParsedExtension::NSCertType)(i) + } + + fn parse_nscomment_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + match parse_der_ia5string(i) { + Ok((i, obj)) => { + let s = obj.as_str()?; + Ok((i, ParsedExtension::NsCertComment(s))) + } + Err(e) => { + // Some implementations encode the comment directly, without + // wrapping it in an IA5String + if let Ok(s) = std::str::from_utf8(i) { + Ok((&[], ParsedExtension::NsCertComment(s))) + } else { + Err(e) + } + } + } + } + + // CertificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation + // + // PolicyInformation ::= SEQUENCE { + // policyIdentifier CertPolicyId, + // policyQualifiers SEQUENCE SIZE (1..MAX) OF + // PolicyQualifierInfo OPTIONAL } + // + // CertPolicyId ::= OBJECT IDENTIFIER + // + // PolicyQualifierInfo ::= SEQUENCE { + // policyQualifierId PolicyQualifierId, + // qualifier ANY DEFINED BY policyQualifierId } + // + // -- Implementations that recognize additional policy qualifiers MUST + // -- augment the following definition for PolicyQualifierId + // + // PolicyQualifierId ::= OBJECT IDENTIFIER ( id-qt-cps | id-qt-unotice ) + pub(super) fn parse_certificatepolicies( + i: &[u8], + ) -> IResult<&[u8], Vec<PolicyInformation>, BerError> { + fn parse_policy_qualifier_info(i: &[u8]) -> IResult<&[u8], PolicyQualifierInfo, BerError> { + parse_der_sequence_defined_g(|content, _| { + let (rem, policy_qualifier_id) = Oid::from_der(content)?; + let info = PolicyQualifierInfo { + policy_qualifier_id, + qualifier: rem, + }; + Ok((&[], info)) + })(i) + } + fn parse_policy_information(i: &[u8]) -> IResult<&[u8], PolicyInformation, BerError> { + parse_der_sequence_defined_g(|content, _| { + let (rem, policy_id) = Oid::from_der(content)?; + let (rem, policy_qualifiers) = + opt(complete(parse_der_sequence_defined_g(|content, _| { + many1(complete(parse_policy_qualifier_info))(content) + })))(rem)?; + let info = PolicyInformation { + policy_id, + policy_qualifiers, + }; + Ok((rem, info)) + })(i) + } + parse_der_sequence_of_v(parse_policy_information)(i) + } + + fn parse_certificatepolicies_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + map( + parse_certificatepolicies, + ParsedExtension::CertificatePolicies, + )(i) + } + + // CRLReason ::= ENUMERATED { ... + fn parse_reason_code(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + let (rest, obj) = parse_der_enum(i)?; + let code = obj + .content + .as_u32() + .or(Err(Err::Error(BerError::BerValueError)))?; + if code > 10 { + return Err(Err::Error(BerError::BerValueError)); + } + let ret = ParsedExtension::ReasonCode(ReasonCode(code as u8)); + Ok((rest, ret)) + } + + // invalidityDate ::= GeneralizedTime + fn parse_invalidity_date(i: &[u8]) -> ParseResult<ParsedExtension> { + let (rest, t) = GeneralizedTime::from_der(i)?; + let dt = t.utc_datetime()?; + Ok((rest, ParsedExtension::InvalidityDate(ASN1Time::new(dt)))) + } + + // CRLNumber ::= INTEGER (0..MAX) + // Note from RFC 3280: "CRL verifiers MUST be able to handle CRLNumber values up to 20 octets." + fn parse_crl_number(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + let (rest, num) = map_res(parse_der_integer, |obj| obj.as_biguint())(i)?; + Ok((rest, ParsedExtension::CRLNumber(num))) + } + + fn parse_sct_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + map( + parse_ct_signed_certificate_timestamp_list, + ParsedExtension::SCT, + )(i) + } +} + +/// Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension +pub(crate) fn parse_extension_sequence(i: &[u8]) -> X509Result<Vec<X509Extension>> { + parse_der_sequence_defined_g(|a, _| all_consuming(many0(complete(X509Extension::from_der)))(a))( + i, + ) +} + +pub(crate) fn parse_extensions(i: &[u8], explicit_tag: Tag) -> X509Result<Vec<X509Extension>> { + if i.is_empty() { + return Ok((i, Vec::new())); + } + + match der_read_element_header(i) { + Ok((rem, hdr)) => { + if hdr.tag() != explicit_tag { + return Err(Err::Error(X509Error::InvalidExtensions)); + } + all_consuming(parse_extension_sequence)(rem) + } + Err(_) => Err(X509Error::InvalidExtensions.into()), + } +} + +/// Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension +pub(crate) fn parse_extension_envelope_sequence(i: &[u8]) -> X509Result<Vec<X509Extension>> { + let parser = X509ExtensionParser::new().with_deep_parse_extensions(false); + + parse_der_sequence_defined_g(move |a, _| all_consuming(many0(complete(parser)))(a))(i) +} + +pub(crate) fn parse_extensions_envelope( + i: &[u8], + explicit_tag: Tag, +) -> X509Result<Vec<X509Extension>> { + if i.is_empty() { + return Ok((i, Vec::new())); + } + + match der_read_element_header(i) { + Ok((rem, hdr)) => { + if hdr.tag() != explicit_tag { + return Err(Err::Error(X509Error::InvalidExtensions)); + } + all_consuming(parse_extension_envelope_sequence)(rem) + } + Err(_) => Err(X509Error::InvalidExtensions.into()), + } +} + +fn der_read_critical(i: &[u8]) -> BerResult<bool> { + // Some certificates do not respect the DER BOOLEAN constraint (true must be encoded as 0xff) + // so we attempt to parse as BER + let (rem, obj) = opt(parse_ber_bool)(i)?; + let value = obj + .map(|o| o.as_bool().unwrap_or_default()) // unwrap cannot fail, we just read a bool + .unwrap_or(false) // default critical value + ; + Ok((rem, value)) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_keyusage_flags() { + let ku = KeyUsage { flags: 98 }; + assert!(!ku.digital_signature()); + assert!(ku.non_repudiation()); + assert!(!ku.key_encipherment()); + assert!(!ku.data_encipherment()); + assert!(!ku.key_agreement()); + assert!(ku.key_cert_sign()); + assert!(ku.crl_sign()); + assert!(!ku.encipher_only()); + assert!(!ku.decipher_only()); + } + + #[test] + fn test_extensions1() { + use der_parser::oid; + let crt = crate::parse_x509_certificate(include_bytes!("../../assets/extension1.der")) + .unwrap() + .1; + let tbs = &crt.tbs_certificate; + let bc = crt + .basic_constraints() + .expect("could not get basic constraints") + .expect("no basic constraints found"); + assert_eq!( + bc.value, + &BasicConstraints { + ca: true, + path_len_constraint: Some(1) + } + ); + { + let ku = tbs + .key_usage() + .expect("could not get key usage") + .expect("no key usage found") + .value; + assert!(ku.digital_signature()); + assert!(!ku.non_repudiation()); + assert!(ku.key_encipherment()); + assert!(ku.data_encipherment()); + assert!(ku.key_agreement()); + assert!(!ku.key_cert_sign()); + assert!(!ku.crl_sign()); + assert!(ku.encipher_only()); + assert!(ku.decipher_only()); + } + { + let eku = tbs + .extended_key_usage() + .expect("could not get extended key usage") + .expect("no extended key usage found") + .value; + assert!(!eku.any); + assert!(eku.server_auth); + assert!(!eku.client_auth); + assert!(eku.code_signing); + assert!(!eku.email_protection); + assert!(eku.time_stamping); + assert!(!eku.ocsp_signing); + assert_eq!(eku.other, vec![oid!(1.2.3 .4 .0 .42)]); + } + assert_eq!( + tbs.policy_constraints() + .expect("could not get policy constraints") + .expect("no policy constraints found") + .value, + &PolicyConstraints { + require_explicit_policy: None, + inhibit_policy_mapping: Some(10) + } + ); + let val = tbs + .inhibit_anypolicy() + .expect("could not get inhibit_anypolicy") + .expect("no inhibit_anypolicy found") + .value; + assert_eq!(val, &InhibitAnyPolicy { skip_certs: 2 }); + { + let alt_names = &tbs + .subject_alternative_name() + .expect("could not get subject alt names") + .expect("no subject alt names found") + .value + .general_names; + assert_eq!(alt_names[0], GeneralName::RFC822Name("foo@example.com")); + assert_eq!(alt_names[1], GeneralName::URI("http://my.url.here/")); + assert_eq!( + alt_names[2], + GeneralName::IPAddress([192, 168, 7, 1].as_ref()) + ); + assert_eq!( + format!( + "{}", + match alt_names[3] { + GeneralName::DirectoryName(ref dn) => dn, + _ => unreachable!(), + } + ), + "C=UK, O=My Organization, OU=My Unit, CN=My Name" + ); + assert_eq!(alt_names[4], GeneralName::DNSName("localhost")); + assert_eq!(alt_names[5], GeneralName::RegisteredID(oid!(1.2.90 .0))); + assert_eq!( + alt_names[6], + GeneralName::OtherName(oid!(1.2.3 .4), b"\xA0\x17\x0C\x15some other identifier") + ); + } + + { + let name_constraints = &tbs + .name_constraints() + .expect("could not get name constraints") + .expect("no name constraints found") + .value; + assert_eq!(name_constraints.permitted_subtrees, None); + assert_eq!( + name_constraints.excluded_subtrees, + Some(vec![ + GeneralSubtree { + base: GeneralName::IPAddress([192, 168, 0, 0, 255, 255, 0, 0].as_ref()) + }, + GeneralSubtree { + base: GeneralName::RFC822Name("foo.com") + }, + ]) + ); + } + } + + #[test] + fn test_extensions2() { + use der_parser::oid; + let crt = crate::parse_x509_certificate(include_bytes!("../../assets/extension2.der")) + .unwrap() + .1; + let tbs = crt.tbs_certificate; + assert_eq!( + tbs.policy_constraints() + .expect("could not get policy constraints") + .expect("no policy constraints found") + .value, + &PolicyConstraints { + require_explicit_policy: Some(5000), + inhibit_policy_mapping: None + } + ); + { + let pm = tbs + .policy_mappings() + .expect("could not get policy_mappings") + .expect("no policy_mappings found") + .value + .clone() + .into_hashmap(); + let mut pm_ref = HashMap::new(); + pm_ref.insert(oid!(2.34.23), vec![oid!(2.2)]); + pm_ref.insert(oid!(1.1), vec![oid!(0.0.4)]); + pm_ref.insert(oid!(2.2), vec![oid!(2.2.1), oid!(2.2.3)]); + assert_eq!(pm, pm_ref); + } + } + + #[test] + fn test_extensions_crl_distribution_points() { + // Extension not present + { + let crt = crate::parse_x509_certificate(include_bytes!( + "../../assets/crl-ext/crl-no-crl.der" + )) + .unwrap() + .1; + assert!(crt + .tbs_certificate + .extensions_map() + .unwrap() + .get(&OID_X509_EXT_CRL_DISTRIBUTION_POINTS) + .is_none()); + } + // CRLDistributionPoints has 1 entry with 1 URI + { + let crt = crate::parse_x509_certificate(include_bytes!( + "../../assets/crl-ext/crl-simple.der" + )) + .unwrap() + .1; + let crl = crt + .tbs_certificate + .extensions_map() + .unwrap() + .get(&OID_X509_EXT_CRL_DISTRIBUTION_POINTS) + .unwrap() + .parsed_extension(); + assert!(matches!(crl, ParsedExtension::CRLDistributionPoints(_))); + if let ParsedExtension::CRLDistributionPoints(crl) = crl { + assert_eq!(crl.len(), 1); + assert!(crl[0].reasons.is_none()); + assert!(crl[0].crl_issuer.is_none()); + let distribution_point = crl[0].distribution_point.as_ref().unwrap(); + assert!(matches!( + distribution_point, + DistributionPointName::FullName(_) + )); + if let DistributionPointName::FullName(names) = distribution_point { + assert_eq!(names.len(), 1); + assert!(matches!(names[0], GeneralName::URI(_))); + if let GeneralName::URI(uri) = names[0] { + assert_eq!(uri, "http://example.com/myca.crl") + } + } + } + } + // CRLDistributionPoints has 2 entries + { + let crt = crate::parse_x509_certificate(include_bytes!( + "../../assets/crl-ext/crl-complex.der" + )) + .unwrap() + .1; + let crl = crt + .tbs_certificate + .extensions_map() + .unwrap() + .get(&OID_X509_EXT_CRL_DISTRIBUTION_POINTS) + .unwrap() + .parsed_extension(); + assert!(matches!(crl, ParsedExtension::CRLDistributionPoints(_))); + if let ParsedExtension::CRLDistributionPoints(crl) = crl { + assert_eq!(crl.len(), 2); + // First CRL Distribution point + let reasons = crl[0].reasons.as_ref().unwrap(); + assert!(reasons.key_compromise()); + assert!(reasons.ca_compromise()); + assert!(!reasons.affilation_changed()); + assert!(!reasons.superseded()); + assert!(!reasons.cessation_of_operation()); + assert!(!reasons.certificate_hold()); + assert!(!reasons.privelege_withdrawn()); + assert!(reasons.aa_compromise()); + assert_eq!( + format!("{}", reasons), + "Key Compromise, CA Compromise, AA Compromise" + ); + let issuers = crl[0].crl_issuer.as_ref().unwrap(); + assert_eq!(issuers.len(), 1); + assert!(matches!(issuers[0], GeneralName::DirectoryName(_))); + if let GeneralName::DirectoryName(name) = &issuers[0] { + assert_eq!(name.to_string(), "C=US, O=Organisation, CN=Some Name"); + } + let distribution_point = crl[0].distribution_point.as_ref().unwrap(); + assert!(matches!( + distribution_point, + DistributionPointName::FullName(_) + )); + if let DistributionPointName::FullName(names) = distribution_point { + assert_eq!(names.len(), 1); + assert!(matches!(names[0], GeneralName::URI(_))); + if let GeneralName::URI(uri) = names[0] { + assert_eq!(uri, "http://example.com/myca.crl") + } + } + // Second CRL Distribution point + let reasons = crl[1].reasons.as_ref().unwrap(); + assert!(reasons.key_compromise()); + assert!(reasons.ca_compromise()); + assert!(!reasons.affilation_changed()); + assert!(!reasons.superseded()); + assert!(!reasons.cessation_of_operation()); + assert!(!reasons.certificate_hold()); + assert!(!reasons.privelege_withdrawn()); + assert!(!reasons.aa_compromise()); + assert_eq!(format!("{}", reasons), "Key Compromise, CA Compromise"); + assert!(crl[1].crl_issuer.is_none()); + let distribution_point = crl[1].distribution_point.as_ref().unwrap(); + assert!(matches!( + distribution_point, + DistributionPointName::FullName(_) + )); + if let DistributionPointName::FullName(names) = distribution_point { + assert_eq!(names.len(), 1); + assert!(matches!(names[0], GeneralName::URI(_))); + if let GeneralName::URI(uri) = names[0] { + assert_eq!(uri, "http://example.com/myca2.crl") + } + } + } + } + } + + // Test cases for: + // - parsing SubjectAlternativeName + // - parsing NameConstraints +} diff --git a/rust/vendor/x509-parser/src/extensions/nameconstraints.rs b/rust/vendor/x509-parser/src/extensions/nameconstraints.rs new file mode 100644 index 0000000..da9a999 --- /dev/null +++ b/rust/vendor/x509-parser/src/extensions/nameconstraints.rs @@ -0,0 +1,59 @@ +use super::GeneralName; +use crate::error::{X509Error, X509Result}; +use crate::extensions::parse_generalname; +use asn1_rs::FromDer; +use der_parser::der::*; +use der_parser::error::BerError; +use nom::combinator::{all_consuming, complete, map, opt}; +use nom::multi::many1; +use nom::{Err, IResult}; + +#[derive(Clone, Debug, PartialEq)] +pub struct NameConstraints<'a> { + pub permitted_subtrees: Option<Vec<GeneralSubtree<'a>>>, + pub excluded_subtrees: Option<Vec<GeneralSubtree<'a>>>, +} + +impl<'a> FromDer<'a, X509Error> for NameConstraints<'a> { + fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { + parse_nameconstraints(i).map_err(Err::convert) + } +} + +#[derive(Clone, Debug, PartialEq)] +/// Represents the structure used in the name constraints extensions. +/// The fields minimum and maximum are not supported (openssl also has no support). +pub struct GeneralSubtree<'a> { + pub base: GeneralName<'a>, + // minimum: u32, + // maximum: Option<u32>, +} + +pub(crate) fn parse_nameconstraints(i: &[u8]) -> IResult<&[u8], NameConstraints, BerError> { + fn parse_subtree(i: &[u8]) -> IResult<&[u8], GeneralSubtree, BerError> { + parse_der_sequence_defined_g(|input, _| { + map(parse_generalname, |base| GeneralSubtree { base })(input) + })(i) + } + fn parse_subtrees(i: &[u8]) -> IResult<&[u8], Vec<GeneralSubtree>, BerError> { + all_consuming(many1(complete(parse_subtree)))(i) + } + + let (ret, named_constraints) = parse_der_sequence_defined_g(|input, _| { + let (rem, permitted_subtrees) = + opt(complete(parse_der_tagged_explicit_g(0, |input, _| { + parse_subtrees(input) + })))(input)?; + let (rem, excluded_subtrees) = + opt(complete(parse_der_tagged_explicit_g(1, |input, _| { + parse_subtrees(input) + })))(rem)?; + let named_constraints = NameConstraints { + permitted_subtrees, + excluded_subtrees, + }; + Ok((rem, named_constraints)) + })(i)?; + + Ok((ret, named_constraints)) +} diff --git a/rust/vendor/x509-parser/src/extensions/policymappings.rs b/rust/vendor/x509-parser/src/extensions/policymappings.rs new file mode 100644 index 0000000..2bd367d --- /dev/null +++ b/rust/vendor/x509-parser/src/extensions/policymappings.rs @@ -0,0 +1,92 @@ +use crate::error::{X509Error, X509Result}; +use asn1_rs::{DerSequence, Error, FromDer, Oid}; +use nom::{Err, IResult}; +use std::collections::HashMap; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PolicyMappings<'a> { + pub mappings: Vec<PolicyMapping<'a>>, +} + +impl<'a> FromDer<'a, X509Error> for PolicyMappings<'a> { + fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { + parse_policymappings(i).map_err(Err::convert) + } +} + +impl<'a> PolicyMappings<'a> { + /// Returns a `HashMap` mapping `Oid` to the list of references to `Oid` + /// + /// If several names match the same `Oid`, they are merged in the same entry. + pub fn as_hashmap(&self) -> HashMap<Oid<'a>, Vec<&Oid<'a>>> { + // create the hashmap and merge entries with same OID + let mut m: HashMap<Oid, Vec<&_>> = HashMap::new(); + for desc in &self.mappings { + let PolicyMapping { + issuer_domain_policy: left, + subject_domain_policy: right, + } = desc; + if let Some(l) = m.get_mut(left) { + l.push(right); + } else { + m.insert(left.clone(), vec![right]); + } + } + m + } + + /// Returns a `HashMap` mapping `Oid` to the list of `Oid` (consuming the input) + /// + /// If several names match the same `Oid`, they are merged in the same entry. + pub fn into_hashmap(self) -> HashMap<Oid<'a>, Vec<Oid<'a>>> { + let mut l = self.mappings; + // create the hashmap and merge entries with same OID + let mut m: HashMap<Oid, Vec<_>> = HashMap::new(); + for mapping in l.drain(..) { + let PolicyMapping { + issuer_domain_policy: left, + subject_domain_policy: right, + } = mapping; + if let Some(general_names) = m.get_mut(&left) { + general_names.push(right); + } else { + m.insert(left, vec![right]); + } + } + m + } +} + +#[derive(Clone, Debug, PartialEq, Eq, DerSequence)] +pub struct PolicyMapping<'a> { + pub issuer_domain_policy: Oid<'a>, + pub subject_domain_policy: Oid<'a>, +} + +impl<'a> PolicyMapping<'a> { + pub const fn new(issuer_domain_policy: Oid<'a>, subject_domain_policy: Oid<'a>) -> Self { + PolicyMapping { + issuer_domain_policy, + subject_domain_policy, + } + } +} + +// PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE { +// issuerDomainPolicy CertPolicyId, +// subjectDomainPolicy CertPolicyId } +pub(crate) fn parse_policymappings(i: &[u8]) -> IResult<&[u8], PolicyMappings, Error> { + let (ret, pairs) = <Vec<PolicyMapping>>::from_der(i)?; + // let mut mappings: HashMap<Oid, Vec<Oid>> = HashMap::new(); + let mappings = pairs; + // let mut mappings = Vec::new(); + // for pair in pairs.iter() { + // // XXX this should go to Validate + // // if left.bytes() == oid!(raw 2.5.29.32.0) || right.bytes() == oid!(raw 2.5.29.32.0) { + // // // mapping to or from anyPolicy is not allowed + // // return Err(Err::Failure(BerError::InvalidTag)); + // // } + // mappings.push(PolicyMapping::new(left, right)); + // } + Ok((ret, PolicyMappings { mappings })) +} diff --git a/rust/vendor/x509-parser/src/extensions/sct.rs b/rust/vendor/x509-parser/src/extensions/sct.rs new file mode 100644 index 0000000..646f774 --- /dev/null +++ b/rust/vendor/x509-parser/src/extensions/sct.rs @@ -0,0 +1,124 @@ +//! Certificate transparency [RFC6962](https://datatracker.ietf.org/doc/html/rfc6962) +//! +//! Code borrowed from tls-parser crate (file <https://github.com/rusticata/tls-parser/blob/tls-parser-0.11.0/src/certificate_transparency.rs>) + +use std::convert::TryInto; + +use asn1_rs::FromDer; +use der_parser::error::BerError; +use nom::bytes::streaming::take; +use nom::combinator::{complete, map_parser}; +use nom::multi::{length_data, many1}; +use nom::number::streaming::{be_u16, be_u64, be_u8}; +use nom::IResult; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct SignedCertificateTimestamp<'a> { + pub version: CtVersion, + pub id: CtLogID<'a>, + pub timestamp: u64, + pub extensions: CtExtensions<'a>, + pub signature: DigitallySigned<'a>, +} + +/// Certificate Transparency Version as defined in +/// [RFC6962 Section 3.2](https://datatracker.ietf.org/doc/html/rfc6962#section-3.2) +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct CtVersion(pub u8); + +impl CtVersion { + pub const V1: CtVersion = CtVersion(0); +} + +/// LogID as defined in +/// [RFC6962 Section 3.2](https://datatracker.ietf.org/doc/html/rfc6962#section-3.2) +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CtLogID<'a> { + pub key_id: &'a [u8; 32], +} + +/// CtExtensions as defined in +/// [RFC6962 Section 3.2](https://datatracker.ietf.org/doc/html/rfc6962#section-3.2) +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CtExtensions<'a>(pub &'a [u8]); + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct DigitallySigned<'a> { + pub hash_alg_id: u8, + pub sign_alg_id: u8, + pub data: &'a [u8], +} + +/// Parses a list of Signed Certificate Timestamp entries +pub fn parse_ct_signed_certificate_timestamp_list( + i: &[u8], +) -> IResult<&[u8], Vec<SignedCertificateTimestamp>, BerError> { + // use nom::HexDisplay; + // eprintln!("{}", i.to_hex(16)); + let (rem, b) = <&[u8]>::from_der(i)?; + let (b, sct_len) = be_u16(b)?; + let (_, sct_list) = map_parser( + take(sct_len as usize), + many1(complete(parse_ct_signed_certificate_timestamp)), + )(b)?; + Ok((rem, sct_list)) +} + +/// Parses as single Signed Certificate Timestamp entry +pub fn parse_ct_signed_certificate_timestamp( + i: &[u8], +) -> IResult<&[u8], SignedCertificateTimestamp, BerError> { + map_parser( + length_data(be_u16), + parse_ct_signed_certificate_timestamp_content, + )(i) +} + +pub(crate) fn parse_ct_signed_certificate_timestamp_content( + i: &[u8], +) -> IResult<&[u8], SignedCertificateTimestamp, BerError> { + let (i, version) = be_u8(i)?; + let (i, id) = parse_log_id(i)?; + let (i, timestamp) = be_u64(i)?; + let (i, extensions) = parse_ct_extensions(i)?; + let (i, signature) = parse_digitally_signed(i)?; + let sct = SignedCertificateTimestamp { + version: CtVersion(version), + id, + timestamp, + extensions, + signature, + }; + Ok((i, sct)) +} + +// Safety: cannot fail, take() returns exactly 32 bytes +fn parse_log_id(i: &[u8]) -> IResult<&[u8], CtLogID, BerError> { + let (i, key_id) = take(32usize)(i)?; + Ok(( + i, + CtLogID { + key_id: key_id + .try_into() + .expect("take(32) is in sync with key_id size"), + }, + )) +} + +fn parse_ct_extensions(i: &[u8]) -> IResult<&[u8], CtExtensions, BerError> { + let (i, ext_len) = be_u16(i)?; + let (i, ext_data) = take(ext_len as usize)(i)?; + Ok((i, CtExtensions(ext_data))) +} + +fn parse_digitally_signed(i: &[u8]) -> IResult<&[u8], DigitallySigned, BerError> { + let (i, hash_alg_id) = be_u8(i)?; + let (i, sign_alg_id) = be_u8(i)?; + let (i, data) = length_data(be_u16)(i)?; + let signed = DigitallySigned { + hash_alg_id, + sign_alg_id, + data, + }; + Ok((i, signed)) +} diff --git a/rust/vendor/x509-parser/src/lib.rs b/rust/vendor/x509-parser/src/lib.rs new file mode 100644 index 0000000..1ddd357 --- /dev/null +++ b/rust/vendor/x509-parser/src/lib.rs @@ -0,0 +1,213 @@ +//! [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE-MIT) +//! [![Apache License 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](./LICENSE-APACHE) +//! [![docs.rs](https://docs.rs/x509-parser/badge.svg)](https://docs.rs/x509-parser) +//! [![crates.io](https://img.shields.io/crates/v/x509-parser.svg)](https://crates.io/crates/x509-parser) +//! [![Download numbers](https://img.shields.io/crates/d/x509-parser.svg)](https://crates.io/crates/x509-parser) +//! [![Github CI](https://github.com/rusticata/x509-parser/workflows/Continuous%20integration/badge.svg)](https://github.com/rusticata/x509-parser/actions) +//! [![Minimum rustc version](https://img.shields.io/badge/rustc-1.57.0+-lightgray.svg)](#rust-version-requirements) +//! +//! # X.509 Parser +//! +//! A X.509 v3 ([RFC5280]) parser, implemented with the [nom](https://github.com/Geal/nom) +//! parser combinator framework. +//! +//! It is written in pure Rust, fast, and makes extensive use of zero-copy. A lot of care is taken +//! to ensure security and safety of this crate, including design (recursion limit, defensive +//! programming), tests, and fuzzing. It also aims to be panic-free. +//! +//! The code is available on [Github](https://github.com/rusticata/x509-parser) +//! and is part of the [Rusticata](https://github.com/rusticata) project. +//! +//! Certificates are usually encoded in two main formats: PEM (usually the most common format) or +//! DER. A PEM-encoded certificate is a container, storing a DER object. See the +//! [`pem`](pem/index.html) module for more documentation. +//! +//! To decode a DER-encoded certificate, the main parsing method is +//! `X509Certificate::from_der` ( +//! part of the [`FromDer`](prelude/trait.FromDer.html) trait +//! ), which builds a +//! [`X509Certificate`](certificate/struct.X509Certificate.html) object. +//! +//! An alternative method is to use [`X509CertificateParser`](certificate/struct.X509CertificateParser.html), +//! which allows specifying parsing options (for example, not automatically parsing option contents). +//! +//! The returned objects for parsers follow the definitions of the RFC. This means that accessing +//! fields is done by accessing struct members recursively. Some helper functions are provided, for +//! example [`X509Certificate::issuer()`](certificate/struct.X509Certificate.html#method.issuer) returns the +//! same as accessing `<object>.tbs_certificate.issuer`. +//! +//! For PEM-encoded certificates, use the [`pem`](pem/index.html) module. +//! +//! # Examples +//! +//! Parsing a certificate in DER format: +//! +//! ```rust +//! use x509_parser::prelude::*; +//! +//! static IGCA_DER: &[u8] = include_bytes!("../assets/IGC_A.der"); +//! +//! # fn main() { +//! let res = X509Certificate::from_der(IGCA_DER); +//! match res { +//! Ok((rem, cert)) => { +//! assert!(rem.is_empty()); +//! // +//! assert_eq!(cert.version(), X509Version::V3); +//! }, +//! _ => panic!("x509 parsing failed: {:?}", res), +//! } +//! # } +//! ``` +//! +//! To parse a CRL and print information about revoked certificates: +//! +//! ```rust +//! # use x509_parser::prelude::*; +//! # +//! # static DER: &[u8] = include_bytes!("../assets/example.crl"); +//! # +//! # fn main() { +//! let res = CertificateRevocationList::from_der(DER); +//! match res { +//! Ok((_rem, crl)) => { +//! for revoked in crl.iter_revoked_certificates() { +//! println!("Revoked certificate serial: {}", revoked.raw_serial_as_string()); +//! println!(" Reason: {}", revoked.reason_code().unwrap_or_default().1); +//! } +//! }, +//! _ => panic!("CRL parsing failed: {:?}", res), +//! } +//! # } +//! ``` +//! +//! See also `examples/print-cert.rs`. +//! +//! # Features +//! +//! - The `verify` feature adds support for (cryptographic) signature verification, based on `ring`. +//! It adds the +//! [`X509Certificate::verify_signature()`](certificate/struct.X509Certificate.html#method.verify_signature) +//! to `X509Certificate`. +//! +//! ```rust +//! # #[cfg(feature = "verify")] +//! # use x509_parser::certificate::X509Certificate; +//! /// Cryptographic signature verification: returns true if certificate was signed by issuer +//! #[cfg(feature = "verify")] +//! pub fn check_signature(cert: &X509Certificate<'_>, issuer: &X509Certificate<'_>) -> bool { +//! let issuer_public_key = issuer.public_key(); +//! cert +//! .verify_signature(Some(issuer_public_key)) +//! .is_ok() +//! } +//! ``` +//! +//! - The `validate` features add methods to run more validation functions on the certificate structure +//! and values using the [`Validate`](validate/trait.Validate.html) trait. +//! It does not validate any cryptographic parameter (see `verify` above). +//! +//! ## Rust version requirements +//! +//! `x509-parser` requires **Rustc version 1.57 or greater**, based on der-parser +//! dependencies and for proc-macro attributes support. +//! +//! Note that due to breaking changes in the `time` crate, a specific version of this +//! crate must be specified for compiler versions <= 1.57: +//! `cargo update -p time --precise 0.3.9` +//! +//! [RFC5280]: https://tools.ietf.org/html/rfc5280 + +#![deny(/*missing_docs,*/ + unstable_features, + unused_import_braces, unused_qualifications)] +#![warn( + missing_debug_implementations, + /* missing_docs, + rust_2018_idioms,*/ + unreachable_pub +)] +#![forbid(unsafe_code)] +#![deny(rustdoc::broken_intra_doc_links)] +#![doc(test( + no_crate_inject, + attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables)) +))] +#![cfg_attr(docsrs, feature(doc_cfg))] + +pub mod certificate; +pub mod certification_request; +pub mod cri_attributes; +pub mod error; +pub mod extensions; +pub mod objects; +pub mod pem; +pub mod prelude; +pub mod public_key; +pub mod revocation_list; +pub mod signature_algorithm; +pub mod signature_value; +pub mod time; +pub mod utils; +#[cfg(feature = "validate")] +#[cfg_attr(docsrs, doc(cfg(feature = "validate")))] +pub mod validate; +#[cfg(feature = "verify")] +#[cfg_attr(docsrs, doc(cfg(feature = "verify")))] +pub mod verify; +pub mod x509; + +// reexports +pub use der_parser; +pub use der_parser::num_bigint; +pub use nom; +pub use oid_registry; + +use asn1_rs::FromDer; +use certificate::X509Certificate; +use error::X509Result; +use revocation_list::CertificateRevocationList; + +/// Parse a **DER-encoded** X.509 Certificate, and return the remaining of the input and the built +/// object. +/// +/// +/// This function is an alias to [X509Certificate::from_der](certificate::X509Certificate::from_der). See this function +/// for more information. +/// +/// For PEM-encoded certificates, use the [`pem`](pem/index.html) module. +#[inline] +pub fn parse_x509_certificate(i: &[u8]) -> X509Result<X509Certificate> { + X509Certificate::from_der(i) +} + +/// Parse a DER-encoded X.509 v2 CRL, and return the remaining of the input and the built +/// object. +/// +/// This function is an alias to [CertificateRevocationList::from_der](revocation_list::CertificateRevocationList::from_der). See this function +/// for more information. +#[inline] +pub fn parse_x509_crl(i: &[u8]) -> X509Result<CertificateRevocationList> { + CertificateRevocationList::from_der(i) +} + +/// Parse a DER-encoded X.509 Certificate, and return the remaining of the input and the built +#[deprecated( + since = "0.9.0", + note = "please use `parse_x509_certificate` or `X509Certificate::from_der` instead" +)] +#[inline] +pub fn parse_x509_der(i: &[u8]) -> X509Result<X509Certificate> { + X509Certificate::from_der(i) +} + +/// Parse a DER-encoded X.509 v2 CRL, and return the remaining of the input and the built +/// object. +#[deprecated( + since = "0.9.0", + note = "please use `parse_x509_crl` or `CertificateRevocationList::from_der` instead" +)] +#[inline] +pub fn parse_crl_der(i: &[u8]) -> X509Result<CertificateRevocationList> { + CertificateRevocationList::from_der(i) +} diff --git a/rust/vendor/x509-parser/src/objects.rs b/rust/vendor/x509-parser/src/objects.rs new file mode 100644 index 0000000..9e3dc63 --- /dev/null +++ b/rust/vendor/x509-parser/src/objects.rs @@ -0,0 +1,92 @@ +//! X.509 helper objects definitions and registry +//! +//! All OID objects and definitions are now stored in the [oid-registry](https://crates.io/crates/oid-registry) crate. +//! +//! This crate is re-exporting `oid-registry`, so to access the OID constants the +//! `x509_parser::oid_oid_registry` namespace can be used (see example below). +//! +//! ## Example +//! +//! To get the short name for a given OID: +//! +//! ```rust +//! use x509_parser::objects::*; +//! use x509_parser::oid_registry::*; +//! +//! let oid = &OID_X509_COMMON_NAME; +//! let sn = oid2sn(oid, oid_registry()); +//! assert_eq!(sn, Ok("commonName")); +//! ``` + +use crate::error::NidError; +use asn1_rs::{oid, Oid}; +use lazy_static::lazy_static; +use oid_registry::*; +use std::collections::HashMap; + +lazy_static! { + static ref OID_REGISTRY: OidRegistry<'static> = { + let mut reg = OidRegistry::default().with_all_crypto().with_x509(); + // OIDs not in the default registry can be added here + let entry = OidEntry::new("id-mgf1", "Mask Generator Function 1 (MGF1)"); + reg.insert(oid! {1.2.840.113549.1.1.8}, entry); + reg + }; + static ref ABBREV_MAP: HashMap<Oid<'static>, &'static str> = { + let mut m = HashMap::new(); + m.insert(OID_X509_COMMON_NAME, "CN"); + m.insert(OID_X509_COUNTRY_NAME, "C"); + m.insert(OID_X509_LOCALITY_NAME, "L"); + m.insert(OID_X509_STATE_OR_PROVINCE_NAME, "ST"); + m.insert(OID_X509_ORGANIZATION_NAME, "O"); + m.insert(OID_X509_ORGANIZATIONAL_UNIT, "OU"); + m.insert(OID_DOMAIN_COMPONENT, "DC"); + m.insert(OID_PKCS9_EMAIL_ADDRESS, "Email"); + m + }; +} + +/// Return the abbreviation (for ex. CN for Common Name), or if not found, the OID short name +pub fn oid2abbrev<'a>(oid: &'a Oid, registry: &'a OidRegistry) -> Result<&'a str, NidError> { + if let Some(abbrev) = ABBREV_MAP.get(oid) { + return Ok(abbrev); + } + registry.get(oid).map(|entry| entry.sn()).ok_or(NidError) +} + +/// Returns the short name corresponding to the OID +pub fn oid2sn<'a>(oid: &'a Oid, registry: &'a OidRegistry) -> Result<&'a str, NidError> { + registry.get(oid).map(|o| o.sn()).ok_or(NidError) +} + +/// Returns the description corresponding to the OID +pub fn oid2description<'a>(oid: &'a Oid, registry: &'a OidRegistry) -> Result<&'a str, NidError> { + registry.get(oid).map(|o| o.description()).ok_or(NidError) +} + +/// Return a reference to the default registry of known OIDs +pub fn oid_registry() -> &'static OidRegistry<'static> { + &OID_REGISTRY +} + +#[cfg(test)] +mod tests { + use super::*; + use der_parser::oid; + + // This test is meant to check syntax of pattern matching with OID objects + #[test] + fn test_oid_match() { + let oid = oid!(1.2.840 .113549 .1 .1 .5); + if oid == OID_PKCS1_SHA1WITHRSA { + // ok + } + // matching is not possible with Cow constants in pattern, + // see https://rust-lang.github.io/rfcs/1445-restrict-constants-in-patterns.html + // + // match oid { + // OID_PKCS1_SHA1WITHRSA => (), + // _ => (), + // } + } +} diff --git a/rust/vendor/x509-parser/src/pem.rs b/rust/vendor/x509-parser/src/pem.rs new file mode 100644 index 0000000..f8bfc7a --- /dev/null +++ b/rust/vendor/x509-parser/src/pem.rs @@ -0,0 +1,255 @@ +//! Decoding functions for PEM-encoded data +//! +//! A PEM object is a container, which can store (amongst other formats) a public X.509 +//! Certificate, or a CRL, etc. It contains only printable characters. +//! PEM-encoded binary data is essentially a beginning and matching end tag that encloses +//! base64-encoded binary data (see: +//! <https://en.wikipedia.org/wiki/Privacy-enhanced_Electronic_Mail>). +//! +//! # Examples +//! +//! To parse a certificate in PEM format, first create the `Pem` object, then decode +//! contents: +//! +//! ```rust,no_run +//! use x509_parser::pem::Pem; +//! use x509_parser::x509::X509Version; +//! +//! static IGCA_PEM: &str = "../assets/IGC_A.pem"; +//! +//! # fn main() { +//! let data = std::fs::read(IGCA_PEM).expect("Could not read file"); +//! for pem in Pem::iter_from_buffer(&data) { +//! let pem = pem.expect("Reading next PEM block failed"); +//! let x509 = pem.parse_x509().expect("X.509: decoding DER failed"); +//! assert_eq!(x509.tbs_certificate.version, X509Version::V3); +//! } +//! # } +//! ``` +//! +//! This is the most direct method to parse PEM data. +//! +//! Another method to parse the certificate is to use `parse_x509_pem`: +//! +//! ```rust,no_run +//! use x509_parser::pem::parse_x509_pem; +//! use x509_parser::parse_x509_certificate; +//! +//! static IGCA_PEM: &[u8] = include_bytes!("../assets/IGC_A.pem"); +//! +//! # fn main() { +//! let res = parse_x509_pem(IGCA_PEM); +//! match res { +//! Ok((rem, pem)) => { +//! assert!(rem.is_empty()); +//! // +//! assert_eq!(pem.label, String::from("CERTIFICATE")); +//! // +//! let res_x509 = parse_x509_certificate(&pem.contents); +//! assert!(res_x509.is_ok()); +//! }, +//! _ => panic!("PEM parsing failed: {:?}", res), +//! } +//! # } +//! ``` +//! +//! Note that all methods require to store the `Pem` object in a variable, mainly because decoding +//! the PEM object requires allocation of buffers, and that the lifetime of X.509 certificates will +//! be bound to these buffers. + +use crate::certificate::X509Certificate; +use crate::error::{PEMError, X509Error}; +use crate::parse_x509_certificate; +use nom::{Err, IResult}; +use std::io::{BufRead, Cursor, Seek}; + +/// Representation of PEM data +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Pem { + /// The PEM label + pub label: String, + /// The PEM decoded data + pub contents: Vec<u8>, +} + +#[deprecated(since = "0.8.3", note = "please use `parse_x509_pem` instead")] +pub fn pem_to_der(i: &[u8]) -> IResult<&[u8], Pem, PEMError> { + parse_x509_pem(i) +} + +/// Read a PEM-encoded structure, and decode the base64 data +/// +/// Return a structure describing the PEM object: the enclosing tag, and the data. +/// Allocates a new buffer for the decoded data. +/// +/// Note that only the *first* PEM block is decoded. To iterate all blocks from PEM data, +/// use [`Pem::iter_from_buffer`]. +/// +/// For X.509 (`CERTIFICATE` tag), the data is a certificate, encoded in DER. To parse the +/// certificate content, use `Pem::parse_x509` or `parse_x509_certificate`. +pub fn parse_x509_pem(i: &[u8]) -> IResult<&'_ [u8], Pem, PEMError> { + let reader = Cursor::new(i); + let res = Pem::read(reader); + match res { + Ok((pem, bytes_read)) => Ok((&i[bytes_read..], pem)), + Err(e) => Err(Err::Error(e)), + } +} + +impl Pem { + /// Read the next PEM-encoded structure, and decode the base64 data + /// + /// Returns the certificate (encoded in DER) and the number of bytes read. + /// Allocates a new buffer for the decoded data. + /// + /// Note that a PEM file can contain multiple PEM blocks. This function returns the + /// *first* decoded object, starting from the current reader position. + /// To get all objects, call this function repeatedly until `PEMError::MissingHeader` + /// is returned. + /// + /// # Examples + /// ``` + /// let file = std::fs::File::open("assets/certificate.pem").unwrap(); + /// let subject = x509_parser::pem::Pem::read(std::io::BufReader::new(file)) + /// .unwrap().0 + /// .parse_x509().unwrap() + /// .tbs_certificate.subject.to_string(); + /// assert_eq!(subject, "CN=lists.for-our.info"); + /// ``` + pub fn read(mut r: impl BufRead + Seek) -> Result<(Pem, usize), PEMError> { + let mut line = String::new(); + let label = loop { + let num_bytes = r.read_line(&mut line)?; + if num_bytes == 0 { + // EOF + return Err(PEMError::MissingHeader); + } + if !line.starts_with("-----BEGIN ") { + line.clear(); + continue; + } + let v: Vec<&str> = line.split("-----").collect(); + if v.len() < 3 || !v[0].is_empty() { + return Err(PEMError::InvalidHeader); + } + let label = v[1].strip_prefix("BEGIN ").ok_or(PEMError::InvalidHeader)?; + break label; + }; + let label = label.split('-').next().ok_or(PEMError::InvalidHeader)?; + let mut s = String::new(); + loop { + let mut l = String::new(); + let num_bytes = r.read_line(&mut l)?; + if num_bytes == 0 { + return Err(PEMError::IncompletePEM); + } + if l.starts_with("-----END ") { + // finished reading + break; + } + s.push_str(l.trim_end()); + } + + let contents = data_encoding::BASE64 + .decode(s.as_bytes()) + .or(Err(PEMError::Base64DecodeError))?; + let pem = Pem { + label: label.to_string(), + contents, + }; + Ok((pem, r.stream_position()? as usize)) + } + + /// Decode the PEM contents into a X.509 object + pub fn parse_x509(&self) -> Result<X509Certificate, ::nom::Err<X509Error>> { + parse_x509_certificate(&self.contents).map(|(_, x509)| x509) + } + + /// Returns an iterator over the PEM-encapsulated parts of a buffer + /// + /// Only the sections enclosed in blocks starting with `-----BEGIN xxx-----` + /// and ending with `-----END xxx-----` will be considered. + /// Lines before, between or after such blocks will be ignored. + /// + /// The iterator is fallible: `next()` returns a `Result<Pem, PEMError>` object. + /// An error indicates a block is present but invalid. + /// + /// If the buffer does not contain any block, iterator will be empty. + pub fn iter_from_buffer(i: &[u8]) -> PemIterator<Cursor<&[u8]>> { + let reader = Cursor::new(i); + PemIterator { reader } + } + + /// Returns an iterator over the PEM-encapsulated parts of a reader + /// + /// Only the sections enclosed in blocks starting with `-----BEGIN xxx-----` + /// and ending with `-----END xxx-----` will be considered. + /// Lines before, between or after such blocks will be ignored. + /// + /// The iterator is fallible: `next()` returns a `Result<Pem, PEMError>` object. + /// An error indicates a block is present but invalid. + /// + /// If the reader does not contain any block, iterator will be empty. + pub fn iter_from_reader<R: BufRead + Seek>(reader: R) -> PemIterator<R> { + PemIterator { reader } + } +} + +/// Iterator over PEM-encapsulated blocks +/// +/// Only the sections enclosed in blocks starting with `-----BEGIN xxx-----` +/// and ending with `-----END xxx-----` will be considered. +/// Lines before, between or after such blocks will be ignored. +/// +/// The iterator is fallible: `next()` returns a `Result<Pem, PEMError>` object. +/// An error indicates a block is present but invalid. +/// +/// If the buffer does not contain any block, iterator will be empty. +#[allow(missing_debug_implementations)] +pub struct PemIterator<Reader: BufRead + Seek> { + reader: Reader, +} + +impl<R: BufRead + Seek> Iterator for PemIterator<R> { + type Item = Result<Pem, PEMError>; + + fn next(&mut self) -> Option<Self::Item> { + if let Ok(&[]) = self.reader.fill_buf() { + return None; + } + let reader = self.reader.by_ref(); + let r = Pem::read(reader).map(|(pem, _)| pem); + if let Err(PEMError::MissingHeader) = r { + None + } else { + Some(r) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn read_pem_from_file() { + let file = std::io::BufReader::new(std::fs::File::open("assets/certificate.pem").unwrap()); + let subject = Pem::read(file) + .unwrap() + .0 + .parse_x509() + .unwrap() + .tbs_certificate + .subject + .to_string(); + assert_eq!(subject, "CN=lists.for-our.info"); + } + + #[test] + fn pem_multi_word_label() { + const PEM_BYTES: &[u8] = + b"-----BEGIN MULTI WORD LABEL-----\n-----END MULTI WORD LABEL-----"; + let (_, pem) = parse_x509_pem(PEM_BYTES).expect("should parse pem"); + assert_eq!(pem.label, "MULTI WORD LABEL"); + } +} diff --git a/rust/vendor/x509-parser/src/prelude.rs b/rust/vendor/x509-parser/src/prelude.rs new file mode 100644 index 0000000..a1749ce --- /dev/null +++ b/rust/vendor/x509-parser/src/prelude.rs @@ -0,0 +1,18 @@ +//! A "prelude" for users of the x509-parser crate. + +pub use crate::certificate::*; +pub use crate::certification_request::*; +pub use crate::cri_attributes::*; +pub use crate::error::*; +pub use crate::extensions::*; +pub use crate::objects::*; +pub use crate::pem::*; +pub use crate::revocation_list::*; +pub use crate::time::*; +pub use crate::utils::*; +#[cfg(feature = "validate")] +pub use crate::validate::*; +pub use crate::x509::*; +pub use crate::*; + +pub use asn1_rs::FromDer; diff --git a/rust/vendor/x509-parser/src/public_key.rs b/rust/vendor/x509-parser/src/public_key.rs new file mode 100644 index 0000000..4016a9f --- /dev/null +++ b/rust/vendor/x509-parser/src/public_key.rs @@ -0,0 +1,132 @@ +use crate::error::*; +use asn1_rs::FromDer; +use der_parser::{ + der::{parse_der_integer, parse_der_sequence_defined_g}, + error::BerResult, +}; + +/// Public Key value +#[derive(Debug, PartialEq, Eq)] +pub enum PublicKey<'a> { + RSA(RSAPublicKey<'a>), + EC(ECPoint<'a>), + /// DSAPublicKey ::= INTEGER -- public key, Y (RFC 3279) + DSA(&'a [u8]), + /// GostR3410-94-PublicKey ::= OCTET STRING -- public key, Y (RFC 4491) + GostR3410(&'a [u8]), + /// GostR3410-2012-256-PublicKey ::= OCTET STRING (64), + /// GostR3410-2012-512-PublicKey ::= OCTET STRING (128). (RFC 4491-bis) + GostR3410_2012(&'a [u8]), + + Unknown(&'a [u8]), +} + +impl<'a> PublicKey<'a> { + /// Return the key size (in bits) or 0 + pub fn key_size(&self) -> usize { + match self { + Self::EC(ec) => ec.key_size(), + Self::RSA(rsa) => rsa.key_size(), + Self::DSA(y) | Self::GostR3410(y) => y.len() * 8, + _ => 0, + } + } +} + +/// RSA public Key, defined in rfc3279 +#[derive(Debug, PartialEq, Eq)] +pub struct RSAPublicKey<'a> { + /// Raw bytes of the modulus + /// + /// This possibly includes a leading 0 if the MSB is 1 + pub modulus: &'a [u8], + /// Raw bytes of the exponent + /// + /// This possibly includes a leading 0 if the MSB is 1 + pub exponent: &'a [u8], +} + +impl<'a> RSAPublicKey<'a> { + /// Attempt to convert exponent to u64 + /// + /// Returns an error if integer is too large, empty, or negative + pub fn try_exponent(&self) -> Result<u64, X509Error> { + let mut buf = [0u8; 8]; + if self.exponent.is_empty() || self.exponent[0] & 0x80 != 0 || self.exponent.len() > 8 { + return Err(X509Error::InvalidNumber); + } + buf[8_usize.saturating_sub(self.exponent.len())..].copy_from_slice(self.exponent); + let int = <u64>::from_be_bytes(buf); + Ok(int) + } + + /// Return the key size (in bits) or 0 + pub fn key_size(&self) -> usize { + if !self.modulus.is_empty() && self.modulus[0] & 0x80 == 0 { + // XXX len must substract leading zeroes + let modulus = &self.modulus[1..]; + 8 * modulus.len() + } else { + 0 + } + } +} + +// helper function to parse with error type BerError +fn parse_rsa_key(bytes: &[u8]) -> BerResult<RSAPublicKey> { + parse_der_sequence_defined_g(move |i, _| { + let (i, obj_modulus) = parse_der_integer(i)?; + let (i, obj_exponent) = parse_der_integer(i)?; + let modulus = obj_modulus.as_slice()?; + let exponent = obj_exponent.as_slice()?; + let key = RSAPublicKey { modulus, exponent }; + Ok((i, key)) + })(bytes) +} + +impl<'a> FromDer<'a, X509Error> for RSAPublicKey<'a> { + fn from_der(bytes: &'a [u8]) -> X509Result<'a, Self> { + parse_rsa_key(bytes).map_err(|_| nom::Err::Error(X509Error::InvalidSPKI)) + } +} + +/// Elliptic Curve point, as defined in [RFC5480](https://datatracker.ietf.org/doc/html/rfc5480) +#[derive(Debug, PartialEq, Eq)] +pub struct ECPoint<'a> { + data: &'a [u8], +} + +impl<'a> ECPoint<'a> { + /// EC Point content (See Standards for Efficient Cryptography Group (SECG), "SEC1: Elliptic Curve Cryptography") + pub fn data(&'a self) -> &'a [u8] { + self.data + } + + /// Return the key size (in bits) or 0 + pub fn key_size(&self) -> usize { + match self.data { + [] => { + // empty + 0 + } + [4, rem @ ..] => { + // uncompressed + rem.len() * 8 / 2 + } + [2..=3, rem @ ..] => { + // compressed + rem.len() * 8 + } + _ => { + // invalid + 0 + } + } + } +} + +impl<'a> From<&'a [u8]> for ECPoint<'a> { + fn from(data: &'a [u8]) -> Self { + ECPoint { data } + } +} diff --git a/rust/vendor/x509-parser/src/revocation_list.rs b/rust/vendor/x509-parser/src/revocation_list.rs new file mode 100644 index 0000000..55cf8f3 --- /dev/null +++ b/rust/vendor/x509-parser/src/revocation_list.rs @@ -0,0 +1,365 @@ +use crate::error::{X509Error, X509Result}; +use crate::extensions::*; +use crate::time::ASN1Time; +use crate::utils::format_serial; +use crate::x509::{ + parse_serial, parse_signature_value, AlgorithmIdentifier, ReasonCode, X509Name, X509Version, +}; + +#[cfg(feature = "verify")] +use crate::verify::verify_signature; +#[cfg(feature = "verify")] +use crate::x509::SubjectPublicKeyInfo; +use asn1_rs::{BitString, FromDer}; +use der_parser::ber::Tag; +use der_parser::der::*; +use der_parser::num_bigint::BigUint; +use der_parser::oid::Oid; +use nom::combinator::{all_consuming, complete, map, opt}; +use nom::multi::many0; +use nom::Offset; +use oid_registry::*; +use std::collections::HashMap; + +/// An X.509 v2 Certificate Revocation List (CRL). +/// +/// X.509 v2 CRLs are defined in [RFC5280](https://tools.ietf.org/html/rfc5280). +/// +/// # Example +/// +/// To parse a CRL and print information about revoked certificates: +/// +/// ```rust +/// use x509_parser::prelude::FromDer; +/// use x509_parser::revocation_list::CertificateRevocationList; +/// +/// # static DER: &'static [u8] = include_bytes!("../assets/example.crl"); +/// # +/// # fn main() { +/// let res = CertificateRevocationList::from_der(DER); +/// match res { +/// Ok((_rem, crl)) => { +/// for revoked in crl.iter_revoked_certificates() { +/// println!("Revoked certificate serial: {}", revoked.raw_serial_as_string()); +/// println!(" Reason: {}", revoked.reason_code().unwrap_or_default().1); +/// } +/// }, +/// _ => panic!("CRL parsing failed: {:?}", res), +/// } +/// # } +/// ``` +#[derive(Clone, Debug)] +pub struct CertificateRevocationList<'a> { + pub tbs_cert_list: TbsCertList<'a>, + pub signature_algorithm: AlgorithmIdentifier<'a>, + pub signature_value: BitString<'a>, +} + +impl<'a> CertificateRevocationList<'a> { + /// Get the version of the encoded certificate + pub fn version(&self) -> Option<X509Version> { + self.tbs_cert_list.version + } + + /// Get the certificate issuer. + #[inline] + pub fn issuer(&self) -> &X509Name { + &self.tbs_cert_list.issuer + } + + /// Get the date and time of the last (this) update. + #[inline] + pub fn last_update(&self) -> ASN1Time { + self.tbs_cert_list.this_update + } + + /// Get the date and time of the next update, if present. + #[inline] + pub fn next_update(&self) -> Option<ASN1Time> { + self.tbs_cert_list.next_update + } + + /// Return an iterator over the `RevokedCertificate` objects + pub fn iter_revoked_certificates(&self) -> impl Iterator<Item = &RevokedCertificate<'a>> { + self.tbs_cert_list.revoked_certificates.iter() + } + + /// Get the CRL extensions. + #[inline] + pub fn extensions(&self) -> &[X509Extension] { + &self.tbs_cert_list.extensions + } + + /// Get the CRL number, if present + /// + /// Note that the returned value is a `BigUint`, because of the following RFC specification: + /// <pre> + /// Given the requirements above, CRL numbers can be expected to contain long integers. CRL + /// verifiers MUST be able to handle CRLNumber values up to 20 octets. Conformant CRL issuers + /// MUST NOT use CRLNumber values longer than 20 octets. + /// </pre> + pub fn crl_number(&self) -> Option<&BigUint> { + self.extensions() + .iter() + .find(|&ext| ext.oid == OID_X509_EXT_CRL_NUMBER) + .and_then(|ext| match ext.parsed_extension { + ParsedExtension::CRLNumber(ref num) => Some(num), + _ => None, + }) + } + + /// Verify the cryptographic signature of this certificate revocation list + /// + /// `public_key` is the public key of the **signer**. + /// + /// Not all algorithms are supported, this function is limited to what `ring` supports. + #[cfg(feature = "verify")] + #[cfg_attr(docsrs, doc(cfg(feature = "verify")))] + pub fn verify_signature(&self, public_key: &SubjectPublicKeyInfo) -> Result<(), X509Error> { + verify_signature( + public_key, + &self.signature_algorithm, + &self.signature_value, + self.tbs_cert_list.raw, + ) + } +} + +/// <pre> +/// CertificateList ::= SEQUENCE { +/// tbsCertList TBSCertList, +/// signatureAlgorithm AlgorithmIdentifier, +/// signatureValue BIT STRING } +/// </pre> +impl<'a> FromDer<'a, X509Error> for CertificateRevocationList<'a> { + fn from_der(i: &'a [u8]) -> X509Result<Self> { + parse_der_sequence_defined_g(|i, _| { + let (i, tbs_cert_list) = TbsCertList::from_der(i)?; + let (i, signature_algorithm) = AlgorithmIdentifier::from_der(i)?; + let (i, signature_value) = parse_signature_value(i)?; + let crl = CertificateRevocationList { + tbs_cert_list, + signature_algorithm, + signature_value, + }; + Ok((i, crl)) + })(i) + } +} + +/// The sequence TBSCertList contains information about the certificates that have +/// been revoked by the CA that issued the CRL. +/// +/// RFC5280 definition: +/// +/// <pre> +/// TBSCertList ::= SEQUENCE { +/// version Version OPTIONAL, +/// -- if present, MUST be v2 +/// signature AlgorithmIdentifier, +/// issuer Name, +/// thisUpdate Time, +/// nextUpdate Time OPTIONAL, +/// revokedCertificates SEQUENCE OF SEQUENCE { +/// userCertificate CertificateSerialNumber, +/// revocationDate Time, +/// crlEntryExtensions Extensions OPTIONAL +/// -- if present, version MUST be v2 +/// } OPTIONAL, +/// crlExtensions [0] EXPLICIT Extensions OPTIONAL +/// -- if present, version MUST be v2 +/// } +/// </pre> +#[derive(Clone, Debug, PartialEq)] +pub struct TbsCertList<'a> { + pub version: Option<X509Version>, + pub signature: AlgorithmIdentifier<'a>, + pub issuer: X509Name<'a>, + pub this_update: ASN1Time, + pub next_update: Option<ASN1Time>, + pub revoked_certificates: Vec<RevokedCertificate<'a>>, + extensions: Vec<X509Extension<'a>>, + pub(crate) raw: &'a [u8], +} + +impl<'a> TbsCertList<'a> { + /// Returns the certificate extensions + #[inline] + pub fn extensions(&self) -> &[X509Extension] { + &self.extensions + } + + /// Returns an iterator over the certificate extensions + #[inline] + pub fn iter_extensions(&self) -> impl Iterator<Item = &X509Extension> { + 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> { + 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>, 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) + }) + } +} + +impl<'a> AsRef<[u8]> for TbsCertList<'a> { + fn as_ref(&self) -> &[u8] { + self.raw + } +} + +impl<'a> FromDer<'a, X509Error> for TbsCertList<'a> { + fn from_der(i: &'a [u8]) -> X509Result<Self> { + let start_i = i; + parse_der_sequence_defined_g(move |i, _| { + let (i, version) = + opt(map(parse_der_u32, X509Version))(i).or(Err(X509Error::InvalidVersion))?; + let (i, signature) = AlgorithmIdentifier::from_der(i)?; + let (i, issuer) = X509Name::from_der(i)?; + let (i, this_update) = ASN1Time::from_der(i)?; + let (i, next_update) = ASN1Time::from_der_opt(i)?; + let (i, revoked_certificates) = opt(complete(parse_revoked_certificates))(i)?; + let (i, extensions) = parse_extensions(i, Tag(0))?; + let len = start_i.offset(i); + let tbs = TbsCertList { + version, + signature, + issuer, + this_update, + next_update, + revoked_certificates: revoked_certificates.unwrap_or_default(), + extensions, + raw: &start_i[..len], + }; + Ok((i, tbs)) + })(i) + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct RevokedCertificate<'a> { + /// The Serial number of the revoked certificate + pub user_certificate: BigUint, + /// The date on which the revocation occurred is specified. + pub revocation_date: ASN1Time, + /// Additional information about revocation + extensions: Vec<X509Extension<'a>>, + pub(crate) raw_serial: &'a [u8], +} + +impl<'a> RevokedCertificate<'a> { + /// Return the serial number of the revoked certificate + pub fn serial(&self) -> &BigUint { + &self.user_certificate + } + + /// Get the CRL entry extensions. + #[inline] + pub fn extensions(&self) -> &[X509Extension] { + &self.extensions + } + + /// Returns an iterator over the CRL entry extensions + #[inline] + pub fn iter_extensions(&self) -> impl Iterator<Item = &X509Extension> { + self.extensions.iter() + } + + /// Searches for a CRL entry 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> { + self.extensions.iter().find(|&ext| ext.oid == *oid) + } + + /// Builds and returns a map of CRL entry extensions. + /// + /// If an extension is present twice, this will fail and return `DuplicateExtensions`. + pub fn extensions_map(&self) -> Result<HashMap<Oid, &X509Extension>, 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) + }) + } + + /// Get the raw bytes of the certificate serial number + pub fn raw_serial(&self) -> &[u8] { + self.raw_serial + } + + /// Get a formatted string of the certificate serial number, separated by ':' + pub fn raw_serial_as_string(&self) -> String { + format_serial(self.raw_serial) + } + + /// Get the code identifying the reason for the revocation, if present + pub fn reason_code(&self) -> Option<(bool, ReasonCode)> { + self.find_extension(&OID_X509_EXT_REASON_CODE) + .and_then(|ext| match ext.parsed_extension { + ParsedExtension::ReasonCode(code) => Some((ext.critical, code)), + _ => None, + }) + } + + /// Get the invalidity date, if present + /// + /// The invalidity date is the date on which it is known or suspected that the private + /// key was compromised or that the certificate otherwise became invalid. + pub fn invalidity_date(&self) -> Option<(bool, ASN1Time)> { + self.find_extension(&OID_X509_EXT_INVALIDITY_DATE) + .and_then(|ext| match ext.parsed_extension { + ParsedExtension::InvalidityDate(date) => Some((ext.critical, date)), + _ => None, + }) + } +} + +// revokedCertificates SEQUENCE OF SEQUENCE { +// userCertificate CertificateSerialNumber, +// revocationDate Time, +// crlEntryExtensions Extensions OPTIONAL +// -- if present, MUST be v2 +// } OPTIONAL, +impl<'a> FromDer<'a, X509Error> for RevokedCertificate<'a> { + fn from_der(i: &'a [u8]) -> X509Result<Self> { + parse_der_sequence_defined_g(|i, _| { + let (i, (raw_serial, user_certificate)) = parse_serial(i)?; + let (i, revocation_date) = ASN1Time::from_der(i)?; + let (i, extensions) = opt(complete(parse_extension_sequence))(i)?; + let revoked = RevokedCertificate { + user_certificate, + revocation_date, + extensions: extensions.unwrap_or_default(), + raw_serial, + }; + Ok((i, revoked)) + })(i) + } +} + +fn parse_revoked_certificates(i: &[u8]) -> X509Result<Vec<RevokedCertificate>> { + parse_der_sequence_defined_g(|a, _| { + all_consuming(many0(complete(RevokedCertificate::from_der)))(a) + })(i) +} diff --git a/rust/vendor/x509-parser/src/signature_algorithm.rs b/rust/vendor/x509-parser/src/signature_algorithm.rs new file mode 100644 index 0000000..24a37a4 --- /dev/null +++ b/rust/vendor/x509-parser/src/signature_algorithm.rs @@ -0,0 +1,326 @@ +use crate::error::X509Error; +use crate::x509::AlgorithmIdentifier; +use asn1_rs::{ + oid, Any, CheckDerConstraints, Class, DerAutoDerive, Error, FromDer, Oid, OptTaggedExplicit, + OptTaggedParser, Tag, +}; +use core::convert::TryFrom; +use oid_registry::*; + +#[allow(non_camel_case_types)] +#[derive(Debug, PartialEq)] +pub enum SignatureAlgorithm<'a> { + RSA, + RSASSA_PSS(Box<RsaSsaPssParams<'a>>), + RSAAES_OAEP(Box<RsaAesOaepParams<'a>>), + DSA, + ECDSA, + ED25519, +} + +impl<'a, 'b> TryFrom<&'b AlgorithmIdentifier<'a>> for SignatureAlgorithm<'a> { + type Error = X509Error; + + fn try_from(value: &'b AlgorithmIdentifier<'a>) -> Result<Self, Self::Error> { + if value.algorithm.starts_with(&oid! {1.2.840.113549.1.1}) { + // children of PKCS1 are all RSA + // test if RSASSA-PSS + if value.algorithm == OID_PKCS1_RSASSAPSS { + let params = match value.parameters.as_ref() { + Some(any) => any, + None => return Err(X509Error::InvalidSignatureValue), + }; + let params = RsaSsaPssParams::try_from(params) + .map_err(|_| X509Error::InvalidSignatureValue)?; + Ok(SignatureAlgorithm::RSASSA_PSS(Box::new(params))) + } else { + // rfc3279#section-2.2.1: the parameters component of that type SHALL be + // the ASN.1 type NULL + // We could enforce presence of NULL, but that would make a strict parser + // so it would best go to a verifier. + Ok(SignatureAlgorithm::RSA) + } + } else if test_ecdsa_oid(&value.algorithm) { + // parameter should be NULL - see above + Ok(SignatureAlgorithm::ECDSA) + } else if value.algorithm.starts_with(&oid! {1.2.840.10040.4}) { + // parameter should be NULL - see above + Ok(SignatureAlgorithm::DSA) + } else if value.algorithm == OID_SIG_ED25519 { + Ok(SignatureAlgorithm::ED25519) + } else if value.algorithm == oid! {1.2.840.113549.1.1.7} { + let params = match value.parameters.as_ref() { + Some(any) => any, + None => return Err(X509Error::InvalidSignatureValue), + }; + let params = + RsaAesOaepParams::try_from(params).map_err(|_| X509Error::InvalidSignatureValue)?; + Ok(SignatureAlgorithm::RSAAES_OAEP(Box::new(params))) + } else { + if cfg!(debug_assertions) { + // TODO: remove debug + eprintln!("bad Signature AlgorithmIdentifier: {}", value.algorithm); + } + Err(X509Error::InvalidSignatureValue) + } + } +} + +#[inline] +fn test_ecdsa_oid(oid: &Oid) -> bool { + // test if oid is a child from {ansi-x962 signatures} + oid.starts_with(&oid! {1.2.840.10045.4}) +} + +// RSASSA-PSS public keys [RFC4055](https://www.rfc-editor.org/rfc/rfc4055.html) + +// RSASSA-PSS-params ::= SEQUENCE { +// hashAlgorithm [0] HashAlgorithm DEFAULT +// sha1Identifier, +// maskGenAlgorithm [1] MaskGenAlgorithm DEFAULT +// mgf1SHA1Identifier, +// saltLength [2] INTEGER DEFAULT 20, +// trailerField [3] INTEGER DEFAULT 1 } +#[derive(Debug, PartialEq)] +pub struct RsaSsaPssParams<'a> { + hash_alg: Option<AlgorithmIdentifier<'a>>, + mask_gen_algorithm: Option<AlgorithmIdentifier<'a>>, + salt_length: Option<u32>, + trailer_field: Option<u32>, +} + +impl<'a> RsaSsaPssParams<'a> { + /// Get a reference to the rsa ssa pss params's hash algorithm. + pub fn hash_algorithm(&self) -> Option<&AlgorithmIdentifier> { + self.hash_alg.as_ref() + } + + /// Return the hash algorithm OID, or SHA1 if absent (RFC4055) + pub fn hash_algorithm_oid(&self) -> &'a Oid { + const SHA1: &Oid = &OID_HASH_SHA1; + self.hash_alg + .as_ref() + .map(|alg| &alg.algorithm) + .unwrap_or(SHA1) + } + + /// Get a reference to the rsa ssa pss params's mask generation algorithm. + pub fn mask_gen_algorithm_raw(&self) -> Option<&AlgorithmIdentifier> { + self.mask_gen_algorithm.as_ref() + } + + /// Get the rsa ssa pss params's mask generation algorithm. + /// + /// If the algorithm encoding is invalid, raise an error `InvalidAlgorithmIdentifier` + pub fn mask_gen_algorithm(&self) -> Result<MaskGenAlgorithm, X509Error> { + match self.mask_gen_algorithm.as_ref() { + Some(alg) => { + let (_, hash) = alg + .parameters() + .and_then(|any| Oid::from_der(any.data).ok()) + .ok_or(X509Error::InvalidAlgorithmIdentifier)?; + Ok(MaskGenAlgorithm::new(alg.algorithm.clone(), hash)) + } + _ => { + Ok(MaskGenAlgorithm::new( + oid! {1.2.840.113549.1.1.8}, // id-mgf1 + OID_HASH_SHA1, + )) + } + } + } + + /// Return the salt length + pub fn salt_length(&self) -> u32 { + self.salt_length.unwrap_or(20) + } + + /// Return the trailer field (value must be `1` according to RFC4055) + pub fn trailer_field(&self) -> u32 { + self.trailer_field.unwrap_or(1) + } +} + +impl<'a> TryFrom<Any<'a>> for RsaSsaPssParams<'a> { + type Error = X509Error; + + fn try_from(value: Any<'a>) -> Result<Self, Self::Error> { + Self::try_from(&value) + } +} + +impl<'a, 'b> TryFrom<&'b Any<'a>> for RsaSsaPssParams<'a> { + type Error = X509Error; + + fn try_from(value: &'b Any<'a>) -> Result<Self, Self::Error> { + value.tag().assert_eq(Tag::Sequence)?; + let i = &value.data; + // let (i, hash_alg) = OptTaggedExplicit::<_, X509Error, 0>::from_der(i)?; + let (i, hash_alg) = OptTaggedParser::new(Class::ContextSpecific, Tag(0)) + .parse_der(i, |_, inner| AlgorithmIdentifier::from_der(inner))?; + // let (i, mask_gen_algorithm) = OptTaggedExplicit::<_, Error, 1>::from_der(i)?; + let (i, mask_gen_algorithm) = OptTaggedParser::new(Class::ContextSpecific, Tag(1)) + .parse_der(i, |_, inner| AlgorithmIdentifier::from_der(inner))?; + let (i, salt_length) = OptTaggedExplicit::<_, Error, 2>::from_der(i)?; + let (_, trailer_field) = OptTaggedExplicit::<_, Error, 3>::from_der(i)?; + let params = RsaSsaPssParams { + hash_alg, + mask_gen_algorithm, + salt_length: salt_length.map(|t| t.into_inner()), + trailer_field: trailer_field.map(|t| t.into_inner()), + }; + Ok(params) + } +} + +impl CheckDerConstraints for RsaSsaPssParams<'_> { + fn check_constraints(any: &Any) -> asn1_rs::Result<()> { + any.header.assert_constructed()?; + Ok(()) + } +} + +impl DerAutoDerive for RsaSsaPssParams<'_> {} + +#[derive(Debug, PartialEq, Eq)] +pub struct MaskGenAlgorithm<'a, 'b> { + pub mgf: Oid<'a>, + pub hash: Oid<'b>, +} + +impl<'a, 'b> MaskGenAlgorithm<'a, 'b> { + pub const fn new(mgf: Oid<'a>, hash: Oid<'b>) -> Self { + Self { mgf, hash } + } +} + +// RSAAES-OAEP public keys [RFC8017](https://www.rfc-editor.org/rfc/rfc8017.html) + +// RSAES-OAEP-params ::= SEQUENCE { +// hashFunc [0] AlgorithmIdentifier DEFAULT +// sha1Identifier, +// maskGenFunc [1] AlgorithmIdentifier DEFAULT +// mgf1SHA1Identifier, +// pSourceFunc [2] AlgorithmIdentifier DEFAULT +// pSpecifiedEmptyIdentifier } +// +// pSpecifiedEmptyIdentifier AlgorithmIdentifier ::= +// { id-pSpecified, nullOctetString } +// +// nullOctetString OCTET STRING (SIZE (0)) ::= { ''H } +#[derive(Debug, PartialEq)] +pub struct RsaAesOaepParams<'a> { + hash_alg: Option<AlgorithmIdentifier<'a>>, + mask_gen_alg: Option<AlgorithmIdentifier<'a>>, + p_source_alg: Option<AlgorithmIdentifier<'a>>, +} + +impl<'a> RsaAesOaepParams<'a> { + pub const EMPTY: &'static AlgorithmIdentifier<'static> = &AlgorithmIdentifier::new( + oid! {1.2.840.113549.1.1.9}, // id-pSpecified + None, + ); + + /// Get a reference to the rsa aes oaep params's hash algorithm. + pub fn hash_algorithm(&self) -> Option<&AlgorithmIdentifier> { + self.hash_alg.as_ref() + } + + /// Return the hash algorithm OID, or SHA1 if absent (RFC4055) + pub fn hash_algorithm_oid(&self) -> &'a Oid { + const SHA1: &Oid = &OID_HASH_SHA1; + self.hash_alg + .as_ref() + .map(|alg| &alg.algorithm) + .unwrap_or(SHA1) + } + + /// Get a reference to the rsa ssa pss params's mask generation algorithm. + pub fn mask_gen_algorithm_raw(&self) -> Option<&AlgorithmIdentifier> { + self.mask_gen_alg.as_ref() + } + + /// Get the rsa ssa pss params's mask generation algorithm. + /// + /// If the algorithm encoding is invalid, raise an error `InvalidAlgorithmIdentifier` + pub fn mask_gen_algorithm(&self) -> Result<MaskGenAlgorithm, X509Error> { + match self.mask_gen_alg.as_ref() { + Some(alg) => { + let (_, hash) = alg + .parameters() + .and_then(|any| Oid::from_der(any.data).ok()) + .ok_or(X509Error::InvalidAlgorithmIdentifier)?; + Ok(MaskGenAlgorithm::new(alg.algorithm.clone(), hash)) + } + _ => { + Ok(MaskGenAlgorithm::new( + oid! {1.2.840.113549.1.1.8}, // id-mgf1 + OID_HASH_SHA1, + )) + } + } + } + + /// Return the pSourceFunc algorithm + pub fn p_source_alg(&'a self) -> &'a AlgorithmIdentifier { + self.p_source_alg.as_ref().unwrap_or(Self::EMPTY) + } +} + +impl<'a> TryFrom<Any<'a>> for RsaAesOaepParams<'a> { + type Error = X509Error; + + fn try_from(value: Any<'a>) -> Result<Self, Self::Error> { + Self::try_from(&value) + } +} + +// hashFunc [0] AlgorithmIdentifier DEFAULT +// sha1Identifier, +// maskGenFunc [1] AlgorithmIdentifier DEFAULT +// mgf1SHA1Identifier, +// pSourceFunc [2] AlgorithmIdentifier DEFAULT +// pSpecifiedEmptyIdentifier } +impl<'a, 'b> TryFrom<&'b Any<'a>> for RsaAesOaepParams<'a> { + type Error = X509Error; + + fn try_from(value: &'b Any<'a>) -> Result<Self, Self::Error> { + value.tag().assert_eq(Tag::Sequence)?; + let i = &value.data; + // let (i, hash_alg) = OptTaggedExplicit::<_, X509Error, 0>::from_der(i)?; + let (i, hash_alg) = OptTaggedParser::new(Class::ContextSpecific, Tag(0)) + .parse_der(i, |_, inner| AlgorithmIdentifier::from_der(inner))?; + // let (i, mask_gen_algorithm) = OptTaggedExplicit::<_, Error, 1>::from_der(i)?; + let (i, mask_gen_alg) = OptTaggedParser::new(Class::ContextSpecific, Tag(1)) + .parse_der(i, |_, inner| AlgorithmIdentifier::from_der(inner))?; + let (_, p_source_alg) = OptTaggedParser::new(Class::ContextSpecific, Tag(2)) + .parse_der(i, |_, inner| AlgorithmIdentifier::from_der(inner))?; + let params = RsaAesOaepParams { + hash_alg, + mask_gen_alg, + p_source_alg, + }; + Ok(params) + } +} + +impl CheckDerConstraints for RsaAesOaepParams<'_> { + fn check_constraints(any: &Any) -> asn1_rs::Result<()> { + any.header.assert_constructed()?; + Ok(()) + } +} + +impl DerAutoDerive for RsaAesOaepParams<'_> {} + +// ECC subject public key information [RFC5480](https://datatracker.ietf.org/doc/rfc5480/) + +// ECParameters ::= CHOICE { +// namedCurve OBJECT IDENTIFIER +// -- implicitCurve NULL +// -- specifiedCurve SpecifiedECDomain +// } +// -- implicitCurve and specifiedCurve MUST NOT be used in PKIX. +// -- Details for SpecifiedECDomain can be found in [X9.62]. +// -- Any future additions to this CHOICE should be coordinated +// -- with ANSI X9. diff --git a/rust/vendor/x509-parser/src/signature_value.rs b/rust/vendor/x509-parser/src/signature_value.rs new file mode 100644 index 0000000..df13bd0 --- /dev/null +++ b/rust/vendor/x509-parser/src/signature_value.rs @@ -0,0 +1,11 @@ +use asn1_rs::{DerSequence, Integer}; + +/// ECDSA Signature Value (RFC3279) +// Ecdsa-Sig-Value ::= SEQUENCE { +// r INTEGER, +// s INTEGER } +#[derive(Debug, PartialEq, Eq, DerSequence)] +pub struct EcdsaSigValue<'a> { + pub r: Integer<'a>, + pub s: Integer<'a>, +} diff --git a/rust/vendor/x509-parser/src/time.rs b/rust/vendor/x509-parser/src/time.rs new file mode 100644 index 0000000..7d2b008 --- /dev/null +++ b/rust/vendor/x509-parser/src/time.rs @@ -0,0 +1,179 @@ +use asn1_rs::nom::Err; +use asn1_rs::{Error, FromDer, GeneralizedTime, Header, ParseResult, UtcTime}; +use der_parser::ber::{Tag, MAX_OBJECT_SIZE}; +use std::fmt; +use std::ops::{Add, Sub}; +use time::macros::format_description; +use time::{Duration, OffsetDateTime}; + +use crate::error::{X509Error, X509Result}; + +/// An ASN.1 timestamp. +#[derive(Copy, Clone, Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] +pub struct ASN1Time(OffsetDateTime); + +impl ASN1Time { + pub(crate) fn from_der_opt(i: &[u8]) -> X509Result<Option<Self>> { + if i.is_empty() { + return Ok((i, None)); + } + match parse_choice_of_time(i) { + Ok((rem, dt)) => Ok((rem, Some(ASN1Time(dt)))), + Err(Err::Error(Error::InvalidTag)) | Err(Err::Error(Error::UnexpectedTag { .. })) => { + Ok((i, None)) + } + Err(_) => Err(Err::Error(X509Error::InvalidDate)), + } + } + + #[inline] + pub const fn new(dt: OffsetDateTime) -> Self { + Self(dt) + } + + #[inline] + pub const fn to_datetime(&self) -> OffsetDateTime { + self.0 + } + + /// Makes a new `ASN1Time` from the number of non-leap seconds since Epoch + pub fn from_timestamp(secs: i64) -> Result<Self, X509Error> { + let dt = OffsetDateTime::from_unix_timestamp(secs).map_err(|_| X509Error::InvalidDate)?; + Ok(ASN1Time(dt)) + } + + /// Returns the number of non-leap seconds since January 1, 1970 0:00:00 UTC (aka "UNIX timestamp"). + #[inline] + pub fn timestamp(&self) -> i64 { + self.0.unix_timestamp() + } + + /// Returns a `ASN1Time` which corresponds to the current date. + #[inline] + pub fn now() -> Self { + ASN1Time(OffsetDateTime::now_utc()) + } + + /// Returns an RFC 2822 date and time string such as `Tue, 1 Jul 2003 10:52:37 +0200`. + /// + /// Conversion to RFC2822 date can fail if date cannot be represented in this format, + /// for example if year < 1900. + /// + /// For an infallible conversion to string, use `.to_string()`. + #[inline] + pub fn to_rfc2822(self) -> Result<String, String> { + self.0 + .format(&time::format_description::well_known::Rfc2822) + .map_err(|e| e.to_string()) + } +} + +impl<'a> FromDer<'a, X509Error> for ASN1Time { + fn from_der(i: &[u8]) -> X509Result<Self> { + let (rem, dt) = parse_choice_of_time(i).map_err(|_| X509Error::InvalidDate)?; + Ok((rem, ASN1Time(dt))) + } +} + +pub(crate) fn parse_choice_of_time(i: &[u8]) -> ParseResult<OffsetDateTime> { + if let Ok((rem, t)) = UtcTime::from_der(i) { + let dt = t.utc_adjusted_datetime()?; + return Ok((rem, dt)); + } + if let Ok((rem, t)) = GeneralizedTime::from_der(i) { + let dt = t.utc_datetime()?; + return Ok((rem, dt)); + } + parse_malformed_date(i) +} + +// allow relaxed parsing of UTCTime (ex: 370116130016+0000) +fn parse_malformed_date(i: &[u8]) -> ParseResult<OffsetDateTime> { + #[allow(clippy::trivially_copy_pass_by_ref)] + // fn check_char(b: &u8) -> bool { + // (0x20 <= *b && *b <= 0x7f) || (*b == b'+') + // } + let (_rem, hdr) = Header::from_der(i)?; + let len = hdr.length().definite()?; + if len > MAX_OBJECT_SIZE { + return Err(nom::Err::Error(Error::InvalidLength)); + } + match hdr.tag() { + Tag::UtcTime => { + // // if we are in this function, the PrintableString could not be validated. + // // Accept it without validating charset, because some tools do not respect the charset + // // restrictions (for ex. they use '*' while explicingly disallowed) + // let (rem, data) = take(len as usize)(rem)?; + // if !data.iter().all(check_char) { + // return Err(nom::Err::Error(BerError::BerValueError)); + // } + // let s = std::str::from_utf8(data).map_err(|_| BerError::BerValueError)?; + // let content = BerObjectContent::UTCTime(s); + // let obj = DerObject::from_header_and_content(hdr, content); + // Ok((rem, obj)) + Err(nom::Err::Error(Error::BerValueError)) + } + _ => Err(nom::Err::Error(Error::unexpected_tag(None, hdr.tag()))), + } +} + +impl fmt::Display for ASN1Time { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let format = format_description!("[month repr:short] [day padding:space] [hour]:[minute]:[second] [year padding:none] [offset_hour sign:mandatory]:[offset_minute]"); + let s = self + .0 + .format(format) + .unwrap_or_else(|e| format!("Invalid date: {}", e)); + f.write_str(&s) + } +} + +impl Add<Duration> for ASN1Time { + type Output = Option<ASN1Time>; + + #[inline] + fn add(self, rhs: Duration) -> Option<ASN1Time> { + Some(ASN1Time(self.0 + rhs)) + } +} + +impl Sub<ASN1Time> for ASN1Time { + type Output = Option<Duration>; + + #[inline] + fn sub(self, rhs: ASN1Time) -> Option<Duration> { + if self.0 > rhs.0 { + Some(self.0 - rhs.0) + } else { + None + } + } +} + +impl From<OffsetDateTime> for ASN1Time { + fn from(dt: OffsetDateTime) -> Self { + ASN1Time(dt) + } +} + +#[cfg(test)] +mod tests { + use time::macros::datetime; + + use super::ASN1Time; + + #[test] + fn test_time_to_string() { + let d = datetime!(1 - 1 - 1 12:34:56 UTC); + let t = ASN1Time::from(d); + assert_eq!(t.to_string(), "Jan 1 12:34:56 1 +00:00".to_string()); + } + + #[test] + fn test_nonrfc2822_date() { + // test year < 1900 + let d = datetime!(1 - 1 - 1 00:00:00 UTC); + let t = ASN1Time::from(d); + assert!(t.to_rfc2822().is_err()); + } +} diff --git a/rust/vendor/x509-parser/src/utils.rs b/rust/vendor/x509-parser/src/utils.rs new file mode 100644 index 0000000..72b4d99 --- /dev/null +++ b/rust/vendor/x509-parser/src/utils.rs @@ -0,0 +1,19 @@ +/// Formats a slice to a colon-separated hex string (for ex `01:02:ff:ff`) +pub fn format_serial(i: &[u8]) -> String { + let mut s = i.iter().fold(String::with_capacity(3 * i.len()), |a, b| { + a + &format!("{:02x}:", b) + }); + s.pop(); + s +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_format_serial() { + let b: &[u8] = &[1, 2, 3, 4, 0xff]; + assert_eq!("01:02:03:04:ff", format_serial(b)); + } +} diff --git a/rust/vendor/x509-parser/src/validate/certificate.rs b/rust/vendor/x509-parser/src/validate/certificate.rs new file mode 100644 index 0000000..d375a80 --- /dev/null +++ b/rust/vendor/x509-parser/src/validate/certificate.rs @@ -0,0 +1,17 @@ +use crate::certificate::*; +use crate::validate::*; + +use extensions::X509ExtensionsValidator; + +#[derive(Debug)] +pub struct X509CertificateValidator; + +impl<'a> Validator<'a> for X509CertificateValidator { + type Item = X509Certificate<'a>; + + fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool { + let mut res = true; + res &= X509ExtensionsValidator.validate(&item.extensions(), l); + res + } +} diff --git a/rust/vendor/x509-parser/src/validate/extensions.rs b/rust/vendor/x509-parser/src/validate/extensions.rs new file mode 100644 index 0000000..a838f6e --- /dev/null +++ b/rust/vendor/x509-parser/src/validate/extensions.rs @@ -0,0 +1,115 @@ +use crate::extensions::*; +use crate::validate::*; +use std::collections::HashSet; + +// extra-pedantic checks + +const WARN_SHOULD_BE_CRITICAL: bool = false; + +macro_rules! test_critical { + (MUST $ext:ident, $l:ident, $name:expr) => { + if !$ext.critical { + $l.err(&format!("Extension {} MUST be critical, but is not", $name)); + } + }; + (MUST NOT $ext:ident, $l:ident, $name:expr) => { + if $ext.critical { + $l.err(&format!("Extension {} MUST NOT be critical, but is", $name)); + } + }; + (SHOULD $ext:ident, $l:ident, $name:expr) => { + if WARN_SHOULD_BE_CRITICAL && !$ext.critical { + $l.warn(&format!( + "Extension {} SHOULD be critical, but is not", + $name + )); + } + }; + (SHOULD NOT $ext:ident, $l:ident, $name:expr) => { + if WARN_SHOULD_BE_CRITICAL && $ext.critical { + $l.warn(&format!( + "Extension {} SHOULD NOT be critical, but is", + $name + )); + } + }; +} + +#[derive(Debug)] +pub struct X509ExtensionsValidator; + +impl<'a> Validator<'a> for X509ExtensionsValidator { + type Item = &'a [X509Extension<'a>]; + + fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool { + let mut res = true; + // check for duplicate extensions + { + let mut m = HashSet::new(); + for ext in item.iter() { + if m.contains(&ext.oid) { + l.err(&format!("Duplicate extension {}", ext.oid)); + res = false; + } else { + m.insert(ext.oid.clone()); + } + } + } + + for ext in item.iter() { + // specific extension checks + match ext.parsed_extension() { + ParsedExtension::AuthorityKeyIdentifier(aki) => { + // Conforming CAs MUST mark this extension as non-critical + test_critical!(MUST NOT ext, l, "AKI"); + // issuer or serial is present must be either both present or both absent + if aki.authority_cert_issuer.is_some() ^ aki.authority_cert_serial.is_some() { + l.warn("AKI: only one of Issuer and Serial is present"); + } + } + ParsedExtension::CertificatePolicies(policies) => { + // A certificate policy OID MUST NOT appear more than once in a + // certificate policies extension. + let mut policy_oids = HashSet::new(); + for policy_info in policies { + if policy_oids.contains(&policy_info.policy_id) { + l.err(&format!( + "Certificate Policies: duplicate policy {}", + policy_info.policy_id + )); + res = false; + } else { + policy_oids.insert(policy_info.policy_id.clone()); + } + } + } + ParsedExtension::KeyUsage(ku) => { + // SHOULD be critical + test_critical!(SHOULD ext, l, "KeyUsage"); + // When the keyUsage extension appears in a certificate, at least one of the bits + // MUST be set to 1. + if ku.flags == 0 { + l.err("KeyUsage: all flags are set to 0"); + } + } + ParsedExtension::SubjectAlternativeName(san) => { + // SHOULD be non-critical + test_critical!(SHOULD NOT ext, l, "SubjectAltName"); + 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) { + l.warn(&format!("Invalid charset in 'SAN' entry '{}'", s)); + } + } + _ => (), + } + } + } + _ => (), + } + } + res + } +} diff --git a/rust/vendor/x509-parser/src/validate/loggers.rs b/rust/vendor/x509-parser/src/validate/loggers.rs new file mode 100644 index 0000000..0052f8d --- /dev/null +++ b/rust/vendor/x509-parser/src/validate/loggers.rs @@ -0,0 +1,83 @@ +pub trait Logger { + fn warn(&mut self, message: &str); + + fn err(&mut self, message: &str); +} + +/// Simple Logger for [`Validator`](crate::validate::Validator) trait, storing messages in `Vec` +#[derive(Debug, Default)] +pub struct VecLogger { + warnings: Vec<String>, + errors: Vec<String>, +} + +impl VecLogger { + /// Get stored warnings + pub fn warnings(&self) -> &[String] { + &self.warnings + } + + /// Get stored errors + pub fn errors(&self) -> &[String] { + &self.errors + } +} + +impl Logger for VecLogger { + fn warn(&mut self, message: &str) { + self.warnings.push(message.to_owned()) + } + + fn err(&mut self, message: &str) { + self.errors.push(message.to_owned()) + } +} + +/// Simple Logger for [`Validator`](crate::validate::Validator) trait, printing messages to `stderr` +#[derive(Debug, Default)] +pub struct StderrLogger; + +impl Logger for StderrLogger { + fn warn(&mut self, message: &str) { + eprintln!("[W] {}", message); + } + + fn err(&mut self, message: &str) { + eprintln!("[E] {}", message); + } +} + +/// Simple Logger for [`Validator`](crate::validate::Validator) trait, using closures for `warn`/`err`. +#[derive(Debug, Default)] +pub struct CallbackLogger<W, E> +where + W: FnMut(&str), + E: FnMut(&str), +{ + warn: W, + err: E, +} + +impl<W, E> CallbackLogger<W, E> +where + W: FnMut(&str), + E: FnMut(&str), +{ + pub fn new(warn: W, err: E) -> Self { + CallbackLogger { warn, err } + } +} + +impl<W, E> Logger for CallbackLogger<W, E> +where + W: FnMut(&str), + E: FnMut(&str), +{ + fn warn(&mut self, message: &str) { + (self.warn)(message); + } + + fn err(&mut self, message: &str) { + (self.err)(message); + } +} diff --git a/rust/vendor/x509-parser/src/validate/mod.rs b/rust/vendor/x509-parser/src/validate/mod.rs new file mode 100644 index 0000000..a2cba26 --- /dev/null +++ b/rust/vendor/x509-parser/src/validate/mod.rs @@ -0,0 +1,262 @@ +mod certificate; +mod extensions; +mod loggers; +mod name; +mod structure; +use std::marker::PhantomData; + +pub use certificate::*; +pub use extensions::*; +pub use loggers::*; +pub use name::*; +pub use structure::*; + +/// Trait for validating item (for ex. validate X.509 structure) +/// +/// # Examples +/// +/// Using callbacks: +/// +/// ``` +/// use x509_parser::certificate::X509Certificate; +/// # #[allow(deprecated)] +/// use x509_parser::validate::Validate; +/// # #[allow(deprecated)] +/// #[cfg(feature = "validate")] +/// fn validate_certificate(x509: &X509Certificate<'_>) -> Result<(), &'static str> { +/// println!(" Subject: {}", x509.subject()); +/// // validate and print warnings and errors to stderr +/// let ok = x509.validate( +/// |msg| { +/// eprintln!(" [W] {}", msg); +/// }, +/// |msg| { +/// eprintln!(" [E] {}", msg); +/// }, +/// ); +/// print!("Structure validation status: "); +/// if ok { +/// println!("Ok"); +/// Ok(()) +/// } else { +/// println!("FAIL"); +/// Err("validation failed") +/// } +/// } +/// ``` +/// +/// Collecting warnings and errors to `Vec`: +/// +/// ``` +/// use x509_parser::certificate::X509Certificate; +/// # #[allow(deprecated)] +/// use x509_parser::validate::Validate; +/// +/// # #[allow(deprecated)] +/// #[cfg(feature = "validate")] +/// fn validate_certificate(x509: &X509Certificate<'_>) -> Result<(), &'static str> { +/// println!(" Subject: {}", x509.subject()); +/// // validate and print warnings and errors to stderr +/// let (ok, warnings, errors) = x509.validate_to_vec(); +/// print!("Structure validation status: "); +/// if ok { +/// println!("Ok"); +/// } else { +/// println!("FAIL"); +/// } +/// for warning in &warnings { +/// eprintln!(" [W] {}", warning); +/// } +/// for error in &errors { +/// eprintln!(" [E] {}", error); +/// } +/// println!(); +/// if !errors.is_empty() { +/// return Err("validation failed"); +/// } +/// Ok(()) +/// } +/// ``` +#[deprecated(since = "0.13.0", note = "please use `X509StructureValidator` instead")] +pub trait Validate { + /// Attempts to validate current item. + /// + /// Returns `true` if item was validated. + /// + /// Call `warn()` if a non-fatal error was encountered, and `err()` + /// if the error is fatal. These fucntions receive a description of the error. + fn validate<W, E>(&self, warn: W, err: E) -> bool + where + W: FnMut(&str), + E: FnMut(&str); + + /// Attempts to validate current item, storing warning and errors in `Vec`. + /// + /// Returns the validation result (`true` if validated), the list of warnings, + /// and the list of errors. + fn validate_to_vec(&self) -> (bool, Vec<String>, Vec<String>) { + let mut warn_list = Vec::new(); + let mut err_list = Vec::new(); + let res = self.validate( + |s| warn_list.push(s.to_owned()), + |s| err_list.push(s.to_owned()), + ); + (res, warn_list, err_list) + } +} + +/// Trait for build item validators (for ex. validate X.509 structure) +/// +/// See [`X509StructureValidator`] for a default implementation, validating the +/// DER structure of a X.509 Certificate. +/// +/// See implementors of the [`Logger`] trait for methods to collect or handle warnings and errors. +/// +/// # Examples +/// +/// Collecting warnings and errors to `Vec`: +/// +/// ``` +/// use x509_parser::certificate::X509Certificate; +/// use x509_parser::validate::*; +/// +/// # #[allow(deprecated)] +/// #[cfg(feature = "validate")] +/// fn validate_certificate(x509: &X509Certificate<'_>) -> Result<(), &'static str> { +/// let mut logger = VecLogger::default(); +/// println!(" Subject: {}", x509.subject()); +/// // validate and print warnings and errors to stderr +/// let ok = X509StructureValidator.validate(&x509, &mut logger); +/// print!("Structure validation status: "); +/// if ok { +/// println!("Ok"); +/// } else { +/// println!("FAIL"); +/// } +/// for warning in logger.warnings() { +/// eprintln!(" [W] {}", warning); +/// } +/// for error in logger.errors() { +/// eprintln!(" [E] {}", error); +/// } +/// println!(); +/// if !logger.errors().is_empty() { +/// return Err("validation failed"); +/// } +/// Ok(()) +/// } +/// ``` +pub trait Validator<'a> { + /// The item to validate + type Item; + + /// Attempts to validate current item. + /// + /// Returns `true` if item was validated. + /// + /// Call `l.warn()` if a non-fatal error was encountered, and `l.err()` + /// if the error is fatal. These functions receive a description of the error. + fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool; + + fn chain<V2>(self, v2: V2) -> ChainValidator<'a, Self, V2, Self::Item> + where + Self: Sized, + V2: Validator<'a, Item = Self::Item>, + { + ChainValidator { + v1: self, + v2, + _p: PhantomData, + } + } +} + +#[derive(Debug)] +pub struct ChainValidator<'a, A, B, I> +where + A: Validator<'a, Item = I>, + B: Validator<'a, Item = I>, +{ + v1: A, + v2: B, + _p: PhantomData<&'a ()>, +} + +impl<'a, A, B, I> Validator<'a> for ChainValidator<'a, A, B, I> +where + A: Validator<'a, Item = I>, + B: Validator<'a, Item = I>, +{ + type Item = I; + + fn validate<L: Logger>(&'_ self, item: &'a Self::Item, l: &'_ mut L) -> bool { + self.v1.validate(item, l) & self.v2.validate(item, l) + } +} + +#[allow(deprecated)] +#[cfg(test)] +mod tests { + use crate::validate::*; + + struct V1 { + a: u32, + } + + impl Validate for V1 { + fn validate<W, E>(&self, mut warn: W, _err: E) -> bool + where + W: FnMut(&str), + E: FnMut(&str), + { + if self.a > 10 { + warn("a is greater than 10"); + } + true + } + } + + struct V1Validator; + + impl<'a> Validator<'a> for V1Validator { + type Item = V1; + + fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool { + if item.a > 10 { + l.warn("a is greater than 10"); + } + true + } + } + + #[test] + fn validate_warn() { + let v1 = V1 { a: 1 }; + let (res, warn, err) = v1.validate_to_vec(); + assert!(res); + assert!(warn.is_empty()); + assert!(err.is_empty()); + // same, with one warning + let v20 = V1 { a: 20 }; + let (res, warn, err) = v20.validate_to_vec(); + assert!(res); + assert_eq!(warn, vec!["a is greater than 10".to_string()]); + assert!(err.is_empty()); + } + + #[test] + fn validator_warn() { + let mut logger = VecLogger::default(); + let v1 = V1 { a: 1 }; + let res = V1Validator.validate(&v1, &mut logger); + assert!(res); + assert!(logger.warnings().is_empty()); + assert!(logger.errors().is_empty()); + // same, with one warning + let v20 = V1 { a: 20 }; + let res = V1Validator.validate(&v20, &mut logger); + assert!(res); + assert_eq!(logger.warnings(), &["a is greater than 10".to_string()]); + assert!(logger.errors().is_empty()); + } +} diff --git a/rust/vendor/x509-parser/src/validate/name.rs b/rust/vendor/x509-parser/src/validate/name.rs new file mode 100644 index 0000000..569f157 --- /dev/null +++ b/rust/vendor/x509-parser/src/validate/name.rs @@ -0,0 +1,32 @@ +use crate::validate::*; +use crate::x509::*; +use asn1_rs::Tag; + +#[derive(Debug)] +pub struct X509NameStructureValidator; + +impl<'a> Validator<'a> for X509NameStructureValidator { + type Item = X509Name<'a>; + + fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool { + let res = true; + // subject/issuer: verify charsets + // - wildcards in PrintableString + // - non-IA5 in IA5String + for attr in item.iter_attributes() { + match attr.attr_value().tag() { + Tag::PrintableString | Tag::Ia5String => { + let b = attr.attr_value().as_bytes(); + if !b.iter().all(u8::is_ascii) { + l.warn(&format!( + "Invalid charset in X.509 Name, component {}", + attr.attr_type() + )); + } + } + _ => (), + } + } + res + } +} diff --git a/rust/vendor/x509-parser/src/validate/structure.rs b/rust/vendor/x509-parser/src/validate/structure.rs new file mode 100644 index 0000000..191a3d7 --- /dev/null +++ b/rust/vendor/x509-parser/src/validate/structure.rs @@ -0,0 +1,160 @@ +use super::{Logger, Validator, X509NameStructureValidator}; +use crate::certificate::*; +use crate::extensions::{GeneralName, ParsedExtension}; +use crate::public_key::PublicKey; +use crate::x509::{SubjectPublicKeyInfo, X509Version}; + +/// Default X.509 structure validator for `X509Certificate` +/// +/// This [`Validator`] iterates the X.509 Certificate fields, and verifies the +/// DER encoding and structure: +/// - numbers with wrong encoding/sign (for ex. serial number) +/// - strings with characters not allowed in DER type (for ex. '*' in `PrintableString`) +/// +/// # Examples +/// +/// Validate structure, collect warnings and errors to a `Vec`: +/// +/// ``` +/// use x509_parser::certificate::X509Certificate; +/// use x509_parser::validate::*; +/// +/// # #[allow(deprecated)] +/// #[cfg(feature = "validate")] +/// fn validate_certificate(x509: &X509Certificate<'_>) -> Result<(), &'static str> { +/// let mut logger = VecLogger::default(); +/// println!(" Subject: {}", x509.subject()); +/// // validate and print warnings and errors to stderr +/// let ok = X509StructureValidator.validate(&x509, &mut logger); +/// print!("Structure validation status: "); +/// if ok { +/// println!("Ok"); +/// } else { +/// println!("FAIL"); +/// } +/// for warning in logger.warnings() { +/// eprintln!(" [W] {}", warning); +/// } +/// for error in logger.errors() { +/// eprintln!(" [E] {}", error); +/// } +/// println!(); +/// if !logger.errors().is_empty() { +/// return Err("validation failed"); +/// } +/// Ok(()) +/// } +/// ``` +#[derive(Debug, Default)] +pub struct X509StructureValidator; + +impl<'a> Validator<'a> for X509StructureValidator { + type Item = X509Certificate<'a>; + + fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool { + let mut res = true; + res &= TbsCertificateStructureValidator.validate(&item.tbs_certificate, l); + res + } +} + +/// Default X.509 structure validator for `TbsCertificate` +#[derive(Debug, Default)] +pub struct TbsCertificateStructureValidator; + +impl<'a> Validator<'a> for TbsCertificateStructureValidator { + type Item = TbsCertificate<'a>; + + fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool { + let mut res = true; + // version must be 0, 1 or 2 + if item.version.0 >= 3 { + l.err("Invalid version"); + res = false; + } + // extensions require v3 + if !item.extensions().is_empty() && item.version != X509Version::V3 { + l.err("Extensions present but version is not 3"); + res = false; + } + let b = item.raw_serial(); + if b.is_empty() { + l.err("Serial is empty"); + res = false; + } else { + // check MSB of serial + if b[0] & 0x80 != 0 { + l.warn("Serial number is negative"); + } + // check leading zeroes in serial + if b.len() > 1 && b[0] == 0 && b[1] & 0x80 == 0 { + l.warn("Leading zeroes in serial number"); + } + } + // subject/issuer: verify charsets + res &= X509NameStructureValidator.validate(&item.subject, l); + res &= X509NameStructureValidator.validate(&item.issuer, l); + // subject public key + res &= X509PublicKeyValidator.validate(&item.subject_pki, l); + // check for parse errors or unsupported extensions + for ext in item.extensions() { + if let ParsedExtension::UnsupportedExtension { .. } = &ext.parsed_extension { + l.warn(&format!("Unsupported extension {}", ext.oid)); + } + if let ParsedExtension::ParseError { error } = &ext.parsed_extension { + l.err(&format!("Parse error in extension {}: {}", ext.oid, error)); + res = false; + } + } + // check extensions + for ext in item.extensions() { + // 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) { + l.warn(&format!("Invalid charset in 'SAN' entry '{}'", s)); + } + } + _ => (), + } + } + } + } + res + } +} + +#[derive(Debug, Default)] +pub struct X509PublicKeyValidator; + +impl<'a> Validator<'a> for X509PublicKeyValidator { + type Item = SubjectPublicKeyInfo<'a>; + + fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool { + let mut res = true; + // res &= TbsCertificateStructureValidator.validate(&item.tbs_certificate, l); + match item.parsed() { + Ok(PublicKey::RSA(rsa)) => { + if rsa.modulus[0] & 0x80 != 0 { + l.warn("Public key: (RSA) modulus is negative"); + } + if rsa.exponent[0] & 0x80 != 0 { + l.warn("Public key: (RSA) exponent is negative"); + } + } + Ok(PublicKey::Unknown(_b)) => { + l.warn("Unknown public key type"); + } + Ok(_) => {} + Err(_) => { + l.err("Invalid public key"); + res = false; + } + } + res + } +} diff --git a/rust/vendor/x509-parser/src/verify.rs b/rust/vendor/x509-parser/src/verify.rs new file mode 100644 index 0000000..c056d77 --- /dev/null +++ b/rust/vendor/x509-parser/src/verify.rs @@ -0,0 +1,78 @@ +use crate::prelude::*; +use asn1_rs::BitString; +use oid_registry::{ + OID_EC_P256, OID_NIST_EC_P384, OID_PKCS1_SHA1WITHRSA, OID_PKCS1_SHA256WITHRSA, + OID_PKCS1_SHA384WITHRSA, OID_PKCS1_SHA512WITHRSA, OID_SHA1_WITH_RSA, OID_SIG_ECDSA_WITH_SHA256, + OID_SIG_ECDSA_WITH_SHA384, OID_SIG_ED25519, +}; + +/// Verify the cryptographic signature of the raw data (can be a certificate, a CRL or a CSR). +/// +/// `public_key` is the public key of the **signer**. +/// +/// Not all algorithms are supported, this function is limited to what `ring` supports. +pub fn verify_signature( + public_key: &SubjectPublicKeyInfo, + signature_algorithm: &AlgorithmIdentifier, + signature_value: &BitString, + raw_data: &[u8], +) -> Result<(), X509Error> { + use ring::signature; + let signature_algorithm = &signature_algorithm.algorithm; + // identify verification algorithm + let verification_alg: &dyn signature::VerificationAlgorithm = if *signature_algorithm + == OID_PKCS1_SHA1WITHRSA + || *signature_algorithm == OID_SHA1_WITH_RSA + { + &signature::RSA_PKCS1_1024_8192_SHA1_FOR_LEGACY_USE_ONLY + } else if *signature_algorithm == OID_PKCS1_SHA256WITHRSA { + &signature::RSA_PKCS1_2048_8192_SHA256 + } else if *signature_algorithm == OID_PKCS1_SHA384WITHRSA { + &signature::RSA_PKCS1_2048_8192_SHA384 + } else if *signature_algorithm == OID_PKCS1_SHA512WITHRSA { + &signature::RSA_PKCS1_2048_8192_SHA512 + } else if *signature_algorithm == OID_SIG_ECDSA_WITH_SHA256 { + get_ec_curve_sha(&public_key.algorithm, 256) + .ok_or(X509Error::SignatureUnsupportedAlgorithm)? + } else if *signature_algorithm == OID_SIG_ECDSA_WITH_SHA384 { + get_ec_curve_sha(&public_key.algorithm, 384) + .ok_or(X509Error::SignatureUnsupportedAlgorithm)? + } else if *signature_algorithm == OID_SIG_ED25519 { + &signature::ED25519 + } else { + return Err(X509Error::SignatureUnsupportedAlgorithm); + }; + // get public key + let key = + signature::UnparsedPublicKey::new(verification_alg, &public_key.subject_public_key.data); + // verify signature + key.verify(raw_data, &signature_value.data) + .or(Err(X509Error::SignatureVerificationError)) +} + +/// Find the verification algorithm for the given EC curve and SHA digest size +/// +/// Not all algorithms are supported, we are limited to what `ring` supports. +fn get_ec_curve_sha( + pubkey_alg: &AlgorithmIdentifier, + sha_len: usize, +) -> Option<&'static dyn ring::signature::VerificationAlgorithm> { + use ring::signature; + let curve_oid = pubkey_alg.parameters.as_ref()?.as_oid().ok()?; + // let curve_oid = pubkey_alg.parameters.as_ref()?.as_oid().ok()?; + if curve_oid == OID_EC_P256 { + match sha_len { + 256 => Some(&signature::ECDSA_P256_SHA256_ASN1), + 384 => Some(&signature::ECDSA_P256_SHA384_ASN1), + _ => None, + } + } else if curve_oid == OID_NIST_EC_P384 { + match sha_len { + 256 => Some(&signature::ECDSA_P384_SHA256_ASN1), + 384 => Some(&signature::ECDSA_P384_SHA384_ASN1), + _ => None, + } + } else { + None + } +} diff --git a/rust/vendor/x509-parser/src/x509.rs b/rust/vendor/x509-parser/src/x509.rs new file mode 100644 index 0000000..5052ebd --- /dev/null +++ b/rust/vendor/x509-parser/src/x509.rs @@ -0,0 +1,667 @@ +//! X.509 objects and types +//! +//! Based on RFC5280 +//! + +use crate::error::{X509Error, X509Result}; +use crate::objects::*; +use crate::public_key::*; + +use asn1_rs::{ + Any, BitString, BmpString, DerSequence, FromBer, FromDer, Oid, OptTaggedParser, ParseResult, +}; +use core::convert::TryFrom; +use data_encoding::HEXUPPER; +use der_parser::ber::MAX_OBJECT_SIZE; +use der_parser::der::*; +use der_parser::error::*; +use der_parser::num_bigint::BigUint; +use der_parser::*; +use nom::branch::alt; +use nom::bytes::complete::take; +use nom::combinator::{complete, map}; +use nom::multi::{many0, many1}; +use nom::{Err, Offset}; +use oid_registry::*; +use rusticata_macros::newtype_enum; +use std::fmt; +use std::iter::FromIterator; + +/// The version of the encoded certificate. +/// +/// When extensions are used, as expected in this profile, version MUST be 3 +/// (value is `2`). If no extensions are present, but a UniqueIdentifier +/// is present, the version SHOULD be 2 (value is `1`); however, the +/// version MAY be 3. If only basic fields are present, the version +/// SHOULD be 1 (the value is omitted from the certificate as the default +/// value); however, the version MAY be 2 or 3. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct X509Version(pub u32); + +impl X509Version { + /// Parse [0] EXPLICIT Version DEFAULT v1 + pub(crate) fn from_der_tagged_0(i: &[u8]) -> X509Result<X509Version> { + let (rem, opt_version) = OptTaggedParser::from(0) + .parse_der(i, |_, data| Self::from_der(data)) + .map_err(Err::convert)?; + let version = opt_version.unwrap_or(X509Version::V1); + Ok((rem, version)) + } +} + +// Version ::= INTEGER { v1(0), v2(1), v3(2) } +impl<'a> FromDer<'a, X509Error> for X509Version { + fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { + map(<u32>::from_der, X509Version)(i).map_err(|_| Err::Error(X509Error::InvalidVersion)) + } +} + +newtype_enum! { + impl display X509Version { + V1 = 0, + V2 = 1, + V3 = 2, + } +} + +/// A generic attribute type and value +/// +/// These objects are used as [`RelativeDistinguishedName`] components. +#[derive(Clone, Debug, PartialEq)] +pub struct AttributeTypeAndValue<'a> { + attr_type: Oid<'a>, + attr_value: Any<'a>, // ANY -- DEFINED BY AttributeType +} + +impl<'a> AttributeTypeAndValue<'a> { + /// Builds a new `AttributeTypeAndValue` + #[inline] + pub const fn new(attr_type: Oid<'a>, attr_value: Any<'a>) -> Self { + AttributeTypeAndValue { + attr_type, + attr_value, + } + } + + /// Returns the attribute type + #[inline] + pub const fn attr_type(&self) -> &Oid<'a> { + &self.attr_type + } + + /// Returns the attribute value, as `ANY` + #[inline] + pub const fn attr_value(&self) -> &Any<'a> { + &self.attr_value + } + + /// Attempt to get the content as `str`. + /// This can fail if the object does not contain a string type. + /// + /// Note: the [`TryFrom`](core::convert::TryFrom) trait is implemented for `&str`, so this is + /// equivalent to `attr.try_into()`. + /// + /// Only NumericString, PrintableString, UTF8String and IA5String + /// are considered here. Other string types can be read using `as_slice`. + #[inline] + pub fn as_str(&self) -> Result<&'a str, X509Error> { + // TODO: replace this with helper function, when it is added to asn1-rs + match self.attr_value.tag() { + Tag::NumericString | Tag::PrintableString | Tag::Utf8String | Tag::Ia5String => { + let s = core::str::from_utf8(self.attr_value.data) + .map_err(|_| X509Error::InvalidAttributes)?; + Ok(s) + } + t => Err(X509Error::Der(Error::unexpected_tag(None, t))), + } + } + + /// Get the content as a slice. + #[inline] + pub fn as_slice(&self) -> &[u8] { + self.attr_value.as_bytes() + } +} + +impl<'a, 'b> TryFrom<&'a AttributeTypeAndValue<'b>> for &'a str { + type Error = X509Error; + + fn try_from(value: &'a AttributeTypeAndValue<'b>) -> Result<Self, Self::Error> { + value.attr_value.as_str().map_err(|e| e.into()) + } +} + +impl<'a, 'b> From<&'a AttributeTypeAndValue<'b>> for &'a [u8] { + fn from(value: &'a AttributeTypeAndValue<'b>) -> Self { + value.as_slice() + } +} + +// AttributeTypeAndValue ::= SEQUENCE { +// type AttributeType, +// value AttributeValue } +impl<'a> FromDer<'a, X509Error> for AttributeTypeAndValue<'a> { + fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { + parse_der_sequence_defined_g(|i, _| { + let (i, attr_type) = Oid::from_der(i).or(Err(X509Error::InvalidX509Name))?; + let (i, attr_value) = parse_attribute_value(i).or(Err(X509Error::InvalidX509Name))?; + let attr = AttributeTypeAndValue::new(attr_type, attr_value); + Ok((i, attr)) + })(i) + } +} + +// AttributeValue ::= ANY -- DEFINED BY AttributeType +#[inline] +fn parse_attribute_value(i: &[u8]) -> ParseResult<Any, Error> { + alt((Any::from_der, parse_malformed_string))(i) +} + +fn parse_malformed_string(i: &[u8]) -> ParseResult<Any, Error> { + let (rem, hdr) = Header::from_der(i)?; + let len = hdr.length().definite()?; + if len > MAX_OBJECT_SIZE { + return Err(nom::Err::Error(Error::InvalidLength)); + } + match hdr.tag() { + Tag::PrintableString => { + // if we are in this function, the PrintableString could not be validated. + // Accept it without validating charset, because some tools do not respect the charset + // restrictions (for ex. they use '*' while explicingly disallowed) + let (rem, data) = take(len)(rem)?; + // check valid encoding + let _ = std::str::from_utf8(data).map_err(|_| Error::BerValueError)?; + let obj = Any::new(hdr, data); + Ok((rem, obj)) + } + t => Err(nom::Err::Error(Error::unexpected_tag( + Some(Tag::PrintableString), + t, + ))), + } +} + +/// A Relative Distinguished Name element. +/// +/// These objects are used as [`X509Name`] components. +#[derive(Clone, Debug, PartialEq)] +pub struct RelativeDistinguishedName<'a> { + set: Vec<AttributeTypeAndValue<'a>>, +} + +impl<'a> RelativeDistinguishedName<'a> { + /// Builds a new `RelativeDistinguishedName` + #[inline] + pub const fn new(set: Vec<AttributeTypeAndValue<'a>>) -> Self { + RelativeDistinguishedName { set } + } + + /// Return an iterator over the components of this object + pub fn iter(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> { + self.set.iter() + } +} + +impl<'a> FromIterator<AttributeTypeAndValue<'a>> for RelativeDistinguishedName<'a> { + fn from_iter<T: IntoIterator<Item = AttributeTypeAndValue<'a>>>(iter: T) -> Self { + let set = iter.into_iter().collect(); + RelativeDistinguishedName { set } + } +} + +impl<'a> FromDer<'a, X509Error> for RelativeDistinguishedName<'a> { + fn from_der(i: &'a [u8]) -> X509Result<Self> { + parse_der_set_defined_g(|i, _| { + let (i, set) = many1(complete(AttributeTypeAndValue::from_der))(i)?; + let rdn = RelativeDistinguishedName { set }; + Ok((i, rdn)) + })(i) + } +} + +#[derive(Clone, Debug, PartialEq)] +pub struct SubjectPublicKeyInfo<'a> { + pub algorithm: AlgorithmIdentifier<'a>, + pub subject_public_key: BitString<'a>, + /// A raw unparsed PKIX, ASN.1 DER form (see RFC 5280, Section 4.1). + /// + /// Note: use the [`Self::parsed()`] function to parse this object. + pub raw: &'a [u8], +} + +impl<'a> SubjectPublicKeyInfo<'a> { + /// Attempt to parse the public key, and return the parsed version or an error + pub fn parsed(&self) -> Result<PublicKey, X509Error> { + let b = &self.subject_public_key.data; + if self.algorithm.algorithm == OID_PKCS1_RSAENCRYPTION { + let (_, key) = RSAPublicKey::from_der(b).map_err(|_| X509Error::InvalidSPKI)?; + Ok(PublicKey::RSA(key)) + } else if self.algorithm.algorithm == OID_KEY_TYPE_EC_PUBLIC_KEY { + let key = ECPoint::from(b.as_ref()); + Ok(PublicKey::EC(key)) + } else if self.algorithm.algorithm == OID_KEY_TYPE_DSA { + let s = parse_der_integer(b) + .and_then(|(_, obj)| obj.as_slice().map_err(Err::Error)) + .or(Err(X509Error::InvalidSPKI))?; + Ok(PublicKey::DSA(s)) + } else if self.algorithm.algorithm == OID_GOST_R3410_2001 { + let (_, s) = <&[u8]>::from_der(b).or(Err(X509Error::InvalidSPKI))?; + Ok(PublicKey::GostR3410(s)) + } else if self.algorithm.algorithm == OID_KEY_TYPE_GOST_R3410_2012_256 + || self.algorithm.algorithm == OID_KEY_TYPE_GOST_R3410_2012_512 + { + let (_, s) = <&[u8]>::from_der(b).or(Err(X509Error::InvalidSPKI))?; + Ok(PublicKey::GostR3410_2012(s)) + } else { + Ok(PublicKey::Unknown(b)) + } + } +} + +impl<'a> FromDer<'a, X509Error> for SubjectPublicKeyInfo<'a> { + /// Parse the SubjectPublicKeyInfo struct portion of a DER-encoded X.509 Certificate + fn from_der(i: &'a [u8]) -> X509Result<Self> { + let start_i = i; + parse_der_sequence_defined_g(move |i, _| { + let (i, algorithm) = AlgorithmIdentifier::from_der(i)?; + let (i, subject_public_key) = BitString::from_der(i).or(Err(X509Error::InvalidSPKI))?; + let len = start_i.offset(i); + let raw = &start_i[..len]; + let spki = SubjectPublicKeyInfo { + algorithm, + subject_public_key, + raw, + }; + Ok((i, spki)) + })(i) + } +} + +/// Algorithm identifier +/// +/// An algorithm identifier is defined by the following ASN.1 structure: +/// +/// <pre> +/// AlgorithmIdentifier ::= SEQUENCE { +/// algorithm OBJECT IDENTIFIER, +/// parameters ANY DEFINED BY algorithm OPTIONAL } +/// </pre> +/// +/// The algorithm identifier is used to identify a cryptographic +/// algorithm. The OBJECT IDENTIFIER component identifies the algorithm +/// (such as DSA with SHA-1). The contents of the optional parameters +/// field will vary according to the algorithm identified. +#[derive(Clone, Debug, PartialEq, DerSequence)] +#[error(X509Error)] +pub struct AlgorithmIdentifier<'a> { + #[map_err(|_| X509Error::InvalidAlgorithmIdentifier)] + pub algorithm: Oid<'a>, + #[optional] + pub parameters: Option<Any<'a>>, +} + +impl<'a> AlgorithmIdentifier<'a> { + /// Create a new `AlgorithmIdentifier` + pub const fn new(algorithm: Oid<'a>, parameters: Option<Any<'a>>) -> Self { + Self { + algorithm, + parameters, + } + } + + /// Get the algorithm OID + pub const fn oid(&'a self) -> &'a Oid { + &self.algorithm + } + + /// Get a reference to the algorithm parameters, if present + pub const fn parameters(&'a self) -> Option<&'a Any> { + self.parameters.as_ref() + } +} + +/// X.509 Name (as used in `Issuer` and `Subject` fields) +/// +/// The Name describes a hierarchical name composed of attributes, such +/// as country name, and corresponding values, such as US. The type of +/// the component AttributeValue is determined by the AttributeType; in +/// general it will be a DirectoryString. +#[derive(Clone, Debug, PartialEq)] +pub struct X509Name<'a> { + pub(crate) rdn_seq: Vec<RelativeDistinguishedName<'a>>, + pub(crate) raw: &'a [u8], +} + +impl<'a> fmt::Display for X509Name<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match x509name_to_string(&self.rdn_seq, oid_registry()) { + Ok(o) => write!(f, "{}", o), + Err(_) => write!(f, "<X509Error: Invalid X.509 name>"), + } + } +} + +impl<'a> X509Name<'a> { + /// Builds a new `X509Name` from the provided elements. + #[inline] + pub const fn new(rdn_seq: Vec<RelativeDistinguishedName<'a>>, raw: &'a [u8]) -> Self { + X509Name { rdn_seq, raw } + } + + /// Attempt to format the current name, using the given registry to convert OIDs to strings. + /// + /// Note: a default registry is provided with this crate, and is returned by the + /// [`oid_registry()`] method. + pub fn to_string_with_registry(&self, oid_registry: &OidRegistry) -> Result<String, X509Error> { + x509name_to_string(&self.rdn_seq, oid_registry) + } + + // Not using the AsRef trait, as that would not give back the full 'a lifetime + pub fn as_raw(&self) -> &'a [u8] { + self.raw + } + + /// Return an iterator over the `RelativeDistinguishedName` components of the name + pub fn iter(&self) -> impl Iterator<Item = &RelativeDistinguishedName<'a>> { + self.rdn_seq.iter() + } + + /// Return an iterator over the `RelativeDistinguishedName` components of the name + pub fn iter_rdn(&self) -> impl Iterator<Item = &RelativeDistinguishedName<'a>> { + self.rdn_seq.iter() + } + + /// Return an iterator over the attribute types and values of the name + pub fn iter_attributes(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> { + self.rdn_seq.iter().flat_map(|rdn| rdn.set.iter()) + } + + /// Return an iterator over the components identified by the given OID + /// + /// The type of the component AttributeValue is determined by the AttributeType; in + /// general it will be a DirectoryString. + /// + /// Attributes with same OID may be present multiple times, so the returned object is + /// an iterator. + /// Expected number of objects in this iterator are + /// - 0: not found + /// - 1: present once (common case) + /// - 2 or more: attribute is present multiple times + pub fn iter_by_oid(&self, oid: &Oid<'a>) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> { + // this is necessary, otherwise rustc complains + // that caller creates a temporary value for reference (for ex. + // `self.iter_by_oid(&OID_X509_LOCALITY_NAME)` + // ) + let oid = oid.clone(); + self.iter_attributes() + .filter(move |obj| obj.attr_type == oid) + } + + /// Return an iterator over the `CommonName` attributes of the X.509 Name. + /// + /// Returned iterator can be empty if there are no `CommonName` attributes. + /// If you expect only one `CommonName` to be present, then using `next()` will + /// get an `Option<&AttributeTypeAndValue>`. + /// + /// A common operation is to extract the `CommonName` as a string. + /// + /// ``` + /// use x509_parser::x509::X509Name; + /// + /// fn get_first_cn_as_str<'a>(name: &'a X509Name<'_>) -> Option<&'a str> { + /// name.iter_common_name() + /// .next() + /// .and_then(|cn| cn.as_str().ok()) + /// } + /// ``` + /// + /// Note that there are multiple reasons for failure or incorrect behavior, for ex. if + /// the attribute is present multiple times, or is not a UTF-8 encoded string (it can be + /// UTF-16, or even an OCTETSTRING according to the standard). + pub fn iter_common_name(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> { + self.iter_by_oid(&OID_X509_COMMON_NAME) + } + + /// Return an iterator over the `Country` attributes of the X.509 Name. + pub fn iter_country(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> { + self.iter_by_oid(&OID_X509_COUNTRY_NAME) + } + + /// Return an iterator over the `Organization` attributes of the X.509 Name. + pub fn iter_organization(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> { + self.iter_by_oid(&OID_X509_ORGANIZATION_NAME) + } + + /// Return an iterator over the `OrganizationalUnit` attributes of the X.509 Name. + pub fn iter_organizational_unit(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> { + self.iter_by_oid(&OID_X509_ORGANIZATIONAL_UNIT) + } + + /// Return an iterator over the `StateOrProvinceName` attributes of the X.509 Name. + pub fn iter_state_or_province(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> { + self.iter_by_oid(&OID_X509_STATE_OR_PROVINCE_NAME) + } + + /// Return an iterator over the `Locality` attributes of the X.509 Name. + pub fn iter_locality(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> { + self.iter_by_oid(&OID_X509_LOCALITY_NAME) + } + + /// Return an iterator over the `EmailAddress` attributes of the X.509 Name. + pub fn iter_email(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> { + self.iter_by_oid(&OID_PKCS9_EMAIL_ADDRESS) + } +} + +impl<'a> FromIterator<RelativeDistinguishedName<'a>> for X509Name<'a> { + fn from_iter<T: IntoIterator<Item = RelativeDistinguishedName<'a>>>(iter: T) -> Self { + let rdn_seq = iter.into_iter().collect(); + X509Name { rdn_seq, raw: &[] } + } +} + +impl<'a> From<X509Name<'a>> for Vec<RelativeDistinguishedName<'a>> { + fn from(name: X509Name<'a>) -> Self { + name.rdn_seq + } +} + +impl<'a> FromDer<'a, X509Error> for X509Name<'a> { + /// Parse the X.501 type Name, used for ex in issuer and subject of a X.509 certificate + fn from_der(i: &'a [u8]) -> X509Result<Self> { + let start_i = i; + parse_der_sequence_defined_g(move |i, _| { + let (i, rdn_seq) = many0(complete(RelativeDistinguishedName::from_der))(i)?; + let len = start_i.offset(i); + let name = X509Name { + rdn_seq, + raw: &start_i[..len], + }; + Ok((i, name)) + })(i) + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub struct ReasonCode(pub u8); + +newtype_enum! { +impl display ReasonCode { + Unspecified = 0, + KeyCompromise = 1, + CACompromise = 2, + AffiliationChanged = 3, + Superseded = 4, + CessationOfOperation = 5, + CertificateHold = 6, + // value 7 is not used + RemoveFromCRL = 8, + PrivilegeWithdrawn = 9, + AACompromise = 10, +} +} + +impl Default for ReasonCode { + fn default() -> Self { + ReasonCode::Unspecified + } +} + +// Attempt to convert attribute to string. If type is not a string, return value is the hex +// encoding of the attribute value +fn attribute_value_to_string(attr: &Any, _attr_type: &Oid) -> Result<String, X509Error> { + // TODO: replace this with helper function, when it is added to asn1-rs + match attr.tag() { + Tag::NumericString + | Tag::VisibleString + | Tag::PrintableString + | Tag::GeneralString + | Tag::ObjectDescriptor + | Tag::GraphicString + | Tag::T61String + | Tag::VideotexString + | Tag::Utf8String + | Tag::Ia5String => { + let s = core::str::from_utf8(attr.data).map_err(|_| X509Error::InvalidAttributes)?; + Ok(s.to_owned()) + } + Tag::BmpString => { + // TODO: remove this when a new release of asn1-rs removes the need to consume attr in try_from + let any = attr.clone(); + let s = BmpString::try_from(any).map_err(|_| X509Error::InvalidAttributes)?; + Ok(s.string()) + } + _ => { + // type is not a string, get slice and convert it to base64 + Ok(HEXUPPER.encode(attr.as_bytes())) + } + } +} + +/// Convert a DER representation of a X.509 name to a human-readable string +/// +/// RDNs are separated with "," +/// Multiple RDNs are separated with "+" +/// +/// Attributes that cannot be represented by a string are hex-encoded +fn x509name_to_string( + rdn_seq: &[RelativeDistinguishedName], + oid_registry: &OidRegistry, +) -> Result<String, X509Error> { + rdn_seq.iter().fold(Ok(String::new()), |acc, rdn| { + acc.and_then(|mut _vec| { + rdn.set + .iter() + .fold(Ok(String::new()), |acc2, attr| { + acc2.and_then(|mut _vec2| { + let val_str = attribute_value_to_string(&attr.attr_value, &attr.attr_type)?; + // look ABBREV, and if not found, use shortname + let abbrev = match oid2abbrev(&attr.attr_type, oid_registry) { + Ok(s) => String::from(s), + _ => format!("{:?}", attr.attr_type), + }; + let rdn = format!("{}={}", abbrev, val_str); + match _vec2.len() { + 0 => Ok(rdn), + _ => Ok(_vec2 + " + " + &rdn), + } + }) + }) + .map(|v| match _vec.len() { + 0 => v, + _ => _vec + ", " + &v, + }) + }) + }) +} + +pub(crate) fn parse_signature_value(i: &[u8]) -> X509Result<BitString> { + BitString::from_der(i).or(Err(Err::Error(X509Error::InvalidSignatureValue))) +} + +pub(crate) fn parse_serial(i: &[u8]) -> X509Result<(&[u8], BigUint)> { + let (rem, any) = Any::from_ber(i).map_err(|_| X509Error::InvalidSerial)?; + // RFC 5280 4.1.2.2: "The serial number MUST be a positive integer" + // however, many CAs do not respect this and send integers with MSB set, + // so we do not use `as_biguint()` + any.tag() + .assert_eq(Tag::Integer) + .map_err(|_| X509Error::InvalidSerial)?; + let slice = any.data; + let big = BigUint::from_bytes_be(slice); + Ok((rem, (slice, big))) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_x509_version() { + // correct version + let data: &[u8] = &[0xa0, 0x03, 0x02, 0x01, 0x00]; + let r = X509Version::from_der_tagged_0(data); + assert!(r.is_ok()); + + // wrong tag + let data: &[u8] = &[0xa1, 0x03, 0x02, 0x01, 0x00]; + let r = X509Version::from_der_tagged_0(data); + assert!(r.is_ok()); + + // short read + let data: &[u8] = &[0xa0, 0x01]; + let r = X509Version::from_der_tagged_0(data); + assert!(r.is_err()); + + // short read with wrong tag + let data: &[u8] = &[0xa1, 0x01]; + let r = X509Version::from_der_tagged_0(data); + assert!(r.is_err()); + } + + #[test] + fn test_x509_name() { + let name = X509Name { + rdn_seq: vec![ + RelativeDistinguishedName { + set: vec![AttributeTypeAndValue { + attr_type: oid! {2.5.4.6}, // countryName + attr_value: Any::from_tag_and_data(Tag::PrintableString, b"FR"), + }], + }, + RelativeDistinguishedName { + set: vec![AttributeTypeAndValue { + attr_type: oid! {2.5.4.8}, // stateOrProvinceName + attr_value: Any::from_tag_and_data(Tag::PrintableString, b"Some-State"), + }], + }, + RelativeDistinguishedName { + set: vec![AttributeTypeAndValue { + attr_type: oid! {2.5.4.10}, // organizationName + attr_value: Any::from_tag_and_data( + Tag::PrintableString, + b"Internet Widgits Pty Ltd", + ), + }], + }, + RelativeDistinguishedName { + set: vec![ + AttributeTypeAndValue { + attr_type: oid! {2.5.4.3}, // CN + attr_value: Any::from_tag_and_data(Tag::PrintableString, b"Test1"), + }, + AttributeTypeAndValue { + attr_type: oid! {2.5.4.3}, // CN + attr_value: Any::from_tag_and_data(Tag::PrintableString, b"Test2"), + }, + ], + }, + ], + raw: &[], // incorrect, but enough for testing + }; + assert_eq!( + name.to_string(), + "C=FR, ST=Some-State, O=Internet Widgits Pty Ltd, CN=Test1 + CN=Test2" + ); + } +} |