1use super::objects::Sources;
4use super::{ArchiveTimeStamp, FIELD};
5use crate::{
6 Error,
7 digest::{DigestBatch, DigestError, DigestMemo},
8 stream::{self, Sink, Source, StreamError},
9 timestamp::{ComputedInput, ImprintContext, ImprintProof, InputFailure},
10};
11use core::cell::{OnceCell, RefCell};
12use core::cmp::Ordering;
13use structured_asn1::{
14 digest::Resolution,
15 memory::{MemoryError, OwnedVec, SortError, Work},
16};
17use structured_pkix::algorithm::digest::{self, AsnDigest};
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum Membership {
22 Object,
24 Group,
26}
27
28#[derive(Debug)]
30pub enum InputError<SE, PE> {
31 Processing(Error),
33 Provider(PE),
35 Source(SE),
37 Unsupported,
39 MalformedAlgorithm,
41 ObjectCount,
43 Membership,
45 HashWidth,
47 OutputWidth,
49 ForeignRenewal,
51 ForeignDigest,
53}
54
55pub struct ObjectInput<'input, 'document, S> {
61 stamp: &'input ArchiveTimeStamp<'document>,
62 sources: &'input mut [S],
63 membership: Membership,
64 previous: Option<&'input super::PriorSequence<'input, 'document>>,
65}
66impl<'i, 'd, S> ObjectInput<'i, 'd, S> {
67 #[must_use]
69 pub fn new(
70 stamp: &'i ArchiveTimeStamp<'d>,
71 sources: &'i mut [S],
72 membership: Membership,
73 ) -> Self {
74 Self {
75 stamp,
76 sources,
77 membership,
78 previous: None,
79 }
80 }
81 #[must_use]
85 pub fn renewed(
86 stamp: &'i ArchiveTimeStamp<'d>,
87 sources: &'i mut [S],
88 membership: Membership,
89 previous: &'i super::PriorSequence<'i, 'd>,
90 ) -> Self {
91 Self {
92 stamp,
93 sources,
94 membership,
95 previous: Some(previous),
96 }
97 }
98}
99
100pub(super) struct Hasher<'p, 'i, P: AsnDigest<'p>, W: Work> {
101 pub(super) provider: &'p P,
102 pub(super) identity: &'i P::Identity,
103 pub(super) work: W,
104 pub(super) at: u64,
105}
106impl<'p, P: AsnDigest<'p>, W: Work> Hasher<'p, '_, P, W> {
107 pub(super) fn resource<SE>(&self, cause: MemoryError) -> InputError<SE, P::Error> {
108 InputError::Processing(Error::resource(cause, FIELD, self.at))
109 }
110 pub(super) fn start<SE>(&self) -> Result<P::State, InputError<SE, P::Error>> {
111 match self
112 .provider
113 .start(self.identity, &self.work)
114 .map_err(InputError::Provider)?
115 {
116 Resolution::Supported(state) => Ok(state),
117 Resolution::Unsupported => Err(InputError::Unsupported),
118 Resolution::Malformed => Err(InputError::MalformedAlgorithm),
119 }
120 }
121 pub(super) fn finish<SE>(
122 &self,
123 state: P::State,
124 ) -> Result<P::Output, InputError<SE, P::Error>> {
125 let output = self
126 .provider
127 .finish(state, &self.work)
128 .map_err(InputError::Provider)?;
129 if let Some(width) = self.provider.width(self.identity)
130 && output.as_ref().len() != width
131 {
132 return Err(InputError::OutputWidth);
133 }
134 Ok(output)
135 }
136 pub(super) fn update<SE>(
137 &self,
138 state: &mut P::State,
139 bytes: &[u8],
140 ) -> Result<(), InputError<SE, P::Error>> {
141 self.work
142 .charge_work(1)
143 .map_err(|cause| self.resource(cause))?;
144 self.provider
145 .update(state, bytes, &self.work)
146 .map_err(InputError::Provider)
147 }
148 pub(super) fn hash_source<S: Source>(
149 &self,
150 source: &mut S,
151 ) -> Result<P::Output, InputError<S::Error, P::Error>> {
152 let mut state = self.start()?;
153 let mut sink = Feed {
154 provider: self.provider,
155 state: &mut state,
156 };
157 stream::run(source, &mut sink, &self.work, FIELD, self.at).map_err(
158 |cause| match cause {
159 StreamError::Source(cause) => InputError::Source(cause),
160 StreamError::Callback(cause) => InputError::Provider(cause),
161 StreamError::Processing(cause) => InputError::Processing(cause),
162 },
163 )?;
164 self.finish(state)
165 }
166 fn hash_list<SE>(
167 &self,
168 values: &[Hash<'_, P::Output>],
169 ) -> Result<P::Output, InputError<SE, P::Error>> {
170 let mut state = self.start()?;
171 for value in values {
172 self.update(&mut state, value.bytes())?;
173 }
174 self.finish(state)
175 }
176 fn compare<SE>(&self, a: &[u8], b: &[u8]) -> Result<Ordering, InputError<SE, P::Error>> {
177 self.work
178 .charge_work(1 + a.len().min(b.len()) as u64)
179 .map_err(|cause| self.resource(cause))?;
180 Ok(a.cmp(b))
181 }
182}
183
184struct Feed<'p, 's, P: AsnDigest<'p>> {
185 provider: &'p P,
186 state: &'s mut P::State,
187}
188impl<'p, P: AsnDigest<'p>> Sink for Feed<'p, '_, P> {
189 type Error = P::Error;
190 fn write<W: Work>(&mut self, bytes: &[u8], work: &W) -> Result<(), Self::Error> {
191 self.provider.update(self.state, bytes, work)
192 }
193}
194
195enum Hash<'d, O> {
196 Borrowed(&'d [u8]),
197 Joined(OwnedVec<u8>),
198 Root(Root<'d, O>),
199}
200impl<O: AsRef<[u8]>> Hash<'_, O> {
201 fn bytes(&self) -> &[u8] {
202 match self {
203 Self::Borrowed(bytes) => bytes,
204 Self::Joined(bytes) => bytes,
205 Self::Root(output) => output.output().as_ref(),
206 }
207 }
208}
209
210impl<P, PE, S: Source> ComputedInput<P, PE> for ObjectInput<'_, '_, S> {
211 type Error = InputError<S::Error, PE>;
212 fn compute<'context, 'reader, 'document>(
213 &mut self,
214 context: &'context ImprintContext<'reader, 'document, P, Self::Error>,
215 ) -> Result<ImprintProof<'context>, InputFailure<Self::Error>>
216 where
217 P: AsnDigest<'document, Error = PE>,
218 {
219 let stamp = self.stamp;
220 let (root, written) = self.compute_root(context).map_err(InputFailure::Source)?;
221 let identity = written.as_ref().unwrap_or(context.identity());
222 let origin = stamp
223 .original
224 .reader_in(context.reader())
225 .map_err(|cause| InputFailure::Source(InputError::Processing(cause)))?;
226 let execution = origin.execution_query(context.reader());
227 context
228 .compare_derived(|expected, _| {
229 context
230 .provider()
231 .matches(identity, root.output(), expected, &execution)
232 .map_err(InputError::Provider)
233 })
234 .map_err(InputFailure::Refused)
235 }
236}
237impl<S: Source> ObjectInput<'_, '_, S> {
238 fn compute_root<'p, P: AsnDigest<'p>>(
239 &mut self,
240 context: &ImprintContext<'_, 'p, P, InputError<S::Error, P::Error>>,
241 ) -> ResolvedRoot<'_, P::Output, P::Identity, S::Error, P::Error> {
242 if self.sources.is_empty()
243 || (self.membership == Membership::Object && self.sources.len() != 1)
244 {
245 return Err(InputError::ObjectCount);
246 }
247 if !membership_shape(self.stamp, self.sources.len(), self.membership) {
248 return Err(InputError::Membership);
249 }
250 if let Some(previous) = self.previous
251 && !previous.contains(self.stamp)
252 {
253 return Err(InputError::ForeignRenewal);
254 }
255 let origin = self
256 .stamp
257 .original
258 .reader_in(context.reader())
259 .map_err(InputError::Processing)?;
260 let work = origin.execution_query(context.reader());
261 let provider = context.provider();
264 let written = if let Some(identifier) = self.stamp.algorithm {
265 match digest::resolve(provider, &identifier, &work)
266 .map_err(|cause| InputError::Processing(Error::pkix(cause)))?
267 {
268 Resolution::Supported(identity) => Some(identity),
269 Resolution::Unsupported => return Err(InputError::Unsupported),
270 Resolution::Malformed => return Err(InputError::MalformedAlgorithm),
271 }
272 } else {
273 None
274 };
275 let identity = written.as_ref().unwrap_or(context.identity());
276 let hasher = Hasher {
277 provider,
278 identity,
279 work,
280 at: self.stamp.original.element.span().start,
281 };
282 let output = root(
283 &hasher,
284 self.stamp,
285 self.sources,
286 self.membership,
287 self.previous,
288 )?;
289 Ok((output, written))
290 }
291}
292
293fn root<'s, 'p, P: AsnDigest<'p>, W: Work, S: Source>(
294 hasher: &Hasher<'p, '_, P, W>,
295 stamp: &'s ArchiveTimeStamp<'_>,
296 sources: &mut [S],
297 membership: Membership,
298 previous: Option<&super::PriorSequence<'_, '_>>,
299) -> RootResult<'s, P::Output, S::Error, P::Error> {
300 validate_tree(hasher, stamp)?;
301 let mut objects = hasher.work.vector();
302 objects
303 .try_reserve_exact(sources.len())
304 .map_err(|cause| hasher.resource(cause))?;
305 let previous_hash = previous.map(|prior| prior.hash(hasher)).transpose()?;
306 for source in sources {
307 let mut output = hasher.hash_source(source)?;
308 if let Some(previous) = &previous_hash {
309 let mut state = hasher.start()?;
310 hasher.update(&mut state, output.as_ref())?;
311 hasher.update(&mut state, previous.as_ref())?;
312 output = hasher.finish(state)?;
313 }
314 objects
315 .try_push(Hash::Root(Root::Owned(output)))
316 .map_err(|cause| hasher.resource(cause))?;
317 }
318 fold(hasher, stamp, objects, membership)
319}
320
321fn validate_tree<'p, P: AsnDigest<'p>, W: Work, SE>(
322 hasher: &Hasher<'p, '_, P, W>,
323 stamp: &ArchiveTimeStamp<'_>,
324) -> Result<(), InputError<SE, P::Error>> {
325 let width = hasher.provider.width(hasher.identity);
327 if let Some(tree) = &stamp.tree {
328 for level in tree {
329 hasher
330 .work
331 .charge_work(1)
332 .map_err(|cause| hasher.resource(cause))?;
333 for hash in &level.hashes {
334 hasher
335 .work
336 .charge_work(1)
337 .map_err(|cause| hasher.resource(cause))?;
338 if let Some(width) = width
339 && hash.len() != width
340 {
341 return Err(InputError::HashWidth);
342 }
343 }
344 }
345 }
346 Ok(())
347}
348
349pub(super) fn membership_shape(
350 stamp: &ArchiveTimeStamp<'_>,
351 objects: usize,
352 membership: Membership,
353) -> bool {
354 let Some(tree) = &stamp.tree else {
355 return objects == 1;
356 };
357 let Some(first) = tree.first() else {
358 return false;
359 };
360 if first.hashes.is_empty() {
361 return false;
362 }
363 membership != Membership::Group || first.hashes.len() == objects
364}
365
366pub(super) enum Root<'s, O> {
367 Owned(O),
368 Borrowed(&'s O),
369}
370type RootResult<'s, O, SE, PE> = Result<Root<'s, O>, InputError<SE, PE>>;
371type ResolvedRoot<'s, O, I, SE, PE> = Result<(Root<'s, O>, Option<I>), InputError<SE, PE>>;
372impl<O> Root<'_, O> {
373 pub(super) fn output(&self) -> &O {
374 match self {
375 Self::Owned(o) => o,
376 Self::Borrowed(o) => o,
377 }
378 }
379}
380
381fn fold<'s, 'p, P: AsnDigest<'p>, W: Work, SE>(
382 hasher: &Hasher<'p, '_, P, W>,
383 stamp: &'s ArchiveTimeStamp<'_>,
384 mut objects: OwnedVec<Hash<'s, P::Output>, W>,
385 membership: Membership,
386) -> Result<Root<'s, P::Output>, InputError<SE, P::Error>> {
387 sort(hasher, &mut objects)?;
388 let Some(tree) = &stamp.tree else {
389 if objects.len() != 1 {
390 return Err(InputError::Membership);
391 }
392 let Some(Hash::Root(output)) = objects.pop() else {
393 unreachable!("one complete output")
394 };
395 return Ok(output);
396 };
397 let Some(first) = tree.first() else {
398 return Err(InputError::Membership);
399 };
400 let mut siblings = hasher.work.vector();
401 for hash in &first.hashes {
402 siblings
403 .try_push(read_hash(hash, hasher)?)
404 .map_err(|cause| hasher.resource(cause))?;
405 }
406 sort(hasher, &mut siblings)?;
407 if membership == Membership::Group {
408 if objects.len() != siblings.len() {
409 return Err(InputError::Membership);
410 }
411 for (object, hash) in objects.iter().zip(siblings.iter()) {
412 if hasher.compare(object.bytes(), hash.bytes())? != Ordering::Equal {
413 return Err(InputError::Membership);
414 }
415 }
416 } else {
417 let mut found = false;
418 for hash in &siblings {
419 if hasher.compare(objects[0].bytes(), hash.bytes())? == Ordering::Equal {
420 found = true;
421 break;
422 }
423 }
424 if !found {
425 return Err(InputError::Membership);
426 }
427 }
428 let mut current = if siblings.len() == 1 {
429 let Some(Hash::Root(output)) = objects.pop() else {
431 unreachable!("matched singleton has one actual output")
432 };
433 output
434 } else {
435 Root::Owned(hasher.hash_list(&siblings)?)
436 };
437 drop(objects);
438 for level in &tree[1..] {
439 siblings.clear();
440 siblings
441 .try_push(Hash::Root(current))
442 .map_err(|cause| hasher.resource(cause))?;
443 for hash in &level.hashes {
444 siblings
445 .try_push(read_hash(hash, hasher)?)
446 .map_err(|cause| hasher.resource(cause))?;
447 }
448 sort(hasher, &mut siblings)?;
449 current = if siblings.len() == 1 {
450 let Some(Hash::Root(output)) = siblings.pop() else {
451 unreachable!("computed child")
452 };
453 output
454 } else {
455 Root::Owned(hasher.hash_list(&siblings)?)
456 };
457 }
458 Ok(current)
459}
460
461fn read_hash<'d, 'p, P: AsnDigest<'p>, W: Work, SE>(
462 hash: &crate::Octets<'d>,
463 hasher: &Hasher<'p, '_, P, W>,
464) -> Result<Hash<'d, P::Output>, InputError<SE, P::Error>> {
465 if let Some(bytes) = hash.as_one() {
466 return Ok(Hash::Borrowed(bytes));
467 }
468 let mut joined = hasher.work.vector();
469 joined
470 .try_reserve_exact(hash.len())
471 .map_err(|cause| hasher.resource(cause))?;
472 for piece in hash.pieces() {
473 hasher
474 .work
475 .charge_work(1)
476 .map_err(|cause| hasher.resource(cause))?;
477 joined
478 .try_extend_from_slice(piece.as_bytes())
479 .map_err(|cause| hasher.resource(cause))?;
480 }
481 Ok(Hash::Joined(joined.into_owned()))
482}
483
484fn sort<'p, P: AsnDigest<'p>, W: Work, SE>(
485 hasher: &Hasher<'p, '_, P, W>,
486 siblings: &mut OwnedVec<Hash<'_, P::Output>, W>,
487) -> Result<(), InputError<SE, P::Error>> {
488 siblings
489 .try_sort_unstable_by(|a, b| hasher.compare(a.bytes(), b.bytes()))
490 .map_err(|cause| match cause {
491 SortError::Resource(cause) => hasher.resource(cause),
492 SortError::Comparison(cause) => cause,
493 })
494}
495
496pub(super) struct MemoSet<'p, P: AsnDigest<'p>> {
500 pub(super) provider: &'p P,
501 pub(super) memos: OwnedVec<DigestMemo<'p, P>>,
502}
503trait RootComputation<P> {
504 type Output: AsRef<[u8]>;
505 type Error;
506 type SourceError;
507 fn belongs_to_provider(&self, provider: &P) -> bool;
508 fn root<'s>(
509 &'s self,
510 stamp: &'s ArchiveTimeStamp<'_>,
511 ordinal: usize,
512 membership: Membership,
513 previous: Option<&super::PriorSequence<'_, '_>>,
514 query: &structured_asn1::Reader<'_>,
515 ) -> RootResult<'s, Self::Output, Self::SourceError, Self::Error>;
516 fn matches<W: Work>(
517 &self,
518 ordinal: usize,
519 output: &Self::Output,
520 expected: &[u8],
521 work: &W,
522 ) -> Result<bool, InputError<Self::SourceError, Self::Error>>;
523}
524impl<'p, P: AsnDigest<'p>> RootComputation<P> for MemoSet<'p, P> {
525 type Output = P::Output;
526 type Error = P::Error;
527 type SourceError = core::convert::Infallible;
528 fn belongs_to_provider(&self, provider: &P) -> bool {
529 core::ptr::eq(self.provider, provider)
530 }
531 fn root<'s>(
532 &'s self,
533 stamp: &'s ArchiveTimeStamp<'_>,
534 ordinal: usize,
535 membership: Membership,
536 previous: Option<&super::PriorSequence<'_, '_>>,
537 query: &structured_asn1::Reader<'_>,
538 ) -> Result<Root<'s, P::Output>, InputError<core::convert::Infallible, P::Error>> {
539 let origin = stamp
540 .original
541 .reader_in(query)
542 .map_err(InputError::Processing)?;
543 let work = origin.execution_query(query);
544 if self.memos.is_empty() || (membership == Membership::Object && self.memos.len() != 1) {
545 return Err(InputError::ObjectCount);
546 }
547 let mut objects = work.vector();
548 objects
549 .try_reserve_exact(self.memos.len())
550 .map_err(|cause| {
551 InputError::Processing(Error::resource(
552 cause,
553 FIELD,
554 stamp.original.element.span().start,
555 ))
556 })?;
557 let first = self.memos[0]
558 .for_request(ordinal, &work)
559 .map_err(map_digest)?
560 .ok_or(InputError::ForeignDigest)?;
561 if !first.finished().from_provider(self.provider) {
562 return Err(InputError::ForeignDigest);
563 }
564 if let Some(prior) = previous
565 && !prior.contains(stamp)
566 {
567 return Err(InputError::ForeignRenewal);
568 }
569 let hasher = Hasher {
570 provider: self.provider,
571 identity: first.identity(),
572 work,
573 at: stamp.original.element.span().start,
574 };
575 validate_tree(&hasher, stamp)?;
576 let previous = previous.map(|prior| prior.hash(&hasher)).transpose()?;
577 for memo in &self.memos {
578 let value = memo
579 .for_request(ordinal, &hasher.work)
580 .map_err(map_digest)?
581 .ok_or(InputError::ForeignDigest)?;
582 if !value.finished().from_provider(self.provider) {
583 return Err(InputError::ForeignDigest);
584 }
585 if self
586 .provider
587 .compare(value.identity(), hasher.identity, &hasher.work)
588 .map_err(InputError::Provider)?
589 != Ordering::Equal
590 {
591 return Err(InputError::ForeignDigest);
592 }
593 let output = if let Some(previous) = &previous {
594 let mut state = hasher.start()?;
595 hasher.update(&mut state, value.as_bytes())?;
596 hasher.update(&mut state, previous.as_ref())?;
597 Root::Owned(hasher.finish(state)?)
598 } else {
599 Root::Borrowed(value.finished().output())
600 };
601 objects
602 .try_push(Hash::Root(output))
603 .map_err(|cause| hasher.resource(cause))?;
604 }
605 fold(&hasher, stamp, objects, membership)
606 }
607 fn matches<W: Work>(
608 &self,
609 ordinal: usize,
610 output: &P::Output,
611 expected: &[u8],
612 work: &W,
613 ) -> Result<bool, InputError<core::convert::Infallible, P::Error>> {
614 let first = self.memos[0]
615 .for_request(ordinal, work)
616 .map_err(map_digest)?
617 .ok_or(InputError::ForeignDigest)?;
618 self.provider
619 .matches(first.identity(), output, expected, work)
620 .map_err(InputError::Provider)
621 }
622}
623
624pub(super) struct LazyMemos<'s, 'p, P: AsnDigest<'p>, S: Sources<'p, P>> {
628 provider: &'p P,
629 algorithms: &'s [structured_pkix::algorithm::digest::Algorithm],
630 sources: RefCell<S>,
631 initialized: OnceCell<MemoSet<'p, P>>,
632}
633impl<'s, 'p, P: AsnDigest<'p>, S: Sources<'p, P>> LazyMemos<'s, 'p, P, S> {
634 pub(super) fn new(
635 provider: &'p P,
636 algorithms: &'s [structured_pkix::algorithm::digest::Algorithm],
637 sources: S,
638 ) -> Self {
639 Self {
640 provider,
641 algorithms,
642 sources: RefCell::new(sources),
643 initialized: OnceCell::new(),
644 }
645 }
646 fn ready(
647 &self,
648 stamp: &ArchiveTimeStamp<'_>,
649 query: &structured_asn1::Reader<'_>,
650 ) -> Result<&MemoSet<'p, P>, InputError<S::Error, P::Error>> {
651 if let Some(memos) = self.initialized.get() {
652 return Ok(memos);
653 }
654 let origin = stamp
655 .original
656 .reader_in(query)
657 .map_err(InputError::Processing)?;
658 let work = origin.execution_query(query);
659 let at = stamp.original.element.span().start;
660 let resource = |cause| InputError::Processing(Error::resource(cause, FIELD, at));
661 let mut sources = self
662 .sources
663 .try_borrow_mut()
664 .map_err(|_| InputError::ForeignDigest)?;
665 let mut memos = work.vector();
666 memos.try_reserve_exact(sources.len()).map_err(resource)?;
667 for index in 0..sources.len() {
668 let mut identities = work.vector();
669 for algorithm in self.algorithms {
670 work.charge_work(1).map_err(resource)?;
671 let identity = self
672 .provider
673 .identity(*algorithm)
674 .ok_or(InputError::Unsupported)?;
675 identities.try_push(identity).map_err(resource)?;
676 }
677 let batch =
678 DigestBatch::prepare(self.provider, identities.into_owned(), &work, FIELD, at)
679 .map_err(map_digest)?;
680 let memo = sources
681 .run(index, batch, &work)
682 .map_err(|cause| match cause {
683 StreamError::Source(cause) => InputError::Source(cause),
684 StreamError::Callback(cause) => map_digest(cause),
685 StreamError::Processing(cause) => InputError::Processing(cause),
686 })?;
687 memos.try_push(memo).map_err(resource)?;
688 }
689 self.initialized
690 .set(MemoSet {
691 provider: self.provider,
692 memos: memos.into_owned(),
693 })
694 .map_err(|_| InputError::ForeignDigest)?;
695 self.initialized.get().ok_or(InputError::ForeignDigest)
696 }
697}
698impl<'p, P: AsnDigest<'p>, S: Sources<'p, P>> RootComputation<P> for LazyMemos<'_, 'p, P, S> {
699 type Output = P::Output;
700 type Error = P::Error;
701 type SourceError = S::Error;
702 fn belongs_to_provider(&self, provider: &P) -> bool {
703 core::ptr::eq(self.provider, provider)
704 }
705 fn root<'s>(
706 &'s self,
707 stamp: &'s ArchiveTimeStamp<'_>,
708 ordinal: usize,
709 membership: Membership,
710 previous: Option<&super::PriorSequence<'_, '_>>,
711 query: &structured_asn1::Reader<'_>,
712 ) -> Result<Root<'s, P::Output>, InputError<S::Error, P::Error>> {
713 self.ready(stamp, query)?
714 .root(stamp, ordinal, membership, previous, query)
715 .map_err(widen)
716 }
717 fn matches<W: Work>(
718 &self,
719 ordinal: usize,
720 output: &P::Output,
721 expected: &[u8],
722 work: &W,
723 ) -> Result<bool, InputError<S::Error, P::Error>> {
724 self.initialized
725 .get()
726 .ok_or(InputError::ForeignDigest)?
727 .matches(ordinal, output, expected, work)
728 .map_err(widen)
729 }
730}
731fn widen<SE, PE>(cause: InputError<core::convert::Infallible, PE>) -> InputError<SE, PE> {
732 match cause {
733 InputError::Processing(cause) => InputError::Processing(cause),
734 InputError::Provider(cause) => InputError::Provider(cause),
735 InputError::Source(never) => match never {},
736 InputError::Unsupported => InputError::Unsupported,
737 InputError::MalformedAlgorithm => InputError::MalformedAlgorithm,
738 InputError::ObjectCount => InputError::ObjectCount,
739 InputError::Membership => InputError::Membership,
740 InputError::HashWidth => InputError::HashWidth,
741 InputError::OutputWidth => InputError::OutputWidth,
742 InputError::ForeignRenewal => InputError::ForeignRenewal,
743 InputError::ForeignDigest => InputError::ForeignDigest,
744 }
745}
746pub(super) struct CachedInput<'s, 'd, F> {
747 pub(super) stamp: &'s ArchiveTimeStamp<'d>,
748 pub(super) memos: &'s F,
749 pub(super) ordinal: usize,
750 pub(super) membership: Membership,
751 pub(super) previous: Option<&'s super::PriorSequence<'s, 'd>>,
752}
753impl<P, PE, SE, F: RootComputation<P, Error = PE, SourceError = SE>> ComputedInput<P, PE>
754 for CachedInput<'_, '_, F>
755{
756 type Error = InputError<SE, PE>;
757 fn compute<'context, 'reader, 'document>(
758 &mut self,
759 context: &'context ImprintContext<'reader, 'document, P, Self::Error>,
760 ) -> Result<ImprintProof<'context>, InputFailure<Self::Error>>
761 where
762 P: AsnDigest<'document, Error = PE>,
763 {
764 if !self.memos.belongs_to_provider(context.provider()) {
765 return Err(InputFailure::Source(InputError::ForeignDigest));
766 }
767 let root = self
768 .memos
769 .root(
770 self.stamp,
771 self.ordinal,
772 self.membership,
773 self.previous,
774 context.reader(),
775 )
776 .map_err(InputFailure::Source)?;
777 context
778 .compare_derived(|expected, work| {
779 self.memos
780 .matches(self.ordinal, root.output(), expected, work)
781 })
782 .map_err(InputFailure::Refused)
783 }
784}
785fn map_digest<SE, PE>(cause: DigestError<PE>) -> InputError<SE, PE> {
786 match cause {
787 DigestError::Processing(cause) => InputError::Processing(cause),
788 DigestError::Callback(cause) => InputError::Provider(cause),
789 DigestError::OutputWidth { .. } => InputError::OutputWidth,
790 DigestError::Unstreamable => InputError::Unsupported,
791 }
792}