structured_cms/attribute_certificate/validation/usage/
status.rs1use super::super::{Profile, Revocation, charge};
3use crate::Result;
4use structured_asn1::memory::Work;
5use structured_pkix::crl::{Coverage, EvidenceSource};
6use structured_pkix::ocsp::{BoundEvidence, NonceBinding, UpdateWindow};
7use structured_pkix::revocation::{RevocationTarget, TargetKind};
8use structured_pkix::temporal::Reading;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum Status {
14 Good,
16 NoRevAvailable,
18 Missing,
20 Target,
22 Time,
24 Scheme,
26 Reasons,
28 Window(UpdateWindow),
30 Nonce(NonceBinding),
32 Ocsp(structured_pkix::ocsp::Status),
34}
35
36#[derive(Debug, Clone, Copy)]
39pub struct NoStatus;
40
41mod sealed {
42 pub trait Sealed {}
43}
44
45pub trait Evidence: sealed::Sealed {
50 fn check<W: Work>(&self, profile: &Profile<'_, '_>, now: Reading, work: &W) -> Result<Status>;
53}
54impl sealed::Sealed for NoStatus {}
55impl Evidence for NoStatus {
56 fn check<W: Work>(&self, profile: &Profile<'_, '_>, _: Reading, work: &W) -> Result<Status> {
57 charge(
58 &work.execution_for(profile.certificate().parser),
59 1,
60 profile.certificate().element().span().start,
61 )?;
62 Ok(if profile.revocation() == Revocation::NoRevAvailable {
63 Status::NoRevAvailable
64 } else {
65 Status::Missing
66 })
67 }
68}
69
70fn target<'a, T: RevocationTarget<'a>, W: Work>(
71 target: &T,
72 profile: &Profile<'_, '_>,
73 work: &W,
74) -> Result<bool> {
75 let left = target.execution_in(work);
76 let ac = profile.certificate();
77 let right = work.execution_for(ac.parser);
78 charge(&left, 1, target.span().start)?;
79 charge(&right, 1, ac.element().span().start)?;
80 let original = target.encoded();
81 let actual = ac.element().as_bytes();
82 if target.kind() != TargetKind::Attribute || original.len() != actual.len() {
83 return Ok(false);
84 }
85 if core::ptr::eq(original, actual) {
86 return Ok(true);
87 }
88 charge(&left, original.len() as u64, target.span().start)?;
89 charge(&right, actual.len() as u64, ac.element().span().start)?;
90 Ok(original == actual)
91}
92
93impl<'a, T: RevocationTarget<'a>> sealed::Sealed for Coverage<'_, 'a, T> {}
94impl<'a, T: RevocationTarget<'a>> Evidence for Coverage<'_, 'a, T> {
95 fn check<W: Work>(&self, profile: &Profile<'_, '_>, now: Reading, work: &W) -> Result<Status> {
96 if !target(self.target(), profile, work)? {
97 return Ok(Status::Target);
98 }
99 let execution = work.execution_for(profile.certificate().parser);
100 charge(&execution, 3, profile.certificate().element().span().start)?;
101 if profile.revocation() != Revocation::Required {
102 return Ok(Status::Scheme);
103 }
104 if self.evaluated_at() != now {
105 return Ok(Status::Time);
106 }
107 charge(
110 &execution,
111 self.sources().len() as u64,
112 profile.certificate().element().span().start,
113 )?;
114 for absence in self.sources() {
115 let admit = |source: structured_pkix::crl::Authenticated<'_, '_>| {
116 let signature = source.crl().signature_value();
117 charge(
118 &signature.execution_in(&execution),
119 1,
120 signature.span().start,
121 )
122 };
123 match absence.source() {
124 EvidenceSource::Complete(source) => admit(source)?,
125 EvidenceSource::Delta { base, delta } => {
126 admit(base)?;
127 admit(delta)?;
128 }
129 }
130 }
131 Ok(if self.reasons().is_complete() {
132 Status::Good
133 } else {
134 Status::Reasons
135 })
136 }
137}
138
139impl<'a, T: RevocationTarget<'a>> sealed::Sealed for BoundEvidence<'_, 'a, '_, '_, T> {}
140impl<'a, T: RevocationTarget<'a>> Evidence for BoundEvidence<'_, 'a, '_, '_, T> {
141 fn check<W: Work>(&self, profile: &Profile<'_, '_>, now: Reading, work: &W) -> Result<Status> {
142 if !target(self.target(), profile, work)? {
143 return Ok(Status::Target);
144 }
145 let evidence = self.evidence();
146 let signature = evidence.source().signed().response().signature_value();
147 let execution = signature.execution_in(work);
148 charge(&execution, 5, signature.span().start)?;
149 if profile.revocation() != Revocation::Required {
150 return Ok(Status::Scheme);
151 }
152 if self.evaluated_at() != now {
153 return Ok(Status::Time);
154 }
155 if evidence.window() != UpdateWindow::Within {
156 return Ok(Status::Window(evidence.window()));
157 }
158 if matches!(
159 evidence.nonce(),
160 NonceBinding::Missing | NonceBinding::Differs
161 ) {
162 return Ok(Status::Nonce(evidence.nonce()));
163 }
164 Ok(
165 if matches!(evidence.status(), structured_pkix::ocsp::Status::Good) {
166 Status::Good
167 } else {
168 Status::Ocsp(evidence.status())
169 },
170 )
171 }
172}