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