1mod checks;
4pub use checks::Checks;
5
6use crate::{
7 error::{Error as ArchiveError, resource},
8 uri::{self, Part},
9 zip::{Member, Names},
10};
11use structured_asn1::{memory::Work, range::RangeSource};
12use structured_xades::{
13 binary::{self, OwnedBinaryXml},
14 certificate::{Agreement, DigestProvider},
15 digest,
16 reference::{LexicalSource, VisitError},
17 scalar::{self, UriValue, UriXml},
18 xml::{Access, AccessError, Name, Xml},
19};
20
21pub struct Catalogue<'v, 's, N, D, G, S: RangeSource> {
26 base: Member<'v, 's, S>,
27 reading: crate::manifest::Reading<N, D, G>,
28 signatures: structured_asn1::memory::OwnedVec<Target<'v, 's, S>>,
29 objects: structured_asn1::memory::OwnedVec<Target<'v, 's, S>>,
30}
31
32#[derive(Debug)]
34pub enum ResolveError<E, N, SE> {
35 Archive(ArchiveError<SE>),
37 Reference(AccessError<VisitError<E, ArchiveError<SE>>, N>),
39 Storage {
41 node: N,
43 cause: structured_asn1::memory::MemoryError,
45 },
46}
47
48pub type ResolveResult<'v, 's, X, S> = Result<
50 Catalogue<'v, 's, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation, S>,
51 ResolveError<<X as Xml>::Error, <X as Xml>::Id, <S as RangeSource>::Error>,
52>;
53type MemberResult<'v, 's, S> = crate::error::SourceResult<Option<Member<'v, 's, S>>, S>;
54
55pub fn resolve_manifest<'v, 's, X: UriXml, S: RangeSource, W: Work>(
58 base: Member<'v, 's, S>,
59 reading: crate::manifest::Reading<X::Id, X::Document, X::Generation>,
60 xml: &X,
61 work: &W,
62) -> ResolveResult<'v, 's, X, S> {
63 let execution = base.session().query(work);
64 base.archive()
65 .admit(&execution, base.central_span())
66 .map_err(ResolveError::Archive)?;
67 let access = Access::new(xml, reading.snapshot(), &execution);
68 access.inspect(reading.root(), |_| ()).map_err(|cause| {
71 ResolveError::Reference(match cause {
72 AccessError::Xml(cause) => AccessError::Xml(VisitError::Processing(cause)),
73 AccessError::Changed { node } => AccessError::Changed { node },
74 AccessError::Resource { node, cause } => AccessError::Resource { node, cause },
75 })
76 })?;
77 let mut signatures = execution.vector();
78 let mut objects = execution.vector();
79 signatures
80 .try_reserve_exact(reading.signatures().len())
81 .map_err(|cause| ResolveError::Storage {
82 node: reading.root(),
83 cause,
84 })?;
85 objects
86 .try_reserve_exact(reading.objects().len())
87 .map_err(|cause| ResolveError::Storage {
88 node: reading.root(),
89 cause,
90 })?;
91 for node in reading.signatures() {
92 let target = resolve(&base, &access, *node).map_err(ResolveError::Reference)?;
93 signatures
94 .try_push(target)
95 .map_err(|cause| ResolveError::Storage { node: *node, cause })?;
96 }
97 for object in reading.objects() {
98 let target = resolve(&base, &access, object.node).map_err(ResolveError::Reference)?;
99 objects
100 .try_push(target)
101 .map_err(|cause| ResolveError::Storage {
102 node: object.node,
103 cause,
104 })?;
105 }
106 base.archive()
107 .admit(&execution, base.central_span())
108 .map_err(ResolveError::Archive)?;
109 Ok(Catalogue {
110 base,
111 reading,
112 signatures: signatures.into_owned(),
113 objects: objects.into_owned(),
114 })
115}
116
117impl<'v, 's, N: Copy, D: Copy + Eq, G: Copy + Eq, S: RangeSource> Catalogue<'v, 's, N, D, G, S> {
118 #[must_use]
120 pub fn base(&self) -> &Member<'v, 's, S> {
121 &self.base
122 }
123 #[must_use]
125 pub fn reading(&self) -> &crate::manifest::Reading<N, D, G> {
126 &self.reading
127 }
128 #[must_use]
130 pub fn signatures(&self) -> &[Target<'v, 's, S>] {
131 &self.signatures
132 }
133 #[must_use]
135 pub fn objects(&self) -> &[Target<'v, 's, S>] {
136 &self.objects
137 }
138
139 pub fn signature_member<W: Work>(&self, work: &W) -> MemberResult<'v, 's, S> {
143 self.base.archive().admit(work, self.base.central_span())?;
144 if self.signatures.len() != 1 {
145 return Ok(None);
146 }
147 unique(&self.signatures[0], work)
148 }
149 pub fn object_member<W: Work>(&self, ordinal: usize, work: &W) -> MemberResult<'v, 's, S> {
152 self.base.archive().admit(work, self.base.central_span())?;
153 let Some(target) = self.objects.get(ordinal) else {
154 return Ok(None);
155 };
156 unique(target, work)
157 }
158 pub fn group<W: Work>(
162 &self,
163 work: &W,
164 ) -> crate::zip::Acquisition<Option<crate::evidence::Group<'v, 's, S>>, S> {
165 let archive = self.base.archive();
166 let execution = self.base.session().query(work);
167 let fail = |cause| crate::zip::ReadFailure::Processing {
168 member: self.base.id(),
169 cause,
170 };
171 archive
172 .admit(&execution, self.base.central_span())
173 .map_err(fail)?;
174 if self.objects.is_empty() {
175 return Ok(None);
176 }
177 let mut members = execution.vector();
178 for target in &self.objects {
179 archive
180 .admit(&execution, self.base.central_span())
181 .map_err(fail)?;
182 let Some(member) = unique(target, &execution).map_err(fail)? else {
183 return Ok(None);
184 };
185 members
186 .try_push(member.id())
187 .map_err(|cause| fail(resource(self.base.central_span(), cause)))?;
188 }
189 crate::evidence::Group::new(archive, members.into_owned(), &execution).map(Some)
190 }
191}
192
193fn unique<'v, 's, S: RangeSource, W: Work>(
194 target: &Target<'v, 's, S>,
195 work: &W,
196) -> Result<Option<Member<'v, 's, S>>, ArchiveError<S::Error>> {
197 if let Target::Members {
198 names,
199 part: Part::Whole,
200 } = target
201 && names.len() == 1
202 {
203 names.member(0, work)
204 } else {
205 Ok(None)
206 }
207}
208
209#[derive(Debug, Clone, Copy, PartialEq, Eq)]
211pub enum Unchecked {
212 NotExecuted,
214 Input,
216 Method,
218 Value,
220}
221
222#[derive(Debug, Clone, Copy, PartialEq, Eq)]
224pub enum DigestReading {
225 Unchecked(Unchecked),
227 Checked(Agreement),
229}
230
231pub struct ObjectCheck<'v, 's, N, D, G, S: RangeSource> {
234 snapshot: structured_xades::xml::Snapshot<D, G>,
235 root: N,
236 object: crate::manifest::Object<N>,
237 target: Target<'v, 's, S>,
238 digest: DigestReading,
239}
240impl<'v, 's, N: Copy, D: Copy, G: Copy, S: RangeSource> ObjectCheck<'v, 's, N, D, G, S> {
241 #[must_use]
243 pub fn snapshot(&self) -> structured_xades::xml::Snapshot<D, G> {
244 self.snapshot
245 }
246 #[must_use]
248 pub fn root(&self) -> N {
249 self.root
250 }
251 #[must_use]
253 pub fn object(&self) -> crate::manifest::Object<N> {
254 self.object
255 }
256 #[must_use]
258 pub fn target(&self) -> &Target<'v, 's, S> {
259 &self.target
260 }
261 #[must_use]
263 pub fn digest(&self) -> DigestReading {
264 self.digest
265 }
266}
267
268#[derive(Debug)]
270pub enum CheckError<E, PE, N, D, G, SE, SD, SG> {
271 Reference(AccessError<VisitError<E, ArchiveError<SE>>, N>),
273 Input(crate::zip::ReadFailure<SE, SD, SG>),
275 Digest(ObjectDigestError<E, PE, N, D, G, SE, SD, SG>),
277}
278pub type ObjectDigestError<E, PE, N, D, G, SE, SD, SG> =
280 binary::Error<E, digest::Error<crate::zip::ReadFailure<SE, SD, SG>, PE>, N, D, G>;
281
282pub type CheckResult<'v, 's, X, S, PE> = Result<
284 Option<ObjectCheck<'v, 's, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation, S>>,
285 CheckError<
286 <X as Xml>::Error,
287 PE,
288 <X as Xml>::Id,
289 <X as Xml>::Document,
290 <X as Xml>::Generation,
291 <S as RangeSource>::Error,
292 <S as RangeSource>::Document,
293 <S as RangeSource>::Generation,
294 >,
295>;
296
297pub async fn check_object<'p, 'v, 's, X, S, P, W>(
306 base: &Member<'v, 's, S>,
307 reading: &crate::manifest::Reading<X::Id, X::Document, X::Generation>,
308 ordinal: usize,
309 xml: &X,
310 provider: &'p P,
311 work: &W,
312) -> CheckResult<'v, 's, X, S, P::Error>
313where
314 X: UriXml + OwnedBinaryXml,
315 S: RangeSource,
316 P: DigestProvider<'p, X>,
317 W: Work,
318{
319 let access = Access::new(xml, reading.snapshot(), work);
320 access.inspect(reading.root(), |_| ()).map_err(|cause| {
322 CheckError::Reference(match cause {
323 AccessError::Xml(cause) => AccessError::Xml(VisitError::Processing(cause)),
324 AccessError::Resource { node, cause } => AccessError::Resource { node, cause },
325 AccessError::Changed { node } => AccessError::Changed { node },
326 })
327 })?;
328 let Some(object) = reading.objects().get(ordinal).copied() else {
329 return Ok(None);
330 };
331 let target = resolve(base, &access, object.node).map_err(CheckError::Reference)?;
332 let answer = match (object.method, object.value) {
333 (crate::manifest::Slot::One(method), crate::manifest::Slot::One(value)) => {
334 if let Target::Members {
335 names,
336 part: Part::Whole,
337 } = &target
338 && names.len() == 1
339 {
340 let member = names
341 .member(0, work)
342 .map_err(|cause| {
343 CheckError::Reference(AccessError::Xml(VisitError::Visitor(cause)))
344 })?
345 .expect("the immutable exact candidate range contains one original member");
346 let mut source = member.source(work).map_err(CheckError::Input)?;
347 DigestReading::Checked(
348 digest::check_xml_async(
349 xml,
350 digest::Reference {
351 snapshot: reading.snapshot(),
352 method,
353 value,
354 },
355 &mut source,
356 provider,
357 work,
358 )
359 .await
360 .map_err(CheckError::Digest)?,
361 )
362 } else {
363 DigestReading::Unchecked(Unchecked::Input)
364 }
365 }
366 (crate::manifest::Slot::One(_), _) => DigestReading::Unchecked(Unchecked::Value),
367 _ => DigestReading::Unchecked(Unchecked::Method),
368 };
369 base.archive()
370 .admit(work, base.central_span())
371 .map_err(|cause| CheckError::Reference(AccessError::Xml(VisitError::Visitor(cause))))?;
372 Ok(Some(ObjectCheck {
373 snapshot: reading.snapshot(),
374 root: reading.root(),
375 object,
376 target,
377 digest: answer,
378 }))
379}
380
381pub enum Target<'v, 's, S: RangeSource> {
384 Absent,
386 Malformed,
388 Unsupported,
390 SameDocument(Part),
392 Root(Part),
394 Members {
396 names: Names<'v, 's, S>,
398 part: Part,
400 },
401 Negative(uri::Negative),
403}
404
405pub type ErrorOf<X, S> = AccessError<
407 VisitError<<X as Xml>::Error, ArchiveError<<S as RangeSource>::Error>>,
408 <X as Xml>::Id,
409>;
410
411pub fn resolve<'v, 's, X: UriXml, S: RangeSource, W: Work>(
416 base: &Member<'v, 's, S>,
417 access: &Access<'_, X, W>,
418 node: X::Id,
419) -> Result<Target<'v, 's, S>, ErrorOf<X, S>> {
420 scalar::with_uri(
421 access,
422 node,
423 LexicalSource::Attribute(Name {
424 namespace: "",
425 local: "URI",
426 }),
427 |value| {
428 let work = access.execution();
429 let archive = base.archive();
430 archive.admit(work, base.central_span())?;
431 let target = match value {
432 UriValue::Absent => Target::Absent,
433 UriValue::ComplexContent | UriValue::Malformed => Target::Malformed,
434 UriValue::Unsupported => Target::Unsupported,
435 UriValue::Valid(value) => match uri::resolve(base.name(), value, work)
436 .map_err(|cause| resource(base.central_span(), cause))?
437 {
438 uri::Target::SameDocument(part) => Target::SameDocument(part),
439 uri::Target::Root(part) => Target::Root(part),
440 uri::Target::Negative(cause) => Target::Negative(cause),
441 uri::Target::Member { name, part } => Target::Members {
442 names: archive.find(name.as_ref(), work)?,
443 part,
444 },
445 },
446 };
447 archive.admit(work, base.central_span())?;
448 Ok(target)
449 },
450 )
451}