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