1use super::{
4 Agreement, Catalogue, CertificateXml, CheckedCertificate, DigestError, DigestProvider,
5 Expected, References,
6};
7use crate::binary::{self, BinaryXml, Location};
8use crate::xml::{Access, Xml};
9use structured_asn1::memory::{OwnedVec, Work};
10use structured_ltv::material::PathCertificate;
11use structured_ltv::pki::{CheckedPath, SignerReceipt};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct PathDigest {
16 pub reference: usize,
18 pub agreement: Agreement,
20 pub certificate: Option<PathCertificate>,
22}
23
24pub struct PathDigests<'view, 'document, N, D, G> {
28 references: &'view References<N, D, G>,
29 path: &'view CheckedPath<'document>,
30 entries: OwnedVec<PathDigest>,
31 all_match: bool,
32}
33
34pub type PathDigestResult<'view, 'document, X, PE> = binary::BinaryResult<
36 PathDigests<'view, 'document, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>,
37 X,
38 DigestError<PE>,
39>;
40
41pub struct CheckedPathCertificate<'view, 'document, N, D, G> {
45 selected: CheckedCertificate<'view, 'document, N, D, G>,
46 digests: PathDigests<'view, 'document, N, D, G>,
47}
48
49impl<'view, 'document, N, D, G> CheckedPathCertificate<'view, 'document, N, D, G> {
50 #[must_use]
52 pub fn selected(&self) -> &CheckedCertificate<'view, 'document, N, D, G> {
53 &self.selected
54 }
55 #[must_use]
57 pub fn digests(&self) -> &PathDigests<'view, 'document, N, D, G> {
58 &self.digests
59 }
60 #[must_use]
63 pub fn is_bound(&self) -> bool {
64 self.selected.is_bound() && self.digests.all_match()
65 }
66}
67
68impl<'view, 'document, N: Copy, D: Copy, G: Copy> SignerReceipt<'view, 'document>
70 for CheckedPathCertificate<'view, 'document, N, D, G>
71{
72 type Id = N;
73 type Document = D;
74 type Generation = G;
75
76 fn signature(&self) -> Location<N, D, G> {
77 let references = self.selected.reference();
78 Location {
79 snapshot: references.snapshot(),
80 node: references.signature(),
81 }
82 }
83
84 fn path(&self) -> Option<&'view CheckedPath<'document>> {
85 self.is_bound().then_some(self.digests.path)
86 }
87}
88
89pub type PathCheckResult<'view, 'document, X, PE> = super::CertificateResult<
91 CheckedPathCertificate<
92 'view,
93 'document,
94 <X as Xml>::Id,
95 <X as Xml>::Document,
96 <X as Xml>::Generation,
97 >,
98 X,
99 PE,
100>;
101
102pub fn check_path<
112 'view,
113 'document,
114 'p,
115 X: BinaryXml + CertificateXml,
116 P: DigestProvider<'p, X>,
117 W: Work,
118>(
119 xml: &X,
120 bound: &crate::binding::Bound<X::Id, X::Document, X::Generation>,
121 references: &'view Catalogue<X>,
122 path: &'view CheckedPath<'document>,
123 provider: &'p P,
124 execution: &W,
125) -> PathCheckResult<'view, 'document, X, P::Error> {
126 let certificate = path
127 .members()
128 .last()
129 .expect("a checked path has a target")
130 .certificate();
131 let key = super::bind_key::<X, P::Error, W>(
132 xml,
133 bound,
134 references,
135 certificate,
136 path.facts().key,
137 execution,
138 )?;
139 let digests = match_path(xml, references, path, provider, execution)
140 .map_err(super::CheckError::Digest)?;
141 let digest = digests
142 .entries
143 .first()
144 .expect("certificate references are nonempty")
145 .agreement;
146 Ok(CheckedPathCertificate {
147 selected: CheckedCertificate {
148 reference: references,
149 certificate,
150 key,
151 digest,
152 },
153 digests,
154 })
155}
156
157impl<'view, 'document, N, D, G> PathDigests<'view, 'document, N, D, G> {
158 #[must_use]
160 pub fn references(&self) -> &'view References<N, D, G> {
161 self.references
162 }
163 #[must_use]
165 pub fn path(&self) -> &'view CheckedPath<'document> {
166 self.path
167 }
168 #[must_use]
170 pub fn entries(&self) -> &[PathDigest] {
171 &self.entries
172 }
173 #[must_use]
176 pub fn all_match(&self) -> bool {
177 self.all_match
178 }
179}
180
181pub fn match_path<'view, 'document, 'p, X: BinaryXml, P: DigestProvider<'p, X>, W: Work>(
200 xml: &X,
201 references: &'view Catalogue<X>,
202 path: &'view CheckedPath<'document>,
203 provider: &'p P,
204 execution: &W,
205) -> PathDigestResult<'view, 'document, X, P::Error> {
206 let access = Access::new(xml, references.snapshot(), execution);
207 let location = Location {
208 snapshot: references.snapshot(),
209 node: references.node(),
210 };
211 access
212 .query(references.node(), |_, _, _| Ok(()))
213 .map_err(|cause| binary::Error::Access { location, cause })?;
214 let mut entries = execution.vector();
215 entries
216 .try_reserve_exact(references.entries().len())
217 .map_err(|cause| binary::Error::Callback {
218 location,
219 cause: DigestError::Resource(cause),
220 })?;
221 let mut all_match = true;
222 for ordinal in 0..references.entries().len() {
223 execution
224 .charge_work(1)
225 .map_err(|cause| binary::Error::Callback {
226 location,
227 cause: DigestError::Resource(cause),
228 })?;
229 let reference = &references.entries()[ordinal];
230 let answer = if let Some(answer) = super::preflight_method(&access, reference.method)
231 .map_err(|cause| binary::Error::Access {
232 location: Location {
233 node: reference.method,
234 ..location
235 },
236 cause,
237 })? {
238 PathDigest {
239 reference: ordinal,
240 agreement: answer,
241 certificate: None,
242 }
243 } else {
244 binary::with_octets(
245 xml,
246 Location {
247 node: reference.digest,
248 ..location
249 },
250 execution,
251 |expected| {
252 Ok::<_, core::convert::Infallible>(match_reference(
253 &access, reference, ordinal, path, provider, expected,
254 ))
255 },
256 )
257 .map_err(|error| error.map_callback(|never| match never {}))??
258 };
259 if answer.agreement != Agreement::Matches {
260 all_match = false;
261 }
262 entries
263 .try_push(answer)
264 .map_err(|cause| binary::Error::Callback {
265 location,
266 cause: DigestError::Resource(cause),
267 })?;
268 }
269 Ok(PathDigests {
270 references,
271 path,
272 entries: entries.into_owned(),
273 all_match,
274 })
275}
276
277fn match_reference<'p, X: Xml, P: DigestProvider<'p, X>, W: Work>(
278 access: &Access<'_, X, W>,
279 reference: &super::Reference<X::Id>,
280 ordinal: usize,
281 path: &CheckedPath<'_>,
282 provider: &'p P,
283 expected: &[u8],
284) -> binary::BinaryResult<PathDigest, X, DigestError<P::Error>> {
285 let identity = match super::resolve_expected(access, reference, provider, expected)? {
286 Expected::Ready(identity) => identity,
287 Expected::Answer(agreement) => {
288 return Ok(PathDigest {
289 reference: ordinal,
290 agreement,
291 certificate: None,
292 });
293 }
294 };
295 let mut answer = PathDigest {
296 reference: ordinal,
297 agreement: Agreement::Differs,
298 certificate: None,
299 };
300 let admit = || {
301 access
302 .execution()
303 .charge_work(1)
304 .map_err(|cause| binary::Error::Callback {
305 location: Location {
306 snapshot: access.snapshot(),
307 node: reference.node,
308 },
309 cause: DigestError::<P::Error>::Resource(cause),
310 })
311 };
312 let members = path.members();
313 if ordinal == 0 {
314 admit()?;
315 let target = members.last().expect("a checked path has a target");
316 answer.agreement = super::compare_resolved(
317 access,
318 reference,
319 target.certificate(),
320 provider,
321 &identity,
322 expected,
323 )?;
324 if answer.agreement == Agreement::Matches {
325 answer.certificate = Some(PathCertificate::Member(members.len() - 1));
326 }
327 return Ok(answer);
328 }
329 for (index, member) in members.iter().enumerate() {
330 admit()?;
331 match super::compare_resolved(
332 access,
333 reference,
334 member.certificate(),
335 provider,
336 &identity,
337 expected,
338 )? {
339 Agreement::Matches => {
340 answer.agreement = Agreement::Matches;
341 answer.certificate = Some(PathCertificate::Member(index));
342 return Ok(answer);
343 }
344 Agreement::Differs => {}
345 other => {
348 answer.agreement = other;
349 return Ok(answer);
350 }
351 }
352 }
353 admit()?;
354 if let Some(certificate) = path.anchor().certificate() {
355 let agreement = super::compare_resolved(
356 access,
357 reference,
358 certificate,
359 provider,
360 &identity,
361 expected,
362 )?;
363 if agreement != Agreement::Differs {
364 answer.agreement = agreement;
365 }
366 if agreement == Agreement::Matches {
367 answer.certificate = Some(PathCertificate::Anchor);
368 }
369 }
370 Ok(answer)
371}