Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Further limits on expensive path building #163

Merged
merged 5 commits into from
Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 4 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@
/// 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 certificate violates one or more name constraints.
NameConstraintViolation,

Expand Down Expand Up @@ -202,48 +205,49 @@
match &self {
// Errors related to certificate validity
Error::CertNotValidYet | Error::CertExpired => 290,
Error::CertNotValidForName => 280,

Check warning on line 208 in src/error.rs

View check run for this annotation

Codecov / codecov/patch

src/error.rs#L208

Added line #L208 was not covered by tests
Error::CertRevoked | Error::UnknownRevocationStatus => 270,
Error::InvalidCrlSignatureForPublicKey | Error::InvalidSignatureForPublicKey => 260,
Error::SignatureAlgorithmMismatch => 250,

Check warning on line 211 in src/error.rs

View check run for this annotation

Codecov / codecov/patch

src/error.rs#L211

Added line #L211 was not covered by tests
Error::RequiredEkuNotFound => 240,
Error::NameConstraintViolation => 230,
Error::PathLenConstraintViolated => 220,

Check warning on line 214 in src/error.rs

View check run for this annotation

Codecov / codecov/patch

src/error.rs#L214

Added line #L214 was not covered by tests
Error::CaUsedAsEndEntity | Error::EndEntityUsedAsCa => 210,
Error::IssuerNotCrlSigner => 200,

// Errors related to supported features used in an invalid way.
Error::InvalidCertValidity => 190,

Check warning on line 219 in src/error.rs

View check run for this annotation

Codecov / codecov/patch

src/error.rs#L219

Added line #L219 was not covered by tests
Error::InvalidNetworkMaskConstraint => 180,
Error::InvalidSerialNumber => 170,
Error::InvalidCrlNumber => 160,

Check warning on line 222 in src/error.rs

View check run for this annotation

Codecov / codecov/patch

src/error.rs#L221-L222

Added lines #L221 - L222 were not covered by tests

// Errors related to unsupported features.
Error::UnsupportedCrlSignatureAlgorithmForPublicKey
| Error::UnsupportedSignatureAlgorithmForPublicKey => 150,

Check warning on line 226 in src/error.rs

View check run for this annotation

Codecov / codecov/patch

src/error.rs#L226

Added line #L226 was not covered by tests
Error::UnsupportedCrlSignatureAlgorithm | Error::UnsupportedSignatureAlgorithm => 140,
Error::UnsupportedCriticalExtension => 130,
Error::UnsupportedCertVersion => 130,
Error::UnsupportedCrlVersion => 120,
Error::UnsupportedDeltaCrl => 110,
Error::UnsupportedIndirectCrl => 100,
Error::UnsupportedRevocationReason => 90,
Error::UnsupportedRevocationReasonsPartitioning => 80,
Error::UnsupportedCrlIssuingDistributionPoint => 70,

Check warning on line 235 in src/error.rs

View check run for this annotation

Codecov / codecov/patch

src/error.rs#L228-L235

Added lines #L228 - L235 were not covered by tests
Error::MaximumPathDepthExceeded => 61,

// Errors related to malformed data.
Error::MalformedDnsIdentifier => 60,
Error::MalformedNameConstraint => 50,
Error::MalformedExtensions | Error::TrailingData(_) => 40,
Error::ExtensionValueInvalid => 30,

Check warning on line 242 in src/error.rs

View check run for this annotation

Codecov / codecov/patch

src/error.rs#L240-L242

Added lines #L240 - L242 were not covered by tests

// Generic DER errors.
Error::BadDerTime => 20,

Check warning on line 245 in src/error.rs

View check run for this annotation

Codecov / codecov/patch

src/error.rs#L245

Added line #L245 was not covered by tests
Error::BadDer => 10,

// Special case errors - not subject to ranking.
Error::MaximumSignatureChecksExceeded => 0,
Error::MaximumPathBuildCallsExceeded => 0,

Check warning on line 250 in src/error.rs

View check run for this annotation

Codecov / codecov/patch

src/error.rs#L250

Added line #L250 was not covered by tests

// Default catch all error - should be renamed in the future.
Error::UnknownIssuer => 0,
Expand Down
82 changes: 80 additions & 2 deletions src/verify_cert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,7 @@ fn build_chain_inner(
const MAX_SUB_CA_COUNT: usize = 6;

if sub_ca_count >= MAX_SUB_CA_COUNT {
// TODO(XXX): Candidate for a more specific error - Error::PathTooDeep?
return Err(Error::UnknownIssuer);
return Err(Error::MaximumPathDepthExceeded);
cpu marked this conversation as resolved.
Show resolved Hide resolved
}
}
UsedAsCa::No => {
Expand Down Expand Up @@ -923,4 +922,83 @@ mod tests {
Error::MaximumPathBuildCallsExceeded
);
}

#[cfg(feature = "alloc")]
fn build_linear_chain(chain_length: usize) -> Result<(), Error> {
use crate::{extract_trust_anchor, ECDSA_P256_SHA256};
use crate::{EndEntityCert, Time};

let alg = &rcgen::PKCS_ECDSA_P256_SHA256;

let make_issuer = |index: usize| {
let mut ca_params = rcgen::CertificateParams::new(Vec::new());
ca_params.distinguished_name.push(
rcgen::DnType::OrganizationName,
format!("Bogus Subject {index}"),
);
ca_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
ca_params.key_usages = vec![
rcgen::KeyUsagePurpose::KeyCertSign,
rcgen::KeyUsagePurpose::DigitalSignature,
rcgen::KeyUsagePurpose::CrlSign,
];
ca_params.alg = alg;
rcgen::Certificate::from_params(ca_params).unwrap()
};

let ca_cert = make_issuer(chain_length);
let ca_cert_der = CertificateDer::from(ca_cert.serialize_der().unwrap());

let mut intermediates = Vec::with_capacity(chain_length);
let mut issuer = ca_cert;
for i in 0..chain_length {
let intermediate = make_issuer(i);
let intermediate_der = intermediate.serialize_der_with_signer(&issuer).unwrap();
intermediates.push(intermediate_der);
issuer = intermediate;
}

let mut ee_params = rcgen::CertificateParams::new(vec!["example.com".to_string()]);
ee_params.is_ca = rcgen::IsCa::ExplicitNoCa;
ee_params.alg = alg;
let ee_cert = rcgen::Certificate::from_params(ee_params).unwrap();
let ee_cert_der = CertificateDer::from(ee_cert.serialize_der_with_signer(&issuer).unwrap());

let anchors = &[extract_trust_anchor(&ca_cert_der).unwrap()];
let time = Time::from_seconds_since_unix_epoch(0x1fed_f00d);
let cert = EndEntityCert::try_from(&ee_cert_der).unwrap();
let intermediates_der = intermediates
.iter()
.map(|x| CertificateDer::from(x.as_ref()))
.collect::<Vec<_>>();

build_chain(
&ChainOptions {
eku: KeyUsage::server_auth(),
supported_sig_algs: &[ECDSA_P256_SHA256],
trust_anchors: anchors,
intermediate_certs: &intermediates_der,
revocation: None,
},
cert.inner(),
time,
)
}

#[test]
#[cfg(feature = "alloc")]
fn longest_allowed_path() {
assert_eq!(build_linear_chain(1), Ok(()));
assert_eq!(build_linear_chain(2), Ok(()));
assert_eq!(build_linear_chain(3), Ok(()));
assert_eq!(build_linear_chain(4), Ok(()));
assert_eq!(build_linear_chain(5), Ok(()));
assert_eq!(build_linear_chain(6), Ok(()));
}

#[test]
#[cfg(feature = "alloc")]
fn path_too_long() {
assert_eq!(build_linear_chain(7), Err(Error::MaximumPathDepthExceeded));
}
}