1use crate::role;
4use crate::xml::{Access, AccessError, Snapshot, Xml};
5use structured_asn1::memory::{MemoryError, OwnedVec, Work};
6use structured_ltv::pki::{self as ltv, Authorities, CredentialGroup, CredentialReceipts};
7
8#[derive(Debug, Clone, Copy)]
11pub struct Credential<N, D, G> {
12 snapshot: Snapshot<D, G>,
13 signature: N,
14 entry: role::Entry<N>,
15}
16
17impl<N: Copy, D: Copy, G: Copy> Credential<N, D, G> {
18 #[must_use]
20 pub fn snapshot(self) -> Snapshot<D, G> {
21 self.snapshot
22 }
23 #[must_use]
25 pub fn signature(self) -> N {
26 self.signature
27 }
28 #[must_use]
30 pub fn entry(self) -> role::Entry<N> {
31 self.entry
32 }
33}
34
35pub use structured_ltv::credential::Authentication;
36
37pub trait Provider<'path, 'document, X: Xml> {
54 type Evidence;
56 type Error;
58
59 fn authenticate<W: Work>(
61 &self,
62 access: &Access<'_, X, W>,
63 credential: Credential<X::Id, X::Document, X::Generation>,
64 ) -> Result<Authentication<'path, 'document, Self::Evidence>, Self::Error>;
65}
66
67pub struct Checked<'path, 'document, E, N, D, G> {
69 source: Credential<N, D, G>,
70 evidence: E,
71 authorities: Authorities<'path, 'document>,
72}
73
74impl<'path, 'document, E, N: Copy, D: Copy, G: Copy> Checked<'path, 'document, E, N, D, G> {
75 #[must_use]
77 pub fn source(&self) -> Credential<N, D, G> {
78 self.source
79 }
80 #[must_use]
82 pub fn evidence(&self) -> &E {
83 &self.evidence
84 }
85 #[must_use]
88 pub fn authorities(&self) -> Authorities<'path, 'document> {
89 self.authorities
90 }
91}
92
93pub struct Credentials<'path, 'document, E, N, D, G> {
96 snapshot: Snapshot<D, G>,
97 signature: N,
98 entries: OwnedVec<Checked<'path, 'document, E, N, D, G>>,
99}
100
101impl<'path, 'document, E, N: Copy, D: Copy, G: Copy> Credentials<'path, 'document, E, N, D, G> {
102 #[must_use]
104 pub fn snapshot(&self) -> Snapshot<D, G> {
105 self.snapshot
106 }
107 #[must_use]
109 pub fn signature(&self) -> N {
110 self.signature
111 }
112 #[must_use]
114 pub fn entries(&self) -> &[Checked<'path, 'document, E, N, D, G>] {
115 &self.entries
116 }
117}
118
119impl<'path, 'document, E, N: Copy, D: Copy, G: Copy> CredentialGroup<'path, 'document>
121 for Credentials<'path, 'document, E, N, D, G>
122{
123 type Id = N;
124
125 fn signature(&self) -> N {
126 self.signature
127 }
128
129 fn len(&self) -> usize {
130 self.entries.len()
131 }
132
133 fn credential(&self, ordinal: usize) -> ltv::Credential<'path, 'document, N> {
134 let checked = &self.entries[ordinal];
135 ltv::Credential {
136 signature: checked.source.signature,
137 node: checked.source.entry.node,
138 authorities: checked.authorities,
139 }
140 }
141}
142
143#[derive(Debug)]
145pub enum Error<E, PE, N> {
146 Foreign {
148 signature: N,
150 },
151 Access(AccessError<E, N>),
153 Unsupported {
155 node: N,
157 },
158 Rejected {
160 node: N,
162 },
163 MissingAuthorities {
167 node: N,
169 },
170 Provider {
172 node: N,
174 cause: PE,
176 },
177 Resource {
179 node: N,
181 cause: MemoryError,
183 },
184}
185
186pub type CheckResult<'path, 'document, X, P> = Result<
188 Credentials<
189 'path,
190 'document,
191 <P as Provider<'path, 'document, X>>::Evidence,
192 <X as Xml>::Id,
193 <X as Xml>::Document,
194 <X as Xml>::Generation,
195 >,
196 Error<<X as Xml>::Error, <P as Provider<'path, 'document, X>>::Error, <X as Xml>::Id>,
197>;
198
199pub fn check<'path, 'document, X: Xml, P: Provider<'path, 'document, X>, W: Work>(
206 roles: &role::Catalogue<X>,
207 provider: &P,
208 access: &Access<'_, X, W>,
209) -> CheckResult<'path, 'document, X, P> {
210 if roles.snapshot() != access.snapshot() {
211 return Err(Error::Access(AccessError::Changed { node: roles.node() }));
212 }
213 let mut entries = access
214 .query(roles.node(), |_, _, work| Ok(work.vector()))
215 .map_err(Error::Access)?;
216 for entry in roles.entries() {
217 access
218 .query(entry.node, |_, _, work| Ok(work.charge_work(1)))
219 .map_err(Error::Access)?
220 .map_err(|cause| Error::Resource {
221 node: entry.node,
222 cause,
223 })?;
224 if entry.kind == role::Kind::Claimed {
225 continue;
226 }
227 let source = Credential {
228 snapshot: roles.snapshot(),
229 signature: roles.signature(),
230 entry: *entry,
231 };
232 let outcome = access
233 .query(entry.node, |_, _, _| {
234 Ok(provider.authenticate(access, source))
235 })
236 .map_err(Error::Access)?
237 .map_err(|cause| Error::Provider {
238 node: entry.node,
239 cause,
240 })?;
241 let (evidence, authorities) = match outcome {
242 Authentication::Unsupported => return Err(Error::Unsupported { node: entry.node }),
243 Authentication::Rejected => return Err(Error::Rejected { node: entry.node }),
244 Authentication::Authenticated {
245 evidence,
246 authorities,
247 } => (evidence, authorities),
248 };
249 if let Authorities::Paths(paths) = authorities
250 && paths.is_empty()
251 {
252 return Err(Error::MissingAuthorities { node: entry.node });
253 }
254 entries
255 .try_push(Checked {
256 source,
257 evidence,
258 authorities,
259 })
260 .map_err(|cause| Error::Resource {
261 node: entry.node,
262 cause,
263 })?;
264 }
265 access
266 .query(roles.node(), |_, _, _| Ok(()))
267 .map_err(Error::Access)?;
268 Ok(Credentials {
269 snapshot: roles.snapshot(),
270 signature: roles.signature(),
271 entries: entries.into_owned(),
272 })
273}
274
275pub struct CounterCredentials<'path, 'document, E, N, D, G> {
277 source: crate::counter::Checked<N, D, G>,
278 credentials: Credentials<'path, 'document, E, N, D, G>,
279}
280
281impl<'path, 'document, E, N, D, G> CounterCredentials<'path, 'document, E, N, D, G> {
282 #[must_use]
284 pub fn source(&self) -> &crate::counter::Checked<N, D, G> {
285 &self.source
286 }
287 #[must_use]
289 pub fn credentials(&self) -> &Credentials<'path, 'document, E, N, D, G> {
290 &self.credentials
291 }
292}
293
294pub struct ParticipantCredentials<'path, 'document, E, N, D, G> {
298 snapshot: Snapshot<D, G>,
299 signature: N,
300 root: Option<Credentials<'path, 'document, E, N, D, G>>,
301 counters: OwnedVec<CounterCredentials<'path, 'document, E, N, D, G>>,
302}
303
304impl<'path, 'document, E, N: Copy, D: Copy, G: Copy>
305 ParticipantCredentials<'path, 'document, E, N, D, G>
306{
307 #[must_use]
309 pub fn snapshot(&self) -> Snapshot<D, G> {
310 self.snapshot
311 }
312 #[must_use]
314 pub fn signature(&self) -> N {
315 self.signature
316 }
317 #[must_use]
319 pub fn root(&self) -> Option<&Credentials<'path, 'document, E, N, D, G>> {
320 self.root.as_ref()
321 }
322 #[must_use]
325 pub fn counters(&self) -> &[CounterCredentials<'path, 'document, E, N, D, G>] {
326 &self.counters
327 }
328}
329
330impl<'path, 'document, E, N: Copy, D: Copy, G: Copy> CredentialReceipts<'path, 'document>
332 for ParticipantCredentials<'path, 'document, E, N, D, G>
333{
334 type Id = N;
335 type Document = D;
336 type Generation = G;
337 type Group = Credentials<'path, 'document, E, N, D, G>;
338
339 fn snapshot(&self) -> Snapshot<D, G> {
340 self.snapshot
341 }
342
343 fn signature(&self) -> N {
344 self.signature
345 }
346
347 fn root(&self) -> Option<&Self::Group> {
348 self.root.as_ref()
349 }
350
351 fn counters(&self) -> usize {
352 self.counters.len()
353 }
354
355 fn counter(&self, ordinal: usize) -> (N, &Self::Group) {
356 let counter = &self.counters[ordinal];
357 (counter.source.original().child(), &counter.credentials)
358 }
359}
360
361pub type ParticipantResult<'path, 'document, X, P> = Result<
363 ParticipantCredentials<
364 'path,
365 'document,
366 <P as Provider<'path, 'document, X>>::Evidence,
367 <X as Xml>::Id,
368 <X as Xml>::Document,
369 <X as Xml>::Generation,
370 >,
371 Error<<X as Xml>::Error, <P as Provider<'path, 'document, X>>::Error, <X as Xml>::Id>,
372>;
373
374pub fn check_participants<'path, 'document, X: Xml, P: Provider<'path, 'document, X>, W: Work>(
379 prepared: &crate::prepare::Prepared<X>,
380 counters: &crate::pki::CounterPaths<'_, '_, X::Id, X::Document, X::Generation>,
381 provider: &P,
382 access: &Access<'_, X, W>,
383) -> ParticipantResult<'path, 'document, X, P> {
384 use structured_asn1::memory::HashWork;
385 let layout = prepared.bound().containers();
386 let signature = layout.signature();
387 if layout.snapshot() != access.snapshot() || counters.snapshot() != access.snapshot() {
388 return Err(Error::Access(AccessError::Changed { node: signature }));
389 }
390 let mut groups = access
391 .query(signature, |_, _, work| Ok(work.vector()))
392 .map_err(Error::Access)?;
393 let cost = signature
394 .hash_work()
395 .and_then(|cost| {
396 cost.checked_add(counters.signature().hash_work()?)
397 .ok_or(MemoryError::WorkExceeded)
398 })
399 .map_err(|cause| Error::Resource {
400 node: signature,
401 cause,
402 })?;
403 access
404 .execution()
405 .charge_work(cost)
406 .map_err(|cause| Error::Resource {
407 node: signature,
408 cause,
409 })?;
410 if signature != counters.signature() {
411 return Err(Error::Foreign { signature });
412 }
413 let root = match prepared.roles() {
414 Some(roles) => Some(check(roles, provider, access)?),
415 None => None,
416 };
417 for actor in counters.actors() {
418 access
419 .query(signature, |_, _, work| Ok(work.charge_work(1)))
420 .map_err(Error::Access)?
421 .map_err(|cause| Error::Resource {
422 node: signature,
423 cause,
424 })?;
425 let Some(roles) = actor.roles() else {
426 continue;
427 };
428 let credentials = check(roles, provider, access)?;
429 groups
430 .try_push(CounterCredentials {
431 source: *actor.signer().counter(),
432 credentials,
433 })
434 .map_err(|cause| Error::Resource {
435 node: roles.node(),
436 cause,
437 })?;
438 }
439 access
440 .query(signature, |_, _, _| Ok(()))
441 .map_err(Error::Access)?;
442 Ok(ParticipantCredentials {
443 snapshot: layout.snapshot(),
444 signature,
445 root,
446 counters: groups.into_owned(),
447 })
448}