webpki/
x509.rs

1// Copyright 2015 Brian Smith.
2//
3// Permission to use, copy, modify, and/or distribute this software for any
4// purpose with or without fee is hereby granted, provided that the above
5// copyright notice and this permission notice appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
10// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15use crate::der::{self, CONSTRUCTED, CONTEXT_SPECIFIC, DerIterator, FromDer};
16use crate::error::{DerTypeId, Error};
17use crate::subject_name::GeneralName;
18
19pub(crate) struct Extension<'a> {
20    pub(crate) critical: bool,
21    pub(crate) id: untrusted::Input<'a>,
22    pub(crate) value: untrusted::Input<'a>,
23}
24
25impl Extension<'_> {
26    pub(crate) fn unsupported(&self, policy: UnknownExtensionPolicy) -> Result<(), Error> {
27        match (policy, self.critical) {
28            (UnknownExtensionPolicy::Strict, true) => Err(Error::UnsupportedCriticalExtension),
29            _ => Ok(()),
30        }
31    }
32}
33
34impl<'a> FromDer<'a> for Extension<'a> {
35    fn from_der(reader: &mut untrusted::Reader<'a>) -> Result<Self, Error> {
36        let id = der::expect_tag(reader, der::Tag::OID)?;
37        let critical = bool::from_der(reader)?;
38        let value = der::expect_tag(reader, der::Tag::OctetString)?;
39        Ok(Extension {
40            id,
41            critical,
42            value,
43        })
44    }
45
46    const TYPE_ID: DerTypeId = DerTypeId::Extension;
47}
48
49pub(crate) fn set_extension_once<T>(
50    destination: &mut Option<T>,
51    parser: impl Fn() -> Result<T, Error>,
52) -> Result<(), Error> {
53    match destination {
54        // The extension value has already been set, indicating that we encountered it
55        // more than once in our serialized data. That's invalid!
56        Some(..) => Err(Error::ExtensionValueInvalid),
57        None => {
58            *destination = Some(parser()?);
59            Ok(())
60        }
61    }
62}
63
64pub(crate) fn remember_extension(
65    extension: &Extension<'_>,
66    ext_policy: UnknownExtensionPolicy,
67    mut handler: impl FnMut(u8) -> Result<(), Error>,
68) -> Result<(), Error> {
69    // ISO arc for standard certificate and CRL extensions.
70    // https://www.rfc-editor.org/rfc/rfc5280#appendix-A.2
71    static ID_CE: [u8; 2] = oid![2, 5, 29];
72
73    if extension.id.len() != ID_CE.len() + 1
74        || !extension.id.as_slice_less_safe().starts_with(&ID_CE)
75    {
76        return extension.unsupported(ext_policy);
77    }
78
79    // safety: we verify len is non-zero and has the correct prefix above.
80    let last_octet = *extension.id.as_slice_less_safe().last().unwrap();
81    handler(last_octet)
82}
83
84#[derive(Clone, Copy, Debug, Default)]
85pub(crate) enum UnknownExtensionPolicy {
86    #[default]
87    Strict,
88    IgnoreCritical,
89}
90
91/// A certificate revocation list (CRL) distribution point name, describing a source of
92/// CRL information for a given certificate as described in RFC 5280 section 4.2.3.13[^1].
93///
94/// [^1]: <https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.13>
95pub(crate) enum DistributionPointName<'a> {
96    /// The distribution point name is a relative distinguished name, relative to the CRL issuer.
97    NameRelativeToCrlIssuer,
98    /// The distribution point name is a sequence of [GeneralName] items.
99    FullName(DerIterator<'a, GeneralName<'a>>),
100}
101
102impl<'a> FromDer<'a> for DistributionPointName<'a> {
103    fn from_der(reader: &mut untrusted::Reader<'a>) -> Result<Self, Error> {
104        // RFC 5280 section ยง4.2.1.13:
105        //   When the distributionPoint field is present, it contains either a
106        //   SEQUENCE of general names or a single value, nameRelativeToCRLIssuer
107        const FULL_NAME_TAG: u8 = CONTEXT_SPECIFIC | CONSTRUCTED;
108        const NAME_RELATIVE_TO_CRL_ISSUER_TAG: u8 = CONTEXT_SPECIFIC | CONSTRUCTED | 1;
109
110        let (tag, value) = der::read_tag_and_get_value(reader)?;
111        match tag {
112            FULL_NAME_TAG => Ok(DistributionPointName::FullName(DerIterator::new(value))),
113            NAME_RELATIVE_TO_CRL_ISSUER_TAG => Ok(DistributionPointName::NameRelativeToCrlIssuer),
114            _ => Err(Error::BadDer),
115        }
116    }
117
118    const TYPE_ID: DerTypeId = DerTypeId::DistributionPointName;
119}