webpki/
cert.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, signed_data, Error};
16
17pub enum EndEntityOrCA<'a> {
18    EndEntity,
19    CA(&'a Cert<'a>),
20}
21
22pub struct Cert<'a> {
23    pub ee_or_ca: EndEntityOrCA<'a>,
24
25    pub signed_data: signed_data::SignedData<'a>,
26    pub issuer: untrusted::Input<'a>,
27    pub validity: untrusted::Input<'a>,
28    pub subject: untrusted::Input<'a>,
29    pub spki: der::Value<'a>,
30
31    pub basic_constraints: Option<untrusted::Input<'a>>,
32    pub eku: Option<untrusted::Input<'a>>,
33    pub name_constraints: Option<untrusted::Input<'a>>,
34    pub subject_alt_name: Option<untrusted::Input<'a>>,
35}
36
37pub fn parse_cert<'a>(
38    cert_der: untrusted::Input<'a>, ee_or_ca: EndEntityOrCA<'a>,
39) -> Result<Cert<'a>, Error> {
40    parse_cert_internal(cert_der, ee_or_ca, certificate_serial_number)
41}
42
43/// Used by `parse_cert` for regular certificates (end-entity and intermediate)
44/// and by `cert_der_as_trust_anchor` for trust anchors encoded as
45/// certificates.
46pub(crate) fn parse_cert_internal<'a>(
47    cert_der: untrusted::Input<'a>, ee_or_ca: EndEntityOrCA<'a>,
48    serial_number: fn(input: &mut untrusted::Reader<'_>) -> Result<(), Error>,
49) -> Result<Cert<'a>, Error> {
50    let (tbs, signed_data) = cert_der.read_all(Error::BadDER, |cert_der| {
51        der::nested(
52            cert_der,
53            der::Tag::Sequence,
54            Error::BadDER,
55            signed_data::parse_signed_data,
56        )
57    })?;
58
59    tbs.read_all(Error::BadDER, |tbs| {
60        version3(tbs)?;
61        serial_number(tbs)?;
62
63        let signature = der::expect_tag_and_get_value(tbs, der::Tag::Sequence)?;
64        // TODO: In mozilla::pkix, the comparison is done based on the
65        // normalized value (ignoring whether or not there is an optional NULL
66        // parameter for RSA-based algorithms), so this may be too strict.
67        if signature != signed_data.algorithm {
68            return Err(Error::SignatureAlgorithmMismatch);
69        }
70
71        let issuer = der::expect_tag_and_get_value(tbs, der::Tag::Sequence)?;
72        let validity = der::expect_tag_and_get_value(tbs, der::Tag::Sequence)?;
73        let subject = der::expect_tag_and_get_value(tbs, der::Tag::Sequence)?;
74        let spki = der::expect_tag(tbs, der::Tag::Sequence)?;
75
76        // In theory there could be fields [1] issuerUniqueID and [2]
77        // subjectUniqueID, but in practice there never are, and to keep the
78        // code small and simple we don't accept any certificates that do
79        // contain them.
80
81        let mut cert = Cert {
82            ee_or_ca,
83
84            signed_data,
85            issuer,
86            validity,
87            subject,
88            spki,
89
90            basic_constraints: None,
91            eku: None,
92            name_constraints: None,
93            subject_alt_name: None,
94        };
95
96        // mozilla::pkix allows the extensions to be omitted. However, since
97        // the subjectAltName extension is mandatory, the extensions are
98        // mandatory too, and we enforce that. Also, mozilla::pkix includes
99        // special logic for handling critical Netscape Cert Type extensions.
100        // That has been intentionally omitted.
101
102        der::nested_mut(
103            tbs,
104            der::Tag::ContextSpecificConstructed3,
105            Error::BadDER,
106            |tagged| {
107                der::nested_of_mut(
108                    tagged,
109                    der::Tag::Sequence,
110                    der::Tag::Sequence,
111                    Error::BadDER,
112                    |extension| {
113                        let extn_id = der::expect_tag_and_get_value(extension, der::Tag::OID)?;
114                        let critical = der::optional_boolean(extension)?;
115                        let extn_value =
116                            der::expect_tag_and_get_value(extension, der::Tag::OctetString)?;
117                        match remember_extension(&mut cert, extn_id, extn_value)? {
118                            Understood::No if critical => Err(Error::UnsupportedCriticalExtension),
119                            _ => Ok(()),
120                        }
121                    },
122                )
123            },
124        )?;
125
126        Ok(cert)
127    })
128}
129
130// mozilla::pkix supports v1, v2, v3, and v4, including both the implicit
131// (correct) and explicit (incorrect) encoding of v1. We allow only v3.
132fn version3(input: &mut untrusted::Reader) -> Result<(), Error> {
133    der::nested(
134        input,
135        der::Tag::ContextSpecificConstructed0,
136        Error::BadDER,
137        |input| {
138            let version = der::small_nonnegative_integer(input)?;
139            if version != 2 {
140                // v3
141                return Err(Error::UnsupportedCertVersion);
142            }
143            Ok(())
144        },
145    )
146}
147
148pub fn certificate_serial_number(input: &mut untrusted::Reader) -> Result<(), Error> {
149    // https://tools.ietf.org/html/rfc5280#section-4.1.2.2:
150    // * Conforming CAs MUST NOT use serialNumber values longer than 20 octets."
151    // * "The serial number MUST be a positive integer [...]"
152
153    let value = der::positive_integer(input)?;
154    if value.big_endian_without_leading_zero().len() > 20 {
155        return Err(Error::BadDER);
156    }
157    Ok(())
158}
159
160enum Understood {
161    Yes,
162    No,
163}
164
165fn remember_extension<'a>(
166    cert: &mut Cert<'a>, extn_id: untrusted::Input, value: untrusted::Input<'a>,
167) -> Result<Understood, Error> {
168    // We don't do anything with certificate policies so we can safely ignore
169    // all policy-related stuff. We assume that the policy-related extensions
170    // are not marked critical.
171
172    // id-ce 2.5.29
173    static ID_CE: [u8; 2] = oid![2, 5, 29];
174
175    if extn_id.len() != ID_CE.len() + 1 || !extn_id.as_slice_less_safe().starts_with(&ID_CE) {
176        return Ok(Understood::No);
177    }
178
179    let out = match *extn_id.as_slice_less_safe().last().unwrap() {
180        // id-ce-keyUsage 2.5.29.15. We ignore the KeyUsage extension. For CA
181        // certificates, BasicConstraints.cA makes KeyUsage redundant. Firefox
182        // and other common browsers do not check KeyUsage for end-entities,
183        // though it would be kind of nice to ensure that a KeyUsage without
184        // the keyEncipherment bit could not be used for RSA key exchange.
185        15 => {
186            return Ok(Understood::Yes);
187        },
188
189        // id-ce-subjectAltName 2.5.29.17
190        17 => &mut cert.subject_alt_name,
191
192        // id-ce-basicConstraints 2.5.29.19
193        19 => &mut cert.basic_constraints,
194
195        // id-ce-nameConstraints 2.5.29.30
196        30 => &mut cert.name_constraints,
197
198        // id-ce-extKeyUsage 2.5.29.37
199        37 => &mut cert.eku,
200
201        _ => {
202            return Ok(Understood::No);
203        },
204    };
205
206    match *out {
207        Some(..) => {
208            // The certificate contains more than one instance of this
209            // extension.
210            return Err(Error::ExtensionValueInvalid);
211        },
212        None => {
213            // All the extensions that we care about are wrapped in a SEQUENCE.
214            let sequence_value = value.read_all(Error::BadDER, |value| {
215                der::expect_tag_and_get_value(value, der::Tag::Sequence)
216            })?;
217            *out = Some(sequence_value);
218        },
219    }
220
221    Ok(Understood::Yes)
222}