Skip to main content

structured_cms/
ess.rs

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