Skip to main content

structured_asic/
evidence.rs

1//! RFC4998 execution over the actual original `ASiC` member group.
2
3use crate::{
4    content::Content,
5    error::Error as ArchiveError,
6    zip::{Archive, MemberId, ReadFailure},
7};
8use structured_asn1::{
9    Reader,
10    memory::{Execution, OwnedVec, Work},
11    range::RangeSource,
12};
13use structured_cms::{
14    digest::{DigestBatch, DigestMemo, PassResult},
15    evidence::{
16        AsyncObjects, EvidenceRecord, Membership,
17        verify::{Authorities, Decision, Historical, Report, VerificationError},
18    },
19    provider::SignatureProvider,
20    stream::StreamError,
21    timestamp::{ProfileFailure, ProfileInput, TokenExtension},
22};
23use structured_pkix::{
24    algorithm::digest::AsnDigest,
25    temporal::{Instant, LeapTable},
26};
27
28mod manifest;
29pub use manifest::{ManifestChecked, ManifestError, ManifestInputError};
30
31enum PlanFailure<E> {
32    Archive(ArchiveError<E>),
33    Plan(structured_cms::evidence::verify::PlanError),
34}
35
36/// Original object occurrences, preserving multiplicity and supplied order.
37/// The vector is moved; no member name, decoded bytes or account is cloned.
38pub struct Group<'v, 's, S: RangeSource> {
39    archive: &'v Archive<'s, S>,
40    members: OwnedVec<MemberId<S::Document, S::Generation>>,
41}
42impl<'v, 's, S: RangeSource> Group<'v, 's, S> {
43    /// Admits every original member and its decoder before provider states can
44    /// start. Duplicate occurrences remain duplicates for exact ERS membership.
45    pub fn new<W: Work>(
46        archive: &'v Archive<'s, S>,
47        members: OwnedVec<MemberId<S::Document, S::Generation>>,
48        work: &W,
49    ) -> crate::zip::Acquisition<Self, S> {
50        for id in &members {
51            let member = archive
52                .original(*id, work)
53                .map_err(|cause| ReadFailure::Processing { member: *id, cause })?;
54            member.source(work)?;
55        }
56        Ok(Self { archive, members })
57    }
58    /// Exact original objects. Traversal during verification is separately paid.
59    #[must_use]
60    pub fn members(&self) -> &[MemberId<S::Document, S::Generation>] {
61        &self.members
62    }
63}
64
65struct Objects<'g, 'v, 's, S: RangeSource>(&'g Group<'v, 's, S>);
66impl<'p, P: AsnDigest<'p>, S: RangeSource> AsyncObjects<'p, P> for Objects<'_, '_, '_, S> {
67    type Error = ReadFailure<S::Error, S::Document, S::Generation>;
68    fn len(&self) -> usize {
69        self.0.members.len()
70    }
71    async fn run<W: Work>(
72        &mut self,
73        index: usize,
74        batch: DigestBatch<'p, P>,
75        work: &W,
76    ) -> PassResult<DigestMemo<'p, P>, Self::Error, P::Error> {
77        let id = self.0.members[index];
78        let member =
79            self.0.archive.original(id, work).map_err(|cause| {
80                StreamError::Source(ReadFailure::Processing { member: id, cause })
81            })?;
82        let mut source = member.source(work).map_err(StreamError::Source)?;
83        batch.run_async(&mut source, work).await
84    }
85}
86
87/// Complete original association and all common ERS timestamp/history answers.
88/// This alone does not establish a manifest's declared reference digests.
89pub struct Checked<'a, 'd, 'v, 's, S: RangeSource> {
90    evidence: MemberId<S::Document, S::Generation>,
91    objects: Group<'v, 's, S>,
92    report: Report<'a, 'd>,
93}
94impl<'a, 'd, 'v, 's, S: RangeSource> Checked<'a, 'd, 'v, 's, S> {
95    /// Original evidence-record member, independent of verification outcome.
96    #[must_use]
97    pub fn evidence(&self) -> MemberId<S::Document, S::Generation> {
98        self.evidence
99    }
100    /// Actual group consumed by the common reduced-tree computation.
101    #[must_use]
102    pub fn objects(&self) -> &Group<'v, 's, S> {
103        &self.objects
104    }
105    /// Every actual original timestamp and all chronology/history findings.
106    #[must_use]
107    pub fn report(&self) -> &Report<'a, 'd> {
108        &self.report
109    }
110}
111
112/// Archive/source refusal is separate from the common ERS execution failure.
113#[derive(Debug)]
114pub enum Error<SE, PE, RE, AE, HE, FE, D, G> {
115    /// Original view or source/current execution did not admit this association.
116    Archive(ArchiveError<SE>),
117    /// Common typed preflight, object, provider, selector or profile cause.
118    Check(VerificationError<ReadFailure<SE, D, G>, PE, RE, AE, HE, FE>),
119}
120
121impl<'v, 's, S: RangeSource> Content<'v, 's, S> {
122    #[expect(
123        clippy::result_large_err,
124        reason = "retained source errors must survive allocation refusal without allocating an error box"
125    )]
126    fn evidence_plan<'a>(
127        &'a self,
128        record: &'a EvidenceRecord<'a>,
129        parent: &Reader<'a>,
130        archive: &Archive<'_, S>,
131    ) -> Result<structured_cms::evidence::verify::InputPlan<'a, 'a>, PlanFailure<S::Error>> {
132        let work = parent.execution();
133        let original = record.original().element();
134        self.require_original(original.as_bytes(), original.span().start, parent, &work)
135            .map_err(PlanFailure::Archive)?;
136        if !core::ptr::eq(self.member().archive(), archive)
137            || original.as_bytes().len() != self.bytes().len()
138            || original.span().start != 0
139        {
140            return Err(PlanFailure::Archive(ArchiveError::Foreign {
141                range: self.enclosure(),
142            }));
143        }
144        record.input_plan(parent).map_err(PlanFailure::Plan)
145    }
146    /// Checks all original record participants, both renewal relations and
147    /// chronology with the ONE shared ERS kernel. The original object group is
148    /// preflighted before states and streamed once for all chain algorithms.
149    #[expect(
150        clippy::too_many_arguments,
151        reason = "source-bound ERS keeps common provider, selected authorities, UTC and three scoped callbacks explicit"
152    )]
153    #[expect(
154        clippy::result_large_err,
155        reason = "preserve complete original diagnostic maps without allocating on a resource failure"
156    )]
157    pub async fn check_evidence<'a, P, PE, A, RE, HE, FE>(
158        &'a self,
159        record: &'a EvidenceRecord<'a>,
160        parent: &Reader<'a>,
161        provider: &'a P,
162        authorities: &'a A,
163        utc: &LeapTable<'a>,
164        reference: Instant,
165        objects: Group<'v, 's, S>,
166        membership: Membership,
167        recognizes: impl FnMut(usize, &TokenExtension<'_>, &Execution<'_>) -> Result<bool, RE>,
168        historical: impl FnMut(Historical<'_, 'a>, &Execution<'_>) -> Result<Decision, HE>,
169        profile: impl for<'view, 'scope> FnMut(
170            usize,
171            ProfileInput<'view, 'a, 'a, 'scope>,
172        ) -> Result<(), ProfileFailure<FE>>,
173    ) -> Result<
174        Checked<'a, 'a, 'v, 's, S>,
175        Error<S::Error, PE, RE, A::Error, HE, FE, S::Document, S::Generation>,
176    >
177    where
178        P: for<'scope> SignatureProvider<'scope, Error = PE>,
179        A: Authorities<'a>,
180    {
181        let work = parent.execution();
182        let plan = self
183            .evidence_plan(record, parent, objects.archive)
184            .map_err(|cause| match cause {
185                PlanFailure::Archive(cause) => Error::Archive(cause),
186                PlanFailure::Plan(cause) => Error::Check(VerificationError::Plan(cause)),
187            })?;
188        let report = plan
189            .verify_async_with_profile(
190                provider,
191                parent,
192                authorities,
193                utc,
194                reference,
195                Objects(&objects),
196                membership,
197                recognizes,
198                historical,
199                profile,
200            )
201            .await
202            .map_err(Error::Check)?;
203        self.member()
204            .archive()
205            .admit(&work, self.enclosure())
206            .map_err(Error::Archive)?;
207        Ok(Checked {
208            evidence: self.member().id(),
209            objects,
210            report,
211        })
212    }
213}