From 06791ba196750d12423a3bd7ac3f90ba1be7b75c Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Mon, 3 Feb 2025 16:21:36 +0100 Subject: [PATCH 01/24] Update lock file --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9b1b067..31f8f86 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,9 +49,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "cc" -version = "1.2.10" +version = "1.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" +checksum = "e4730490333d58093109dc02c23174c3f4d490998c3fed3cc8e82d57afedb9cf" dependencies = [ "shlex", ] @@ -278,9 +278,9 @@ checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" [[package]] name = "syn" -version = "2.0.96" +version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", From 4d8ce1f9379d9931f6cc3205c542d894a1cdec48 Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Mon, 3 Feb 2025 17:06:41 +0100 Subject: [PATCH 02/24] Visitor: add method to visit unknown extension and those with parse errors Make sure specialized methods of visitor pattern are always called once per extension. Previously, unknown extension had no specialized method and could only be accessed by iterating on all extensions. --- src/visitor/certificate_visitor.rs | 160 ++++++++++++++++------------- src/visitor/crl_visitor.rs | 84 ++++++++------- 2 files changed, 135 insertions(+), 109 deletions(-) diff --git a/src/visitor/certificate_visitor.rs b/src/visitor/certificate_visitor.rs index c6df8e2..a2178cc 100644 --- a/src/visitor/certificate_visitor.rs +++ b/src/visitor/certificate_visitor.rs @@ -110,47 +110,75 @@ pub trait X509CertificateVisitor { /// Invoked for extensions, after visiting children fn post_visit_extensions(&mut self, _extensions: &[X509Extension]) {} - /// Invoked for the "Authority Key Identifier" (if present) + /// Invoked for the "Authority Key Identifier" extension (if present) fn visit_extension_aki(&mut self, _aki: &AuthorityKeyIdentifier) {} - /// Invoked for the "Subject Key Identifier" (if present) + /// Invoked for the "Subject Key Identifier" extension (if present) fn visit_extension_ski(&mut self, _id: &KeyIdentifier) {} - /// Invoked for the "Key Usage" (if present) + /// Invoked for the "Key Usage" extension (if present) fn visit_extension_key_usage(&mut self, _usage: &KeyUsage) {} - /// Invoked for the "Certificate Policies" (if present) + /// Invoked for the "Certificate Policies" extension (if present) fn visit_extension_certificate_policies(&mut self, _policies: &CertificatePolicies) {} - /// Invoked for the "Subject Alternative Name" (if present) + /// Invoked for the "Subject Alternative Name" extension (if present) fn visit_extension_subject_alternative_name(&mut self, _san: &SubjectAlternativeName) {} - /// Invoked for the "Issuer Alternative Name" (if present) + /// Invoked for the "Issuer Alternative Name" extension (if present) fn visit_extension_issuer_alternative_name(&mut self, _ian: &IssuerAlternativeName) {} - /// Invoked for the "Basic Constraints" (if present) + /// Invoked for the "Basic Constraints" extension (if present) fn visit_extension_basic_constraints(&mut self, _bc: &BasicConstraints) {} - /// Invoked for the "Name Constraints" (if present) + /// Invoked for the "Name Constraints" extension (if present) fn visit_extension_name_constraints(&mut self, _constraints: &NameConstraints) {} - /// Invoked for the "Policy Constraints" (if present) + /// Invoked for the "Name Constraints" extension (if present) + fn visit_extension_nscert_comment(&mut self, _nscert_comment: &str) {} + + /// Invoked for the "Name Constraints" extension (if present) + fn visit_extension_nscert_type(&mut self, _nscert_type: &NSCertType) {} + + /// Invoked for the "Policy Constraints" extension (if present) fn visit_extension_policy_constraints(&mut self, _constraints: &PolicyConstraints) {} - /// Invoked for the "Extended Key Usage" (if present) + /// Invoked for the "Policy Mappings" extension (if present) + fn visit_extension_policy_mappings(&mut self, _mappings: &PolicyMappings) {} + + /// Invoked for the "Extended Key Usage" extension (if present) fn visit_extension_extended_key_usage(&mut self, _usage: &ExtendedKeyUsage) {} - /// Invoked for the "CRL Distribution Points" (if present) + /// Invoked for the "CRL Distribution Points" extension (if present) fn visit_extension_crl_distribution_points(&mut self, _crl: &CRLDistributionPoints) {} - /// Invoked for the "Inhibit anyPolicy" (if present) + /// Invoked for the "Inhibit anyPolicy" extension (if present) fn visit_extension_inhibit_anypolicy(&mut self, _policy: &InhibitAnyPolicy) {} - /// Invoked for the "Authority Information Access" (if present) + /// Invoked for the "Authority Information Access" extension (if present) fn visit_extension_authority_information_access(&mut self, _info: &AuthorityInfoAccess) {} - /// Invoked for the "Signed Certificate Timestamp" (SCT) (if present) + /// Invoked for the "Signed Certificate Timestamp" (SCT) extension (if present) fn visit_extension_sct(&mut self, _sct: &[SignedCertificateTimestamp]) {} + + /// Invoked for any other extension than the specific (recognized) types + /// + /// This can happen for several reasons: + /// - the parser did not recognize the extension content + /// - the parser was explicitly asked to not parse extension content + /// - the extension could be correct (for ex in a CRL), but is not supposed to be part of a Certificate + fn visit_extension_unknown(&mut self, _ext: &X509Extension) {} + + /// Invoked for any extension than caused a parse error + /// + /// Normally, this should not match anything except for invalid data. + /// This could match any known extension malformed or wrongly encoded. + fn visit_extension_parse_error( + &mut self, + _extension: &X509Extension, + _error: &asn1_rs::Err, + ) { + } } impl X509Certificate<'_> { @@ -166,80 +194,70 @@ impl X509Certificate<'_> { impl TbsCertificate<'_> { /// Run the provided `visitor` over the [`TbsCertificate`] object pub fn walk(&self, visitor: &mut V) { - visitor.visit_version(&self.version); - visitor.visit_serial_number(self.raw_serial()); - visitor.visit_tbs_signature_algorithm(&self.signature); - visitor.visit_issuer(&self.issuer); - visitor.visit_validity(&self.validity); - visitor.visit_subject(&self.subject); - visitor.visit_subject_public_key_info(&self.subject_pki); - visitor.visit_issuer_unique_id(self.issuer_uid.as_ref()); - visitor.visit_subject_unique_id(self.subject_uid.as_ref()); - visitor.pre_visit_extensions(self.extensions()); + // shorten name to reduce line length + let v = visitor; + v.visit_version(&self.version); + v.visit_serial_number(self.raw_serial()); + v.visit_tbs_signature_algorithm(&self.signature); + v.visit_issuer(&self.issuer); + v.visit_validity(&self.validity); + v.visit_subject(&self.subject); + v.visit_subject_public_key_info(&self.subject_pki); + v.visit_issuer_unique_id(self.issuer_uid.as_ref()); + v.visit_subject_unique_id(self.subject_uid.as_ref()); + v.pre_visit_extensions(self.extensions()); for extension in self.extensions() { - visitor.visit_extension(extension); + v.visit_extension(extension); - if extension.oid == OID_X509_EXT_AUTHORITY_KEY_IDENTIFIER { - if let ParsedExtension::AuthorityKeyIdentifier(aki) = &extension.parsed_extension { - visitor.visit_extension_aki(aki); - } - } else if extension.oid == OID_X509_EXT_SUBJECT_KEY_IDENTIFIER { - if let ParsedExtension::SubjectKeyIdentifier(id) = &extension.parsed_extension { - visitor.visit_extension_ski(id); + match extension.parsed_extension() { + ParsedExtension::AuthorityInfoAccess(info) => { + v.visit_extension_authority_information_access(info) } - } else if extension.oid == OID_X509_EXT_KEY_USAGE { - if let ParsedExtension::KeyUsage(usage) = &extension.parsed_extension { - visitor.visit_extension_key_usage(usage); + ParsedExtension::AuthorityKeyIdentifier(aki) => v.visit_extension_aki(aki), + ParsedExtension::BasicConstraints(bc) => v.visit_extension_basic_constraints(bc), + ParsedExtension::CertificatePolicies(policies) => { + v.visit_extension_certificate_policies(policies) } - } else if extension.oid == OID_X509_EXT_CERTIFICATE_POLICIES { - if let ParsedExtension::CertificatePolicies(policies) = &extension.parsed_extension - { - visitor.visit_extension_certificate_policies(policies); + ParsedExtension::CRLDistributionPoints(crl) => { + v.visit_extension_crl_distribution_points(crl) } - } else if extension.oid == OID_X509_EXT_SUBJECT_ALT_NAME { - if let ParsedExtension::SubjectAlternativeName(san) = &extension.parsed_extension { - visitor.visit_extension_subject_alternative_name(san); + ParsedExtension::ExtendedKeyUsage(usage) => { + v.visit_extension_extended_key_usage(usage) } - } else if extension.oid == OID_X509_EXT_ISSUER_ALT_NAME { - if let ParsedExtension::IssuerAlternativeName(ian) = &extension.parsed_extension { - visitor.visit_extension_issuer_alternative_name(ian); + ParsedExtension::InhibitAnyPolicy(policy) => { + v.visit_extension_inhibit_anypolicy(policy) } - } else if extension.oid == OID_X509_EXT_BASIC_CONSTRAINTS { - if let ParsedExtension::BasicConstraints(bc) = &extension.parsed_extension { - visitor.visit_extension_basic_constraints(bc); + ParsedExtension::IssuerAlternativeName(ian) => { + v.visit_extension_issuer_alternative_name(ian) } - } else if extension.oid == OID_X509_EXT_NAME_CONSTRAINTS { - if let ParsedExtension::NameConstraints(constraints) = &extension.parsed_extension { - visitor.visit_extension_name_constraints(constraints); + ParsedExtension::KeyUsage(usage) => v.visit_extension_key_usage(usage), + ParsedExtension::NSCertType(nscert_type) => { + v.visit_extension_nscert_type(nscert_type) } - } else if extension.oid == OID_X509_EXT_POLICY_CONSTRAINTS { - if let ParsedExtension::PolicyConstraints(constraints) = &extension.parsed_extension - { - visitor.visit_extension_policy_constraints(constraints); + ParsedExtension::NameConstraints(constraints) => { + v.visit_extension_name_constraints(constraints) } - } else if extension.oid == OID_X509_EXT_EXTENDED_KEY_USAGE { - if let ParsedExtension::ExtendedKeyUsage(usage) = &extension.parsed_extension { - visitor.visit_extension_extended_key_usage(usage); + ParsedExtension::NsCertComment(comment) => { + v.visit_extension_nscert_comment(comment) } - } else if extension.oid == OID_X509_EXT_CRL_DISTRIBUTION_POINTS { - if let ParsedExtension::CRLDistributionPoints(crl) = &extension.parsed_extension { - visitor.visit_extension_crl_distribution_points(crl); + ParsedExtension::PolicyConstraints(constraints) => { + v.visit_extension_policy_constraints(constraints) } - } else if extension.oid == OID_X509_EXT_INHIBITANT_ANY_POLICY { - if let ParsedExtension::InhibitAnyPolicy(policy) = &extension.parsed_extension { - visitor.visit_extension_inhibit_anypolicy(policy); + ParsedExtension::PolicyMappings(mappings) => { + v.visit_extension_policy_mappings(mappings) } - } else if extension.oid == OID_PKIX_AUTHORITY_INFO_ACCESS { - if let ParsedExtension::AuthorityInfoAccess(info) = &extension.parsed_extension { - visitor.visit_extension_authority_information_access(info); + ParsedExtension::SCT(sct) => v.visit_extension_sct(sct), + ParsedExtension::SubjectAlternativeName(san) => { + v.visit_extension_subject_alternative_name(san) } - } else if extension.oid == OID_CT_LIST_SCT { - if let ParsedExtension::SCT(sct) = &extension.parsed_extension { - visitor.visit_extension_sct(sct); + ParsedExtension::SubjectKeyIdentifier(id) => v.visit_extension_ski(id), + ParsedExtension::ParseError { error } => { + v.visit_extension_parse_error(extension, error) } + _ => v.visit_extension_unknown(extension), } } - visitor.post_visit_extensions(self.extensions()); + v.post_visit_extensions(self.extensions()); } } diff --git a/src/visitor/crl_visitor.rs b/src/visitor/crl_visitor.rs index c67b77b..edaff65 100644 --- a/src/visitor/crl_visitor.rs +++ b/src/visitor/crl_visitor.rs @@ -122,6 +122,25 @@ pub trait CertificateRevocationListVisitor { /// Invoked for the "Signed Certificate Timestamp" (SCT) (if present) fn visit_extension_sct(&mut self, _sct: &[SignedCertificateTimestamp]) {} + + /// Invoked for any other extension than the specific (recognized) types + /// + /// This can happen for several reasons: + /// - the parser did not recognize the extension content + /// - the parser was explicitly asked to not parse extension content + /// - the extension could be correct (for ex in a CRL), but is not supposed to be part of a Certificate + fn visit_extension_unknown(&mut self, _ext: &X509Extension) {} + + /// Invoked for any extension than caused a parse error + /// + /// Normally, this should not match anything except for invalid data. + /// This could match any known extension malformed or wrongly encoded. + fn visit_extension_parse_error( + &mut self, + _extension: &X509Extension, + _error: &asn1_rs::Err, + ) { + } } impl CertificateRevocationList<'_> { @@ -137,54 +156,43 @@ impl CertificateRevocationList<'_> { impl TbsCertList<'_> { /// Run the provided `visitor` over the [`TbsCertList`] object pub fn walk(&self, visitor: &mut V) { - visitor.visit_version(self.version.as_ref()); - visitor.visit_tbs_signature_algorithm(&self.signature); - visitor.visit_issuer(&self.issuer); - visitor.visit_this_update(&self.this_update); - visitor.visit_next_update(self.next_update.as_ref()); - visitor.visit_revoked_certificates(&self.revoked_certificates); + // shorten name to reduce line length + let v = visitor; + v.visit_version(self.version.as_ref()); + v.visit_tbs_signature_algorithm(&self.signature); + v.visit_issuer(&self.issuer); + v.visit_this_update(&self.this_update); + v.visit_next_update(self.next_update.as_ref()); + v.visit_revoked_certificates(&self.revoked_certificates); for certificate in &self.revoked_certificates { - visitor.visit_revoked_certificate(certificate); + v.visit_revoked_certificate(certificate); } - visitor.pre_visit_extensions(self.extensions()); + v.pre_visit_extensions(self.extensions()); for extension in self.extensions() { - visitor.visit_extension(extension); + v.visit_extension(extension); - if extension.oid == OID_X509_EXT_AUTHORITY_KEY_IDENTIFIER { - if let ParsedExtension::AuthorityKeyIdentifier(aki) = &extension.parsed_extension { - visitor.visit_extension_aki(aki); - } - } else if extension.oid == OID_X509_EXT_ISSUER_ALT_NAME { - if let ParsedExtension::IssuerAlternativeName(ian) = &extension.parsed_extension { - visitor.visit_extension_issuer_alternative_name(ian); - } - } else if extension.oid == OID_X509_EXT_CRL_NUMBER { - if let ParsedExtension::CRLNumber(number) = &extension.parsed_extension { - visitor.visit_extension_crl_number(number); - } - } else if extension.oid == OID_X509_EXT_ISSUER_DISTRIBUTION_POINT { - if let ParsedExtension::IssuingDistributionPoint(dp) = &extension.parsed_extension { - visitor.visit_extension_issuing_distribution_point(dp); - } - } else if extension.oid == OID_PKIX_AUTHORITY_INFO_ACCESS { - if let ParsedExtension::AuthorityInfoAccess(info) = &extension.parsed_extension { - visitor.visit_extension_authority_information_access(info); + match extension.parsed_extension() { + ParsedExtension::AuthorityInfoAccess(info) => { + v.visit_extension_authority_information_access(info) } - } else if extension.oid == OID_X509_EXT_REASON_CODE { - if let ParsedExtension::ReasonCode(code) = &extension.parsed_extension { - visitor.visit_extension_reason_code(code); + ParsedExtension::AuthorityKeyIdentifier(aki) => v.visit_extension_aki(aki), + ParsedExtension::CRLNumber(number) => v.visit_extension_crl_number(number), + ParsedExtension::InvalidityDate(time) => v.visit_extension_invalidity_date(time), + ParsedExtension::IssuerAlternativeName(ian) => { + v.visit_extension_issuer_alternative_name(ian) } - } else if extension.oid == OID_X509_EXT_INVALIDITY_DATE { - if let ParsedExtension::InvalidityDate(time) = &extension.parsed_extension { - visitor.visit_extension_invalidity_date(time); + ParsedExtension::IssuingDistributionPoint(dp) => { + v.visit_extension_issuing_distribution_point(dp) } - } else if extension.oid == OID_CT_LIST_SCT { - if let ParsedExtension::SCT(sct) = &extension.parsed_extension { - visitor.visit_extension_sct(sct); + ParsedExtension::ReasonCode(code) => v.visit_extension_reason_code(code), + ParsedExtension::SCT(sct) => v.visit_extension_sct(sct), + ParsedExtension::ParseError { error } => { + v.visit_extension_parse_error(extension, error) } + _ => v.visit_extension_unknown(extension), } } - visitor.post_visit_extensions(self.extensions()); + v.post_visit_extensions(self.extensions()); } } From 83d41574574f8d871f3cca187ce6a1a3c50349ba Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Mon, 3 Feb 2025 17:25:57 +0100 Subject: [PATCH 03/24] PEM: ignore lines in comments which contain invalid UTF-8 characters (Closes #180) --- src/pem.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/pem.rs b/src/pem.rs index 9b8e12f..73c3738 100644 --- a/src/pem.rs +++ b/src/pem.rs @@ -61,7 +61,7 @@ use crate::certificate::X509Certificate; use crate::error::{PEMError, X509Error}; use crate::parse_x509_certificate; use nom::{Err, IResult}; -use std::io::{BufRead, Cursor, Seek}; +use std::io::{BufRead, Cursor, ErrorKind, Seek}; /// Representation of PEM data #[derive(Clone, PartialEq, Eq, Debug)] @@ -119,7 +119,16 @@ impl Pem { 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)?; + let num_bytes = match r.read_line(&mut line) { + Ok(line) => line, + Err(e) if e.kind() == ErrorKind::InvalidData => { + // some tools put invalid UTF-8 data in PEM comment section. Ignore line + continue; + } + Err(e) => { + return Err(e.into()); + } + }; if num_bytes == 0 { // EOF return Err(PEMError::MissingHeader); From ce50cdb38a742dbe85a9a2f5fd79ad4caf2bf42a Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Mon, 3 Feb 2025 17:54:52 +0100 Subject: [PATCH 04/24] GeneralName: add a new variant `Invalid` so an invalid entry does not stop parsing for the entire list of names (for ex in SAN) --- examples/print-cert.rs | 1 + src/extensions/generalname.rs | 96 ++++++++++++++++++++--------------- 2 files changed, 57 insertions(+), 40 deletions(-) diff --git a/examples/print-cert.rs b/examples/print-cert.rs index 9792085..7ab6b8d 100644 --- a/examples/print-cert.rs +++ b/examples/print-cert.rs @@ -40,6 +40,7 @@ fn generalname_to_string(gn: &GeneralName) -> String { GeneralName::RegisteredID(oid) => format!("RegisteredID:{}", oid), GeneralName::URI(n) => format!("URI:{}", n), GeneralName::X400Address(obj) => format!("X400Address:{:?}", obj), + GeneralName::Invalid(tag, b) => format!("Invalid:tag={},data={:?}", tag, b), } } diff --git a/src/extensions/generalname.rs b/src/extensions/generalname.rs index 894e3ba..21d3315 100644 --- a/src/extensions/generalname.rs +++ b/src/extensions/generalname.rs @@ -1,7 +1,7 @@ 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 asn1_rs::{Any, CheckDerConstraints, Class, Error, FromDer, Oid, Sequence, Tag}; use core::convert::TryFrom; use nom::combinator::all_consuming; use nom::{Err, IResult}; @@ -11,7 +11,7 @@ use std::fmt; /// Represents a GeneralName as defined in RFC5280. There /// is no support X.400 addresses and EDIPartyName. /// -/// String formats are not validated. +/// String formats are not validated (except for valid UTF-8). pub enum GeneralName<'a> { OtherName(Oid<'a>, &'a [u8]), /// More or less an e-mail, the format is not checked. @@ -30,6 +30,8 @@ pub enum GeneralName<'a> { /// An ip address, provided as encoded. IPAddress(&'a [u8]), RegisteredID(Oid<'a>), + /// Invalid data (for ex. invalid UTF-8 data in DNSName entry) + Invalid(Tag, &'a [u8]), } impl<'a> TryFrom> for GeneralName<'a> { @@ -37,49 +39,54 @@ impl<'a> TryFrom> for GeneralName<'a> { fn try_from(any: Any<'a>) -> Result { any.class().assert_eq(Class::ContextSpecific)?; - fn ia5str(any: Any) -> Result<&str, Err> { - // 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(|_| 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())), + + let name = match parse_generalname_entry(any.clone()) { + Ok(name) => name, + Err(_) => GeneralName::Invalid(any.tag(), any.data), }; Ok(name) } } +fn parse_generalname_entry( + any: Any<'_>, +) -> Result, as TryFrom>>::Error> { + Ok(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())), + }) +} + impl CheckDerConstraints for GeneralName<'_> { fn check_constraints(any: &Any) -> asn1_rs::Result<()> { Sequence::check_constraints(any) @@ -104,10 +111,19 @@ impl fmt::Display for GeneralName<'_> { GeneralName::URI(s) => write!(f, "URI({})", s), GeneralName::IPAddress(b) => write!(f, "IPAddress({})", format_serial(b)), GeneralName::RegisteredID(oid) => write!(f, "RegisteredID({})", oid), + GeneralName::Invalid(tag, b) => { + write!(f, "Invalid(tag={}, data={})", tag, format_serial(b)) + } } } } +fn ia5str(any: Any) -> Result<&str, Err> { + // 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(|_| Err::Failure(Error::BerValueError)) +} + pub(crate) fn parse_generalname(i: &[u8]) -> IResult<&[u8], GeneralName, Error> { let (rest, any) = Any::from_der(i)?; let gn = GeneralName::try_from(any)?; From ec3fcae88a399725ccf59b2b10bdfce99c15b985 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Mon, 10 Feb 2025 12:07:38 -0500 Subject: [PATCH 05/24] Cargo: 0.17.0 -> 0.18.0 This commit updates the version number to reflect that a breaking change was made in `master`. This in turn fixes the `cargo-semver-checks-action` finding in CI. --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 31f8f86..014b98f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -442,7 +442,7 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "x509-parser" -version = "0.17.0" +version = "0.18.0" dependencies = [ "asn1-rs", "data-encoding", diff --git a/Cargo.toml b/Cargo.toml index 89c8286..62b9ad5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "x509-parser" -version = "0.17.0" +version = "0.18.0" description = "Parser for the X.509 v3 format (RFC 5280 certificates)" license = "MIT OR Apache-2.0" keywords = ["X509","Certificate","parser","nom"] From 27dd7d2e7370737903251c15f554f887b05da827 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Mon, 10 Feb 2025 12:14:30 -0500 Subject: [PATCH 06/24] Cargo: update cc, data-encoding, oid-registry Updates 3 packages to latest compatible versions Updating cc v1.2.11 -> v1.2.13 Updating data-encoding v2.7.0 -> v2.8.0 Updating oid-registry v0.8.0 -> v0.8.1 Notably the oid-registry update requires replacing usages of the deprecated typo'd `OID_X509_EXT_INHIBITANT_ANY_POLICY` OID with `OID_X509_EXT_INHIBIT_ANY_POLICY`. --- Cargo.lock | 12 ++++++------ src/certificate.rs | 2 +- src/extensions/mod.rs | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 014b98f..30e3e16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,9 +49,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "cc" -version = "1.2.11" +version = "1.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4730490333d58093109dc02c23174c3f4d490998c3fed3cc8e82d57afedb9cf" +checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda" dependencies = [ "shlex", ] @@ -64,9 +64,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "data-encoding" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e60eed09d8c01d3cee5b7d30acb059b76614c918fa0f992e0dd6eeb10daad6f" +checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" [[package]] name = "der-parser" @@ -189,9 +189,9 @@ dependencies = [ [[package]] name = "oid-registry" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "264c56d1492c13e769662197fb6b94e0a52abe52d27efac374615799a4bf453d" +checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" dependencies = [ "asn1-rs", ] diff --git a/src/certificate.rs b/src/certificate.rs index 3abb5bf..7026383 100644 --- a/src/certificate.rs +++ b/src/certificate.rs @@ -436,7 +436,7 @@ impl<'a> TbsCertificate<'a> { pub fn inhibit_anypolicy( &self, ) -> Result>, X509Error> { - self.get_extension_unique(&OID_X509_EXT_INHIBITANT_ANY_POLICY)? + self.get_extension_unique(&OID_X509_EXT_INHIBIT_ANY_POLICY)? .map_or(Ok(None), |ext| match ext.parsed_extension { ParsedExtension::InhibitAnyPolicy(ref value) => { Ok(Some(BasicExtension::new(ext.critical, value))) diff --git a/src/extensions/mod.rs b/src/extensions/mod.rs index 025ed89..0e4ea94 100644 --- a/src/extensions/mod.rs +++ b/src/extensions/mod.rs @@ -672,7 +672,7 @@ pub(crate) mod parser { ); add!( m, - OID_X509_EXT_INHIBITANT_ANY_POLICY, + OID_X509_EXT_INHIBIT_ANY_POLICY, parse_inhibitanypolicy_ext ); add!( From 01da307055964a522cd9fb3b40f909e09242b7ed Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Tue, 4 Feb 2025 10:39:46 +0100 Subject: [PATCH 07/24] Add support for SubjectInfoAccess extension --- Cargo.lock | 3 +- Cargo.toml | 6 ++- src/extensions/mod.rs | 89 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 30e3e16..70229f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -190,8 +190,7 @@ dependencies = [ [[package]] name = "oid-registry" version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" +source = "git+https://github.com/rusticata/oid-registry?branch=master#33b20513b73ce04e1de48232beba3fa89df33b1d" dependencies = [ "asn1-rs", ] diff --git a/Cargo.toml b/Cargo.toml index 62b9ad5..b08a268 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,9 +46,13 @@ asn1-rs = { version = "0.7.0", features=["datetime"] } data-encoding = "2.2.1" lazy_static = "1.4" nom = "7.0" -oid-registry = { version="0.8", features=["crypto", "x509", "x962"] } +oid-registry = { version="0.8.1", features=["crypto", "x509", "x962"] } rusticata-macros = "4.0" ring = { version="0.17.7", optional=true } der-parser = { version = "10.0", features=["bigint"] } thiserror = "2.0" time = { version="0.3.35", features=["formatting"] } + +# XXX: remove this before release! +[patch.crates-io] +oid-registry = { git="https://github.com/rusticata/oid-registry", branch="master" } diff --git a/src/extensions/mod.rs b/src/extensions/mod.rs index 0e4ea94..5eb2f27 100644 --- a/src/extensions/mod.rs +++ b/src/extensions/mod.rs @@ -216,6 +216,8 @@ pub enum ParsedExtension<'a> { InhibitAnyPolicy(InhibitAnyPolicy), /// Section 4.2.2.1 of rfc 5280 AuthorityInfoAccess(AuthorityInfoAccess<'a>), + /// Section 4.2.2.2 of rfc 5280 + SubjectInfoAccess(SubjectInfoAccess<'a>), /// Netscape certificate type (subject is SSL client, an SSL server, or a CA) NSCertType(NSCertType), /// Netscape certificate comment @@ -440,6 +442,68 @@ impl<'a> FromDer<'a, X509Error> for AuthorityInfoAccess<'a> { } } +#[derive(Clone, Debug, PartialEq)] +pub struct SubjectInfoAccess<'a> { + pub accessdescs: Vec>, +} + +impl<'a> SubjectInfoAccess<'a> { + /// Returns an iterator over the Access Descriptors + pub fn iter(&self) -> impl Iterator> { + 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, Vec<&GeneralName<'a>>> { + // create the hashmap and merge entries with same OID + let mut m: HashMap> = 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, Vec>> { + let mut aia_list = self.accessdescs; + // create the hashmap and merge entries with same OID + let mut m: HashMap> = 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 SubjectInfoAccess<'a> { + fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { + parser::parse_subjectinfoaccess(i).map_err(Err::convert) + } +} + +/// AccessDescription ::= SEQUENCE { +/// accessMethod OBJECT IDENTIFIER, +/// accessLocation GeneralName } #[derive(Clone, Debug, PartialEq)] pub struct AccessDescription<'a> { pub access_method: Oid<'a>, @@ -680,6 +744,7 @@ pub(crate) mod parser { OID_PKIX_AUTHORITY_INFO_ACCESS, parse_authorityinfoaccess_ext ); + add!(m, OID_PKIX_SUBJECT_INFO_ACCESS, parse_subjectinfoaccess_ext); add!( m, OID_X509_EXT_AUTHORITY_KEY_IDENTIFIER, @@ -1018,6 +1083,30 @@ pub(crate) mod parser { )(i) } + // SubjectInfoAccessSyntax ::= + // SEQUENCE SIZE (1..MAX) OF AccessDescription + // + // AccessDescription ::= SEQUENCE { + // accessMethod OBJECT IDENTIFIER, + // accessLocation GeneralName } + pub(super) fn parse_subjectinfoaccess(i: &[u8]) -> IResult<&[u8], SubjectInfoAccess, BerError> { + fn parse_sia(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_sia)(i)?; + Ok((ret, SubjectInfoAccess { accessdescs })) + } + + fn parse_subjectinfoaccess_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + map(parse_subjectinfoaccess, ParsedExtension::SubjectInfoAccess)(i) + } + fn parse_aki_content<'a>( i: &'a [u8], _hdr: Header<'_>, From 6c9d3440edb6eef63c19d41407794948e9fa1f1b Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Thu, 27 Feb 2025 09:28:07 +0100 Subject: [PATCH 08/24] Update lock file (including dependency on ring) --- Cargo.lock | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 70229f6..3054e17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,9 +49,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "cc" -version = "1.2.13" +version = "1.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda" +checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af" dependencies = [ "shlex", ] @@ -127,9 +127,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.169" +version = "0.2.170" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" [[package]] name = "memchr" @@ -221,15 +221,14 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.8" +version = "0.17.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "da5349ae27d3887ca812fb375b45a4fbb36d8d12d2df394968cd86e35683fe73" dependencies = [ "cc", "cfg-if", "getrandom", "libc", - "spin", "untrusted", "windows-sys", ] @@ -245,18 +244,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.218" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" dependencies = [ "proc-macro2", "quote", @@ -269,12 +268,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - [[package]] name = "syn" version = "2.0.98" @@ -350,9 +343,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" +checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" [[package]] name = "untrusted" From 623237801d6451d74b54b46f157a3dc30fe14c40 Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Wed, 12 Mar 2025 09:21:02 +0100 Subject: [PATCH 09/24] Update ring to >= 0.17.12 (Closes #190 - RUSTSEC-2025-0009) --- Cargo.lock | 60 +++++++++++++++++++++++++++--------------------------- Cargo.toml | 2 +- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3054e17..0586348 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,9 +49,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "cc" -version = "1.2.15" +version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c736e259eea577f443d5c86c304f9f4ae0295c43f3ba05c21f1d66b5f06001af" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" dependencies = [ "shlex", ] @@ -115,9 +115,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "lazy_static" @@ -127,9 +127,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.170" +version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "memchr" @@ -203,27 +203,27 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.38" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "ring" -version = "0.17.11" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da5349ae27d3887ca812fb375b45a4fbb36d8d12d2df394968cd86e35683fe73" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", @@ -244,18 +244,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.218" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", @@ -270,9 +270,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "syn" -version = "2.0.98" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -292,18 +292,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.11" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", @@ -312,9 +312,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.37" +version = "0.3.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8" dependencies = [ "deranged", "itoa", @@ -327,15 +327,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef" [[package]] name = "time-macros" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c" dependencies = [ "num-conv", "time-core", @@ -343,9 +343,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "untrusted" diff --git a/Cargo.toml b/Cargo.toml index b08a268..6aa3368 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,7 +48,7 @@ lazy_static = "1.4" nom = "7.0" oid-registry = { version="0.8.1", features=["crypto", "x509", "x962"] } rusticata-macros = "4.0" -ring = { version="0.17.7", optional=true } +ring = { version="0.17.12", optional=true } der-parser = { version = "10.0", features=["bigint"] } thiserror = "2.0" time = { version="0.3.35", features=["formatting"] } From a92bbab5cdc630f7d2c0410736bee55f0e3710e8 Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Wed, 12 Mar 2025 15:21:29 +0100 Subject: [PATCH 10/24] CI: build with `--locked`, and add one job for stable build without `--locked` --- .github/workflows/rust.yml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 436f7e4..f1fd338 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -22,6 +22,14 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} + - run: RUSTFLAGS="-D warnings" cargo check --locked + + check-notlocked: + name: Check (not locked) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable - name: Cargo update run: cargo update - run: RUSTFLAGS="-D warnings" cargo check @@ -40,9 +48,7 @@ jobs: - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - - name: Cargo update - run: cargo update - - run: RUSTFLAGS="-D warnings" cargo check --all-targets --all-features + - run: RUSTFLAGS="-D warnings" cargo check --locked --all-targets --all-features test: name: Test Suite @@ -51,7 +57,7 @@ jobs: steps: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable - - run: cargo test --all-features + - run: cargo test --locked --all-features test_features: name: Test suite (with features) @@ -69,7 +75,7 @@ jobs: - uses: actions/checkout@v4 - name: Install stable toolchain uses: dtolnay/rust-toolchain@stable - - run: cargo test ${{ matrix.features }} + - run: cargo test --locked ${{ matrix.features }} fmt: name: Rustfmt @@ -90,7 +96,7 @@ jobs: - uses: dtolnay/rust-toolchain@nightly with: components: clippy - - run: cargo clippy --all-features -- -D warnings + - run: cargo clippy --locked --all-features -- -D warnings doc: name: Build documentation From 762809b6ff862291e4d7c311eb309ce5c5f640e4 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Mon, 28 Apr 2025 14:58:39 -0400 Subject: [PATCH 11/24] clippy: fix uninlined_format_args findings Applies the result of `cargo clippy --fix` for findings of the form: ``` warning: variables can be used directly in the `format!` string --> src/extensions/generalname.rs:105:47 | 105 | GeneralName::OtherName(oid, _) => write!(f, "OtherName({}, [...])", oid), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args = note: `#[warn(clippy::uninlined_format_args)]` on by default help: change this to | 105 - GeneralName::OtherName(oid, _) => write!(f, "OtherName({}, [...])", oid), 105 + GeneralName::OtherName(oid, _) => write!(f, "OtherName({oid}, [...])"), ``` --- examples/print-cert.rs | 92 +++++++++++++++++------------------ examples/print-crl.rs | 4 +- src/extensions/generalname.rs | 12 ++--- src/extensions/mod.rs | 4 +- src/time.rs | 2 +- src/utils.rs | 2 +- src/validate/extensions.rs | 2 +- src/validate/loggers.rs | 4 +- src/validate/structure.rs | 2 +- src/x509.rs | 4 +- tests/readcrl.rs | 2 +- tests/verify.rs | 12 ++--- 12 files changed, 71 insertions(+), 71 deletions(-) diff --git a/examples/print-cert.rs b/examples/print-cert.rs index 7ab6b8d..00eb022 100644 --- a/examples/print-cert.rs +++ b/examples/print-cert.rs @@ -25,22 +25,22 @@ fn print_hex_dump(bytes: &[u8], max_len: usize) { fn format_oid(oid: &Oid) -> String { match oid2sn(oid, oid_registry()) { Ok(s) => s.to_owned(), - _ => format!("{}", oid), + _ => format!("{oid}"), } } fn generalname_to_string(gn: &GeneralName) -> String { match gn { - GeneralName::DNSName(name) => format!("DNSName:{}", name), - GeneralName::DirectoryName(n) => format!("DirName:{}", n), - GeneralName::EDIPartyName(obj) => format!("EDIPartyName:{:?}", obj), - GeneralName::IPAddress(n) => format!("IPAddress:{:?}", n), - GeneralName::OtherName(oid, n) => format!("OtherName:{}, {:?}", oid, n), - GeneralName::RFC822Name(n) => format!("RFC822Name:{}", n), - GeneralName::RegisteredID(oid) => format!("RegisteredID:{}", oid), - GeneralName::URI(n) => format!("URI:{}", n), - GeneralName::X400Address(obj) => format!("X400Address:{:?}", obj), - GeneralName::Invalid(tag, b) => format!("Invalid:tag={},data={:?}", tag, b), + GeneralName::DNSName(name) => format!("DNSName:{name}"), + GeneralName::DirectoryName(n) => format!("DirName:{n}"), + GeneralName::EDIPartyName(obj) => format!("EDIPartyName:{obj:?}"), + GeneralName::IPAddress(n) => format!("IPAddress:{n:?}"), + GeneralName::OtherName(oid, n) => format!("OtherName:{oid}, {n:?}"), + GeneralName::RFC822Name(n) => format!("RFC822Name:{n}"), + GeneralName::RegisteredID(oid) => format!("RegisteredID:{oid}"), + GeneralName::URI(n) => format!("URI:{n}"), + GeneralName::X400Address(obj) => format!("X400Address:{obj:?}"), + GeneralName::Invalid(tag, b) => format!("Invalid:tag={tag},data={b:?}"), } } @@ -55,11 +55,11 @@ fn print_x509_extension(oid: &Oid, ext: &X509Extension) { ParsedExtension::AuthorityKeyIdentifier(aki) => { println!(" X509v3 Authority Key Identifier"); if let Some(key_id) = &aki.key_identifier { - println!(" Key Identifier: {:x}", key_id); + println!(" Key Identifier: {key_id:x}"); } if let Some(issuer) = &aki.authority_cert_issuer { for name in issuer { - println!(" Cert Issuer: {}", name); + println!(" Cert Issuer: {name}"); } } if let Some(serial) = aki.authority_cert_serial { @@ -73,10 +73,10 @@ fn print_x509_extension(oid: &Oid, ext: &X509Extension) { println!(" X509v3 CRL Distribution Points:"); for point in points.iter() { if let Some(name) = &point.distribution_point { - println!(" Full Name: {:?}", name); + println!(" Full Name: {name:?}"); } if let Some(reasons) = &point.reasons { - println!(" Reasons: {}", reasons); + println!(" Reasons: {reasons}"); } if let Some(crl_issuer) = &point.crl_issuer { print!(" CRL Issuer: "); @@ -89,44 +89,44 @@ fn print_x509_extension(oid: &Oid, ext: &X509Extension) { } } ParsedExtension::KeyUsage(ku) => { - println!(" X509v3 Key Usage: {}", ku); + println!(" X509v3 Key Usage: {ku}"); } ParsedExtension::NSCertType(ty) => { - println!(" Netscape Cert Type: {}", ty); + println!(" Netscape Cert Type: {ty}"); } ParsedExtension::SubjectAlternativeName(san) => { for name in &san.general_names { let s = match name { GeneralName::DNSName(s) => { - format!("DNS:{}", s) + format!("DNS:{s}") } GeneralName::IPAddress(b) => { let ip = match b.len() { 4 => { let b = <[u8; 4]>::try_from(*b).unwrap(); let ip = Ipv4Addr::from(b); - format!("{}", ip) + format!("{ip}") } 16 => { let b = <[u8; 16]>::try_from(*b).unwrap(); let ip = Ipv6Addr::from(b); - format!("{}", ip) + format!("{ip}") } - l => format!("invalid (len={})", l), + l => format!("invalid (len={l})"), }; - format!("IP Address:{}", ip) + format!("IP Address:{ip}") } _ => { - format!("{:?}", name) + format!("{name:?}") } }; - println!(" X509v3 SAN: {}", s); + println!(" X509v3 SAN: {s}"); } } ParsedExtension::SubjectKeyIdentifier(id) => { - println!(" X509v3 Subject Key Identifier: {:x}", id); + println!(" X509v3 Subject Key Identifier: {id:x}"); } - x => println!(" {:?}", x), + x => println!(" {x:?}"), } } @@ -156,7 +156,7 @@ fn print_x509_digest_algorithm(alg: &AlgorithmIdentifier, level: usize) { fn print_x509_info(x509: &X509Certificate) -> io::Result<()> { let version = x509.version(); if version.0 < 3 { - println!(" Version: {}", version); + println!(" Version: {version}"); } else { println!(" Version: INVALID({})", version.0); } @@ -173,7 +173,7 @@ fn print_x509_info(x509: &X509Certificate) -> io::Result<()> { println!(" Signature Value:"); for l in format_number_to_hex_with_colon(&x509.signature_value.data, 16) { - println!(" {}", l); + println!(" {l}"); } println!(" Extensions:"); for ext in x509.extensions() { @@ -194,10 +194,10 @@ fn print_x509_info(x509: &X509Certificate) -> io::Result<()> { println!("FAIL"); } for warning in logger.warnings() { - println!(" [W] {}", warning); + println!(" [W] {warning}"); } for error in logger.errors() { - println!(" [E] {}", error); + println!(" [E] {error}"); } println!(); if VALIDATE_ERRORS_FATAL && !logger.errors().is_empty() { @@ -244,7 +244,7 @@ fn print_x509_signature_algorithm(signature_algorithm: &AlgorithmIdentifier, ind indent_s, format_oid(params.hash_algorithm_oid()), ); - print!("{}Mask Generation Function: ", indent_s); + print!("{indent_s}Mask Generation Function: "); if let Ok(mask_gen) = params.mask_gen_algorithm() { println!( "{}/{}", @@ -264,7 +264,7 @@ fn print_x509_signature_algorithm(signature_algorithm: &AlgorithmIdentifier, ind indent_s, format_oid(params.hash_algorithm_oid()), ); - print!("{}Mask Generation Function: ", indent_s); + print!("{indent_s}Mask Generation Function: "); if let Ok(mask_gen) = params.mask_gen_algorithm() { println!( "{}/{}", @@ -283,7 +283,7 @@ fn print_x509_signature_algorithm(signature_algorithm: &AlgorithmIdentifier, ind } } Err(e) => { - eprintln!("Could not parse signature algorithm: {}", e); + eprintln!("Could not parse signature algorithm: {e}"); println!(" Signature Algorithm:"); print_x509_digest_algorithm(signature_algorithm, indent); } @@ -298,10 +298,10 @@ fn print_x509_ski(public_key: &SubjectPublicKeyInfo) { println!(" RSA Public Key: ({} bit)", rsa.key_size()); // print_hex_dump(rsa.modulus, 1024); for l in format_number_to_hex_with_colon(rsa.modulus, 16) { - println!(" {}", l); + println!(" {l}"); } if let Ok(e) = rsa.try_exponent() { - println!(" exponent: 0x{:x} ({})", e, e); + println!(" exponent: 0x{e:x} ({e})"); } else { println!(" exponent: :"); print_hex_dump(rsa.exponent, 32); @@ -310,7 +310,7 @@ fn print_x509_ski(public_key: &SubjectPublicKeyInfo) { Ok(PublicKey::EC(ec)) => { println!(" EC Public Key: ({} bit)", ec.key_size()); for l in format_number_to_hex_with_colon(ec.data(), 16) { - println!(" {}", l); + println!(" {l}"); } // // identify curve // if let Some(params) = &public_key.algorithm.parameters { @@ -329,19 +329,19 @@ fn print_x509_ski(public_key: &SubjectPublicKeyInfo) { Ok(PublicKey::DSA(y)) => { println!(" DSA Public Key: ({} bit)", 8 * y.len()); for l in format_number_to_hex_with_colon(y, 16) { - println!(" {}", l); + println!(" {l}"); } } Ok(PublicKey::GostR3410(y)) => { println!(" GOST R 34.10-94 Public Key: ({} bit)", 8 * y.len()); for l in format_number_to_hex_with_colon(y, 16) { - println!(" {}", l); + println!(" {l}"); } } Ok(PublicKey::GostR3410_2012(y)) => { println!(" GOST R 34.10-2012 Public Key: ({} bit)", 8 * y.len()); for l in format_number_to_hex_with_colon(y, 16) { - println!(" {}", l); + println!(" {l}"); } } Ok(PublicKey::Unknown(b)) => { @@ -349,7 +349,7 @@ fn print_x509_ski(public_key: &SubjectPublicKeyInfo) { print_hex_dump(b, 256); if let Ok((rem, res)) = der_parser::parse_der(b) { eprintln!("rem: {} bytes", rem.len()); - eprintln!("{:?}", res); + eprintln!("{res:?}"); } else { eprintln!(" "); } @@ -366,7 +366,7 @@ fn format_number_to_hex_with_colon(b: &[u8], row_size: usize) -> Vec { let mut v = Vec::with_capacity(1 + b.len() / row_size); for r in b.chunks(row_size) { let s = r.iter().fold(String::with_capacity(3 * r.len()), |a, b| { - a + &format!("{:02x}:", b) + a + &format!("{b:02x}:") }); v.push(s) } @@ -380,11 +380,11 @@ fn handle_certificate(file_name: &str, data: &[u8]) -> io::Result<()> { Ok(()) } Err(e) => { - let s = format!("Error while parsing {}: {}", file_name, e); + let s = format!("Error while parsing {file_name}: {e}"); if PARSE_ERRORS_FATAL { Err(io::Error::new(io::ErrorKind::Other, s)) } else { - eprintln!("{}", s); + eprintln!("{s}"); Ok(()) } } @@ -393,7 +393,7 @@ fn handle_certificate(file_name: &str, data: &[u8]) -> io::Result<()> { pub fn main() -> io::Result<()> { for file_name in env::args().skip(1) { - println!("File: {}", file_name); + println!("File: {file_name}"); let data = std::fs::read(file_name.clone()).expect("Unable to read file"); if matches!((data[0], data[1]), (0x30, 0x81..=0x83)) { // probably DER @@ -404,11 +404,11 @@ pub fn main() -> io::Result<()> { match pem { Ok(pem) => { let data = &pem.contents; - println!("Certificate [{}]", n); + println!("Certificate [{n}]"); handle_certificate(&file_name, data)?; } Err(e) => { - eprintln!("Error while decoding PEM entry {}: {}", n, e); + eprintln!("Error while decoding PEM entry {n}: {e}"); } } } diff --git a/examples/print-crl.rs b/examples/print-crl.rs index 73337cb..e36db5b 100644 --- a/examples/print-crl.rs +++ b/examples/print-crl.rs @@ -16,7 +16,7 @@ fn print_hex_dump(bytes: &[u8], max_len: usize) { fn format_oid(oid: &Oid) -> String { match oid2sn(oid, oid_registry()) { Ok(s) => s.to_owned(), - _ => format!("{}", oid), + _ => format!("{oid}"), } } @@ -136,7 +136,7 @@ pub fn main() -> io::Result<()> { // placeholder to store decoded PEM data, if needed let tmpdata; - println!("File: {}", file_name); + println!("File: {file_name}"); let data = std::fs::read(file_name.clone()).expect("Unable to read file"); let der_data: &[u8] = if (data[0], data[1]) == (0x30, 0x82) { // probably DER diff --git a/src/extensions/generalname.rs b/src/extensions/generalname.rs index 21d3315..77199ea 100644 --- a/src/extensions/generalname.rs +++ b/src/extensions/generalname.rs @@ -102,15 +102,15 @@ impl<'a> FromDer<'a, X509Error> for GeneralName<'a> { impl fmt::Display for GeneralName<'_> { 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::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()"), - GeneralName::DirectoryName(dn) => write!(f, "DirectoryName({})", dn), + GeneralName::DirectoryName(dn) => write!(f, "DirectoryName({dn})"), GeneralName::EDIPartyName(_) => write!(f, "EDIPartyName()"), - GeneralName::URI(s) => write!(f, "URI({})", s), + GeneralName::URI(s) => write!(f, "URI({s})"), GeneralName::IPAddress(b) => write!(f, "IPAddress({})", format_serial(b)), - GeneralName::RegisteredID(oid) => write!(f, "RegisteredID({})", oid), + GeneralName::RegisteredID(oid) => write!(f, "RegisteredID({oid})"), GeneralName::Invalid(tag, b) => { write!(f, "Invalid(tag={}, data={})", tag, format_serial(b)) } diff --git a/src/extensions/mod.rs b/src/extensions/mod.rs index 5eb2f27..dd545c1 100644 --- a/src/extensions/mod.rs +++ b/src/extensions/mod.rs @@ -1590,7 +1590,7 @@ mod tests { assert!(!reasons.privelege_withdrawn()); assert!(reasons.aa_compromise()); assert_eq!( - format!("{}", reasons), + format!("{reasons}"), "Key Compromise, CA Compromise, AA Compromise" ); let issuers = crl[0].crl_issuer.as_ref().unwrap(); @@ -1621,7 +1621,7 @@ mod tests { assert!(!reasons.certificate_hold()); assert!(!reasons.privelege_withdrawn()); assert!(!reasons.aa_compromise()); - assert_eq!(format!("{}", reasons), "Key Compromise, CA 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!( diff --git a/src/time.rs b/src/time.rs index caa0927..6dae5ca 100644 --- a/src/time.rs +++ b/src/time.rs @@ -164,7 +164,7 @@ impl fmt::Display for ASN1Time { let s = self .time .format(format) - .unwrap_or_else(|e| format!("Invalid date: {}", e)); + .unwrap_or_else(|e| format!("Invalid date: {e}")); f.write_str(&s) } } diff --git a/src/utils.rs b/src/utils.rs index 72b4d99..f1754c1 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,7 +1,7 @@ /// 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) + a + &format!("{b:02x}:") }); s.pop(); s diff --git a/src/validate/extensions.rs b/src/validate/extensions.rs index a838f6e..7f7780e 100644 --- a/src/validate/extensions.rs +++ b/src/validate/extensions.rs @@ -100,7 +100,7 @@ impl<'a> Validator<'a> for X509ExtensionsValidator { 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)); + l.warn(&format!("Invalid charset in 'SAN' entry '{s}'")); } } _ => (), diff --git a/src/validate/loggers.rs b/src/validate/loggers.rs index 0052f8d..988bdbd 100644 --- a/src/validate/loggers.rs +++ b/src/validate/loggers.rs @@ -39,11 +39,11 @@ pub struct StderrLogger; impl Logger for StderrLogger { fn warn(&mut self, message: &str) { - eprintln!("[W] {}", message); + eprintln!("[W] {message}"); } fn err(&mut self, message: &str) { - eprintln!("[E] {}", message); + eprintln!("[E] {message}"); } } diff --git a/src/validate/structure.rs b/src/validate/structure.rs index 9896e38..f8f33c9 100644 --- a/src/validate/structure.rs +++ b/src/validate/structure.rs @@ -143,7 +143,7 @@ impl<'a> Validator<'a> for TbsCertificateStructureValidator { 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)); + l.warn(&format!("Invalid charset in 'SAN' entry '{s}'")); } } _ => (), diff --git a/src/x509.rs b/src/x509.rs index b4f1df9..4142dbf 100644 --- a/src/x509.rs +++ b/src/x509.rs @@ -335,7 +335,7 @@ pub struct X509Name<'a> { impl fmt::Display for X509Name<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match x509name_to_string(&self.rdn_seq, oid_registry()) { - Ok(o) => write!(f, "{}", o), + Ok(o) => write!(f, "{o}"), Err(_) => write!(f, ""), } } @@ -558,7 +558,7 @@ fn x509name_to_string( Ok(s) => String::from(s), _ => format!("{:?}", attr.attr_type), }; - let rdn = format!("{}={}", abbrev, val_str); + let rdn = format!("{abbrev}={val_str}"); match acc2.len() { 0 => Ok(rdn), _ => Ok(acc2 + " + " + &rdn), diff --git a/tests/readcrl.rs b/tests/readcrl.rs index 3bc88d2..0fa7a86 100644 --- a/tests/readcrl.rs +++ b/tests/readcrl.rs @@ -9,7 +9,7 @@ fn read_crl_verify() { let (_, x509_ca) = X509Certificate::from_der(CA_DATA).expect("could not parse certificate"); let (_, crl) = parse_x509_crl(CRL_DATA).expect("could not parse revocation list"); let res = crl.verify_signature(&x509_ca.tbs_certificate.subject_pki); - eprintln!("Verification: {:?}", res); + eprintln!("Verification: {res:?}"); assert!(res.is_ok()); } diff --git a/tests/verify.rs b/tests/verify.rs index 848239d..e51d119 100644 --- a/tests/verify.rs +++ b/tests/verify.rs @@ -11,7 +11,7 @@ fn test_signature_verification() { // for a root CA, verify self-signature let (_, x509_ca) = parse_x509_certificate(CA_DER).expect("could not parse certificate"); let res = x509_ca.verify_signature(None); - eprintln!("Verification: {:?}", res); + eprintln!("Verification: {res:?}"); assert!(res.is_ok()); // for a standard certificate, first load the authority, then the certificate, and verify it @@ -19,7 +19,7 @@ fn test_signature_verification() { parse_x509_certificate(CA_LETSENCRYPT_X3).expect("could not parse certificate"); let (_, x509_cert) = parse_x509_certificate(CERT_DER).expect("could not parse certificate"); let res = x509_cert.verify_signature(Some(&x509_ca.tbs_certificate.subject_pki)); - eprintln!("Verification: {:?}", res); + eprintln!("Verification: {res:?}"); assert!(res.is_ok()); } @@ -30,7 +30,7 @@ fn test_signature_verification_ed25519() { // this certificate is self-signed let (_, x509_ca) = parse_x509_certificate(ED25519_DER).expect("could not parse certificate"); let res = x509_ca.verify_signature(None); - eprintln!("Verification: {:?}", res); + eprintln!("Verification: {res:?}"); assert!(res.is_ok()); } @@ -46,7 +46,7 @@ fn test_signature_verification_rsa_pss_sha256() { let (_, x509) = parse_x509_certificate(RSA_PSS_SELF_SIGNED_SHA256).expect("could not parse certificate"); let res = x509.verify_signature(None); - eprintln!("Verification: {:?}", res); + eprintln!("Verification: {res:?}"); assert!(res.is_ok()); } @@ -55,7 +55,7 @@ fn test_signature_verification_rsa_pss_sha384() { let (_, x509) = parse_x509_certificate(RSA_PSS_SELF_SIGNED_SHA384).expect("could not parse certificate"); let res = x509.verify_signature(None); - eprintln!("Verification: {:?}", res); + eprintln!("Verification: {res:?}"); assert!(res.is_ok()); } @@ -64,6 +64,6 @@ fn test_signature_verification_rsa_pss_sha512() { let (_, x509) = parse_x509_certificate(RSA_PSS_SELF_SIGNED_SHA512).expect("could not parse certificate"); let res = x509.verify_signature(None); - eprintln!("Verification: {:?}", res); + eprintln!("Verification: {res:?}"); assert!(res.is_ok()); } From 2877a7328011ef1acc6084e598fd0321f57c5f94 Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Mon, 4 Aug 2025 17:57:10 +0200 Subject: [PATCH 12/24] Update cargo dependencies --- Cargo.lock | 72 +++++++++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0586348..3af3016 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "asn1-rs" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "607495ec7113b178fbba7a6166a27f99e774359ef4823adbefd756b5b81d7970" +checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" dependencies = [ "asn1-rs-derive", "asn1-rs-impl", @@ -43,30 +43,30 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "cc" -version = "1.2.16" +version = "1.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" dependencies = [ "shlex", ] [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "data-encoding" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "der-parser" @@ -84,9 +84,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.11" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", ] @@ -104,9 +104,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", @@ -127,15 +127,15 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.171" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "minimal-lexical" @@ -190,7 +190,8 @@ dependencies = [ [[package]] name = "oid-registry" version = "0.8.1" -source = "git+https://github.com/rusticata/oid-registry?branch=master#33b20513b73ce04e1de48232beba3fa89df33b1d" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7" dependencies = [ "asn1-rs", ] @@ -203,9 +204,9 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -270,9 +271,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "syn" -version = "2.0.100" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -281,9 +282,9 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", @@ -312,9 +313,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.39" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", @@ -327,15 +328,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.20" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", @@ -355,9 +356,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "windows-sys" @@ -447,3 +448,8 @@ dependencies = [ "thiserror", "time", ] + +[[patch.unused]] +name = "oid-registry" +version = "0.9.0-beta.1" +source = "git+https://github.com/rusticata/oid-registry?branch=master#4bf1446eb8a3834ee481f49116674c23d48e5caa" From ae473af527cfeaf8cc3c688d000e11bd69489a25 Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Mon, 4 Aug 2025 18:01:12 +0200 Subject: [PATCH 13/24] Remove unused dependency on git repostitory for oid-registry --- Cargo.lock | 5 ----- Cargo.toml | 3 --- 2 files changed, 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3af3016..368df95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -448,8 +448,3 @@ dependencies = [ "thiserror", "time", ] - -[[patch.unused]] -name = "oid-registry" -version = "0.9.0-beta.1" -source = "git+https://github.com/rusticata/oid-registry?branch=master#4bf1446eb8a3834ee481f49116674c23d48e5caa" diff --git a/Cargo.toml b/Cargo.toml index 6aa3368..9fca921 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,3 @@ der-parser = { version = "10.0", features=["bigint"] } thiserror = "2.0" time = { version="0.3.35", features=["formatting"] } -# XXX: remove this before release! -[patch.crates-io] -oid-registry = { git="https://github.com/rusticata/oid-registry", branch="master" } From 3e59840cdd283263bf8f098fed5e806c883dc887 Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Mon, 4 Aug 2025 18:09:16 +0200 Subject: [PATCH 14/24] Fix clippy (nightly) warnings: hiding a lifetime that's elided elsewhere is confusing --- src/certificate.rs | 24 ++++---- src/certification_request.rs | 10 ++-- src/cri_attributes.rs | 10 ++-- src/extensions/generalname.rs | 4 +- src/extensions/keyusage.rs | 2 +- src/extensions/mod.rs | 95 ++++++++++++++++++------------- src/extensions/nameconstraints.rs | 6 +- src/extensions/policymappings.rs | 2 +- src/extensions/sct.rs | 12 ++-- src/lib.rs | 8 +-- src/pem.rs | 2 +- src/public_key.rs | 2 +- src/revocation_list.rs | 22 +++---- src/signature_algorithm.rs | 16 +++--- src/time.rs | 8 +-- src/x509.rs | 12 ++-- 16 files changed, 126 insertions(+), 109 deletions(-) diff --git a/src/certificate.rs b/src/certificate.rs index 7026383..e1c8843 100644 --- a/src/certificate.rs +++ b/src/certificate.rs @@ -288,13 +288,13 @@ impl<'a> TbsCertificate<'a> { /// Get the certificate subject. #[inline] - pub fn subject(&self) -> &X509Name { + pub fn subject(&self) -> &X509Name<'_> { &self.subject } /// Get the certificate issuer. #[inline] - pub fn issuer(&self) -> &X509Name { + pub fn issuer(&self) -> &X509Name<'_> { &self.issuer } @@ -306,7 +306,7 @@ impl<'a> TbsCertificate<'a> { /// Get the certificate public key information. #[inline] - pub fn public_key(&self) -> &SubjectPublicKeyInfo { + pub fn public_key(&self) -> &SubjectPublicKeyInfo<'_> { &self.subject_pki } @@ -353,7 +353,7 @@ impl<'a> TbsCertificate<'a> { /// 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>, X509Error> { + pub fn extensions_map(&self) -> Result, &X509Extension<'a>>, X509Error> { self.extensions .iter() .try_fold(HashMap::new(), |mut m, ext| { @@ -403,7 +403,7 @@ impl<'a> TbsCertificate<'a> { /// or an error if the extension is invalid, or is present twice or more. pub fn extended_key_usage( &self, - ) -> Result>, X509Error> { + ) -> Result>>, X509Error> { self.get_extension_unique(&OID_X509_EXT_EXTENDED_KEY_USAGE)? .map_or(Ok(None), |ext| match ext.parsed_extension { ParsedExtension::ExtendedKeyUsage(ref value) => { @@ -449,7 +449,9 @@ impl<'a> TbsCertificate<'a> { /// /// 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>, X509Error> { + pub fn policy_mappings( + &self, + ) -> Result>>, X509Error> { self.get_extension_unique(&OID_X509_EXT_POLICY_MAPPINGS)? .map_or(Ok(None), |ext| match ext.parsed_extension { ParsedExtension::PolicyMappings(ref value) => { @@ -479,7 +481,9 @@ impl<'a> TbsCertificate<'a> { /// /// 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>, X509Error> { + pub fn name_constraints( + &self, + ) -> Result>>, X509Error> { self.get_extension_unique(&OID_X509_EXT_NAME_CONSTRAINTS)? .map_or(Ok(None), |ext| match ext.parsed_extension { ParsedExtension::NameConstraints(ref value) => { @@ -716,7 +720,7 @@ impl Validity { } impl FromDer<'_, X509Error> for Validity { - fn from_der(i: &[u8]) -> X509Result { + 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)?; @@ -739,14 +743,14 @@ impl<'a> UniqueIdentifier<'a> { } // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL - fn from_der_subject(i: &[u8]) -> X509Result> { + fn from_der_subject(i: &[u8]) -> X509Result<'_, Option>> { Self::parse::<2>(i).map_err(|_| X509Error::InvalidSubjectUID.into()) } // Parse a [tag] UniqueIdentifier OPTIONAL // // UniqueIdentifier ::= BIT STRING - fn parse(i: &[u8]) -> BerResult> { + fn parse(i: &[u8]) -> BerResult<'_, Option>> { let (rem, unique_id) = OptTaggedImplicit::::from_der(i)?; let unique_id = unique_id.map(|u| UniqueIdentifier(u.into_inner())); Ok((rem, unique_id)) diff --git a/src/certification_request.rs b/src/certification_request.rs index 1f16beb..dba019a 100644 --- a/src/certification_request.rs +++ b/src/certification_request.rs @@ -22,7 +22,7 @@ pub struct X509CertificationRequest<'a> { } impl X509CertificationRequest<'_> { - pub fn requested_extensions(&self) -> Option> { + pub fn requested_extensions(&self) -> Option>> { self.certification_request_info .iter_attributes() .find_map(|attr| { @@ -102,27 +102,27 @@ pub struct X509CertificationRequestInfo<'a> { impl X509CertificationRequestInfo<'_> { /// Get the CRL entry extensions. #[inline] - pub fn attributes(&self) -> &[X509CriAttribute] { + pub fn attributes(&self) -> &[X509CriAttribute<'_>] { &self.attributes } /// Returns an iterator over the CRL entry extensions #[inline] - pub fn iter_attributes(&self) -> impl Iterator { + pub fn iter_attributes(&self) -> impl Iterator> { 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> { + 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, X509Error> { + pub fn attributes_map(&self) -> Result, &X509CriAttribute<'_>>, X509Error> { self.attributes .iter() .try_fold(HashMap::new(), |mut m, ext| { diff --git a/src/cri_attributes.rs b/src/cri_attributes.rs index 78cb036..dc819b6 100644 --- a/src/cri_attributes.rs +++ b/src/cri_attributes.rs @@ -116,12 +116,12 @@ pub(crate) mod parser { } } - pub(super) fn parse_extension_request(i: &[u8]) -> X509Result { + 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 { + fn parse_extension_request_attr(i: &[u8]) -> X509Result<'_, ParsedCriAttribute<'_>> { map( parse_extension_request, ParsedCriAttribute::ExtensionRequest, @@ -143,7 +143,7 @@ pub(crate) mod parser { // utf8String UTF8String (SIZE (1..MAX)), // bmpString BMPString (SIZE (1..MAX)) // } - pub(super) fn parse_challenge_password(i: &[u8]) -> X509Result { + pub(super) fn parse_challenge_password(i: &[u8]) -> X509Result<'_, ChallengePassword> { let (rem, obj) = match alt(( parse_der_utf8string, parse_der_printablestring, @@ -161,7 +161,7 @@ pub(crate) mod parser { } } - fn parse_challenge_password_attr(i: &[u8]) -> X509Result { + fn parse_challenge_password_attr(i: &[u8]) -> X509Result<'_, ParsedCriAttribute<'_>> { map( parse_challenge_password, ParsedCriAttribute::ChallengePassword, @@ -169,7 +169,7 @@ pub(crate) mod parser { } } -pub(crate) fn parse_cri_attributes(i: &[u8]) -> X509Result> { +pub(crate) fn parse_cri_attributes(i: &[u8]) -> X509Result<'_, Vec>> { 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) diff --git a/src/extensions/generalname.rs b/src/extensions/generalname.rs index 77199ea..83a5b18 100644 --- a/src/extensions/generalname.rs +++ b/src/extensions/generalname.rs @@ -118,13 +118,13 @@ impl fmt::Display for GeneralName<'_> { } } -fn ia5str(any: Any) -> Result<&str, Err> { +fn ia5str(any: Any<'_>) -> Result<&str, Err> { // 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(|_| Err::Failure(Error::BerValueError)) } -pub(crate) fn parse_generalname(i: &[u8]) -> IResult<&[u8], GeneralName, Error> { +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/src/extensions/keyusage.rs b/src/extensions/keyusage.rs index ffb7f76..e68e198 100644 --- a/src/extensions/keyusage.rs +++ b/src/extensions/keyusage.rs @@ -110,7 +110,7 @@ pub(crate) fn parse_keyusage(i: &[u8]) -> IResult<&[u8], KeyUsage, BerError> { Ok((rest, KeyUsage { flags })) } -pub(crate) fn parse_extendedkeyusage(i: &[u8]) -> IResult<&[u8], ExtendedKeyUsage, BerError> { +pub(crate) fn parse_extendedkeyusage(i: &[u8]) -> IResult<&[u8], ExtendedKeyUsage<'_>, BerError> { let (ret, seq) = >::from_der(i)?; let mut seen = std::collections::HashSet::new(); let mut eku = ExtendedKeyUsage { diff --git a/src/extensions/mod.rs b/src/extensions/mod.rs index dd545c1..3bd4922 100644 --- a/src/extensions/mod.rs +++ b/src/extensions/mod.rs @@ -843,17 +843,17 @@ pub(crate) mod parser { } } - fn parse_basicconstraints_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + 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> { + 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> { + ) -> IResult<&[u8], ParsedExtension<'_>, BerError> { parse_der_sequence_defined_g(|input, _| { let (i, general_names) = all_consuming(many0(complete(cut(parse_generalname))))(input)?; Ok(( @@ -865,7 +865,7 @@ pub(crate) mod parser { pub(super) fn parse_issueralternativename_ext( i: &[u8], - ) -> IResult<&[u8], ParsedExtension, BerError> { + ) -> IResult<&[u8], ParsedExtension<'_>, BerError> { parse_der_sequence_defined_g(|input, _| { let (i, general_names) = all_consuming(many0(complete(cut(parse_generalname))))(input)?; Ok(( @@ -893,15 +893,15 @@ pub(crate) mod parser { })(i) } - fn parse_policyconstraints_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + 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> { + 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> { + fn parse_inhibitanypolicy_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension<'_>, BerError> { let (ret, skip_certs) = parse_der_u32(i)?; Ok(( ret, @@ -909,14 +909,16 @@ pub(crate) mod parser { )) } - fn parse_extendedkeyusage_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + 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> { + fn parse_distributionpointname( + i: &[u8], + ) -> IResult<&[u8], DistributionPointName<'_>, BerError> { let (rem, header) = der_read_element_header(i)?; match header.tag().0 { 0 => { @@ -962,7 +964,7 @@ pub(crate) mod parser { } } - fn parse_crlissuer_content(i: &[u8]) -> BerResult> { + fn parse_crlissuer_content(i: &[u8]) -> BerResult<'_, Vec>> { many1(complete(parse_generalname))(i) } @@ -972,7 +974,7 @@ pub(crate) mod parser { // cRLIssuer [2] GeneralNames OPTIONAL } pub(super) fn parse_crldistributionpoint( i: &[u8], - ) -> IResult<&[u8], CRLDistributionPoint, BerError> { + ) -> IResult<&[u8], CRLDistributionPoint<'_>, BerError> { parse_der_sequence_defined_g(|content, _| { let (rem, distribution_point) = opt(complete(parse_der_tagged_explicit_g(0, |b, _| { @@ -993,12 +995,12 @@ pub(crate) mod parser { pub(super) fn parse_crldistributionpoints( i: &[u8], - ) -> IResult<&[u8], CRLDistributionPoints, BerError> { + ) -> 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> { + fn parse_crldistributionpoints_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension<'_>, BerError> { map( parse_crldistributionpoints, ParsedExtension::CRLDistributionPoints, @@ -1014,7 +1016,7 @@ pub(crate) mod parser { // onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE } pub(super) fn parse_issuingdistributionpoint( i: &[u8], - ) -> IResult<&[u8], IssuingDistributionPoint, BerError> { + ) -> IResult<&[u8], IssuingDistributionPoint<'_>, BerError> { parse_der_sequence_defined_g(|content, _| { let parse_tagged_bool = |tag: u32, rem| -> IResult<&[u8], bool, BerError> { let (rem, value) = opt(complete(|_| { @@ -1047,7 +1049,9 @@ pub(crate) mod parser { })(i) } - fn parse_issuingdistributionpoint_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + fn parse_issuingdistributionpoint_ext( + i: &[u8], + ) -> IResult<&[u8], ParsedExtension<'_>, BerError> { map( parse_issuingdistributionpoint, ParsedExtension::IssuingDistributionPoint, @@ -1062,8 +1066,8 @@ pub(crate) mod parser { // accessLocation GeneralName } pub(super) fn parse_authorityinfoaccess( i: &[u8], - ) -> IResult<&[u8], AuthorityInfoAccess, BerError> { - fn parse_aia(i: &[u8]) -> IResult<&[u8], AccessDescription, BerError> { + ) -> 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)?; @@ -1076,7 +1080,7 @@ pub(crate) mod parser { Ok((ret, AuthorityInfoAccess { accessdescs })) } - fn parse_authorityinfoaccess_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + fn parse_authorityinfoaccess_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension<'_>, BerError> { map( parse_authorityinfoaccess, ParsedExtension::AuthorityInfoAccess, @@ -1089,8 +1093,10 @@ pub(crate) mod parser { // AccessDescription ::= SEQUENCE { // accessMethod OBJECT IDENTIFIER, // accessLocation GeneralName } - pub(super) fn parse_subjectinfoaccess(i: &[u8]) -> IResult<&[u8], SubjectInfoAccess, BerError> { - fn parse_sia(i: &[u8]) -> IResult<&[u8], AccessDescription, BerError> { + pub(super) fn parse_subjectinfoaccess( + i: &[u8], + ) -> IResult<&[u8], SubjectInfoAccess<'_>, BerError> { + fn parse_sia(i: &[u8]) -> IResult<&[u8], AccessDescription<'_>, BerError> { parse_der_sequence_defined_g(|content, _| { // Read first element, an oid. let (gn, oid) = Oid::from_der(content)?; @@ -1103,7 +1109,7 @@ pub(crate) mod parser { Ok((ret, SubjectInfoAccess { accessdescs })) } - fn parse_subjectinfoaccess_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + fn parse_subjectinfoaccess_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension<'_>, BerError> { map(parse_subjectinfoaccess, ParsedExtension::SubjectInfoAccess)(i) } @@ -1134,29 +1140,29 @@ pub(crate) mod parser { // RFC 5280 section 4.2.1.1: Authority Key Identifier pub(super) fn parse_authoritykeyidentifier( i: &[u8], - ) -> IResult<&[u8], AuthorityKeyIdentifier, BerError> { + ) -> 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> { + 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> { + 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> { + 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> { + fn parse_keyusage_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension<'_>, BerError> { map(parse_keyusage, ParsedExtension::KeyUsage)(i) } @@ -1174,11 +1180,11 @@ pub(crate) mod parser { Ok((rest, NSCertType(flags))) } - fn parse_nscerttype_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + 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> { + fn parse_nscomment_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension<'_>, BerError> { match parse_der_ia5string(i) { Ok((i, obj)) => { let s = obj.as_str()?; @@ -1215,8 +1221,10 @@ pub(crate) mod parser { // PolicyQualifierId ::= OBJECT IDENTIFIER ( id-qt-cps | id-qt-unotice ) pub(super) fn parse_certificatepolicies( i: &[u8], - ) -> IResult<&[u8], Vec, BerError> { - fn parse_policy_qualifier_info(i: &[u8]) -> IResult<&[u8], PolicyQualifierInfo, BerError> { + ) -> IResult<&[u8], Vec>, 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 { @@ -1226,7 +1234,7 @@ pub(crate) mod parser { Ok((&[], info)) })(i) } - fn parse_policy_information(i: &[u8]) -> IResult<&[u8], PolicyInformation, BerError> { + 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) = @@ -1243,7 +1251,7 @@ pub(crate) mod parser { parse_der_sequence_of_v(parse_policy_information)(i) } - fn parse_certificatepolicies_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + fn parse_certificatepolicies_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension<'_>, BerError> { map( parse_certificatepolicies, ParsedExtension::CertificatePolicies, @@ -1251,7 +1259,7 @@ pub(crate) mod parser { } // CRLReason ::= ENUMERATED { ... - fn parse_reason_code(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { + fn parse_reason_code(i: &[u8]) -> IResult<&[u8], ParsedExtension<'_>, BerError> { let (rest, obj) = parse_der_enum(i)?; let code = obj .content @@ -1265,7 +1273,7 @@ pub(crate) mod parser { } // invalidityDate ::= GeneralizedTime - fn parse_invalidity_date(i: &[u8]) -> ParseResult { + 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)))) @@ -1273,12 +1281,12 @@ pub(crate) mod parser { // 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> { + 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> { + fn parse_sct_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension<'_>, BerError> { map( parse_ct_signed_certificate_timestamp_list, ParsedExtension::SCT, @@ -1287,13 +1295,16 @@ pub(crate) mod parser { } /// Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension -pub(crate) fn parse_extension_sequence(i: &[u8]) -> X509Result> { +pub(crate) fn parse_extension_sequence(i: &[u8]) -> X509Result<'_, Vec>> { 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> { +pub(crate) fn parse_extensions( + i: &[u8], + explicit_tag: Tag, +) -> X509Result<'_, Vec>> { if i.is_empty() { return Ok((i, Vec::new())); } @@ -1310,7 +1321,9 @@ pub(crate) fn parse_extensions(i: &[u8], explicit_tag: Tag) -> X509Result X509Result> { +pub(crate) fn parse_extension_envelope_sequence( + i: &[u8], +) -> X509Result<'_, Vec>> { let parser = X509ExtensionParser::new().with_deep_parse_extensions(false); parse_der_sequence_defined_g(move |a, _| all_consuming(many0(complete(parser)))(a))(i) @@ -1319,7 +1332,7 @@ pub(crate) fn parse_extension_envelope_sequence(i: &[u8]) -> X509Result X509Result> { +) -> X509Result<'_, Vec>> { if i.is_empty() { return Ok((i, Vec::new())); } @@ -1335,7 +1348,7 @@ pub(crate) fn parse_extensions_envelope( } } -fn der_read_critical(i: &[u8]) -> BerResult { +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)?; diff --git a/src/extensions/nameconstraints.rs b/src/extensions/nameconstraints.rs index da9a999..3dadbcb 100644 --- a/src/extensions/nameconstraints.rs +++ b/src/extensions/nameconstraints.rs @@ -29,13 +29,13 @@ pub struct GeneralSubtree<'a> { // maximum: Option, } -pub(crate) fn parse_nameconstraints(i: &[u8]) -> IResult<&[u8], NameConstraints, BerError> { - fn parse_subtree(i: &[u8]) -> IResult<&[u8], GeneralSubtree, BerError> { +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, BerError> { + fn parse_subtrees(i: &[u8]) -> IResult<&[u8], Vec>, BerError> { all_consuming(many1(complete(parse_subtree)))(i) } diff --git a/src/extensions/policymappings.rs b/src/extensions/policymappings.rs index 2bd367d..380da8f 100644 --- a/src/extensions/policymappings.rs +++ b/src/extensions/policymappings.rs @@ -75,7 +75,7 @@ impl<'a> PolicyMapping<'a> { // PolicyMappings ::= SEQUENCE SIZE (1..MAX) OF SEQUENCE { // issuerDomainPolicy CertPolicyId, // subjectDomainPolicy CertPolicyId } -pub(crate) fn parse_policymappings(i: &[u8]) -> IResult<&[u8], PolicyMappings, Error> { +pub(crate) fn parse_policymappings(i: &[u8]) -> IResult<&[u8], PolicyMappings<'_>, Error> { let (ret, pairs) = >::from_der(i)?; // let mut mappings: HashMap> = HashMap::new(); let mappings = pairs; diff --git a/src/extensions/sct.rs b/src/extensions/sct.rs index 646f774..f2db52a 100644 --- a/src/extensions/sct.rs +++ b/src/extensions/sct.rs @@ -52,7 +52,7 @@ pub struct DigitallySigned<'a> { /// Parses a list of Signed Certificate Timestamp entries pub fn parse_ct_signed_certificate_timestamp_list( i: &[u8], -) -> IResult<&[u8], Vec, BerError> { +) -> IResult<&[u8], Vec>, BerError> { // use nom::HexDisplay; // eprintln!("{}", i.to_hex(16)); let (rem, b) = <&[u8]>::from_der(i)?; @@ -67,7 +67,7 @@ pub fn parse_ct_signed_certificate_timestamp_list( /// Parses as single Signed Certificate Timestamp entry pub fn parse_ct_signed_certificate_timestamp( i: &[u8], -) -> IResult<&[u8], SignedCertificateTimestamp, BerError> { +) -> IResult<&[u8], SignedCertificateTimestamp<'_>, BerError> { map_parser( length_data(be_u16), parse_ct_signed_certificate_timestamp_content, @@ -76,7 +76,7 @@ pub fn parse_ct_signed_certificate_timestamp( pub(crate) fn parse_ct_signed_certificate_timestamp_content( i: &[u8], -) -> IResult<&[u8], SignedCertificateTimestamp, BerError> { +) -> IResult<&[u8], SignedCertificateTimestamp<'_>, BerError> { let (i, version) = be_u8(i)?; let (i, id) = parse_log_id(i)?; let (i, timestamp) = be_u64(i)?; @@ -93,7 +93,7 @@ pub(crate) fn parse_ct_signed_certificate_timestamp_content( } // Safety: cannot fail, take() returns exactly 32 bytes -fn parse_log_id(i: &[u8]) -> IResult<&[u8], CtLogID, BerError> { +fn parse_log_id(i: &[u8]) -> IResult<&[u8], CtLogID<'_>, BerError> { let (i, key_id) = take(32usize)(i)?; Ok(( i, @@ -105,13 +105,13 @@ fn parse_log_id(i: &[u8]) -> IResult<&[u8], CtLogID, BerError> { )) } -fn parse_ct_extensions(i: &[u8]) -> IResult<&[u8], CtExtensions, BerError> { +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> { +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)?; diff --git a/src/lib.rs b/src/lib.rs index e695030..b1cc6f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -177,7 +177,7 @@ use revocation_list::CertificateRevocationList; /// /// For PEM-encoded certificates, use the [`pem`](pem/index.html) module. #[inline] -pub fn parse_x509_certificate(i: &[u8]) -> X509Result { +pub fn parse_x509_certificate(i: &[u8]) -> X509Result<'_, X509Certificate<'_>> { X509Certificate::from_der(i) } @@ -187,7 +187,7 @@ pub fn parse_x509_certificate(i: &[u8]) -> X509Result { /// 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 { +pub fn parse_x509_crl(i: &[u8]) -> X509Result<'_, CertificateRevocationList<'_>> { CertificateRevocationList::from_der(i) } @@ -197,7 +197,7 @@ pub fn parse_x509_crl(i: &[u8]) -> X509Result { note = "please use `parse_x509_certificate` or `X509Certificate::from_der` instead" )] #[inline] -pub fn parse_x509_der(i: &[u8]) -> X509Result { +pub fn parse_x509_der(i: &[u8]) -> X509Result<'_, X509Certificate<'_>> { X509Certificate::from_der(i) } @@ -208,6 +208,6 @@ pub fn parse_x509_der(i: &[u8]) -> X509Result { note = "please use `parse_x509_crl` or `CertificateRevocationList::from_der` instead" )] #[inline] -pub fn parse_crl_der(i: &[u8]) -> X509Result { +pub fn parse_crl_der(i: &[u8]) -> X509Result<'_, CertificateRevocationList<'_>> { CertificateRevocationList::from_der(i) } diff --git a/src/pem.rs b/src/pem.rs index 73c3738..f2f930f 100644 --- a/src/pem.rs +++ b/src/pem.rs @@ -170,7 +170,7 @@ impl Pem { } /// Decode the PEM contents into a X.509 object - pub fn parse_x509(&self) -> Result> { + pub fn parse_x509(&self) -> Result, Err> { parse_x509_certificate(&self.contents).map(|(_, x509)| x509) } diff --git a/src/public_key.rs b/src/public_key.rs index 9a6fe7a..413ffe1 100644 --- a/src/public_key.rs +++ b/src/public_key.rs @@ -73,7 +73,7 @@ impl RSAPublicKey<'_> { } // helper function to parse with error type BerError -fn parse_rsa_key(bytes: &[u8]) -> BerResult { +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)?; diff --git a/src/revocation_list.rs b/src/revocation_list.rs index 903e87b..19d3c45 100644 --- a/src/revocation_list.rs +++ b/src/revocation_list.rs @@ -61,7 +61,7 @@ impl<'a> CertificateRevocationList<'a> { /// Get the certificate issuer. #[inline] - pub fn issuer(&self) -> &X509Name { + pub fn issuer(&self) -> &X509Name<'_> { &self.tbs_cert_list.issuer } @@ -84,7 +84,7 @@ impl<'a> CertificateRevocationList<'a> { /// Get the CRL extensions. #[inline] - pub fn extensions(&self) -> &[X509Extension] { + pub fn extensions(&self) -> &[X509Extension<'_>] { &self.tbs_cert_list.extensions } @@ -183,27 +183,27 @@ pub struct TbsCertList<'a> { impl TbsCertList<'_> { /// Returns the certificate extensions #[inline] - pub fn extensions(&self) -> &[X509Extension] { + pub fn extensions(&self) -> &[X509Extension<'_>] { &self.extensions } /// Returns an iterator over the certificate extensions #[inline] - pub fn iter_extensions(&self) -> impl Iterator { + pub fn iter_extensions(&self) -> impl Iterator> { 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> { + 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, X509Error> { + pub fn extensions_map(&self) -> Result, &X509Extension<'_>>, X509Error> { self.extensions .iter() .try_fold(HashMap::new(), |mut m, ext| { @@ -269,27 +269,27 @@ impl RevokedCertificate<'_> { /// Get the CRL entry extensions. #[inline] - pub fn extensions(&self) -> &[X509Extension] { + pub fn extensions(&self) -> &[X509Extension<'_>] { &self.extensions } /// Returns an iterator over the CRL entry extensions #[inline] - pub fn iter_extensions(&self) -> impl Iterator { + pub fn iter_extensions(&self) -> impl Iterator> { 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> { + 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, X509Error> { + pub fn extensions_map(&self) -> Result, &X509Extension<'_>>, X509Error> { self.extensions .iter() .try_fold(HashMap::new(), |mut m, ext| { @@ -356,7 +356,7 @@ impl<'a> FromDer<'a, X509Error> for RevokedCertificate<'a> { } } -fn parse_revoked_certificates(i: &[u8]) -> X509Result> { +fn parse_revoked_certificates(i: &[u8]) -> X509Result<'_, Vec>> { parse_der_sequence_defined_g(|a, _| { all_consuming(many0(complete(RevokedCertificate::from_der)))(a) })(i) diff --git a/src/signature_algorithm.rs b/src/signature_algorithm.rs index c833aa5..f8adb98 100644 --- a/src/signature_algorithm.rs +++ b/src/signature_algorithm.rs @@ -91,12 +91,12 @@ pub struct RsaSsaPssParams<'a> { impl<'a> RsaSsaPssParams<'a> { /// Get a reference to the rsa ssa pss params's hash algorithm. - pub fn hash_algorithm(&self) -> Option<&AlgorithmIdentifier> { + 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 { + pub fn hash_algorithm_oid(&self) -> &'a Oid<'_> { const SHA1: &Oid = &OID_HASH_SHA1; self.hash_alg .as_ref() @@ -105,14 +105,14 @@ impl<'a> RsaSsaPssParams<'a> { } /// Get a reference to the rsa ssa pss params's mask generation algorithm. - pub fn mask_gen_algorithm_raw(&self) -> Option<&AlgorithmIdentifier> { + 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 { + pub fn mask_gen_algorithm(&self) -> Result, X509Error> { match self.mask_gen_algorithm.as_ref() { Some(alg) => { let (_, hash) = alg @@ -222,12 +222,12 @@ impl<'a> RsaAesOaepParams<'a> { ); /// Get a reference to the rsa aes oaep params's hash algorithm. - pub fn hash_algorithm(&self) -> Option<&AlgorithmIdentifier> { + 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 { + pub fn hash_algorithm_oid(&self) -> &'a Oid<'_> { const SHA1: &Oid = &OID_HASH_SHA1; self.hash_alg .as_ref() @@ -236,14 +236,14 @@ impl<'a> RsaAesOaepParams<'a> { } /// Get a reference to the rsa ssa pss params's mask generation algorithm. - pub fn mask_gen_algorithm_raw(&self) -> Option<&AlgorithmIdentifier> { + 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 { + pub fn mask_gen_algorithm(&self) -> Result, X509Error> { match self.mask_gen_alg.as_ref() { Some(alg) => { let (_, hash) = alg diff --git a/src/time.rs b/src/time.rs index 6dae5ca..73086dc 100644 --- a/src/time.rs +++ b/src/time.rs @@ -16,7 +16,7 @@ pub struct ASN1Time { } impl ASN1Time { - pub(crate) fn from_der_opt(i: &[u8]) -> X509Result> { + pub(crate) fn from_der_opt(i: &[u8]) -> X509Result<'_, Option> { if i.is_empty() { return Ok((i, None)); } @@ -110,13 +110,13 @@ impl ASN1Time { } impl FromDer<'_, X509Error> for ASN1Time { - fn from_der(i: &[u8]) -> X509Result { + fn from_der(i: &[u8]) -> X509Result<'_, Self> { let (rem, time) = parse_choice_of_time(i).map_err(|_| X509Error::InvalidDate)?; Ok((rem, time)) } } -pub(crate) fn parse_choice_of_time(i: &[u8]) -> ParseResult { +pub(crate) fn parse_choice_of_time(i: &[u8]) -> ParseResult<'_, ASN1Time> { if let Ok((rem, t)) = UtcTime::from_der(i) { let dt = t.utc_adjusted_datetime()?; return Ok((rem, ASN1Time::new_utc(dt))); @@ -129,7 +129,7 @@ pub(crate) fn parse_choice_of_time(i: &[u8]) -> ParseResult { } // allow relaxed parsing of UTCTime (ex: 370116130016+0000) -fn parse_malformed_date(i: &[u8]) -> ParseResult { +fn parse_malformed_date(i: &[u8]) -> ParseResult<'_, ASN1Time> { #[allow(clippy::trivially_copy_pass_by_ref)] // fn check_char(b: &u8) -> bool { // (0x20 <= *b && *b <= 0x7f) || (*b == b'+') diff --git a/src/x509.rs b/src/x509.rs index 4142dbf..711e47c 100644 --- a/src/x509.rs +++ b/src/x509.rs @@ -40,7 +40,7 @@ pub struct X509Version(pub u32); impl X509Version { /// Parse `[0]` EXPLICIT Version DEFAULT v1 - pub(crate) fn from_der_tagged_0(i: &[u8]) -> X509Result { + 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)?; @@ -153,11 +153,11 @@ impl<'a> FromDer<'a, X509Error> for AttributeTypeAndValue<'a> { // AttributeValue ::= ANY -- DEFINED BY AttributeType #[inline] -fn parse_attribute_value(i: &[u8]) -> ParseResult { +fn parse_attribute_value(i: &[u8]) -> ParseResult<'_, Any<'_>, Error> { alt((Any::from_der, parse_malformed_string))(i) } -fn parse_malformed_string(i: &[u8]) -> ParseResult { +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 { @@ -231,7 +231,7 @@ pub struct SubjectPublicKeyInfo<'a> { impl SubjectPublicKeyInfo<'_> { /// Attempt to parse the public key, and return the parsed version or an error - pub fn parsed(&self) -> Result { + pub fn parsed(&self) -> Result, 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)?; @@ -571,11 +571,11 @@ fn x509name_to_string( }) } -pub(crate) fn parse_signature_value(i: &[u8]) -> X509Result { +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)> { +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, From 8c54b1acfe12ebae59a065954ab9073a0c5eddab Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Thu, 31 Jul 2025 15:07:15 +0200 Subject: [PATCH 15/24] Add new feature `verify-aws` to used `aws-lc-rs` as crypto provider instead of `ring` --- Cargo.lock | 423 ++++++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 2 + src/lib.rs | 4 +- src/verify.rs | 23 +-- 4 files changed, 427 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 368df95..2caca0c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "asn1-rs" version = "0.7.1" @@ -47,21 +56,105 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "aws-lc-rs" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba" +dependencies = [ + "aws-lc-sys", + "untrusted 0.7.1", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + [[package]] name = "cc" version = "1.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" dependencies = [ + "jobserver", + "libc", "shlex", ] +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + [[package]] name = "data-encoding" version = "2.9.0" @@ -102,6 +195,34 @@ dependencies = [ "syn", ] +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "errno" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "getrandom" version = "0.2.16" @@ -110,7 +231,43 @@ checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", ] [[package]] @@ -119,18 +276,56 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "jobserver" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.3", + "libc", +] + [[package]] name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +[[package]] +name = "libloading" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +dependencies = [ + "cfg-if", + "windows-targets 0.53.3", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + [[package]] name = "memchr" version = "2.7.5" @@ -196,12 +391,28 @@ dependencies = [ "asn1-rs", ] +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + [[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "prettyplease" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff24dfcda44452b9816fff4cd4227e1bb73ff5a2f1bc1105aa92fb8565ce44d2" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" version = "1.0.95" @@ -220,6 +431,41 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "ring" version = "0.17.14" @@ -228,12 +474,18 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.16", "libc", - "untrusted", - "windows-sys", + "untrusted 0.9.0", + "windows-sys 0.52.0", ] +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rusticata-macros" version = "4.1.0" @@ -243,6 +495,19 @@ dependencies = [ "nom", ] +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + [[package]] name = "serde" version = "1.0.219" @@ -348,6 +613,12 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "untrusted" version = "0.9.0" @@ -360,13 +631,58 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", ] [[package]] @@ -375,14 +691,31 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", ] [[package]] @@ -391,53 +724,111 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + [[package]] name = "x509-parser" version = "0.18.0" dependencies = [ "asn1-rs", + "aws-lc-rs", "data-encoding", "der-parser", "lazy_static", @@ -448,3 +839,9 @@ dependencies = [ "thiserror", "time", ] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index 9fca921..38893fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,10 +38,12 @@ rustdoc-args = ["--cfg", "docsrs"] [features] default = [] +verify-aws = ["aws-lc-rs"] verify = ["ring"] validate = [] [dependencies] +aws-lc-rs = { version = "1.0", optional = true } asn1-rs = { version = "0.7.0", features=["datetime"] } data-encoding = "2.2.1" lazy_static = "1.4" diff --git a/src/lib.rs b/src/lib.rs index b1cc6f6..937a39c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -150,8 +150,8 @@ 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")))] +#[cfg(any(feature = "verify", feature = "verify-aws"))] +#[cfg_attr(docsrs, doc(cfg(any(feature = "verify", feature = "verify-aws"))))] pub mod verify; pub mod visitor; pub mod x509; diff --git a/src/verify.rs b/src/verify.rs index 0e5288b..94a2fce 100644 --- a/src/verify.rs +++ b/src/verify.rs @@ -9,19 +9,25 @@ use oid_registry::{ }; use std::convert::TryFrom; +// Since the `signature` object is similar in ring and in aws-lc-rs, we just use simple logic +// to determine which one to use. +// If both verify and verify-aws features are enabled, aws will be used. +#[cfg(feature = "verify-aws")] +use aws_lc_rs::signature; +#[cfg(all(feature = "verify", not(feature = "verify-aws")))] +use ring::signature; + /// 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. +/// Not all algorithms are supported, this function is limited to what `aws_lc_rs` or `ring` supports. pub fn verify_signature( public_key: &SubjectPublicKeyInfo, signature_algorithm: &AlgorithmIdentifier, signature_value: &BitString, raw_data: &[u8], ) -> Result<(), X509Error> { - use ring::signature; - let AlgorithmIdentifier { algorithm: signature_algorithm, parameters: signature_algorithm_parameters, @@ -63,12 +69,11 @@ pub fn verify_signature( /// 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. +/// Not all algorithms are supported, we are limited to what `aws_lc_rs` or `ring`supports. fn get_ec_curve_sha( pubkey_alg: &AlgorithmIdentifier, sha_len: usize, -) -> Option<&'static dyn ring::signature::VerificationAlgorithm> { - use ring::signature; +) -> Option<&'static dyn signature::VerificationAlgorithm> { 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 { @@ -90,13 +95,11 @@ fn get_ec_curve_sha( /// Find the verification algorithm for the given RSA-PSS parameters /// -/// Not all algorithms are supported, we are limited to what `ring` supports. +/// Not all algorithms are supported, we are limited to what `aws_lc_rs` or `ring` supports. /// Notably, the SHA-1 hash algorithm is not supported. fn get_rsa_pss_verification_algo( params: &Option, -) -> Option<&'static dyn ring::signature::VerificationAlgorithm> { - use ring::signature; - +) -> Option<&'static dyn signature::VerificationAlgorithm> { let params = params.as_ref()?; let params = RsaSsaPssParams::try_from(params).ok()?; let hash_algo = params.hash_algorithm_oid(); From 0b87559a55a429037c82902af65c67552810c5ae Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Thu, 31 Jul 2025 15:08:07 +0200 Subject: [PATCH 16/24] CI: add `verify-aws` feature test in test matrix --- .github/workflows/rust.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f1fd338..6330417 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -70,6 +70,7 @@ jobs: - --features=default - --all-features - --features=verify + - --features=verify-aws - --features=validate steps: - uses: actions/checkout@v4 From e57a1b8314063fe6abec85816264755b21f87f75 Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Fri, 1 Aug 2025 12:26:33 +0200 Subject: [PATCH 17/24] Add documentation for features `verify` and `verify-aws` --- src/lib.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 937a39c..1817746 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -105,6 +105,13 @@ //! } //! ``` //! +//! - The `verify-aws` feature offers the same support for signature verification, but based on +//! `aws-lc-rs` instead of `ring`. +//! +//! - _Note_: if both `verify` and `verify-aws` features are enabled (which happens when using +//! `--all-features`), the verification will use `aws-lc-rs`. It also has the side-effect of +//! having a dependency on `ring`, even if it is not used. +//! //! - 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). From b335e9c1804222880e68294f4e2cae75f0941ff9 Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Wed, 13 Aug 2025 18:39:29 +0200 Subject: [PATCH 18/24] Run `cargo update` --- Cargo.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2caca0c..fc0081c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -111,9 +111,9 @@ checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] name = "cc" -version = "1.2.31" +version = "1.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" +checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e" dependencies = [ "jobserver", "libc", @@ -248,9 +248,9 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "home" @@ -300,9 +300,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.174" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libloading" @@ -415,9 +415,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "d61789d7719defeb74ea5fe81f2fdfdbd28a803847077cecce2ff14e1472f6f1" dependencies = [ "unicode-ident", ] @@ -536,9 +536,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "syn" -version = "2.0.104" +version = "2.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "7bc3fcb250e53458e712715cf74285c1f889686520d79294a9ef3bd7aa1fc619" dependencies = [ "proc-macro2", "quote", @@ -558,18 +558,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227" dependencies = [ "proc-macro2", "quote", From c6577528174ce011d12d55aaff010ca89eb8c073 Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Wed, 13 Aug 2025 18:33:29 +0200 Subject: [PATCH 19/24] Add `as_raw` method to Certificate to expose raw bytes (closes #217) --- src/certificate.rs | 19 ++++++++++++++++--- tests/readcert.rs | 2 ++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/certificate.rs b/src/certificate.rs index e1c8843..a5ebb0a 100644 --- a/src/certificate.rs +++ b/src/certificate.rs @@ -66,9 +66,17 @@ pub struct X509Certificate<'a> { pub tbs_certificate: TbsCertificate<'a>, pub signature_algorithm: AlgorithmIdentifier<'a>, pub signature_value: BitString<'a>, + + pub(crate) raw: &'a [u8], } -impl X509Certificate<'_> { +impl<'a> X509Certificate<'a> { + /// Return a reference to the raw bytes used to parse the certificate + // Not using the AsRef trait, as that would not give back the full 'a lifetime + pub fn as_raw(&self) -> &'a [u8] { + self.raw + } + /// Verify the cryptographic signature of this certificate /// /// `public_key` is the public key of the **signer**. For a self-signed certificate, @@ -212,7 +220,8 @@ impl Default for X509CertificateParser { 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, _| { + let start_i = input; + let (rem, mut cert) = 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); @@ -223,9 +232,13 @@ impl<'a> Parser<&'a [u8], X509Certificate<'a>, X509Error> for X509CertificatePar tbs_certificate, signature_algorithm, signature_value, + raw: &[], }; Ok((i, cert)) - })(input) + })(input)?; + let len = start_i.offset(rem); + cert.raw = &start_i[..len]; + Ok((rem, cert)) } } diff --git a/tests/readcert.rs b/tests/readcert.rs index 05fc80a..9e8c4a8 100644 --- a/tests/readcert.rs +++ b/tests/readcert.rs @@ -123,6 +123,8 @@ fn test_x509_parser() { assert!(tbs_cert.is_ca()); // assert_eq!(tbs_cert.as_ref(), &IGCA_DER[4..(8 + 746)]); + // check that cert.as_raw() returns the certificate bytes + assert_eq!(cert.as_raw(), IGCA_DER); } _ => panic!("x509 parsing failed: {:?}", res), } From 950e457d3620a43f545490b9756127968101119f Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Wed, 13 Aug 2025 18:42:34 +0200 Subject: [PATCH 20/24] Add `as_raw` method to CertificateRevocationList to expose raw bytes --- src/revocation_list.rs | 17 +++++++++++++++-- tests/readcrl.rs | 3 +++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/revocation_list.rs b/src/revocation_list.rs index 19d3c45..f7c7414 100644 --- a/src/revocation_list.rs +++ b/src/revocation_list.rs @@ -51,6 +51,8 @@ pub struct CertificateRevocationList<'a> { pub tbs_cert_list: TbsCertList<'a>, pub signature_algorithm: AlgorithmIdentifier<'a>, pub signature_value: BitString<'a>, + + pub(crate) raw: &'a [u8], } impl<'a> CertificateRevocationList<'a> { @@ -121,6 +123,12 @@ impl<'a> CertificateRevocationList<'a> { self.tbs_cert_list.raw, ) } + /// + /// Return a reference to the raw bytes used to parse the Certificate Revocation List + // Not using the AsRef trait, as that would not give back the full 'a lifetime + pub fn as_raw(&self) -> &'a [u8] { + self.raw + } } ///
@@ -131,7 +139,8 @@ impl<'a> CertificateRevocationList<'a> {
 /// 
impl<'a> FromDer<'a, X509Error> for CertificateRevocationList<'a> { fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { - parse_der_sequence_defined_g(|i, _| { + let start_i = i; + let (rem, mut crl) = 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)?; @@ -139,9 +148,13 @@ impl<'a> FromDer<'a, X509Error> for CertificateRevocationList<'a> { tbs_cert_list, signature_algorithm, signature_value, + raw: &[], }; Ok((i, crl)) - })(i) + })(i)?; + let len = start_i.offset(rem); + crl.raw = &start_i[..len]; + Ok((rem, crl)) } } diff --git a/tests/readcrl.rs b/tests/readcrl.rs index 0fa7a86..710d5d7 100644 --- a/tests/readcrl.rs +++ b/tests/readcrl.rs @@ -11,6 +11,9 @@ fn read_crl_verify() { let res = crl.verify_signature(&x509_ca.tbs_certificate.subject_pki); eprintln!("Verification: {res:?}"); assert!(res.is_ok()); + + // check that `.as_raw()` returns the input bytes + assert_eq!(crl.as_raw(), CRL_DATA); } fn crl_idp<'a>(crl: &'a CertificateRevocationList) -> &'a IssuingDistributionPoint<'a> { From f94893c41e6303c8262389c34153c2c78be190b9 Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Wed, 13 Aug 2025 18:52:32 +0200 Subject: [PATCH 21/24] Add `as_raw` method to X509CertificationRequest to expose raw bytes --- src/certification_request.rs | 19 ++++++++++++++++--- tests/readcsr.rs | 7 +++++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/certification_request.rs b/src/certification_request.rs index dba019a..99a69d0 100644 --- a/src/certification_request.rs +++ b/src/certification_request.rs @@ -19,9 +19,17 @@ pub struct X509CertificationRequest<'a> { pub certification_request_info: X509CertificationRequestInfo<'a>, pub signature_algorithm: AlgorithmIdentifier<'a>, pub signature_value: BitString<'a>, + + pub(crate) raw: &'a [u8], } -impl X509CertificationRequest<'_> { +impl<'a> X509CertificationRequest<'a> { + /// Return a reference to the raw bytes used to parse the Certificate Request + // Not using the AsRef trait, as that would not give back the full 'a lifetime + pub fn as_raw(&self) -> &'a [u8] { + self.raw + } + pub fn requested_extensions(&self) -> Option>> { self.certification_request_info .iter_attributes() @@ -59,7 +67,8 @@ impl X509CertificationRequest<'_> { /// impl<'a> FromDer<'a, X509Error> for X509CertificationRequest<'a> { fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { - parse_der_sequence_defined_g(|i, _| { + let start_i = i; + let (rem, mut req) = 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)?; @@ -67,9 +76,13 @@ impl<'a> FromDer<'a, X509Error> for X509CertificationRequest<'a> { certification_request_info, signature_algorithm, signature_value, + raw: &[], }; Ok((i, cert)) - })(i) + })(i)?; + let len = start_i.offset(rem); + req.raw = &start_i[..len]; + Ok((rem, req)) } } diff --git a/tests/readcsr.rs b/tests/readcsr.rs index 1124c48..fc61ffd 100644 --- a/tests/readcsr.rs +++ b/tests/readcsr.rs @@ -109,8 +109,8 @@ fn read_csr_with_challenge_password() { #[cfg(feature = "verify")] #[test] fn read_csr_verify() { - let der = pem::parse_x509_pem(CSR_DATA).unwrap().1; - let (_, csr) = X509CertificationRequest::from_der(&der.contents).expect("could not parse CSR"); + let pem = pem::parse_x509_pem(CSR_DATA).unwrap().1; + let (_, csr) = X509CertificationRequest::from_der(&pem.contents).expect("could not parse CSR"); csr.verify_signature().unwrap(); let mut der = pem::parse_x509_pem(CSR_DATA).unwrap().1; @@ -122,4 +122,7 @@ fn read_csr_verify() { let (_, csr) = X509CertificationRequest::from_der(&der.contents).expect("could not parse CSR"); csr.verify_signature().unwrap_err(); + + // check that `.as_raw()` returns the input bytes + assert_eq!(csr.as_raw(), &der.contents); } From 9d2b06d1011689449282af1449a4bfad41fd88e9 Mon Sep 17 00:00:00 2001 From: Pierre Chifflier Date: Wed, 13 Aug 2025 19:29:03 +0200 Subject: [PATCH 22/24] Add `AsRef<[u8]>` implementations (based on `as_raw()`) --- src/certificate.rs | 6 ++++++ src/certification_request.rs | 6 ++++++ src/revocation_list.rs | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/src/certificate.rs b/src/certificate.rs index a5ebb0a..4b74da9 100644 --- a/src/certificate.rs +++ b/src/certificate.rs @@ -103,6 +103,12 @@ impl<'a> X509Certificate<'a> { } } +impl<'a> AsRef<[u8]> for X509Certificate<'a> { + fn as_ref(&self) -> &[u8] { + self.as_raw() + } +} + impl<'a> Deref for X509Certificate<'a> { type Target = TbsCertificate<'a>; diff --git a/src/certification_request.rs b/src/certification_request.rs index 99a69d0..2e8e770 100644 --- a/src/certification_request.rs +++ b/src/certification_request.rs @@ -58,6 +58,12 @@ impl<'a> X509CertificationRequest<'a> { } } +impl<'a> AsRef<[u8]> for X509CertificationRequest<'a> { + fn as_ref(&self) -> &[u8] { + self.as_raw() + } +} + ///
 /// CertificationRequest ::= SEQUENCE {
 ///     certificationRequestInfo CertificationRequestInfo,
diff --git a/src/revocation_list.rs b/src/revocation_list.rs
index f7c7414..ca05024 100644
--- a/src/revocation_list.rs
+++ b/src/revocation_list.rs
@@ -131,6 +131,12 @@ impl<'a> CertificateRevocationList<'a> {
     }
 }
 
+impl<'a> AsRef<[u8]> for CertificateRevocationList<'a> {
+    fn as_ref(&self) -> &[u8] {
+        self.as_raw()
+    }
+}
+
 /// 
 /// CertificateList  ::=  SEQUENCE  {
 ///      tbsCertList          TBSCertList,

From ba89c10f893fbfc642f8d3c6a75e99500569938b Mon Sep 17 00:00:00 2001
From: Pierre Chifflier 
Date: Fri, 29 Aug 2025 10:14:24 +0200
Subject: [PATCH 23/24] Improve documentation for the `as_raw` methods

---
 src/certificate.rs           |  9 +++++++--
 src/certification_request.rs |  8 ++++++--
 src/revocation_list.rs       | 10 ++++++++--
 3 files changed, 21 insertions(+), 6 deletions(-)

diff --git a/src/certificate.rs b/src/certificate.rs
index 4b74da9..75e46e7 100644
--- a/src/certificate.rs
+++ b/src/certificate.rs
@@ -67,12 +67,17 @@ pub struct X509Certificate<'a> {
     pub signature_algorithm: AlgorithmIdentifier<'a>,
     pub signature_value: BitString<'a>,
 
+    /// Complete raw ASN.1 DER content (TBS certificate, signature algorithm and signature).
     pub(crate) raw: &'a [u8],
 }
 
 impl<'a> X509Certificate<'a> {
-    /// Return a reference to the raw bytes used to parse the certificate
-    // Not using the AsRef trait, as that would not give back the full 'a lifetime
+    /// Return the raw ASN.1 DER content of the complete signed certificate that was parsed.
+    ///
+    /// This includes the to-be-signed (TBS) certificate, the signature algorithm, and the signature.
+    /// If you want just the ASN.1 DER of the TBS certificate, prefer [`TbsCertificate::as_ref()`].
+    ///
+    /// We avoid the `AsRef` trait in this instance to ensure the full lifetime of the `X509Certificate` is used.
     pub fn as_raw(&self) -> &'a [u8] {
         self.raw
     }
diff --git a/src/certification_request.rs b/src/certification_request.rs
index 2e8e770..83af7a6 100644
--- a/src/certification_request.rs
+++ b/src/certification_request.rs
@@ -20,12 +20,16 @@ pub struct X509CertificationRequest<'a> {
     pub signature_algorithm: AlgorithmIdentifier<'a>,
     pub signature_value: BitString<'a>,
 
+    /// Complete raw ASN.1 DER content (request info, signature algorithm and signature).
     pub(crate) raw: &'a [u8],
 }
 
 impl<'a> X509CertificationRequest<'a> {
-    /// Return a reference to the raw bytes used to parse the Certificate Request
-    // Not using the AsRef trait, as that would not give back the full 'a lifetime
+    /// Return the raw ASN.1 DER content of the complete signed certification request that was parsed.
+    ///
+    /// This includes the certification request info, the signature algorithm, and the signature.
+    ///
+    /// We avoid the `AsRef` trait in this instance to ensure the full lifetime of the `X509CertificationRequest` is used.
     pub fn as_raw(&self) -> &'a [u8] {
         self.raw
     }
diff --git a/src/revocation_list.rs b/src/revocation_list.rs
index ca05024..f154dec 100644
--- a/src/revocation_list.rs
+++ b/src/revocation_list.rs
@@ -52,6 +52,7 @@ pub struct CertificateRevocationList<'a> {
     pub signature_algorithm: AlgorithmIdentifier<'a>,
     pub signature_value: BitString<'a>,
 
+    /// Complete raw ASN.1 DER content (TBS certificate list, signature algorithm and signature).
     pub(crate) raw: &'a [u8],
 }
 
@@ -123,9 +124,14 @@ impl<'a> CertificateRevocationList<'a> {
             self.tbs_cert_list.raw,
         )
     }
+
+    ///
+    /// Return the raw ASN.1 DER content of the complete signed certificate revocation list that was parsed.
+    ///
+    /// This includes the to-be-signed (TBS) certificate list, the signature algorithm, and the signature.
+    /// If you want just the ASN.1 DER of the TBS certificate list, prefer [`TbsCertList::as_ref()`].
     ///
-    /// Return a reference to the raw bytes used to parse the Certificate Revocation List
-    // Not using the AsRef trait, as that would not give back the full 'a lifetime
+    /// We avoid the `AsRef` trait in this instance to ensure the full lifetime of the `CertificateRevocationList` is used.
     pub fn as_raw(&self) -> &'a [u8] {
         self.raw
     }

From 1524f7db3b9eb8189aa45c029ab633a92e393632 Mon Sep 17 00:00:00 2001
From: Pierre Chifflier 
Date: Fri, 29 Aug 2025 10:50:38 +0200
Subject: [PATCH 24/24] Prepare release 0.18.0

---
 CHANGELOG.md | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e813581..8f4b501 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,32 @@
 
 ### Thanks
 
+## 0.18.0
+
+### Added/Changed
+
+- Update lock file and dependencies
+- Fix clippy warnings
+- Visitor: add method to visit unknown extension and those with parse errors
+- Add new feature `verify-aws` to used `aws-lc-rs` as crypto provider instead of `ring`
+  - The features are exclusive, so only one should be used
+  - If both are specified, `aws-lc-rs` is used (but both dependencies are included)
+- Add `as_raw` methods to `X509Certificate`, `CertificateRevocationList` and `X509CertificationRequest`
+  - This method exposes the raw ASN.1 DER bytes used to build the object (#217)
+
+Extensions:
+- Add support for SubjectInfoAccess extension
+- GeneralName: add a new variant `Invalid` so an invalid entry does not stop
+  parsing for the entire list of names (for ex in SAN)
+
+### Fixed
+
+- PEM: ignore lines in comments which contain invalid UTF-8 characters (#180)
+
+### Thanks
+
+- Daniel McCarney
+
 ## 0.17.0
 
 ### Added/Changed/Fixed