1use super::{
6 Anchor, BuildError, BuildResult, Interval, Meter, Policy, Truncation, Verified, Verify, Watch,
7 WorkingKey, cost, sorting_rounds,
8};
9use crate::constraints::BasicConstraints;
10use crate::extension::{Extension, Recognizer};
11use crate::{Certificate, ErrorCode, Name, oid};
12use structured_asn1::Oid;
13use structured_asn1::memory::{Memory, MemoryError, OwnedVec, Work};
14
15mod processing;
16
17#[derive(Debug, Clone, Copy)]
21pub struct EffectiveAnchor<'a> {
22 anchor: Anchor<'a>,
23 key: WorkingKey<'a>,
24}
25
26impl<'a> EffectiveAnchor<'a> {
27 pub fn bind<E: Work>(
31 anchor: Anchor<'a>,
32 key: WorkingKey<'a>,
33 execution: &E,
34 ) -> crate::Result<Option<Self>> {
35 let left = anchor.key().as_bytes();
36 let right = key.key().as_bytes();
37 let left_work = anchor.key().execution_in(execution);
38 let right_work = key.key().execution_in(execution);
39 let left_error = |reason| {
40 crate::Error::resource(reason, "anchor.key", anchor.key().algorithm().offset())
41 };
42 let right_error =
43 |reason| crate::Error::resource(reason, "anchor.key", key.key().algorithm().offset());
44 left_work.charge_work(1).map_err(left_error)?;
48 right_work.charge_work(1).map_err(right_error)?;
49 if left.len() != right.len() {
50 return Ok(None);
51 }
52 if core::ptr::eq(left, right) {
53 return Ok(Some(Self { anchor, key }));
54 }
55 left_work
56 .charge_work(left.len() as u64)
57 .map_err(left_error)?;
58 right_work
59 .charge_work(right.len() as u64)
60 .map_err(right_error)?;
61 if left != right {
62 return Ok(None);
63 }
64 Ok(Some(Self { anchor, key }))
65 }
66
67 #[must_use]
69 pub fn anchor(self) -> Anchor<'a> {
70 self.anchor
71 }
72
73 #[must_use]
75 pub fn key(self) -> WorkingKey<'a> {
76 self.key
77 }
78
79 fn standing(anchor: Anchor<'a>) -> Self {
80 Self {
81 anchor,
82 key: WorkingKey::standing(anchor.key()),
83 }
84 }
85}
86
87#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89pub enum ConstraintSource {
90 Anchor,
92 Certificate(usize),
94}
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub enum NameConstraintFailure {
99 Excluded,
101 NotPermitted,
103 Unenforceable,
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109#[non_exhaustive]
110pub enum RejectionReason<'a> {
111 EmptyPath,
113 DuplicateCertificate {
115 earlier: usize,
117 },
118 AnchorIncluded,
120 Validity,
122 Issuer,
124 NotAuthority,
126 KeyCertSign,
128 EmptyAuthorityName,
130 PathLength {
132 source: ConstraintSource,
134 limit: u32,
136 },
137 Signature,
139 UnsupportedSignature,
141 CriticalExtension(Oid<'a>),
143 MalformedExtension {
145 extension: Oid<'a>,
147 code: ErrorCode,
149 field: &'static str,
151 offset: u64,
153 },
154 NameConstraintsOnNonAuthority,
156 NameConstraints {
158 source: ConstraintSource,
160 failure: NameConstraintFailure,
162 },
163 Policies,
165}
166
167#[derive(Debug, Clone, Copy, PartialEq, Eq)]
169pub struct Rejection<'a> {
170 pub at: Option<ConstraintSource>,
172 pub reason: RejectionReason<'a>,
174}
175
176#[derive(Debug)]
180pub struct ValidationFacts<'a> {
181 pub key: WorkingKey<'a>,
183 pub non_self_issued_intermediates: u32,
186 policies: OwnedVec<Oid<'a>>,
187}
188
189impl<'a> ValidationFacts<'a> {
190 #[must_use]
192 pub fn policies(&self) -> &[Oid<'a>] {
193 &self.policies
194 }
195
196 #[must_use]
198 pub fn into_policies(self) -> OwnedVec<Oid<'a>> {
199 self.policies
200 }
201}
202
203#[derive(Debug)]
205pub enum Validation<'a> {
206 Accepted(ValidationFacts<'a>),
208 Rejected(Rejection<'a>),
210 Incomplete(Truncation),
212}
213
214#[derive(Debug, Clone, Copy)]
216pub struct ValidationBounds {
217 pub depth: usize,
219 pub work: u64,
221 pub policies: usize,
223}
224
225impl Default for ValidationBounds {
226 fn default() -> Self {
227 let bounds = super::Bounds::default();
228 Self {
229 depth: bounds.depth,
230 work: bounds.work,
231 policies: bounds.policies,
232 }
233 }
234}
235
236pub struct ValidationContext<'a, 'p, V: Verify<E>, W: Watch, R: Recognizer<E>, E: Work = Memory> {
240 pub execution: &'p E,
242 pub interval: Interval,
244 pub policy: Policy<'a>,
246 pub bounds: ValidationBounds,
248 pub verifier: &'p V,
250 pub watch: &'p W,
252 pub recognizer: &'p R,
254}
255
256pub(super) struct Settings<'a, 'p, R: Recognizer<E>, E: Work = Memory> {
257 pub(super) execution: &'p E,
258 pub(super) interval: Interval,
259 pub(super) policy: Policy<'a>,
260 pub(super) policies: usize,
261 pub(super) recognizer: &'p R,
262}
263
264#[derive(Debug, Clone, Copy)]
266pub(super) struct CertificateFacts<'a> {
267 pub(super) self_issued: bool,
268 pub(super) basic: Option<BasicConstraints>,
269 pub(super) issuing: Option<RejectionReason<'a>>,
270 pub(super) rejected: Option<RejectionReason<'a>>,
271}
272
273pub(super) trait Driver<'a, R: Recognizer<E>, E: Work = Memory>: Meter {
274 type VerificationError;
275 fn verified(
276 &mut self,
277 certificate: &'a Certificate<'a>,
278 key: WorkingKey<'a>,
279 ) -> BuildResult<Option<Verified>, Self::VerificationError, R::Error>;
280 fn facts(
281 &mut self,
282 certificate: &'a Certificate<'a>,
283 settings: &Settings<'a, '_, R, E>,
284 ) -> BuildResult<Option<CertificateFacts<'a>>, Self::VerificationError, R::Error>;
285}
286
287#[derive(Clone, Copy)]
291pub(super) enum Chain<'a, 'p> {
292 Supplied(&'p [&'a Certificate<'a>]),
293 Constructed(&'p [&'a Certificate<'a>]),
294}
295
296enum Decoded<T> {
297 Value(Option<T>),
298 Rejected(RejectionReason<'static>),
299 Incomplete,
300}
301
302fn decode<T>(
303 meter: &mut impl Meter,
304 kind: Oid<'static>,
305 action: impl FnOnce() -> crate::Result<Option<T>>,
306) -> crate::Result<Decoded<T>> {
307 if !meter.spend(cost::EXTENSION)? {
308 return Ok(Decoded::Incomplete);
309 }
310 Ok(match decoded(kind, action())? {
311 Ok(value) => Decoded::Value(value),
312 Err(reason) => Decoded::Rejected(reason),
313 })
314}
315
316fn decoded<T>(
317 kind: Oid<'static>,
318 result: crate::Result<T>,
319) -> crate::Result<core::result::Result<T, RejectionReason<'static>>> {
320 match result {
321 Ok(value) => Ok(Ok(value)),
322 Err(error) if error.is_resource() => Err(error),
323 Err(error) => Ok(Err(RejectionReason::MalformedExtension {
324 extension: kind,
325 code: error.code(),
326 field: error.field(),
327 offset: error.offset(),
328 })),
329 }
330}
331
332pub(super) fn spend_names(
333 meter: &mut impl Meter,
334 left: &Name<'_>,
335 right: &Name<'_>,
336) -> crate::Result<bool> {
337 let parts = left.parts().len() as u64 + right.parts().len() as u64;
338 if !meter.spend(parts)? {
339 return Ok(false);
340 }
341 let bytes = left.as_bytes().len() as u64 + right.as_bytes().len() as u64;
342 let rounds = sorting_rounds(left).max(sorting_rounds(right));
343 let work = rounds
344 .checked_mul(bytes / cost::COMPARED)
345 .and_then(|work| work.checked_add(cost::NAME))
346 .ok_or_else(|| left.resource(MemoryError::WorkExceeded))?;
347 meter.spend(work)
348}
349
350fn critical_policy<E: Work>(
351 item: &Extension<'_>,
352 execution: &E,
353) -> Option<(Oid<'static>, crate::Result<bool>)> {
354 let kind = item.kind();
356 if kind == oid::CERTIFICATE_POLICIES {
357 Some((
358 oid::CERTIFICATE_POLICIES,
359 crate::policy::CertificatePolicies::read(item, execution)
363 .and_then(|policies| policies.understood(item.parser(), execution)),
364 ))
365 } else if kind == oid::POLICY_CONSTRAINTS {
366 Some((
367 oid::POLICY_CONSTRAINTS,
368 crate::constraints::PolicyConstraints::read(item, execution).map(|_| true),
369 ))
370 } else if kind == oid::POLICY_MAPPINGS {
371 Some((
372 oid::POLICY_MAPPINGS,
373 crate::policy::PolicyMappings::read(item, execution).map(|_| true),
374 ))
375 } else if kind == oid::INHIBIT_ANY_POLICY {
376 Some((
377 oid::INHIBIT_ANY_POLICY,
378 crate::constraints::InhibitAnyPolicy::read(item, execution).map(|_| true),
379 ))
380 } else {
381 None
382 }
383}
384
385pub(super) fn certificate_facts<'a, E, R: Recognizer<Q>, M: Meter, Q: Work>(
386 certificate: &'a Certificate<'a>,
387 settings: &Settings<'a, '_, R, Q>,
388 meter: &mut M,
389) -> BuildResult<Option<CertificateFacts<'a>>, E, R::Error> {
390 let execution = settings.execution;
391 let extensions = certificate.tbs().extensions();
392 if !meter.spend(cost::CHECK)? {
393 return Ok(None);
394 }
395 let mut facts = CertificateFacts {
396 self_issued: false,
397 basic: None,
398 issuing: Some(RejectionReason::NotAuthority),
399 rejected: None,
400 };
401 if !settings.interval.inside(certificate.tbs().validity()) {
402 facts.rejected = Some(RejectionReason::Validity);
403 return Ok(Some(facts));
404 }
405 if !spend_names(
406 meter,
407 certificate.tbs().subject(),
408 certificate.tbs().issuer(),
409 )? {
410 return Ok(None);
411 }
412 facts.self_issued = certificate.tbs().subject().matches(
413 certificate.tbs().issuer(),
414 &certificate.execution_in(execution),
415 )?;
416 match decode(meter, oid::BASIC_CONSTRAINTS, || {
417 extensions.basic_constraints(execution)
418 })? {
419 Decoded::Value(basic) => facts.basic = basic,
420 Decoded::Rejected(reason) => {
421 facts.rejected = Some(reason);
422 return Ok(Some(facts));
423 }
424 Decoded::Incomplete => return Ok(None),
425 }
426 if facts.basic.is_some_and(BasicConstraints::is_authority) {
427 facts.issuing = None;
428 if certificate.tbs().subject().is_empty() {
429 facts.rejected = Some(RejectionReason::EmptyAuthorityName);
430 return Ok(Some(facts));
431 }
432 match decode(meter, oid::KEY_USAGE, || extensions.key_usage(execution))? {
433 Decoded::Value(Some(usage)) if !usage.key_cert_sign() => {
434 facts.issuing = Some(RejectionReason::KeyCertSign);
435 }
436 Decoded::Rejected(reason) => facts.issuing = Some(reason),
437 Decoded::Incomplete => return Ok(None),
438 Decoded::Value(_) => {}
439 }
440 }
441 for demand in extensions.criticals(execution) {
442 let demand = demand?;
443 if !meter.spend(cost::EXTENSION)? {
444 return Ok(None);
445 }
446 if demand.kind() == oid::NAME_CONSTRAINTS || demand.kind() == oid::BASIC_CONSTRAINTS {
450 continue;
451 }
452 if demand.kind() == oid::KEY_USAGE
453 && facts.basic.is_some_and(BasicConstraints::is_authority)
454 {
455 if matches!(
456 facts.issuing,
457 Some(RejectionReason::MalformedExtension { .. })
458 ) {
459 facts.rejected = facts.issuing;
460 return Ok(Some(facts));
461 }
462 continue;
463 }
464 if let Some((kind, result)) = critical_policy(demand, execution) {
470 let reason = match decoded(kind, result)? {
471 Ok(true) => continue,
472 Ok(false) => RejectionReason::CriticalExtension(kind),
473 Err(reason) => reason,
474 };
475 facts.rejected = Some(reason);
476 return Ok(Some(facts));
477 }
478 if extensions.understands(demand, settings.recognizer, execution)? {
479 continue;
480 }
481 facts.rejected = Some(RejectionReason::CriticalExtension(demand.kind()));
482 return Ok(Some(facts));
483 }
484 Ok(Some(facts))
485}
486
487fn rejection(at: ConstraintSource, reason: RejectionReason<'_>) -> Validation<'_> {
488 Validation::Rejected(Rejection {
489 at: Some(at),
490 reason,
491 })
492}
493
494pub(super) fn process<'a, R: Recognizer<E>, D: Driver<'a, R, E>, E: Work>(
495 input: Chain<'a, '_>,
496 anchor: Anchor<'a>,
497 settings: &Settings<'a, '_, R, E>,
498 driver: &mut D,
499) -> BuildResult<Validation<'a>, D::VerificationError, R::Error> {
500 processing::process(input, &EffectiveAnchor::standing(anchor), settings, driver)
501}
502
503struct Supplied<'a, 'p, V: Verify<E>, W: Watch, R: Recognizer<E>, E: Work> {
504 context: &'p ValidationContext<'a, 'p, V, W, R, E>,
505 origin: &'a Certificate<'a>,
506 spent: u64,
507 halted: Option<Truncation>,
508}
509
510impl<V: Verify<E>, W: Watch, R: Recognizer<E>, E: Work> Supplied<'_, '_, V, W, R, E> {
511 fn admit(&mut self, work: u64) -> crate::Result<Option<Truncation>> {
512 if work > self.context.bounds.work - self.spent {
513 return Ok(Some(Truncation::Work));
514 }
515 if self.context.watch.out_of_time() {
516 return Ok(Some(Truncation::Time));
517 }
518 self.context
519 .execution
520 .charge_work(work)
521 .map_err(|reason| self.origin.tbs().subject().resource(reason))?;
522 self.spent += work;
523 Ok(None)
524 }
525}
526
527impl<V: Verify<E>, W: Watch, R: Recognizer<E>, E: Work> Meter for Supplied<'_, '_, V, W, R, E> {
528 fn spend(&mut self, work: u64) -> crate::Result<bool> {
529 if let Some(halted) = self.admit(work)? {
530 self.halted = Some(halted);
531 return Ok(false);
532 }
533 Ok(true)
534 }
535}
536
537impl<'a, V: Verify<E>, W: Watch, R: Recognizer<E>, E: Work> Driver<'a, R, E>
538 for Supplied<'a, '_, V, W, R, E>
539{
540 type VerificationError = V::Error;
541 fn verified(
542 &mut self,
543 certificate: &'a Certificate<'a>,
544 key: WorkingKey<'a>,
545 ) -> BuildResult<Option<Verified>, V::Error, R::Error> {
546 if !self.spend(cost::SIGNATURE)? {
547 return Ok(None);
548 }
549 self.context
550 .verifier
551 .verify(
552 &certificate.signature_algorithm(),
553 key,
554 certificate.signed_bytes(),
555 certificate.signature_value(),
556 self.context.execution,
557 )
558 .map(Some)
559 .map_err(BuildError::Verification)
560 }
561 fn facts(
562 &mut self,
563 certificate: &'a Certificate<'a>,
564 settings: &Settings<'a, '_, R, E>,
565 ) -> BuildResult<Option<CertificateFacts<'a>>, V::Error, R::Error> {
566 certificate_facts(certificate, settings, self)
567 }
568}
569
570pub fn validate<'a, V: Verify<E>, W: Watch, R: Recognizer<E>, E: Work>(
603 chain: &[&'a Certificate<'a>],
604 anchor: Anchor<'a>,
605 context: &ValidationContext<'a, '_, V, W, R, E>,
606) -> BuildResult<Validation<'a>, V::Error, R::Error> {
607 validate_effective(chain, EffectiveAnchor::standing(anchor), context)
608}
609
610pub fn validate_effective<'a, V: Verify<E>, W: Watch, R: Recognizer<E>, E: Work>(
616 chain: &[&'a Certificate<'a>],
617 effective: EffectiveAnchor<'a>,
618 context: &ValidationContext<'a, '_, V, W, R, E>,
619) -> BuildResult<Validation<'a>, V::Error, R::Error> {
620 let anchor = effective.anchor;
621 let Some(target) = chain.first() else {
622 return Ok(Validation::Rejected(Rejection {
623 at: None,
624 reason: RejectionReason::EmptyPath,
625 }));
626 };
627 if chain.len() > context.bounds.depth {
628 return Ok(Validation::Incomplete(Truncation::Depth));
629 }
630 let length = u32::try_from(chain.len()).map_err(|_| {
631 target
632 .tbs()
633 .subject()
634 .resource(MemoryError::CapacityOverflow)
635 })?;
636 length.checked_add(1).ok_or_else(|| {
637 target
638 .tbs()
639 .subject()
640 .resource(MemoryError::CapacityOverflow)
641 })?;
642 let mut driver = Supplied {
643 context,
644 origin: target,
645 spent: 0,
646 halted: None,
647 };
648 let mut seen = context.execution.map();
649 for (index, certificate) in chain.iter().enumerate() {
650 if let Some(halted) =
651 driver.admit(cost::ENTRY + certificate.as_bytes().len() as u64 / cost::HASHED)?
652 {
653 return Ok(Validation::Incomplete(halted));
654 }
655 if let Some(earlier) = seen
656 .try_insert(certificate.as_bytes(), index)
657 .map_err(|reason| certificate.tbs().subject().resource(reason))?
658 {
659 return Ok(rejection(
660 ConstraintSource::Certificate(index),
661 RejectionReason::DuplicateCertificate { earlier },
662 ));
663 }
664 if let Some(published) = anchor.certificate() {
665 let bytes = certificate.as_bytes().len().min(published.as_bytes().len()) as u64;
666 if let Some(halted) = driver.admit(bytes / cost::COMPARED)? {
667 return Ok(Validation::Incomplete(halted));
668 }
669 context
670 .execution
671 .charge_work(bytes)
672 .map_err(|reason| certificate.tbs().subject().resource(reason))?;
673 if super::same(certificate, published) {
674 return Ok(rejection(
675 ConstraintSource::Certificate(index),
676 RejectionReason::AnchorIncluded,
677 ));
678 }
679 }
680 }
681 drop(seen);
682 let settings = Settings {
683 execution: context.execution,
684 interval: context.interval,
685 policy: context.policy,
686 policies: context.bounds.policies,
687 recognizer: context.recognizer,
688 };
689 let outcome = processing::process(Chain::Supplied(chain), &effective, &settings, &mut driver)?;
690 if let Some(halted) = driver.halted {
691 return Ok(Validation::Incomplete(halted));
692 }
693 Ok(outcome)
694}
695
696#[cfg(test)]
697mod tests {
698 use super::*;
699 use crate::fixtures::{CertificateParts, oid as encoded_oid, printable, seq, set};
700 use crate::signature::SignatureValue;
701 use core::cell::Cell;
702 use structured_asn1::Parser;
703
704 struct Answer {
705 result: Verified,
706 calls: Cell<usize>,
707 }
708 impl Verify for Answer {
709 type Error = core::convert::Infallible;
710 fn verify(
711 &self,
712 _algorithm: &crate::AlgorithmIdentifier<'_>,
713 _key: WorkingKey<'_>,
714 _signed: &[u8],
715 _signature: SignatureValue<'_>,
716 _execution: &Memory,
717 ) -> core::result::Result<Verified, Self::Error> {
718 self.calls.set(self.calls.get() + 1);
719 Ok(self.result)
720 }
721 }
722
723 fn target() -> alloc::vec::Vec<u8> {
724 CertificateParts {
725 subject: seq(&[set(&[seq(&[
726 encoded_oid(oid::COMMON_NAME),
727 printable("target"),
728 ])])]),
729 ..CertificateParts::default()
730 }
731 .build()
732 }
733
734 fn root() -> alloc::vec::Vec<u8> {
735 let parts = CertificateParts::default();
736 CertificateParts {
737 subject: parts.issuer.clone(),
738 ..parts
739 }
740 .build()
741 }
742
743 #[test]
744 fn supplied_path_separates_success_no_and_unsupported() {
745 let root_der = root();
747 let leaf_der = target();
748 let parser = Parser::der(crate::fixtures::memory());
749 let root = Certificate::read(&parser, &root_der, 0).unwrap();
750 let leaf = Certificate::read(&parser, &leaf_der, 100).unwrap();
751 let anchor = Anchor::new(root.tbs().subject(), root.tbs().public_key());
752 for result in [Verified::Yes, Verified::No, Verified::Unsupported] {
753 let execution = crate::fixtures::memory();
754 let verifier = Answer {
755 result,
756 calls: Cell::new(0),
757 };
758 let context = ValidationContext {
759 execution: &execution,
760 interval: Interval::at(leaf.tbs().validity().not_before().seconds()),
761 policy: Policy::default(),
762 bounds: ValidationBounds::default(),
763 verifier: &verifier,
764 watch: &(),
765 recognizer: &(),
766 };
767 let outcome = crate::path::validate(&[&leaf], anchor, &context).unwrap();
768 match (result, outcome) {
769 (Verified::Yes, Validation::Accepted(facts)) => {
770 assert_eq!(
771 facts.key.key().as_bytes(),
772 leaf.tbs().public_key().as_bytes()
773 );
774 assert_eq!(facts.policies(), []);
775 }
776 (Verified::No, Validation::Rejected(rejection)) => {
777 assert_eq!(rejection.at, Some(ConstraintSource::Certificate(0)));
778 assert_eq!(rejection.reason, RejectionReason::Signature);
779 }
780 (Verified::Unsupported, Validation::Rejected(rejection)) => {
781 assert_eq!(rejection.at, Some(ConstraintSource::Certificate(0)));
782 assert_eq!(rejection.reason, RejectionReason::UnsupportedSignature);
783 }
784 other => panic!("unexpected public path result: {other:?}"),
785 }
786 assert_eq!(verifier.calls.get(), 1);
787 assert_eq!(execution.used(), 0);
788 }
789 }
790
791 #[test]
792 fn supplied_local_work_refusal_never_calls_the_provider() {
793 let root_der = root();
795 let leaf_der = target();
796 let parser = Parser::der(crate::fixtures::memory());
797 let root = Certificate::read(&parser, &root_der, 0).unwrap();
798 let leaf = Certificate::read(&parser, &leaf_der, 100).unwrap();
799 let execution = crate::fixtures::memory();
800 let verifier = Answer {
801 result: Verified::Yes,
802 calls: Cell::new(0),
803 };
804 let context = ValidationContext {
805 execution: &execution,
806 interval: Interval::at(leaf.tbs().validity().not_before().seconds()),
807 policy: Policy::default(),
808 bounds: ValidationBounds {
809 work: 0,
810 ..ValidationBounds::default()
811 },
812 verifier: &verifier,
813 watch: &(),
814 recognizer: &(),
815 };
816 assert!(matches!(
817 crate::path::validate(
818 &[&leaf],
819 Anchor::new(root.tbs().subject(), root.tbs().public_key()),
820 &context
821 )
822 .unwrap(),
823 Validation::Incomplete(Truncation::Work)
824 ));
825 assert_eq!(verifier.calls.get(), 0);
826 assert_eq!(execution.used(), 0);
827 assert_eq!(execution.work_used(), 0);
828 }
829}