1use super::{ImprintAgreement, ImprintError, MessageImprint};
4use crate::{Error, binding::DigestBinding, digest::DigestError, provider::Finished};
5use core::cell::{Cell, RefCell};
6use structured_asn1::{Reader, Span, digest::Resolution, memory::OwnedVec};
7use structured_pkix::algorithm::digest::AsnDigest;
8
9pub trait ComputedInput<P, PE> {
17 type Error;
19
20 fn compute<'context, 'reader, 'document>(
22 &mut self,
23 context: &'context ImprintContext<'reader, 'document, P, Self::Error>,
24 ) -> Result<ImprintProof<'context>, InputFailure<Self::Error>>
25 where
26 P: AsnDigest<'document, Error = PE>;
27}
28
29pub trait AsyncComputedInput<P, PE> {
32 type Error;
34 fn compute<'context: 'scope, 'reader, 'document, 'scope>(
36 &'scope mut self,
37 context: &'context ImprintContext<'reader, 'document, P, Self::Error>,
38 ) -> impl core::future::Future<Output = Result<ImprintProof<'context>, InputFailure<Self::Error>>>
39 where
40 P: AsnDigest<'document, Error = PE>;
41}
42
43#[derive(Debug)]
45pub enum InputFailure<E> {
46 Source(E),
48 Refused(crate::stream::Refused),
50}
51impl<E> From<crate::stream::Refused> for InputFailure<E> {
52 fn from(value: crate::stream::Refused) -> Self {
53 Self::Refused(value)
54 }
55}
56
57pub struct ImprintProof<'context> {
62 owner: &'context u8,
63 agreement: ImprintAgreement,
64}
65impl ImprintProof<'_> {
66 #[must_use]
68 pub fn agreement(&self) -> ImprintAgreement {
69 self.agreement
70 }
71}
72
73enum Expected<'a> {
74 Borrowed(&'a [u8]),
75 Joined(OwnedVec<u8>),
76}
77impl Expected<'_> {
78 fn bytes(&self) -> &[u8] {
79 match self {
80 Self::Borrowed(bytes) => bytes,
81 Self::Joined(bytes) => bytes,
82 }
83 }
84}
85
86pub struct ImprintContext<'reader, 'document, P: AsnDigest<'document>, E> {
92 provider: &'document P,
93 identity: P::Identity,
94 reader: &'reader Reader<'document>,
95 expected: Expected<'document>,
96 field: &'static str,
97 span: Span,
98 owner: u8,
99 active: Cell<usize>,
100 abandoned: Cell<bool>,
101 failure: RefCell<Option<ImprintError<E, P::Error>>>,
102}
103
104pub struct ImprintState<'context, 'reader, 'document, P: AsnDigest<'document>, E> {
108 context: &'context ImprintContext<'reader, 'document, P, E>,
109 state: Option<P::State>,
110}
111
112pub struct ImprintDigest<'context, 'reader, 'document, P: AsnDigest<'document>, E> {
116 context: &'context ImprintContext<'reader, 'document, P, E>,
117 output: P::Output,
118}
119impl<'document, P: AsnDigest<'document>, E> ImprintDigest<'_, '_, 'document, P, E> {
120 #[must_use]
122 pub fn as_bytes(&self) -> &[u8] {
123 self.output.as_ref()
124 }
125 #[must_use]
127 pub fn finished(&self) -> Finished<'_, P, P::Identity, P::Output> {
128 Finished::new(self.context.provider, &self.context.identity, &self.output)
129 }
130}
131
132impl<'reader, 'document, P: AsnDigest<'document>, E> ImprintContext<'reader, 'document, P, E> {
133 #[must_use]
135 pub fn reader(&self) -> &Reader<'document> {
136 self.reader
137 }
138 #[must_use]
140 pub fn identity(&self) -> &P::Identity {
141 &self.identity
142 }
143 pub(crate) fn provider(&self) -> &'document P {
144 self.provider
145 }
146 fn failed(&self) -> bool {
147 self.failure.borrow().is_some()
148 }
149 fn refuse(&self, cause: ImprintError<E, P::Error>) -> crate::stream::Refused {
150 let mut failure = self.failure.borrow_mut();
151 if failure.is_none() {
152 *failure = Some(cause);
153 }
154 crate::stream::Refused
155 }
156 fn admit(&self) -> Result<(), crate::stream::Refused> {
157 if self.failed() {
158 return Err(crate::stream::Refused);
159 }
160 self.reader.charge(1).map_err(|cause| {
161 self.refuse(ImprintError::Processing(Error::encoding(cause, self.field)))
162 })
163 }
164 fn admit_pass(&self) -> Result<(), crate::stream::Refused> {
165 if self.failed() {
166 return Err(crate::stream::Refused);
167 }
168 self.reader.charge(1).map_err(|cause| {
169 self.refuse(ImprintError::Pass(crate::stream::StreamError::Processing(
170 Error::encoding(cause, self.field),
171 )))
172 })
173 }
174 pub fn start(
176 &self,
177 ) -> Result<ImprintState<'_, 'reader, 'document, P, E>, crate::stream::Refused> {
178 self.admit()?;
179 let Some(active) = self.active.get().checked_add(1) else {
180 return Err(self.refuse(ImprintError::IncompleteInput));
181 };
182 let state = self
183 .provider
184 .start(&self.identity, &self.reader.execution())
185 .map_err(|cause| self.refuse(ImprintError::Digest(DigestError::Callback(cause))))?;
186 let Resolution::Supported(state) = state else {
187 return Err(self.refuse(ImprintError::Digest(DigestError::Unstreamable)));
188 };
189 self.active.set(active);
190 Ok(ImprintState {
191 context: self,
192 state: Some(state),
193 })
194 }
195 pub fn stream<'context, S: crate::stream::Source<Error = E>>(
199 &'context self,
200 source: &mut S,
201 states: &mut [&mut ImprintState<'context, 'reader, 'document, P, E>],
202 ) -> Result<(), crate::stream::Refused> {
203 self.stream_with(source, states, &self.reader.execution())
204 }
205
206 pub async fn stream_async<'context, S: crate::stream::AsyncSource<Error = E>>(
210 &'context self,
211 source: &mut S,
212 states: &mut [&mut ImprintState<'context, 'reader, 'document, P, E>],
213 ) -> Result<(), crate::stream::Refused> {
214 self.admit()?;
215 if states.is_empty() {
216 return Err(self.refuse(ImprintError::IncompleteInput));
217 }
218 for state in &*states {
219 self.admit()?;
220 if !core::ptr::eq(&raw const state.context.owner, &raw const self.owner) {
221 return Err(self.refuse(ImprintError::ForeignInput));
222 }
223 }
224 match crate::stream::run_async(
225 source,
226 &mut Fanout { states },
227 &self.reader.execution(),
228 self.field,
229 self.span.start,
230 )
231 .await
232 {
233 Ok(()) => Ok(()),
234 Err(crate::stream::StreamError::Source(cause)) => Err(self.refuse(ImprintError::Pass(
235 crate::stream::StreamError::Source(cause),
236 ))),
237 Err(crate::stream::StreamError::Processing(cause)) => Err(self.refuse(
238 ImprintError::Pass(crate::stream::StreamError::Processing(cause)),
239 )),
240 Err(crate::stream::StreamError::Callback(_)) => Err(crate::stream::Refused),
241 }
242 }
243
244 pub fn stream_from<'context, S: crate::stream::Source<Error = E>>(
249 &'context self,
250 source: &mut S,
251 origin: &Reader<'_>,
252 states: &mut [&mut ImprintState<'context, 'reader, 'document, P, E>],
253 ) -> Result<(), crate::stream::Refused> {
254 self.stream_with(source, states, &origin.execution_query(self.reader))
255 }
256
257 fn stream_with<'context, S, W>(
258 &'context self,
259 source: &mut S,
260 states: &mut [&mut ImprintState<'context, 'reader, 'document, P, E>],
261 execution: &W,
262 ) -> Result<(), crate::stream::Refused>
263 where
264 S: crate::stream::Source<Error = E>,
265 W: structured_asn1::memory::Work,
266 {
267 self.admit()?;
268 if states.is_empty() {
269 return Err(self.refuse(ImprintError::IncompleteInput));
270 }
271 for state in &*states {
272 self.admit()?;
273 if !core::ptr::eq(&raw const state.context.owner, &raw const self.owner) {
274 return Err(self.refuse(ImprintError::ForeignInput));
275 }
276 }
277 let result = crate::stream::run(
278 source,
279 &mut Fanout { states },
280 execution,
281 self.field,
282 self.span.start,
283 );
284 match result {
285 Ok(()) => Ok(()),
286 Err(crate::stream::StreamError::Source(cause)) => Err(self.refuse(ImprintError::Pass(
287 crate::stream::StreamError::Source(cause),
288 ))),
289 Err(crate::stream::StreamError::Processing(cause)) => Err(self.refuse(
290 ImprintError::Pass(crate::stream::StreamError::Processing(cause)),
291 )),
292 Err(crate::stream::StreamError::Callback(_)) => Err(crate::stream::Refused),
294 }
295 }
296 pub fn compare<'context>(
299 &'context self,
300 digest: &ImprintDigest<'_, '_, 'document, P, E>,
301 ) -> Result<ImprintProof<'context>, crate::stream::Refused> {
302 self.admit()?;
303 if !core::ptr::eq(&raw const digest.context.owner, &raw const self.owner) {
304 return Err(self.refuse(ImprintError::ForeignInput));
305 }
306 self.compare_output(&digest.output)
307 }
308
309 pub(crate) fn compare_output<'context>(
313 &'context self,
314 output: &P::Output,
315 ) -> Result<ImprintProof<'context>, crate::stream::Refused> {
316 self.admit()?;
317 let result = crate::binding::matches_digest(
318 self.provider,
319 Finished::new(self.provider, &self.identity, output),
320 self.expected.bytes(),
321 &self.reader.execution(),
322 self.field,
323 self.span,
324 )
325 .map_err(|cause| self.refuse(ImprintError::Comparison(cause)))?;
326 Ok(ImprintProof {
327 owner: &self.owner,
328 agreement: match result {
329 DigestBinding::Matches => ImprintAgreement::Matches,
330 DigestBinding::Differs => ImprintAgreement::Differs,
331 DigestBinding::Malformed(_) => ImprintAgreement::MalformedHash,
332 },
333 })
334 }
335
336 pub(crate) fn compare_derived(
340 &self,
341 compare: impl FnOnce(&[u8], &structured_asn1::memory::Execution<'_>) -> Result<bool, E>,
342 ) -> Result<ImprintProof<'_>, crate::stream::Refused> {
343 self.admit()?;
344 let matches =
345 compare(self.expected.bytes(), &self.reader.execution()).map_err(|cause| {
346 self.refuse(ImprintError::Pass(crate::stream::StreamError::Source(
347 cause,
348 )))
349 })?;
350 Ok(ImprintProof {
351 owner: &self.owner,
352 agreement: if matches {
353 ImprintAgreement::Matches
354 } else {
355 ImprintAgreement::Differs
356 },
357 })
358 }
359}
360
361impl<'context, 'reader, 'document, P: AsnDigest<'document>, E>
362 ImprintState<'context, 'reader, 'document, P, E>
363{
364 pub fn write(&mut self, bytes: &[u8]) -> Result<(), crate::stream::Refused> {
367 self.write_with(bytes, &self.context.reader.execution())
368 }
369 pub fn write_from(
373 &mut self,
374 bytes: &[u8],
375 origin: &Reader<'_>,
376 ) -> Result<(), crate::stream::Refused> {
377 self.write_with(bytes, &origin.execution_query(self.context.reader))
378 }
379 fn write_with<W: structured_asn1::memory::Work>(
380 &mut self,
381 bytes: &[u8],
382 execution: &W,
383 ) -> Result<(), crate::stream::Refused> {
384 self.context.admit_pass()?;
385 execution.charge_work(1).map_err(|cause| {
389 self.context
390 .refuse(ImprintError::Pass(crate::stream::StreamError::Processing(
391 Error::resource(cause, self.context.field, self.context.span.start),
392 )))
393 })?;
394 self.context
395 .provider
396 .update(
397 self.state
398 .as_mut()
399 .expect("unfinished state owns its backend"),
400 bytes,
401 execution,
402 )
403 .map_err(|cause| {
404 self.context
405 .refuse(ImprintError::Pass(crate::stream::StreamError::Callback(
406 DigestError::Callback(cause),
407 )))
408 })
409 }
410 pub fn finish(
416 mut self,
417 ) -> Result<ImprintDigest<'context, 'reader, 'document, P, E>, crate::stream::Refused> {
418 self.context.admit_pass()?;
419 let output = self
420 .context
421 .provider
422 .finish(
423 self.state
424 .take()
425 .expect("unfinished state owns its backend"),
426 &self.context.reader.execution(),
427 )
428 .map_err(|cause| {
429 self.context
430 .refuse(ImprintError::Pass(crate::stream::StreamError::Callback(
431 DigestError::Callback(cause),
432 )))
433 })?;
434 if let Some(expected) = self.context.provider.width(&self.context.identity)
435 && output.as_ref().len() != expected
436 {
437 return Err(self.context.refuse(ImprintError::Pass(
438 crate::stream::StreamError::Callback(DigestError::OutputWidth {
439 expected,
440 actual: output.as_ref().len(),
441 }),
442 )));
443 }
444 Ok(ImprintDigest {
445 context: self.context,
446 output,
447 })
448 }
449}
450impl<'document, P: AsnDigest<'document>, E> Drop for ImprintState<'_, '_, 'document, P, E> {
451 fn drop(&mut self) {
452 let active = self.context.active.get();
453 debug_assert!(active > 0);
454 self.context.active.set(active - 1);
455 if self.state.is_some() {
456 self.context.abandoned.set(true);
457 }
458 }
459}
460
461struct Fanout<'states, 'state, 'context, 'reader, 'document, P: AsnDigest<'document>, E> {
462 states: &'states mut [&'state mut ImprintState<'context, 'reader, 'document, P, E>],
463}
464impl<'document, P: AsnDigest<'document>, E> crate::stream::Sink
465 for Fanout<'_, '_, '_, '_, 'document, P, E>
466{
467 type Error = crate::stream::Refused;
468 fn write<W: structured_asn1::memory::Work>(
469 &mut self,
470 bytes: &[u8],
471 execution: &W,
472 ) -> Result<(), Self::Error> {
473 for state in &mut *self.states {
474 state.write_with(bytes, execution)?;
475 }
476 Ok(())
477 }
478}
479
480pub struct RawInput<'source, S>(&'source mut S);
482impl<'source, S> RawInput<'source, S> {
483 #[must_use]
485 pub fn new(source: &'source mut S) -> Self {
486 Self(source)
487 }
488}
489impl<P, PE, S: crate::stream::Source> ComputedInput<P, PE> for RawInput<'_, S> {
490 type Error = S::Error;
491 fn compute<'context, 'reader, 'document>(
492 &mut self,
493 context: &'context ImprintContext<'reader, 'document, P, Self::Error>,
494 ) -> Result<ImprintProof<'context>, InputFailure<Self::Error>>
495 where
496 P: AsnDigest<'document, Error = PE>,
497 {
498 let mut state = context.start()?;
499 context.stream(self.0, &mut [&mut state])?;
500 let output = state.finish()?;
501 context.compare(&output).map_err(InputFailure::Refused)
502 }
503}
504
505pub struct AsyncRawInput<'source, S>(&'source mut S);
507impl<'source, S> AsyncRawInput<'source, S> {
508 #[must_use]
510 pub fn new(source: &'source mut S) -> Self {
511 Self(source)
512 }
513}
514impl<P, PE, S: crate::stream::AsyncSource> AsyncComputedInput<P, PE> for AsyncRawInput<'_, S> {
515 type Error = S::Error;
516 async fn compute<'context: 'scope, 'reader, 'document, 'scope>(
517 &'scope mut self,
518 context: &'context ImprintContext<'reader, 'document, P, Self::Error>,
519 ) -> Result<ImprintProof<'context>, InputFailure<Self::Error>>
520 where
521 P: AsnDigest<'document, Error = PE>,
522 {
523 let mut state = context.start()?;
524 context.stream_async(self.0, &mut [&mut state]).await?;
525 let output = state.finish()?;
526 context.compare(&output).map_err(InputFailure::Refused)
527 }
528}
529
530pub(super) async fn compute_async<'a, P, I>(
531 imprint: &MessageImprint<'a>,
532 provider: &'a P,
533 parent: &Reader<'a>,
534 input: &mut I,
535 identity: P::Identity,
536) -> Result<ImprintAgreement, ImprintError<I::Error, P::Error>>
537where
538 P: AsnDigest<'a>,
539 I: AsyncComputedInput<P, P::Error>,
540{
541 let context = make_context(imprint, provider, parent, identity)?;
542 let result = input.compute(&context).await;
543 finish_input(&context, result)
544}
545
546pub(super) fn compute<'a, P, I>(
547 imprint: &MessageImprint<'a>,
548 provider: &'a P,
549 parent: &Reader<'a>,
550 input: &mut I,
551 identity: P::Identity,
552) -> Result<ImprintAgreement, ImprintError<I::Error, P::Error>>
553where
554 P: AsnDigest<'a>,
555 I: ComputedInput<P, P::Error>,
556{
557 let context = make_context(imprint, provider, parent, identity)?;
558 let result = input.compute(&context);
559 finish_input(&context, result)
560}
561
562fn make_context<'reader, 'a, P: AsnDigest<'a>, E>(
563 imprint: &MessageImprint<'a>,
564 provider: &'a P,
565 parent: &'reader Reader<'a>,
566 identity: P::Identity,
567) -> Result<ImprintContext<'reader, 'a, P, E>, ImprintError<E, P::Error>> {
568 let field = imprint.fields().hashed;
569 let span = imprint.hashed().span();
570 let expected = match imprint.hashed().as_one() {
572 Some(bytes) => Expected::Borrowed(bytes),
573 None => Expected::Joined(
574 imprint
575 .hashed()
576 .copy_in(parent, field, span.start)
577 .map_err(ImprintError::Processing)?,
578 ),
579 };
580 Ok(ImprintContext {
581 provider,
582 identity,
583 reader: parent,
584 expected,
585 field,
586 span,
587 owner: 0,
588 active: Cell::new(0),
589 abandoned: Cell::new(false),
590 failure: RefCell::new(None),
591 })
592}
593
594fn finish_input<'context, 'reader, 'document, P: AsnDigest<'document>, E>(
595 context: &'context ImprintContext<'reader, 'document, P, E>,
596 result: Result<ImprintProof<'context>, InputFailure<E>>,
597) -> Result<ImprintAgreement, ImprintError<E, P::Error>> {
598 if let Some(cause) = context.failure.borrow_mut().take() {
599 return Err(cause);
600 }
601 let proof = match result {
602 Ok(proof) => proof,
603 Err(InputFailure::Source(cause)) => {
604 return Err(ImprintError::Pass(crate::stream::StreamError::Source(
605 cause,
606 )));
607 }
608 Err(InputFailure::Refused(_)) => return Err(ImprintError::IncompleteInput),
609 };
610 if !core::ptr::eq(proof.owner, &raw const context.owner) {
611 return Err(ImprintError::ForeignInput);
612 }
613 if context.active.get() != 0 || context.abandoned.get() {
614 return Err(ImprintError::IncompleteInput);
615 }
616 Ok(proof.agreement)
617}