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

Implement iteration through certificate chain as an iterator. #198

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
25 changes: 25 additions & 0 deletions src/cert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,31 @@ pub struct Cert<'a> {
pub subject_alt_name: Option<untrusted::Input<'a>>,
}

impl<'a> Cert<'a> {
/// Returns an iterator of all the certs in the chain so far.
///
/// The first item will be `self` and the last item will be the end-entity
/// certificate.
pub fn iter_from_self_through_end_entity(&'a self) -> impl Iterator<Item = &'a Cert<'a>> + 'a {
struct Iter<'c> {
next: Option<&'c Cert<'c>>,
}
impl<'c> Iterator for Iter<'c> {
type Item = &'c Cert<'c>;

fn next(&mut self) -> Option<Self::Item> {
let next_next = match self.next?.ee_or_ca {
EndEntityOrCA::EndEntity => None,
EndEntityOrCA::CA(c) => Some(c),
};
core::mem::replace(&mut self.next, next_next)
}
}

Iter { next: Some(self) }
}
}

pub fn parse_cert<'a>(
cert_der: untrusted::Input<'a>,
ee_or_ca: EndEntityOrCA<'a>,
Expand Down
36 changes: 17 additions & 19 deletions src/name/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@ use super::{
dns_name::{self, DnsNameRef},
ip_address,
};
use crate::{
cert::{Cert, EndEntityOrCA},
der, Error,
};
use crate::{cert::Cert, der, Error};

pub fn verify_cert_dns_name(
cert: &crate::EndEntityCert,
Expand Down Expand Up @@ -79,21 +76,22 @@ pub fn check_name_constraints(
let permitted_subtrees = parse_subtrees(input, der::Tag::ContextSpecificConstructed0)?;
let excluded_subtrees = parse_subtrees(input, der::Tag::ContextSpecificConstructed1)?;

let mut child = subordinate_certs;
loop {
iterate_names(child.subject, child.subject_alt_name, Ok(()), &|name| {
check_presented_id_conforms_to_constraints(name, permitted_subtrees, excluded_subtrees)
})?;

child = match child.ee_or_ca {
EndEntityOrCA::CA(child_cert) => child_cert,
EndEntityOrCA::EndEntity => {
break;
}
};
}

Ok(())
subordinate_certs
.iter_from_self_through_end_entity()
.try_for_each(|subordinate| {
iterate_names(
subordinate.subject,
subordinate.subject_alt_name,
Ok(()),
&|name| {
check_presented_id_conforms_to_constraints(
name,
permitted_subtrees,
excluded_subtrees,
)
},
)
})
}

fn check_presented_id_conforms_to_constraints(
Expand Down
49 changes: 14 additions & 35 deletions src/verify_cert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,22 +89,13 @@ pub fn build_chain(
return Err(Error::UnknownIssuer);
}

// Prevent loops; see RFC 4158 section 5.2.
let mut prev = cert;
loop {
if potential_issuer.spki.value() == prev.spki.value()
&& potential_issuer.subject == prev.subject
{
return Err(Error::UnknownIssuer);
}
match &prev.ee_or_ca {
EndEntityOrCA::EndEntity => {
break;
}
EndEntityOrCA::CA(child_cert) => {
prev = child_cert;
}
}
// See RFC 4158 section 5.2.
let loop_detected = cert.iter_from_self_through_end_entity().any(|cert| {
potential_issuer.spki.value() == cert.spki.value()
&& potential_issuer.subject == cert.subject
});
if loop_detected {
return Err(Error::UnknownIssuer);
}

untrusted::read_all_optional(potential_issuer.name_constraints, Error::BadDER, |value| {
Expand Down Expand Up @@ -133,25 +124,13 @@ fn check_signatures(
cert_chain: &Cert,
trust_anchor_key: untrusted::Input,
) -> Result<(), Error> {
let mut spki_value = trust_anchor_key;
let mut cert = cert_chain;
loop {
signed_data::verify_signed_data(supported_sig_algs, spki_value, &cert.signed_data)?;

// TODO: check revocation

match &cert.ee_or_ca {
EndEntityOrCA::CA(child_cert) => {
spki_value = cert.spki.value();
cert = child_cert;
}
EndEntityOrCA::EndEntity => {
break;
}
}
}

Ok(())
cert_chain
.iter_from_self_through_end_entity()
.try_fold(trust_anchor_key, |spki_value, cert| {
signed_data::verify_signed_data(supported_sig_algs, spki_value, &cert.signed_data)?;
Ok(cert.spki.value())
})
.map(|_| ())
}

fn check_issuer_independent_properties(
Expand Down