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 | |
15 | use crate::der::{self, DerIterator, FromDer, CONSTRUCTED, CONTEXT_SPECIFIC}; |
16 | use crate::error::{DerTypeId, Error}; |
17 | use crate::subject_name::GeneralName; |
18 | |
19 | pub(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 | |
25 | impl<'a> Extension<'a> { |
26 | pub(crate) fn unsupported(&self) -> Result<(), Error> { |
27 | match self.critical { |
28 | true => Err(Error::UnsupportedCriticalExtension), |
29 | false => Ok(()), |
30 | } |
31 | } |
32 | } |
33 | |
34 | impl<'a> FromDer<'a> for Extension<'a> { |
35 | fn from_der(reader: &mut untrusted::Reader<'a>) -> Result<Self, Error> { |
36 | let id: Input<'_> = der::expect_tag(input:reader, der::Tag::OID)?; |
37 | let critical: bool = bool::from_der(reader)?; |
38 | let value: Input<'_> = der::expect_tag(input: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 | |
49 | pub(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 | |
64 | pub(crate) fn remember_extension( |
65 | extension: &Extension, |
66 | mut handler: impl FnMut(u8) -> Result<(), Error>, |
67 | ) -> Result<(), Error> { |
68 | // ISO arc for standard certificate and CRL extensions. |
69 | // https://www.rfc-editor.org/rfc/rfc5280#appendix-A.2 |
70 | static ID_CE: [u8; 2] = oid![2, 5, 29]; |
71 | |
72 | if extension.id.len() != ID_CE.len() + 1 |
73 | || !extension.id.as_slice_less_safe().starts_with(&ID_CE) |
74 | { |
75 | return extension.unsupported(); |
76 | } |
77 | |
78 | // safety: we verify len is non-zero and has the correct prefix above. |
79 | let last_octet: u8 = *extension.id.as_slice_less_safe().last().unwrap(); |
80 | handler(last_octet) |
81 | } |
82 | |
83 | /// A certificate revocation list (CRL) distribution point name, describing a source of |
84 | /// CRL information for a given certificate as described in RFC 5280 section 4.2.3.13[^1]. |
85 | /// |
86 | /// [^1]: <https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.13> |
87 | pub(crate) enum DistributionPointName<'a> { |
88 | /// The distribution point name is a relative distinguished name, relative to the CRL issuer. |
89 | NameRelativeToCrlIssuer, |
90 | /// The distribution point name is a sequence of [GeneralName] items. |
91 | FullName(DerIterator<'a, GeneralName<'a>>), |
92 | } |
93 | |
94 | impl<'a> FromDer<'a> for DistributionPointName<'a> { |
95 | fn from_der(reader: &mut untrusted::Reader<'a>) -> Result<DistributionPointName<'a>, Error> { |
96 | // RFC 5280 section ยง4.2.1.13: |
97 | // When the distributionPoint field is present, it contains either a |
98 | // SEQUENCE of general names or a single value, nameRelativeToCRLIssuer |
99 | const FULL_NAME_TAG: u8 = CONTEXT_SPECIFIC | CONSTRUCTED; |
100 | const NAME_RELATIVE_TO_CRL_ISSUER_TAG: u8 = CONTEXT_SPECIFIC | CONSTRUCTED | 1; |
101 | |
102 | let (tag: u8, value: Input<'_>) = der::read_tag_and_get_value(input:reader)?; |
103 | match tag { |
104 | FULL_NAME_TAG => Ok(DistributionPointName::FullName(DerIterator::new(input:value))), |
105 | NAME_RELATIVE_TO_CRL_ISSUER_TAG => Ok(DistributionPointName::NameRelativeToCrlIssuer), |
106 | _ => Err(Error::BadDer), |
107 | } |
108 | } |
109 | |
110 | const TYPE_ID: DerTypeId = DerTypeId::DistributionPointName; |
111 | } |
112 | |