1use crate::{
16 cert::{self, Cert, EndEntityOrCA},
17 der, name, signed_data, time, Error, SignatureAlgorithm, TrustAnchor,
18};
19
20pub fn build_chain(
21 required_eku_if_present: KeyPurposeId, supported_sig_algs: &[&SignatureAlgorithm],
22 trust_anchors: &[TrustAnchor], intermediate_certs: &[&[u8]], cert: &Cert, time: time::Time,
23 sub_ca_count: usize,
24) -> Result<(), Error> {
25 let used_as_ca = used_as_ca(&cert.ee_or_ca);
26
27 check_issuer_independent_properties(
28 cert,
29 time,
30 used_as_ca,
31 sub_ca_count,
32 required_eku_if_present,
33 )?;
34
35 match used_as_ca {
38 UsedAsCA::Yes => {
39 const MAX_SUB_CA_COUNT: usize = 6;
40
41 if sub_ca_count >= MAX_SUB_CA_COUNT {
42 return Err(Error::UnknownIssuer);
43 }
44 },
45 UsedAsCA::No => {
46 assert_eq!(0, sub_ca_count);
47 },
48 }
49
50 match loop_while_non_fatal_error(trust_anchors, |trust_anchor: &TrustAnchor| {
53 let trust_anchor_subject = untrusted::Input::from(trust_anchor.subject);
54 if cert.issuer != trust_anchor_subject {
55 return Err(Error::UnknownIssuer);
56 }
57
58 let name_constraints = trust_anchor.name_constraints.map(untrusted::Input::from);
59
60 untrusted::read_all_optional(name_constraints, Error::BadDER, |value| {
61 name::check_name_constraints(value, &cert)
62 })?;
63
64 let trust_anchor_spki = untrusted::Input::from(trust_anchor.spki);
65
66 check_signatures(supported_sig_algs, cert, trust_anchor_spki)?;
69
70 Ok(())
71 }) {
72 Ok(()) => {
73 return Ok(());
74 },
75 Err(..) => {
76 },
78 }
79
80 loop_while_non_fatal_error(intermediate_certs, |cert_der| {
81 let potential_issuer =
82 cert::parse_cert(untrusted::Input::from(*cert_der), EndEntityOrCA::CA(&cert))?;
83
84 if potential_issuer.subject != cert.issuer {
85 return Err(Error::UnknownIssuer);
86 }
87
88 let mut prev = cert;
90 loop {
91 if potential_issuer.spki.value() == prev.spki.value()
92 && potential_issuer.subject == prev.subject
93 {
94 return Err(Error::UnknownIssuer);
95 }
96 match &prev.ee_or_ca {
97 &EndEntityOrCA::EndEntity => {
98 break;
99 },
100 &EndEntityOrCA::CA(child_cert) => {
101 prev = child_cert;
102 },
103 }
104 }
105
106 untrusted::read_all_optional(potential_issuer.name_constraints, Error::BadDER, |value| {
107 name::check_name_constraints(value, &cert)
108 })?;
109
110 let next_sub_ca_count = match used_as_ca {
111 UsedAsCA::No => sub_ca_count,
112 UsedAsCA::Yes => sub_ca_count + 1,
113 };
114
115 build_chain(
116 required_eku_if_present,
117 supported_sig_algs,
118 trust_anchors,
119 intermediate_certs,
120 &potential_issuer,
121 time,
122 next_sub_ca_count,
123 )
124 })
125}
126
127fn check_signatures(
128 supported_sig_algs: &[&SignatureAlgorithm], cert_chain: &Cert,
129 trust_anchor_key: untrusted::Input,
130) -> Result<(), Error> {
131 let mut spki_value = trust_anchor_key;
132 let mut cert = cert_chain;
133 loop {
134 signed_data::verify_signed_data(supported_sig_algs, spki_value, &cert.signed_data)?;
135
136 match &cert.ee_or_ca {
139 &EndEntityOrCA::CA(child_cert) => {
140 spki_value = cert.spki.value();
141 cert = child_cert;
142 },
143 &EndEntityOrCA::EndEntity => {
144 break;
145 },
146 }
147 }
148
149 Ok(())
150}
151
152fn check_issuer_independent_properties(
153 cert: &Cert, time: time::Time, used_as_ca: UsedAsCA, sub_ca_count: usize,
154 required_eku_if_present: KeyPurposeId,
155) -> Result<(), Error> {
156 cert.validity
165 .read_all(Error::BadDER, |value| check_validity(value, time))?;
166 untrusted::read_all_optional(cert.basic_constraints, Error::BadDER, |value| {
167 check_basic_constraints(value, used_as_ca, sub_ca_count)
168 })?;
169 untrusted::read_all_optional(cert.eku, Error::BadDER, |value| {
170 check_eku(value, required_eku_if_present)
171 })?;
172
173 Ok(())
174}
175
176fn check_validity(input: &mut untrusted::Reader, time: time::Time) -> Result<(), Error> {
178 let not_before = der::time_choice(input)?;
179 let not_after = der::time_choice(input)?;
180
181 if not_before > not_after {
182 return Err(Error::InvalidCertValidity);
183 }
184 if time < not_before {
185 return Err(Error::CertNotValidYet);
186 }
187 if time > not_after {
188 return Err(Error::CertExpired);
189 }
190
191 Ok(())
196}
197
198#[derive(Clone, Copy)]
199enum UsedAsCA {
200 Yes,
201 No,
202}
203
204fn used_as_ca(ee_or_ca: &EndEntityOrCA) -> UsedAsCA {
205 match ee_or_ca {
206 &EndEntityOrCA::EndEntity => UsedAsCA::No,
207 &EndEntityOrCA::CA(..) => UsedAsCA::Yes,
208 }
209}
210
211fn check_basic_constraints(
213 input: Option<&mut untrusted::Reader>, used_as_ca: UsedAsCA, sub_ca_count: usize,
214) -> Result<(), Error> {
215 let (is_ca, path_len_constraint) = match input {
216 Some(input) => {
217 let is_ca = der::optional_boolean(input)?;
218
219 let path_len_constraint = if !input.at_end() {
224 let value = der::small_nonnegative_integer(input)?;
225 Some(value as usize)
226 } else {
227 None
228 };
229
230 (is_ca, path_len_constraint)
231 },
232 None => (false, None),
233 };
234
235 match (used_as_ca, is_ca, path_len_constraint) {
236 (UsedAsCA::No, true, _) => Err(Error::CAUsedAsEndEntity),
237 (UsedAsCA::Yes, false, _) => Err(Error::EndEntityUsedAsCA),
238 (UsedAsCA::Yes, true, Some(len)) if sub_ca_count > len =>
239 Err(Error::PathLenConstraintViolated),
240 _ => Ok(()),
241 }
242}
243
244#[derive(Clone, Copy)]
245pub struct KeyPurposeId {
246 oid_value: untrusted::Input<'static>,
247}
248
249pub static EKU_SERVER_AUTH: KeyPurposeId = KeyPurposeId {
254 oid_value: untrusted::Input::from(&[(40 * 1) + 3, 6, 1, 5, 5, 7, 3, 1]),
255};
256
257pub static EKU_CLIENT_AUTH: KeyPurposeId = KeyPurposeId {
259 oid_value: untrusted::Input::from(&[(40 * 1) + 3, 6, 1, 5, 5, 7, 3, 2]),
260};
261
262pub static EKU_OCSP_SIGNING: KeyPurposeId = KeyPurposeId {
264 oid_value: untrusted::Input::from(&[(40 * 1) + 3, 6, 1, 5, 5, 7, 3, 9]),
265};
266
267fn check_eku(
284 input: Option<&mut untrusted::Reader>, required_eku_if_present: KeyPurposeId,
285) -> Result<(), Error> {
286 match input {
287 Some(input) => {
288 loop {
289 let value = der::expect_tag_and_get_value(input, der::Tag::OID)?;
290 if value == required_eku_if_present.oid_value {
291 input.skip_to_end();
292 break;
293 }
294 if input.at_end() {
295 return Err(Error::RequiredEKUNotFound);
296 }
297 }
298 Ok(())
299 },
300 None => {
301 if required_eku_if_present.oid_value == EKU_OCSP_SIGNING.oid_value {
311 return Err(Error::RequiredEKUNotFound);
312 }
313
314 Ok(())
315 },
316 }
317}
318
319fn loop_while_non_fatal_error<V, F>(values: V, f: F) -> Result<(), Error>
320where
321 V: IntoIterator,
322 F: Fn(V::Item) -> Result<(), Error>,
323{
324 for v in values {
325 match f(v) {
326 Ok(()) => {
327 return Ok(());
328 },
329 Err(..) => {
330 },
332 }
333 }
334 Err(Error::UnknownIssuer)
335}