use {Error, der, signed_data};
use untrusted;
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: untrusted::Input<'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 fn parse_cert_internal<'a>(
cert_der: untrusted::Input<'a>, ee_or_ca: EndEntityOrCA<'a>,
serial_number: fn(input: &mut untrusted::Reader<'a>)
-> 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_and_get_value(tbs, der::Tag::Sequence)?;
let mut cert: Cert<'a> = Cert {
ee_or_ca: ee_or_ca,
signed_data: signed_data,
issuer: issuer,
validity: validity,
subject: subject,
spki: spki,
basic_constraints: None,
eku: None,
name_constraints: None,
subject_alt_name: None,
};
der::nested_mut(tbs, der::Tag::ContextSpecificConstructed3,
Error::BadDER, |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: untrusted::Input<'a> =
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::BadDER, |input| {
let version = der::small_nonnegative_integer(input)?;
if version != 2 { return Err(Error::UnsupportedCertVersion);
}
Ok(())
})
}
pub fn certificate_serial_number<'a>(input: &mut untrusted::Reader<'a>)
-> Result<(), Error> {
let value = der::positive_integer(input)?;
if value.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)
}