1use crate::{
4 binding::Mapping,
5 timestamp::{Node, Pades},
6};
7use crate::{
8 document::Document,
9 error::{Error, ErrorKind, ReadFailure},
10 prepare::Preparation,
11 range::RangeSource,
12 signature::CoveredError,
13};
14use structured_asn1::{Reader, memory::Work};
15use structured_cades::{
16 basic::{BindError, SignerBinding},
17 counters::Paths,
18};
19use structured_cms::{
20 prepare::{self, PrepareError},
21 provider::SignatureProvider,
22 verify::{self, ContentMode, DocumentBatch, DocumentChecks},
23};
24use structured_ltv::pki::{CounterReceipts, SignerReceipt};
25use structured_pkix::{Certificate, algorithm::digest::AsnDigest, path::WorkingKey};
26
27#[derive(Debug)]
30pub enum CheckError<E, P> {
31 Pdf(ReadFailure<E>),
33 Prepare(PrepareError<P>),
35 Check(verify::CheckError<P>),
37 Pass(verify::PassError<CoveredError<E>, P>),
39 Unsupported(prepare::Part),
41 Malformed(structured_cms::Error),
43}
44
45pub struct Mathematical<'v, 'p, 'm, 'd, 'a, 's, D, G> {
48 prepared: &'v Preparation<'p, 'm, 'd, 'a, 's, D, G>,
49 checks: DocumentChecks<'v>,
50}
51
52impl<'v, 'p, 'm, 'd, 'a, 's, D: Copy + Eq, G: Copy + Eq>
53 Mathematical<'v, 'p, 'm, 'd, 'a, 's, D, G>
54{
55 #[must_use]
57 pub fn checks(&self) -> &DocumentChecks<'v> {
58 &self.checks
59 }
60
61 #[must_use]
63 pub fn signature(&self) -> crate::signature::Signature<'a, 's, D, G> {
64 self.prepared.container.signature()
65 }
66
67 #[must_use]
70 pub fn input_verified(&self) -> bool {
71 self.checks.checks().first().is_some_and(|checked| {
72 matches!(
73 checked.content(),
74 verify::ContentAgreement::Matches | verify::ContentAgreement::Direct
75 ) && checked.signature() == Some(structured_cms::provider::SignatureAnswer::Valid)
76 })
77 }
78
79 pub fn bind<'r, 'path, 'q, P: AsnDigest<'q>>(
86 &'r self,
87 certificate: &'path Certificate<'d>,
88 path: Option<&'path structured_ltv::pki::CheckedPath<'d>>,
89 provider: &'q P,
90 parent: &Reader<'q>,
91 ) -> Result<Bound<'v, 'p, 'm, 'd, 'a, 's, 'path, D, G>, BindError<P::Error>>
92 where
93 'd: 'q,
94 {
95 let checked = self.checks.checks().first().expect("one prepared root");
96 let binding = structured_cades::basic::bind(
97 self.prepared.cms,
98 &self.checks,
99 checked,
100 certificate,
101 path,
102 provider,
103 parent,
104 )?;
105 let source = binding.signature();
106 let signature = self.prepared.container.signature();
107 let mapping = Mapping {
108 snapshot: signature.owner().value.stamp.snapshot,
109 root: Node::of(signature.location()),
110 carrier: signature.contents(),
111 cms_root: source.node,
112 cms: source.snapshot,
113 };
114 Ok(Bound {
115 prepared: self.prepared,
116 binding,
117 mapping,
118 })
119 }
120}
121
122pub struct Bound<'v, 'p, 'm, 'd, 'a, 's, 'path, D, G> {
125 prepared: &'v Preparation<'p, 'm, 'd, 'a, 's, D, G>,
126 binding: SignerBinding<'p, 'm, 'd, 'path>,
127 pub(crate) mapping: Mapping<'m, 'd, D, G>,
128}
129
130impl<'d, 'path, D: Copy + Eq, G: Copy + Eq> SignerReceipt<'path, 'd>
131 for Bound<'_, '_, '_, 'd, '_, '_, 'path, D, G>
132{
133 type Id = Node;
134 type Document = D;
135 type Generation = G;
136 fn signature(&self) -> structured_ltv::source::Location<Node, D, G> {
137 structured_ltv::source::Location {
138 snapshot: self.mapping.snapshot,
139 node: self.mapping.root,
140 }
141 }
142 fn path(&self) -> Option<&'path structured_ltv::pki::CheckedPath<'d>> {
143 self.binding.path()
144 }
145}
146
147pub struct Counters<'r, 'm, 'd, 'path, D, G> {
149 pub(crate) common: Paths<'r, 'm, 'd, 'path>,
150 mapping: Mapping<'m, 'd, D, G>,
151}
152impl<'d, 'path, D: Copy + Eq, G: Copy + Eq> CounterReceipts<'path, 'd>
153 for Counters<'_, '_, 'd, 'path, D, G>
154{
155 type Id = Node;
156 type Document = D;
157 type Generation = G;
158 fn snapshot(&self) -> structured_ltv::source::Snapshot<D, G> {
159 self.mapping.snapshot
160 }
161 fn signature(&self) -> Node {
162 self.mapping.root
163 }
164 fn len(&self) -> usize {
165 self.common.len()
166 }
167 fn counter(&self, ordinal: usize) -> structured_ltv::pki::Counter<'path, 'd, Node> {
168 let actual = self.common.counter(ordinal);
169 structured_ltv::pki::Counter {
170 signature: self.mapping.node(actual.signature),
171 property: self.mapping.node(actual.property),
172 path: actual.path,
173 }
174 }
175}
176
177impl<'v, 'p, 'm, 'd, 'a, 's, 'path, D: Copy + Eq, G: Copy + Eq>
178 Bound<'v, 'p, 'm, 'd, 'a, 's, 'path, D, G>
179{
180 #[must_use]
182 pub fn binding(&self) -> &SignerBinding<'p, 'm, 'd, 'path> {
183 &self.binding
184 }
185
186 #[must_use]
188 pub fn prepared(&self) -> &'v Preparation<'p, 'm, 'd, 'a, 's, D, G> {
189 self.prepared
190 }
191}
192
193pub struct Basic<'r, 'v, 'p, 'm, 'd, 'a, 's, 'path, D, G> {
196 bound: &'r Bound<'v, 'p, 'm, 'd, 'a, 's, 'path, D, G>,
197 counters: Counters<'r, 'm, 'd, 'path, D, G>,
198 complete: bool,
199}
200
201impl<D: Copy + Eq, G: Copy + Eq> Basic<'_, '_, '_, '_, '_, '_, '_, '_, D, G> {
202 #[must_use]
205 pub fn all_basic_requirements_met(&self) -> bool {
206 self.complete
207 }
208}
209
210impl<'r, 'v, 'p, 'm, 'd, 'a, 's, 'path, D: Copy + Eq, G: Copy + Eq>
211 Basic<'r, 'v, 'p, 'm, 'd, 'a, 's, 'path, D, G>
212{
213 #[must_use]
215 pub fn binding(&self) -> &'r Bound<'v, 'p, 'm, 'd, 'a, 's, 'path, D, G> {
216 self.bound
217 }
218 #[must_use]
220 pub fn counters(&self) -> &Counters<'r, 'm, 'd, 'path, D, G> {
221 &self.counters
222 }
223 pub fn timestamp_actor<'c>(
227 &self,
228 actor: structured_ltv::timestamp::CheckedActor<
229 'c,
230 'd,
231 structured_cades::source::Node,
232 structured_cades::source::DocumentId<'m, 'd>,
233 (),
234 structured_cades::Cades,
235 >,
236 operation: &impl Work,
237 ) -> crate::Result<structured_ltv::timestamp::CheckedActor<'c, 'd, Node, D, G, Pades>> {
238 self.bound.mapping.actor(
239 actor,
240 &self
241 .bound
242 .prepared
243 .container
244 .signature()
245 .owner()
246 .value
247 .session
248 .query(operation),
249 )
250 }
251}
252
253impl<'r, 'v, 'p, 'm, 'd, 'a, 's, 'path, D: Copy + Eq, G: Copy + Eq>
254 structured_ltv::baseline::BasicStage<'path, 'd>
255 for Basic<'r, 'v, 'p, 'm, 'd, 'a, 's, 'path, D, G>
256{
257 type Id = Node;
258 type Document = D;
259 type Generation = G;
260 type Signer = Bound<'v, 'p, 'm, 'd, 'a, 's, 'path, D, G>;
261 type Counters = Counters<'r, 'm, 'd, 'path, D, G>;
262 fn signer(&self) -> &Self::Signer {
263 self.bound
264 }
265 fn counters(&self) -> &Self::Counters {
266 &self.counters
267 }
268 fn all_requirements_met(&self) -> bool {
269 self.complete
270 }
271}
272
273pub fn basic<'r, 'v, 'p, 'm, 'd, 'a, 's, 'path, D: Copy + Eq, G: Copy + Eq>(
277 bound: &'r Bound<'v, 'p, 'm, 'd, 'a, 's, 'path, D, G>,
278 counters: Paths<'r, 'm, 'd, 'path>,
279 parent: &Reader<'_>,
280) -> structured_cades::Result<Basic<'r, 'v, 'p, 'm, 'd, 'a, 's, 'path, D, G>> {
281 let common = structured_cades::basic::basic(&bound.binding, counters, parent)?;
282 let counters = Counters {
283 common: common.into_counters(),
284 mapping: bound.mapping,
285 };
286 let complete = bound.prepared.all_basic_format_requirements_met() && bound.binding.is_bound();
287 Ok(Basic {
288 bound,
289 counters,
290 complete,
291 })
292}
293
294impl<'p, 'm, 'd, 'a, 's, D: Copy + Eq, G: Copy + Eq> Preparation<'p, 'm, 'd, 'a, 's, D, G> {
295 pub async fn verify<'v, S, P>(
299 &'v self,
300 document: &'v Document<'s, S>,
301 provider: &'v P,
302 key: WorkingKey<'v>,
303 parent: &Reader<'v>,
304 ) -> Result<Mathematical<'v, 'p, 'm, 'd, 'a, 's, D, G>, CheckError<S::Error, P::Error>>
305 where
306 'm: 'v,
307 'd: 'v,
308 'a: 'v,
309 's: 'v,
310 S: RangeSource<Document = D, Generation = G>,
311 P: SignatureProvider<'v>,
312 {
313 let message = self.cms.document().message();
314 self.container
315 .require(message, parent)
316 .map_err(|cause| CheckError::Pdf(cause.into()))?;
317 let work = parent.execution();
318 if message.signers().len() != 1 || !message.content().is_detached() {
319 return Err(CheckError::Pdf(
320 Error {
321 kind: ErrorKind::Syntax(crate::error::Syntax::Container),
322 location: self.container.signature().contents(),
323 }
324 .into(),
325 ));
326 }
327 let plan = match prepare::resolve(
328 provider,
329 self.cms.signer(),
330 key,
331 parent,
332 structured_cms::binding::Scope::Content(message.content().kind()),
333 )
334 .map_err(CheckError::Prepare)?
335 {
336 prepare::Preparation::Ready(plan) => plan,
337 prepare::Preparation::Unsupported(part) => return Err(CheckError::Unsupported(part)),
338 prepare::Preparation::Malformed(cause) => return Err(CheckError::Malformed(cause)),
339 };
340 let mut requests = work.vector();
341 requests.try_push(plan).map_err(|cause| {
342 CheckError::Pdf(
343 Error {
344 kind: ErrorKind::Resource(cause),
345 location: self.container.signature().location(),
346 }
347 .into(),
348 )
349 })?;
350 let batch = DocumentBatch::prepare(
351 provider,
352 message,
353 requests.into_owned(),
354 ContentMode::Detached,
355 parent,
356 )
357 .map_err(CheckError::Check)?;
358 let mut source = document
359 .covered(self.container.signature(), &work)
360 .map_err(CheckError::Pdf)?;
361 let checks = batch
362 .run_detached_async(&mut source)
363 .await
364 .map_err(CheckError::Pass)?;
365 Ok(Mathematical {
366 prepared: self,
367 checks,
368 })
369 }
370}