1use crate::{
7 Cades, Level,
8 basic::{Basic, SignerBinding},
9 material::{self, Placement},
10 prepare::Preparation,
11 source::{DocumentId, Node},
12};
13use structured_asn1::memory::Work;
14use structured_ltv::baseline::{ArchiveMaterial, Archives, CredentialMaterial, LongTerm, Timed};
15
16pub use structured_ltv::baseline::{archive_material, archives, long_term, timestamped};
17
18pub type BasicOf<'a, 'm, 'd> = Basic<'a, 'a, 'm, 'd, 'a>;
20pub type TimedOf<'a, 'm, 'd> = Timed<
22 'a,
23 'a,
24 'd,
25 BasicOf<'a, 'm, 'd>,
26 SignerBinding<'a, 'm, 'd, 'a>,
27 Node,
28 DocumentId<'m, 'd>,
29 (),
30 Cades,
31>;
32pub type CredentialsOf<'a, 'm, 'd, E> = material::credentials::Report<'a, 'a, 'm, 'd, 'a, E>;
34pub type LongTermOf<'a, 'm, 'd, E> = LongTerm<
36 'a,
37 TimedOf<'a, 'm, 'd>,
38 material::Checks<'a, 'a, 'a, 'a, 'd, 'm>,
39 CredentialsOf<'a, 'm, 'd, E>,
40>;
41pub type ArchivesOf<'a, 'm, 'd, E> = Archives<
43 'a,
44 Preparation<'m, 'd>,
45 ArchiveMaterial<
46 'a,
47 structured_ltv::material::ParticipantMaterial<
48 'a,
49 'a,
50 'a,
51 'a,
52 'd,
53 Node,
54 DocumentId<'m, 'd>,
55 (),
56 Placement,
57 Cades,
58 >,
59 CredentialsOf<'a, 'm, 'd, E>,
60 >,
61>;
62
63pub enum Evidence<'a, 'm, 'd, E> {
67 Basic(&'a BasicOf<'a, 'm, 'd>),
69 Timestamped(&'a TimedOf<'a, 'm, 'd>),
71 LongTerm(&'a LongTermOf<'a, 'm, 'd, E>),
73 Archival {
75 long_term: &'a LongTermOf<'a, 'm, 'd, E>,
77 archives: &'a ArchivesOf<'a, 'm, 'd, E>,
79 },
80}
81impl<'a, 'm, 'd> Evidence<'a, 'm, 'd, ()> {
82 #[must_use]
84 pub fn basic(report: &'a BasicOf<'a, 'm, 'd>) -> Self {
85 Self::Basic(report)
86 }
87 #[must_use]
89 pub fn timestamped(report: &'a TimedOf<'a, 'm, 'd>) -> Self {
90 Self::Timestamped(report)
91 }
92}
93impl<'m, 'd, E> Evidence<'_, 'm, 'd, E> {
94 #[must_use]
96 pub fn level(&self) -> Level {
97 match self {
98 Self::Basic(_) => Level::Basic,
99 Self::Timestamped(_) => Level::Timestamped,
100 Self::LongTerm(_) => Level::LongTerm,
101 Self::Archival { .. } => Level::Archival,
102 }
103 }
104 fn prepared(&self) -> &Preparation<'m, 'd> {
105 match self {
106 Self::Basic(basic) => basic.binding().prepared(),
107 Self::Timestamped(timed) => timed.basic().binding().prepared(),
108 Self::LongTerm(long)
109 | Self::Archival {
110 long_term: long, ..
111 } => long.timed().basic().binding().prepared(),
112 }
113 }
114}
115
116pub struct Assessment<'a, 'm, 'd, E> {
121 evidence: Evidence<'a, 'm, 'd, E>,
122 met: bool,
123}
124impl<'a, 'm, 'd, E> Assessment<'a, 'm, 'd, E> {
125 #[must_use]
127 pub fn requested(&self) -> Level {
128 self.evidence.level()
129 }
130 #[must_use]
132 pub fn evidence(&self) -> &Evidence<'a, 'm, 'd, E> {
133 &self.evidence
134 }
135 #[must_use]
137 pub fn all_requirements_met(&self) -> bool {
138 self.met
139 }
140}
141
142#[derive(Debug, Clone, Copy, PartialEq, Eq)]
144pub enum LevelError {
145 UnavailablePreservation {
148 property: Node,
150 kind: crate::preservation::Kind,
152 },
153 UnavailablePreservationSource {
155 source: Node,
157 },
158 Mismatch {
160 requested: Level,
162 supplied: Level,
164 },
165 Foreign,
167 Resource(structured_asn1::memory::MemoryError),
169}
170
171pub fn assess<'a, 'm, 'd, E>(
178 evidence: Evidence<'a, 'm, 'd, E>,
179 operation: &structured_asn1::Reader<'_>,
180) -> Result<Assessment<'a, 'm, 'd, E>, LevelError> {
181 let prepared = evidence.prepared();
182 let requested = prepared.requested();
183 let supplied = evidence.level();
184 if requested != supplied {
185 return Err(LevelError::Mismatch {
186 requested,
187 supplied,
188 });
189 }
190 prepared
191 .document()
192 .message()
193 .require_session(operation, "cades.baseline")
194 .map_err(|_| LevelError::Foreign)?;
195 let work = operation.execution();
196 work.charge_work(1).map_err(LevelError::Resource)?;
197 if requested >= Level::Timestamped {
198 for required in prepared.preservation() {
199 work.charge_work(1).map_err(LevelError::Resource)?;
200 if !required.kind.is_reference_timestamp() && !required.kind.is_archive() {
205 return Err(LevelError::UnavailablePreservation {
206 property: required.property,
207 kind: required.kind,
208 });
209 }
210 }
211 if !prepared.preservation().is_empty() {
212 for finding in prepared.preservation_findings() {
213 work.charge_work(1).map_err(LevelError::Resource)?;
214 if finding.severity == crate::prepare::Severity::Required {
215 return Err(LevelError::UnavailablePreservationSource {
216 source: finding.location.node,
217 });
218 }
219 }
220 }
221 }
222 let met = match &evidence {
223 Evidence::Basic(basic) => basic.all_basic_requirements_met(),
224 Evidence::Timestamped(timed) => timed_met(timed),
225 Evidence::LongTerm(long) => long_met(long),
226 Evidence::Archival {
227 long_term,
228 archives,
229 } => {
230 work.charge_work(1).map_err(LevelError::Resource)?;
231 if !core::ptr::eq(archives.census(), prepared) {
232 return Err(LevelError::Foreign);
233 }
234 let credentials = long_term.credentials().credentials();
235 for archive in archives.reports() {
236 work.charge_work(1).map_err(LevelError::Resource)?;
237 if !core::ptr::eq(archive.credentials().credentials(), credentials) {
238 return Err(LevelError::Foreign);
239 }
240 }
241 long_met(long_term) && archives.all_archive_material_covered()
242 }
243 };
244 let met = met && prepared.all_format_requirements_met();
245 Ok(Assessment { evidence, met })
246}
247
248fn timed_met(timed: &TimedOf<'_, '_, '_>) -> bool {
249 timed.basic().all_basic_requirements_met() && timed.all_static_time_requirements_met()
250}
251fn long_met<E>(long: &LongTermOf<'_, '_, '_, E>) -> bool {
252 timed_met(long.timed()) && long.all_material_requirements_met()
253}