Skip to main content

structured_ltv/pki/
root_visits.rs

1//! Per-actor dependency attribution through the existing authority kernel.
2use super::closure::{Key, follow};
3use super::{AuthorityClosure, AuthorityClosureError, AuthorityNode, AuthorityOrigin};
4use structured_asn1::memory::{MemoryError, OwnedMap, OwnedVec, Work};
5
6/// Complete traversal or an unchanged visitor/processing refusal. A visitor
7/// may have external side effects; no rollback of those effects is promised.
8#[derive(Debug)]
9pub enum AuthorityVisitError<E> {
10    /// Current dependency lookup, traversal or scratch admission failed.
11    Processing(AuthorityClosureError),
12    /// First original visitor refusal; no subsequent roots are visited.
13    Visitor(E),
14}
15
16struct VisitState {
17    index: usize,
18    root: Option<usize>,
19}
20struct Pending<'view, 'document, W: Work> {
21    index: OwnedMap<Key<'view, 'document>, VisitState, W>,
22    queue: OwnedVec<usize, W>,
23}
24impl<'view, 'document, W: Work> Pending<'view, 'document, W> {
25    fn need(
26        &mut self,
27        key: Key<'view, 'document>,
28        root: usize,
29        origin: AuthorityOrigin,
30        work: &W,
31    ) -> Result<(), AuthorityClosureError> {
32        let state = self
33            .index
34            .get_mut(&key, work)?
35            .ok_or(AuthorityClosureError::MissingPath { origin })?;
36        work.charge_work(1)?;
37        if state.root != Some(root) {
38            self.queue.try_push(state.index)?;
39            state.root = Some(root);
40        }
41        Ok(())
42    }
43}
44
45impl<'view, 'certificate, 'document> AuthorityClosure<'view, 'certificate, 'document> {
46    /// Visit each root's exact reachable authority nodes once, including the
47    /// root itself. The root ordinal retains individual actor attribution even
48    /// when different TSA/signature/credential actors share a path. This is the
49    /// relation needed for EN319132-1 ยง6.3(x), not aggregate TSA-purpose bits.
50    /// <https://www.etsi.org/deliver/etsi_en/319100_319199/31913201/01.03.01_60/en_31913201v010301p.pdf>
51    ///
52    /// Reuses the SAME complete/delta/OCSP dependency kernel as closure and role
53    /// propagation. No parsing, provider, cryptographic/status check is replayed.
54    /// One current-operation index and iterative queue are reused across roots;
55    /// cycles terminate, shared paths remain attributed to each original root.
56    /// Work admits each edge/lookup/visitor dispatch, and scratch capacity is
57    /// released on success/refusal. The visitor funds its own dependent work.
58    /// No partial returned collection or baseline/Good verdict is manufactured.
59    pub fn visit_roots<E, W: Work>(
60        &self,
61        work: &W,
62        mut visitor: impl FnMut(
63            usize,
64            &'view AuthorityNode<'view, 'certificate, 'document>,
65        ) -> Result<(), E>,
66    ) -> Result<(), AuthorityVisitError<E>> {
67        let processing = AuthorityVisitError::Processing;
68        let resource = |cause: MemoryError| processing(AuthorityClosureError::Resource(cause));
69        work.charge_work(1).map_err(resource)?;
70        let mut pending = Pending {
71            index: work.map(),
72            queue: work.vector(),
73        };
74        let mut responders = work.map();
75        for (index, node) in self.nodes().iter().enumerate() {
76            work.charge_work(1).map_err(resource)?;
77            let key = Key {
78                path: node.statuses.path(),
79                at: node.statuses.evaluated_at(),
80            };
81            if pending
82                .index
83                .try_insert(key, VisitState { index, root: None })
84                .map_err(resource)?
85                .is_some()
86            {
87                return Err(processing(AuthorityClosureError::DuplicateNode { index }));
88            }
89            for responder in node.responders {
90                work.charge_work(1).map_err(resource)?;
91                let key = core::ptr::from_ref(responder.target()).addr();
92                if responders
93                    .try_insert(key, *responder)
94                    .map_err(resource)?
95                    .is_some()
96                {
97                    return Err(processing(AuthorityClosureError::DuplicateResponder {
98                        node: index,
99                    }));
100                }
101            }
102        }
103        for (root, statuses) in self.roots().iter().enumerate() {
104            work.charge_work(1).map_err(resource)?;
105            pending.queue.clear();
106            pending
107                .need(
108                    Key {
109                        path: statuses.path(),
110                        at: statuses.evaluated_at(),
111                    },
112                    root,
113                    AuthorityOrigin::Root { index: root },
114                    work,
115                )
116                .map_err(processing)?;
117            let mut cursor = 0;
118            while cursor < pending.queue.len() {
119                work.charge_work(1).map_err(resource)?;
120                let index = pending.queue[cursor];
121                let node = self.nodes()[index];
122                visitor(root, node).map_err(AuthorityVisitError::Visitor)?;
123                follow(
124                    node.statuses,
125                    index,
126                    &responders,
127                    &mut |path, at, origin, work| {
128                        pending.need(Key { path, at }, root, origin, work)
129                    },
130                    work,
131                )
132                .map_err(processing)?;
133                cursor += 1;
134            }
135        }
136        Ok(())
137    }
138}