// Copyright 2015 Brian Smith. // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 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) -> Result<(), Error> { match self.critical { true => Err(Error::UnsupportedCriticalExtension), false => Ok(()), } } } impl<'a> FromDer<'a> for Extension<'a> { fn from_der(reader: &mut untrusted::Reader<'a>) -> Result { 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( destination: &mut Option, parser: impl Fn() -> Result, ) -> Result<(), Error> { match destination { // The extension value has already been set, indicating that we encountered it // more than once in our serialized data. That's invalid! Some(..) => Err(Error::ExtensionValueInvalid), None => { *destination = Some(parser()?); Ok(()) } } } pub(crate) fn remember_extension( extension: &Extension<'_>, mut handler: impl FnMut(u8) -> Result<(), Error>, ) -> Result<(), Error> { // ISO arc for standard certificate and CRL extensions. // https://www.rfc-editor.org/rfc/rfc5280#appendix-A.2 static ID_CE: [u8; 2] = oid![2, 5, 29]; if extension.id.len() != ID_CE.len() + 1 || !extension.id.as_slice_less_safe().starts_with(&ID_CE) { return extension.unsupported(); } // safety: we verify len is non-zero and has the correct prefix above. let last_octet = *extension.id.as_slice_less_safe().last().unwrap(); handler(last_octet) } /// A certificate revocation list (CRL) distribution point name, describing a source of /// CRL information for a given certificate as described in RFC 5280 section 4.2.3.13[^1]. /// /// [^1]: pub(crate) enum DistributionPointName<'a> { /// The distribution point name is a relative distinguished name, relative to the CRL issuer. NameRelativeToCrlIssuer, /// The distribution point name is a sequence of [GeneralName] items. FullName(DerIterator<'a, GeneralName<'a>>), } impl<'a> FromDer<'a> for DistributionPointName<'a> { fn from_der(reader: &mut untrusted::Reader<'a>) -> Result { // RFC 5280 section ยง4.2.1.13: // When the distributionPoint field is present, it contains either a // SEQUENCE of general names or a single value, nameRelativeToCRLIssuer 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; }