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