1use crate::{Error, Result};
5use structured_asn1::memory::{OwnedVec, Work};
6use structured_asn1::{Element, Oid, Reader, Span, Tag, universal};
7use structured_cms::{ErrorCode, Octets, attribute::Attribute, oid, signer::SignerInfo};
8use structured_pkix::algorithm::{self, AlgorithmIdentifier};
9
10pub(crate) mod legacy;
11pub mod values;
12
13#[derive(Debug)]
16pub struct Fault {
17 pub attribute: Oid<'static>,
19 pub field: &'static str,
21 pub cause: Error,
23}
24
25#[derive(Debug, Clone, Copy)]
28pub struct OpenValue<'a> {
29 pub role: OpenRole,
31 pub kind: Option<Oid<'a>>,
33 pub enclosure: Element<'a>,
35 pub value: Option<crate::source::Value<'a>>,
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum OpenRole {
43 Claimed,
45 AttributeCertificate,
47 OtherAttributeCertificate,
49 SignedAssertion,
51 CommitmentQualifier,
53 PolicyQualifier,
55}
56
57pub struct Policy<'a> {
61 pub identifier: Oid<'a>,
63 pub algorithm: AlgorithmIdentifier<'a>,
65 pub hash: Octets<'a>,
67 pub hash_unknown: bool,
69}
70
71pub struct Store<'a> {
73 pub specification: Element<'a>,
75 pub document: crate::source::Value<'a>,
77}
78
79pub struct Properties<'a> {
82 faults: OwnedVec<Fault>,
83 open: OwnedVec<OpenValue<'a>>,
84 issuer_hints: OwnedVec<Span>,
85 policy: Option<Policy<'a>>,
86 store: Option<Store<'a>>,
87 values: Option<values::Values<'a>>,
88}
89
90impl<'a> Properties<'a> {
91 pub fn read(signer: &SignerInfo<'a>, parent: &Reader<'a>) -> Result<Self> {
100 signer.require_session(parent, "cades.properties")?;
101 let work = parent.execution();
102 let mut policy = None;
103 let mut store = None;
104 let mut values = None;
105 let mut faults = work.vector();
106 let mut open = work.vector();
107 let mut issuer_hints = work.vector();
108 for set in [signer.signed_attributes(), signer.unsigned_attributes()]
109 .into_iter()
110 .flatten()
111 {
112 for attribute in set.items() {
113 parent
116 .charge(11 * (1 + attribute.kind().as_bytes().len().min(12) as u64))
117 .map_err(|cause| Error::encoding(cause, "cades.properties"))?;
118 let Some((kind, field)) = known(attribute.kind()) else {
119 legacy::inspect(
120 attribute,
121 parent,
122 &mut faults,
123 &mut issuer_hints,
124 &mut values,
125 &work,
126 )?;
127 continue;
128 };
129 let reading = read_value(
130 attribute,
131 kind,
132 field,
133 parent,
134 &mut open,
135 &mut policy,
136 &mut store,
137 );
138 if let Err(cause) = reading {
139 if cause.is_terminal() {
140 return Err(cause);
141 }
142 let at = cause.offset();
143 faults
144 .try_push(Fault {
145 attribute: kind,
146 field,
147 cause,
148 })
149 .map_err(|cause| Error::resource(cause, field, at))?;
150 }
151 }
152 }
153 Ok(Self {
154 faults: faults.into_owned(),
155 open: open.into_owned(),
156 issuer_hints: issuer_hints.into_owned(),
157 policy,
158 store,
159 values: values.map(values::Values::into_owned),
160 })
161 }
162 #[must_use]
164 pub fn faults(&self) -> &[Fault] {
165 &self.faults
166 }
167 pub(crate) fn issuer_hints(&self) -> &[Span] {
168 &self.issuer_hints
169 }
170 #[must_use]
172 pub fn open_values(&self) -> &[OpenValue<'a>] {
173 &self.open
174 }
175 #[must_use]
177 pub fn policy(&self) -> Option<&Policy<'a>> {
178 self.policy.as_ref()
179 }
180 #[must_use]
182 pub fn store(&self) -> Option<&Store<'a>> {
183 self.store.as_ref()
184 }
185 pub fn legacy_values(&self) -> Option<&values::Values<'a>> {
188 self.values.as_ref()
189 }
190
191 pub(crate) fn has_legacy_values(signer: &SignerInfo<'_>, parent: &Reader<'_>) -> Result<bool> {
192 for set in [signer.signed_attributes(), signer.unsigned_attributes()]
193 .into_iter()
194 .flatten()
195 {
196 for attribute in set.items() {
197 parent
198 .charge(2 * (1 + attribute.kind().as_bytes().len().min(11) as u64))
199 .map_err(|cause| Error::encoding(cause, "cades.legacy"))?;
200 if attribute.kind() == legacy::LEGACY[2] || attribute.kind() == legacy::LEGACY[3] {
201 return Ok(true);
202 }
203 }
204 }
205 Ok(false)
206 }
207}
208
209fn known(kind: Oid<'_>) -> Option<(Oid<'static>, &'static str)> {
210 for (expected, field) in [
211 (oid::COMMITMENT_TYPE_INDICATION, "commitmentTypeIndication"),
212 (oid::MIME_TYPE, "mimeType"),
213 (oid::CONTENT_HINTS, "contentHints"),
214 (oid::CONTENT_IDENTIFIER, "contentIdentifier"),
215 (oid::CONTENT_REFERENCE, "contentReference"),
216 (oid::SIGNER_LOCATION, "signerLocation"),
217 (oid::SIGNER_ATTRIBUTES_V2, "signerAttributesV2"),
218 (
219 oid::SIGNATURE_POLICY_IDENTIFIER,
220 "signaturePolicyIdentifier",
221 ),
222 (oid::SIGNATURE_POLICY_STORE, "signaturePolicyStore"),
223 (oid::SIGNATURE_TIMESTAMP, "signatureTimeStamp"),
224 (oid::COUNTERSIGNATURE, "countersignature"),
225 ] {
226 if kind == expected {
227 return Some((expected, field));
228 }
229 }
230 None
231}
232
233fn read_value<'a, W: Work>(
234 attribute: &Attribute<'a>,
235 kind: Oid<'static>,
236 field: &'static str,
237 parent: &Reader<'a>,
238 open: &mut OwnedVec<OpenValue<'a>, W>,
239 policy: &mut Option<Policy<'a>>,
240 store: &mut Option<Store<'a>>,
241) -> Result<()> {
242 if kind == oid::COUNTERSIGNATURE {
243 return if attribute.values().is_empty() {
244 Err(syntax(field, attribute.span().start))
245 } else {
246 Ok(())
247 };
248 }
249 if kind == oid::SIGNATURE_TIMESTAMP {
250 let mut der = parent
256 .reread(attribute.element(), structured_asn1::Encoding::Der)
257 .map_err(|cause| Error::encoding(cause, field))?;
258 let element = der
259 .expect(Tag::constructed(universal::SEQUENCE))
260 .map_err(|cause| Error::encoding(cause, field))?;
261 return structured_pkix::der::conforms(&der, &element, field).map_err(Error::Pkix);
262 }
263 if attribute.values().len() != 1 {
264 return Err(syntax(field, attribute.span().start));
265 }
266 let mut reader = attribute
267 .value_reader_in(0, parent, field)?
268 .ok_or_else(|| syntax(field, attribute.span().start))?;
269 let value = reader
270 .next_element()
271 .map_err(|cause| Error::encoding(cause, field))?
272 .ok_or_else(|| syntax(field, reader.offset()))?;
273 if !reader.is_empty() {
274 return Err(syntax(field, reader.offset()));
275 }
276 if kind == oid::MIME_TYPE {
277 require_tag(&value, Tag::primitive(universal::UTF8_STRING), field)?;
278 structured_pkix::der::conforms(&reader, &value, field).map_err(Error::Pkix)?;
279 let valid =
280 structured_cms::mime::media_type_or_headers(value.contents(), &reader.execution())
281 .map_err(|cause| Error::resource(cause, field, value.span().start))?;
282 return if valid {
283 Ok(())
284 } else {
285 Err(syntax(field, value.span().start))
286 };
287 }
288 if kind == oid::CONTENT_IDENTIFIER {
289 Octets::read(&reader, &value, field)?;
290 return Ok(());
291 }
292 let inside = sequence(&reader, &value, field)?;
293 read_sequence(inside, kind, field, open, policy, store)
294}
295
296fn read_sequence<'a, W: Work>(
297 mut inside: Reader<'a>,
298 kind: Oid<'static>,
299 field: &'static str,
300 open: &mut OwnedVec<OpenValue<'a>, W>,
301 policy: &mut Option<Policy<'a>>,
302 store: &mut Option<Store<'a>>,
303) -> Result<()> {
304 if kind == oid::CONTENT_HINTS {
305 let first = next(&mut inside, field)?;
306 if first.tag() == Tag::primitive(universal::UTF8_STRING) {
307 string(&inside, &first, field)?;
308 inside
309 .read_oid()
310 .map_err(|cause| Error::encoding(cause, field))?;
311 } else {
312 inside
313 .oid(&first)
314 .map_err(|cause| Error::encoding(cause, field))?;
315 }
316 } else if kind == oid::CONTENT_REFERENCE {
317 inside
318 .read_oid()
319 .map_err(|cause| Error::encoding(cause, field))?;
320 for _ in 0..2 {
321 let part = next(&mut inside, field)?;
322 Octets::read(&inside, &part, field)?;
323 }
324 } else if kind == oid::COMMITMENT_TYPE_INDICATION {
325 inside
326 .read_oid()
327 .map_err(|cause| Error::encoding(cause, field))?;
328 if let Some(qualifiers) = inside
329 .next_element()
330 .map_err(|cause| Error::encoding(cause, field))?
331 {
332 let mut values = sequence(&inside, &qualifiers, field)?;
333 if values.is_empty() {
334 return Err(syntax(field, qualifiers.span().start));
335 }
336 while let Some(value) = values
337 .next_element()
338 .map_err(|cause| Error::encoding(cause, field))?
339 {
340 open_pair(&values, &value, OpenRole::CommitmentQualifier, field, open)?;
341 }
342 }
343 } else if kind == oid::SIGNER_LOCATION {
344 location(&mut inside, field)?;
345 } else if kind == oid::SIGNER_ATTRIBUTES_V2 {
346 attributes(&mut inside, field, open)?;
347 } else if kind == oid::SIGNATURE_POLICY_IDENTIFIER {
348 let identifier = inside
349 .read_oid()
350 .map_err(|cause| Error::encoding(cause, field))?;
351 let hash_value = next(&mut inside, field)?;
352 let mut hash_fields = sequence(&inside, &hash_value, field)?;
353 let id = next(&mut hash_fields, field)?;
354 let algorithm = algorithm::read(&hash_fields, &id, field).map_err(Error::Pkix)?;
355 let hash_value = next(&mut hash_fields, field)?;
356 let hash = Octets::read(&hash_fields, &hash_value, field)?;
357 end(&hash_fields, field)?;
358 let mut hash_unknown = true;
359 for piece in hash.pieces() {
360 inside
361 .charge(1 + piece.as_bytes().len() as u64)
362 .map_err(|cause| Error::encoding(cause, field))?;
363 if piece.as_bytes().iter().any(|byte| *byte != 0) {
364 hash_unknown = false;
365 }
366 }
367 if let Some(qualifiers) = inside
368 .next_element()
369 .map_err(|cause| Error::encoding(cause, field))?
370 {
371 let mut values = sequence(&inside, &qualifiers, field)?;
372 if values.is_empty() {
373 return Err(syntax(field, qualifiers.span().start));
374 }
375 while let Some(value) = values
376 .next_element()
377 .map_err(|cause| Error::encoding(cause, field))?
378 {
379 open_pair(&values, &value, OpenRole::PolicyQualifier, field, open)?;
380 }
381 }
382 *policy = Some(Policy {
383 identifier,
384 algorithm,
385 hash,
386 hash_unknown,
387 });
388 } else if kind == oid::SIGNATURE_POLICY_STORE {
389 let specification = next(&mut inside, field)?;
390 spec(&inside, &specification, field)?;
391 let document = next(&mut inside, field)?;
392 if document.tag().number() == Some(universal::OCTET_STRING) {
393 Octets::read(&inside, &document, field)?;
394 } else {
395 uri_value(&inside, &document, field)?;
399 }
400 *store = Some(Store {
401 specification,
402 document: crate::source::Value::of(&inside, document, field),
403 });
404 }
405 end(&inside, field)
406}
407
408fn location(reader: &mut Reader<'_>, field: &'static str) -> Result<()> {
409 let mut previous = None;
412 while let Some(value) = reader
413 .next_element()
414 .map_err(|cause| Error::encoding(cause, field))?
415 {
416 let Some(number) = value.tag().number() else {
417 return Err(syntax(field, value.span().start));
418 };
419 if number > 2
420 || value.tag() != Tag::context(true, number)
421 || previous.is_some_and(|p| number <= p)
422 {
423 return Err(syntax(field, value.span().start));
424 }
425 previous = Some(number);
426 let mut explicit = reader
427 .enter(&value)
428 .map_err(|cause| Error::encoding(cause, field))?;
429 let part = next(&mut explicit, field)?;
430 end(&explicit, field)?;
431 if number < 2 {
432 directory_string(&explicit, &part, field)?;
433 } else {
434 let mut lines = sequence(&explicit, &part, field)?;
435 let mut count = 0;
436 while let Some(line) = lines
437 .next_element()
438 .map_err(|cause| Error::encoding(cause, field))?
439 {
440 count += 1;
441 if count > 6 {
442 return Err(syntax(field, line.span().start));
443 }
444 directory_string(&lines, &line, field)?;
445 }
446 if count == 0 {
447 return Err(syntax(field, part.span().start));
448 }
449 }
450 }
451 if previous.is_none() {
452 return Err(syntax(field, reader.offset()));
453 }
454 Ok(())
455}
456
457fn claimed_value(claimed: &Attribute<'_>, parent: &Reader<'_>, field: &'static str) -> Result<()> {
458 parent
459 .charge(
460 1 + claimed
461 .kind()
462 .as_bytes()
463 .len()
464 .min(oid::CLAIMED_SAML_ASSERTION.as_bytes().len()) as u64,
465 )
466 .map_err(|cause| Error::encoding(cause, field))?;
467 if claimed.kind() != oid::CLAIMED_SAML_ASSERTION {
468 return Ok(());
469 }
470 claimed.single(field)?;
474 let mut reader = claimed
475 .value_reader_in(0, parent, field)?
476 .ok_or_else(|| syntax(field, claimed.span().start))?;
477 let value = next(&mut reader, field)?;
478 Octets::read(&reader, &value, field)?;
479 Ok(())
480}
481
482fn attributes<'a, W: Work>(
483 reader: &mut Reader<'a>,
484 field: &'static str,
485 open: &mut OwnedVec<OpenValue<'a>, W>,
486) -> Result<()> {
487 let mut previous = None;
488 let mut populated = false;
489 while let Some(value) = reader
490 .next_element()
491 .map_err(|cause| Error::encoding(cause, field))?
492 {
493 let Some(number) = value.tag().number() else {
494 return Err(syntax(field, value.span().start));
495 };
496 if number > 2
497 || value.tag() != Tag::context(true, number)
498 || previous.is_some_and(|p| number <= p)
499 {
500 return Err(syntax(field, value.span().start));
501 }
502 previous = Some(number);
503 let mut explicit = reader
504 .enter(&value)
505 .map_err(|cause| Error::encoding(cause, field))?;
506 let part = next(&mut explicit, field)?;
507 end(&explicit, field)?;
508 let mut entries = sequence(&explicit, &part, field)?;
509 if number != 0 && entries.is_empty() {
511 return Err(syntax(field, part.span().start));
512 }
513 while let Some(entry) = entries
514 .next_element()
515 .map_err(|cause| Error::encoding(cause, field))?
516 {
517 populated = true;
518 if number == 0 {
519 let claimed = Attribute::read(&entries, &entry, field)?;
520 claimed_value(&claimed, &entries, field)?;
521 push(
522 open,
523 OpenValue {
524 role: OpenRole::Claimed,
525 kind: Some(claimed.kind()),
526 enclosure: entry,
527 value: Some(crate::source::Value::of(&entries, entry, field)),
528 },
529 field,
530 )?;
531 } else if number == 1 {
532 let choice = entry.tag();
533 if choice != Tag::context(true, 0) && choice != Tag::context(true, 1) {
534 return Err(syntax(field, entry.span().start));
535 }
536 let mut explicit = entries
537 .enter(&entry)
538 .map_err(|cause| Error::encoding(cause, field))?;
539 let certificate = next(&mut explicit, field)?;
540 end(&explicit, field)?;
541 if choice.number() == Some(0) {
542 require_tag(&certificate, Tag::constructed(universal::SEQUENCE), field)?;
543 push(
544 open,
545 OpenValue {
546 role: OpenRole::AttributeCertificate,
547 kind: None,
548 enclosure: certificate,
549 value: Some(crate::source::Value::of(&explicit, certificate, field)),
550 },
551 field,
552 )?;
553 } else {
554 open_pair(
555 &explicit,
556 &certificate,
557 OpenRole::OtherAttributeCertificate,
558 field,
559 open,
560 )?;
561 }
562 } else {
563 open_pair(&entries, &entry, OpenRole::SignedAssertion, field, open)?;
564 }
565 }
566 }
567 if !populated {
572 return Err(syntax(field, reader.offset()));
573 }
574 Ok(())
575}
576
577fn open_pair<'a, W: Work>(
578 parent: &Reader<'a>,
579 element: &Element<'a>,
580 role: OpenRole,
581 field: &'static str,
582 open: &mut OwnedVec<OpenValue<'a>, W>,
583) -> Result<()> {
584 let mut pair = sequence(parent, element, field)?;
585 let kind = pair
586 .read_oid()
587 .map_err(|cause| Error::encoding(cause, field))?;
588 let value = pair
589 .next_element()
590 .map_err(|cause| Error::encoding(cause, field))?;
591 end(&pair, field)?;
592 if role == OpenRole::SignedAssertion {
593 let saml = Oid::from_bytes(&[4, 0, 0x81, 0x95, 0x32, 3, 0]);
594 parent
595 .charge(1 + kind.as_bytes().len().min(saml.as_bytes().len()) as u64)
596 .map_err(|cause| Error::encoding(cause, field))?;
597 if kind == saml
601 && let Some(part) = value
602 {
603 Octets::read(&pair, &part, field)?;
604 }
605 }
606 if role == OpenRole::PolicyQualifier
607 && let Some(part) = value
608 {
609 let uri = Oid::from_bytes(&[42, 134, 72, 134, 247, 13, 1, 9, 16, 5, 1]);
613 let notice = Oid::from_bytes(&[42, 134, 72, 134, 247, 13, 1, 9, 16, 5, 2]);
614 let specification = Oid::from_bytes(&[4, 0, 0x81, 0x95, 0x32, 2, 1]);
615 parent
616 .charge(3 * (1 + kind.as_bytes().len() as u64))
617 .map_err(|cause| Error::encoding(cause, field))?;
618 if kind == uri || kind == notice || kind == specification {
619 if kind == uri {
620 uri_value(&pair, &part, field)?;
621 } else if kind == specification {
622 spec(&pair, &part, field)?;
623 } else if !structured_pkix::policy::signature_notice_understood(&pair, &part)
624 .map_err(Error::Pkix)?
625 {
626 return Err(syntax(field, part.span().start));
627 }
628 }
629 }
630 push(
631 open,
632 OpenValue {
633 role,
634 kind: Some(kind),
635 enclosure: *element,
636 value: value.map(|value| crate::source::Value::of(&pair, value, field)),
637 },
638 field,
639 )
640}
641
642fn push<'a, W: Work>(
643 out: &mut OwnedVec<OpenValue<'a>, W>,
644 value: OpenValue<'a>,
645 field: &'static str,
646) -> Result<()> {
647 let at = value.enclosure.span().start;
648 out.try_push(value)
649 .map_err(|cause| Error::resource(cause, field, at))
650}
651
652fn directory_string(parent: &Reader<'_>, value: &Element<'_>, field: &'static str) -> Result<()> {
653 if ![12, 19, 20, 28, 30]
654 .into_iter()
655 .any(|number| value.tag() == Tag::primitive(number))
656 {
657 return Err(syntax(field, value.span().start));
658 }
659 string(parent, value, field)
660}
661
662fn string<'a>(parent: &Reader<'a>, value: &Element<'a>, field: &'static str) -> Result<()> {
663 if value.contents().is_empty() {
664 return Err(syntax(field, value.span().start));
665 }
666 structured_pkix::der::conforms(parent, value, field).map_err(Error::Pkix)
667}
668
669fn spec<'a>(parent: &Reader<'a>, value: &Element<'a>, field: &'static str) -> Result<()> {
670 if value.tag() == Tag::primitive(universal::OID) {
671 parent
672 .oid(value)
673 .map_err(|cause| Error::encoding(cause, field))?;
674 } else {
675 uri_value(parent, value, field)?;
676 }
677 Ok(())
678}
679
680fn uri_value<'a>(parent: &Reader<'a>, value: &Element<'a>, field: &'static str) -> Result<()> {
681 let octets = Octets::read_ia5(parent, value, field)?;
682 let work = parent.execution();
683 let joined;
684 let bytes = if let Some(one) = octets.as_one() {
685 one
686 } else {
687 joined = octets.copy_in(parent, field, value.span().start)?;
688 &joined
689 };
690 work.charge_work(1 + bytes.len() as u64)
691 .map_err(|cause| Error::resource(cause, field, value.span().start))?;
692 let text = core::str::from_utf8(bytes).map_err(|_| syntax(field, value.span().start))?;
693 if !structured_pkix::general_name::uri_syntax(text, &work)
694 .map_err(|cause| Error::resource(cause, field, value.span().start))?
695 {
696 return Err(syntax(field, value.span().start));
697 }
698 Ok(())
699}
700
701fn sequence<'a>(
702 parent: &Reader<'a>,
703 value: &Element<'a>,
704 field: &'static str,
705) -> Result<Reader<'a>> {
706 require_tag(value, Tag::constructed(universal::SEQUENCE), field)?;
707 parent
708 .enter(value)
709 .map_err(|cause| Error::encoding(cause, field))
710}
711fn next<'a>(reader: &mut Reader<'a>, field: &'static str) -> Result<Element<'a>> {
712 reader
713 .next_element()
714 .map_err(|cause| Error::encoding(cause, field))?
715 .ok_or_else(|| syntax(field, reader.offset()))
716}
717fn end(reader: &Reader<'_>, field: &'static str) -> Result<()> {
718 if reader.is_empty() {
719 Ok(())
720 } else {
721 Err(syntax(field, reader.offset()))
722 }
723}
724fn require_tag(value: &Element<'_>, tag: Tag, field: &'static str) -> Result<()> {
725 if value.tag() == tag {
726 Ok(())
727 } else {
728 Err(syntax(field, value.span().start))
729 }
730}
731fn syntax(field: &'static str, at: u64) -> Error {
732 Error::Syntax {
733 code: ErrorCode::AttributeSyntax,
734 field,
735 at,
736 }
737}