Skip to main content

structured_cms/
ess.rs

1//! Original ESS certificate bindings and signed validation-set restrictions.
2
3use crate::provider::{DigestAlgorithm, ImpliedDigest};
4use crate::signer::{SignerInfo, required};
5use crate::{Error, ErrorCode, Octets, Result, oid};
6use structured_asn1::memory::{OwnedVec, Work};
7use structured_asn1::{Element, Integer, Reader, Span, Tag, universal};
8use structured_pkix::general_name::{self, GeneralName};
9
10mod check;
11pub use check::{Binding, BindingError, Bindings};
12
13/// The signed attribute's schema, not the CMS content digest algorithm.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum Version {
16    /// SHA-1 is mandatory in `ESSCertID` (RFC2634 §5.4.1).
17    V1,
18    /// `ESSCertIDv2` carries an algorithm or omits its SHA-256 default.
19    V2,
20}
21
22impl Version {
23    /// The attribute identifier that selects this schema.
24    #[must_use]
25    pub fn identifier(self) -> structured_asn1::Oid<'static> {
26        match self {
27            Self::V1 => oid::SIGNING_CERTIFICATE,
28            Self::V2 => oid::SIGNING_CERTIFICATE_V2,
29        }
30    }
31    fn field(self) -> &'static str {
32        match self {
33            Self::V1 => "signerInfo.signedAttrs.signingCertificate",
34            Self::V2 => "signerInfo.signedAttrs.signingCertificateV2",
35        }
36    }
37}
38
39/// Optional issuerSerial is never silently discarded after a read failure.
40#[derive(Debug)]
41pub enum IssuerReading<'a> {
42    /// The schema field was not written.
43    Absent,
44    /// Original names and serial, including a separately reported profile fault.
45    Present(IssuerSerial<'a>),
46    /// The original field was written but could not be interpreted uniquely.
47    /// Its diagnostic is retained in the enclosing signing-certificate faults.
48    Unreadable(Element<'a>),
49}
50
51/// Agreement of the optional signed issuer/serial restriction alone.
52/// Hash agreement, retained faults and certificate trust remain separate.
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub enum IssuerAgreement {
55    /// No optional restriction was written; this says nothing about the hash.
56    Unstated,
57    /// A readable directoryName and serial identify this certificate's issuer.
58    Matches,
59    /// Readable written restrictions identify something else.
60    Differs,
61    /// The written restriction has no uniquely readable identity.
62    Unreadable,
63}
64
65/// Original certificate issuer names and serial, without normalization/copying.
66#[derive(Debug)]
67pub struct IssuerSerial<'a> {
68    names: OwnedVec<GeneralName<'a>>,
69    serial: Integer<'a>,
70    names_element: Element<'a>,
71    element: Element<'a>,
72}
73
74impl<'a> IssuerSerial<'a> {
75    /// Every written issuer name, in its original order.
76    #[must_use]
77    pub fn names(&self) -> &[GeneralName<'a>] {
78        &self.names
79    }
80    /// Original serial-number contents, without re-encoding.
81    #[must_use]
82    pub fn serial(&self) -> Integer<'a> {
83        self.serial
84    }
85    /// Exact original `GeneralNames` element for independently retained evidence.
86    #[must_use]
87    pub fn names_element(&self) -> &Element<'a> {
88        &self.names_element
89    }
90    /// Original issuerSerial location.
91    #[must_use]
92    pub fn span(&self) -> Span {
93        self.element.span()
94    }
95
96    fn matches_with<'c, 'p>(
97        &self,
98        certificate: &structured_pkix::Certificate<'c>,
99        parent: &'p Reader<'_>,
100        prepared: &mut Option<structured_pkix::Ordered<'c, structured_asn1::memory::Execution<'p>>>,
101        field: &'static str,
102        metadata_at: u64,
103    ) -> Result<IssuerAgreement> {
104        let execution = parent.execution();
105        execution
106            .charge_work(1)
107            .map_err(|reason| Error::resource(reason, field, metadata_at))?;
108        let at = self.element.span().start;
109        let serial = self.serial.as_bytes();
110        let actual = certificate.tbs().serial().as_bytes();
111        execution
112            .charge_work(1 + serial.len().max(actual.len()) as u64)
113            .map_err(|reason| Error::resource(reason, field, at))?;
114        if serial != actual {
115            return Ok(IssuerAgreement::Differs);
116        }
117        for name in &self.names {
118            execution
119                .charge_work(1)
120                .map_err(|reason| Error::resource(reason, field, at))?;
121            if let GeneralName::Directory(name) = name {
122                let actual = match &*prepared {
123                    Some(actual) => actual,
124                    None => prepared.insert(
125                        certificate
126                            .tbs()
127                            .issuer()
128                            .ordered_in(parent)
129                            .map_err(Error::pkix)?,
130                    ),
131                };
132                let asked = name.ordered_in(parent).map_err(Error::pkix)?;
133                if asked.order(actual, &execution).map_err(Error::pkix)?
134                    == core::cmp::Ordering::Equal
135                {
136                    return Ok(IssuerAgreement::Matches);
137                }
138            }
139        }
140        Ok(IssuerAgreement::Differs)
141    }
142}
143
144/// Original standalone ESS issuer hint and independently retained diagnostics.
145///
146/// This reads the `IssuerSerial` type referenced by RFC5035 §4 and by
147/// `SigningCertificateV2/IssuerSerialV2` in ETSI EN319132-1 §5.2.2. It shares
148/// the signing-certificate reader's kernel. A readable value with faults is
149/// inspection data, not a conforming issuer assertion or certificate proof.
150/// <https://www.rfc-editor.org/rfc/rfc5035#section-4>
151#[derive(Debug)]
152pub struct IssuerSerialReading<'a> {
153    field: &'static str,
154    value: IssuerSerial<'a>,
155    faults: OwnedVec<Error>,
156}
157
158impl<'a> IssuerSerialReading<'a> {
159    /// Reads the original element using its producing reader and current budget.
160    ///
161    /// `field` identifies the original enclosing protocol field. No replacement
162    /// parser, manufactured signing-certificate container or re-encoding is
163    /// needed. Syntax that cannot produce a reading and resource/processing
164    /// refusals are errors; readable cardinality/trailing violations remain
165    /// located faults. Every fault must be considered before acceptance.
166    pub fn read(parent: &Reader<'a>, element: &Element<'a>, field: &'static str) -> Result<Self> {
167        let mut faults = parent.execution().vector();
168        let value = read_issuer_serial(parent, element, field, &mut faults)?;
169        Ok(Self {
170            field,
171            value,
172            faults: faults.into_owned(),
173        })
174    }
175
176    /// Original parsed issuer hint; obtaining it performs no new traversal.
177    #[must_use]
178    pub fn value(&self) -> &IssuerSerial<'a> {
179        &self.value
180    }
181
182    /// All original diagnostics, distinct from resource/processing failures.
183    #[must_use]
184    pub fn faults(&self) -> &[Error] {
185        &self.faults
186    }
187
188    /// Compares the original standalone hint through the existing ESS core.
189    /// The current query funds comparison; original Name sources retain their
190    /// lazy-decoding limits. It need not be the earlier hint/certificate session.
191    /// Original diagnostics, digest/key binding, path and trust stay separate.
192    pub fn matches_certificate(
193        &self,
194        certificate: &structured_pkix::Certificate<'_>,
195        parent: &Reader<'_>,
196    ) -> Result<IssuerAgreement> {
197        self.value.matches_with(
198            certificate,
199            parent,
200            &mut None,
201            self.field,
202            self.value.element.span().start,
203        )
204    }
205
206    /// Moves the original view and diagnostic ownership without copying storage.
207    ///
208    /// The borrowed hint keeps its source lifetime; diagnostics independently
209    /// retain their allocation charge until freed, even after the parser drops.
210    #[must_use]
211    pub fn into_parts(self) -> (IssuerSerial<'a>, OwnedVec<Error>) {
212        (self.value, self.faults)
213    }
214}
215
216/// One readable certificate identifier; hash verification is a separate step.
217#[derive(Debug)]
218pub struct CertificateId<'a> {
219    digest: DigestAlgorithm<'a>,
220    hashed: Octets<'a>,
221    issuer: IssuerReading<'a>,
222    element: Element<'a>,
223    field: &'static str,
224}
225
226impl<'a> CertificateId<'a> {
227    /// Original written identity or the exact schema-supplied identity.
228    #[must_use]
229    pub fn digest_algorithm(&self) -> DigestAlgorithm<'a> {
230        self.digest
231    }
232    /// Logical certHash octets over the complete original DER certificate.
233    #[must_use]
234    pub fn hashed(&self) -> &Octets<'a> {
235        &self.hashed
236    }
237    /// Absence, readable value and unreadable written field remain distinct.
238    #[must_use]
239    pub fn issuer_serial(&self) -> &IssuerReading<'a> {
240        &self.issuer
241    }
242    /// Original identifier element, without serialization.
243    #[must_use]
244    pub fn element(&self) -> &Element<'a> {
245        &self.element
246    }
247
248    /// Compares issuer/serial using the current producing query ceilings.
249    ///
250    /// The certificate can come from another retained source session; its
251    /// canonical issuer is prepared once for this query, without re-decoding
252    /// the certificate or creating a replacement Parser. Resource refusal is
253    /// separate from Differs, Unstated and Unreadable. This does not check the
254    /// certificate hash, signature, path, profile faults or trust.
255    pub fn matches_issuer(
256        &self,
257        certificate: &structured_pkix::Certificate<'_>,
258        parent: &Reader<'_>,
259    ) -> Result<IssuerAgreement> {
260        self.matches_issuer_with(certificate, parent, &mut None)
261    }
262
263    fn matches_issuer_with<'c, 'p>(
264        &self,
265        certificate: &structured_pkix::Certificate<'c>,
266        parent: &'p Reader<'_>,
267        prepared: &mut Option<structured_pkix::Ordered<'c, structured_asn1::memory::Execution<'p>>>,
268    ) -> Result<IssuerAgreement> {
269        if let IssuerReading::Present(issuer) = &self.issuer {
270            return issuer.matches_with(
271                certificate,
272                parent,
273                prepared,
274                self.field,
275                self.element.span().start,
276            );
277        }
278        parent
279            .execution()
280            .charge_work(1)
281            .map_err(|reason| Error::resource(reason, self.field, self.element.span().start))?;
282        Ok(match self.issuer {
283            IssuerReading::Absent => IssuerAgreement::Unstated,
284            _ => IssuerAgreement::Unreadable,
285        })
286    }
287}
288
289/// Later identifiers must remain in the signed restriction set even if unreadable.
290#[derive(Debug)]
291pub enum CertificateReading<'a> {
292    /// Index into [`SigningCertificate::identifiers`], without one allocation
293    /// per identifier or inflating every unreadable restriction's metadata.
294    Read(usize),
295    /// Original unreadable restriction; never treated as an absent restriction.
296    Unreadable(Element<'a>),
297}
298
299/// One signing-certificate value, preserving the whole signed validation set.
300/// V1 and V2 values are evaluated independently (RFC5035 §2).
301#[derive(Debug)]
302pub struct SigningCertificate<'a> {
303    version: Version,
304    certificates: OwnedVec<CertificateReading<'a>>,
305    identifiers: OwnedVec<CertificateId<'a>>,
306    policies: Option<Element<'a>>,
307    faults: OwnedVec<Error>,
308    element: Element<'a>,
309}
310
311/// Both independently evaluated ESS attributes, with placement/cardinality
312/// diagnostics separate from certificate/hash/path checking.
313#[derive(Debug)]
314pub struct SigningAttributes<'a> {
315    signed_attributes_present: bool,
316    v1: Option<SigningCertificate<'a>>,
317    v2: Option<SigningCertificate<'a>>,
318    faults: OwnedVec<Error>,
319}
320
321impl<'a> SigningAttributes<'a> {
322    /// Reads the recognized signed fields using original value provenance.
323    ///
324    /// RFC5035 §§3/5 require one instance and one value in signed attributes.
325    /// Byte-identical repeats retain one reading plus a cardinality fault;
326    /// conflicting values refuse selection. Unsigned instances are retained
327    /// as placement faults, never used as certificate bindings. No resolver,
328    /// hash state, signature or caller payload is accessed during this step.
329    /// <https://www.rfc-editor.org/rfc/rfc5035#section-3>
330    pub fn read(signer: &SignerInfo<'a>, parent: &Reader<'a>) -> Result<Self> {
331        signer.require_session(parent, "signerInfo.signedAttrs")?;
332        let execution = parent.execution();
333        execution.charge_work(1).map_err(|reason| {
334            Error::resource(reason, "signerInfo.signedAttrs", signer.span().start)
335        })?;
336        let mut faults = execution.vector();
337        if let Some(unsigned) = signer.unsigned_attributes() {
338            for version in [Version::V1, Version::V2] {
339                for result in unsigned.all_in(version.identifier(), parent) {
340                    let attribute = result?;
341                    let field = match version {
342                        Version::V1 => "signerInfo.unsignedAttrs.signingCertificate",
343                        Version::V2 => "signerInfo.unsignedAttrs.signingCertificateV2",
344                    };
345                    record(
346                        Error::schema(ErrorCode::AttributePlacement, field, attribute.span().start),
347                        &mut faults,
348                        field,
349                    )?;
350                }
351            }
352        }
353        let v1 = read_attribute(signer, parent, Version::V1, &mut faults)?;
354        let v2 = read_attribute(signer, parent, Version::V2, &mut faults)?;
355        if v1.is_none()
356            && let Some(v2) = &v2
357        {
358            // RFC5035 §§1/2 retains mandatory V1 compatibility when SHA-1
359            // is used for certificate identification. V2 is independent, not
360            // a waiver of the companion attribute. This checks declared
361            // identities, without granting the selected provider permission
362            // to accept SHA-1 or bypassing parameter validation.
363            // https://www.rfc-editor.org/rfc/rfc5035#section-2
364            for identifier in v2.identifiers() {
365                execution.charge_work(1).map_err(|reason| {
366                    Error::resource(reason, Version::V2.field(), identifier.element.span().start)
367                })?;
368                if let DigestAlgorithm::Written(algorithm) = identifier.digest {
369                    execution
370                        .charge_work(
371                            algorithm
372                                .algorithm()
373                                .as_bytes()
374                                .len()
375                                .min(oid::SHA1.as_bytes().len()) as u64,
376                        )
377                        .map_err(|reason| {
378                            Error::resource(reason, Version::V2.field(), algorithm.offset())
379                        })?;
380                    if algorithm.algorithm() == oid::SHA1 {
381                        record(
382                            Error::schema(
383                                ErrorCode::MissingField,
384                                Version::V1.field(),
385                                identifier.element.span().start,
386                            ),
387                            &mut faults,
388                            Version::V1.field(),
389                        )?;
390                        break;
391                    }
392                }
393            }
394        }
395        Ok(Self {
396            signed_attributes_present: signer.signed_attributes().is_some(),
397            v1,
398            v2,
399            faults: faults.into_owned(),
400        })
401    }
402
403    /// Whether any signed attributes existed, independently of ESS presence.
404    #[must_use]
405    pub fn signed_attributes_present(&self) -> bool {
406        self.signed_attributes_present
407    }
408    /// Original V1 signer/path restrictions, when written.
409    #[must_use]
410    pub fn v1(&self) -> Option<&SigningCertificate<'a>> {
411        self.v1.as_ref()
412    }
413    /// Independently evaluated original V2 signer/path restrictions.
414    #[must_use]
415    pub fn v2(&self) -> Option<&SigningCertificate<'a>> {
416        self.v2.as_ref()
417    }
418    /// Placement and identical-repetition diagnostics; nested schema faults
419    /// remain with each [`SigningCertificate::faults`] collection.
420    #[must_use]
421    pub fn faults(&self) -> &[Error] {
422        &self.faults
423    }
424}
425
426fn read_attribute<'a, W: Work>(
427    signer: &SignerInfo<'a>,
428    parent: &Reader<'a>,
429    version: Version,
430    faults: &mut OwnedVec<Error, W>,
431) -> Result<Option<SigningCertificate<'a>>> {
432    let Some(attributes) = signer.signed_attributes() else {
433        return Ok(None);
434    };
435    let field = version.field();
436    let mut first: Option<&crate::attribute::Attribute<'a>> = None;
437    for result in attributes.all_in(version.identifier(), parent) {
438        let attribute = result?;
439        if let Some(earlier) = first {
440            if !same_encoding(
441                parent,
442                earlier.as_bytes(),
443                attribute.as_bytes(),
444                field,
445                attribute.span().start,
446            )? {
447                return Err(Error::schema(
448                    ErrorCode::AttributeCardinality,
449                    field,
450                    attribute.span().start,
451                ));
452            }
453            record(
454                Error::schema(
455                    ErrorCode::AttributeCardinality,
456                    field,
457                    attribute.span().start,
458                ),
459                faults,
460                field,
461            )?;
462        } else {
463            first = Some(attribute);
464        }
465    }
466    let Some(attribute) = first else {
467        return Ok(None);
468    };
469    let Some(value) = attribute.values().first() else {
470        return Err(Error::schema(
471            ErrorCode::AttributeCardinality,
472            field,
473            attribute.span().start,
474        ));
475    };
476    for extra in &attribute.values()[1..] {
477        if !same_encoding(
478            parent,
479            value.as_bytes(),
480            extra.as_bytes(),
481            field,
482            extra.span().start,
483        )? {
484            return Err(Error::schema(
485                ErrorCode::AttributeCardinality,
486                field,
487                extra.span().start,
488            ));
489        }
490        record(
491            Error::schema(ErrorCode::AttributeCardinality, field, extra.span().start),
492            faults,
493            field,
494        )?;
495    }
496    let mut reader = attribute
497        .value_reader_in(0, parent, field)?
498        .ok_or_else(|| Error::schema(ErrorCode::MissingField, field, attribute.span().start))?;
499    let original = required(&mut reader, field)?;
500    SigningCertificate::read(&reader, &original, version).map(Some)
501}
502
503fn same_encoding(
504    parent: &Reader<'_>,
505    left: &[u8],
506    right: &[u8],
507    field: &'static str,
508    at: u64,
509) -> Result<bool> {
510    let execution = parent.execution();
511    execution
512        .charge_work(1)
513        .map_err(|reason| Error::resource(reason, field, at))?;
514    if left.len() != right.len() {
515        return Ok(false);
516    }
517    execution
518        .charge_work(left.len() as u64)
519        .map_err(|reason| Error::resource(reason, field, at))?;
520    Ok(left == right)
521}
522
523impl<'a> SigningCertificate<'a> {
524    /// Reads the original value with inherited depth/local and aggregate work.
525    ///
526    /// The first identifier must be readable to name the signer. Later malformed
527    /// identifiers/policies remain original restrictions plus retained faults;
528    /// no resource failure becomes a readable fault or an omitted restriction.
529    /// This reading establishes neither certificate hash agreement nor trust.
530    pub fn read(parent: &Reader<'a>, element: &Element<'a>, version: Version) -> Result<Self> {
531        let field = version.field();
532        require_sequence(element, field)?;
533        let mut outer = parent
534            .enter(element)
535            .map_err(|error| Error::encoding(error, field))?;
536        let certs = required(&mut outer, field)?;
537        require_sequence(&certs, field)?;
538        let mut list = outer
539            .enter(&certs)
540            .map_err(|error| Error::encoding(error, field))?;
541        let execution = parent.execution();
542        let mut faults = execution.vector();
543        let mut certificates = execution.vector();
544        let mut identifiers = execution.vector();
545        let first = required(&mut list, field)?;
546        let signer = read_identifier(&list, &first, version, &mut faults)?;
547        identifiers
548            .try_push(signer)
549            .map_err(|reason| Error::resource(reason, field, first.span().start))?;
550        certificates
551            .try_push(CertificateReading::Read(0))
552            .map_err(|reason| Error::resource(reason, field, first.span().start))?;
553        while let Some(entry) = list
554            .next_element()
555            .map_err(|error| Error::encoding(error, field))?
556        {
557            let reading = match read_identifier(&list, &entry, version, &mut faults) {
558                Ok(identifier) => {
559                    let index = identifiers.len();
560                    identifiers
561                        .try_push(identifier)
562                        .map_err(|reason| Error::resource(reason, field, entry.span().start))?;
563                    CertificateReading::Read(index)
564                }
565                Err(error) => {
566                    record(error, &mut faults, field)?;
567                    CertificateReading::Unreadable(entry)
568                }
569            };
570            certificates
571                .try_push(reading)
572                .map_err(|reason| Error::resource(reason, field, entry.span().start))?;
573        }
574        let policies = outer
575            .next_element()
576            .map_err(|error| Error::encoding(error, field))?;
577        if let Some(policies) = &policies
578            && let Err(error) = validate_policies(&outer, policies, field)
579        {
580            record(error, &mut faults, field)?;
581        }
582        if !outer.is_empty() {
583            record(
584                Error::schema(ErrorCode::TrailingData, field, outer.offset()),
585                &mut faults,
586                field,
587            )?;
588        }
589        Ok(Self {
590            version,
591            certificates: certificates.into_owned(),
592            identifiers: identifiers.into_owned(),
593            policies,
594            faults: faults.into_owned(),
595            element: *element,
596        })
597    }
598
599    /// Which independently evaluated attribute selected this value.
600    #[must_use]
601    pub fn version(&self) -> Version {
602        self.version
603    }
604    /// First signer identity and all subsequent signed restrictions in order.
605    #[must_use]
606    pub fn certificates(&self) -> &[CertificateReading<'a>] {
607        &self.certificates
608    }
609    /// Flat readable identities referenced by the original-order catalogue.
610    #[must_use]
611    pub fn identifiers(&self) -> &[CertificateId<'a>] {
612        &self.identifiers
613    }
614    /// The required first certificate identity, without a repeated lookup.
615    #[must_use]
616    pub fn signer(&self) -> &CertificateId<'a> {
617        // read() refuses an unreadable/missing first identifier before
618        // exposing this value; subsequent identities never move this slot.
619        &self.identifiers[0]
620    }
621    /// Original optional policy terms, including any separately reported fault.
622    #[must_use]
623    pub fn policies(&self) -> Option<&Element<'a>> {
624        self.policies.as_ref()
625    }
626    /// Readable schema/profile faults with original source locations.
627    #[must_use]
628    pub fn faults(&self) -> &[Error] {
629        &self.faults
630    }
631    /// Original source element.
632    #[must_use]
633    pub fn element(&self) -> &Element<'a> {
634        &self.element
635    }
636}
637
638fn read_identifier<'a, W: Work>(
639    parent: &Reader<'a>,
640    element: &Element<'a>,
641    version: Version,
642    faults: &mut OwnedVec<Error, W>,
643) -> Result<CertificateId<'a>> {
644    let field = version.field();
645    require_sequence(element, field)?;
646    let mut inside = parent
647        .enter(element)
648        .map_err(|error| Error::encoding(error, field))?;
649    let mut next = required(&mut inside, field)?;
650    // RFC2634 §5.4.1 mandates SHA-1 for V1. RFC5035 §4's ASN.1 DEFAULT
651    // mandates SHA-256 for omitted V2 hashAlgorithm, not SHA-1 (the original
652    // prose's SHA-1 description is corrected by erratum2364). These have no
653    // encoded AlgorithmIdentifier, so do not fabricate DER/NULL parameters.
654    // https://www.rfc-editor.org/rfc/rfc5035#section-4
655    let mut digest = DigestAlgorithm::Implied {
656        algorithm: match version {
657            Version::V1 => ImpliedDigest::EssSha1,
658            Version::V2 => ImpliedDigest::EssV2Sha256,
659        },
660        field,
661        span: element.span(),
662    };
663    if version == Version::V2 && next.tag() == Tag::constructed(universal::SEQUENCE) {
664        let algorithm =
665            structured_pkix::algorithm::read(&inside, &next, field).map_err(Error::pkix)?;
666        inside
667            .charge(
668                1 + algorithm
669                    .algorithm()
670                    .as_bytes()
671                    .len()
672                    .max(oid::SHA256.as_bytes().len()) as u64,
673            )
674            .map_err(|error| Error::encoding(error, field))?;
675        // X.690 §11.5: DER omits only the exact ASN.1 default. Written
676        // SHA-256 with NULL is not this absent-parameter default value;
677        // selected-provider SHA-2 parameter rules remain a separate step.
678        if algorithm.algorithm() == oid::SHA256 && algorithm.parameters().is_none() {
679            record(
680                Error::schema(ErrorCode::DefaultWritten, field, next.span().start),
681                faults,
682                field,
683            )?;
684        }
685        digest = DigestAlgorithm::Written(algorithm);
686        next = required(&mut inside, field)?;
687    } else if version == Version::V2 && next.tag() == Tag::primitive(universal::OID) {
688        let kind = inside
689            .oid(&next)
690            .map_err(|error| Error::encoding(error, field))?;
691        inside
692            .charge(1 + kind.as_bytes().len().max(oid::SHA256.as_bytes().len()) as u64)
693            .map_err(|error| Error::encoding(error, field))?;
694        // Recovery is not specified by ESS. The exact bare default OID has
695        // the same one SHA-256 reading either as a malformed identifier or
696        // extra data beside the default; any other bare OID is ambiguous.
697        if kind != oid::SHA256 {
698            return Err(Error::schema(
699                ErrorCode::AttributeSyntax,
700                field,
701                next.span().start,
702            ));
703        }
704        record(
705            Error::schema(ErrorCode::AttributeSyntax, field, next.span().start),
706            faults,
707            field,
708        )?;
709        next = required(&mut inside, field)?;
710    }
711    let hashed = Octets::read(&inside, &next, field)?;
712    let issuer = match inside
713        .next_element()
714        .map_err(|error| Error::encoding(error, field))?
715    {
716        None => IssuerReading::Absent,
717        Some(issuer) => match read_issuer_serial(&inside, &issuer, field, faults) {
718            Ok(value) => IssuerReading::Present(value),
719            Err(error) => {
720                record(error, faults, field)?;
721                IssuerReading::Unreadable(issuer)
722            }
723        },
724    };
725    if !inside.is_empty() {
726        record(
727            Error::schema(ErrorCode::TrailingData, field, inside.offset()),
728            faults,
729            field,
730        )?;
731    }
732    Ok(CertificateId {
733        digest,
734        hashed,
735        issuer,
736        element: *element,
737        field,
738    })
739}
740
741fn read_issuer_serial<'a, W: Work>(
742    parent: &Reader<'a>,
743    element: &Element<'a>,
744    field: &'static str,
745    faults: &mut OwnedVec<Error, W>,
746) -> Result<IssuerSerial<'a>> {
747    require_sequence(element, field)?;
748    let mut inside = parent
749        .enter(element)
750        .map_err(|error| Error::encoding(error, field))?;
751    let names_element = required(&mut inside, field)?;
752    require_sequence(&names_element, field)?;
753    let serial = inside
754        .read_integer()
755        .map_err(|error| Error::encoding(error, field))?;
756    if !inside.is_empty() {
757        record(
758            Error::schema(ErrorCode::TrailingData, field, inside.offset()),
759            faults,
760            field,
761        )?;
762    }
763    let mut list = inside
764        .enter(&names_element)
765        .map_err(|error| Error::encoding(error, field))?;
766    let mut names = parent.execution().vector();
767    while let Some(name) = list
768        .next_element()
769        .map_err(|error| Error::encoding(error, field))?
770    {
771        let value = general_name::read(&list, &name, field).map_err(Error::pkix)?;
772        names
773            .try_push(value)
774            .map_err(|reason| Error::resource(reason, field, name.span().start))?;
775    }
776    // RFC5035 §4: public-key certificates require exactly their issuer in
777    // the directoryName choice. Other readable names are retained as faults,
778    // never collapsed into a different issuer or an absent optional field.
779    // https://www.rfc-editor.org/rfc/rfc5035#section-4
780    if !matches!(&*names, [GeneralName::Directory(_)]) {
781        record(
782            Error::schema(
783                ErrorCode::AttributeSyntax,
784                field,
785                names_element.span().start,
786            ),
787            faults,
788            field,
789        )?;
790    }
791    Ok(IssuerSerial {
792        names: names.into_owned(),
793        serial,
794        names_element,
795        element: *element,
796    })
797}
798
799fn record<W: Work>(
800    error: Error,
801    faults: &mut OwnedVec<Error, W>,
802    field: &'static str,
803) -> Result<()> {
804    if error.is_terminal() {
805        return Err(error);
806    }
807    let at = error.offset();
808    faults
809        .try_push(error)
810        .map_err(|reason| Error::resource(reason, field, at))
811}
812
813fn require_sequence(element: &Element<'_>, field: &'static str) -> Result<()> {
814    if element.tag() != Tag::constructed(universal::SEQUENCE) {
815        return Err(Error::schema(
816            ErrorCode::UnexpectedTag,
817            field,
818            element.span().start,
819        ));
820    }
821    Ok(())
822}
823
824fn validate_policies(
825    parent: &Reader<'_>,
826    element: &Element<'_>,
827    field: &'static str,
828) -> Result<()> {
829    require_sequence(element, field)?;
830    let mut list = parent
831        .enter(element)
832        .map_err(|error| Error::encoding(error, field))?;
833    while let Some(policy) = list
834        .next_element()
835        .map_err(|error| Error::encoding(error, field))?
836    {
837        require_sequence(&policy, field)?;
838        let mut information = list
839            .enter(&policy)
840            .map_err(|error| Error::encoding(error, field))?;
841        information
842            .read_oid()
843            .map_err(|error| Error::encoding(error, field))?;
844        if let Some(qualifiers) = information
845            .next_element()
846            .map_err(|error| Error::encoding(error, field))?
847        {
848            require_sequence(&qualifiers, field)?;
849            let mut members = information
850                .enter(&qualifiers)
851                .map_err(|error| Error::encoding(error, field))?;
852            if members.is_empty() {
853                return Err(Error::schema(
854                    ErrorCode::AttributeSyntax,
855                    field,
856                    qualifiers.span().start,
857                ));
858            }
859            while let Some(member) = members
860                .next_element()
861                .map_err(|error| Error::encoding(error, field))?
862            {
863                require_sequence(&member, field)?;
864                let mut pair = members
865                    .enter(&member)
866                    .map_err(|error| Error::encoding(error, field))?;
867                let kind = pair
868                    .read_oid()
869                    .map_err(|error| Error::encoding(error, field))?;
870                let qualifier = required(&mut pair, field)?;
871                let work = 2
872                    + kind
873                        .as_bytes()
874                        .len()
875                        .min(structured_pkix::oid::CPS_QUALIFIER.as_bytes().len())
876                        as u64
877                    + kind
878                        .as_bytes()
879                        .len()
880                        .min(structured_pkix::oid::USER_NOTICE_QUALIFIER.as_bytes().len())
881                        as u64;
882                pair.charge(work)
883                    .map_err(|error| Error::encoding(error, field))?;
884                if (kind == structured_pkix::oid::CPS_QUALIFIER
885                    || kind == structured_pkix::oid::USER_NOTICE_QUALIFIER)
886                    && !structured_pkix::policy::qualifier_understood(&pair, kind, &qualifier)
887                        .map_err(Error::pkix)?
888                {
889                    return Err(Error::schema(
890                        ErrorCode::AttributeSyntax,
891                        field,
892                        qualifier.span().start,
893                    ));
894                }
895                if !pair.is_empty() {
896                    return Err(Error::schema(ErrorCode::TrailingData, field, pair.offset()));
897                }
898            }
899        }
900        if !information.is_empty() {
901            return Err(Error::schema(
902                ErrorCode::TrailingData,
903                field,
904                information.offset(),
905            ));
906        }
907    }
908    Ok(())
909}