1use super::origin::Origin;
4use super::{AuthorityClosure, AuthorityClosureError, AuthorityNode, CheckedPath, PathStatuses};
5use crate::source::{Location, Query, ScopeError, Snapshot};
6use crate::timestamp::{ObligationError, TimestampPaths, TimestampScope};
7use core::marker::PhantomData;
8use structured_asn1::memory::{HashWork, MemoryError, OwnedVec, Work};
9use structured_pkix::path::EffectiveAnchor;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum Participant<N> {
14 Signer(N),
16 Counter {
19 signature: N,
21 property: N,
23 },
24 Credential {
26 signature: N,
28 node: N,
30 authority: usize,
32 },
33 Timestamp {
35 property: N,
37 token: N,
39 },
40}
41
42#[derive(Clone, Copy)]
44pub enum ParticipantAuthority<'path, 'document> {
45 Path(&'path CheckedPath<'document>),
47 Anchor(&'path EffectiveAnchor<'document>),
50 CertificateFree,
53}
54
55#[derive(Clone, Copy)]
58pub enum Authorities<'path, 'document> {
59 Paths(&'path [&'path CheckedPath<'document>]),
62 DirectAnchor(&'path EffectiveAnchor<'document>),
66 CertificateFree,
70}
71
72pub trait SignerReceipt<'path, 'document> {
78 type Id: Copy;
80 type Document: Copy;
82 type Generation: Copy;
84
85 fn signature(&self) -> Location<Self::Id, Self::Document, Self::Generation>;
87 fn path(&self) -> Option<&'path CheckedPath<'document>>;
90}
91
92#[derive(Clone, Copy)]
94pub struct Counter<'path, 'document, N> {
95 pub signature: N,
97 pub property: N,
99 pub path: &'path CheckedPath<'document>,
101}
102
103pub trait CounterReceipts<'path, 'document> {
107 type Id: Copy;
109 type Document: Copy;
111 type Generation: Copy;
113
114 fn snapshot(&self) -> Snapshot<Self::Document, Self::Generation>;
116 fn signature(&self) -> Self::Id;
118 fn len(&self) -> usize;
120 fn is_empty(&self) -> bool {
122 self.len() == 0
123 }
124 fn counter(&self, ordinal: usize) -> Counter<'path, 'document, Self::Id>;
129}
130
131#[derive(Clone, Copy)]
133pub struct Credential<'path, 'document, N> {
134 pub signature: N,
136 pub node: N,
138 pub authorities: Authorities<'path, 'document>,
140}
141
142pub trait CredentialGroup<'path, 'document> {
144 type Id: Copy;
146
147 fn signature(&self) -> Self::Id;
149 fn len(&self) -> usize;
151 fn is_empty(&self) -> bool {
153 self.len() == 0
154 }
155 fn credential(&self, ordinal: usize) -> Credential<'path, 'document, Self::Id>;
160}
161
162pub trait CredentialReceipts<'path, 'document> {
167 type Id: Copy;
169 type Document: Copy;
171 type Generation: Copy;
173 type Group: CredentialGroup<'path, 'document, Id = Self::Id>;
175
176 fn snapshot(&self) -> Snapshot<Self::Document, Self::Generation>;
178 fn signature(&self) -> Self::Id;
180 fn root(&self) -> Option<&Self::Group>;
182 fn counters(&self) -> usize;
184 fn counter(&self, ordinal: usize) -> (Self::Id, &Self::Group);
189}
190
191pub struct ParticipantRequirement<'path, 'document, N> {
193 participant: Participant<N>,
194 authority: ParticipantAuthority<'path, 'document>,
195}
196
197impl<'path, 'document, N: Copy> ParticipantRequirement<'path, 'document, N> {
198 #[must_use]
200 pub fn participant(&self) -> Participant<N> {
201 self.participant
202 }
203 #[must_use]
205 pub fn authority(&self) -> ParticipantAuthority<'path, 'document> {
206 self.authority
207 }
208}
209
210pub struct ParticipantRequirements<'path, 'document, N, D, G, I> {
216 scope: TimestampScope<N, D, G>,
217 signature: N,
218 entries: OwnedVec<ParticipantRequirement<'path, 'document, N>>,
219 origins: Origins<'path>,
220 issuer: PhantomData<I>,
221}
222
223struct Origins<'a> {
224 signer: Origin<'a>,
225 counters: Origin<'a>,
226 credentials: Origin<'a>,
227 timestamps: Origin<'a>,
228}
229
230impl<'path, 'document, N: Copy, D: Copy, G: Copy, I>
231 ParticipantRequirements<'path, 'document, N, D, G, I>
232{
233 #[must_use]
235 pub fn scope(&self) -> TimestampScope<N, D, G> {
236 self.scope
237 }
238 #[must_use]
240 pub fn signature(&self) -> N {
241 self.signature
242 }
243 #[must_use]
245 pub fn entries(&self) -> &[ParticipantRequirement<'path, 'document, N>] {
246 &self.entries
247 }
248
249 #[must_use]
253 pub fn same_credentials<C>(&self, credentials: &C) -> bool {
254 self.origins.credentials.matches(credentials)
255 }
256
257 pub fn require_inputs<S, C, R, Q>(
263 &self,
264 signer: &S,
265 counters: &C,
266 credentials: &R,
267 timestamps: &TimestampPaths<'_, '_, '_, N, D, G, I>,
268 query: &Q,
269 ) -> Result<(), ParticipantError<N>>
270 where
271 D: Eq,
272 G: Eq,
273 Q: Query<Id = N, Document = D, Generation = G>,
274 {
275 let signature = self.signature;
276 if self.scope.location().snapshot != query.snapshot() {
277 return Err(ParticipantError::Foreign { signature });
278 }
279 query.admit(signature, 5)?;
281 if !self.origins.signer.matches(signer)
282 || !self.origins.counters.matches(counters)
283 || !self.same_credentials(credentials)
284 || !self.origins.timestamps.matches(timestamps)
285 {
286 return Err(ParticipantError::Foreign { signature });
287 }
288 Ok(())
289 }
290}
291
292#[derive(Debug, Clone, Copy, PartialEq, Eq)]
294pub enum ParticipantError<N> {
295 CounterTimestamps,
297 Obligation(ObligationError<N>),
299 Scope(ScopeError<N>),
301 Foreign {
303 signature: N,
305 },
306 UnprovenSigner,
309 MissingAuthorities {
312 node: N,
314 },
315 Resource {
317 node: N,
319 cause: MemoryError,
321 },
322}
323
324pub type ParticipantResult<'path, 'document, Q, I> = Result<
326 ParticipantRequirements<
327 'path,
328 'document,
329 <Q as Query>::Id,
330 <Q as Query>::Document,
331 <Q as Query>::Generation,
332 I,
333 >,
334 ParticipantError<<Q as Query>::Id>,
335>;
336
337impl<N> From<ScopeError<N>> for ParticipantError<N> {
338 fn from(cause: ScopeError<N>) -> Self {
339 Self::Scope(cause)
340 }
341}
342
343pub struct ParticipantClosure<'requirements, 'view, 'certificate, 'document, N, D, G, I> {
347 requirements: &'requirements ParticipantRequirements<'view, 'document, N, D, G, I>,
348 authorities: AuthorityClosure<'view, 'certificate, 'document>,
349}
350
351impl<'requirements, 'view, 'certificate, 'document, N, D, G, I>
352 ParticipantClosure<'requirements, 'view, 'certificate, 'document, N, D, G, I>
353{
354 #[must_use]
356 pub fn requirements(
357 &self,
358 ) -> &'requirements ParticipantRequirements<'view, 'document, N, D, G, I> {
359 self.requirements
360 }
361
362 #[must_use]
364 pub fn authorities(&self) -> &AuthorityClosure<'view, 'certificate, 'document> {
365 &self.authorities
366 }
367}
368
369#[derive(Debug)]
371pub enum ParticipantClosureError<N> {
372 Scope(ScopeError<N>),
374 Foreign,
376 Count,
379 Path {
381 participant: usize,
383 },
384 Authority(AuthorityClosureError),
386 Resource(MemoryError),
388}
389
390pub type ClosureResult<'requirements, 'view, 'certificate, 'document, N, D, G, I> = Result<
392 ParticipantClosure<'requirements, 'view, 'certificate, 'document, N, D, G, I>,
393 ParticipantClosureError<N>,
394>;
395
396impl<'path, 'document, N: Copy + Eq, D: Copy + Eq, G: Copy + Eq, I>
397 ParticipantRequirements<'path, 'document, N, D, G, I>
398{
399 pub fn close_statuses<'requirements, 'view, 'certificate, Q>(
408 &'requirements self,
409 roots: &'view [&'view PathStatuses<'view, 'certificate, 'document>],
410 supplied: &'view [AuthorityNode<'view, 'certificate, 'document>],
411 query: &Q,
412 ) -> ClosureResult<'requirements, 'view, 'certificate, 'document, N, D, G, I>
413 where
414 Q: Query<Id = N, Document = D, Generation = G>,
415 'path: 'view,
416 {
417 if self.scope.location().snapshot != query.snapshot() {
418 return Err(ParticipantClosureError::Foreign);
419 }
420 query
421 .admit(self.signature, 1)
422 .map_err(ParticipantClosureError::Scope)?;
423 let work = query.execution();
424 let mut cursor = 0;
425 for (participant, entry) in self.entries.iter().enumerate() {
426 work.charge_work(1)
427 .map_err(ParticipantClosureError::Resource)?;
428 let ParticipantAuthority::Path(path) = entry.authority else {
429 continue;
430 };
431 let Some(root) = roots.get(cursor) else {
432 return Err(ParticipantClosureError::Count);
433 };
434 if !core::ptr::eq(path, root.path()) {
435 return Err(ParticipantClosureError::Path { participant });
436 }
437 cursor += 1;
438 }
439 if cursor != roots.len() {
440 return Err(ParticipantClosureError::Count);
441 }
442 let authorities = super::close_authorities(roots, supplied, work)
443 .map_err(ParticipantClosureError::Authority)?;
444 Ok(ParticipantClosure {
445 requirements: self,
446 authorities,
447 })
448 }
449}
450
451struct Collect<'path, 'document, N, W: Work> {
452 entries: OwnedVec<ParticipantRequirement<'path, 'document, N>, W>,
453}
454
455impl<'path, 'document, N: Copy, W: Work> Collect<'path, 'document, N, W> {
456 fn push<Q: Query<Id = N, Work = W>>(
457 &mut self,
458 participant: Participant<N>,
459 authority: ParticipantAuthority<'path, 'document>,
460 node: N,
461 query: &Q,
462 ) -> Result<(), ParticipantError<N>> {
463 query.admit(node, 2)?;
464 self.entries
465 .try_push(ParticipantRequirement {
466 participant,
467 authority,
468 })
469 .map_err(|cause| ParticipantError::Resource { node, cause })
470 }
471
472 fn credentials<Q: Query<Id = N, Work = W>, R: CredentialGroup<'path, 'document, Id = N>>(
473 &mut self,
474 credentials: &R,
475 query: &Q,
476 ) -> Result<(), ParticipantError<N>> {
477 for ordinal in 0..credentials.len() {
478 query.admit(credentials.signature(), 2)?;
479 let credential = credentials.credential(ordinal);
480 let node = credential.node;
481 let participant = |authority| Participant::Credential {
482 signature: credential.signature,
483 node,
484 authority,
485 };
486 match credential.authorities {
487 Authorities::Paths([]) => {
490 return Err(ParticipantError::MissingAuthorities { node });
491 }
492 Authorities::Paths(paths) => {
493 for (index, path) in paths.iter().enumerate() {
494 self.push(
495 participant(index),
496 ParticipantAuthority::Path(path),
497 node,
498 query,
499 )?;
500 }
501 }
502 Authorities::DirectAnchor(anchor) => self.push(
503 participant(0),
504 ParticipantAuthority::Anchor(anchor),
505 node,
506 query,
507 )?,
508 Authorities::CertificateFree => self.push(
509 participant(0),
510 ParticipantAuthority::CertificateFree,
511 node,
512 query,
513 )?,
514 }
515 }
516 Ok(())
517 }
518}
519
520fn counter_and_role_requirements<'path, 'document, C, R, Q, I>(
524 collect: &mut Collect<'path, 'document, Q::Id, Q::Work>,
525 counters: &C,
526 credentials: &R,
527 timestamps: &TimestampPaths<'path, '_, 'document, Q::Id, Q::Document, Q::Generation, I>,
528 query: &Q,
529) -> Result<(), ParticipantError<Q::Id>>
530where
531 Q: Query,
532 C: CounterReceipts<'path, 'document, Id = Q::Id>,
533 R: CredentialReceipts<'path, 'document, Id = Q::Id>,
534{
535 let signature = counters.signature();
536 let mut ordinal = 0;
537 while ordinal < counters.len() {
538 query.admit(signature, 2)?;
539 let selected = timestamps
540 .required()
541 .counter_in_scope(ordinal, query)
542 .map_err(ParticipantError::Obligation)?;
543 let counter = counters.counter(ordinal);
544 ordinal += 1;
545 if !selected {
546 continue;
547 }
548 collect.push(
549 Participant::Counter {
550 signature: counter.signature,
551 property: counter.property,
552 },
553 ParticipantAuthority::Path(counter.path),
554 counter.property,
555 query,
556 )?;
557 }
558 if let Some(root) = credentials.root() {
559 collect.credentials(root, query)?;
560 }
561 let mut credential = 0;
562 let mut ordinal = 0;
563 while ordinal < counters.len() && credential < credentials.counters() {
564 query.admit(signature, 2)?;
565 let (child, group) = credentials.counter(credential);
566 let same = query
567 .equal(counters.counter(ordinal).signature, child)
568 .map_err(|cause| ParticipantError::Resource {
569 node: signature,
570 cause,
571 })?;
572 if same {
573 if timestamps
574 .required()
575 .counter_in_scope(ordinal, query)
576 .map_err(ParticipantError::Obligation)?
577 {
578 collect.credentials(group, query)?;
579 }
580 credential += 1;
581 }
582 ordinal += 1;
583 }
584 if credential != credentials.counters() {
585 return Err(ParticipantError::Foreign { signature });
586 }
587 Ok(())
588}
589
590pub fn participants<'path, 'document, S, C, R, Q, I>(
607 _issuer: I,
608 signer: &'path S,
609 counters: &'path C,
610 credentials: &'path R,
611 timestamps: &'path TimestampPaths<'path, '_, 'document, Q::Id, Q::Document, Q::Generation, I>,
612 query: &Q,
613) -> ParticipantResult<'path, 'document, Q, I>
614where
615 Q: Query,
616 S: SignerReceipt<
617 'path,
618 'document,
619 Id = Q::Id,
620 Document = Q::Document,
621 Generation = Q::Generation,
622 >,
623 C: CounterReceipts<
624 'path,
625 'document,
626 Id = Q::Id,
627 Document = Q::Document,
628 Generation = Q::Generation,
629 >,
630 R: CredentialReceipts<
631 'path,
632 'document,
633 Id = Q::Id,
634 Document = Q::Document,
635 Generation = Q::Generation,
636 >,
637{
638 let selected = signer.signature();
639 let signature = selected.node;
640 let scope = timestamps.required().scope();
641 if selected.snapshot != query.snapshot()
642 || counters.snapshot() != query.snapshot()
643 || credentials.snapshot() != query.snapshot()
644 || scope.location().snapshot != query.snapshot()
645 {
646 return Err(ParticipantError::Foreign { signature });
647 }
648 query.admit(signature, 1)?;
649 let entries = query.execution().vector();
650 for actual in [
651 counters.signature(),
652 credentials.signature(),
653 timestamps.required().signature(),
654 ] {
655 let cost = signature
656 .hash_work()
657 .and_then(|cost| {
658 cost.checked_add(actual.hash_work()?)
659 .ok_or(MemoryError::WorkExceeded)
660 })
661 .map_err(|cause| ParticipantError::Resource {
662 node: signature,
663 cause,
664 })?;
665 query
666 .execution()
667 .charge_work(cost)
668 .map_err(|cause| ParticipantError::Resource {
669 node: signature,
670 cause,
671 })?;
672 if actual != signature {
673 return Err(ParticipantError::Foreign { signature });
674 }
675 }
676 let Some(path) = signer.path() else {
677 return Err(ParticipantError::UnprovenSigner);
678 };
679 if !timestamps
680 .required()
681 .includes_counters(counters, query)
682 .map_err(ParticipantError::Scope)?
683 {
684 return Err(ParticipantError::CounterTimestamps);
685 }
686 let mut collect = Collect { entries };
687 collect.push(
688 Participant::Signer(signature),
689 ParticipantAuthority::Path(path),
690 signature,
691 query,
692 )?;
693 counter_and_role_requirements(&mut collect, counters, credentials, timestamps, query)?;
694 for path in timestamps.paths() {
695 query.admit(signature, 2)?;
696 let source = path.actor().verified();
697 collect.push(
698 Participant::Timestamp {
699 property: source.property(),
700 token: source.token().node,
701 },
702 ParticipantAuthority::Path(path.path()),
703 source.property(),
704 query,
705 )?;
706 }
707 query.admit(signature, 1)?;
708 Ok(ParticipantRequirements {
709 scope,
710 signature,
711 entries: collect.entries.into_owned(),
712 origins: Origins {
713 signer: Origin::of(signer),
714 counters: Origin::of(counters),
715 credentials: Origin::of(credentials),
716 timestamps: Origin::of(timestamps),
717 },
718 issuer: PhantomData,
719 })
720}