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 execution
259 .charge_work(core::mem::size_of_val(&*self.pending) as u64)
260 .map_err(refusal)?;
261 for request in self.pending {
262 execution.charge_work(1).map_err(refusal)?;
263 let (content, signature) = check(
264 self.provider,
265 request.signer,
266 request.key,
267 &request.binding,
268 request.challenge,
269 &completed,
270 execution,
271 )
272 .map_err(StreamError::Callback)?;
273 results
274 .try_push(Checked {
275 signer: request.signer,
276 key: request.key,
277 content,
278 signature,
279 })
280 .map_err(refusal)?;
281 }
282 Ok(results.into_owned())
283 }
284}
285
286fn check<'a, P: SignatureProvider<'a>, W: Work>(
287 provider: &'a P,
288 signer: &'a SignerInfo<'a>,
289 key: WorkingKey<'a>,
290 binding: &ResolvedBinding<'a>,
291 challenge: Challenge<'a, P>,
292 completed: &crate::payload::Completed<'a, P>,
293 execution: &W,
294) -> Result<(ContentAgreement, Option<SignatureAnswer>), CheckError<P::Error>> {
295 let digest = match &challenge {
296 Challenge::RawPayload(index) => {
297 debug_assert!(*index < completed.raw().len());
298 return Ok((ContentAgreement::Direct, Some(completed.raw()[*index])));
299 }
300 Challenge::RawAttributes { digest, .. } | Challenge::Prehashed { digest, .. } => *digest,
301 };
302 let payload = completed
303 .digests()
304 .for_request(digest, execution)
305 .map_err(digest_error)?;
306 let payload = payload.expect("prepared request ordinal has a completed output");
307 if let ResolvedBinding::Attributes {
308 digest: expected,
309 digest_span,
310 } = binding
311 {
312 match binding::matches_digest(
313 provider,
314 payload.finished(),
315 expected,
316 execution,
317 "signerInfo.signedAttrs.messageDigest",
318 *digest_span,
319 )
320 .map_err(bind_error)?
321 {
322 DigestBinding::Differs => return Ok((ContentAgreement::Differs, None)),
323 DigestBinding::Malformed(error) => {
324 return Ok((ContentAgreement::Malformed(error), None));
325 }
326 DigestBinding::Matches => {}
327 }
328 let attributes = signer
329 .signed_attributes()
330 .expect("prepared attrs binding retained its original attrs");
331 let mut ranges = attributes
332 .signed_bytes()
333 .expect("prepared signed attrs have SET signature ranges");
334 let signature = match challenge {
335 Challenge::RawAttributes { mut state, .. } => {
336 stream::run(
337 &mut ranges,
338 &mut RawSink {
339 provider,
340 state: &mut state,
341 },
342 execution,
343 "signerInfo.signedAttrs",
344 attributes.element().span().start,
345 )
346 .map_err(|error| range_error(error, attributes.element().span().start))?;
347 provider
348 .finish_raw(state, execution)
349 .map_err(CheckError::Callback)?
350 }
351 Challenge::Prehashed { identity, .. } => {
352 let related = completed
353 .digests()
354 .digest_related(
355 digest,
356 &mut ranges,
357 execution,
358 "signerInfo.signedAttrs",
359 attributes.element().span().start,
360 )
361 .map_err(|error| related_error(error, attributes.element().span().start))?
362 .expect("prepared request ordinal retains its identity");
363 provider
364 .verify_prehashed(
365 &identity,
366 key,
367 signer.signature(),
368 related.prehash(),
369 execution,
370 )
371 .map_err(CheckError::Callback)?
372 }
373 Challenge::RawPayload(_) => {
374 unreachable!("raw payload challenges returned before attrs processing")
375 }
376 };
377 return Ok((ContentAgreement::Matches, Some(signature)));
378 }
379 let Challenge::Prehashed { identity, .. } = challenge else {
380 unreachable!("only prehashed direct input reaches the digest branch");
381 };
382 let signature = provider
383 .verify_prehashed(
384 &identity,
385 key,
386 signer.signature(),
387 payload.prehash(),
388 execution,
389 )
390 .map_err(CheckError::Callback)?;
391 Ok((ContentAgreement::Direct, Some(signature)))
392}
393
394struct RawSink<'s, 'a, P: SignatureProvider<'a>> {
395 provider: &'a P,
396 state: &'s mut P::RawState,
397}
398impl<'a, P: SignatureProvider<'a>> Sink for RawSink<'_, 'a, P> {
399 type Error = P::Error;
400 fn write<W: Work>(&mut self, bytes: &[u8], execution: &W) -> Result<(), Self::Error> {
401 self.provider.update_raw(self.state, bytes, execution)
402 }
403}
404
405fn digest_error<E>(error: DigestError<E>) -> CheckError<E> {
406 match error {
407 DigestError::Processing(error) => CheckError::Processing(error),
408 DigestError::Callback(error) => CheckError::Callback(error),
409 DigestError::OutputWidth { expected, actual } => {
410 CheckError::OutputWidth { expected, actual }
411 }
412 DigestError::Unstreamable => CheckError::Unstreamable,
413 }
414}
415fn payload_error<E>(error: PayloadError<E>) -> CheckError<E> {
416 match error {
417 PayloadError::Processing(error) => CheckError::Processing(error),
418 PayloadError::Callback(error) => CheckError::Callback(error),
419 PayloadError::Digest(error) => digest_error(error),
420 }
421}
422fn bind_error<E>(error: BindError<E>) -> CheckError<E> {
423 match error {
424 BindError::Processing(error) => CheckError::Processing(error),
425 BindError::Callback(error) => CheckError::Callback(error),
426 BindError::OutputWidth { expected, actual } => CheckError::OutputWidth { expected, actual },
427 }
428}
429fn range_error<E>(error: StreamError<RangesError, E>, at: u64) -> CheckError<E> {
430 match error {
431 StreamError::Source(RangesError::Resource(reason)) => {
432 CheckError::Processing(Error::resource(reason, "signerInfo.signedAttrs", at))
433 }
434 StreamError::Source(RangesError::Receiver(_)) => {
435 unreachable!("stream guard preserves the original sink refusal")
436 }
437 StreamError::Processing(error) => CheckError::Processing(error),
438 StreamError::Callback(error) => CheckError::Callback(error),
439 }
440}
441fn related_error<E>(error: StreamError<RangesError, DigestError<E>>, at: u64) -> CheckError<E> {
442 match error {
443 StreamError::Callback(error) => digest_error(error),
444 error => range_error(error, at).map_callback(),
445 }
446}
447
448impl<E> CheckError<DigestError<E>> {
449 fn map_callback(self) -> CheckError<E> {
450 match self {
451 Self::Processing(error) => CheckError::Processing(error),
452 Self::Callback(error) => digest_error(error),
453 Self::OutputWidth { expected, actual } => CheckError::OutputWidth { expected, actual },
454 Self::Unstreamable => CheckError::Unstreamable,
455 }
456 }
457}