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
14#[derive(Debug)]
16pub enum CheckError<E> {
17 Processing(Error),
19 Callback(E),
21 OutputWidth {
23 expected: usize,
25 actual: usize,
27 },
28 Unstreamable,
30}
31
32pub type PassError<SE, PE> = StreamError<SE, CheckError<PE>>;
34
35#[derive(Debug)]
37pub enum ContentAgreement {
38 Direct,
40 Matches,
42 Differs,
44 Malformed(Error),
46}
47
48pub struct Checked<'a> {
50 signer: &'a SignerInfo<'a>,
51 key: WorkingKey<'a>,
52 content: ContentAgreement,
53 signature: Option<SignatureAnswer>,
54}
55
56impl<'a> Checked<'a> {
57 #[must_use]
59 pub fn signer(&self) -> &'a SignerInfo<'a> {
60 self.signer
61 }
62 #[must_use]
65 pub fn key(&self) -> WorkingKey<'a> {
66 self.key
67 }
68 #[must_use]
70 pub fn content(&self) -> &ContentAgreement {
71 &self.content
72 }
73 #[must_use]
76 pub fn signature(&self) -> Option<SignatureAnswer> {
77 self.signature
78 }
79}
80
81enum Challenge<'a, P: SignatureProvider<'a>> {
82 RawPayload(usize),
83 RawAttributes {
84 digest: usize,
85 state: P::RawState,
86 },
87 Prehashed {
88 digest: usize,
89 identity: P::SignatureIdentity,
90 },
91}
92
93struct Pending<'a, P: SignatureProvider<'a>> {
94 signer: &'a SignerInfo<'a>,
95 key: WorkingKey<'a>,
96 binding: ResolvedBinding<'a>,
97 challenge: Challenge<'a, P>,
98}
99
100pub struct Batch<'a, P: SignatureProvider<'a>> {
107 provider: &'a P,
108 payload: PayloadBatch<'a, P>,
109 pending: OwnedVec<Pending<'a, P>>,
110 field: &'static str,
111 at: u64,
112}
113
114impl<'a, P: SignatureProvider<'a>> Batch<'a, P> {
115 pub fn prepare<W: Work>(
122 provider: &'a P,
123 requests: OwnedVec<ResolvedSigner<'a, P>>,
124 execution: &W,
125 field: &'static str,
126 at: u64,
127 ) -> Result<Self, CheckError<P::Error>> {
128 let refusal = |reason| CheckError::Processing(Error::resource(reason, field, at));
129 let mut digest_count = 0;
130 let mut raw_count = 0;
131 for request in &requests {
132 execution.charge_work(1).map_err(refusal)?;
133 if !core::ptr::eq(provider, request.provider()) {
134 return Err(CheckError::Processing(Error::schema(
135 ErrorCode::ProviderMismatch,
136 field,
137 request.signer().span().start,
138 )));
139 }
140 if matches!(request.signature(), SignatureMode::Raw(_))
141 && matches!(request.binding(), ResolvedBinding::Direct)
142 {
143 raw_count += 1;
144 } else {
145 digest_count += 1;
146 }
147 }
148 let mut pending = execution.vector();
149 let mut identities = execution.vector();
150 let mut raw = execution.vector();
151 pending.try_reserve_exact(requests.len()).map_err(refusal)?;
152 identities
153 .try_reserve_exact(digest_count)
154 .map_err(refusal)?;
155 raw.try_reserve_exact(raw_count).map_err(refusal)?;
156 execution
157 .charge_work(core::mem::size_of_val(&*requests) as u64)
158 .map_err(refusal)?;
159 for request in requests {
160 execution.charge_work(1).map_err(refusal)?;
161 let parts = request.into_parts();
162 let challenge = match parts.signature {
163 SignatureMode::Raw(identity)
164 if matches!(parts.binding, ResolvedBinding::Direct) =>
165 {
166 let index = raw.len();
167 raw.try_push(RawRequest {
168 identity,
169 key: parts.key,
170 signature: parts.signer.signature(),
171 })
172 .map_err(refusal)?;
173 Challenge::RawPayload(index)
174 }
175 SignatureMode::Raw(identity) => {
176 let digest = identities.len();
177 identities.try_push(parts.digest).map_err(refusal)?;
178 let state = provider
179 .start_raw(&identity, parts.key, parts.signer.signature(), execution)
180 .map_err(CheckError::Callback)?;
181 Challenge::RawAttributes { digest, state }
182 }
183 SignatureMode::Prehashed(identity) => {
184 let digest = identities.len();
185 identities.try_push(parts.digest).map_err(refusal)?;
186 Challenge::Prehashed { digest, identity }
187 }
188 };
189 pending
190 .try_push(Pending {
191 signer: parts.signer,
192 key: parts.key,
193 binding: parts.binding,
194 challenge,
195 })
196 .map_err(refusal)?;
197 }
198 let digests = DigestBatch::prepare(provider, identities.into_owned(), execution, field, at)
199 .map_err(digest_error)?;
200 let payload =
201 PayloadBatch::prepare(digests, raw.into_owned(), execution).map_err(payload_error)?;
202 Ok(Self {
203 provider,
204 payload,
205 pending: pending.into_owned(),
206 field,
207 at,
208 })
209 }
210
211 pub fn run<S: Source, W: Work>(
219 self,
220 source: &mut S,
221 execution: &W,
222 ) -> Result<OwnedVec<Checked<'a>>, PassError<S::Error, P::Error>> {
223 let refusal =
224 |reason| StreamError::Processing(Error::resource(reason, self.field, self.at));
225 let mut results = execution.vector();
226 results
227 .try_reserve_exact(self.pending.len())
228 .map_err(refusal)?;
229 if self.pending.is_empty() {
231 return Ok(results.into_owned());
232 }
233 let completed = self
234 .payload
235 .run(source, execution)
236 .map_err(|error| match error {
237 StreamError::Source(error) => StreamError::Source(error),
238 StreamError::Processing(error) => StreamError::Processing(error),
239 StreamError::Callback(error) => StreamError::Callback(payload_error(error)),
240 })?;
241 execution
242 .charge_work(core::mem::size_of_val(&*self.pending) as u64)
243 .map_err(refusal)?;
244 for request in self.pending {
245 execution.charge_work(1).map_err(refusal)?;
246 let (content, signature) = check(
247 self.provider,
248 request.signer,
249 request.key,
250 &request.binding,
251 request.challenge,
252 &completed,
253 execution,
254 )
255 .map_err(StreamError::Callback)?;
256 results
257 .try_push(Checked {
258 signer: request.signer,
259 key: request.key,
260 content,
261 signature,
262 })
263 .map_err(refusal)?;
264 }
265 Ok(results.into_owned())
266 }
267}
268
269fn check<'a, P: SignatureProvider<'a>, W: Work>(
270 provider: &'a P,
271 signer: &'a SignerInfo<'a>,
272 key: WorkingKey<'a>,
273 binding: &ResolvedBinding<'a>,
274 challenge: Challenge<'a, P>,
275 completed: &crate::payload::Completed<'a, P>,
276 execution: &W,
277) -> Result<(ContentAgreement, Option<SignatureAnswer>), CheckError<P::Error>> {
278 let digest = match &challenge {
279 Challenge::RawPayload(index) => {
280 debug_assert!(*index < completed.raw().len());
281 return Ok((ContentAgreement::Direct, Some(completed.raw()[*index])));
282 }
283 Challenge::RawAttributes { digest, .. } | Challenge::Prehashed { digest, .. } => *digest,
284 };
285 let payload = completed
286 .digests()
287 .for_request(digest, execution)
288 .map_err(digest_error)?;
289 let payload = payload.expect("prepared request ordinal has a completed output");
290 if let ResolvedBinding::Attributes {
291 digest: expected,
292 digest_span,
293 } = binding
294 {
295 match binding::matches_digest(
296 provider,
297 payload.finished(),
298 expected,
299 execution,
300 "signerInfo.signedAttrs.messageDigest",
301 *digest_span,
302 )
303 .map_err(bind_error)?
304 {
305 DigestBinding::Differs => return Ok((ContentAgreement::Differs, None)),
306 DigestBinding::Malformed(error) => {
307 return Ok((ContentAgreement::Malformed(error), None));
308 }
309 DigestBinding::Matches => {}
310 }
311 let attributes = signer
312 .signed_attributes()
313 .expect("prepared attrs binding retained its original attrs");
314 let mut ranges = attributes
315 .signed_bytes()
316 .expect("prepared signed attrs have SET signature ranges");
317 let signature = match challenge {
318 Challenge::RawAttributes { mut state, .. } => {
319 stream::run(
320 &mut ranges,
321 &mut RawSink {
322 provider,
323 state: &mut state,
324 },
325 execution,
326 "signerInfo.signedAttrs",
327 attributes.element().span().start,
328 )
329 .map_err(|error| range_error(error, attributes.element().span().start))?;
330 provider
331 .finish_raw(state, execution)
332 .map_err(CheckError::Callback)?
333 }
334 Challenge::Prehashed { identity, .. } => {
335 let related = completed
336 .digests()
337 .digest_related(
338 digest,
339 &mut ranges,
340 execution,
341 "signerInfo.signedAttrs",
342 attributes.element().span().start,
343 )
344 .map_err(|error| related_error(error, attributes.element().span().start))?
345 .expect("prepared request ordinal retains its identity");
346 provider
347 .verify_prehashed(
348 &identity,
349 key,
350 signer.signature(),
351 related.prehash(),
352 execution,
353 )
354 .map_err(CheckError::Callback)?
355 }
356 Challenge::RawPayload(_) => {
357 unreachable!("raw payload challenges returned before attrs processing")
358 }
359 };
360 return Ok((ContentAgreement::Matches, Some(signature)));
361 }
362 let Challenge::Prehashed { identity, .. } = challenge else {
363 unreachable!("only prehashed direct input reaches the digest branch");
364 };
365 let signature = provider
366 .verify_prehashed(
367 &identity,
368 key,
369 signer.signature(),
370 payload.prehash(),
371 execution,
372 )
373 .map_err(CheckError::Callback)?;
374 Ok((ContentAgreement::Direct, Some(signature)))
375}
376
377struct RawSink<'s, 'a, P: SignatureProvider<'a>> {
378 provider: &'a P,
379 state: &'s mut P::RawState,
380}
381impl<'a, P: SignatureProvider<'a>> Sink for RawSink<'_, 'a, P> {
382 type Error = P::Error;
383 fn write<W: Work>(&mut self, bytes: &[u8], execution: &W) -> Result<(), Self::Error> {
384 self.provider.update_raw(self.state, bytes, execution)
385 }
386}
387
388fn digest_error<E>(error: DigestError<E>) -> CheckError<E> {
389 match error {
390 DigestError::Processing(error) => CheckError::Processing(error),
391 DigestError::Callback(error) => CheckError::Callback(error),
392 DigestError::OutputWidth { expected, actual } => {
393 CheckError::OutputWidth { expected, actual }
394 }
395 DigestError::Unstreamable => CheckError::Unstreamable,
396 }
397}
398fn payload_error<E>(error: PayloadError<E>) -> CheckError<E> {
399 match error {
400 PayloadError::Processing(error) => CheckError::Processing(error),
401 PayloadError::Callback(error) => CheckError::Callback(error),
402 PayloadError::Digest(error) => digest_error(error),
403 }
404}
405fn bind_error<E>(error: BindError<E>) -> CheckError<E> {
406 match error {
407 BindError::Processing(error) => CheckError::Processing(error),
408 BindError::Callback(error) => CheckError::Callback(error),
409 BindError::OutputWidth { expected, actual } => CheckError::OutputWidth { expected, actual },
410 }
411}
412fn range_error<E>(error: StreamError<RangesError, E>, at: u64) -> CheckError<E> {
413 match error {
414 StreamError::Source(RangesError::Resource(reason)) => {
415 CheckError::Processing(Error::resource(reason, "signerInfo.signedAttrs", at))
416 }
417 StreamError::Source(RangesError::Receiver(_)) => {
418 unreachable!("stream guard preserves the original sink refusal")
419 }
420 StreamError::Processing(error) => CheckError::Processing(error),
421 StreamError::Callback(error) => CheckError::Callback(error),
422 }
423}
424fn related_error<E>(error: StreamError<RangesError, DigestError<E>>, at: u64) -> CheckError<E> {
425 match error {
426 StreamError::Callback(error) => digest_error(error),
427 error => range_error(error, at).map_callback(),
428 }
429}
430
431impl<E> CheckError<DigestError<E>> {
432 fn map_callback(self) -> CheckError<E> {
433 match self {
434 Self::Processing(error) => CheckError::Processing(error),
435 Self::Callback(error) => digest_error(error),
436 Self::OutputWidth { expected, actual } => CheckError::OutputWidth { expected, actual },
437 Self::Unstreamable => CheckError::Unstreamable,
438 }
439 }
440}