Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 25 additions & 14 deletions src/cert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,17 +344,20 @@ fn remember_cert_extension<'a>(
};

set_extension_once(out, || {
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.
Standard(15) => Ok(value.read_bytes_to_end()),

// signedCertificateTimestampList is an OCTET STRING
SignedCertificateTimestampList => der::expect_tag(value, Tag::OctetString),

// All other remembered certificate extensions are wrapped in a Sequence.
_ => der::expect_tag(value, Tag::Sequence),
})
extension.value.read_all(
Error::TrailingData(DerTypeId::Extension),
|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.
Standard(15) => Ok(value.read_bytes_to_end()),

// signedCertificateTimestampList is an OCTET STRING
SignedCertificateTimestampList => der::expect_tag(value, Tag::OctetString),

// All other remembered certificate extensions are wrapped in a Sequence.
_ => der::expect_tag(value, Tag::Sequence),
},
)
})
})
}
Expand All @@ -379,9 +382,17 @@ pub(crate) struct CrlDistributionPoint<'a> {
impl<'a> CrlDistributionPoint<'a> {
/// Return the distribution point names (if any).
pub(crate) fn names(&self) -> Result<Option<DistributionPointName<'a>>, Error> {
self.distribution_point
.map(|input| DistributionPointName::from_der(&mut untrusted::Reader::new(input)))
.transpose()
untrusted::read_all_optional(
self.distribution_point,
Error::TrailingData(DerTypeId::DistributionPointName),
|reader| {
let Some(reader) = reader else {
return Ok(None);
};

Ok(Some(DistributionPointName::from_der(reader)?))
},
)
}
}

Expand Down
38 changes: 21 additions & 17 deletions src/crl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use pki_types::{SignatureVerificationAlgorithm, UnixTime};

use crate::error::Error;
use crate::verify_cert::{Budget, PathNode, Role};
use crate::{der, public_values_eq};
use crate::{DerTypeId, der, public_values_eq};

mod types;
pub use types::{
Expand Down Expand Up @@ -204,22 +204,26 @@ enum KeyUsageMode {
impl KeyUsageMode {
// https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.3
fn check(self, input: Option<untrusted::Input<'_>>) -> Result<(), Error> {
let bit_string = match input {
Some(input) => {
der::expect_tag(&mut untrusted::Reader::new(input), der::Tag::BitString)?
}
// While RFC 5280 requires KeyUsage be present, historically the absence of a KeyUsage
// has been treated as "Any Usage". We follow that convention here and assume the absence
// of KeyUsage implies the required_ku_bit_if_present we're checking for.
None => return Ok(()),
};

let flags = der::bit_string_flags(bit_string)?;
#[expect(clippy::as_conversions)] // u8 always fits in usize.
match flags.bit_set(self as usize) {
true => Ok(()),
false => Err(Error::IssuerNotCrlSigner),
}
untrusted::read_all_optional(
input,
Error::TrailingData(DerTypeId::KeyUsageExtension),
|reader| {
let Some(reader) = reader else {
// While RFC 5280 requires KeyUsage be present, historically the absence of a KeyUsage
// has been treated as "Any Usage". We follow that convention here and assume the absence
// of KeyUsage implies the required_ku_bit_if_present we're checking for.
return Ok(());
};

let bit_string = der::expect_tag(reader, der::Tag::BitString)?;
let flags = der::bit_string_flags(bit_string)?;
#[expect(clippy::as_conversions)] // u8 always fits in usize.
match flags.bit_set(self as usize) {
true => Ok(()),
false => Err(Error::IssuerNotCrlSigner),
}
},
)
}
}

Expand Down
14 changes: 11 additions & 3 deletions src/crl/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -641,9 +641,17 @@ impl<'a> IssuingDistributionPoint<'a> {

/// Return the distribution point names (if any).
pub(crate) fn names(&self) -> Result<Option<DistributionPointName<'a>>, Error> {
self.distribution_point
.map(|input| DistributionPointName::from_der(&mut untrusted::Reader::new(input)))
.transpose()
untrusted::read_all_optional(
self.distribution_point,
Error::TrailingData(DerTypeId::DistributionPointName),
|reader| {
let Some(reader) = reader else {
return Ok(None);
};

Ok(Some(DistributionPointName::from_der(reader)?))
},
)
}

/// Returns true if the CRL can be considered authoritative for the given certificate. We make
Expand Down
1 change: 1 addition & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,4 +428,5 @@ pub enum DerTypeId {
IssuingDistributionPoint,
IssuerUniqueId,
SubjectUniqueId,
KeyUsageExtension,
}
28 changes: 17 additions & 11 deletions src/verify_cert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use crate::end_entity::EndEntityCert;
use crate::error::Error;
#[cfg(feature = "alloc")]
use crate::spki_for_anchor;
use crate::{public_values_eq, subject_name};
use crate::{DerTypeId, public_values_eq, subject_name};

/// Build a [`VerifiedPath`] for an end-entity certificate from the given trust anchors.
// Use `'a` for lifetimes that we don't care about, `'p` for lifetimes that become a part of
Expand Down Expand Up @@ -455,16 +455,22 @@ fn check_issuer_independent_properties(
fn check_key_usage_cert_sign(key_usage: untrusted::Input<'_>, role: Role) -> Result<(), Error> {
// The KeyUsage extension is a BIT STRING; keyCertSign is bit 5.
const KEY_CERT_SIGN: usize = 5;
let bit_string = der::expect_tag(&mut untrusted::Reader::new(key_usage), der::Tag::BitString)?;
match (
role,
der::bit_string_flags(bit_string)?.bit_set(KEY_CERT_SIGN),
) {
(Role::Issuer, true) | (Role::EndEntity, false) => Ok(()),
(Role::Issuer, false) => Err(Error::IssuerNotCertSigner),
// Disallowed per https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.9
(Role::EndEntity, true) => Err(Error::EndEntityCertHasCertSignKeyUsage),
}

key_usage.read_all(
Error::TrailingData(DerTypeId::KeyUsageExtension),
|reader| {
let bit_string = der::expect_tag(reader, der::Tag::BitString)?;
match (
role,
der::bit_string_flags(bit_string)?.bit_set(KEY_CERT_SIGN),
) {
(Role::Issuer, true) | (Role::EndEntity, false) => Ok(()),
(Role::Issuer, false) => Err(Error::IssuerNotCertSigner),
// Disallowed per https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.9
(Role::EndEntity, true) => Err(Error::EndEntityCertHasCertSignKeyUsage),
}
},
)
}

fn check_eku(
Expand Down