1use super::{Index, Open};
7use crate::{
8 Cades,
9 counters::Paths,
10 credential::{Checked, Complete},
11 source::{DocumentId, Node, Original},
12 timestamp::ArchiveView,
13};
14use structured_asn1::{
15 Reader,
16 memory::{MemoryError, OwnedMap, OwnedVec, Work},
17};
18use structured_ltv::{
19 baseline::CredentialMaterial,
20 material::{ArchiveError, Archived, ArtifactRef, IndexError},
21 pki::{CounterReceipts, CredentialReceipts},
22 source::{Query, Snapshot},
23 timestamp::{ObligationError, TimestampRequirements, TimestampScope},
24};
25
26#[derive(Clone, Copy)]
28pub enum Need<'value, 'source, 'model, 'document> {
29 Known(ArtifactRef<'value, 'source>),
31 Open(Original<'model, 'document>),
33 Missing,
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub enum Completeness {
40 Complete,
42 Unsupported,
44 Unprocessed,
46 Unusable,
48 Malformed,
50}
51
52#[derive(Debug)]
54pub enum VisitError<E, V> {
55 Processing(E),
57 Visitor(V),
59}
60
61pub trait Provider<E> {
69 type Error;
71 fn visit<'m, 'd, V>(
73 &self,
74 credential: &Checked<'m, 'd, '_, E>,
75 open: &[Open<'d>],
76 operation: &Reader<'d>,
77 visitor: impl for<'value, 'source> FnMut(Need<'value, 'source, 'm, 'd>) -> Result<(), V>,
78 ) -> Result<Completeness, VisitError<Self::Error, V>>;
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83pub enum Missing {
84 Absent,
86 Uncovered,
88}
89
90#[derive(Debug, Clone, Copy, PartialEq, Eq)]
92pub struct Finding {
93 pub dependency: usize,
95 pub cause: Missing,
97}
98
99pub struct Entry<'model, 'document> {
101 source: Original<'model, 'document>,
102 completeness: Completeness,
103 findings: OwnedVec<Finding>,
104}
105impl<'m, 'd> Entry<'m, 'd> {
106 #[must_use]
108 pub fn source(&self) -> Original<'m, 'd> {
109 self.source
110 }
111 #[must_use]
113 pub fn completeness(&self) -> Completeness {
114 self.completeness
115 }
116 #[must_use]
118 pub fn findings(&self) -> &[Finding] {
119 &self.findings
120 }
121}
122
123pub struct Report<'auth, 'receipt, 'model, 'document, 'path, E> {
125 credentials: &'auth Complete<'receipt, 'model, 'document, 'path, E>,
126 scope: TimestampScope<Node, DocumentId<'model, 'document>, ()>,
127 entries: OwnedVec<Entry<'model, 'document>>,
128 complete: bool,
129}
130impl<'m, 'd, E> Report<'_, '_, 'm, 'd, '_, E> {
131 #[must_use]
133 pub fn entries(&self) -> &[Entry<'m, 'd>] {
134 &self.entries
135 }
136 #[must_use]
138 pub fn all_requirements_met(&self) -> bool {
139 self.complete
140 }
141}
142impl<'r, 'm, 'd, 'p, E> CredentialMaterial for Report<'_, 'r, 'm, 'd, 'p, E> {
143 type Id = Node;
144 type Document = DocumentId<'m, 'd>;
145 type Generation = ();
146 type Credentials = Complete<'r, 'm, 'd, 'p, E>;
147 fn scope(&self) -> TimestampScope<Node, Self::Document, ()> {
148 self.scope
149 }
150 fn snapshot(&self) -> Snapshot<Self::Document, ()> {
151 self.credentials.snapshot()
152 }
153 fn credentials(&self) -> &Self::Credentials {
154 self.credentials
155 }
156 fn all_requirements_met(&self) -> bool {
157 self.complete
158 }
159}
160
161#[derive(Debug)]
163pub enum Error<E> {
164 Foreign {
166 node: Node,
168 },
169 Processing(crate::Error),
171 Obligation(ObligationError<Node>),
173 Index(IndexError<Node>),
175 Archive(ArchiveError<Node>),
177 Provider {
179 node: Node,
181 cause: E,
183 },
184 Resource {
186 node: Node,
188 cause: MemoryError,
190 },
191 CallbackProtocol {
193 node: Node,
195 },
196}
197
198pub struct Scope<'a, 'm, 'd, 'p> {
201 archive: Option<ArchiveView<'a, 'm, 'd, 'p>>,
202}
203impl<'a, 'm, 'd, 'p> Scope<'a, 'm, 'd, 'p> {
204 #[must_use]
206 pub fn whole() -> Self {
207 Self { archive: None }
208 }
209 #[must_use]
212 pub fn before_archive(archive: impl Into<ArchiveView<'a, 'm, 'd, 'p>>) -> Self {
213 Self {
214 archive: Some(archive.into()),
215 }
216 }
217 fn require<E, Q: Query<Id = Node, Document = DocumentId<'m, 'd>, Generation = ()>, W: Work>(
218 &self,
219 required: &TimestampRequirements<Node, DocumentId<'m, 'd>, (), Cades>,
220 index: &Index<'_, 'm, 'd, W>,
221 query: &Q,
222 ) -> Result<(), Error<E>> {
223 let location = required.scope().location();
224 if location.snapshot != query.snapshot() {
225 return Err(Error::Foreign {
226 node: location.node,
227 });
228 }
229 index
230 .require_signature(required.signature(), query)
231 .map_err(Error::Index)?;
232 match (required.scope(), self.archive) {
233 (TimestampScope::All(_), None) => Ok(()),
234 (TimestampScope::BeforeArchive(_), Some(archive)) => {
235 let actor = archive.timestamp().actor().ok_or(Error::Foreign {
236 node: location.node,
237 })?;
238 let evidence = actor.verified();
239 query
240 .admit(location.node, 3)
241 .map_err(|cause| Error::Index(IndexError::Scope(cause)))?;
242 if evidence.signature() != required.signature()
243 || archive.coverage().subject() != location.node
244 || evidence.token().snapshot != location.snapshot
245 {
246 return Err(Error::Foreign {
247 node: location.node,
248 });
249 }
250 archive
251 .coverage()
252 .require_evidence(evidence, location.node, query)
253 .map_err(|cause| Error::Archive(ArchiveError::Coverage(cause)))
254 }
255 _ => Err(Error::Foreign {
256 node: location.node,
257 }),
258 }
259 }
260 fn missing<E, Q: Query<Id = Node, Document = DocumentId<'m, 'd>, Generation = ()>, W: Work>(
261 &self,
262 need: Need<'_, '_, 'm, 'd>,
263 index: &Index<'_, 'm, 'd, W>,
264 open: &OwnedMap<Node, (), Q::Work>,
265 query: &Q,
266 ) -> Result<Option<Missing>, Error<E>> {
267 match need {
268 Need::Missing => Ok(Some(Missing::Absent)),
269 Need::Known(artifact) => {
270 if let Some(archive) = self.archive {
271 let actor = archive.timestamp().actor().ok_or(Error::Foreign {
272 node: index.signature(),
273 })?;
274 return index
275 .archived(artifact, archive.coverage(), actor.verified(), query)
276 .map(|answer| match answer {
277 Archived::Present(_) => None,
278 Archived::Missing => Some(Missing::Absent),
279 Archived::Uncovered => Some(Missing::Uncovered),
280 })
281 .map_err(Error::Archive);
282 }
283 let mut copies = index.artifact(artifact, query).map_err(Error::Index)?;
284 copies
285 .next()
286 .transpose()
287 .map(|value| {
288 if value.is_some() {
289 None
290 } else {
291 Some(Missing::Absent)
292 }
293 })
294 .map_err(Error::Index)
295 }
296 Need::Open(location) => {
297 if location.snapshot != query.snapshot() {
298 return Err(Error::Foreign {
299 node: location.node,
300 });
301 }
302 if open
303 .get(&location.node, query.execution())
304 .map_err(|cause| Error::Resource {
305 node: location.node,
306 cause,
307 })?
308 .is_none()
309 {
310 return Ok(Some(Missing::Absent));
311 }
312 if let Some(archive) = self.archive {
313 let actor = archive.timestamp().actor().ok_or(Error::Foreign {
314 node: location.node,
315 })?;
316 return archive
317 .coverage()
318 .covers(actor.verified(), location.node, query)
319 .map(|covered| {
320 if covered {
321 None
322 } else {
323 Some(Missing::Uncovered)
324 }
325 })
326 .map_err(|cause| Error::Archive(ArchiveError::Coverage(cause)));
327 }
328 Ok(None)
329 }
330 }
331 }
332}
333
334pub fn check<'a, 'r, 'm, 'd, 'p, E, P: Provider<E>, W: Work>(
338 credentials: &'a Complete<'r, 'm, 'd, 'p, E>,
339 counters: &Paths<'_, 'm, 'd, 'p>,
340 required: &TimestampRequirements<Node, DocumentId<'m, 'd>, (), Cades>,
341 index: &Index<'_, 'm, 'd, W>,
342 scope: &Scope<'_, 'm, 'd, '_>,
343 provider: &P,
344 operation: &Reader<'d>,
345) -> Result<Report<'a, 'r, 'm, 'd, 'p, E>, Error<P::Error>> {
346 let document = credentials.snapshot().document.source();
347 document
348 .message()
349 .require_session(operation, "cades.credential.material")
350 .map_err(|cause| Error::Processing(crate::Error::Cms(cause)))?;
351 let execution = operation.execution();
352 let query = &structured_ltv::source::Scope::new(&document, credentials.snapshot(), &execution);
353 scope.require(required, index, query)?;
354 let node = credentials.signature();
355 query
356 .admit(node, 3)
357 .map_err(|cause| Error::Index(IndexError::Scope(cause)))?;
358 if credentials.snapshot() != query.snapshot()
359 || counters.snapshot() != query.snapshot()
360 || node != required.signature()
361 || node != counters.signature()
362 {
363 return Err(Error::Foreign { node });
364 }
365 required
366 .includes_counters(counters, query)
367 .map_err(|cause| Error::Index(IndexError::Scope(cause)))?;
368 let work = query.execution();
369 let mut open = work.map();
370 for entry in index.open() {
371 work.charge_work(1).map_err(|cause| Error::Resource {
372 node: entry.node,
373 cause,
374 })?;
375 open.try_insert(entry.node, ())
376 .map_err(|cause| Error::Resource {
377 node: entry.node,
378 cause,
379 })?;
380 }
381 let mut selected = work.vector();
382 for entry in credentials.report.root() {
383 selected
384 .try_push(entry)
385 .map_err(|cause| Error::Resource { node, cause })?;
386 }
387 let mut group = 0;
388 for ordinal in 0..counters.len() {
389 work.charge_work(2)
390 .map_err(|cause| Error::Resource { node, cause })?;
391 let Some((signature, entries)) = credentials
392 .report
393 .counter(group, work)
394 .map_err(|cause| Error::Resource { node, cause })?
395 else {
396 break;
397 };
398 if signature != counters.counter(ordinal).signature {
399 continue;
400 }
401 if required
402 .counter_in_scope(ordinal, query)
403 .map_err(Error::Obligation)?
404 {
405 for entry in entries {
406 selected
407 .try_push(entry)
408 .map_err(|cause| Error::Resource { node, cause })?;
409 }
410 }
411 group += 1;
412 }
413 if group != credentials.report.counters() {
414 return Err(Error::Foreign { node });
415 }
416 let mut entries = work.vector();
417 entries
418 .try_reserve_exact(selected.len())
419 .map_err(|cause| Error::Resource { node, cause })?;
420 let mut complete = true;
421 for credential in selected {
422 let entry = one(credential, index, scope, provider, operation, query, &open)?;
423 if entry.completeness != Completeness::Complete || !entry.findings.is_empty() {
424 complete = false;
425 }
426 entries
427 .try_push(entry)
428 .map_err(|cause| Error::Resource { node, cause })?;
429 }
430 query
431 .admit(node, 1)
432 .map_err(|cause| Error::Index(IndexError::Scope(cause)))?;
433 Ok(Report {
434 credentials,
435 scope: required.scope(),
436 entries: entries.into_owned(),
437 complete,
438 })
439}
440
441fn one<'m, 'd, E, P: Provider<E>, Q, W: Work>(
442 credential: &Checked<'m, 'd, '_, E>,
443 index: &Index<'_, 'm, 'd, W>,
444 scope: &Scope<'_, 'm, 'd, '_>,
445 provider: &P,
446 operation: &Reader<'d>,
447 query: &Q,
448 open: &OwnedMap<Node, (), Q::Work>,
449) -> Result<Entry<'m, 'd>, Error<P::Error>>
450where
451 Q: Query<Id = Node, Document = DocumentId<'m, 'd>, Generation = ()>,
452{
453 let work = query.execution();
454 let node = credential.source().node;
455 let mut findings = work.vector();
456 let mut ordinal = 0usize;
457 let mut first = None;
458 let answer = provider.visit(credential, index.open(), operation, |need| {
459 if first.is_some() {
460 return Err(());
461 }
462 let answer = (|| {
463 work.charge_work(1)
464 .map_err(|cause| Error::Resource { node, cause })?;
465 let dependency = ordinal;
466 ordinal = ordinal.checked_add(1).ok_or(Error::Resource {
467 node,
468 cause: MemoryError::WorkExceeded,
469 })?;
470 if let Some(cause) = scope.missing(need, index, open, query)? {
471 findings
472 .try_push(Finding { dependency, cause })
473 .map_err(|cause| Error::Resource { node, cause })?;
474 }
475 Ok(())
476 })();
477 match answer {
478 Ok(()) => Ok(()),
479 Err(cause) => {
480 first = Some(cause);
481 Err(())
482 }
483 }
484 });
485 if let Some(cause) = first {
486 return Err(cause);
487 }
488 let completeness = match answer {
489 Ok(value) => value,
490 Err(VisitError::Processing(cause)) => return Err(Error::Provider { node, cause }),
491 Err(VisitError::Visitor(())) => return Err(Error::CallbackProtocol { node }),
492 };
493 Ok(Entry {
494 source: credential.source(),
495 completeness,
496 findings: findings.into_owned(),
497 })
498}