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
29#[derive(Debug)]
31pub enum InputFailure<E> {
32 Source(E),
34 Refused(crate::stream::Refused),
36}
37impl<E> From<crate::stream::Refused> for InputFailure<E> {
38 fn from(value: crate::stream::Refused) -> Self {
39 Self::Refused(value)
40 }
41}
42
43pub struct ImprintProof<'context> {
48 owner: &'context u8,
49 agreement: ImprintAgreement,
50}
51impl ImprintProof<'_> {
52 #[must_use]
54 pub fn agreement(&self) -> ImprintAgreement {
55 self.agreement
56 }
57}
58
59enum Expected<'a> {
60 Borrowed(&'a [u8]),
61 Joined(OwnedVec<u8>),
62}
63impl Expected<'_> {
64 fn bytes(&self) -> &[u8] {
65 match self {
66 Self::Borrowed(bytes) => bytes,
67 Self::Joined(bytes) => bytes,
68 }
69 }
70}
71
72pub struct ImprintContext<'reader, 'document, P: AsnDigest<'document>, E> {
78 provider: &'document P,
79 identity: P::Identity,
80 reader: &'reader Reader<'document>,
81 expected: Expected<'document>,
82 field: &'static str,
83 span: Span,
84 owner: u8,
85 active: Cell<usize>,
86 abandoned: Cell<bool>,
87 failure: RefCell<Option<ImprintError<E, P::Error>>>,
88}
89
90pub struct ImprintState<'context, 'reader, 'document, P: AsnDigest<'document>, E> {
94 context: &'context ImprintContext<'reader, 'document, P, E>,
95 state: Option<P::State>,
96}
97
98pub struct ImprintDigest<'context, 'reader, 'document, P: AsnDigest<'document>, E> {
102 context: &'context ImprintContext<'reader, 'document, P, E>,
103 output: P::Output,
104}
105impl<'document, P: AsnDigest<'document>, E> ImprintDigest<'_, '_, 'document, P, E> {
106 #[must_use]
108 pub fn as_bytes(&self) -> &[u8] {
109 self.output.as_ref()
110 }
111 #[must_use]
113 pub fn finished(&self) -> Finished<'_, P, P::Identity, P::Output> {
114 Finished::new(self.context.provider, &self.context.identity, &self.output)
115 }
116}
117
118impl<'reader, 'document, P: AsnDigest<'document>, E> ImprintContext<'reader, 'document, P, E> {
119 #[must_use]
121 pub fn reader(&self) -> &Reader<'document> {
122 self.reader
123 }
124 #[must_use]
126 pub fn identity(&self) -> &P::Identity {
127 &self.identity
128 }
129 pub(crate) fn provider(&self) -> &'document P {
130 self.provider
131 }
132 fn failed(&self) -> bool {
133 self.failure.borrow().is_some()
134 }
135 fn refuse(&self, cause: ImprintError<E, P::Error>) -> crate::stream::Refused {
136 let mut failure = self.failure.borrow_mut();
137 if failure.is_none() {
138 *failure = Some(cause);
139 }
140 crate::stream::Refused
141 }
142 fn admit(&self) -> Result<(), crate::stream::Refused> {
143 if self.failed() {
144 return Err(crate::stream::Refused);
145 }
146 self.reader.charge(1).map_err(|cause| {
147 self.refuse(ImprintError::Processing(Error::encoding(cause, self.field)))
148 })
149 }
150 fn admit_pass(&self) -> Result<(), crate::stream::Refused> {
151 if self.failed() {
152 return Err(crate::stream::Refused);
153 }
154 self.reader.charge(1).map_err(|cause| {
155 self.refuse(ImprintError::Pass(crate::stream::StreamError::Processing(
156 Error::encoding(cause, self.field),
157 )))
158 })
159 }
160 pub fn start(
162 &self,
163 ) -> Result<ImprintState<'_, 'reader, 'document, P, E>, crate::stream::Refused> {
164 self.admit()?;
165 let Some(active) = self.active.get().checked_add(1) else {
166 return Err(self.refuse(ImprintError::IncompleteInput));
167 };
168 let state = self
169 .provider
170 .start(&self.identity, &self.reader.execution())
171 .map_err(|cause| self.refuse(ImprintError::Digest(DigestError::Callback(cause))))?;
172 let Resolution::Supported(state) = state else {
173 return Err(self.refuse(ImprintError::Digest(DigestError::Unstreamable)));
174 };
175 self.active.set(active);
176 Ok(ImprintState {
177 context: self,
178 state: Some(state),
179 })
180 }
181 pub fn stream<'context, S: crate::stream::Source<Error = E>>(
185 &'context self,
186 source: &mut S,
187 states: &mut [&mut ImprintState<'context, 'reader, 'document, P, E>],
188 ) -> Result<(), crate::stream::Refused> {
189 self.stream_with(source, states, &self.reader.execution())
190 }
191
192 pub fn stream_from<'context, S: crate::stream::Source<Error = E>>(
197 &'context self,
198 source: &mut S,
199 origin: &Reader<'_>,
200 states: &mut [&mut ImprintState<'context, 'reader, 'document, P, E>],
201 ) -> Result<(), crate::stream::Refused> {
202 self.stream_with(source, states, &origin.execution_query(self.reader))
203 }
204
205 fn stream_with<'context, S, W>(
206 &'context self,
207 source: &mut S,
208 states: &mut [&mut ImprintState<'context, 'reader, 'document, P, E>],
209 execution: &W,
210 ) -> Result<(), crate::stream::Refused>
211 where
212 S: crate::stream::Source<Error = E>,
213 W: structured_asn1::memory::Work,
214 {
215 self.admit()?;
216 if states.is_empty() {
217 return Err(self.refuse(ImprintError::IncompleteInput));
218 }
219 for state in &*states {
220 self.admit()?;
221 if !core::ptr::eq(&raw const state.context.owner, &raw const self.owner) {
222 return Err(self.refuse(ImprintError::ForeignInput));
223 }
224 }
225 let result = crate::stream::run(
226 source,
227 &mut Fanout { states },
228 execution,
229 self.field,
230 self.span.start,
231 );
232 match result {
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),
242 }
243 }
244 pub fn compare<'context>(
247 &'context self,
248 digest: &ImprintDigest<'_, '_, 'document, P, E>,
249 ) -> Result<ImprintProof<'context>, crate::stream::Refused> {
250 self.admit()?;
251 if !core::ptr::eq(&raw const digest.context.owner, &raw const self.owner) {
252 return Err(self.refuse(ImprintError::ForeignInput));
253 }
254 self.compare_output(&digest.output)
255 }
256
257 pub(crate) fn compare_output<'context>(
261 &'context self,
262 output: &P::Output,
263 ) -> Result<ImprintProof<'context>, crate::stream::Refused> {
264 self.admit()?;
265 let result = crate::binding::matches_digest(
266 self.provider,
267 Finished::new(self.provider, &self.identity, output),
268 self.expected.bytes(),
269 &self.reader.execution(),
270 self.field,
271 self.span,
272 )
273 .map_err(|cause| self.refuse(ImprintError::Comparison(cause)))?;
274 Ok(ImprintProof {
275 owner: &self.owner,
276 agreement: match result {
277 DigestBinding::Matches => ImprintAgreement::Matches,
278 DigestBinding::Differs => ImprintAgreement::Differs,
279 DigestBinding::Malformed(_) => ImprintAgreement::MalformedHash,
280 },
281 })
282 }
283
284 pub(crate) fn compare_derived(
288 &self,
289 compare: impl FnOnce(&[u8], &structured_asn1::memory::Execution<'_>) -> Result<bool, E>,
290 ) -> Result<ImprintProof<'_>, crate::stream::Refused> {
291 self.admit()?;
292 let matches =
293 compare(self.expected.bytes(), &self.reader.execution()).map_err(|cause| {
294 self.refuse(ImprintError::Pass(crate::stream::StreamError::Source(
295 cause,
296 )))
297 })?;
298 Ok(ImprintProof {
299 owner: &self.owner,
300 agreement: if matches {
301 ImprintAgreement::Matches
302 } else {
303 ImprintAgreement::Differs
304 },
305 })
306 }
307}
308
309impl<'context, 'reader, 'document, P: AsnDigest<'document>, E>
310 ImprintState<'context, 'reader, 'document, P, E>
311{
312 pub fn write(&mut self, bytes: &[u8]) -> Result<(), crate::stream::Refused> {
315 self.write_with(bytes, &self.context.reader.execution())
316 }
317 pub fn write_from(
321 &mut self,
322 bytes: &[u8],
323 origin: &Reader<'_>,
324 ) -> Result<(), crate::stream::Refused> {
325 self.write_with(bytes, &origin.execution_query(self.context.reader))
326 }
327 fn write_with<W: structured_asn1::memory::Work>(
328 &mut self,
329 bytes: &[u8],
330 execution: &W,
331 ) -> Result<(), crate::stream::Refused> {
332 self.context.admit_pass()?;
333 execution.charge_work(1).map_err(|cause| {
337 self.context
338 .refuse(ImprintError::Pass(crate::stream::StreamError::Processing(
339 Error::resource(cause, self.context.field, self.context.span.start),
340 )))
341 })?;
342 self.context
343 .provider
344 .update(
345 self.state
346 .as_mut()
347 .expect("unfinished state owns its backend"),
348 bytes,
349 execution,
350 )
351 .map_err(|cause| {
352 self.context
353 .refuse(ImprintError::Pass(crate::stream::StreamError::Callback(
354 DigestError::Callback(cause),
355 )))
356 })
357 }
358 pub fn finish(
364 mut self,
365 ) -> Result<ImprintDigest<'context, 'reader, 'document, P, E>, crate::stream::Refused> {
366 self.context.admit_pass()?;
367 let output = self
368 .context
369 .provider
370 .finish(
371 self.state
372 .take()
373 .expect("unfinished state owns its backend"),
374 &self.context.reader.execution(),
375 )
376 .map_err(|cause| {
377 self.context
378 .refuse(ImprintError::Pass(crate::stream::StreamError::Callback(
379 DigestError::Callback(cause),
380 )))
381 })?;
382 if let Some(expected) = self.context.provider.width(&self.context.identity)
383 && output.as_ref().len() != expected
384 {
385 return Err(self.context.refuse(ImprintError::Pass(
386 crate::stream::StreamError::Callback(DigestError::OutputWidth {
387 expected,
388 actual: output.as_ref().len(),
389 }),
390 )));
391 }
392 Ok(ImprintDigest {
393 context: self.context,
394 output,
395 })
396 }
397}
398impl<'document, P: AsnDigest<'document>, E> Drop for ImprintState<'_, '_, 'document, P, E> {
399 fn drop(&mut self) {
400 let active = self.context.active.get();
401 debug_assert!(active > 0);
402 self.context.active.set(active - 1);
403 if self.state.is_some() {
404 self.context.abandoned.set(true);
405 }
406 }
407}
408
409struct Fanout<'states, 'state, 'context, 'reader, 'document, P: AsnDigest<'document>, E> {
410 states: &'states mut [&'state mut ImprintState<'context, 'reader, 'document, P, E>],
411}
412impl<'document, P: AsnDigest<'document>, E> crate::stream::Sink
413 for Fanout<'_, '_, '_, '_, 'document, P, E>
414{
415 type Error = crate::stream::Refused;
416 fn write<W: structured_asn1::memory::Work>(
417 &mut self,
418 bytes: &[u8],
419 execution: &W,
420 ) -> Result<(), Self::Error> {
421 for state in &mut *self.states {
422 state.write_with(bytes, execution)?;
423 }
424 Ok(())
425 }
426}
427
428pub struct RawInput<'source, S>(&'source mut S);
430impl<'source, S> RawInput<'source, S> {
431 #[must_use]
433 pub fn new(source: &'source mut S) -> Self {
434 Self(source)
435 }
436}
437impl<P, PE, S: crate::stream::Source> ComputedInput<P, PE> for RawInput<'_, S> {
438 type Error = S::Error;
439 fn compute<'context, 'reader, 'document>(
440 &mut self,
441 context: &'context ImprintContext<'reader, 'document, P, Self::Error>,
442 ) -> Result<ImprintProof<'context>, InputFailure<Self::Error>>
443 where
444 P: AsnDigest<'document, Error = PE>,
445 {
446 let mut state = context.start()?;
447 context.stream(self.0, &mut [&mut state])?;
448 let output = state.finish()?;
449 context.compare(&output).map_err(InputFailure::Refused)
450 }
451}
452
453pub(super) fn compute<'a, P, I>(
454 imprint: &MessageImprint<'a>,
455 provider: &'a P,
456 parent: &Reader<'a>,
457 input: &mut I,
458 identity: P::Identity,
459) -> Result<ImprintAgreement, ImprintError<I::Error, P::Error>>
460where
461 P: AsnDigest<'a>,
462 I: ComputedInput<P, P::Error>,
463{
464 let field = imprint.fields().hashed;
465 let span = imprint.hashed().span();
466 let expected = match imprint.hashed().as_one() {
468 Some(bytes) => Expected::Borrowed(bytes),
469 None => Expected::Joined(
470 imprint
471 .hashed()
472 .copy_in(parent, field, span.start)
473 .map_err(ImprintError::Processing)?,
474 ),
475 };
476 let context = ImprintContext {
477 provider,
478 identity,
479 reader: parent,
480 expected,
481 field,
482 span,
483 owner: 0,
484 active: Cell::new(0),
485 abandoned: Cell::new(false),
486 failure: RefCell::new(None),
487 };
488 let result = input.compute(&context);
489 if let Some(cause) = context.failure.borrow_mut().take() {
490 return Err(cause);
491 }
492 let proof = match result {
493 Ok(proof) => proof,
494 Err(InputFailure::Source(cause)) => {
495 return Err(ImprintError::Pass(crate::stream::StreamError::Source(
496 cause,
497 )));
498 }
499 Err(InputFailure::Refused(_)) => return Err(ImprintError::IncompleteInput),
500 };
501 if !core::ptr::eq(proof.owner, &raw const context.owner) {
502 return Err(ImprintError::ForeignInput);
503 }
504 if context.active.get() != 0 || context.abandoned.get() {
505 return Err(ImprintError::IncompleteInput);
506 }
507 Ok(proof.agreement)
508}