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 fn failed(&self) -> bool {
130 self.failure.borrow().is_some()
131 }
132 fn refuse(&self, cause: ImprintError<E, P::Error>) -> crate::stream::Refused {
133 let mut failure = self.failure.borrow_mut();
134 if failure.is_none() {
135 *failure = Some(cause);
136 }
137 crate::stream::Refused
138 }
139 fn admit(&self) -> Result<(), crate::stream::Refused> {
140 if self.failed() {
141 return Err(crate::stream::Refused);
142 }
143 self.reader.charge(1).map_err(|cause| {
144 self.refuse(ImprintError::Processing(Error::encoding(cause, self.field)))
145 })
146 }
147 fn admit_pass(&self) -> Result<(), crate::stream::Refused> {
148 if self.failed() {
149 return Err(crate::stream::Refused);
150 }
151 self.reader.charge(1).map_err(|cause| {
152 self.refuse(ImprintError::Pass(crate::stream::StreamError::Processing(
153 Error::encoding(cause, self.field),
154 )))
155 })
156 }
157 pub fn start(
159 &self,
160 ) -> Result<ImprintState<'_, 'reader, 'document, P, E>, crate::stream::Refused> {
161 self.admit()?;
162 let Some(active) = self.active.get().checked_add(1) else {
163 return Err(self.refuse(ImprintError::IncompleteInput));
164 };
165 let state = self
166 .provider
167 .start(&self.identity, &self.reader.execution())
168 .map_err(|cause| self.refuse(ImprintError::Digest(DigestError::Callback(cause))))?;
169 let Resolution::Supported(state) = state else {
170 return Err(self.refuse(ImprintError::Digest(DigestError::Unstreamable)));
171 };
172 self.active.set(active);
173 Ok(ImprintState {
174 context: self,
175 state: Some(state),
176 })
177 }
178 pub fn stream<'context, S: crate::stream::Source<Error = E>>(
182 &'context self,
183 source: &mut S,
184 states: &mut [&mut ImprintState<'context, 'reader, 'document, P, E>],
185 ) -> Result<(), crate::stream::Refused> {
186 self.stream_with(source, states, &self.reader.execution())
187 }
188
189 pub fn stream_from<'context, S: crate::stream::Source<Error = E>>(
194 &'context self,
195 source: &mut S,
196 origin: &Reader<'_>,
197 states: &mut [&mut ImprintState<'context, 'reader, 'document, P, E>],
198 ) -> Result<(), crate::stream::Refused> {
199 self.stream_with(source, states, &origin.execution_query(self.reader))
200 }
201
202 fn stream_with<'context, S, W>(
203 &'context self,
204 source: &mut S,
205 states: &mut [&mut ImprintState<'context, 'reader, 'document, P, E>],
206 execution: &W,
207 ) -> Result<(), crate::stream::Refused>
208 where
209 S: crate::stream::Source<Error = E>,
210 W: structured_asn1::memory::Work,
211 {
212 self.admit()?;
213 if states.is_empty() {
214 return Err(self.refuse(ImprintError::IncompleteInput));
215 }
216 for state in &*states {
217 self.admit()?;
218 if !core::ptr::eq(&raw const state.context.owner, &raw const self.owner) {
219 return Err(self.refuse(ImprintError::ForeignInput));
220 }
221 }
222 let result = crate::stream::run(
223 source,
224 &mut Fanout { states },
225 execution,
226 self.field,
227 self.span.start,
228 );
229 match result {
230 Ok(()) => Ok(()),
231 Err(crate::stream::StreamError::Source(cause)) => Err(self.refuse(ImprintError::Pass(
232 crate::stream::StreamError::Source(cause),
233 ))),
234 Err(crate::stream::StreamError::Processing(cause)) => Err(self.refuse(
235 ImprintError::Pass(crate::stream::StreamError::Processing(cause)),
236 )),
237 Err(crate::stream::StreamError::Callback(_)) => Err(crate::stream::Refused),
239 }
240 }
241 pub fn compare<'context>(
244 &'context self,
245 digest: &ImprintDigest<'_, '_, 'document, P, E>,
246 ) -> Result<ImprintProof<'context>, crate::stream::Refused> {
247 self.admit()?;
248 if !core::ptr::eq(&raw const digest.context.owner, &raw const self.owner) {
249 return Err(self.refuse(ImprintError::ForeignInput));
250 }
251 let result = crate::binding::matches_digest(
252 self.provider,
253 digest.finished(),
254 self.expected.bytes(),
255 &self.reader.execution(),
256 self.field,
257 self.span,
258 )
259 .map_err(|cause| self.refuse(ImprintError::Comparison(cause)))?;
260 Ok(ImprintProof {
261 owner: &self.owner,
262 agreement: match result {
263 DigestBinding::Matches => ImprintAgreement::Matches,
264 DigestBinding::Differs => ImprintAgreement::Differs,
265 DigestBinding::Malformed(_) => ImprintAgreement::MalformedHash,
266 },
267 })
268 }
269}
270
271impl<'context, 'reader, 'document, P: AsnDigest<'document>, E>
272 ImprintState<'context, 'reader, 'document, P, E>
273{
274 pub fn write(&mut self, bytes: &[u8]) -> Result<(), crate::stream::Refused> {
277 self.write_with(bytes, &self.context.reader.execution())
278 }
279 pub fn write_from(
283 &mut self,
284 bytes: &[u8],
285 origin: &Reader<'_>,
286 ) -> Result<(), crate::stream::Refused> {
287 self.write_with(bytes, &origin.execution_query(self.context.reader))
288 }
289 fn write_with<W: structured_asn1::memory::Work>(
290 &mut self,
291 bytes: &[u8],
292 execution: &W,
293 ) -> Result<(), crate::stream::Refused> {
294 self.context.admit_pass()?;
295 execution.charge_work(1).map_err(|cause| {
299 self.context
300 .refuse(ImprintError::Pass(crate::stream::StreamError::Processing(
301 Error::resource(cause, self.context.field, self.context.span.start),
302 )))
303 })?;
304 self.context
305 .provider
306 .update(
307 self.state
308 .as_mut()
309 .expect("unfinished state owns its backend"),
310 bytes,
311 execution,
312 )
313 .map_err(|cause| {
314 self.context
315 .refuse(ImprintError::Pass(crate::stream::StreamError::Callback(
316 DigestError::Callback(cause),
317 )))
318 })
319 }
320 pub fn finish(
326 mut self,
327 ) -> Result<ImprintDigest<'context, 'reader, 'document, P, E>, crate::stream::Refused> {
328 self.context.admit_pass()?;
329 let output = self
330 .context
331 .provider
332 .finish(
333 self.state
334 .take()
335 .expect("unfinished state owns its backend"),
336 &self.context.reader.execution(),
337 )
338 .map_err(|cause| {
339 self.context
340 .refuse(ImprintError::Pass(crate::stream::StreamError::Callback(
341 DigestError::Callback(cause),
342 )))
343 })?;
344 if let Some(expected) = self.context.provider.width(&self.context.identity)
345 && output.as_ref().len() != expected
346 {
347 return Err(self.context.refuse(ImprintError::Pass(
348 crate::stream::StreamError::Callback(DigestError::OutputWidth {
349 expected,
350 actual: output.as_ref().len(),
351 }),
352 )));
353 }
354 Ok(ImprintDigest {
355 context: self.context,
356 output,
357 })
358 }
359}
360impl<'document, P: AsnDigest<'document>, E> Drop for ImprintState<'_, '_, 'document, P, E> {
361 fn drop(&mut self) {
362 let active = self.context.active.get();
363 debug_assert!(active > 0);
364 self.context.active.set(active - 1);
365 if self.state.is_some() {
366 self.context.abandoned.set(true);
367 }
368 }
369}
370
371struct Fanout<'states, 'state, 'context, 'reader, 'document, P: AsnDigest<'document>, E> {
372 states: &'states mut [&'state mut ImprintState<'context, 'reader, 'document, P, E>],
373}
374impl<'document, P: AsnDigest<'document>, E> crate::stream::Sink
375 for Fanout<'_, '_, '_, '_, 'document, P, E>
376{
377 type Error = crate::stream::Refused;
378 fn write<W: structured_asn1::memory::Work>(
379 &mut self,
380 bytes: &[u8],
381 execution: &W,
382 ) -> Result<(), Self::Error> {
383 for state in &mut *self.states {
384 state.write_with(bytes, execution)?;
385 }
386 Ok(())
387 }
388}
389
390pub struct RawInput<'source, S>(&'source mut S);
392impl<'source, S> RawInput<'source, S> {
393 #[must_use]
395 pub fn new(source: &'source mut S) -> Self {
396 Self(source)
397 }
398}
399impl<P, PE, S: crate::stream::Source> ComputedInput<P, PE> for RawInput<'_, S> {
400 type Error = S::Error;
401 fn compute<'context, 'reader, 'document>(
402 &mut self,
403 context: &'context ImprintContext<'reader, 'document, P, Self::Error>,
404 ) -> Result<ImprintProof<'context>, InputFailure<Self::Error>>
405 where
406 P: AsnDigest<'document, Error = PE>,
407 {
408 let mut state = context.start()?;
409 context.stream(self.0, &mut [&mut state])?;
410 let output = state.finish()?;
411 context.compare(&output).map_err(InputFailure::Refused)
412 }
413}
414
415pub(super) fn compute<'a, P, I>(
416 imprint: &MessageImprint<'a>,
417 provider: &'a P,
418 parent: &Reader<'a>,
419 input: &mut I,
420 identity: P::Identity,
421) -> Result<ImprintAgreement, ImprintError<I::Error, P::Error>>
422where
423 P: AsnDigest<'a>,
424 I: ComputedInput<P, P::Error>,
425{
426 let field = imprint.fields().hashed;
427 let span = imprint.hashed().span();
428 let expected = match imprint.hashed().as_one() {
430 Some(bytes) => Expected::Borrowed(bytes),
431 None => Expected::Joined(
432 imprint
433 .hashed()
434 .copy_in(parent, field, span.start)
435 .map_err(ImprintError::Processing)?,
436 ),
437 };
438 let context = ImprintContext {
439 provider,
440 identity,
441 reader: parent,
442 expected,
443 field,
444 span,
445 owner: 0,
446 active: Cell::new(0),
447 abandoned: Cell::new(false),
448 failure: RefCell::new(None),
449 };
450 let result = input.compute(&context);
451 if let Some(cause) = context.failure.borrow_mut().take() {
452 return Err(cause);
453 }
454 let proof = match result {
455 Ok(proof) => proof,
456 Err(InputFailure::Source(cause)) => {
457 return Err(ImprintError::Pass(crate::stream::StreamError::Source(
458 cause,
459 )));
460 }
461 Err(InputFailure::Refused(_)) => return Err(ImprintError::IncompleteInput),
462 };
463 if !core::ptr::eq(proof.owner, &raw const context.owner) {
464 return Err(ImprintError::ForeignInput);
465 }
466 if context.active.get() != 0 || context.abandoned.get() {
467 return Err(ImprintError::IncompleteInput);
468 }
469 Ok(proof.agreement)
470}