1use crate::binary::{self, BinaryXml, Location, OwnedBinaryXml};
9use crate::certificate::{self, Agreement, DigestProvider};
10use crate::xml::{Access, Snapshot, Xml};
11use structured_asn1::digest::{Digest, Resolution};
12use structured_asn1::memory::{MemoryError, OwnedVec, Work};
13use structured_cms::stream::{self, Sink, Source, StreamError};
14
15mod batch;
16pub use batch::Batch;
17
18#[derive(Debug, Clone, Copy)]
21pub struct Reference<N, D, G> {
22 pub snapshot: Snapshot<D, G>,
24 pub method: N,
26 pub value: N,
28}
29
30#[derive(Debug)]
32pub enum Error<SE, PE> {
33 ForeignDigest,
35 Unstreamable,
37 Source(SE),
39 Provider(PE),
41 Resource(MemoryError),
43 Stream(structured_cms::Error),
47 OutputWidth {
50 expected: usize,
52 actual: usize,
54 },
55}
56
57impl<SE, PE> Error<SE, PE> {
58 pub fn map_source<E>(self, map: impl FnOnce(SE) -> E) -> Error<E, PE> {
61 match self {
62 Self::ForeignDigest => Error::ForeignDigest,
63 Self::Unstreamable => Error::Unstreamable,
64 Self::Source(cause) => Error::Source(map(cause)),
65 Self::Provider(cause) => Error::Provider(cause),
66 Self::Resource(cause) => Error::Resource(cause),
67 Self::Stream(cause) => Error::Stream(cause),
68 Self::OutputWidth { expected, actual } => Error::OutputWidth { expected, actual },
69 }
70 }
71}
72
73struct Updating<'s, 'p, P: Digest<'p>> {
74 provider: &'p P,
75 state: &'s mut P::State,
76}
77
78impl<'p, P: Digest<'p>> Sink for Updating<'_, 'p, P> {
79 type Error = P::Error;
80 fn write<W: Work>(&mut self, bytes: &[u8], execution: &W) -> Result<(), Self::Error> {
81 self.provider.update(self.state, bytes, execution)
82 }
83}
84
85pub fn check<'p, X: BinaryXml, P: DigestProvider<'p, X>, S: Source, W: Work>(
91 xml: &X,
92 reference: Reference<X::Id, X::Document, X::Generation>,
93 source: &mut S,
94 provider: &'p P,
95 execution: &W,
96) -> binary::BinaryResult<Agreement, X, Error<S::Error, P::Error>> {
97 check_with(
98 xml,
99 reference,
100 provider,
101 execution,
102 |identity, expected, work| {
103 run_resolved::<P, S, W>(&identity, expected, source, provider, work)
104 },
105 )
106 .map(|reading| match reading {
107 Checked::Answer(answer) | Checked::Value(answer) => answer,
108 })
109}
110
111pub async fn check_async<'p, X: OwnedBinaryXml, P: DigestProvider<'p, X>, S, W: Work>(
115 xml: &X,
116 reference: Reference<X::Id, X::Document, X::Generation>,
117 source: &mut S,
118 provider: &'p P,
119 execution: &W,
120) -> binary::BinaryResult<Agreement, X, Error<S::Error, P::Error>>
121where
122 S: stream::AsyncSource,
123{
124 check_async_with::<true, X, P, S, W>(xml, reference, source, provider, execution).await
125}
126
127pub async fn check_xml_async<'p, X: OwnedBinaryXml, P: DigestProvider<'p, X>, S, W: Work>(
131 xml: &X,
132 reference: Reference<X::Id, X::Document, X::Generation>,
133 source: &mut S,
134 provider: &'p P,
135 execution: &W,
136) -> binary::BinaryResult<Agreement, X, Error<S::Error, P::Error>>
137where
138 S: stream::AsyncSource,
139{
140 check_async_with::<false, X, P, S, W>(xml, reference, source, provider, execution).await
141}
142
143pub fn check_xml_finished<'p, X: BinaryXml, P: DigestProvider<'p, X>, W: Work>(
150 xml: &X,
151 reference: Reference<X::Id, X::Document, X::Generation>,
152 actual: structured_cms::provider::Finished<'_, P, P::Identity, P::Output>,
153 execution: &W,
154) -> binary::BinaryResult<Agreement, X, Error<core::convert::Infallible, P::Error>> {
155 let access = Access::new(xml, reference.snapshot, execution);
156 if let Some(answer) = preflight(&access, reference)? {
157 return Ok(answer);
158 }
159 let location = Location {
160 snapshot: reference.snapshot,
161 node: reference.value,
162 };
163 binary::with_octets(xml, location, execution, |expected| {
164 Ok::<_, core::convert::Infallible>(compare_finished(&access, reference, actual, expected))
165 })
166 .map_err(|error| error.map_callback(|never| match never {}))?
167}
168
169fn compare_finished<'p, X: BinaryXml, P: DigestProvider<'p, X>, W: Work>(
170 access: &Access<'_, X, W>,
171 reference: Reference<X::Id, X::Document, X::Generation>,
172 actual: structured_cms::provider::Finished<'_, P, P::Identity, P::Output>,
173 expected: &[u8],
174) -> binary::BinaryResult<Agreement, X, Error<core::convert::Infallible, P::Error>> {
175 let provider = actual.provider();
176 let identity = match resolve_method(access, reference, provider)? {
177 Resolution::Supported(identity) => identity,
178 Resolution::Unsupported => return Ok(Agreement::Unsupported),
179 Resolution::Malformed => return Ok(Agreement::MalformedMethod),
180 };
181 let identity = match certificate::resolved_digest::<false, X, P>(identity, provider) {
182 Ok(identity) => identity,
183 Err(answer) => return Ok(answer),
184 };
185 compare_resolved_finished(access, reference, provider, &identity, actual, expected)
186}
187
188fn compare_resolved_finished<'p, X: Xml, P: Digest<'p>, W: Work>(
189 access: &Access<'_, X, W>,
190 reference: Reference<X::Id, X::Document, X::Generation>,
191 provider: &P,
192 identity: &P::Identity,
193 actual: structured_cms::provider::Finished<'_, P, P::Identity, P::Output>,
194 expected: &[u8],
195) -> binary::BinaryResult<Agreement, X, Error<core::convert::Infallible, P::Error>> {
196 let width = provider.width(identity);
197 if let Some(width) = width
198 && expected.len() != width
199 {
200 return Ok(Agreement::MalformedValue);
201 }
202 let location = Location {
203 snapshot: reference.snapshot,
204 node: reference.value,
205 };
206 access
207 .query_fallible(reference.value, |_, _, work| {
208 work.charge_work(1).map_err(Error::Resource)?;
209 if provider
210 .compare(identity, actual.identity(), work)
211 .map_err(Error::Provider)?
212 != core::cmp::Ordering::Equal
213 {
214 return Ok(Agreement::Differs);
215 }
216 if let Some(width) = width
217 && actual.output().as_ref().len() != width
218 {
219 return Err(Error::OutputWidth {
220 expected: width,
221 actual: actual.output().as_ref().len(),
222 });
223 }
224 let agrees = provider
225 .matches(identity, actual.output(), expected, work)
226 .map_err(Error::Provider)?;
227 Ok(if agrees {
228 Agreement::Matches
229 } else {
230 Agreement::Differs
231 })
232 })
233 .map_err(|cause| match cause {
234 crate::xml::AccessError::Xml(cause) => binary::Error::Callback { location, cause },
235 crate::xml::AccessError::Changed { node } => binary::Error::Access {
236 location,
237 cause: crate::xml::AccessError::Changed { node },
238 },
239 crate::xml::AccessError::Resource { node, cause } => binary::Error::Access {
240 location,
241 cause: crate::xml::AccessError::Resource { node, cause },
242 },
243 })
244}
245
246enum PreparedValue<I> {
247 Answer(Agreement),
248 Ready { identity: I, expected: OwnedVec<u8> },
249}
250
251pub struct Prepared<'p, P: Digest<'p>, N, D, G> {
255 provider: &'p P,
256 reference: Reference<N, D, G>,
257 value: PreparedValue<P::Identity>,
258}
259
260pub type PreparationResult<'p, P, X> = binary::BinaryResult<
265 Prepared<'p, P, <X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>,
266 X,
267 Error<core::convert::Infallible, <P as Digest<'p>>::Error>,
268>;
269
270pub fn prepare_xml<'p, X: OwnedBinaryXml, P: DigestProvider<'p, X>, W: Work>(
272 xml: &X,
273 reference: Reference<X::Id, X::Document, X::Generation>,
274 provider: &'p P,
275 execution: &W,
276) -> PreparationResult<'p, P, X> {
277 let access = Access::new(xml, reference.snapshot, execution);
278 let value = if let Some(answer) = preflight(&access, reference)? {
279 PreparedValue::Answer(answer)
280 } else {
281 match resolve_method(&access, reference, provider)? {
282 Resolution::Unsupported => PreparedValue::Answer(Agreement::Unsupported),
283 Resolution::Malformed => PreparedValue::Answer(Agreement::MalformedMethod),
284 Resolution::Supported(identity) => {
285 match certificate::resolved_digest::<false, X, P>(identity, provider) {
286 Err(answer) => PreparedValue::Answer(answer),
287 Ok(identity) => {
288 let location = Location {
289 snapshot: reference.snapshot,
290 node: reference.value,
291 };
292 let expected = binary::owned(xml, location, execution)
293 .map_err(|cause| binary::Error::Access { location, cause })?
294 .value;
295 if let Some(width) = provider.width(&identity)
296 && expected.len() != width
297 {
298 PreparedValue::Answer(Agreement::MalformedValue)
299 } else {
300 PreparedValue::Ready { identity, expected }
301 }
302 }
303 }
304 }
305 }
306 };
307 Ok(Prepared {
308 provider,
309 reference,
310 value,
311 })
312}
313
314impl<'p, P: Digest<'p>, N: Copy + Eq, D: Copy + Eq, G: Copy + Eq> Prepared<'p, P, N, D, G> {
315 pub fn check_finished<X: Xml<Id = N, Document = D, Generation = G>, W: Work>(
319 &self,
320 xml: &X,
321 actual: structured_cms::provider::Finished<'_, P, P::Identity, P::Output>,
322 execution: &W,
323 ) -> binary::BinaryResult<Agreement, X, Error<core::convert::Infallible, P::Error>> {
324 let access = Access::new(xml, self.reference.snapshot, execution);
325 let location = Location {
326 snapshot: self.reference.snapshot,
327 node: self.reference.value,
328 };
329 access
330 .inspect(self.reference.value, |_| ())
331 .map_err(|cause| binary::Error::Access { location, cause })?;
332 if !actual.from_provider(self.provider) {
333 return Err(binary::Error::Callback {
334 location,
335 cause: Error::ForeignDigest,
336 });
337 }
338 match &self.value {
339 PreparedValue::Answer(answer) => Ok(*answer),
340 PreparedValue::Ready { identity, expected } => compare_resolved_finished(
341 &access,
342 self.reference,
343 self.provider,
344 identity,
345 actual,
346 expected,
347 ),
348 }
349 }
350}
351
352async fn check_async_with<
353 'p,
354 const BASELINE: bool,
355 X: OwnedBinaryXml,
356 P: DigestProvider<'p, X>,
357 S: stream::AsyncSource,
358 W: Work,
359>(
360 xml: &X,
361 reference: Reference<X::Id, X::Document, X::Generation>,
362 source: &mut S,
363 provider: &'p P,
364 execution: &W,
365) -> binary::BinaryResult<Agreement, X, Error<S::Error, P::Error>> {
366 let access = Access::new(xml, reference.snapshot, execution);
367 if let Some(answer) = preflight::<X, W, S::Error, P::Error>(&access, reference)? {
368 return Ok(answer);
369 }
370 let location = Location {
371 snapshot: reference.snapshot,
372 node: reference.value,
373 };
374 let expected = access
375 .query(reference.value, |xml, snapshot, work| {
376 xml.decode_owned(snapshot, reference.value, work)
377 })
378 .map_err(|cause| binary::Error::Access { location, cause })?;
379 let identity = match resolve_method(&access, reference, provider)? {
380 Resolution::Supported(identity) => identity,
381 Resolution::Unsupported => return Ok(Agreement::Unsupported),
382 Resolution::Malformed => return Ok(Agreement::MalformedMethod),
383 };
384 let identity = match certificate::resolved_digest::<BASELINE, X, P>(identity, provider) {
385 Ok(identity) => identity,
386 Err(answer) => return Ok(answer),
387 };
388 let width = provider.width(&identity);
389 if let Some(width) = width
390 && expected.len() != width
391 {
392 return Ok(Agreement::MalformedValue);
393 }
394 let state = match start_resolved::<P, W, S::Error>(&identity, provider, execution)
395 .map_err(|cause| binary::Error::Callback { location, cause })?
396 {
397 Resolution::Supported(state) => state,
398 Resolution::Unsupported => return Ok(Agreement::Unsupported),
399 Resolution::Malformed => return Ok(Agreement::MalformedMethod),
400 };
401 let mut state = state;
402 let mut updating = Updating::<P> {
403 provider,
404 state: &mut state,
405 };
406 let result = stream::run_async(source, &mut updating, execution, "xades.digestInput", 0).await;
407 stream_result(result).map_err(|cause| binary::Error::Callback { location, cause })?;
408 access
412 .query_fallible(reference.value, |_, _, work| {
413 let output = finish_resolved(width, state, provider, work)?;
414 let agrees = provider
415 .matches(&identity, &output, &expected, work)
416 .map_err(Error::Provider)?;
417 Ok(if agrees {
418 Agreement::Matches
419 } else {
420 Agreement::Differs
421 })
422 })
423 .map_err(|cause| match cause {
424 crate::xml::AccessError::Xml(cause) => binary::Error::Callback { location, cause },
425 crate::xml::AccessError::Changed { node } => binary::Error::Access {
426 location,
427 cause: crate::xml::AccessError::Changed { node },
428 },
429 crate::xml::AccessError::Resource { node, cause } => binary::Error::Access {
430 location,
431 cause: crate::xml::AccessError::Resource { node, cause },
432 },
433 })
434}
435
436pub(crate) enum Checked<T> {
437 Answer(Agreement),
438 Value(T),
439}
440
441pub(crate) fn check_with<'p, X: BinaryXml, P: DigestProvider<'p, X>, W: Work, SE, R>(
445 xml: &X,
446 reference: Reference<X::Id, X::Document, X::Generation>,
447 provider: &'p P,
448 execution: &W,
449 visit: impl FnOnce(
450 certificate::FormatDigest<P::Identity>,
451 &[u8],
452 &W,
453 ) -> Result<R, Error<SE, P::Error>>,
454) -> binary::BinaryResult<Checked<R>, X, Error<SE, P::Error>> {
455 let access = Access::new(xml, reference.snapshot, execution);
456 if let Some(answer) = preflight(&access, reference)? {
457 return Ok(Checked::Answer(answer));
458 }
459 binary::with_octets(
460 xml,
461 Location {
462 snapshot: reference.snapshot,
463 node: reference.value,
464 },
465 execution,
466 |expected| {
467 Ok::<_, core::convert::Infallible>(check_expected(
468 &access, reference, expected, provider, visit,
469 ))
470 },
471 )
472 .map_err(|error| error.map_callback(|never| match never {}))?
473}
474
475fn preflight<X: BinaryXml, W: Work, SE, PE>(
476 access: &Access<'_, X, W>,
477 reference: Reference<X::Id, X::Document, X::Generation>,
478) -> binary::BinaryResult<Option<Agreement>, X, Error<SE, PE>> {
479 certificate::preflight_method(access, reference.method).map_err(|cause| binary::Error::Access {
480 location: Location {
481 snapshot: reference.snapshot,
482 node: reference.method,
483 },
484 cause,
485 })
486}
487
488fn check_expected<'p, X: BinaryXml, P: DigestProvider<'p, X>, W: Work, SE, R>(
489 access: &Access<'_, X, W>,
490 reference: Reference<X::Id, X::Document, X::Generation>,
491 expected: &[u8],
492 provider: &'p P,
493 visit: impl FnOnce(
494 certificate::FormatDigest<P::Identity>,
495 &[u8],
496 &W,
497 ) -> Result<R, Error<SE, P::Error>>,
498) -> binary::BinaryResult<Checked<R>, X, Error<SE, P::Error>> {
499 let execution = access.execution();
500 let resolved = resolve_method(access, reference, provider)?;
501 let identity = match resolved {
502 Resolution::Supported(identity) => identity,
503 Resolution::Unsupported => return Ok(Checked::Answer(Agreement::Unsupported)),
504 Resolution::Malformed => return Ok(Checked::Answer(Agreement::MalformedMethod)),
505 };
506 let identity = match certificate::baseline_digest::<X, P>(identity, provider) {
507 certificate::Expected::Ready(identity) => identity,
508 certificate::Expected::Answer(answer) => return Ok(Checked::Answer(answer)),
509 };
510 if let Some(width) = provider.width(identity.identity())
511 && expected.len() != width
512 {
513 return Ok(Checked::Answer(Agreement::MalformedValue));
514 }
515 visit(identity, expected, execution)
516 .map(Checked::Value)
517 .map_err(|cause| binary::Error::Callback {
518 location: Location {
519 snapshot: reference.snapshot,
520 node: reference.value,
521 },
522 cause,
523 })
524}
525
526fn resolve_method<'p, X: Xml, P: DigestProvider<'p, X>, W: Work, SE>(
527 access: &Access<'_, X, W>,
528 reference: Reference<X::Id, X::Document, X::Generation>,
529 provider: &P,
530) -> binary::BinaryResult<Resolution<P::Identity>, X, Error<SE, P::Error>> {
531 let location = Location {
532 snapshot: reference.snapshot,
533 node: reference.method,
534 };
535 access
536 .query(reference.method, |_, _, _| {
537 Ok(provider.resolve(access, reference.method))
538 })
539 .map_err(|cause| binary::Error::Access { location, cause })?
540 .map_err(|cause| binary::Error::Callback {
541 location,
542 cause: Error::Provider(cause),
543 })
544}
545
546pub(crate) fn run_resolved<'p, P: Digest<'p>, S: Source, W: Work>(
547 identity: &certificate::FormatDigest<P::Identity>,
548 expected: &[u8],
549 source: &mut S,
550 provider: &'p P,
551 execution: &W,
552) -> Result<Agreement, Error<S::Error, P::Error>> {
553 let width = provider.width(identity.identity());
554 if let Some(width) = width
555 && expected.len() != width
556 {
557 return Ok(Agreement::MalformedValue);
558 }
559 let output = match compute_resolved::<P, S, W>(identity, source, provider, execution)? {
560 Resolution::Supported(output) => output,
561 Resolution::Unsupported => return Ok(Agreement::Unsupported),
562 Resolution::Malformed => return Ok(Agreement::MalformedMethod),
563 };
564 execution.charge_work(1).map_err(Error::Resource)?;
565 let agrees = provider
566 .matches(identity.identity(), &output, expected, execution)
567 .map_err(Error::Provider)?;
568 Ok(if agrees {
569 Agreement::Matches
570 } else {
571 Agreement::Differs
572 })
573}
574
575type Computation<O, SE, PE> = Result<Resolution<O>, Error<SE, PE>>;
576
577pub(crate) fn compute_resolved<'p, P: Digest<'p>, S: Source, W: Work>(
578 identity: &certificate::FormatDigest<P::Identity>,
579 source: &mut S,
580 provider: &'p P,
581 execution: &W,
582) -> Computation<P::Output, S::Error, P::Error> {
583 let width = provider.width(identity.identity());
584 let mut state =
585 match start_resolved::<P, W, S::Error>(identity.identity(), provider, execution)? {
586 Resolution::Supported(state) => state,
587 Resolution::Unsupported => return Ok(Resolution::Unsupported),
588 Resolution::Malformed => return Ok(Resolution::Malformed),
589 };
590 let mut updating = Updating::<P> {
591 provider,
592 state: &mut state,
593 };
594 stream_result(stream::run(
595 source,
596 &mut updating,
597 execution,
598 "xades.digestInput",
599 0,
600 ))?;
601 finish_resolved(width, state, provider, execution).map(Resolution::Supported)
602}
603
604fn stream_result<SE, PE>(result: Result<(), StreamError<SE, PE>>) -> Result<(), Error<SE, PE>> {
605 match result {
606 Ok(()) => {}
607 Err(StreamError::Source(cause)) => return Err(Error::Source(cause)),
608 Err(StreamError::Callback(cause)) => return Err(Error::Provider(cause)),
609 Err(StreamError::Processing(cause)) => return Err(Error::Stream(cause)),
610 }
611 Ok(())
612}
613
614fn start_resolved<'p, P: Digest<'p>, W: Work, SE>(
615 identity: &P::Identity,
616 provider: &'p P,
617 execution: &W,
618) -> Result<Resolution<P::State>, Error<SE, P::Error>> {
619 execution.charge_work(1).map_err(Error::Resource)?;
620 provider.start(identity, execution).map_err(Error::Provider)
621}
622
623fn finish_resolved<'p, P: Digest<'p>, W: Work, SE>(
624 width: Option<usize>,
625 state: P::State,
626 provider: &'p P,
627 execution: &W,
628) -> Result<P::Output, Error<SE, P::Error>> {
629 execution.charge_work(1).map_err(Error::Resource)?;
630 let output = provider.finish(state, execution).map_err(Error::Provider)?;
631 if let Some(expected) = width
632 && output.as_ref().len() != expected
633 {
634 return Err(Error::OutputWidth {
635 expected,
636 actual: output.as_ref().len(),
637 });
638 }
639 Ok(output)
640}