1use super::{
4 ArchiveError, AsnDigest, AsyncObjects, Authorities, Checked, Content, Decision, DigestBatch,
5 DigestMemo, EvidenceRecord, Execution, Group, Historical, Instant, LeapTable, Membership,
6 OwnedVec, PassResult, PlanFailure, ProfileFailure, ProfileInput, RangeSource, ReadFailure,
7 Reader, SignatureProvider, StreamError, TokenExtension, VerificationError, Work,
8};
9use crate::{
10 manifest::Slot,
11 references::{Catalogue, DigestReading, Unchecked},
12};
13use core::convert::Infallible;
14use structured_xades::{
15 binary::{self, OwnedBinaryXml},
16 certificate::DigestProvider,
17 digest,
18 xml::{Access, Xml},
19};
20
21#[derive(Debug)]
23pub enum ManifestInputError<IE, XE, N> {
24 Input(IE),
26 Xml(XE),
28 Resource {
30 node: N,
32 cause: structured_asn1::memory::MemoryError,
34 },
35 MissingAlgorithm,
37}
38
39type ObjectError<X, S, PE> = ManifestInputError<
40 ReadFailure<
41 <S as RangeSource>::Error,
42 <S as RangeSource>::Document,
43 <S as RangeSource>::Generation,
44 >,
45 binary::Error<
46 <X as Xml>::Error,
47 digest::Error<Infallible, PE>,
48 <X as Xml>::Id,
49 <X as Xml>::Document,
50 <X as Xml>::Generation,
51 >,
52 <X as Xml>::Id,
53>;
54
55#[derive(Debug)]
57pub enum ManifestError<AE, CE, XE> {
58 Archive(AE),
60 Check(CE),
62 Xml(XE),
64}
65
66struct Objects<'q, 'p, 'c, 'v, 's, X: Xml, S: RangeSource, W: Work, P: AsnDigest<'p>> {
67 catalogue: &'c Catalogue<'v, 's, X::Id, X::Document, X::Generation, S>,
68 group: &'q Group<'v, 's, S>,
69 xml: &'q X,
70 answers: &'q mut OwnedVec<DigestReading, W>,
71 prepared: &'q [Option<Prepared<'p, P, X>>],
72}
73type Prepared<'p, P, X> =
74 digest::Prepared<'p, P, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>;
75
76fn unchecked_answers<W: Work>(
77 count: usize,
78 work: &W,
79) -> Result<OwnedVec<DigestReading, W>, structured_asn1::memory::MemoryError> {
80 let mut answers = work.vector();
81 answers.try_reserve_exact(count)?;
82 for _ in 0..count {
83 answers.try_push(DigestReading::Unchecked(Unchecked::NotExecuted))?;
84 }
85 Ok(answers)
86}
87
88impl<'p, X, S, P, W0> AsyncObjects<'p, P> for Objects<'_, 'p, '_, '_, '_, X, S, W0, P>
89where
90 X: OwnedBinaryXml,
91 S: RangeSource,
92 W0: Work,
93 P: AsnDigest<'p> + DigestProvider<'p, X>,
94{
95 type Error = ObjectError<X, S, P::Error>;
96 fn len(&self) -> usize {
97 self.group.members.len()
98 }
99 async fn run<W: Work>(
100 &mut self,
101 index: usize,
102 batch: DigestBatch<'p, P>,
103 work: &W,
104 ) -> PassResult<DigestMemo<'p, P>, Self::Error, P::Error> {
105 let id = self.group.members[index];
106 let member = self.group.archive.original(id, work).map_err(|cause| {
107 StreamError::Source(ManifestInputError::Input(ReadFailure::Processing {
108 member: id,
109 cause,
110 }))
111 })?;
112 let mut source = member
113 .source(work)
114 .map_err(|cause| StreamError::Source(ManifestInputError::Input(cause)))?;
115 let memo = batch
116 .run_async(&mut source, work)
117 .await
118 .map_err(|cause| match cause {
119 StreamError::Source(cause) => StreamError::Source(ManifestInputError::Input(cause)),
120 StreamError::Callback(cause) => StreamError::Callback(cause),
121 StreamError::Processing(cause) => StreamError::Processing(cause),
122 })?;
123 let object = self.catalogue.reading().objects()[index];
124 let answer = match (object.method, object.value) {
125 (Slot::One(_), Slot::One(_)) => {
126 let actual = memo
129 .for_request(0, work)
130 .map_err(StreamError::Callback)?
131 .ok_or(StreamError::Source(ManifestInputError::MissingAlgorithm))?;
132 let prepared = self.prepared[index]
133 .as_ref()
134 .ok_or(StreamError::Source(ManifestInputError::MissingAlgorithm))?;
135 DigestReading::Checked(
136 prepared
137 .check_finished(self.xml, actual.finished(), work)
138 .map_err(|cause| StreamError::Source(ManifestInputError::Xml(cause)))?,
139 )
140 }
141 (Slot::One(_), _) => DigestReading::Unchecked(Unchecked::Value),
142 _ => DigestReading::Unchecked(Unchecked::Method),
143 };
144 work.charge_work(1).map_err(|cause| {
145 StreamError::Source(ManifestInputError::Resource {
146 node: object.node,
147 cause,
148 })
149 })?;
150 self.answers[index] = answer;
151 Ok(memo)
152 }
153}
154
155pub struct ManifestChecked<'a, 'c, 'v, 's, N, D, G, S: RangeSource> {
159 catalogue: &'c Catalogue<'v, 's, N, D, G, S>,
160 checked: Checked<'a, 'a, 'v, 's, S>,
161 answers: OwnedVec<DigestReading>,
162}
163impl<'a, 'c, 'v, 's, N: Copy, D: Copy + Eq, G: Copy + Eq, S: RangeSource>
164 ManifestChecked<'a, 'c, 'v, 's, N, D, G, S>
165{
166 #[must_use]
168 pub fn catalogue(&self) -> &'c Catalogue<'v, 's, N, D, G, S> {
169 self.catalogue
170 }
171 #[must_use]
173 pub fn evidence(&self) -> &Checked<'a, 'a, 'v, 's, S> {
174 &self.checked
175 }
176 #[must_use]
178 pub fn object_digests(&self) -> &[DigestReading] {
179 &self.answers
180 }
181}
182
183impl<'v, 's, S: RangeSource> Content<'v, 's, S> {
184 #[expect(
190 clippy::too_many_arguments,
191 reason = "the existing common ERS provider, authority, UTC and scoped profile/history inputs remain explicit"
192 )]
193 #[expect(
194 clippy::result_large_err,
195 reason = "retain the original XML and ERS source-map failures without requiring allocation to report refusal"
196 )]
197 pub async fn check_manifest_evidence<'a, 'c, X, P, PE, A, RE, HE, FE>(
198 &'a self,
199 record: &'a EvidenceRecord<'a>,
200 parent: &Reader<'a>,
201 provider: &'a P,
202 authorities: &'a A,
203 utc: &LeapTable<'a>,
204 reference: Instant,
205 catalogue: &'c Catalogue<'v, 's, X::Id, X::Document, X::Generation, S>,
206 xml: &X,
207 recognizes: impl FnMut(usize, &TokenExtension<'_>, &Execution<'_>) -> Result<bool, RE>,
208 historical: impl FnMut(Historical<'_, 'a>, &Execution<'_>) -> Result<Decision, HE>,
209 profile: impl for<'view, 'scope> FnMut(
210 usize,
211 ProfileInput<'view, 'a, 'a, 'scope>,
212 ) -> Result<(), ProfileFailure<FE>>,
213 ) -> Result<
214 Option<ManifestChecked<'a, 'c, 'v, 's, X::Id, X::Document, X::Generation, S>>,
215 ManifestError<
216 ArchiveError<S::Error>,
217 VerificationError<ObjectError<X, S, PE>, PE, RE, A::Error, HE, FE>,
218 structured_xades::xml::AccessError<X::Error, X::Id>,
219 >,
220 >
221 where
222 X: OwnedBinaryXml,
223 P: for<'scope> SignatureProvider<'scope, Error = PE>
224 + for<'scope> DigestProvider<'scope, X>,
225 A: Authorities<'a>,
226 {
227 let work = parent.execution();
228 let resource =
229 |cause| ManifestError::Archive(crate::error::resource(self.enclosure(), cause));
230 let plan = self
231 .evidence_plan(record, parent, catalogue.base().archive())
232 .map_err(|cause| match cause {
233 PlanFailure::Archive(cause) => ManifestError::Archive(cause),
234 PlanFailure::Plan(cause) => ManifestError::Check(VerificationError::Plan(cause)),
235 })?;
236 let access = Access::new(xml, catalogue.reading().snapshot(), &work);
237 let root = catalogue.reading().root();
238 access.inspect(root, |_| ()).map_err(ManifestError::Xml)?;
239 let Some(selected) = catalogue
240 .signature_member(&work)
241 .map_err(ManifestError::Archive)?
242 else {
243 return Ok(None);
244 };
245 if selected.id() != self.member().id() {
246 return Err(ManifestError::Archive(ArchiveError::Foreign {
247 range: self.enclosure(),
248 }));
249 }
250 let Some(group) = catalogue.group(&work).map_err(|cause| {
251 ManifestError::Check(VerificationError::Acquisition(StreamError::Source(
252 ManifestInputError::Input(cause),
253 )))
254 })?
255 else {
256 return Ok(None);
257 };
258 let mut answers = unchecked_answers(catalogue.objects().len(), &work).map_err(resource)?;
259 let mut prepared = work.vector();
263 prepared
264 .try_reserve_exact(catalogue.objects().len())
265 .map_err(resource)?;
266 for object in catalogue.reading().objects() {
267 let value = match (object.method, object.value) {
268 (Slot::One(method), Slot::One(value)) => Some(
269 digest::prepare_xml(
270 xml,
271 digest::Reference {
272 snapshot: catalogue.reading().snapshot(),
273 method,
274 value,
275 },
276 provider,
277 &work,
278 )
279 .map_err(|cause| {
280 ManifestError::Check(VerificationError::Acquisition(StreamError::Source(
281 ManifestInputError::Xml(cause),
282 )))
283 })?,
284 ),
285 _ => None,
286 };
287 prepared.try_push(value).map_err(resource)?;
288 }
289 let objects = Objects {
290 catalogue,
291 group: &group,
292 xml,
293 answers: &mut answers,
294 prepared: &prepared,
295 };
296 let report = plan
297 .verify_async_with_profile(
298 provider,
299 parent,
300 authorities,
301 utc,
302 reference,
303 objects,
304 Membership::Group,
305 recognizes,
306 historical,
307 profile,
308 )
309 .await
310 .map_err(ManifestError::Check)?;
311 self.member()
312 .archive()
313 .admit(&work, self.enclosure())
314 .map_err(ManifestError::Archive)?;
315 access.inspect(root, |_| ()).map_err(ManifestError::Xml)?;
316 Ok(Some(ManifestChecked {
317 catalogue,
318 checked: Checked {
319 evidence: self.member().id(),
320 objects: group,
321 report,
322 },
323 answers: answers.into_owned(),
324 }))
325 }
326}