1use super::Unified;
4use crate::{
5 basic::Basic,
6 credential::{Complete, Group},
7 query::Query,
8 range::RangeSource,
9 timestamp::{Node, Pades},
10};
11use structured_asn1::memory::{Memory, MemoryError, OwnedVec, Work};
12use structured_cades::material::credentials::{Need, Provider};
13use structured_ltv::{
14 baseline::CredentialMaterial,
15 material::{
16 ArchiveError, Archived, Coverage, CoverageError, DependencyError, DependencyReport,
17 DependencyVisit, IndexError, Missing,
18 },
19 pki::{CounterReceipts, CredentialGroup},
20 source::{Query as _, ScopeError, Snapshot},
21 timestamp::{CheckedActor, TimestampRequirements, TimestampScope},
22};
23
24pub type CheckedReport<'a, 'r, 'm, 'd, 'p, D, G, E> =
26 Report<'a, Complete<'r, 'm, 'd, 'p, D, G, E>, D, G>;
27
28pub enum Scope<'a, 'c, 'd, D, G, W: Work = Memory> {
30 Whole,
32 Before {
34 coverage: &'a Coverage<Node, D, G, W, Pades>,
36 actor: &'a CheckedActor<'c, 'd, Node, D, G, Pades>,
38 },
39}
40impl<D, G> Scope<'_, '_, '_, D, G> {
41 #[must_use]
43 pub fn whole() -> Self {
44 Self::Whole
45 }
46}
47pub struct Entry {
49 pub node: Node,
51 pub dependencies: DependencyReport,
53}
54pub struct Report<'a, C, D, G> {
56 credentials: &'a C,
57 scope: TimestampScope<Node, D, G>,
58 entries: OwnedVec<Entry>,
59 complete: bool,
60}
61impl<C, D, G> Report<'_, C, D, G> {
62 #[must_use]
64 pub fn entries(&self) -> &[Entry] {
65 &self.entries
66 }
67}
68impl<C, D: Copy, G: Copy> CredentialMaterial for Report<'_, C, D, G> {
69 type Id = Node;
70 type Document = D;
71 type Generation = G;
72 type Credentials = C;
73 fn scope(&self) -> TimestampScope<Node, D, G> {
74 self.scope
75 }
76 fn snapshot(&self) -> Snapshot<D, G> {
77 self.scope.location().snapshot
78 }
79 fn credentials(&self) -> &C {
80 self.credentials
81 }
82 fn all_requirements_met(&self) -> bool {
83 self.complete
84 }
85}
86#[derive(Debug)]
88pub enum Error<E> {
89 Foreign {
91 node: Node,
93 },
94 Scope(ScopeError<Node>),
96 Obligation(structured_ltv::timestamp::ObligationError<Node>),
98 Index(IndexError<Node>),
100 Archive(ArchiveError<Node>),
102 Coverage(CoverageError<Node>),
104 Resource {
106 node: Node,
108 cause: MemoryError,
110 },
111 Cms(structured_cms::Error),
113 Provider {
115 node: Node,
117 cause: E,
119 },
120 CallbackProtocol {
122 node: Node,
124 },
125}
126
127fn dependency<D: Copy + Eq, G: Copy + Eq, E, S, W: Work, O: Work>(
128 need: Need<'_, '_, '_, '_>,
129 mapping: crate::binding::Mapping<'_, '_, D, G>,
130 index: &Unified<'_, '_, D, G>,
131 scope: &Scope<'_, '_, '_, D, G, W>,
132 open: &structured_asn1::memory::OwnedMap<Node, (), O>,
133 node: Node,
134 query: &Query<'_, '_, S>,
135) -> Result<Option<Missing>, Error<E>>
136where
137 S: RangeSource<Document = D, Generation = G>,
138{
139 match need {
140 Need::Missing => Ok(Some(Missing::Absent)),
141 Need::Known(artifact) => {
142 if let Scope::Before { coverage, actor } = scope {
143 return index
144 .index
145 .archived(artifact, coverage, actor.verified(), query)
146 .map(|value| match value {
147 Archived::Present(_) => None,
148 Archived::Missing => Some(Missing::Absent),
149 Archived::Uncovered => Some(Missing::Uncovered),
150 })
151 .map_err(Error::Archive);
152 }
153 let mut copies = index
154 .index
155 .artifact(artifact, query)
156 .map_err(Error::Index)?;
157 Ok(
158 if copies.next().transpose().map_err(Error::Index)?.is_some() {
159 None
160 } else {
161 Some(Missing::Absent)
162 },
163 )
164 }
165 Need::Open(location) => {
166 if location.snapshot != mapping.cms {
167 return Err(Error::Foreign { node });
168 }
169 let node = mapping.node(location.node);
170 if open
171 .get(&node, query.execution())
172 .map_err(|cause| Error::Resource { node, cause })?
173 .is_none()
174 {
175 return Ok(Some(Missing::Absent));
176 }
177 if let Scope::Before { coverage, actor } = scope {
178 return coverage
179 .covers(actor.verified(), node, query)
180 .map(|covered| {
181 if covered {
182 None
183 } else {
184 Some(Missing::Uncovered)
185 }
186 })
187 .map_err(Error::Coverage);
188 }
189 Ok(None)
190 }
191 }
192}
193
194fn scope_binding<
195 D: Copy + Eq,
196 G: Copy + Eq,
197 E,
198 S: RangeSource<Document = D, Generation = G>,
199 W: Work,
200>(
201 scope: &Scope<'_, '_, '_, D, G, W>,
202 required: &TimestampRequirements<Node, D, G, Pades>,
203 index: &Unified<'_, '_, D, G>,
204 root: Node,
205 query: &Query<'_, '_, S>,
206) -> Result<(), Error<E>> {
207 match (scope, required.scope()) {
208 (Scope::Whole, TimestampScope::All(_)) if core::ptr::eq(required, index.required) => {}
209 (Scope::Before { coverage, actor }, TimestampScope::BeforeArchive(location))
210 if location == actor.verified().token() && actor.verified().signature() == root =>
211 {
212 coverage
213 .require_evidence(actor.verified(), actor.verified().property(), query)
214 .map_err(Error::Coverage)?;
215 }
216 _ => return Err(Error::Foreign { node: root }),
217 }
218 Ok(())
219}
220
221pub fn check<'a, 'r, 'm, 'd, 'p, D: Copy + Eq, G: Copy + Eq, E, P, S, W>(
225 basic: &Basic<'_, '_, '_, 'm, 'd, '_, '_, 'p, D, G>,
226 credentials: &'a Complete<'r, 'm, 'd, 'p, D, G, E>,
227 required: &TimestampRequirements<Node, D, G, Pades>,
228 index: &Unified<'_, 'd, D, G>,
229 scope: &Scope<'_, '_, '_, D, G, W>,
230 provider: &P,
231 query: &Query<'_, '_, S>,
232) -> Result<CheckedReport<'a, 'r, 'm, 'd, 'p, D, G, E>, Error<P::Error>>
233where
234 P: Provider<E>,
235 S: RangeSource<Document = D, Generation = G>,
236 W: Work,
237{
238 let bound = basic.binding();
239 let root = bound.mapping.root;
240 let snapshot = bound.mapping.snapshot;
241 query.admit(root, 1).map_err(Error::Scope)?;
242 if !query.belongs(bound.prepared().container.signature().owner().value.session)
243 || !query.belongs(index.session)
244 || query.snapshot() != snapshot
245 || credentials.mapping.snapshot != snapshot
246 || credentials.mapping.cms != bound.mapping.cms
247 || credentials.mapping.root != root
248 || required.signature() != root
249 || required.scope().location().snapshot != snapshot
250 {
251 return Err(Error::Foreign { node: root });
252 }
253 index
254 .index
255 .require_signature(root, query)
256 .map_err(Error::Index)?;
257 scope_binding(scope, required, index, root, query)?;
258 required
259 .includes_counters(basic.counters(), query)
260 .map_err(Error::Scope)?;
261 let mut open = query.execution().map();
262 for value in index.index.open() {
263 query.admit(root, 1).map_err(Error::Scope)?;
264 let node = bound.mapping.node(value.node);
265 open.try_insert(node, ())
266 .map_err(|cause| Error::Resource { node, cause })?;
267 }
268 let fields = bound.prepared().cms.signer().fields_source();
269 let reader = fields.reader_in(query.execution()).map_err(Error::Cms)?;
270 let mut entries = query.execution().vector();
271 let mut complete = true;
272 let mut visit = |group: &Group<'_, 'm, 'd, 'p, D, G, E>| -> Result<(), Error<P::Error>> {
273 for credential in group.common.entries() {
274 query.admit(root, 1).map_err(Error::Scope)?;
275 let node = bound.mapping.node(credential.source().node);
276 let mut visiting =
277 DependencyVisit::new(credential.source().node, query.execution(), |need| {
278 dependency(need, bound.mapping, index, scope, &open, node, query)
279 })
280 .map_err(|cause| Error::Resource { node, cause })?;
281 let answer = provider.visit(credential, index.index.open(), &reader, |need| {
282 visiting.observe(need)
283 });
284 let dependencies = visiting
285 .finish(answer)
286 .map_err(|cause| dependency_error(node, cause))?;
287 if dependencies.completeness != structured_ltv::material::Completeness::Complete
288 || !dependencies.findings.is_empty()
289 {
290 complete = false;
291 }
292 entries
293 .try_push(Entry { node, dependencies })
294 .map_err(|cause| Error::Resource { node, cause })?;
295 }
296 Ok(())
297 };
298 if let Some(group) = &credentials.root {
299 visit(group)?;
300 }
301 let mut counter = 0;
302 for group in &credentials.counters {
303 loop {
304 query.admit(root, 1).map_err(Error::Scope)?;
305 if counter == basic.counters().len() {
306 return Err(Error::Foreign {
307 node: group.signature(),
308 });
309 }
310 let ordinal = counter;
311 counter += 1;
312 if basic.counters().counter(ordinal).signature != group.signature() {
313 continue;
314 }
315 if required
316 .counter_in_scope(ordinal, query)
317 .map_err(Error::Obligation)?
318 {
319 visit(group)?;
320 }
321 break;
322 }
323 }
324 query.admit(root, 1).map_err(Error::Scope)?;
325 Ok(Report {
326 credentials,
327 scope: required.scope(),
328 entries: entries.into_owned(),
329 complete,
330 })
331}
332
333fn dependency_error<N, E>(node: Node, cause: DependencyError<N, Error<E>, E>) -> Error<E> {
334 match cause {
335 DependencyError::Processing { cause, .. } => cause,
336 DependencyError::Resource { cause, .. } => Error::Resource { node, cause },
337 DependencyError::Provider { cause, .. } => Error::Provider { node, cause },
338 DependencyError::CallbackProtocol { .. } => Error::CallbackProtocol { node },
339 }
340}