Skip to content

Commit 57fa975

Browse files
committed
Extract trait for ExtendedKeyUsage validation
1 parent 6700208 commit 57fa975

File tree

3 files changed

+53
-36
lines changed

3 files changed

+53
-36
lines changed

src/end_entity.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use pki_types::{
2121
use crate::crl::RevocationOptions;
2222
use crate::error::Error;
2323
use crate::subject_name::{verify_dns_names, verify_ip_address_names};
24-
use crate::verify_cert::{self, KeyUsage, VerifiedPath};
24+
use crate::verify_cert::{self, ExtendedKeyUsageValidator, VerifiedPath};
2525
use crate::{cert, signed_data};
2626

2727
/// An end-entity certificate.
@@ -31,7 +31,7 @@ use crate::{cert, signed_data};
3131
///
3232
/// * [`EndEntityCert::verify_for_usage()`]: Verify that the peer's certificate
3333
/// is valid for the current usage scenario. For server authentication, use
34-
/// [`KeyUsage::server_auth()`].
34+
/// [`crate::KeyUsage::server_auth()`].
3535
/// * [`EndEntityCert::verify_is_valid_for_subject_name()`]: Verify that the server's
3636
/// certificate is valid for the host or IP address that is being connected to.
3737
/// * [`EndEntityCert::verify_signature()`]: Verify that the signature of server's
@@ -42,7 +42,7 @@ use crate::{cert, signed_data};
4242
///
4343
/// * [`EndEntityCert::verify_for_usage()`]: Verify that the peer's certificate
4444
/// is valid for the current usage scenario. For client authentication, use
45-
/// [`KeyUsage::client_auth()`].
45+
/// [`crate::KeyUsage::client_auth()`].
4646
/// * [`EndEntityCert::verify_signature()`]: Verify that the signature of client's
4747
/// `CertificateVerify` message is valid using the public key from the
4848
/// client's certificate.
@@ -85,7 +85,8 @@ impl EndEntityCert<'_> {
8585
/// * `time` is the time for which the validation is effective (usually the
8686
/// current time).
8787
/// * `usage` is the intended usage of the certificate, indicating what kind
88-
/// of usage we're verifying the certificate for.
88+
/// of usage we're verifying the certificate for. The default [`ExtendedKeyUsageValidator`]
89+
/// implementation is [`KeyUsage`](crate::KeyUsage).
8990
/// * `crls` is the list of certificate revocation lists to check
9091
/// the certificate against.
9192
/// * `verify_path` is an optional verification function for path candidates.
@@ -105,7 +106,7 @@ impl EndEntityCert<'_> {
105106
trust_anchors: &'p [TrustAnchor<'_>],
106107
intermediate_certs: &'p [CertificateDer<'p>],
107108
time: UnixTime,
108-
usage: KeyUsage,
109+
usage: impl ExtendedKeyUsageValidator,
109110
revocation: Option<RevocationOptions<'_>>,
110111
verify_path: Option<&dyn Fn(&VerifiedPath<'_>) -> Result<(), Error>>,
111112
) -> Result<VerifiedPath<'p>, Error> {

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ pub use {
8585
},
8686
rpk_entity::RawPublicKeyEntity,
8787
trust_anchor::anchor_from_trusted_cert,
88-
verify_cert::{KeyUsage, RequiredEkuNotFoundContext, VerifiedPath},
88+
verify_cert::{ExtendedKeyUsageValidator, KeyUsage, RequiredEkuNotFoundContext, VerifiedPath},
8989
};
9090

9191
#[cfg(feature = "alloc")]

src/verify_cert.rs

Lines changed: 46 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,15 @@ use crate::{public_values_eq, signed_data, subject_name};
2828

2929
// Use `'a` for lifetimes that we don't care about, `'p` for lifetimes that become a part of
3030
// the `VerifiedPath`.
31-
pub(crate) struct ChainOptions<'a, 'p> {
32-
pub(crate) eku: KeyUsage,
31+
pub(crate) struct ChainOptions<'a, 'p, V> {
32+
pub(crate) eku: V,
3333
pub(crate) supported_sig_algs: &'a [&'a dyn SignatureVerificationAlgorithm],
3434
pub(crate) trust_anchors: &'p [TrustAnchor<'p>],
3535
pub(crate) intermediate_certs: &'p [CertificateDer<'p>],
3636
pub(crate) revocation: Option<RevocationOptions<'a>>,
3737
}
3838

39-
impl<'a, 'p: 'a> ChainOptions<'a, 'p> {
39+
impl<'a, 'p: 'a, V: ExtendedKeyUsageValidator> ChainOptions<'a, 'p, V> {
4040
pub(crate) fn build_chain(
4141
&self,
4242
end_entity: &'p EndEntityCert<'p>,
@@ -60,7 +60,7 @@ impl<'a, 'p: 'a> ChainOptions<'a, 'p> {
6060
) -> Result<&'p TrustAnchor<'p>, ControlFlow<Error, Error>> {
6161
let role = path.node().role();
6262

63-
check_issuer_independent_properties(path.head(), time, role, sub_ca_count, self.eku)?;
63+
check_issuer_independent_properties(path.head(), time, role, sub_ca_count, &self.eku)?;
6464

6565
// TODO: HPKP checks.
6666

@@ -349,7 +349,7 @@ fn check_issuer_independent_properties(
349349
time: UnixTime,
350350
role: Role,
351351
sub_ca_count: usize,
352-
eku: KeyUsage,
352+
eku: &impl ExtendedKeyUsageValidator,
353353
) -> Result<(), Error> {
354354
// TODO: check_distrust(trust_anchor_subject, trust_anchor_spki)?;
355355
// TODO: Check signature algorithm like mozilla::pkix.
@@ -368,8 +368,8 @@ fn check_issuer_independent_properties(
368368
check_basic_constraints(value, role, sub_ca_count)
369369
})?;
370370
untrusted::read_all_optional(cert.eku, Error::BadDer, |value| match value {
371-
Some(input) => eku.check(KeyPurposeIdIter { input }),
372-
None => eku.check(KeyPurposeIdIter {
371+
Some(input) => eku.validate(KeyPurposeIdIter { input }),
372+
None => eku.validate(KeyPurposeIdIter {
373373
input: &mut untrusted::Reader::new(untrusted::Input::from(&[])),
374374
}),
375375
})?;
@@ -531,11 +531,27 @@ impl KeyUsage {
531531
}
532532
}
533533

534+
/// Yield the OID values of the required extended key usage.
535+
pub fn oid_values(&self) -> impl Iterator<Item = usize> + '_ {
536+
OidDecoder::new(
537+
match &self.inner {
538+
ExtendedKeyUsage::Required(eku) => eku,
539+
ExtendedKeyUsage::RequiredIfPresent(eku) => eku,
540+
}
541+
.oid_value
542+
.as_slice_less_safe(),
543+
)
544+
}
545+
546+
/// Human-readable representation of the server authentication OID.
547+
pub const SERVER_AUTH_REPR: &[usize] = &[1, 3, 6, 1, 5, 5, 7, 3, 1];
548+
/// Human-readable representation of the client authentication OID.
549+
pub const CLIENT_AUTH_REPR: &[usize] = &[1, 3, 6, 1, 5, 5, 7, 3, 2];
550+
}
551+
552+
impl ExtendedKeyUsageValidator for KeyUsage {
534553
// https://tools.ietf.org/html/rfc5280#section-4.2.1.12
535-
fn check<'a>(
536-
&self,
537-
iter: impl Iterator<Item = Result<KeyPurposeId<'a>, Error>>,
538-
) -> Result<(), Error> {
554+
fn validate(&self, iter: KeyPurposeIdIter<'_, '_>) -> Result<(), Error> {
539555
let mut empty = true;
540556
#[cfg(feature = "alloc")]
541557
let mut present = Vec::new();
@@ -556,30 +572,30 @@ impl KeyUsage {
556572
_ => Err(Error::RequiredEkuNotFoundContext(
557573
RequiredEkuNotFoundContext {
558574
#[cfg(feature = "alloc")]
559-
required: KeyUsage { inner: self.inner },
575+
required: Self { inner: self.inner },
560576
#[cfg(feature = "alloc")]
561577
present,
562578
},
563579
)),
564580
}
565581
}
582+
}
566583

567-
/// Yield the OID values of the required extended key usage.
568-
pub fn oid_values(&self) -> impl Iterator<Item = usize> + '_ {
569-
OidDecoder::new(
570-
match &self.inner {
571-
ExtendedKeyUsage::Required(eku) => eku,
572-
ExtendedKeyUsage::RequiredIfPresent(eku) => eku,
573-
}
574-
.oid_value
575-
.as_slice_less_safe(),
576-
)
584+
impl<V: ExtendedKeyUsageValidator> ExtendedKeyUsageValidator for &V {
585+
fn validate(&self, iter: KeyPurposeIdIter<'_, '_>) -> Result<(), Error> {
586+
(*self).validate(iter)
577587
}
588+
}
578589

579-
/// Human-readable representation of the server authentication OID.
580-
pub const SERVER_AUTH_REPR: &[usize] = &[1, 3, 6, 1, 5, 5, 7, 3, 1];
581-
/// Human-readable representation of the client authentication OID.
582-
pub const CLIENT_AUTH_REPR: &[usize] = &[1, 3, 6, 1, 5, 5, 7, 3, 2];
590+
/// A trait for validating the Extended Key Usage (EKU) extensions of a certificate.
591+
pub trait ExtendedKeyUsageValidator {
592+
/// Validate the EKU values in a certificate.
593+
///
594+
/// `iter` yields the EKU OIDs in the certificate, or an error if the EKU extension
595+
/// is malformed. `validate()` should yield `Ok(())` if the EKU values match the
596+
/// required policy, or an `Error` if they do not. Ideally the `Error` should be
597+
/// `Error::RequiredEkuNotFoundContext` if the policy is not met.
598+
fn validate(&self, iter: KeyPurposeIdIter<'_, '_>) -> Result<(), Error>;
583599
}
584600

585601
/// Extended Key Usage (EKU) of a certificate.
@@ -601,7 +617,7 @@ impl ExtendedKeyUsage {
601617
}
602618
}
603619

604-
struct KeyPurposeIdIter<'a, 'r> {
620+
pub struct KeyPurposeIdIter<'a, 'r> {
605621
input: &'r mut untrusted::Reader<'a>,
606622
}
607623

@@ -625,15 +641,15 @@ impl Drop for KeyPurposeIdIter<'_, '_> {
625641

626642
/// An OID value indicating an Extended Key Usage (EKU) key purpose.
627643
#[derive(Clone, Copy)]
628-
struct KeyPurposeId<'a> {
644+
pub struct KeyPurposeId<'a> {
629645
oid_value: untrusted::Input<'a>,
630646
}
631647

632648
impl<'a> KeyPurposeId<'a> {
633649
/// Construct a new [`KeyPurposeId`].
634650
///
635651
/// `oid` is the OBJECT IDENTIFIER in bytes.
636-
const fn new(oid: &'a [u8]) -> Self {
652+
pub const fn new(oid: &'a [u8]) -> Self {
637653
Self {
638654
oid_value: untrusted::Input::from(oid),
639655
}
@@ -903,7 +919,7 @@ mod tests {
903919
#[test]
904920
fn eku_fail_empty() {
905921
let err = KeyUsage::required(EKU_SERVER_AUTH)
906-
.check(KeyPurposeIdIter {
922+
.validate(KeyPurposeIdIter {
907923
input: &mut untrusted::Reader::new(untrusted::Input::from(&[])),
908924
})
909925
.unwrap_err();

0 commit comments

Comments
 (0)