structured_cms/timestamp/
token.rs1use super::{TokenInfo, record};
4use crate::content_source::{ContentError, SourceMap};
5use crate::{ContentInfo, Error, ErrorCode, Result, SignedData, oid};
6use structured_asn1::memory::{OwnedVec, Work};
7use structured_asn1::{Encoding, Parser, Reader};
8
9#[derive(Debug)]
15pub struct Token<'a> {
16 message: SignedData<'a>,
17 faults: OwnedVec<Error>,
18}
19
20#[derive(Debug)]
22pub enum InfoError<E> {
23 Processing(Error),
25 Callback(E),
27}
28
29pub trait AsyncInfoVisitor {
32 type Output;
34 type Error;
36 fn visit<'data: 'scope, 'scope>(
38 &'scope mut self,
39 info: &'scope TokenInfo<'data>,
40 reader: &'scope Reader<'data>,
41 source: &'scope SourceMap,
42 ) -> impl core::future::Future<Output = core::result::Result<Self::Output, Self::Error>>;
43}
44
45struct InformationVisitor<'a, V>(&'a mut V);
46
47impl<V: AsyncInfoVisitor> crate::content_source::AsyncVisitor for InformationVisitor<'_, V> {
48 type Output = V::Output;
49 type Error = InfoError<V::Error>;
50 async fn visit<'data: 'scope, 'scope>(
51 &'scope mut self,
52 reader: &'scope mut Reader<'data>,
53 source: &'scope SourceMap,
54 ) -> core::result::Result<Self::Output, Self::Error> {
55 let info = TokenInfo::read_carried(reader).map_err(InfoError::Processing)?;
56 self.0
57 .visit(&info, reader, source)
58 .await
59 .map_err(InfoError::Callback)
60 }
61}
62
63impl<'a> Token<'a> {
64 pub fn read(parser: &'a Parser, document: &'a [u8], base: u64) -> Result<Self> {
67 let mut root = parser
68 .read(document, base)
69 .map_err(|error| Error::encoding(error, "contentInfo"))?;
70 Self::read_from(&mut root)
71 }
72
73 pub fn read_from(root: &mut Reader<'a>) -> Result<Self> {
79 let (envelope, parent) = ContentInfo::read(root)?;
80 parent
81 .execution()
82 .charge_work(envelope.kind().as_bytes().len() as u64)
83 .map_err(|reason| {
84 Error::resource(reason, "contentInfo.contentType", envelope.span().start)
85 })?;
86 if envelope.kind() != oid::SIGNED_DATA {
87 return Err(Error::schema(
88 ErrorCode::UnexpectedContentType,
89 "contentInfo.contentType",
90 envelope.span().start,
91 ));
92 }
93 let message = SignedData::read_element(&parent, envelope.content())?;
94 Self::from_message(message, &parent)
95 }
96
97 pub fn from_message(message: SignedData<'a>, parent: &Reader<'a>) -> Result<Self> {
107 let field = "signedData.signerInfos";
108 let first = message
109 .signers()
110 .first()
111 .ok_or_else(|| Error::schema(ErrorCode::SignerCount, field, message.span().start))?;
112 first.require_session(parent, field)?;
113 let execution = parent.execution();
114 let mut faults = execution.vector();
115 let mut duplicate = None;
116 for extra in &message.signers()[1..] {
117 execution
118 .charge_work(1)
119 .map_err(|reason| Error::resource(reason, field, extra.span().start))?;
120 if !first.same_value(extra, parent)? {
121 return Err(Error::schema(
122 ErrorCode::SignerCount,
123 field,
124 extra.span().start,
125 ));
126 }
127 duplicate.get_or_insert(extra.span().start);
128 }
129 if let Some(at) = duplicate {
130 record(
131 &mut faults,
132 Error::schema(ErrorCode::ExtraSignature, field, at),
133 )?;
134 }
135 record_counters(first, parent, &mut faults)?;
136 let content = message.content();
137 execution
138 .charge_work(content.kind().as_bytes().len() as u64)
139 .map_err(|reason| {
140 Error::resource(
141 reason,
142 "signedData.encapContentInfo.eContentType",
143 content.span().start,
144 )
145 })?;
146 if content.kind() != oid::TST_INFO {
147 return Err(Error::schema(
148 ErrorCode::UnexpectedContentType,
149 "signedData.encapContentInfo.eContentType",
150 content.span().start,
151 ));
152 }
153 if content.content().is_none() {
154 return Err(Error::schema(
155 ErrorCode::DetachedContent,
156 "signedData.encapContentInfo.eContent",
157 content.span().start,
158 ));
159 }
160 Ok(Self {
161 message,
162 faults: faults.into_owned(),
163 })
164 }
165
166 #[must_use]
168 pub fn message(&self) -> &SignedData<'a> {
169 &self.message
170 }
171
172 #[must_use]
174 pub fn signer(&self) -> &crate::signer::SignerInfo<'a> {
175 &self.message.signers()[0]
176 }
177
178 #[must_use]
180 pub fn faults(&self) -> &[Error] {
181 &self.faults
182 }
183
184 #[expect(
195 clippy::result_large_err,
196 reason = "preserve both original cause and accounted source context without allocating an error envelope"
197 )]
198 pub fn with_info<R, E>(
199 &self,
200 parent: &Reader<'_>,
201 callback: impl for<'scope> FnOnce(
202 &TokenInfo<'scope>,
203 &Reader<'scope>,
204 &SourceMap,
205 ) -> core::result::Result<R, E>,
206 ) -> core::result::Result<R, ContentError<InfoError<E>>> {
207 self.inspect(parent, None, callback)
208 }
209
210 #[expect(
214 clippy::result_large_err,
215 reason = "the original source map accompanies decoding and callback failures after cancellation-safe scoped inspection"
216 )]
217 pub async fn with_info_async<V: AsyncInfoVisitor>(
218 &self,
219 parent: &Reader<'_>,
220 origin: Option<&SourceMap>,
221 visitor: &mut V,
222 ) -> core::result::Result<V::Output, ContentError<InfoError<V::Error>>> {
223 let Some(content) = self.message.content().content() else {
224 return Err(ContentError::Processing(Error::schema(
225 ErrorCode::DetachedContent,
226 "signedData.encapContentInfo.eContent",
227 self.message.content().span().start,
228 )));
229 };
230 content
231 .with_nested_reader_async(
232 parent,
233 origin,
234 Encoding::Ber,
235 "tstInfo",
236 &mut InformationVisitor(visitor),
237 )
238 .await
239 }
240
241 #[expect(
247 clippy::result_large_err,
248 reason = "retain original cause and accounted physical source context"
249 )]
250 pub fn with_info_mapped<R, E>(
251 &self,
252 parent: &Reader<'_>,
253 source: &SourceMap,
254 callback: impl for<'scope> FnOnce(
255 &TokenInfo<'scope>,
256 &Reader<'scope>,
257 &SourceMap,
258 ) -> core::result::Result<R, E>,
259 ) -> core::result::Result<R, ContentError<InfoError<E>>> {
260 self.inspect(parent, Some(source), callback)
261 }
262
263 #[expect(
264 clippy::result_large_err,
265 reason = "retain original cause and accounted physical source context"
266 )]
267 fn inspect<R, E>(
268 &self,
269 parent: &Reader<'_>,
270 source: Option<&SourceMap>,
271 callback: impl for<'scope> FnOnce(
272 &TokenInfo<'scope>,
273 &Reader<'scope>,
274 &SourceMap,
275 ) -> core::result::Result<R, E>,
276 ) -> core::result::Result<R, ContentError<InfoError<E>>> {
277 let Some(content) = self.message.content().content() else {
278 return Err(ContentError::Processing(Error::schema(
279 ErrorCode::DetachedContent,
280 "signedData.encapContentInfo.eContent",
281 self.message.content().span().start,
282 )));
283 };
284 let decode = |reader: &mut Reader<'_>, context: &SourceMap| {
285 let info = TokenInfo::read_carried(reader).map_err(InfoError::Processing)?;
286 callback(&info, reader, context).map_err(InfoError::Callback)
287 };
288 match source {
289 Some(source) => {
290 content.with_nested_reader_mapped(parent, source, Encoding::Ber, "tstInfo", decode)
291 }
292 None => content.with_nested_reader(parent, Encoding::Ber, "tstInfo", decode),
293 }
294 }
295}
296
297fn record_counters<W: Work>(
298 signer: &crate::signer::SignerInfo<'_>,
299 parent: &Reader<'_>,
300 faults: &mut OwnedVec<Error, W>,
301) -> Result<()> {
302 for (attributes, field) in [
307 (
308 signer.signed_attributes(),
309 "signerInfo.signedAttrs.countersignature",
310 ),
311 (
312 signer.unsigned_attributes(),
313 "signerInfo.unsignedAttrs.countersignature",
314 ),
315 ] {
316 let Some(attributes) = attributes else {
317 continue;
318 };
319 for attribute in attributes.all_in(oid::COUNTERSIGNATURE, parent) {
320 let attribute = attribute?;
321 record(
322 faults,
323 Error::schema(ErrorCode::ExtraSignature, field, attribute.span().start),
324 )?;
325 }
326 }
327 Ok(())
328}