use crate::der::{self, CONSTRUCTED, CONTEXT_SPECIFIC, DerIterator, FromDer};
use crate::error::{DerTypeId, Error};
use crate::subject_name::GeneralName;
pub(crate) struct Extension<'a> {
pub(crate) critical: bool,
pub(crate) id: untrusted::Input<'a>,
pub(crate) value: untrusted::Input<'a>,
}
impl Extension<'_> {
pub(crate) fn unsupported(&self, policy: UnknownExtensionPolicy) -> Result<(), Error> {
match (policy, self.critical) {
(UnknownExtensionPolicy::Strict, true) => Err(Error::UnsupportedCriticalExtension),
_ => Ok(()),
}
}
}
impl<'a> FromDer<'a> for Extension<'a> {
fn from_der(reader: &mut untrusted::Reader<'a>) -> Result<Self, Error> {
let id = der::expect_tag(reader, der::Tag::OID)?;
let critical = bool::from_der(reader)?;
let value = der::expect_tag(reader, der::Tag::OctetString)?;
Ok(Extension {
id,
critical,
value,
})
}
const TYPE_ID: DerTypeId = DerTypeId::Extension;
}
pub(crate) fn set_extension_once<T>(
destination: &mut Option<T>,
parser: impl Fn() -> Result<T, Error>,
) -> Result<(), Error> {
match destination {
Some(..) => Err(Error::ExtensionValueInvalid),
None => {
*destination = Some(parser()?);
Ok(())
}
}
}
pub(crate) fn remember_extension(
extension: &Extension<'_>,
ext_policy: UnknownExtensionPolicy,
mut handler: impl FnMut(ExtensionOid) -> Result<(), Error>,
) -> Result<(), Error> {
match ExtensionOid::lookup(extension.id) {
Some(oid) => handler(oid),
None => extension.unsupported(ext_policy),
}
}
#[derive(Clone, Copy, Debug, Default)]
pub(crate) enum UnknownExtensionPolicy {
#[default]
Strict,
IgnoreCritical,
}
pub(crate) enum DistributionPointName<'a> {
NameRelativeToCrlIssuer,
FullName(DerIterator<'a, GeneralName<'a>>),
}
impl<'a> FromDer<'a> for DistributionPointName<'a> {
fn from_der(reader: &mut untrusted::Reader<'a>) -> Result<Self, Error> {
const FULL_NAME_TAG: u8 = CONTEXT_SPECIFIC | CONSTRUCTED;
const NAME_RELATIVE_TO_CRL_ISSUER_TAG: u8 = CONTEXT_SPECIFIC | CONSTRUCTED | 1;
let (tag, value) = der::read_tag_and_get_value(reader)?;
match tag {
FULL_NAME_TAG => Ok(DistributionPointName::FullName(DerIterator::new(value))),
NAME_RELATIVE_TO_CRL_ISSUER_TAG => Ok(DistributionPointName::NameRelativeToCrlIssuer),
_ => Err(Error::BadDer),
}
}
const TYPE_ID: DerTypeId = DerTypeId::DistributionPointName;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum ExtensionOid {
Standard(u8),
SignedCertificateTimestampList,
}
impl ExtensionOid {
fn lookup(id: untrusted::Input<'_>) -> Option<Self> {
match id.as_slice_less_safe() {
v if v == SCT_LIST_OID => Some(Self::SignedCertificateTimestampList),
[first, second, x] if [*first, *second] == ID_CE => Some(Self::Standard(*x)),
_ => None,
}
}
}
const SCT_LIST_OID: [u8; 10] = [40 + 3, 6, 1, 4, 1, 214, 121, 2, 4, 2];
const ID_CE: [u8; 2] = oid!(2, 5, 29);