1use crate::attribute::{Attribute, Attributes};
4use crate::signer::SignerInfo;
5use crate::{Error, ErrorCode, Octets, Result, oid};
6use structured_asn1::memory::Work;
7use structured_asn1::{Oid, Reader, Span, Tag, universal};
8use structured_pkix::AlgorithmIdentifier;
9
10#[derive(Debug)]
12pub enum DigestBinding {
13 Matches,
15 Differs,
17 Malformed(Error),
19}
20
21#[derive(Debug)]
23pub enum BindError<E> {
24 Processing(Error),
26 Callback(E),
28 OutputWidth {
30 expected: usize,
32 actual: usize,
34 },
35}
36
37pub fn matches_digest<'a, P: crate::provider::DigestProvider<'a>, W: Work>(
46 provider: &P,
47 digest: crate::provider::Prehash<'_, '_, P, P::Identity>,
48 expected: &[u8],
49 execution: &W,
50 field: &'static str,
51 span: Span,
52) -> core::result::Result<DigestBinding, BindError<P::Error>> {
53 crate::provider::require_instance(provider, field, span.start)
54 .map_err(BindError::Processing)?;
55 if !digest.from_provider(provider) {
56 return Err(BindError::Processing(Error::schema(
57 ErrorCode::ProviderMismatch,
58 field,
59 span.start,
60 )));
61 }
62 execution
63 .charge_work(1)
64 .map_err(|reason| BindError::Processing(Error::resource(reason, field, span.start)))?;
65 let actual = digest.as_bytes().len();
66 if let Some(width) = provider.digest_width(digest.identity())
67 && width != actual
68 {
69 return Err(BindError::OutputWidth {
70 expected: width,
71 actual,
72 });
73 }
74 if expected.len() != actual {
75 return Ok(DigestBinding::Malformed(Error::schema(
76 ErrorCode::DigestWidth,
77 field,
78 span.start,
79 )));
80 }
81 if provider
82 .matches_digest(digest, expected, execution)
83 .map_err(BindError::Callback)?
84 {
85 Ok(DigestBinding::Matches)
86 } else {
87 Ok(DigestBinding::Differs)
88 }
89}
90
91#[derive(Debug, Clone, Copy)]
93pub enum Scope<'a> {
94 Content(Oid<'a>),
96 CounterSignature,
98}
99
100#[derive(Debug, Clone, Copy)]
102pub struct Protection<'a> {
103 digest: AlgorithmIdentifier<'a>,
104 signature: AlgorithmIdentifier<'a>,
105}
106
107impl<'a> Protection<'a> {
108 #[must_use]
110 pub fn digest(&self) -> AlgorithmIdentifier<'a> {
111 self.digest
112 }
113 #[must_use]
115 pub fn signature(&self) -> AlgorithmIdentifier<'a> {
116 self.signature
117 }
118}
119
120#[derive(Debug)]
122pub enum Binding<'a> {
123 Direct,
125 Attributes {
127 digest: &'a [u8],
129 digest_span: Span,
131 protection: Option<Protection<'a>>,
133 },
134}
135
136pub fn inspect<'a>(
146 signer: &SignerInfo<'a>,
147 parent: &Reader<'a>,
148 scope: Scope<'a>,
149) -> Result<Binding<'a>> {
150 signer.require_session(parent, "signerInfo")?;
151 check_placements(signer, parent)?;
152 let Some(attributes) = signer.signed_attributes() else {
153 if let Scope::Content(kind) = scope {
154 parent
155 .execution()
156 .charge_work(kind.as_bytes().len() as u64)
157 .map_err(|reason| {
158 Error::resource(reason, "signerInfo.signedAttrs", signer.span().start)
159 })?;
160 if kind != oid::DATA {
161 return Err(Error::schema(
162 ErrorCode::MissingField,
163 "signerInfo.signedAttrs",
164 signer.span().start,
165 ));
166 }
167 }
168 return Ok(Binding::Direct);
169 };
170 let content_type = attributes.unique_in(oid::CONTENT_TYPE, parent)?;
171 match scope {
172 Scope::CounterSignature => {
173 if let Some(attribute) = content_type {
174 return Err(Error::schema(
175 ErrorCode::AttributePlacement,
176 "signerInfo.signedAttrs.contentType",
177 attribute.span().start,
178 ));
179 }
180 }
181 Scope::Content(expected) => {
182 let attribute = content_type.ok_or_else(|| {
183 Error::schema(
184 ErrorCode::MissingField,
185 "signerInfo.signedAttrs.contentType",
186 attributes.element().span().start,
187 )
188 })?;
189 attribute.single("signerInfo.signedAttrs.contentType")?;
190 let mut reader = value_reader(attribute, parent, "signerInfo.signedAttrs.contentType")?;
191 let declared = reader
192 .read_oid()
193 .map_err(|error| Error::encoding(error, "signerInfo.signedAttrs.contentType"))?;
194 parent
195 .execution()
196 .charge_work(declared.as_bytes().len() as u64)
197 .map_err(|reason| {
198 Error::resource(
199 reason,
200 "signerInfo.signedAttrs.contentType",
201 attribute.span().start,
202 )
203 })?;
204 if declared != expected {
205 return Err(Error::schema(
206 ErrorCode::ContentTypeMismatch,
207 "signerInfo.signedAttrs.contentType",
208 attribute.span().start,
209 ));
210 }
211 }
212 }
213 let attribute = attributes
214 .unique_in(oid::MESSAGE_DIGEST, parent)?
215 .ok_or_else(|| {
216 Error::schema(
217 ErrorCode::MissingField,
218 "signerInfo.signedAttrs.messageDigest",
219 attributes.element().span().start,
220 )
221 })?;
222 attribute.single("signerInfo.signedAttrs.messageDigest")?;
223 let mut reader = value_reader(attribute, parent, "signerInfo.signedAttrs.messageDigest")?;
224 let element = crate::signer::required(&mut reader, "signerInfo.signedAttrs.messageDigest")?;
225 let digest = Octets::read(&reader, &element, "signerInfo.signedAttrs.messageDigest")?;
226 let digest = digest.as_one().ok_or_else(|| {
230 Error::schema(
231 ErrorCode::UnexpectedTag,
232 "signerInfo.signedAttrs.messageDigest",
233 element.span().start,
234 )
235 })?;
236 check_signing_time(attributes, parent)?;
237 let protection = read_protection(attributes, parent)?;
238 Ok(Binding::Attributes {
239 digest,
240 digest_span: element.span(),
241 protection,
242 })
243}
244
245fn check_placements(signer: &SignerInfo<'_>, parent: &Reader<'_>) -> Result<()> {
246 if let Some(unsigned) = signer.unsigned_attributes() {
247 for kind in [
250 oid::CONTENT_TYPE,
251 oid::MESSAGE_DIGEST,
252 oid::SIGNING_TIME,
253 oid::ALGORITHM_PROTECTION,
254 ] {
255 if let Some(result) = unsigned.all_in(kind, parent).next() {
256 let attribute = result?;
257 return Err(Error::schema(
258 ErrorCode::AttributePlacement,
259 "signerInfo.unsignedAttrs",
260 attribute.span().start,
261 ));
262 }
263 }
264 }
265 if let Some(signed) = signer.signed_attributes()
266 && let Some(result) = signed.all_in(oid::COUNTERSIGNATURE, parent).next()
267 {
268 let attribute = result?;
269 return Err(Error::schema(
270 ErrorCode::AttributePlacement,
271 "signerInfo.signedAttrs.counterSignature",
272 attribute.span().start,
273 ));
274 }
275 Ok(())
276}
277
278fn check_signing_time(attributes: &Attributes<'_>, parent: &Reader<'_>) -> Result<()> {
279 const FIELD: &str = "signerInfo.signedAttrs.signingTime";
280 let Some(attribute) = attributes.unique_in(oid::SIGNING_TIME, parent)? else {
281 return Ok(());
282 };
283 let value = attribute.single(FIELD)?;
284 parent
285 .execution()
286 .charge_work(9)
287 .map_err(|reason| Error::resource(reason, FIELD, value.span().start))?;
288 let valid = if value.tag() == Tag::primitive(universal::UTC_TIME) {
293 value.contents().len() == 13
294 } else if value.tag() == Tag::primitive(universal::GENERALIZED_TIME) {
295 value.contents().len() == 15
296 && !(value.contents()[..4] >= b"1950"[..] && value.contents()[..4] <= b"2049"[..])
297 } else {
298 false
299 };
300 if !valid {
301 return Err(Error::schema(
302 ErrorCode::AttributeSyntax,
303 FIELD,
304 value.span().start,
305 ));
306 }
307 Ok(())
308}
309
310fn value_reader<'a, 'query>(
311 attribute: &Attribute<'a>,
312 parent: &Reader<'query>,
313 field: &'static str,
314) -> Result<Reader<'query>>
315where
316 'a: 'query,
317{
318 attribute.value_reader_in(0, parent, field)?.ok_or_else(|| {
319 Error::schema(
320 ErrorCode::AttributeCardinality,
321 field,
322 attribute.span().start,
323 )
324 })
325}
326
327fn read_protection<'a>(
328 attributes: &Attributes<'a>,
329 parent: &Reader<'a>,
330) -> Result<Option<Protection<'a>>> {
331 const FIELD: &str = "signerInfo.signedAttrs.cmsAlgorithmProtection";
332 let Some(attribute) = attributes.unique_in(oid::ALGORITHM_PROTECTION, parent)? else {
333 return Ok(None);
334 };
335 attribute.single(FIELD)?;
336 let mut reader = value_reader(attribute, parent, FIELD)?;
337 let element = reader
338 .expect(Tag::constructed(universal::SEQUENCE))
339 .map_err(|error| Error::encoding(error, FIELD))?;
340 let mut inside = reader
341 .enter(&element)
342 .map_err(|error| Error::encoding(error, FIELD))?;
343 let digest = crate::signer::required(&mut inside, FIELD)?;
344 let digest = structured_pkix::algorithm::read(&inside, &digest, FIELD).map_err(Error::pkix)?;
345 let signature = crate::signer::required(&mut inside, FIELD)?;
346 let signature = structured_pkix::algorithm::read_implicit(&inside, &signature, 1, FIELD)
347 .map_err(Error::pkix)?;
348 if !inside.is_empty() {
349 return Err(Error::schema(
350 ErrorCode::TrailingData,
351 FIELD,
352 inside.offset(),
353 ));
354 }
355 Ok(Some(Protection { digest, signature }))
356}