Skip to main content

structured_cms/attribute_certificate/validation/usage/
status.rs

1//! Closed adapters for original, already evaluated common status evidence.
2use 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/// Status facts for this exact AC and evaluation label. This enum does not
11/// accept a caller-created Good assertion as input; Evidence is sealed.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum Status {
14    /// Complete common non-revocation evidence for the exact target and time.
15    Good,
16    /// The authenticated profile explicitly elects RFC5755 ยง6's never-revoke scheme.
17    NoRevAvailable,
18    /// No original evidence was supplied for a profile requiring status.
19    Missing,
20    /// Original evidence belongs to a different target.
21    Target,
22    /// Evidence was evaluated at another precise label.
23    Time,
24    /// Evidence contradicts the profile's written never-revoke scheme.
25    Scheme,
26    /// CRL absence coverage omits at least one required reason.
27    Reasons,
28    /// OCSP update window does not establish current status.
29    Window(UpdateWindow),
30    /// The actual request's nonce was not returned or differs.
31    Nonce(NonceBinding),
32    /// Original adverse OCSP status, retaining Unknown/Revoked/NotIssued.
33    Ocsp(structured_pkix::ocsp::Status),
34}
35
36/// No external status source. This only completes the never-revoke scheme when
37/// that extension was actually accepted in the authenticated original profile.
38#[derive(Debug, Clone, Copy)]
39pub struct NoStatus;
40
41mod sealed {
42    pub trait Sealed {}
43}
44
45/// Original status capability implemented only by existing shared evidence and
46/// `NoStatus`. It performs association/summary checks, never repeats a signature,
47/// `CertID` hash, CRL entry census or temporal evaluation. Caller-selected maximum
48/// age, unsolicited-nonce policy and configured responder trust remain external.
49pub trait Evidence: sealed::Sealed {
50    /// Check actual original target, exact label and established status facts
51    /// under the current operation and all sources' producing ceilings.
52    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        // Every contributing source is already scoped/time-qualified and
108        // conflict-free. A retained union is not credit for this query's scan.
109        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}