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, Tag, universal};
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
29impl<'a> Token<'a> {
30 pub fn read(parser: &'a Parser, document: &'a [u8], base: u64) -> Result<Self> {
33 let mut root = parser
34 .read(document, base)
35 .map_err(|error| Error::encoding(error, "contentInfo"))?;
36 let (envelope, parent) = ContentInfo::read(&mut root)?;
37 parent
38 .execution()
39 .charge_work(envelope.kind().as_bytes().len() as u64)
40 .map_err(|reason| {
41 Error::resource(reason, "contentInfo.contentType", envelope.span().start)
42 })?;
43 if envelope.kind() != oid::SIGNED_DATA {
44 return Err(Error::schema(
45 ErrorCode::UnexpectedContentType,
46 "contentInfo.contentType",
47 envelope.span().start,
48 ));
49 }
50 let message = SignedData::read_element(&parent, envelope.content())?;
51 Self::from_message(message, &parent)
52 }
53
54 pub fn from_message(message: SignedData<'a>, parent: &Reader<'a>) -> Result<Self> {
64 let field = "signedData.signerInfos";
65 let first = message
66 .signers()
67 .first()
68 .ok_or_else(|| Error::schema(ErrorCode::SignerCount, field, message.span().start))?;
69 first.require_session(parent, field)?;
70 let execution = parent.execution();
71 let mut faults = execution.vector();
72 let mut duplicate = None;
73 for extra in &message.signers()[1..] {
74 execution
75 .charge_work(1)
76 .map_err(|reason| Error::resource(reason, field, extra.span().start))?;
77 if !first.same_value(extra, parent)? {
78 return Err(Error::schema(
79 ErrorCode::SignerCount,
80 field,
81 extra.span().start,
82 ));
83 }
84 duplicate.get_or_insert(extra.span().start);
85 }
86 if let Some(at) = duplicate {
87 record(
88 &mut faults,
89 Error::schema(ErrorCode::ExtraSignature, field, at),
90 )?;
91 }
92 record_counters(first, parent, &mut faults)?;
93 let content = message.content();
94 execution
95 .charge_work(content.kind().as_bytes().len() as u64)
96 .map_err(|reason| {
97 Error::resource(
98 reason,
99 "signedData.encapContentInfo.eContentType",
100 content.span().start,
101 )
102 })?;
103 if content.kind() != oid::TST_INFO {
104 return Err(Error::schema(
105 ErrorCode::UnexpectedContentType,
106 "signedData.encapContentInfo.eContentType",
107 content.span().start,
108 ));
109 }
110 if content.content().is_none() {
111 return Err(Error::schema(
112 ErrorCode::DetachedContent,
113 "signedData.encapContentInfo.eContent",
114 content.span().start,
115 ));
116 }
117 Ok(Self {
118 message,
119 faults: faults.into_owned(),
120 })
121 }
122
123 #[must_use]
125 pub fn message(&self) -> &SignedData<'a> {
126 &self.message
127 }
128
129 #[must_use]
131 pub fn signer(&self) -> &crate::signer::SignerInfo<'a> {
132 &self.message.signers()[0]
133 }
134
135 #[must_use]
137 pub fn faults(&self) -> &[Error] {
138 &self.faults
139 }
140
141 #[expect(
148 clippy::result_large_err,
149 reason = "preserve both original cause and accounted source context without allocating an error envelope"
150 )]
151 pub fn with_info<R, E>(
152 &self,
153 parent: &Reader<'_>,
154 callback: impl for<'scope> FnOnce(
155 &TokenInfo<'scope>,
156 &Reader<'scope>,
157 &SourceMap,
158 ) -> core::result::Result<R, E>,
159 ) -> core::result::Result<R, ContentError<InfoError<E>>> {
160 self.inspect(parent, None, callback)
161 }
162
163 #[expect(
169 clippy::result_large_err,
170 reason = "retain original cause and accounted physical source context"
171 )]
172 pub fn with_info_mapped<R, E>(
173 &self,
174 parent: &Reader<'_>,
175 source: &SourceMap,
176 callback: impl for<'scope> FnOnce(
177 &TokenInfo<'scope>,
178 &Reader<'scope>,
179 &SourceMap,
180 ) -> core::result::Result<R, E>,
181 ) -> core::result::Result<R, ContentError<InfoError<E>>> {
182 self.inspect(parent, Some(source), callback)
183 }
184
185 #[expect(
186 clippy::result_large_err,
187 reason = "retain original cause and accounted physical source context"
188 )]
189 fn inspect<R, E>(
190 &self,
191 parent: &Reader<'_>,
192 source: Option<&SourceMap>,
193 callback: impl for<'scope> FnOnce(
194 &TokenInfo<'scope>,
195 &Reader<'scope>,
196 &SourceMap,
197 ) -> core::result::Result<R, E>,
198 ) -> core::result::Result<R, ContentError<InfoError<E>>> {
199 let Some(content) = self.message.content().content() else {
200 return Err(ContentError::Processing(Error::schema(
201 ErrorCode::DetachedContent,
202 "signedData.encapContentInfo.eContent",
203 self.message.content().span().start,
204 )));
205 };
206 let decode = |reader: &mut Reader<'_>, context: &SourceMap| {
207 let element = reader
208 .expect(Tag::constructed(universal::SEQUENCE))
209 .map_err(|error| InfoError::Processing(Error::encoding(error, "tstInfo")))?;
210 let info = TokenInfo::read(reader, &element).map_err(InfoError::Processing)?;
211 if !reader.is_empty() {
212 return Err(InfoError::Processing(Error::schema(
213 ErrorCode::TrailingData,
214 "tstInfo",
215 reader.offset(),
216 )));
217 }
218 callback(&info, reader, context).map_err(InfoError::Callback)
219 };
220 match source {
221 Some(source) => {
222 content.with_nested_reader_mapped(parent, source, Encoding::Ber, "tstInfo", decode)
223 }
224 None => content.with_nested_reader(parent, Encoding::Ber, "tstInfo", decode),
225 }
226 }
227}
228
229fn record_counters<W: Work>(
230 signer: &crate::signer::SignerInfo<'_>,
231 parent: &Reader<'_>,
232 faults: &mut OwnedVec<Error, W>,
233) -> Result<()> {
234 for (attributes, field) in [
239 (
240 signer.signed_attributes(),
241 "signerInfo.signedAttrs.countersignature",
242 ),
243 (
244 signer.unsigned_attributes(),
245 "signerInfo.unsignedAttrs.countersignature",
246 ),
247 ] {
248 let Some(attributes) = attributes else {
249 continue;
250 };
251 for attribute in attributes.all_in(oid::COUNTERSIGNATURE, parent) {
252 let attribute = attribute?;
253 record(
254 faults,
255 Error::schema(ErrorCode::ExtraSignature, field, attribute.span().start),
256 )?;
257 }
258 }
259 Ok(())
260}