1use super::{ArchiveCensus, CredentialMaterial};
4use crate::material::ParticipantMaterial;
5use crate::pki::ParticipantError;
6use crate::source::{Query, ScopeError};
7use crate::timestamp::TimestampScope;
8use structured_asn1::memory::{MemoryError, Work};
9
10pub struct ArchiveMaterial<'a, M, C> {
18 x509: &'a M,
19 credentials: &'a C,
20 covered: bool,
21}
22
23impl<M, C> ArchiveMaterial<'_, M, C> {
24 #[must_use]
26 pub fn x509(&self) -> &M {
27 self.x509
28 }
29 #[must_use]
31 pub fn credentials(&self) -> &C {
32 self.credentials
33 }
34 #[must_use]
37 pub fn all_material_covered(&self) -> bool {
38 self.covered
39 }
40}
41
42type X509<'a, 'r, 'v, 'c, 'd, P, Q, I> = ParticipantMaterial<
43 'a,
44 'r,
45 'v,
46 'c,
47 'd,
48 <Q as Query>::Id,
49 <Q as Query>::Document,
50 <Q as Query>::Generation,
51 P,
52 I,
53>;
54
55pub type ArchiveMaterialResult<'a, M, C, N> =
57 Result<ArchiveMaterial<'a, M, C>, ParticipantError<N>>;
58
59pub fn archive_material<'s, 'a, 'r, 'v, 'c, 'd, P, C, Q, I>(
69 x509: &'s X509<'a, 'r, 'v, 'c, 'd, P, Q, I>,
70 credentials: &'s C,
71 query: &Q,
72) -> ArchiveMaterialResult<'s, X509<'a, 'r, 'v, 'c, 'd, P, Q, I>, C, Q::Id>
73where
74 C: CredentialMaterial<Id = Q::Id, Document = Q::Document, Generation = Q::Generation>,
75 Q: Query,
76{
77 let required = x509.inputs().requirements();
78 let scope = required.scope();
79 let original = scope.location();
80 let supplied = credentials.scope();
81 let foreign = || ParticipantError::Foreign {
82 signature: required.signature(),
83 };
84 if !matches!(scope, TimestampScope::BeforeArchive(_))
85 || !matches!(supplied, TimestampScope::BeforeArchive(_))
86 || original.snapshot != query.snapshot()
87 || supplied.location().snapshot != original.snapshot
88 || credentials.snapshot() != original.snapshot
89 {
90 return Err(foreign());
91 }
92 query.admit(original.node, 2)?;
94 if !required.same_credentials(credentials.credentials())
95 || !query
96 .equal(original.node, supplied.location().node)
97 .map_err(|cause| ParticipantError::Resource {
98 node: original.node,
99 cause,
100 })?
101 {
102 return Err(foreign());
103 }
104 Ok(ArchiveMaterial {
105 x509,
106 credentials,
107 covered: x509.all_x509_artifacts_covered() && credentials.all_requirements_met(),
108 })
109}
110
111pub struct Archives<'a, P, R> {
116 census: &'a P,
117 reports: &'a [&'a R],
118 covered: bool,
119}
120
121impl<P, R> Archives<'_, P, R> {
122 #[must_use]
124 pub fn census(&self) -> &P {
125 self.census
126 }
127 #[must_use]
129 pub fn reports(&self) -> &[&R] {
130 self.reports
131 }
132 #[must_use]
135 pub fn all_archive_material_covered(&self) -> bool {
136 self.covered
137 }
138}
139
140#[derive(Debug, Clone, Copy, PartialEq, Eq)]
142pub enum ArchiveCensusError<N> {
143 Foreign {
145 node: N,
147 },
148 Missing {
150 property: N,
152 },
153 Extra,
155 Scope(ScopeError<N>),
157 Resource {
159 node: N,
161 cause: MemoryError,
163 },
164}
165
166pub type ArchivesResult<'a, P, R, N> = Result<Archives<'a, P, R>, ArchiveCensusError<N>>;
168
169type Proof<'a, 'cl, 'r, 'v, 'c, 'd, P, C, Q, I> =
170 ArchiveMaterial<'a, X509<'cl, 'r, 'v, 'c, 'd, P, Q, I>, C>;
171
172pub fn archives<'s, 'a, 'cl, 'r, 'v, 'c, 'd, A, P, C, Q, I>(
178 census: &'s A,
179 reports: &'s [&'s Proof<'a, 'cl, 'r, 'v, 'c, 'd, P, C, Q, I>],
180 query: &Q,
181) -> ArchivesResult<'s, A, Proof<'a, 'cl, 'r, 'v, 'c, 'd, P, C, Q, I>, Q::Id>
182where
183 A: ArchiveCensus<Id = Q::Id, Document = Q::Document, Generation = Q::Generation>,
184 Q: Query,
185{
186 let selected = census.signature();
187 let signature = selected.node;
188 if selected.snapshot != query.snapshot() {
189 return Err(ArchiveCensusError::Foreign { node: signature });
190 }
191 query
192 .admit(signature, 1)
193 .map_err(ArchiveCensusError::Scope)?;
194 let mut cursor = 0;
195 let mut covered = true;
196 for ordinal in 0..census.timestamps() {
197 query
198 .execution()
199 .charge_work(1)
200 .map_err(|cause| ArchiveCensusError::Resource {
201 node: signature,
202 cause,
203 })?;
204 let Some(node) = census.archive(ordinal) else {
205 continue;
206 };
207 let resource = |cause| ArchiveCensusError::Resource { node, cause };
208 let Some(report) = reports.get(cursor) else {
209 return Err(ArchiveCensusError::Missing { property: node });
210 };
211 let required = report.x509.inputs().requirements();
212 let location = required.scope().location();
213 if location.snapshot != query.snapshot()
214 || !query
215 .equal(required.signature(), signature)
216 .map_err(resource)?
217 || !query.equal(location.node, node).map_err(resource)?
218 {
219 return Err(ArchiveCensusError::Foreign { node });
220 }
221 if !report.covered {
222 covered = false;
223 }
224 cursor += 1;
225 }
226 if cursor != reports.len() {
227 return Err(ArchiveCensusError::Extra);
228 }
229 if cursor == 0 {
230 covered = false;
231 }
232 query
233 .admit(signature, 1)
234 .map_err(ArchiveCensusError::Scope)?;
235 Ok(Archives {
236 census,
237 reports,
238 covered,
239 })
240}