1use 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 pub async fn run_detached_async<S: crate::stream::AsyncSource>(
128 self,
129 source: &mut S,
130 ) -> Result<DocumentChecks<'a>, PassError<S::Error, P::Error>> {
131 if self.mode != ContentMode::Detached || !self.message.content().is_detached() {
132 return Err(crate::stream::StreamError::Processing(Error::schema(
133 ErrorCode::InvalidLocation,
134 "signedData.encapContentInfo",
135 self.message.content().span().start,
136 )));
137 }
138 let checks = self.batch.run_async(source, &self.execution).await?;
139 Ok(DocumentChecks {
140 message: self.message,
141 checks,
142 })
143 }
144}
145
146pub struct DocumentChecks<'a> {
150 message: &'a SignedData<'a>,
151 checks: OwnedVec<Checked<'a>>,
152}
153
154impl<'a> DocumentChecks<'a> {
155 #[must_use]
157 pub fn message(&self) -> &'a SignedData<'a> {
158 self.message
159 }
160 #[must_use]
162 pub fn checks(&self) -> &[Checked<'a>] {
163 &self.checks
164 }
165
166 pub fn require<W: Work>(
170 &self,
171 message: &SignedData<'_>,
172 checked: &Checked<'_>,
173 execution: &W,
174 ) -> crate::Result<()> {
175 let at = checked.signer().span().start;
176 execution
177 .charge_work(1)
178 .map_err(|cause| Error::resource(cause, "signedData.signerInfos", at))?;
179 let address = core::ptr::from_ref(checked).cast::<Checked<'a>>();
180 if !core::ptr::eq(message, self.message) || !self.checks.as_ptr_range().contains(&address) {
181 return Err(Error::schema(
182 ErrorCode::InvalidLocation,
183 "signedData.signerInfos",
184 at,
185 ));
186 }
187 Ok(())
188 }
189}