structured_cms/verify/
document.rs1use super::{Batch, CheckError, Checked, PassError};
4use crate::prepare::ResolvedSigner;
5use crate::provider::SignatureProvider;
6use crate::stream::Source;
7use crate::{Error, ErrorCode, SignedData};
8use structured_asn1::Reader;
9use structured_asn1::memory::{Execution, OwnedVec, Work};
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum ContentMode {
14 Carried,
16 Detached,
18}
19
20pub struct DocumentBatch<'operation, 'a, P: SignatureProvider<'a>> {
23 message: &'a SignedData<'a>,
24 batch: Batch<'a, P>,
25 mode: ContentMode,
26 execution: Execution<'operation>,
27}
28
29impl<'operation, 'a, P: SignatureProvider<'a>> DocumentBatch<'operation, 'a, P> {
30 pub fn prepare(
35 provider: &'a P,
36 message: &'a SignedData<'a>,
37 requests: OwnedVec<ResolvedSigner<'a, P>>,
38 mode: ContentMode,
39 parent: &'operation Reader<'a>,
40 ) -> Result<Self, CheckError<P::Error>> {
41 const FIELD: &str = "signedData.encapContentInfo";
42 message
43 .require_session(parent, FIELD)
44 .map_err(CheckError::Processing)?;
45 let execution = parent.execution();
46 execution.charge_work(1).map_err(|cause| {
47 CheckError::Processing(Error::resource(
48 cause,
49 FIELD,
50 message.content().span().start,
51 ))
52 })?;
53 if (mode == ContentMode::Detached) != message.content().is_detached() {
54 return Err(CheckError::Processing(Error::schema(
55 ErrorCode::InvalidLocation,
56 FIELD,
57 message.content().span().start,
58 )));
59 }
60 for request in &requests {
61 execution.charge_work(1).map_err(|cause| {
62 CheckError::Processing(Error::resource(cause, FIELD, request.signer().span().start))
63 })?;
64 message
65 .require_signer(request.signer(), "signedData.signerInfos")
66 .map_err(CheckError::Processing)?;
67 }
68 let batch = Batch::prepare(provider, requests, &execution, FIELD, message.span().start)?;
69 Ok(Self {
70 message,
71 batch,
72 mode,
73 execution,
74 })
75 }
76
77 pub fn run_carried(
81 self,
82 ) -> Result<DocumentChecks<'a>, PassError<crate::octets::SourceError, P::Error>> {
83 let refusal = || {
84 crate::stream::StreamError::Processing(Error::schema(
85 ErrorCode::InvalidLocation,
86 "signedData.encapContentInfo",
87 self.message.content().span().start,
88 ))
89 };
90 if self.mode != ContentMode::Carried {
91 return Err(refusal());
92 }
93 let Some(mut content) = self.message.content().content() else {
94 return Err(refusal());
95 };
96 let checks = self.batch.run(&mut content, &self.execution)?;
97 Ok(DocumentChecks {
98 message: self.message,
99 checks,
100 })
101 }
102
103 pub fn run_detached<S: Source>(
107 self,
108 source: &mut S,
109 ) -> Result<DocumentChecks<'a>, PassError<S::Error, P::Error>> {
110 if self.mode != ContentMode::Detached || !self.message.content().is_detached() {
111 return Err(crate::stream::StreamError::Processing(Error::schema(
112 ErrorCode::InvalidLocation,
113 "signedData.encapContentInfo",
114 self.message.content().span().start,
115 )));
116 }
117 let checks = self.batch.run(source, &self.execution)?;
118 Ok(DocumentChecks {
119 message: self.message,
120 checks,
121 })
122 }
123}
124
125pub struct DocumentChecks<'a> {
129 message: &'a SignedData<'a>,
130 checks: OwnedVec<Checked<'a>>,
131}
132
133impl<'a> DocumentChecks<'a> {
134 #[must_use]
136 pub fn message(&self) -> &'a SignedData<'a> {
137 self.message
138 }
139 #[must_use]
141 pub fn checks(&self) -> &[Checked<'a>] {
142 &self.checks
143 }
144
145 pub fn require<W: Work>(
149 &self,
150 message: &SignedData<'_>,
151 checked: &Checked<'_>,
152 execution: &W,
153 ) -> crate::Result<()> {
154 let at = checked.signer().span().start;
155 execution
156 .charge_work(1)
157 .map_err(|cause| Error::resource(cause, "signedData.signerInfos", at))?;
158 let address = core::ptr::from_ref(checked).cast::<Checked<'a>>();
159 if !core::ptr::eq(message, self.message) || !self.checks.as_ptr_range().contains(&address) {
160 return Err(Error::schema(
161 ErrorCode::InvalidLocation,
162 "signedData.signerInfos",
163 at,
164 ));
165 }
166 Ok(())
167 }
168}