1use crate::binding::{self, BindError, DigestBinding};
4use crate::digest::{DigestBatch, DigestError};
5use crate::payload::{PayloadBatch, PayloadError, RawRequest};
6use crate::prepare::{ResolvedBinding, ResolvedSigner};
7use crate::provider::{SignatureAnswer, SignatureMode, SignatureProvider};
8use crate::signer::SignerInfo;
9use crate::stream::{self, RangesError, Sink, Source, StreamError};
10use crate::{Error, ErrorCode};
11use structured_asn1::memory::{OwnedVec, Work};
12use structured_pkix::path::WorkingKey;
13
14mod document;
15pub use document::{ContentMode, DocumentBatch, DocumentChecks};
16
17#[derive(Debug)]
19pub enum CheckError<E> {
20 Processing(Error),
22 Callback(E),
24 OutputWidth {
26 expected: usize,
28 actual: usize,
30 },
31 Unstreamable,
33}
34
35pub type PassError<SE, PE> = StreamError<SE, CheckError<PE>>;
37
38#[derive(Debug)]
40pub enum ContentAgreement {
41 Direct,
43 Matches,
45 Differs,
47 Malformed(Error),
49}
50
51pub struct Checked<'a> {
53 signer: &'a SignerInfo<'a>,
54 key: WorkingKey<'a>,
55 content: ContentAgreement,
56 signature: Option<SignatureAnswer>,
57}
58
59impl<'a> Checked<'a> {
60 #[must_use]
64 pub fn into_parts(
65 self,
66 ) -> (
67 &'a SignerInfo<'a>,
68 WorkingKey<'a>,
69 ContentAgreement,
70 Option<SignatureAnswer>,
71 ) {
72 (self.signer, self.key, self.content, self.signature)
73 }
74 #[must_use]
76 pub fn signer(&self) -> &'a SignerInfo<'a> {
77 self.signer
78 }
79 #[must_use]
82 pub fn key(&self) -> WorkingKey<'a> {
83 self.key
84 }
85 #[must_use]
87 pub fn content(&self) -> &ContentAgreement {
88 &self.content
89 }
90 #[must_use]
93 pub fn signature(&self) -> Option<SignatureAnswer> {
94 self.signature
95 }
96}
97
98enum Challenge<'a, P: SignatureProvider<'a>> {
99 RawPayload(usize),
100 RawAttributes {
101 digest: usize,
102 state: P::RawState,
103 },
104 Prehashed {
105 digest: usize,
106 identity: P::SignatureIdentity,
107 },
108}
109
110struct Pending<'a, P: SignatureProvider<'a>> {
111 signer: &'a SignerInfo<'a>,
112 key: WorkingKey<'a>,
113 binding: ResolvedBinding<'a>,
114 challenge: Challenge<'a, P>,
115}
116
117pub struct Batch<'a, P: SignatureProvider<'a>> {
124 provider: &'a P,
125 payload: PayloadBatch<'a, P>,
126 pending: OwnedVec<Pending<'a, P>>,
127 field: &'static str,
128 at: u64,
129}
130
131impl<'a, P: SignatureProvider<'a>> Batch<'a, P> {
132 pub fn prepare<W: Work>(
139 provider: &'a P,
140 requests: OwnedVec<ResolvedSigner<'a, P>>,
141 execution: &W,
142 field: &'static str,
143 at: u64,
144 ) -> Result<Self, CheckError<P::Error>> {
145 let refusal = |reason| CheckError::Processing(Error::resource(reason, field, at));
146 let mut digest_count = 0;
147 let mut raw_count = 0;
148 for request in &requests {
149 execution.charge_work(1).map_err(refusal)?;
150 if !core::ptr::eq(provider, request.provider()) {
151 return Err(CheckError::Processing(Error::schema(
152 ErrorCode::ProviderMismatch,
153 field,
154 request.signer().span().start,
155 )));
156 }
157 if matches!(request.signature(), SignatureMode::Raw(_))
158 && matches!(request.binding(), ResolvedBinding::Direct)
159 {
160 raw_count += 1;
161 } else {
162 digest_count += 1;
163 }
164 }
165 let mut pending = execution.vector();
166 let mut identities = execution.vector();
167 let mut raw = execution.vector();
168 pending.try_reserve_exact(requests.len()).map_err(refusal)?;
169 identities
170 .try_reserve_exact(digest_count)
171 .map_err(refusal)?;
172 raw.try_reserve_exact(raw_count).map_err(refusal)?;
173 execution
174 .charge_work(core::mem::size_of_val(&*requests) as u64)
175 .map_err(refusal)?;
176 for request in requests {
177 execution.charge_work(1).map_err(refusal)?;
178 let parts = request.into_parts();
179 let challenge = match parts.signature {
180 SignatureMode::Raw(identity)
181 if matches!(parts.binding, ResolvedBinding::Direct) =>
182 {
183 let index = raw.len();
184 raw.try_push(RawRequest {
185 identity,
186 key: parts.key,
187 signature: parts.signer.signature(),
188 })
189 .map_err(refusal)?;
190 Challenge::RawPayload(index)
191 }
192 SignatureMode::Raw(identity) => {
193 let digest = identities.len();
194 identities.try_push(parts.digest).map_err(refusal)?;
195 let state = provider
196 .start_raw(&identity, parts.key, parts.signer.signature(), execution)
197 .map_err(CheckError::Callback)?;
198 Challenge::RawAttributes { digest, state }
199 }
200 SignatureMode::Prehashed(identity) => {
201 let digest = identities.len();
202 identities.try_push(parts.digest).map_err(refusal)?;
203 Challenge::Prehashed { digest, identity }
204 }
205 };
206 pending
207 .try_push(Pending {
208 signer: parts.signer,
209 key: parts.key,
210 binding: parts.binding,
211 challenge,
212 })
213 .map_err(refusal)?;
214 }
215 let digests = DigestBatch::prepare(provider, identities.into_owned(), execution, field, at)
216 .map_err(digest_error)?;
217 let payload =
218 PayloadBatch::prepare(digests, raw.into_owned(), execution).map_err(payload_error)?;
219 Ok(Self {
220 provider,
221 payload,
222 pending: pending.into_owned(),
223 field,
224 at,
225 })
226 }
227
228 pub fn run<S: Source, W: Work>(
236 self,
237 source: &mut S,
238 execution: &W,
239 ) -> Result<OwnedVec<Checked<'a>>, PassError<S::Error, P::Error>> {
240 let refusal =
241 |reason| StreamError::Processing(Error::resource(reason, self.field, self.at));
242 let mut results = execution.vector();
243 results
244 .try_reserve_exact(self.pending.len())
245 .map_err(refusal)?;
246 if self.pending.is_empty() {
248 return Ok(results.into_owned());
249 }
250 let completed = self
251 .payload
252 .run(source, execution)
253 .map_err(|error| match error {
254 StreamError::Source(error) => StreamError::Source(error),
255 StreamError::Processing(error) => StreamError::Processing(error),
256 StreamError::Callback(error) => StreamError::Callback(payload_error(error)),
257 })?;
258 finish(
259 self.provider,
260 self.pending,
261 &completed,
262 results,
263 execution,
264 self.field,
265 self.at,
266 )
267 }
268
269 pub async fn run_async<S: stream::AsyncSource, W: Work>(
273 self,
274 source: &mut S,
275 execution: &W,
276 ) -> Result<OwnedVec<Checked<'a>>, PassError<S::Error, P::Error>> {
277 let refusal = |cause| StreamError::Processing(Error::resource(cause, self.field, self.at));
278 let mut results = execution.vector();
279 results
280 .try_reserve_exact(self.pending.len())
281 .map_err(refusal)?;
282 if self.pending.is_empty() {
283 return Ok(results.into_owned());
284 }
285 let completed =
286 self.payload
287 .run_async(source, execution)
288 .await
289 .map_err(|error| match error {
290 StreamError::Source(error) => StreamError::Source(error),
291 StreamError::Processing(error) => StreamError::Processing(error),
292 StreamError::Callback(error) => StreamError::Callback(payload_error(error)),
293 })?;
294 finish(
295 self.provider,
296 self.pending,
297 &completed,
298 results,
299 execution,
300 self.field,
301 self.at,
302 )
303 }
304}
305
306fn finish<'a, P: SignatureProvider<'a>, SE, W: Work>(
307 provider: &'a P,
308 pending: OwnedVec<Pending<'a, P>>,
309 completed: &crate::payload::Completed<'a, P>,
310 mut results: OwnedVec<Checked<'a>, W>,
311 execution: &W,
312 field: &'static str,
313 at: u64,
314) -> Result<OwnedVec<Checked<'a>>, PassError<SE, P::Error>> {
315 let refusal = |cause| StreamError::Processing(Error::resource(cause, field, at));
316 execution
317 .charge_work(core::mem::size_of_val(&*pending) as u64)
318 .map_err(refusal)?;
319 for request in pending {
320 execution.charge_work(1).map_err(refusal)?;
321 let (content, signature) = check(
322 provider,
323 request.signer,
324 request.key,
325 &request.binding,
326 request.challenge,
327 completed,
328 execution,
329 )
330 .map_err(StreamError::Callback)?;
331 results
332 .try_push(Checked {
333 signer: request.signer,
334 key: request.key,
335 content,
336 signature,
337 })
338 .map_err(refusal)?;
339 }
340 Ok(results.into_owned())
341}
342
343fn check<'a, P: SignatureProvider<'a>, W: Work>(
344 provider: &'a P,
345 signer: &'a SignerInfo<'a>,
346 key: WorkingKey<'a>,
347 binding: &ResolvedBinding<'a>,
348 challenge: Challenge<'a, P>,
349 completed: &crate::payload::Completed<'a, P>,
350 execution: &W,
351) -> Result<(ContentAgreement, Option<SignatureAnswer>), CheckError<P::Error>> {
352 let digest = match &challenge {
353 Challenge::RawPayload(index) => {
354 debug_assert!(*index < completed.raw().len());
355 return Ok((ContentAgreement::Direct, Some(completed.raw()[*index])));
356 }
357 Challenge::RawAttributes { digest, .. } | Challenge::Prehashed { digest, .. } => *digest,
358 };
359 let payload = completed
360 .digests()
361 .for_request(digest, execution)
362 .map_err(digest_error)?;
363 let payload = payload.expect("prepared request ordinal has a completed output");
364 if let ResolvedBinding::Attributes {
365 digest: expected,
366 digest_span,
367 } = binding
368 {
369 match binding::matches_digest(
370 provider,
371 payload.finished(),
372 expected,
373 execution,
374 "signerInfo.signedAttrs.messageDigest",
375 *digest_span,
376 )
377 .map_err(bind_error)?
378 {
379 DigestBinding::Differs => return Ok((ContentAgreement::Differs, None)),
380 DigestBinding::Malformed(error) => {
381 return Ok((ContentAgreement::Malformed(error), None));
382 }
383 DigestBinding::Matches => {}
384 }
385 let attributes = signer
386 .signed_attributes()
387 .expect("prepared attrs binding retained its original attrs");
388 let mut ranges = attributes
389 .signed_bytes()
390 .expect("prepared signed attrs have SET signature ranges");
391 let signature = match challenge {
392 Challenge::RawAttributes { mut state, .. } => {
393 stream::run(
394 &mut ranges,
395 &mut RawSink {
396 provider,
397 state: &mut state,
398 },
399 execution,
400 "signerInfo.signedAttrs",
401 attributes.element().span().start,
402 )
403 .map_err(|error| range_error(error, attributes.element().span().start))?;
404 provider
405 .finish_raw(state, execution)
406 .map_err(CheckError::Callback)?
407 }
408 Challenge::Prehashed { identity, .. } => {
409 let related = completed
410 .digests()
411 .digest_related(
412 digest,
413 &mut ranges,
414 execution,
415 "signerInfo.signedAttrs",
416 attributes.element().span().start,
417 )
418 .map_err(|error| related_error(error, attributes.element().span().start))?
419 .expect("prepared request ordinal retains its identity");
420 provider
421 .verify_prehashed(
422 &identity,
423 key,
424 signer.signature(),
425 related.prehash(),
426 execution,
427 )
428 .map_err(CheckError::Callback)?
429 }
430 Challenge::RawPayload(_) => {
431 unreachable!("raw payload challenges returned before attrs processing")
432 }
433 };
434 return Ok((ContentAgreement::Matches, Some(signature)));
435 }
436 let Challenge::Prehashed { identity, .. } = challenge else {
437 unreachable!("only prehashed direct input reaches the digest branch");
438 };
439 let signature = provider
440 .verify_prehashed(
441 &identity,
442 key,
443 signer.signature(),
444 payload.prehash(),
445 execution,
446 )
447 .map_err(CheckError::Callback)?;
448 Ok((ContentAgreement::Direct, Some(signature)))
449}
450
451struct RawSink<'s, 'a, P: SignatureProvider<'a>> {
452 provider: &'a P,
453 state: &'s mut P::RawState,
454}
455impl<'a, P: SignatureProvider<'a>> Sink for RawSink<'_, 'a, P> {
456 type Error = P::Error;
457 fn write<W: Work>(&mut self, bytes: &[u8], execution: &W) -> Result<(), Self::Error> {
458 self.provider.update_raw(self.state, bytes, execution)
459 }
460}
461
462fn digest_error<E>(error: DigestError<E>) -> CheckError<E> {
463 match error {
464 DigestError::Processing(error) => CheckError::Processing(error),
465 DigestError::Callback(error) => CheckError::Callback(error),
466 DigestError::OutputWidth { expected, actual } => {
467 CheckError::OutputWidth { expected, actual }
468 }
469 DigestError::Unstreamable => CheckError::Unstreamable,
470 }
471}
472fn payload_error<E>(error: PayloadError<E>) -> CheckError<E> {
473 match error {
474 PayloadError::Processing(error) => CheckError::Processing(error),
475 PayloadError::Callback(error) => CheckError::Callback(error),
476 PayloadError::Digest(error) => digest_error(error),
477 }
478}
479fn bind_error<E>(error: BindError<E>) -> CheckError<E> {
480 match error {
481 BindError::Processing(error) => CheckError::Processing(error),
482 BindError::Callback(error) => CheckError::Callback(error),
483 BindError::OutputWidth { expected, actual } => CheckError::OutputWidth { expected, actual },
484 }
485}
486fn range_error<E>(error: StreamError<RangesError, E>, at: u64) -> CheckError<E> {
487 match error {
488 StreamError::Source(RangesError::Resource(reason)) => {
489 CheckError::Processing(Error::resource(reason, "signerInfo.signedAttrs", at))
490 }
491 StreamError::Source(RangesError::Receiver(_)) => {
492 unreachable!("stream guard preserves the original sink refusal")
493 }
494 StreamError::Processing(error) => CheckError::Processing(error),
495 StreamError::Callback(error) => CheckError::Callback(error),
496 }
497}
498fn related_error<E>(error: StreamError<RangesError, DigestError<E>>, at: u64) -> CheckError<E> {
499 match error {
500 StreamError::Callback(error) => digest_error(error),
501 error => range_error(error, at).map_callback(),
502 }
503}
504
505impl<E> CheckError<DigestError<E>> {
506 fn map_callback(self) -> CheckError<E> {
507 match self {
508 Self::Processing(error) => CheckError::Processing(error),
509 Self::Callback(error) => digest_error(error),
510 Self::OutputWidth { expected, actual } => CheckError::OutputWidth { expected, actual },
511 Self::Unstreamable => CheckError::Unstreamable,
512 }
513 }
514}