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 for attributes in [signer.signed_attributes(), signer.unsigned_attributes()]
156 .into_iter()
157 .flatten()
158 {
159 if let Some(fault) = attributes.fault() {
160 return Err(fault);
161 }
162 }
163 check_placements(signer, parent)?;
164 let Some(attributes) = signer.signed_attributes() else {
165 if let Scope::Content(kind) = scope {
166 parent
167 .execution()
168 .charge_work(kind.as_bytes().len() as u64)
169 .map_err(|reason| {
170 Error::resource(reason, "signerInfo.signedAttrs", signer.span().start)
171 })?;
172 if kind != oid::DATA {
173 return Err(Error::schema(
174 ErrorCode::MissingField,
175 "signerInfo.signedAttrs",
176 signer.span().start,
177 ));
178 }
179 }
180 return Ok(Binding::Direct);
181 };
182 let content_type = attributes.unique_in(oid::CONTENT_TYPE, parent)?;
183 match scope {
184 Scope::CounterSignature => {
185 if let Some(attribute) = content_type {
186 return Err(Error::schema(
187 ErrorCode::AttributePlacement,
188 "signerInfo.signedAttrs.contentType",
189 attribute.span().start,
190 ));
191 }
192 }
193 Scope::Content(expected) => {
194 let attribute = content_type.ok_or_else(|| {
195 Error::schema(
196 ErrorCode::MissingField,
197 "signerInfo.signedAttrs.contentType",
198 attributes.element().span().start,
199 )
200 })?;
201 attribute.single("signerInfo.signedAttrs.contentType")?;
202 let mut reader = value_reader(attribute, parent, "signerInfo.signedAttrs.contentType")?;
203 let declared = reader
204 .read_oid()
205 .map_err(|error| Error::encoding(error, "signerInfo.signedAttrs.contentType"))?;
206 parent
207 .execution()
208 .charge_work(declared.as_bytes().len() as u64)
209 .map_err(|reason| {
210 Error::resource(
211 reason,
212 "signerInfo.signedAttrs.contentType",
213 attribute.span().start,
214 )
215 })?;
216 if declared != expected {
217 return Err(Error::schema(
218 ErrorCode::ContentTypeMismatch,
219 "signerInfo.signedAttrs.contentType",
220 attribute.span().start,
221 ));
222 }
223 }
224 }
225 let attribute = attributes
226 .unique_in(oid::MESSAGE_DIGEST, parent)?
227 .ok_or_else(|| {
228 Error::schema(
229 ErrorCode::MissingField,
230 "signerInfo.signedAttrs.messageDigest",
231 attributes.element().span().start,
232 )
233 })?;
234 attribute.single("signerInfo.signedAttrs.messageDigest")?;
235 let mut reader = value_reader(attribute, parent, "signerInfo.signedAttrs.messageDigest")?;
236 let element = crate::signer::required(&mut reader, "signerInfo.signedAttrs.messageDigest")?;
237 let digest = Octets::read(&reader, &element, "signerInfo.signedAttrs.messageDigest")?;
238 let digest = digest.as_one().ok_or_else(|| {
242 Error::schema(
243 ErrorCode::UnexpectedTag,
244 "signerInfo.signedAttrs.messageDigest",
245 element.span().start,
246 )
247 })?;
248 check_signing_time(attributes, parent)?;
249 let protection = read_protection(attributes, parent)?;
250 Ok(Binding::Attributes {
251 digest,
252 digest_span: element.span(),
253 protection,
254 })
255}
256
257fn check_placements(signer: &SignerInfo<'_>, parent: &Reader<'_>) -> Result<()> {
258 if let Some(unsigned) = signer.unsigned_attributes() {
259 for kind in [
262 oid::CONTENT_TYPE,
263 oid::MESSAGE_DIGEST,
264 oid::SIGNING_TIME,
265 oid::ALGORITHM_PROTECTION,
266 ] {
267 if let Some(result) = unsigned.all_in(kind, parent).next() {
268 let attribute = result?;
269 return Err(Error::schema(
270 ErrorCode::AttributePlacement,
271 "signerInfo.unsignedAttrs",
272 attribute.span().start,
273 ));
274 }
275 }
276 }
277 if let Some(signed) = signer.signed_attributes()
278 && let Some(result) = signed.all_in(oid::COUNTERSIGNATURE, parent).next()
279 {
280 let attribute = result?;
281 return Err(Error::schema(
282 ErrorCode::AttributePlacement,
283 "signerInfo.signedAttrs.counterSignature",
284 attribute.span().start,
285 ));
286 }
287 Ok(())
288}
289
290fn check_signing_time(attributes: &Attributes<'_>, parent: &Reader<'_>) -> Result<()> {
291 const FIELD: &str = "signerInfo.signedAttrs.signingTime";
292 let Some(attribute) = attributes.unique_in(oid::SIGNING_TIME, parent)? else {
293 return Ok(());
294 };
295 let value = attribute.single(FIELD)?;
296 parent
297 .execution()
298 .charge_work(9)
299 .map_err(|reason| Error::resource(reason, FIELD, value.span().start))?;
300 let valid = if value.tag() == Tag::primitive(universal::UTC_TIME) {
305 value.contents().len() == 13
306 } else if value.tag() == Tag::primitive(universal::GENERALIZED_TIME) {
307 value.contents().len() == 15
308 && !(value.contents()[..4] >= b"1950"[..] && value.contents()[..4] <= b"2049"[..])
309 } else {
310 false
311 };
312 if !valid {
313 return Err(Error::schema(
314 ErrorCode::AttributeSyntax,
315 FIELD,
316 value.span().start,
317 ));
318 }
319 Ok(())
320}
321
322fn value_reader<'a, 'query>(
323 attribute: &Attribute<'a>,
324 parent: &Reader<'query>,
325 field: &'static str,
326) -> Result<Reader<'query>>
327where
328 'a: 'query,
329{
330 attribute.value_reader_in(0, parent, field)?.ok_or_else(|| {
331 Error::schema(
332 ErrorCode::AttributeCardinality,
333 field,
334 attribute.span().start,
335 )
336 })
337}
338
339fn read_protection<'a>(
340 attributes: &Attributes<'a>,
341 parent: &Reader<'a>,
342) -> Result<Option<Protection<'a>>> {
343 const FIELD: &str = "signerInfo.signedAttrs.cmsAlgorithmProtection";
344 let Some(attribute) = attributes.unique_in(oid::ALGORITHM_PROTECTION, parent)? else {
345 return Ok(None);
346 };
347 attribute.single(FIELD)?;
348 let mut reader = value_reader(attribute, parent, FIELD)?;
349 let element = reader
350 .expect(Tag::constructed(universal::SEQUENCE))
351 .map_err(|error| Error::encoding(error, FIELD))?;
352 let mut inside = reader
353 .enter(&element)
354 .map_err(|error| Error::encoding(error, FIELD))?;
355 let digest = crate::signer::required(&mut inside, FIELD)?;
356 let digest = structured_pkix::algorithm::read(&inside, &digest, FIELD).map_err(Error::pkix)?;
357 let signature = crate::signer::required(&mut inside, FIELD)?;
358 let signature = structured_pkix::algorithm::read_implicit(&inside, &signature, 1, FIELD)
359 .map_err(Error::pkix)?;
360 if !inside.is_empty() {
361 return Err(Error::schema(
362 ErrorCode::TrailingData,
363 FIELD,
364 inside.offset(),
365 ));
366 }
367 Ok(Some(Protection { digest, signature }))
368}