From 37c81bf0eb11b024b44fb62f9358d2227113c357 Mon Sep 17 00:00:00 2001 From: Joe Birr-Pixton Date: Sun, 24 Aug 2025 16:30:17 +0100 Subject: [PATCH 1/2] Clarify parsing of extension OIDs --- src/x509.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/x509.rs b/src/x509.rs index 87b219ac..ab0c1f09 100644 --- a/src/x509.rs +++ b/src/x509.rs @@ -65,19 +65,10 @@ pub(crate) fn remember_extension( extension: &Extension<'_>, mut handler: impl FnMut(u8) -> Result<(), Error>, ) -> Result<(), Error> { - // ISO arc for standard certificate and CRL extensions. - // https://www.rfc-editor.org/rfc/rfc5280#appendix-A.2 - static ID_CE: [u8; 2] = oid![2, 5, 29]; - - if extension.id.len() != ID_CE.len() + 1 - || !extension.id.as_slice_less_safe().starts_with(&ID_CE) - { - return extension.unsupported(); + match extension.id.as_slice_less_safe() { + [first, second, x] if [*first, *second] == ID_CE => handler(*x), + _ => extension.unsupported(), } - - // safety: we verify len is non-zero and has the correct prefix above. - let last_octet = *extension.id.as_slice_less_safe().last().unwrap(); - handler(last_octet) } /// A certificate revocation list (CRL) distribution point name, describing a source of @@ -109,3 +100,12 @@ impl<'a> FromDer<'a> for DistributionPointName<'a> { const TYPE_ID: DerTypeId = DerTypeId::DistributionPointName; } + +/// ISO arc for standard certificate and CRL extensions. +/// +/// ```text +/// id-ce OBJECT IDENTIFIER ::= {joint-iso-ccitt(2) ds(5) 29} +/// ``` +/// +/// +const ID_CE: [u8; 2] = oid!(2, 5, 29); From abc882291b4ad883296a920cfca1611704d7f778 Mon Sep 17 00:00:00 2001 From: Joe Birr-Pixton Date: Sun, 24 Aug 2025 16:34:19 +0100 Subject: [PATCH 2/2] Prepare to allow extensions under different OID arcs --- src/cert.rs | 20 ++++++++++++-------- src/crl/types.rs | 20 +++++++++++++------- src/x509.rs | 24 ++++++++++++++++++++---- 3 files changed, 45 insertions(+), 19 deletions(-) diff --git a/src/cert.rs b/src/cert.rs index 67c7410f..1a21b8c3 100644 --- a/src/cert.rs +++ b/src/cert.rs @@ -21,7 +21,9 @@ use crate::error::{DerTypeId, Error}; use crate::public_values_eq; use crate::signed_data::SignedData; use crate::subject_name::{GeneralName, NameIterator, WildcardDnsNameRef}; -use crate::x509::{DistributionPointName, Extension, remember_extension, set_extension_once}; +use crate::x509::{ + DistributionPointName, Extension, ExtensionOid, remember_extension, set_extension_once, +}; /// A parsed X509 certificate. pub struct Cert<'a> { @@ -263,25 +265,27 @@ fn remember_cert_extension<'a>( // all policy-related stuff. We assume that the policy-related extensions // are not marked critical. + use ExtensionOid::*; + remember_extension(extension, |id| { let out = match id { // id-ce-keyUsage 2.5.29.15. - 15 => &mut cert.key_usage, + Standard(15) => &mut cert.key_usage, // id-ce-subjectAltName 2.5.29.17 - 17 => &mut cert.subject_alt_name, + Standard(17) => &mut cert.subject_alt_name, // id-ce-basicConstraints 2.5.29.19 - 19 => &mut cert.basic_constraints, + Standard(19) => &mut cert.basic_constraints, // id-ce-nameConstraints 2.5.29.30 - 30 => &mut cert.name_constraints, + Standard(30) => &mut cert.name_constraints, // id-ce-cRLDistributionPoints 2.5.29.31 - 31 => &mut cert.crl_distribution_points, + Standard(31) => &mut cert.crl_distribution_points, // id-ce-extKeyUsage 2.5.29.37 - 37 => &mut cert.eku, + Standard(37) => &mut cert.eku, // Unsupported extension _ => return extension.unsupported(), @@ -291,7 +295,7 @@ fn remember_cert_extension<'a>( extension.value.read_all(Error::BadDer, |value| match id { // Unlike the other extensions we remember KU is a BitString and not a Sequence. We // read the raw bytes here and parse at the time of use. - 15 => Ok(value.read_bytes_to_end()), + Standard(15) => Ok(value.read_bytes_to_end()), // All other remembered certificate extensions are wrapped in a Sequence. _ => der::expect_tag(value, Tag::Sequence), }) diff --git a/src/crl/types.rs b/src/crl/types.rs index 14faca90..c43496b1 100644 --- a/src/crl/types.rs +++ b/src/crl/types.rs @@ -264,10 +264,12 @@ impl<'a> BorrowedCertRevocationList<'a> { } fn remember_extension(&mut self, extension: &Extension<'a>) -> Result<(), Error> { + use crate::x509::ExtensionOid::*; + remember_extension(extension, |id| { match id { // id-ce-cRLNumber 2.5.29.20 - RFC 5280 §5.2.3 - 20 => { + Standard(20) => { // RFC 5280 §5.2.3: // CRL verifiers MUST be able to handle CRLNumber values // up to 20 octets. Conforming CRL issuers MUST NOT use CRLNumber @@ -289,17 +291,17 @@ impl<'a> BorrowedCertRevocationList<'a> { // id-ce-deltaCRLIndicator 2.5.29.27 - RFC 5280 §5.2.4 // We explicitly do not support delta CRLs. - 27 => Err(Error::UnsupportedDeltaCrl), + Standard(27) => Err(Error::UnsupportedDeltaCrl), // id-ce-issuingDistributionPoint 2.5.29.28 - RFC 5280 §5.2.4 // We recognize the extension and retain its value for use. - 28 => { + Standard(28) => { set_extension_once(&mut self.issuing_distribution_point, || Ok(extension.value)) } // id-ce-authorityKeyIdentifier 2.5.29.35 - RFC 5280 §5.2.1, §4.2.1.1 // We recognize the extension but don't retain its value for use. - 35 => Ok(()), + Standard(35) => Ok(()), // Unsupported extension _ => extension.unsupported(), @@ -758,13 +760,17 @@ impl<'a> BorrowedRevokedCert<'a> { } fn remember_extension(&mut self, extension: &Extension<'a>) -> Result<(), Error> { + use crate::x509::ExtensionOid::*; + remember_extension(extension, |id| { match id { // id-ce-cRLReasons 2.5.29.21 - RFC 5280 §5.3.1. - 21 => set_extension_once(&mut self.reason_code, || der::read_all(extension.value)), + Standard(21) => { + set_extension_once(&mut self.reason_code, || der::read_all(extension.value)) + } // id-ce-invalidityDate 2.5.29.24 - RFC 5280 §5.3.2. - 24 => set_extension_once(&mut self.invalidity_date, || { + Standard(24) => set_extension_once(&mut self.invalidity_date, || { extension.value.read_all(Error::BadDer, UnixTime::from_der) }), @@ -775,7 +781,7 @@ impl<'a> BorrowedRevokedCert<'a> { // extension. // We choose not to support indirect CRLs and so turn this into a more specific // error rather than simply letting it fail as an unsupported critical extension. - 29 => Err(Error::UnsupportedIndirectCrl), + Standard(29) => Err(Error::UnsupportedIndirectCrl), // Unsupported extension _ => extension.unsupported(), diff --git a/src/x509.rs b/src/x509.rs index ab0c1f09..79581ecf 100644 --- a/src/x509.rs +++ b/src/x509.rs @@ -63,11 +63,11 @@ pub(crate) fn set_extension_once( pub(crate) fn remember_extension( extension: &Extension<'_>, - mut handler: impl FnMut(u8) -> Result<(), Error>, + mut handler: impl FnMut(ExtensionOid) -> Result<(), Error>, ) -> Result<(), Error> { - match extension.id.as_slice_less_safe() { - [first, second, x] if [*first, *second] == ID_CE => handler(*x), - _ => extension.unsupported(), + match ExtensionOid::lookup(extension.id) { + Some(oid) => handler(oid), + None => extension.unsupported(), } } @@ -101,6 +101,22 @@ impl<'a> FromDer<'a> for DistributionPointName<'a> { const TYPE_ID: DerTypeId = DerTypeId::DistributionPointName; } +/// Simplified representation of supported extension OIDs. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub(crate) enum ExtensionOid { + /// Extensions whose OID is under `id-ce` arc. + Standard(u8), +} + +impl ExtensionOid { + fn lookup(id: untrusted::Input<'_>) -> Option { + match id.as_slice_less_safe() { + [first, second, x] if [*first, *second] == ID_CE => Some(Self::Standard(*x)), + _ => None, + } + } +} + /// ISO arc for standard certificate and CRL extensions. /// /// ```text