Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
a328b56
Track signature limit using `Budget` type
ctz Sep 5, 2023
7fec222
Add comment indicating source of signature budget
ctz Sep 5, 2023
cb0e81d
Apply budget to number of calls to `build_chain_inner`
ctz Sep 5, 2023
0b139ab
Introduce and test for `MaximumPathDepthExceeded` error
ctz Sep 5, 2023
abcbdae
Import Default trait
djc Sep 5, 2023
d2d070c
Force all signature verification operations to consume budget
djc Sep 5, 2023
32d8b1c
Improve readability of build chain calls budget
djc Sep 5, 2023
6b78410
subject_name: clarify var name in check_name_constraints
cpu Aug 31, 2023
7627dff
verify_cert: check_signatures -> check_signed_chain
cpu Sep 1, 2023
f86f3e2
error: alpha sort
cpu Sep 5, 2023
c6d1d1e
verify_cert: pull out `make_issuer` test helper
cpu Sep 6, 2023
c9f1f6b
verify_cert: pull out `make_end_entity` test helper
cpu Sep 6, 2023
d82c740
verify_cert: pull out `verify_chain` test helper
cpu Sep 6, 2023
2b3857f
verify_cert: budget for name constraint comparisons
cpu Sep 5, 2023
debea51
verify_cert: name constraint checking on verified chain
cpu Sep 5, 2023
f34fc7c
verify_cert: take references in verify_chain helper
cpu Sep 7, 2023
2f25bf3
verify_cert: optional `Budget` arg for `verify_chain` helper
cpu Sep 7, 2023
0f64dc1
error: add is_fatal helper, use in verify_cert
cpu Sep 7, 2023
e592bc0
verify_cert: correct handling of fatal errors
cpu Sep 7, 2023
f56fcbf
verify_cert: use enum for build chain error
cpu Sep 7, 2023
8a7bf1a
Cargo: bump version 0.100.2 -> 0.100.3.
cpu Sep 8, 2023
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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ license-file = "LICENSE"
name = "rustls-webpki"
readme = "README.md"
repository = "https://github.com/rustls/webpki"
version = "0.100.2"
version = "0.100.3"

include = [
"Cargo.toml",
Expand Down
4 changes: 0 additions & 4 deletions src/end_entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,6 @@ impl<'a> EndEntityCert<'a> {
intermediate_certs,
&self.inner,
time,
0,
&mut 0_usize,
)
}

Expand Down Expand Up @@ -130,8 +128,6 @@ impl<'a> EndEntityCert<'a> {
intermediate_certs,
&self.inner,
time,
0,
&mut 0_usize,
)
}

Expand Down
77 changes: 56 additions & 21 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

use core::fmt;
use core::ops::ControlFlow;

/// An error that occurs during certificate validation or name validation.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -48,23 +49,47 @@ pub enum Error {
/// the notAfter time is earlier than the notBefore time.
InvalidCertValidity,

/// A iPAddress name constraint was invalid:
/// - it had a sparse network mask (ie, cannot be written in CIDR form).
/// - it was too long or short
InvalidNetworkMaskConstraint,

/// The signature is invalid for the given public key.
InvalidSignatureForPublicKey,

/// The certificate extensions are malformed.
///
/// In particular, webpki requires the DNS name(s) be in the subjectAltName
/// extension as required by the CA/Browser Forum Baseline Requirements
/// and as recommended by RFC6125.
MalformedExtensions,

/// The maximum number of name constraint comparisons has been reached.
MaximumNameConstraintComparisonsExceeded,

/// The maximum number of internal path building calls has been reached. Path complexity is too great.
MaximumPathBuildCallsExceeded,

/// The path search was terminated because it became too deep.
MaximumPathDepthExceeded,

/// The maximum number of signature checks has been reached. Path complexity is too great.
MaximumSignatureChecksExceeded,

/// The certificate violates one or more name constraints.
NameConstraintViolation,

/// The certificate violates one or more path length constraints.
PathLenConstraintViolated,

/// The algorithm in the TBSCertificate "signature" field of a certificate
/// does not match the algorithm in the signature of the certificate.
SignatureAlgorithmMismatch,

/// The certificate is not valid for the Extended Key Usage for which it is
/// being validated.
RequiredEkuNotFound,

/// The algorithm in the TBSCertificate "signature" field of a certificate
/// does not match the algorithm in the signature of the certificate.
SignatureAlgorithmMismatch,

/// A valid issuer for the certificate could not be found.
UnknownIssuer,

Expand All @@ -74,19 +99,13 @@ pub enum Error {
/// is malformed.
UnsupportedCertVersion,

/// The certificate extensions are malformed.
///
/// In particular, webpki requires the DNS name(s) be in the subjectAltName
/// extension as required by the CA/Browser Forum Baseline Requirements
/// and as recommended by RFC6125.
MalformedExtensions,

/// The maximum number of signature checks has been reached. Path complexity is too great.
MaximumSignatureChecksExceeded,

/// The certificate contains an unsupported critical extension.
UnsupportedCriticalExtension,

/// The signature algorithm for a signature is not in the set of supported
/// signature algorithms given.
UnsupportedSignatureAlgorithm,

/// The signature's algorithm does not match the algorithm of the public
/// key it is being validated for. This may be because the public key
/// algorithm's OID isn't recognized (e.g. DSA), or the public key
Expand All @@ -95,15 +114,31 @@ pub enum Error {
/// algorithm and the signature algorithm simply don't match (e.g.
/// verifying an RSA signature with an ECC public key).
UnsupportedSignatureAlgorithmForPublicKey,
}

/// The signature algorithm for a signature is not in the set of supported
/// signature algorithms given.
UnsupportedSignatureAlgorithm,
impl Error {
/// Returns true for errors that should be considered fatal during path building. Errors of
/// this class should halt any further path building and be returned immediately.
#[inline]
pub(crate) fn is_fatal(&self) -> bool {
matches!(
self,
Error::MaximumSignatureChecksExceeded
| Error::MaximumPathBuildCallsExceeded
| Error::MaximumNameConstraintComparisonsExceeded
)
}
}

/// A iPAddress name constraint was invalid:
/// - it had a sparse network mask (ie, cannot be written in CIDR form).
/// - it was too long or short
InvalidNetworkMaskConstraint,
impl From<Error> for ControlFlow<Error, Error> {
fn from(value: Error) -> Self {
match value {
// If an error is fatal, we've exhausted the potential for continued search.
err if err.is_fatal() => Self::Break(err),
// Otherwise we've rejected one candidate chain, but may continue to search for others.
err => Self::Continue(err),
}
}
}

impl fmt::Display for Error {
Expand Down
8 changes: 7 additions & 1 deletion src/signed_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

use crate::verify_cert::Budget;
use crate::{der, Error};
use ring::signature;

Expand Down Expand Up @@ -96,7 +97,10 @@ pub(crate) fn verify_signed_data(
supported_algorithms: &[&SignatureAlgorithm],
spki_value: untrusted::Input,
signed_data: &SignedData,
budget: &mut Budget,
) -> Result<(), Error> {
budget.consume_signature()?;

// We need to verify the signature in `signed_data` using the public key
// in `public_key`. In order to know which *ring* signature verification
// algorithm to use, we need to know the public key algorithm (ECDSA,
Expand Down Expand Up @@ -438,7 +442,8 @@ mod tests {
signed_data::verify_signed_data(
SUPPORTED_ALGORITHMS_IN_TESTS,
spki_value,
&signed_data
&signed_data,
&mut Budget::default()
)
);
}
Expand Down Expand Up @@ -735,6 +740,7 @@ mod tests {
}
}

use crate::verify_cert::Budget;
use alloc::str::Lines;

fn read_pem_section(lines: &mut Lines, section_name: &str) -> Vec<u8> {
Expand Down
22 changes: 17 additions & 5 deletions src/subject_name/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ use super::{
};
use crate::{
cert::{Cert, EndEntityOrCa},
der, Error,
der,
verify_cert::Budget,
Error,
};

pub(crate) fn verify_cert_dns_name(
Expand All @@ -33,7 +35,7 @@ pub(crate) fn verify_cert_dns_name(
cert.subject_alt_name,
SubjectCommonNameContents::Ignore,
Err(Error::CertNotValidForName),
&|name| {
&mut |name| {
if let GeneralName::DnsName(presented_id) = name {
match dns_name::presented_id_matches_reference_id(presented_id, dns_name) {
Some(true) => return NameIteration::Stop(Ok(())),
Expand Down Expand Up @@ -67,7 +69,7 @@ pub(crate) fn verify_cert_subject_name(
cert.inner().subject_alt_name,
SubjectCommonNameContents::Ignore,
Err(Error::CertNotValidForName),
&|name| {
&mut |name| {
if let GeneralName::IpAddress(presented_id) = name {
match ip_address::presented_id_matches_reference_id(presented_id, ip_address) {
Ok(true) => return NameIteration::Stop(Ok(())),
Expand All @@ -87,6 +89,7 @@ pub(crate) fn check_name_constraints(
input: Option<&mut untrusted::Reader>,
subordinate_certs: &Cert,
subject_common_name_contents: SubjectCommonNameContents,
budget: &mut Budget,
) -> Result<(), Error> {
let input = match input {
Some(input) => input,
Expand Down Expand Up @@ -115,11 +118,12 @@ pub(crate) fn check_name_constraints(
child.subject_alt_name,
subject_common_name_contents,
Ok(()),
&|name| {
&mut |name| {
check_presented_id_conforms_to_constraints(
name,
permitted_subtrees,
excluded_subtrees,
budget,
)
},
)?;
Expand All @@ -139,11 +143,13 @@ fn check_presented_id_conforms_to_constraints(
name: GeneralName,
permitted_subtrees: Option<untrusted::Input>,
excluded_subtrees: Option<untrusted::Input>,
budget: &mut Budget,
) -> NameIteration {
match check_presented_id_conforms_to_constraints_in_subtree(
name,
Subtrees::PermittedSubtrees,
permitted_subtrees,
budget,
) {
stop @ NameIteration::Stop(..) => {
return stop;
Expand All @@ -155,6 +161,7 @@ fn check_presented_id_conforms_to_constraints(
name,
Subtrees::ExcludedSubtrees,
excluded_subtrees,
budget,
)
}

Expand All @@ -168,6 +175,7 @@ fn check_presented_id_conforms_to_constraints_in_subtree(
name: GeneralName,
subtrees: Subtrees,
constraints: Option<untrusted::Input>,
budget: &mut Budget,
) -> NameIteration {
let mut constraints = match constraints {
Some(constraints) => untrusted::Reader::new(constraints),
Expand All @@ -180,6 +188,10 @@ fn check_presented_id_conforms_to_constraints_in_subtree(
let mut has_permitted_subtrees_mismatch = false;

while !constraints.at_end() {
if let Err(e) = budget.consume_name_constraint_comparison() {
return NameIteration::Stop(Err(e));
}

// http://tools.ietf.org/html/rfc5280#section-4.2.1.10: "Within this
// profile, the minimum and maximum fields are not used with any name
// forms, thus, the minimum MUST be zero, and maximum MUST be absent."
Expand Down Expand Up @@ -307,7 +319,7 @@ fn iterate_names(
subject_alt_name: Option<untrusted::Input>,
subject_common_name_contents: SubjectCommonNameContents,
result_if_never_stopped_early: Result<(), Error>,
f: &dyn Fn(GeneralName) -> NameIteration,
f: &mut dyn FnMut(GeneralName) -> NameIteration,
) -> Result<(), Error> {
if let Some(subject_alt_name) = subject_alt_name {
let mut subject_alt_name = untrusted::Reader::new(subject_alt_name);
Expand Down
Loading