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 unsigned.placement_faults_in(parent, Err)?;
260 }
261 if let Some(signed) = signer.signed_attributes() {
262 signed.placement_faults_in(parent, Err)?;
263 }
264 Ok(())
265}
266
267fn check_signing_time(attributes: &Attributes<'_>, parent: &Reader<'_>) -> Result<()> {
268 const FIELD: &str = "signerInfo.signedAttrs.signingTime";
269 let Some(attribute) = attributes.unique_in(oid::SIGNING_TIME, parent)? else {
270 return Ok(());
271 };
272 let value = attribute.single(FIELD)?;
273 parent
274 .execution()
275 .charge_work(9)
276 .map_err(|reason| Error::resource(reason, FIELD, value.span().start))?;
277 let valid = if value.tag() == Tag::primitive(universal::UTC_TIME) {
282 value.contents().len() == 13
283 } else if value.tag() == Tag::primitive(universal::GENERALIZED_TIME) {
284 value.contents().len() == 15
285 && !(value.contents()[..4] >= b"1950"[..] && value.contents()[..4] <= b"2049"[..])
286 } else {
287 false
288 };
289 if !valid {
290 return Err(Error::schema(
291 ErrorCode::AttributeSyntax,
292 FIELD,
293 value.span().start,
294 ));
295 }
296 Ok(())
297}
298
299fn value_reader<'a, 'query>(
300 attribute: &Attribute<'a>,
301 parent: &Reader<'query>,
302 field: &'static str,
303) -> Result<Reader<'query>>
304where
305 'a: 'query,
306{
307 attribute.value_reader_in(0, parent, field)?.ok_or_else(|| {
308 Error::schema(
309 ErrorCode::AttributeCardinality,
310 field,
311 attribute.span().start,
312 )
313 })
314}
315
316fn read_protection<'a>(
317 attributes: &Attributes<'a>,
318 parent: &Reader<'a>,
319) -> Result<Option<Protection<'a>>> {
320 const FIELD: &str = "signerInfo.signedAttrs.cmsAlgorithmProtection";
321 let Some(attribute) = attributes.unique_in(oid::ALGORITHM_PROTECTION, parent)? else {
322 return Ok(None);
323 };
324 attribute.single(FIELD)?;
325 let mut reader = value_reader(attribute, parent, FIELD)?;
326 let element = reader
327 .expect(Tag::constructed(universal::SEQUENCE))
328 .map_err(|error| Error::encoding(error, FIELD))?;
329 let mut inside = reader
330 .enter(&element)
331 .map_err(|error| Error::encoding(error, FIELD))?;
332 let digest = crate::signer::required(&mut inside, FIELD)?;
333 let digest = structured_pkix::algorithm::read(&inside, &digest, FIELD).map_err(Error::pkix)?;
334 let signature = crate::signer::required(&mut inside, FIELD)?;
335 let signature = structured_pkix::algorithm::read_implicit(&inside, &signature, 1, FIELD)
336 .map_err(Error::pkix)?;
337 if !inside.is_empty() {
338 return Err(Error::schema(
339 ErrorCode::TrailingData,
340 FIELD,
341 inside.offset(),
342 ));
343 }
344 Ok(Some(Protection { digest, signature }))
345}