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 core::default::Default;
16use core::ops::ControlFlow;
17
18use pki_types::{CertificateDer, SignatureVerificationAlgorithm, TrustAnchor, UnixTime};
19
20use crate::cert::Cert;
21use crate::crl::RevocationOptions;
22use crate::der::{self, FromDer};
23use crate::end_entity::EndEntityCert;
24use crate::error::Error;
25use crate::{public_values_eq, signed_data, subject_name};
26
27// Use `'a` for lifetimes that we don't care about, `'p` for lifetimes that become a part of
28// the `VerifiedPath`.
29pub(crate) struct ChainOptions<'a, 'p> {
30 pub(crate) eku: KeyUsage,
31 pub(crate) supported_sig_algs: &'a [&'a dyn SignatureVerificationAlgorithm],
32 pub(crate) trust_anchors: &'p [TrustAnchor<'p>],
33 pub(crate) intermediate_certs: &'p [CertificateDer<'p>],
34 pub(crate) revocation: Option<RevocationOptions<'a>>,
35}
36
37impl<'a, 'p: 'a> ChainOptions<'a, 'p> {
38 pub(crate) fn build_chain(
39 &self,
40 end_entity: &'p EndEntityCert<'p>,
41 time: UnixTime,
42 verify_path: Option<&dyn Fn(&VerifiedPath<'_>) -> Result<(), Error>>,
43 ) -> Result<VerifiedPath<'p>, Error> {
44 let mut path = PartialPath::new(end_entity);
45 match self.build_chain_inner(&mut path, time, verify_path, 0, &mut Budget::default()) {
46 Ok(anchor) => Ok(VerifiedPath::new(end_entity, anchor, path)),
47 Err(ControlFlow::Break(err)) | Err(ControlFlow::Continue(err)) => Err(err),
48 }
49 }
50
51 fn build_chain_inner(
52 &self,
53 path: &mut PartialPath<'p>,
54 time: UnixTime,
55 verify_path: Option<&dyn Fn(&VerifiedPath<'_>) -> Result<(), Error>>,
56 sub_ca_count: usize,
57 budget: &mut Budget,
58 ) -> Result<&'p TrustAnchor<'p>, ControlFlow<Error, Error>> {
59 let role = path.node().role();
60
61 check_issuer_independent_properties(path.head(), time, role, sub_ca_count, self.eku.inner)?;
62
63 // TODO: HPKP checks.
64
65 let result = loop_while_non_fatal_error(
66 Error::UnknownIssuer,
67 self.trust_anchors,
68 |trust_anchor: &TrustAnchor| {
69 let trust_anchor_subject = untrusted::Input::from(trust_anchor.subject.as_ref());
70 if !public_values_eq(path.head().issuer, trust_anchor_subject) {
71 return Err(Error::UnknownIssuer.into());
72 }
73
74 // TODO: check_distrust(trust_anchor_subject, trust_anchor_spki)?;
75
76 let node = path.node();
77 self.check_signed_chain(&node, trust_anchor, budget)?;
78 check_signed_chain_name_constraints(&node, trust_anchor, budget)?;
79
80 let verify = match verify_path {
81 Some(verify) => verify,
82 None => return Ok(trust_anchor),
83 };
84
85 let candidate = VerifiedPath {
86 end_entity: path.end_entity,
87 intermediates: Intermediates::Borrowed(&path.intermediates[..path.used]),
88 anchor: trust_anchor,
89 };
90
91 match verify(&candidate) {
92 Ok(()) => Ok(trust_anchor),
93 Err(err) => Err(ControlFlow::Continue(err)),
94 }
95 },
96 );
97
98 let err = match result {
99 Ok(anchor) => return Ok(anchor),
100 // Fatal errors should halt further path building.
101 res @ Err(ControlFlow::Break(_)) => return res,
102 // Non-fatal errors should be carried forward as the default_error for subsequent
103 // loop_while_non_fatal_error processing and only returned once all other path-building
104 // options have been exhausted.
105 Err(ControlFlow::Continue(err)) => err,
106 };
107
108 loop_while_non_fatal_error(err, self.intermediate_certs, |cert_der| {
109 let potential_issuer = Cert::from_der(untrusted::Input::from(cert_der))?;
110 if !public_values_eq(potential_issuer.subject, path.head().issuer) {
111 return Err(Error::UnknownIssuer.into());
112 }
113
114 // Prevent loops; see RFC 4158 section 5.2.
115 if path.node().iter().any(|prev| {
116 public_values_eq(potential_issuer.spki, prev.cert.spki)
117 && public_values_eq(potential_issuer.subject, prev.cert.subject)
118 }) {
119 return Err(Error::UnknownIssuer.into());
120 }
121
122 let next_sub_ca_count = match role {
123 Role::EndEntity => sub_ca_count,
124 Role::Issuer => sub_ca_count + 1,
125 };
126
127 budget.consume_build_chain_call()?;
128 path.push(potential_issuer)?;
129 let result = self.build_chain_inner(path, time, verify_path, next_sub_ca_count, budget);
130 if result.is_err() {
131 path.pop();
132 }
133
134 result
135 })
136 }
137
138 fn check_signed_chain(
139 &self,
140 path: &PathNode<'_>,
141 trust_anchor: &TrustAnchor,
142 budget: &mut Budget,
143 ) -> Result<(), ControlFlow<Error, Error>> {
144 let mut spki_value = untrusted::Input::from(trust_anchor.subject_public_key_info.as_ref());
145 let mut issuer_subject = untrusted::Input::from(trust_anchor.subject.as_ref());
146 let mut issuer_key_usage = None; // TODO(XXX): Consider whether to track TrustAnchor KU.
147 for path in path.iter() {
148 signed_data::verify_signed_data(
149 self.supported_sig_algs,
150 spki_value,
151 &path.cert.signed_data,
152 budget,
153 )?;
154
155 if let Some(revocation_opts) = &self.revocation {
156 revocation_opts.check(
157 &path,
158 issuer_subject,
159 spki_value,
160 issuer_key_usage,
161 self.supported_sig_algs,
162 budget,
163 )?;
164 }
165
166 spki_value = path.cert.spki;
167 issuer_subject = path.cert.subject;
168 issuer_key_usage = path.cert.key_usage;
169 }
170
171 Ok(())
172 }
173}
174
175/// Path from end-entity certificate to trust anchor that's been verified.
176///
177/// See [`EndEntityCert::verify_for_usage()`] for more details on what verification entails.
178pub struct VerifiedPath<'p> {
179 end_entity: &'p EndEntityCert<'p>,
180 intermediates: Intermediates<'p>,
181 anchor: &'p TrustAnchor<'p>,
182}
183
184impl<'p> VerifiedPath<'p> {
185 fn new(
186 end_entity: &'p EndEntityCert<'p>,
187 anchor: &'p TrustAnchor<'p>,
188 partial: PartialPath<'p>,
189 ) -> Self {
190 Self {
191 end_entity,
192 intermediates: Intermediates::Owned {
193 certs: partial.intermediates,
194 used: partial.used,
195 },
196 anchor,
197 }
198 }
199
200 /// Yields a (double-ended) iterator over the intermediate certificates in this path.
201 pub fn intermediate_certificates(&'p self) -> IntermediateIterator<'p> {
202 IntermediateIterator {
203 intermediates: self.intermediates.as_ref(),
204 }
205 }
206
207 /// Yields the end-entity certificate for this path.
208 pub fn end_entity(&self) -> &'p EndEntityCert<'p> {
209 self.end_entity
210 }
211
212 /// Yields the trust anchor for this path.
213 pub fn anchor(&self) -> &'p TrustAnchor<'p> {
214 self.anchor
215 }
216}
217
218/// Iterator over a path's intermediate certificates.
219///
220/// Implements [`DoubleEndedIterator`] so it can be traversed in both directions.
221pub struct IntermediateIterator<'a> {
222 /// Invariant: all of these `Option`s are `Some`.
223 intermediates: &'a [Option<Cert<'a>>],
224}
225
226impl<'a> Iterator for IntermediateIterator<'a> {
227 type Item = &'a Cert<'a>;
228
229 fn next(&mut self) -> Option<Self::Item> {
230 match self.intermediates.split_first() {
231 Some((head: &Option>, tail: &[Option>])) => {
232 self.intermediates = tail;
233 Some(head.as_ref().unwrap())
234 }
235 None => None,
236 }
237 }
238}
239
240impl<'a> DoubleEndedIterator for IntermediateIterator<'a> {
241 fn next_back(&mut self) -> Option<Self::Item> {
242 match self.intermediates.split_last() {
243 Some((head: &Option>, tail: &[Option>])) => {
244 self.intermediates = tail;
245 Some(head.as_ref().unwrap())
246 }
247 None => None,
248 }
249 }
250}
251
252#[allow(clippy::large_enum_variant)]
253enum Intermediates<'a> {
254 Owned {
255 certs: [Option<Cert<'a>>; MAX_SUB_CA_COUNT],
256 used: usize,
257 },
258 Borrowed(&'a [Option<Cert<'a>>]),
259}
260
261impl<'a> AsRef<[Option<Cert<'a>>]> for Intermediates<'a> {
262 fn as_ref(&self) -> &[Option<Cert<'a>>] {
263 match self {
264 Intermediates::Owned { certs: &[Option>; 6], used: &usize } => &certs[..*used],
265 Intermediates::Borrowed(certs: &&[Option>]) => certs,
266 }
267 }
268}
269
270fn check_signed_chain_name_constraints(
271 path: &PathNode<'_>,
272 trust_anchor: &TrustAnchor,
273 budget: &mut Budget,
274) -> Result<(), ControlFlow<Error, Error>> {
275 let mut name_constraints: Option> = trust_anchorOption<&Der<'_>>
276 .name_constraints
277 .as_ref()
278 .map(|der: &Der<'_>| untrusted::Input::from(bytes:der.as_ref()));
279
280 for path: PathNode<'_> in path.iter() {
281 untrusted::read_all_optional(input:name_constraints, incomplete_read:Error::BadDer, |value: Option<&mut Reader<'_>>| {
282 subject_name::check_name_constraints(constraints:value, &path, budget)
283 })?;
284
285 name_constraints = path.cert.name_constraints;
286 }
287
288 Ok(())
289}
290
291pub(crate) struct Budget {
292 signatures: usize,
293 build_chain_calls: usize,
294 name_constraint_comparisons: usize,
295}
296
297impl Budget {
298 #[inline]
299 pub(crate) fn consume_signature(&mut self) -> Result<(), Error> {
300 self.signatures = self
301 .signatures
302 .checked_sub(1)
303 .ok_or(Error::MaximumSignatureChecksExceeded)?;
304 Ok(())
305 }
306
307 #[inline]
308 fn consume_build_chain_call(&mut self) -> Result<(), Error> {
309 self.build_chain_calls = self
310 .build_chain_calls
311 .checked_sub(1)
312 .ok_or(Error::MaximumPathBuildCallsExceeded)?;
313 Ok(())
314 }
315
316 #[inline]
317 pub(crate) fn consume_name_constraint_comparison(&mut self) -> Result<(), Error> {
318 self.name_constraint_comparisons = self
319 .name_constraint_comparisons
320 .checked_sub(1)
321 .ok_or(Error::MaximumNameConstraintComparisonsExceeded)?;
322 Ok(())
323 }
324}
325
326impl Default for Budget {
327 fn default() -> Self {
328 Self {
329 // This limit is taken from the remediation for golang CVE-2018-16875. However,
330 // note that golang subsequently implemented AKID matching due to this limit
331 // being hit in real applications (see <https://github.com/spiffe/spire/issues/1004>).
332 // So this may actually be too aggressive.
333 signatures: 100,
334
335 // This limit is taken from mozilla::pkix, see:
336 // <https://github.com/nss-dev/nss/blob/bb4a1d38dd9e92923525ac6b5ed0288479f3f3fc/lib/mozpkix/lib/pkixbuild.cpp#L381-L393>
337 build_chain_calls: 200_000,
338
339 // This limit is taken from golang crypto/x509's default, see:
340 // <https://github.com/golang/go/blob/ac17bb6f13979f2ab9fcd45f0758b43ed72d0973/src/crypto/x509/verify.go#L588-L592>
341 name_constraint_comparisons: 250_000,
342 }
343 }
344}
345
346fn check_issuer_independent_properties(
347 cert: &Cert,
348 time: UnixTime,
349 role: Role,
350 sub_ca_count: usize,
351 eku: ExtendedKeyUsage,
352) -> Result<(), Error> {
353 // TODO: check_distrust(trust_anchor_subject, trust_anchor_spki)?;
354 // TODO: Check signature algorithm like mozilla::pkix.
355 // TODO: Check SPKI like mozilla::pkix.
356 // TODO: check for active distrust like mozilla::pkix.
357
358 // For cert validation, we ignore the KeyUsage extension. For CA
359 // certificates, BasicConstraints.cA makes KeyUsage redundant. Firefox
360 // and other common browsers do not check KeyUsage for end-entities,
361 // though it would be kind of nice to ensure that a KeyUsage without
362 // the keyEncipherment bit could not be used for RSA key exchange.
363
364 cert.validity
365 .read_all(incomplete_read:Error::BadDer, |value: &mut Reader<'_>| check_validity(input:value, time))?;
366 untrusted::read_all_optional(input:cert.basic_constraints, incomplete_read:Error::BadDer, |value: Option<&mut Reader<'_>>| {
367 check_basic_constraints(input:value, role, sub_ca_count)
368 })?;
369 untrusted::read_all_optional(input:cert.eku, incomplete_read:Error::BadDer, |value: Option<&mut Reader<'_>>| eku.check(input:value))?;
370
371 Ok(())
372}
373
374// https://tools.ietf.org/html/rfc5280#section-4.1.2.5
375fn check_validity(input: &mut untrusted::Reader, time: UnixTime) -> Result<(), Error> {
376 let not_before: UnixTime = UnixTime::from_der(reader:input)?;
377 let not_after: UnixTime = UnixTime::from_der(reader:input)?;
378
379 if not_before > not_after {
380 return Err(Error::InvalidCertValidity);
381 }
382 if time < not_before {
383 return Err(Error::CertNotValidYet);
384 }
385 if time > not_after {
386 return Err(Error::CertExpired);
387 }
388
389 // TODO: mozilla::pkix allows the TrustDomain to check not_before and
390 // not_after, to enforce things like a maximum validity period. We should
391 // do something similar.
392
393 Ok(())
394}
395
396// https://tools.ietf.org/html/rfc5280#section-4.2.1.9
397fn check_basic_constraints(
398 input: Option<&mut untrusted::Reader>,
399 role: Role,
400 sub_ca_count: usize,
401) -> Result<(), Error> {
402 let (is_ca, path_len_constraint) = match input {
403 Some(input) => {
404 let is_ca = bool::from_der(input)?;
405
406 // https://bugzilla.mozilla.org/show_bug.cgi?id=985025: RFC 5280
407 // says that a certificate must not have pathLenConstraint unless
408 // it is a CA certificate, but some real-world end-entity
409 // certificates have pathLenConstraint.
410 let path_len_constraint = if !input.at_end() {
411 Some(usize::from(u8::from_der(input)?))
412 } else {
413 None
414 };
415
416 (is_ca, path_len_constraint)
417 }
418 None => (false, None),
419 };
420
421 match (role, is_ca, path_len_constraint) {
422 (Role::EndEntity, true, _) => Err(Error::CaUsedAsEndEntity),
423 (Role::Issuer, false, _) => Err(Error::EndEntityUsedAsCa),
424 (Role::Issuer, true, Some(len)) if sub_ca_count > len => {
425 Err(Error::PathLenConstraintViolated)
426 }
427 _ => Ok(()),
428 }
429}
430
431/// The expected key usage of a certificate.
432///
433/// This type represents the expected key usage of an end entity certificate. Although for most
434/// kinds of certificates the extended key usage extension is optional (and so certificates
435/// not carrying a particular value in the EKU extension are acceptable). If the extension
436/// is present, the certificate MUST only be used for one of the purposes indicated.
437///
438/// <https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.12>
439#[derive(Clone, Copy)]
440pub struct KeyUsage {
441 inner: ExtendedKeyUsage,
442}
443
444impl KeyUsage {
445 /// Construct a new [`KeyUsage`] as appropriate for server certificate authentication.
446 ///
447 /// As specified in <https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.12>, this does not require the certificate to specify the eKU extension.
448 pub const fn server_auth() -> Self {
449 Self {
450 inner: ExtendedKeyUsage::RequiredIfPresent(EKU_SERVER_AUTH),
451 }
452 }
453
454 /// Construct a new [`KeyUsage`] as appropriate for client certificate authentication.
455 ///
456 /// As specified in <>, this does not require the certificate to specify the eKU extension.
457 pub const fn client_auth() -> Self {
458 Self {
459 inner: ExtendedKeyUsage::RequiredIfPresent(EKU_CLIENT_AUTH),
460 }
461 }
462
463 /// Construct a new [`KeyUsage`] requiring a certificate to support the specified OID.
464 pub const fn required(oid: &'static [u8]) -> Self {
465 Self {
466 inner: ExtendedKeyUsage::Required(KeyPurposeId::new(oid)),
467 }
468 }
469}
470
471/// Extended Key Usage (EKU) of a certificate.
472#[derive(Clone, Copy)]
473enum ExtendedKeyUsage {
474 /// The certificate must contain the specified [`KeyPurposeId`] as EKU.
475 Required(KeyPurposeId),
476
477 /// If the certificate has EKUs, then the specified [`KeyPurposeId`] must be included.
478 RequiredIfPresent(KeyPurposeId),
479}
480
481impl ExtendedKeyUsage {
482 // https://tools.ietf.org/html/rfc5280#section-4.2.1.12
483 fn check(&self, input: Option<&mut untrusted::Reader>) -> Result<(), Error> {
484 let input = match (input, self) {
485 (Some(input), _) => input,
486 (None, Self::RequiredIfPresent(_)) => return Ok(()),
487 (None, Self::Required(_)) => return Err(Error::RequiredEkuNotFound),
488 };
489
490 loop {
491 let value = der::expect_tag(input, der::Tag::OID)?;
492 if self.key_purpose_id_equals(value) {
493 input.skip_to_end();
494 break;
495 }
496
497 if input.at_end() {
498 return Err(Error::RequiredEkuNotFound);
499 }
500 }
501
502 Ok(())
503 }
504
505 fn key_purpose_id_equals(&self, value: untrusted::Input<'_>) -> bool {
506 public_values_eq(
507 match self {
508 ExtendedKeyUsage::Required(eku) => *eku,
509 ExtendedKeyUsage::RequiredIfPresent(eku) => *eku,
510 }
511 .oid_value,
512 value,
513 )
514 }
515}
516
517/// An OID value indicating an Extended Key Usage (EKU) key purpose.
518#[derive(Clone, Copy)]
519struct KeyPurposeId {
520 oid_value: untrusted::Input<'static>,
521}
522
523impl KeyPurposeId {
524 /// Construct a new [`KeyPurposeId`].
525 ///
526 /// `oid` is the OBJECT IDENTIFIER in bytes.
527 const fn new(oid: &'static [u8]) -> Self {
528 Self {
529 oid_value: untrusted::Input::from(bytes:oid),
530 }
531 }
532}
533
534impl PartialEq<Self> for KeyPurposeId {
535 fn eq(&self, other: &Self) -> bool {
536 public_values_eq(self.oid_value, b:other.oid_value)
537 }
538}
539
540impl Eq for KeyPurposeId {}
541
542// id-pkix OBJECT IDENTIFIER ::= { 1 3 6 1 5 5 7 }
543// id-kp OBJECT IDENTIFIER ::= { id-pkix 3 }
544
545// id-kp-serverAuth OBJECT IDENTIFIER ::= { id-kp 1 }
546const EKU_SERVER_AUTH: KeyPurposeId = KeyPurposeId::new(&oid!(1, 3, 6, 1, 5, 5, 7, 3, 1));
547
548// id-kp-clientAuth OBJECT IDENTIFIER ::= { id-kp 2 }
549const EKU_CLIENT_AUTH: KeyPurposeId = KeyPurposeId::new(&oid!(1, 3, 6, 1, 5, 5, 7, 3, 2));
550
551fn loop_while_non_fatal_error<'a, V: 'a>(
552 default_error: Error,
553 values: V,
554 mut f: impl FnMut(V::Item) -> Result<&'a TrustAnchor<'a>, ControlFlow<Error, Error>>,
555) -> Result<&'a TrustAnchor<'a>, ControlFlow<Error, Error>>
556where
557 V: IntoIterator,
558{
559 let mut error: Error = default_error;
560 for v: ::Item in values {
561 match f(v) {
562 Ok(anchor: &TrustAnchor<'_>) => return Ok(anchor),
563 // Fatal errors should halt further looping.
564 res: Result<&TrustAnchor<'_>, …> @ Err(ControlFlow::Break(_)) => return res,
565 // Non-fatal errors should be ranked by specificity and only returned
566 // once all other path-building options have been exhausted.
567 Err(ControlFlow::Continue(new_error: Error)) => error = error.most_specific(new_error),
568 }
569 }
570 Err(error.into())
571}
572
573/// A path for consideration in path building.
574///
575/// This represents a partial path because it does not yet contain the trust anchor. It stores
576/// the end-entity certificates, and an array of intermediate certificates.
577pub(crate) struct PartialPath<'a> {
578 end_entity: &'a EndEntityCert<'a>,
579 /// Intermediate certificates, in order from end-entity to trust anchor.
580 ///
581 /// Invariant: all values below `used` are `Some`.
582 intermediates: [Option<Cert<'a>>; MAX_SUB_CA_COUNT],
583 /// The number of `Some` values in `intermediates`.
584 ///
585 /// The next `Cert` passed to `push()` will be placed at `intermediates[used]`.
586 /// If this value is 0, the path contains only the end-entity certificate.
587 used: usize,
588}
589
590impl<'a> PartialPath<'a> {
591 pub(crate) fn new(end_entity: &'a EndEntityCert<'a>) -> Self {
592 Self {
593 end_entity,
594 intermediates: Default::default(),
595 used: 0,
596 }
597 }
598
599 pub(crate) fn push(&mut self, cert: Cert<'a>) -> Result<(), ControlFlow<Error, Error>> {
600 if self.used >= MAX_SUB_CA_COUNT {
601 return Err(Error::MaximumPathDepthExceeded.into());
602 }
603
604 self.intermediates[self.used] = Some(cert);
605 self.used += 1;
606 Ok(())
607 }
608
609 fn pop(&mut self) {
610 debug_assert!(self.used > 0);
611 if self.used == 0 {
612 return;
613 }
614
615 self.used -= 1;
616 self.intermediates[self.used] = None;
617 }
618
619 pub(crate) fn node(&self) -> PathNode<'_> {
620 PathNode {
621 path: self,
622 index: self.used,
623 cert: self.head(),
624 }
625 }
626
627 /// Current head of the path.
628 pub(crate) fn head(&self) -> &Cert<'a> {
629 self.get(self.used)
630 }
631
632 /// Get the certificate at index `idx` in the path.
633 ///
634 // `idx` must be in the range `0..=self.used`; `idx` 0 thus yields the `end_entity`,
635 // while subsequent indexes yield the intermediate at `self.intermediates[idx - 1]`.
636 fn get(&self, idx: usize) -> &Cert<'a> {
637 match idx {
638 0 => self.end_entity,
639 _ => self.intermediates[idx - 1].as_ref().unwrap(),
640 }
641 }
642}
643
644const MAX_SUB_CA_COUNT: usize = 6;
645
646pub(crate) struct PathNode<'a> {
647 /// The path we're iterating.
648 path: &'a PartialPath<'a>,
649 /// The index of the current node in the path (input for `path.get()`).
650 index: usize,
651 /// The [`Cert`] at `index`.
652 pub(crate) cert: &'a Cert<'a>,
653}
654
655impl<'a> PathNode<'a> {
656 pub(crate) fn iter(&self) -> PathIter<'a> {
657 PathIter {
658 path: self.path,
659 next: Some(self.index),
660 }
661 }
662
663 pub(crate) fn role(&self) -> Role {
664 match self.index {
665 0 => Role::EndEntity,
666 _ => Role::Issuer,
667 }
668 }
669}
670
671pub(crate) struct PathIter<'a> {
672 path: &'a PartialPath<'a>,
673 next: Option<usize>,
674}
675
676impl<'a> Iterator for PathIter<'a> {
677 type Item = PathNode<'a>;
678
679 fn next(&mut self) -> Option<Self::Item> {
680 let next: usize = self.next?;
681 self.next = match next {
682 0 => None,
683 _ => Some(next - 1),
684 };
685
686 Some(PathNode {
687 path: self.path,
688 index: next,
689 cert: self.path.get(idx:next),
690 })
691 }
692}
693
694#[derive(Clone, Copy, PartialEq)]
695pub(crate) enum Role {
696 Issuer,
697 EndEntity,
698}
699
700#[cfg(all(test, feature = "alloc", any(feature = "ring", feature = "aws_lc_rs")))]
701mod tests {
702 use super::*;
703 use crate::test_utils::{issuer_params, make_end_entity, make_issuer};
704 use crate::trust_anchor::anchor_from_trusted_cert;
705
706 #[test]
707 fn eku_key_purpose_id() {
708 assert!(ExtendedKeyUsage::RequiredIfPresent(EKU_SERVER_AUTH)
709 .key_purpose_id_equals(EKU_SERVER_AUTH.oid_value))
710 }
711
712 #[cfg(feature = "alloc")]
713 enum ChainTrustAnchor {
714 NotInChain,
715 InChain,
716 }
717
718 fn build_degenerate_chain(
719 intermediate_count: usize,
720 trust_anchor: ChainTrustAnchor,
721 ) -> ControlFlow<Error, Error> {
722 let ca_cert = make_issuer("Bogus Subject");
723 let ca_cert_der = CertificateDer::from(ca_cert.serialize_der().unwrap());
724
725 let mut intermediates = Vec::with_capacity(intermediate_count + 1);
726 if let ChainTrustAnchor::InChain = trust_anchor {
727 intermediates.push(CertificateDer::from(ca_cert_der.to_vec()));
728 }
729
730 let mut issuer = ca_cert;
731 for _ in 0..intermediate_count {
732 let intermediate = make_issuer("Bogus Subject");
733 let intermediate_der = intermediate.serialize_der_with_signer(&issuer).unwrap();
734 intermediates.push(CertificateDer::from(intermediate_der));
735 issuer = intermediate;
736 }
737
738 let trust_anchor = match trust_anchor {
739 ChainTrustAnchor::InChain => {
740 let unused_anchor = make_issuer("Bogus Trust Anchor");
741 CertificateDer::from(unused_anchor.serialize_der().unwrap())
742 }
743 ChainTrustAnchor::NotInChain => ca_cert_der,
744 };
745
746 let ee_der = make_end_entity(&issuer);
747 let ee_cert = EndEntityCert::try_from(&ee_der).unwrap();
748 verify_chain(
749 &[anchor_from_trusted_cert(&trust_anchor).unwrap()],
750 &intermediates,
751 &ee_cert,
752 None,
753 None,
754 )
755 .map(|_| ())
756 .unwrap_err()
757 }
758
759 #[test]
760 fn test_too_many_signatures() {
761 assert!(matches!(
762 build_degenerate_chain(5, ChainTrustAnchor::NotInChain),
763 ControlFlow::Break(Error::MaximumSignatureChecksExceeded)
764 ));
765 }
766
767 #[test]
768 fn test_too_many_path_calls() {
769 assert!(matches!(
770 dbg!(build_degenerate_chain(10, ChainTrustAnchor::InChain)),
771 ControlFlow::Break(Error::MaximumPathBuildCallsExceeded)
772 ));
773 }
774
775 fn build_linear_chain(chain_length: usize) -> Result<(), ControlFlow<Error, Error>> {
776 let ca_cert = make_issuer(format!("Bogus Subject {chain_length}"));
777 let ca_cert_der = CertificateDer::from(ca_cert.serialize_der().unwrap());
778 let anchor = anchor_from_trusted_cert(&ca_cert_der).unwrap();
779 let anchors = &[anchor.clone()];
780
781 let mut intermediates = Vec::with_capacity(chain_length);
782 let mut issuer = ca_cert;
783 for i in 0..chain_length {
784 let intermediate = make_issuer(format!("Bogus Subject {i}"));
785 let intermediate_der = intermediate.serialize_der_with_signer(&issuer).unwrap();
786 intermediates.push(CertificateDer::from(intermediate_der));
787 issuer = intermediate;
788 }
789
790 let ee_der = make_end_entity(&issuer);
791 let ee_cert = EndEntityCert::try_from(&ee_der).unwrap();
792
793 let expected_chain = |path: &VerifiedPath<'_>| {
794 assert_eq!(path.anchor().subject, anchor.subject);
795 assert!(public_values_eq(path.end_entity().subject, ee_cert.subject));
796 assert_eq!(path.intermediate_certificates().count(), chain_length);
797
798 let intermediate_certs = intermediates
799 .iter()
800 .map(|der| Cert::from_der(untrusted::Input::from(der.as_ref())).unwrap())
801 .collect::<Vec<_>>();
802
803 for (cert, expected) in path
804 .intermediate_certificates()
805 .rev()
806 .zip(intermediate_certs.iter())
807 {
808 assert!(public_values_eq(cert.subject, expected.subject));
809 assert_eq!(cert.der(), expected.der());
810 }
811
812 for (cert, expected) in path
813 .intermediate_certificates()
814 .zip(intermediate_certs.iter().rev())
815 {
816 assert!(public_values_eq(cert.subject, expected.subject));
817 assert_eq!(cert.der(), expected.der());
818 }
819
820 Ok(())
821 };
822
823 verify_chain(
824 anchors,
825 &intermediates,
826 &ee_cert,
827 Some(&expected_chain),
828 None,
829 )
830 .map(|_| ())
831 }
832
833 #[test]
834 fn longest_allowed_path() {
835 assert!(build_linear_chain(1).is_ok());
836 assert!(build_linear_chain(2).is_ok());
837 assert!(build_linear_chain(3).is_ok());
838 assert!(build_linear_chain(4).is_ok());
839 assert!(build_linear_chain(5).is_ok());
840 assert!(build_linear_chain(6).is_ok());
841 }
842
843 #[test]
844 fn path_too_long() {
845 assert!(matches!(
846 build_linear_chain(7),
847 Err(ControlFlow::Continue(Error::MaximumPathDepthExceeded))
848 ));
849 }
850
851 #[test]
852 fn name_constraint_budget() {
853 // Issue a trust anchor that imposes name constraints. The constraint should match
854 // the end entity certificate SAN.
855 let mut ca_cert_params = issuer_params("Constrained Root");
856 ca_cert_params.name_constraints = Some(rcgen::NameConstraints {
857 permitted_subtrees: vec![rcgen::GeneralSubtree::DnsName(".com".into())],
858 excluded_subtrees: vec![],
859 });
860 let ca_cert = rcgen::Certificate::from_params(ca_cert_params).unwrap();
861 let ca_cert_der = CertificateDer::from(ca_cert.serialize_der().unwrap());
862 let anchors = &[anchor_from_trusted_cert(&ca_cert_der).unwrap()];
863
864 // Create a series of intermediate issuers. We'll only use one in the actual built path,
865 // helping demonstrate that the name constraint budget is not expended checking certificates
866 // that are not part of the path we compute.
867 const NUM_INTERMEDIATES: usize = 5;
868 let mut intermediates = Vec::with_capacity(NUM_INTERMEDIATES);
869 for i in 0..NUM_INTERMEDIATES {
870 intermediates.push(make_issuer(format!("Intermediate {i}")));
871 }
872
873 // Each intermediate should be issued by the trust anchor.
874 let mut intermediates_der = Vec::with_capacity(NUM_INTERMEDIATES);
875 for intermediate in &intermediates {
876 intermediates_der.push(CertificateDer::from(
877 intermediate.serialize_der_with_signer(&ca_cert).unwrap(),
878 ));
879 }
880
881 // Create an end-entity cert that is issued by the last of the intermediates.
882 let ee_der = make_end_entity(intermediates.last().unwrap());
883 let ee_cert = EndEntityCert::try_from(&ee_der).unwrap();
884
885 // We use a custom budget to make it easier to write a test, otherwise it is tricky to
886 // stuff enough names/constraints into the potential chains while staying within the path
887 // depth limit and the build chain call limit.
888 let passing_budget = Budget {
889 // One comparison against the intermediate's distinguished name.
890 // One comparison against the EE's distinguished name.
891 // One comparison against the EE's SAN.
892 // = 3 total comparisons.
893 name_constraint_comparisons: 3,
894 ..Budget::default()
895 };
896
897 // Validation should succeed with the name constraint comparison budget allocated above.
898 // This shows that we're not consuming budget on unused intermediates: we didn't budget
899 // enough comparisons for that to pass the overall chain building.
900 let path = verify_chain(
901 anchors,
902 &intermediates_der,
903 &ee_cert,
904 None,
905 Some(passing_budget),
906 )
907 .unwrap();
908 assert_eq!(path.anchor().subject, anchors.first().unwrap().subject);
909
910 let failing_budget = Budget {
911 // See passing_budget: 2 comparisons is not sufficient.
912 name_constraint_comparisons: 2,
913 ..Budget::default()
914 };
915 // Validation should fail when the budget is smaller than the number of comparisons performed
916 // on the validated path. This demonstrates we properly fail path building when too many
917 // name constraint comparisons occur.
918 let result = verify_chain(
919 anchors,
920 &intermediates_der,
921 &ee_cert,
922 None,
923 Some(failing_budget),
924 );
925
926 assert!(matches!(
927 result,
928 Err(ControlFlow::Break(
929 Error::MaximumNameConstraintComparisonsExceeded
930 ))
931 ));
932 }
933
934 #[test]
935 fn test_reject_candidate_path() {
936 /*
937 This test builds a PKI like the following diagram depicts. We first verify
938 that we can build a path EE -> B -> A -> TA. Next we supply a custom path verification
939 function that rejects the B->A path, and verify that we build a path EE -> B -> C -> TA.
940
941 ┌───────────┐
942 │ │
943 │ TA │
944 │ │
945 └───┬───┬───┘
946 │ │
947 │ │
948 ┌────────┐◄┘ └──►┌────────┐
949 │ │ │ │
950 │ A │ │ C │
951 │ │ │ │
952 └────┬───┘ └───┬────┘
953 │ │
954 │ │
955 │ ┌─────────┐ │
956 └──►│ │◄──┘
957 │ B │
958 │ │
959 └────┬────┘
960
961
962
963 ┌────▼────┐
964 │ │
965 │ EE │
966 │ │
967 └─────────┘
968 */
969
970 // Create a trust anchor, and use it to issue two distinct intermediate certificates, each
971 // with a unique subject and keypair.
972 let trust_anchor = make_issuer("Trust Anchor");
973 let trust_anchor_der = CertificateDer::from(trust_anchor.serialize_der().unwrap());
974 let trust_anchor_cert =
975 Cert::from_der(untrusted::Input::from(trust_anchor_der.as_ref())).unwrap();
976 let trust_anchors = &[anchor_from_trusted_cert(&trust_anchor_der).unwrap()];
977
978 let intermediate_a = make_issuer("Intermediate A");
979 let intermediate_a_der = CertificateDer::from(
980 intermediate_a
981 .serialize_der_with_signer(&trust_anchor)
982 .unwrap(),
983 );
984 let intermediate_a_cert =
985 Cert::from_der(untrusted::Input::from(intermediate_a_der.as_ref())).unwrap();
986
987 let intermediate_c = make_issuer("Intermediate C");
988 let intermediate_c_der = CertificateDer::from(
989 intermediate_c
990 .serialize_der_with_signer(&trust_anchor)
991 .unwrap(),
992 );
993 let intermediate_c_cert =
994 Cert::from_der(untrusted::Input::from(intermediate_c_der.as_ref())).unwrap();
995
996 // Next, create an intermediate that is issued by both of the intermediates above.
997 // Both should share the same subject, and key pair, but will differ in the issuer.
998 let intermediate_b_key = rcgen::KeyPair::generate(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap();
999 let mut intermediate_b_params = issuer_params("Intermediate");
1000 intermediate_b_params.key_pair = Some(intermediate_b_key);
1001 let intermediate_b = rcgen::Certificate::from_params(intermediate_b_params).unwrap();
1002
1003 let intermediate_b_a_der = CertificateDer::from(
1004 intermediate_b
1005 .serialize_der_with_signer(&intermediate_a)
1006 .unwrap(),
1007 );
1008 let intermediate_b_c_der = CertificateDer::from(
1009 intermediate_b
1010 .serialize_der_with_signer(&intermediate_c)
1011 .unwrap(),
1012 );
1013
1014 let intermediates = &[
1015 intermediate_a_der.clone(),
1016 intermediate_c_der.clone(),
1017 intermediate_b_a_der.clone(),
1018 intermediate_b_c_der.clone(),
1019 ];
1020
1021 // Create an end entity certificate signed by the keypair of the intermediates created above.
1022 let ee = make_end_entity(&intermediate_b);
1023 let ee_cert = &EndEntityCert::try_from(&ee).unwrap();
1024
1025 // We should be able to create a valid path from EE to trust anchor.
1026 let path = verify_chain(trust_anchors, intermediates, ee_cert, None, None).unwrap();
1027 let path_intermediates = path.intermediate_certificates().collect::<Vec<_>>();
1028
1029 // We expect that without applying any additional constraints, that the path will be
1030 // EE -> intermediate_b_a -> intermediate_a -> trust_anchor.
1031 assert_eq!(path_intermediates.len(), 2);
1032 assert_eq!(
1033 path_intermediates[0].issuer(),
1034 intermediate_a_cert.subject()
1035 );
1036 assert_eq!(path_intermediates[1].issuer(), trust_anchor_cert.subject());
1037
1038 // Now, we'll create a function that will reject the intermediate_b_a path.
1039 let expected_chain = |path: &VerifiedPath<'_>| {
1040 for intermediate in path.intermediate_certificates() {
1041 // Reject any intermediates issued by intermediate A.
1042 if intermediate.issuer() == intermediate_a_cert.subject() {
1043 return Err(Error::UnknownIssuer);
1044 }
1045 }
1046
1047 Ok(())
1048 };
1049
1050 // We should still be able to build a valid path.
1051 let path = verify_chain(
1052 trust_anchors,
1053 intermediates,
1054 ee_cert,
1055 Some(&expected_chain),
1056 None,
1057 )
1058 .unwrap();
1059 let path_intermediates = path.intermediate_certificates().collect::<Vec<_>>();
1060
1061 // We expect that the path will now be
1062 // EE -> intermediate_b_c -> intermediate_c -> trust_anchor.
1063 assert_eq!(path_intermediates.len(), 2);
1064 assert_eq!(
1065 path_intermediates[0].issuer(),
1066 intermediate_c_cert.subject()
1067 );
1068 assert_eq!(path_intermediates[1].issuer(), trust_anchor_cert.subject());
1069 }
1070
1071 fn verify_chain<'a>(
1072 trust_anchors: &'a [TrustAnchor<'a>],
1073 intermediate_certs: &'a [CertificateDer<'a>],
1074 ee_cert: &'a EndEntityCert<'a>,
1075 verify_path: Option<&dyn Fn(&VerifiedPath<'_>) -> Result<(), Error>>,
1076 budget: Option<Budget>,
1077 ) -> Result<VerifiedPath<'a>, ControlFlow<Error, Error>> {
1078 use core::time::Duration;
1079
1080 let time = UnixTime::since_unix_epoch(Duration::from_secs(0x1fed_f00d));
1081 let mut path = PartialPath::new(ee_cert);
1082 let opts = ChainOptions {
1083 eku: KeyUsage::server_auth(),
1084 supported_sig_algs: crate::ALL_VERIFICATION_ALGS,
1085 trust_anchors,
1086 intermediate_certs,
1087 revocation: None,
1088 };
1089
1090 match opts.build_chain_inner(
1091 &mut path,
1092 time,
1093 verify_path,
1094 0,
1095 &mut budget.unwrap_or_default(),
1096 ) {
1097 Ok(anchor) => Ok(VerifiedPath::new(ee_cert, anchor, path)),
1098 Err(err) => Err(err),
1099 }
1100 }
1101}
1102

Provided by KDAB

Privacy Policy
Learn Rust with the experts
Find out more