use crate::{der, signed_data, Error};
pub enum EndEntityOrCa<'a> {
EndEntity,
Ca(&'a Cert<'a>),
}
pub struct Cert<'a> {
pub ee_or_ca: EndEntityOrCa<'a>,
pub signed_data: signed_data::SignedData<'a>,
pub issuer: untrusted::Input<'a>,
pub validity: untrusted::Input<'a>,
pub subject: untrusted::Input<'a>,
pub spki: der::Value<'a>,
pub basic_constraints: Option<untrusted::Input<'a>>,
pub eku: Option<untrusted::Input<'a>>,
pub name_constraints: Option<untrusted::Input<'a>>,
pub subject_alt_name: Option<untrusted::Input<'a>>,
}
pub fn parse_cert<'a>(
cert_der: untrusted::Input<'a>,
ee_or_ca: EndEntityOrCa<'a>,
) -> Result<Cert<'a>, Error> {
parse_cert_internal(cert_der, ee_or_ca, certificate_serial_number)
}
pub(crate) fn parse_cert_internal<'a>(
cert_der: untrusted::Input<'a>,
ee_or_ca: EndEntityOrCa<'a>,
serial_number: fn(input: &mut untrusted::Reader<'_>) -> Result<(), Error>,
) -> Result<Cert<'a>, Error> {
let (tbs, signed_data) = cert_der.read_all(Error::BadDer, |cert_der| {
der::nested(
cert_der,
der::Tag::Sequence,
Error::BadDer,
signed_data::parse_signed_data,
)
})?;
tbs.read_all(Error::BadDer, |tbs| {
version3(tbs)?;
serial_number(tbs)?;
let signature = der::expect_tag_and_get_value(tbs, der::Tag::Sequence)?;
if signature != signed_data.algorithm {
return Err(Error::SignatureAlgorithmMismatch);
}
let issuer = der::expect_tag_and_get_value(tbs, der::Tag::Sequence)?;
let validity = der::expect_tag_and_get_value(tbs, der::Tag::Sequence)?;
let subject = der::expect_tag_and_get_value(tbs, der::Tag::Sequence)?;
let spki = der::expect_tag(tbs, der::Tag::Sequence)?;
let mut cert = Cert {
ee_or_ca,
signed_data,
issuer,
validity,
subject,
spki,
basic_constraints: None,
eku: None,
name_constraints: None,
subject_alt_name: None,
};
der::nested(
tbs,
der::Tag::ContextSpecificConstructed3,
Error::MissingOrMalformedExtensions,
|tagged| {
der::nested_of_mut(
tagged,
der::Tag::Sequence,
der::Tag::Sequence,
Error::BadDer,
|extension| {
let extn_id = der::expect_tag_and_get_value(extension, der::Tag::OID)?;
let critical = der::optional_boolean(extension)?;
let extn_value =
der::expect_tag_and_get_value(extension, der::Tag::OctetString)?;
match remember_extension(&mut cert, extn_id, extn_value)? {
Understood::No if critical => Err(Error::UnsupportedCriticalExtension),
_ => Ok(()),
}
},
)
},
)?;
Ok(cert)
})
}
fn version3(input: &mut untrusted::Reader) -> Result<(), Error> {
der::nested(
input,
der::Tag::ContextSpecificConstructed0,
Error::UnsupportedCertVersion,
|input| {
let version = der::small_nonnegative_integer(input)?;
if version != 2 {
return Err(Error::UnsupportedCertVersion);
}
Ok(())
},
)
}
pub fn certificate_serial_number(input: &mut untrusted::Reader) -> Result<(), Error> {
let value = der::positive_integer(input)?;
if value.big_endian_without_leading_zero().len() > 20 {
return Err(Error::BadDer);
}
Ok(())
}
enum Understood {
Yes,
No,
}
fn remember_extension<'a>(
cert: &mut Cert<'a>,
extn_id: untrusted::Input,
value: untrusted::Input<'a>,
) -> Result<Understood, Error> {
static ID_CE: [u8; 2] = oid![2, 5, 29];
if extn_id.len() != ID_CE.len() + 1 || !extn_id.as_slice_less_safe().starts_with(&ID_CE) {
return Ok(Understood::No);
}
let out = match *extn_id.as_slice_less_safe().last().unwrap() {
15 => {
return Ok(Understood::Yes);
}
17 => &mut cert.subject_alt_name,
19 => &mut cert.basic_constraints,
30 => &mut cert.name_constraints,
37 => &mut cert.eku,
_ => {
return Ok(Understood::No);
}
};
match *out {
Some(..) => {
return Err(Error::ExtensionValueInvalid);
}
None => {
let sequence_value = value.read_all(Error::BadDer, |value| {
der::expect_tag_and_get_value(value, der::Tag::Sequence)
})?;
*out = Some(sequence_value);
}
}
Ok(Understood::Yes)
}