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
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 Self::read_from(&mut root)
37 }
38
39 pub fn read_from(root: &mut Reader<'a>) -> Result<Self> {
45 let (envelope, parent) = ContentInfo::read(root)?;
46 parent
47 .execution()
48 .charge_work(envelope.kind().as_bytes().len() as u64)
49 .map_err(|reason| {
50 Error::resource(reason, "contentInfo.contentType", envelope.span().start)
51 })?;
52 if envelope.kind() != oid::SIGNED_DATA {
53 return Err(Error::schema(
54 ErrorCode::UnexpectedContentType,
55 "contentInfo.contentType",
56 envelope.span().start,
57 ));
58 }
59 let message = SignedData::read_element(&parent, envelope.content())?;
60 Self::from_message(message, &parent)
61 }
62
63 pub fn from_message(message: SignedData<'a>, parent: &Reader<'a>) -> Result<Self> {
73 let field = "signedData.signerInfos";
74 let first = message
75 .signers()
76 .first()
77 .ok_or_else(|| Error::schema(ErrorCode::SignerCount, field, message.span().start))?;
78 first.require_session(parent, field)?;
79 let execution = parent.execution();
80 let mut faults = execution.vector();
81 let mut duplicate = None;
82 for extra in &message.signers()[1..] {
83 execution
84 .charge_work(1)
85 .map_err(|reason| Error::resource(reason, field, extra.span().start))?;
86 if !first.same_value(extra, parent)? {
87 return Err(Error::schema(
88 ErrorCode::SignerCount,
89 field,
90 extra.span().start,
91 ));
92 }
93 duplicate.get_or_insert(extra.span().start);
94 }
95 if let Some(at) = duplicate {
96 record(
97 &mut faults,
98 Error::schema(ErrorCode::ExtraSignature, field, at),
99 )?;
100 }
101 record_counters(first, parent, &mut faults)?;
102 let content = message.content();
103 execution
104 .charge_work(content.kind().as_bytes().len() as u64)
105 .map_err(|reason| {
106 Error::resource(
107 reason,
108 "signedData.encapContentInfo.eContentType",
109 content.span().start,
110 )
111 })?;
112 if content.kind() != oid::TST_INFO {
113 return Err(Error::schema(
114 ErrorCode::UnexpectedContentType,
115 "signedData.encapContentInfo.eContentType",
116 content.span().start,
117 ));
118 }
119 if content.content().is_none() {
120 return Err(Error::schema(
121 ErrorCode::DetachedContent,
122 "signedData.encapContentInfo.eContent",
123 content.span().start,
124 ));
125 }
126 Ok(Self {
127 message,
128 faults: faults.into_owned(),
129 })
130 }
131
132 #[must_use]
134 pub fn message(&self) -> &SignedData<'a> {
135 &self.message
136 }
137
138 #[must_use]
140 pub fn signer(&self) -> &crate::signer::SignerInfo<'a> {
141 &self.message.signers()[0]
142 }
143
144 #[must_use]
146 pub fn faults(&self) -> &[Error] {
147 &self.faults
148 }
149
150 #[expect(
161 clippy::result_large_err,
162 reason = "preserve both original cause and accounted source context without allocating an error envelope"
163 )]
164 pub fn with_info<R, E>(
165 &self,
166 parent: &Reader<'_>,
167 callback: impl for<'scope> FnOnce(
168 &TokenInfo<'scope>,
169 &Reader<'scope>,
170 &SourceMap,
171 ) -> core::result::Result<R, E>,
172 ) -> core::result::Result<R, ContentError<InfoError<E>>> {
173 self.inspect(parent, None, callback)
174 }
175
176 #[expect(
182 clippy::result_large_err,
183 reason = "retain original cause and accounted physical source context"
184 )]
185 pub fn with_info_mapped<R, E>(
186 &self,
187 parent: &Reader<'_>,
188 source: &SourceMap,
189 callback: impl for<'scope> FnOnce(
190 &TokenInfo<'scope>,
191 &Reader<'scope>,
192 &SourceMap,
193 ) -> core::result::Result<R, E>,
194 ) -> core::result::Result<R, ContentError<InfoError<E>>> {
195 self.inspect(parent, Some(source), callback)
196 }
197
198 #[expect(
199 clippy::result_large_err,
200 reason = "retain original cause and accounted physical source context"
201 )]
202 fn inspect<R, E>(
203 &self,
204 parent: &Reader<'_>,
205 source: Option<&SourceMap>,
206 callback: impl for<'scope> FnOnce(
207 &TokenInfo<'scope>,
208 &Reader<'scope>,
209 &SourceMap,
210 ) -> core::result::Result<R, E>,
211 ) -> core::result::Result<R, ContentError<InfoError<E>>> {
212 let Some(content) = self.message.content().content() else {
213 return Err(ContentError::Processing(Error::schema(
214 ErrorCode::DetachedContent,
215 "signedData.encapContentInfo.eContent",
216 self.message.content().span().start,
217 )));
218 };
219 let decode = |reader: &mut Reader<'_>, context: &SourceMap| {
220 let info = TokenInfo::read_carried(reader).map_err(InfoError::Processing)?;
221 callback(&info, reader, context).map_err(InfoError::Callback)
222 };
223 match source {
224 Some(source) => {
225 content.with_nested_reader_mapped(parent, source, Encoding::Ber, "tstInfo", decode)
226 }
227 None => content.with_nested_reader(parent, Encoding::Ber, "tstInfo", decode),
228 }
229 }
230}
231
232fn record_counters<W: Work>(
233 signer: &crate::signer::SignerInfo<'_>,
234 parent: &Reader<'_>,
235 faults: &mut OwnedVec<Error, W>,
236) -> Result<()> {
237 for (attributes, field) in [
242 (
243 signer.signed_attributes(),
244 "signerInfo.signedAttrs.countersignature",
245 ),
246 (
247 signer.unsigned_attributes(),
248 "signerInfo.unsignedAttrs.countersignature",
249 ),
250 ] {
251 let Some(attributes) = attributes else {
252 continue;
253 };
254 for attribute in attributes.all_in(oid::COUNTERSIGNATURE, parent) {
255 let attribute = attribute?;
256 record(
257 faults,
258 Error::schema(ErrorCode::ExtraSignature, field, attribute.span().start),
259 )?;
260 }
261 }
262 Ok(())
263}