summaryrefslogtreecommitdiffstats
path: root/rust/vendor/x509-parser/src
diff options
context:
space:
mode:
Diffstat (limited to 'rust/vendor/x509-parser/src')
-rw-r--r--rust/vendor/x509-parser/src/certificate.rs782
-rw-r--r--rust/vendor/x509-parser/src/certification_request.rs166
-rw-r--r--rust/vendor/x509-parser/src/cri_attributes.rs179
-rw-r--r--rust/vendor/x509-parser/src/error.rs123
-rw-r--r--rust/vendor/x509-parser/src/extensions/generalname.rs115
-rw-r--r--rust/vendor/x509-parser/src/extensions/keyusage.rs150
-rw-r--r--rust/vendor/x509-parser/src/extensions/mod.rs1484
-rw-r--r--rust/vendor/x509-parser/src/extensions/nameconstraints.rs59
-rw-r--r--rust/vendor/x509-parser/src/extensions/policymappings.rs92
-rw-r--r--rust/vendor/x509-parser/src/extensions/sct.rs124
-rw-r--r--rust/vendor/x509-parser/src/lib.rs213
-rw-r--r--rust/vendor/x509-parser/src/objects.rs92
-rw-r--r--rust/vendor/x509-parser/src/pem.rs255
-rw-r--r--rust/vendor/x509-parser/src/prelude.rs18
-rw-r--r--rust/vendor/x509-parser/src/public_key.rs132
-rw-r--r--rust/vendor/x509-parser/src/revocation_list.rs365
-rw-r--r--rust/vendor/x509-parser/src/signature_algorithm.rs326
-rw-r--r--rust/vendor/x509-parser/src/signature_value.rs11
-rw-r--r--rust/vendor/x509-parser/src/time.rs179
-rw-r--r--rust/vendor/x509-parser/src/utils.rs19
-rw-r--r--rust/vendor/x509-parser/src/validate/certificate.rs17
-rw-r--r--rust/vendor/x509-parser/src/validate/extensions.rs115
-rw-r--r--rust/vendor/x509-parser/src/validate/loggers.rs83
-rw-r--r--rust/vendor/x509-parser/src/validate/mod.rs262
-rw-r--r--rust/vendor/x509-parser/src/validate/name.rs32
-rw-r--r--rust/vendor/x509-parser/src/validate/structure.rs160
-rw-r--r--rust/vendor/x509-parser/src/verify.rs78
-rw-r--r--rust/vendor/x509-parser/src/x509.rs667
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"
+ );
+ }
+}