1use super::BasicStage;
4use crate::pki::SignerReceipt;
5use crate::source::{Query, ScopeError};
6use crate::timestamp::{
7 Purpose, SignatureTime, SignatureTimeBinding, SignatureTimeError, SignatureTimeFailure,
8 TimestampPaths, TimestampScope, existence_time_path, signature_time_path,
9};
10use structured_asn1::memory::{MemoryError, OwnedVec, Work};
11
12pub enum SignatureTimeFact<'a, S, N, D, G, I> {
15 Bound(SignatureTime<'a, S, N, D, G, I>),
17 Unproven {
19 property: N,
21 cause: SignatureTimeFailure,
23 },
24}
25
26pub struct Timed<'a, 'certificate, 'document, B, S, N, D, G, I> {
31 basic: &'a B,
32 paths: &'a TimestampPaths<'a, 'certificate, 'document, N, D, G, I>,
33 signatures: OwnedVec<SignatureTimeFact<'a, S, N, D, G, I>>,
34 static_complete: bool,
35}
36
37impl<'a, 'c, 'd, B, S, N, D, G, I> Timed<'a, 'c, 'd, B, S, N, D, G, I> {
38 #[must_use]
40 pub fn basic(&self) -> &'a B {
41 self.basic
42 }
43 #[must_use]
45 pub fn paths(&self) -> &'a TimestampPaths<'a, 'c, 'd, N, D, G, I> {
46 self.paths
47 }
48 #[must_use]
50 pub fn signatures(&self) -> &[SignatureTimeFact<'a, S, N, D, G, I>] {
51 &self.signatures
52 }
53 #[must_use]
57 pub fn all_static_time_requirements_met(&self) -> bool {
58 self.static_complete
59 }
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub enum TimeError<N> {
65 Foreign {
67 signature: N,
69 },
70 Scope(ScopeError<N>),
73 Signature(SignatureTimeError<N>),
75 Resource {
77 node: N,
79 cause: MemoryError,
81 },
82}
83
84pub type TimeResult<'a, 'c, 'd, B, S, Q, I> = Result<
86 Timed<'a, 'c, 'd, B, S, <Q as Query>::Id, <Q as Query>::Document, <Q as Query>::Generation, I>,
87 TimeError<<Q as Query>::Id>,
88>;
89
90pub fn timestamped<'a, 'c, 'd, 'path, 'document, B, Q, I>(
98 basic: &'a B,
99 paths: &'a TimestampPaths<'a, 'c, 'd, Q::Id, Q::Document, Q::Generation, I>,
100 query: &Q,
101) -> TimeResult<'a, 'c, 'd, B, B::Signer, Q, I>
102where
103 'document: 'path,
104 B: BasicStage<'path, 'document, Id = Q::Id, Document = Q::Document, Generation = Q::Generation>,
105 Q: Query,
106{
107 timed(basic, paths, None, query)
108}
109
110pub fn timestamped_existence<'a, 'c, 'd, 'path, 'document, B, Q, I>(
117 _issuer: I,
118 basic: &'a B,
119 paths: &'a TimestampPaths<'a, 'c, 'd, Q::Id, Q::Document, Q::Generation, I>,
120 archive: Option<Q::Id>,
121 query: &Q,
122) -> TimeResult<'a, 'c, 'd, B, B::Signer, Q, I>
123where
124 'document: 'path,
125 B: BasicStage<'path, 'document, Id = Q::Id, Document = Q::Document, Generation = Q::Generation>,
126 Q: Query,
127{
128 timed(basic, paths, archive, query)
129}
130
131fn timed<'a, 'c, 'd, 'path, 'document, B, Q, I>(
132 basic: &'a B,
133 paths: &'a TimestampPaths<'a, 'c, 'd, Q::Id, Q::Document, Q::Generation, I>,
134 archive: Option<Q::Id>,
135 query: &Q,
136) -> TimeResult<'a, 'c, 'd, B, B::Signer, Q, I>
137where
138 'document: 'path,
139 B: BasicStage<'path, 'document, Id = Q::Id, Document = Q::Document, Generation = Q::Generation>,
140 Q: Query,
141{
142 let selected = basic.signer().signature();
143 let signature = selected.node;
144 let required = paths.required();
145 let resource = |cause| TimeError::Resource {
146 node: signature,
147 cause,
148 };
149 if !matches!(required.scope(), TimestampScope::All(_))
150 || required.scope().location().snapshot != query.snapshot()
151 || selected.snapshot != query.snapshot()
152 || !query
153 .equal(required.signature(), signature)
154 .map_err(resource)?
155 || !required
156 .includes_counters(basic.counters(), query)
157 .map_err(TimeError::Scope)?
158 {
159 return Err(TimeError::Foreign { signature });
160 }
161 query.admit(signature, 1).map_err(TimeError::Scope)?;
162 let mut signature_written = false;
163 if let Some(archive) = archive {
164 let mut archive_written = false;
165 for entry in required.entries() {
166 query.admit(entry.property, 1).map_err(TimeError::Scope)?;
167 if !query.equal(entry.signature, signature).map_err(resource)? {
168 continue;
169 }
170 if entry.purpose == Purpose::Signature {
171 signature_written = true;
172 }
173 if entry.purpose == Purpose::Archive
174 && query.equal(entry.property, archive).map_err(resource)?
175 {
176 archive_written = true;
177 }
178 }
179 if !archive_written {
180 return Err(TimeError::Foreign { signature });
181 }
182 }
183 let mut signatures = query.execution().vector();
184 let mut static_complete = basic.all_requirements_met();
185 for path in paths.paths() {
186 query.execution().charge_work(1).map_err(resource)?;
187 let verified = path.actor().verified();
188 if verified.purpose() != Purpose::Signature && archive.is_none() {
189 continue;
190 }
191 if !query
192 .equal(verified.signature(), signature)
193 .map_err(resource)?
194 {
195 continue;
196 }
197 let fallback = !signature_written
198 && verified.purpose() == Purpose::Archive
199 && match archive {
200 Some(archive) => query
201 .equal(verified.property(), archive)
202 .map_err(resource)?,
203 None => false,
204 };
205 if verified.purpose() != Purpose::Signature && !fallback {
206 continue;
207 }
208 signatures.try_reserve(1).map_err(resource)?;
209 let association = if fallback {
210 existence_time_path(basic.signer(), path, query)
211 } else {
212 signature_time_path(basic.signer(), path, query)
213 };
214 let fact = match association.map_err(TimeError::Signature)? {
215 SignatureTimeBinding::Bound(value) => SignatureTimeFact::Bound(value),
216 SignatureTimeBinding::Unproven(cause) => {
217 static_complete = false;
218 SignatureTimeFact::Unproven {
219 property: verified.property(),
220 cause,
221 }
222 }
223 };
224 signatures.try_push(fact).map_err(resource)?;
225 }
226 if signatures.is_empty() {
227 static_complete = false;
228 }
229 query.admit(signature, 1).map_err(TimeError::Scope)?;
230 Ok(Timed {
231 basic,
232 paths,
233 signatures: signatures.into_owned(),
234 static_complete,
235 })
236}