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