1use super::Purpose;
4use crate::pki::{CounterReceipts, TimestampPath};
5use crate::source::{Location, Query, ScopeError};
6use core::marker::PhantomData;
7use structured_asn1::memory::OwnedVec;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum TimestampScope<N, D, G> {
12 All(Location<N, D, G>),
14 BeforeArchive(Location<N, D, G>),
18}
19
20impl<N: Copy, D: Copy, G: Copy> TimestampScope<N, D, G> {
21 #[must_use]
23 pub fn location(self) -> Location<N, D, G> {
24 match self {
25 Self::All(location) | Self::BeforeArchive(location) => location,
26 }
27 }
28}
29
30#[derive(Debug, Clone, Copy)]
32pub struct RequiredToken<N> {
33 pub signature: N,
35 pub property: N,
37 pub token: N,
39 pub purpose: Purpose,
41}
42
43pub struct TimestampRequirements<N, D, G, I> {
47 signature: N,
48 scope: TimestampScope<N, D, G>,
49 entries: OwnedVec<RequiredToken<N>>,
50 counters: Option<Counters<N>>,
51 issuer: PhantomData<I>,
52}
53
54struct Counters<N> {
57 signatures: OwnedVec<N>,
58 covered: Option<OwnedVec<bool>>,
59}
60
61impl<N: Copy, D: Copy, G: Copy, I> TimestampRequirements<N, D, G, I> {
62 #[must_use]
67 pub fn new(
68 _issuer: I,
69 signature: N,
70 scope: TimestampScope<N, D, G>,
71 entries: OwnedVec<RequiredToken<N>>,
72 ) -> Self {
73 Self {
74 signature,
75 scope,
76 entries,
77 counters: None,
78 issuer: PhantomData,
79 }
80 }
81
82 #[must_use]
89 pub fn with_counters(
90 _issuer: I,
91 signature: N,
92 scope: TimestampScope<N, D, G>,
93 entries: OwnedVec<RequiredToken<N>>,
94 signatures: OwnedVec<N>,
95 covered: Option<OwnedVec<bool>>,
96 ) -> Self {
97 Self {
98 signature,
99 scope,
100 entries,
101 counters: Some(Counters {
102 signatures,
103 covered,
104 }),
105 issuer: PhantomData,
106 }
107 }
108
109 #[must_use]
111 pub fn signature(&self) -> N {
112 self.signature
113 }
114 #[must_use]
116 pub fn scope(&self) -> TimestampScope<N, D, G> {
117 self.scope
118 }
119 #[must_use]
121 pub fn entries(&self) -> &[RequiredToken<N>] {
122 &self.entries
123 }
124}
125
126pub struct TimestampPaths<'view, 'certificate, 'document, N, D, G, I> {
129 required: &'view TimestampRequirements<N, D, G, I>,
130 paths: &'view [&'view TimestampPath<'view, 'certificate, 'document, N, D, G, I>],
131}
132
133impl<'view, 'certificate, 'document, N, D, G, I>
134 TimestampPaths<'view, 'certificate, 'document, N, D, G, I>
135{
136 #[must_use]
138 pub fn required(&self) -> &'view TimestampRequirements<N, D, G, I> {
139 self.required
140 }
141 #[must_use]
143 pub fn paths(
144 &self,
145 ) -> &'view [&'view TimestampPath<'view, 'certificate, 'document, N, D, G, I>] {
146 self.paths
147 }
148}
149
150#[derive(Debug, Clone, Copy, PartialEq, Eq)]
152pub enum TimestampPathFailure {
153 Count,
155 Source {
157 index: usize,
159 },
160}
161
162pub enum TimestampPathBinding<'view, 'certificate, 'document, N, D, G, I> {
164 Bound(TimestampPaths<'view, 'certificate, 'document, N, D, G, I>),
166 Unproven(TimestampPathFailure),
168}
169
170#[derive(Debug, Clone, Copy, PartialEq, Eq)]
172pub enum ObligationError<N> {
173 Scope(ScopeError<N>),
175 Foreign {
177 node: N,
179 },
180}
181
182impl<N> From<ScopeError<N>> for ObligationError<N> {
183 fn from(cause: ScopeError<N>) -> Self {
184 Self::Scope(cause)
185 }
186}
187
188pub type PathBindingResult<'view, 'certificate, 'document, N, D, G, I> =
190 Result<TimestampPathBinding<'view, 'certificate, 'document, N, D, G, I>, ObligationError<N>>;
191
192impl<N: Copy + Eq, D: Copy + Eq, G: Copy + Eq, I> TimestampRequirements<N, D, G, I> {
193 pub fn counter_in_scope<Q: Query<Id = N, Document = D, Generation = G>>(
197 &self,
198 ordinal: usize,
199 query: &Q,
200 ) -> Result<bool, ObligationError<N>> {
201 let node = self.scope.location().node;
202 if self.scope.location().snapshot != query.snapshot() {
203 return Err(ObligationError::Foreign { node });
204 }
205 query.admit(node, 2)?;
206 match self.scope {
207 TimestampScope::All(_) => Ok(true),
208 TimestampScope::BeforeArchive(_) => {
209 let Some(covered) = self.counters.as_ref().and_then(|c| c.covered.as_ref()) else {
210 return Err(ObligationError::Foreign { node });
211 };
212 covered
213 .get(ordinal)
214 .copied()
215 .ok_or(ObligationError::Foreign { node })
216 }
217 }
218 }
219
220 pub fn includes_counters<
222 'p,
223 'd,
224 C: CounterReceipts<'p, 'd, Id = N>,
225 Q: Query<Id = N, Document = D, Generation = G>,
226 >(
227 &self,
228 counters: &C,
229 query: &Q,
230 ) -> Result<bool, ScopeError<N>>
231 where
232 'd: 'p,
233 {
234 let Some(expected) = &self.counters else {
235 return Ok(counters.len() == 0);
236 };
237 if expected.signatures.len() != counters.len() {
238 return Ok(false);
239 }
240 for (index, expected) in expected.signatures.iter().enumerate() {
241 let node = counters.counter(index).signature;
242 if !query
243 .equal(*expected, node)
244 .map_err(|cause| ScopeError::Resource { node, cause })?
245 {
246 return Ok(false);
247 }
248 }
249 Ok(true)
250 }
251
252 pub fn bind_paths<'view, 'certificate, 'document, Q>(
256 &'view self,
257 paths: &'view [&'view TimestampPath<'view, 'certificate, 'document, N, D, G, I>],
258 query: &Q,
259 ) -> PathBindingResult<'view, 'certificate, 'document, N, D, G, I>
260 where
261 Q: Query<Id = N, Document = D, Generation = G>,
262 {
263 let location = self.scope.location();
264 if location.snapshot != query.snapshot() {
265 return Err(ObligationError::Foreign {
266 node: location.node,
267 });
268 }
269 query.admit(self.signature, 2)?;
271 if paths.len() != self.entries.len() {
272 return Ok(TimestampPathBinding::Unproven(TimestampPathFailure::Count));
273 }
274 let resource = |cause| ScopeError::Resource {
275 node: location.node,
276 cause,
277 };
278 for (index, path) in paths.iter().enumerate() {
279 query.admit(self.signature, 2)?;
280 let expected = &self.entries[index];
281 let actual = path.actor().verified();
282 if !query
283 .equal(actual.signature(), expected.signature)
284 .map_err(resource)?
285 || !query
286 .equal(actual.property(), expected.property)
287 .map_err(resource)?
288 || actual.token().snapshot != location.snapshot
289 || !query
290 .equal(actual.token().node, expected.token)
291 .map_err(resource)?
292 || actual.purpose() != expected.purpose
293 {
294 return Ok(TimestampPathBinding::Unproven(
295 TimestampPathFailure::Source { index },
296 ));
297 }
298 }
299 Ok(TimestampPathBinding::Bound(TimestampPaths {
300 required: self,
301 paths,
302 }))
303 }
304}