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    signed_attributes_present: bool,
317    v1: Option<SigningCertificate<'a>>,
318    v2: Option<SigningCertificate<'a>>,
319    faults: OwnedVec<Error>,
320}
321
322impl<'a> SigningAttributes<'a> {
323    /// Reads the recognized signed fields using original value provenance.
324    ///
325    /// RFC5035 §§3/5 require one instance and one value in signed attributes.
326    /// Byte-identical repeats retain one reading plus a cardinality fault;
327    /// conflicting values refuse selection. Unsigned instances are retained
328    /// as placement faults, never used as certificate bindings. No resolver,
329    /// hash state, signature or caller payload is accessed during this step.
330    /// <https://www.rfc-editor.org/rfc/rfc5035#section-3>
331    pub fn read(signer: &SignerInfo<'a>, parent: &Reader<'a>) -> Result<Self> {
332        signer.require_session(parent, "signerInfo.signedAttrs")?;
333        let execution = parent.execution();
334        execution.charge_work(1).map_err(|reason| {
335            Error::resource(reason, "signerInfo.signedAttrs", signer.span().start)
336        })?;
337        let mut faults = execution.vector();
338        if let Some(unsigned) = signer.unsigned_attributes() {
339            for version in [Version::V1, Version::V2] {
340                for result in unsigned.all_in(version.identifier(), parent) {
341                    let attribute = result?;
342                    let field = match version {
343                        Version::V1 => "signerInfo.unsignedAttrs.signingCertificate",
344                        Version::V2 => "signerInfo.unsignedAttrs.signingCertificateV2",
345                    };
346                    record(
347                        Error::schema(ErrorCode::AttributePlacement, field, attribute.span().start),
348                        &mut faults,
349                        field,
350                    )?;
351                }
352            }
353        }
354        let v1 = read_attribute(signer, parent, Version::V1, &mut faults)?;
355        let v2 = read_attribute(signer, parent, Version::V2, &mut faults)?;
356        if v1.is_none()
357            && let Some(v2) = &v2
358        {
359            // RFC5035 §§1/2 retains mandatory V1 compatibility when SHA-1
360            // is used for certificate identification. V2 is independent, not
361            // a waiver of the companion attribute. This checks declared
362            // identities, without granting the selected provider permission
363            // to accept SHA-1 or bypassing parameter validation.
364            // https://www.rfc-editor.org/rfc/rfc5035#section-2
365            for identifier in v2.identifiers() {
366                execution.charge_work(1).map_err(|reason| {
367                    Error::resource(reason, Version::V2.field(), identifier.element.span().start)
368                })?;
369                if let DigestAlgorithm::Written(algorithm) = identifier.digest {
370                    execution
371                        .charge_work(
372                            algorithm
373                                .algorithm()
374                                .as_bytes()
375                                .len()
376                                .min(oid::SHA1.as_bytes().len()) as u64,
377                        )
378                        .map_err(|reason| {
379                            Error::resource(reason, Version::V2.field(), algorithm.offset())
380                        })?;
381                    if algorithm.algorithm() == oid::SHA1 {
382                        record(
383                            Error::schema(
384                                ErrorCode::MissingField,
385                                Version::V1.field(),
386                                identifier.element.span().start,
387                            ),
388                            &mut faults,
389                            Version::V1.field(),
390                        )?;
391                        break;
392                    }
393                }
394            }
395        }
396        Ok(Self {
397            signed_attributes_present: signer.signed_attributes().is_some(),
398            v1,
399            v2,
400            faults: faults.into_owned(),
401        })
402    }
403
404    /// Whether any signed attributes existed, independently of ESS presence.
405    #[must_use]
406    pub fn signed_attributes_present(&self) -> bool {
407        self.signed_attributes_present
408    }
409    /// Original V1 signer/path restrictions, when written.
410    #[must_use]
411    pub fn v1(&self) -> Option<&SigningCertificate<'a>> {
412        self.v1.as_ref()
413    }
414    /// Independently evaluated original V2 signer/path restrictions.
415    #[must_use]
416    pub fn v2(&self) -> Option<&SigningCertificate<'a>> {
417        self.v2.as_ref()
418    }
419    /// Placement and identical-repetition diagnostics; nested schema faults
420    /// remain with each [`SigningCertificate::faults`] collection.
421    #[must_use]
422    pub fn faults(&self) -> &[Error] {
423        &self.faults
424    }
425}
426
427fn read_attribute<'a, W: Work>(
428    signer: &SignerInfo<'a>,
429    parent: &Reader<'a>,
430    version: Version,
431    faults: &mut OwnedVec<Error, W>,
432) -> Result<Option<SigningCertificate<'a>>> {
433    let Some(attributes) = signer.signed_attributes() else {
434        return Ok(None);
435    };
436    let field = version.field();
437    let mut first: Option<&crate::attribute::Attribute<'a>> = None;
438    for result in attributes.all_in(version.identifier(), parent) {
439        let attribute = result?;
440        if let Some(earlier) = first {
441            if !same_encoding(
442                parent,
443                earlier.as_bytes(),
444                attribute.as_bytes(),
445                field,
446                attribute.span().start,
447            )? {
448                return Err(Error::schema(
449                    ErrorCode::DuplicateAttribute,
450                    field,
451                    attribute.span().start,
452                ));
453            }
454            record(
455                Error::schema(ErrorCode::DuplicateAttribute, field, attribute.span().start),
456                faults,
457                field,
458            )?;
459        } else {
460            first = Some(attribute);
461        }
462    }
463    let Some(attribute) = first else {
464        return Ok(None);
465    };
466    let Some(value) = attribute.values().first() else {
467        return Err(Error::schema(
468            ErrorCode::AttributeCardinality,
469            field,
470            attribute.span().start,
471        ));
472    };
473    for extra in &attribute.values()[1..] {
474        if !same_encoding(
475            parent,
476            value.as_bytes(),
477            extra.as_bytes(),
478            field,
479            extra.span().start,
480        )? {
481            return Err(Error::schema(
482                ErrorCode::AttributeCardinality,
483                field,
484                extra.span().start,
485            ));
486        }
487        record(
488            Error::schema(ErrorCode::AttributeCardinality, field, extra.span().start),
489            faults,
490            field,
491        )?;
492    }
493    let mut reader = attribute
494        .value_reader_in(0, parent, field)?
495        .ok_or_else(|| Error::schema(ErrorCode::MissingField, field, attribute.span().start))?;
496    let original = required(&mut reader, field)?;
497    SigningCertificate::read(&reader, &original, version).map(Some)
498}
499
500impl<'a> SigningCertificate<'a> {
501    /// Reads the original value with inherited depth/local and aggregate work.
502    ///
503    /// The first identifier must be readable to name the signer. Later malformed
504    /// identifiers/policies remain original restrictions plus retained faults;
505    /// no resource failure becomes a readable fault or an omitted restriction.
506    /// This reading establishes neither certificate hash agreement nor trust.
507    pub fn read(parent: &Reader<'a>, element: &Element<'a>, version: Version) -> Result<Self> {
508        let field = version.field();
509        require_sequence(element, field)?;
510        let mut outer = parent
511            .enter(element)
512            .map_err(|error| Error::encoding(error, field))?;
513        let certs = required(&mut outer, field)?;
514        require_sequence(&certs, field)?;
515        let mut list = outer
516            .enter(&certs)
517            .map_err(|error| Error::encoding(error, field))?;
518        let execution = parent.execution();
519        let mut faults = execution.vector();
520        let mut certificates = execution.vector();
521        let mut identifiers = execution.vector();
522        let first = required(&mut list, field)?;
523        let signer = read_identifier(&list, &first, version, &mut faults)?;
524        identifiers
525            .try_push(signer)
526            .map_err(|reason| Error::resource(reason, field, first.span().start))?;
527        certificates
528            .try_push(CertificateReading::Read(0))
529            .map_err(|reason| Error::resource(reason, field, first.span().start))?;
530        while let Some(entry) = list
531            .next_element()
532            .map_err(|error| Error::encoding(error, field))?
533        {
534            let reading = match read_identifier(&list, &entry, version, &mut faults) {
535                Ok(identifier) => {
536                    let index = identifiers.len();
537                    identifiers
538                        .try_push(identifier)
539                        .map_err(|reason| Error::resource(reason, field, entry.span().start))?;
540                    CertificateReading::Read(index)
541                }
542                Err(error) => {
543                    record(error, &mut faults, field)?;
544                    CertificateReading::Unreadable(entry)
545                }
546            };
547            certificates
548                .try_push(reading)
549                .map_err(|reason| Error::resource(reason, field, entry.span().start))?;
550        }
551        let policies = outer
552            .next_element()
553            .map_err(|error| Error::encoding(error, field))?;
554        if let Some(policies) = &policies
555            && let Err(error) = validate_policies(&outer, policies, field)
556        {
557            record(error, &mut faults, field)?;
558        }
559        if !outer.is_empty() {
560            record(
561                Error::schema(ErrorCode::TrailingData, field, outer.offset()),
562                &mut faults,
563                field,
564            )?;
565        }
566        Ok(Self {
567            version,
568            certificates: certificates.into_owned(),
569            identifiers: identifiers.into_owned(),
570            policies,
571            faults: faults.into_owned(),
572            element: *element,
573        })
574    }
575
576    /// Which independently evaluated attribute selected this value.
577    #[must_use]
578    pub fn version(&self) -> Version {
579        self.version
580    }
581    /// First signer identity and all subsequent signed restrictions in order.
582    #[must_use]
583    pub fn certificates(&self) -> &[CertificateReading<'a>] {
584        &self.certificates
585    }
586    /// Flat readable identities referenced by the original-order catalogue.
587    #[must_use]
588    pub fn identifiers(&self) -> &[CertificateId<'a>] {
589        &self.identifiers
590    }
591    /// The required first certificate identity, without a repeated lookup.
592    #[must_use]
593    pub fn signer(&self) -> &CertificateId<'a> {
594        // read() refuses an unreadable/missing first identifier before
595        // exposing this value; subsequent identities never move this slot.
596        &self.identifiers[0]
597    }
598    /// Original optional policy terms, including any separately reported fault.
599    #[must_use]
600    pub fn policies(&self) -> Option<&Element<'a>> {
601        self.policies.as_ref()
602    }
603    /// Readable schema/profile faults with original source locations.
604    #[must_use]
605    pub fn faults(&self) -> &[Error] {
606        &self.faults
607    }
608    /// Original source element.
609    #[must_use]
610    pub fn element(&self) -> &Element<'a> {
611        &self.element
612    }
613}
614
615fn read_identifier<'a, W: Work>(
616    parent: &Reader<'a>,
617    element: &Element<'a>,
618    version: Version,
619    faults: &mut OwnedVec<Error, W>,
620) -> Result<CertificateId<'a>> {
621    let field = version.field();
622    require_sequence(element, field)?;
623    let mut inside = parent
624        .enter(element)
625        .map_err(|error| Error::encoding(error, field))?;
626    let mut next = required(&mut inside, field)?;
627    // RFC2634 §5.4.1 mandates SHA-1 for V1. RFC5035 §4's ASN.1 DEFAULT
628    // mandates SHA-256 for omitted V2 hashAlgorithm, not SHA-1 (the original
629    // prose's SHA-1 description is corrected by erratum2364). These have no
630    // encoded AlgorithmIdentifier, so do not fabricate DER/NULL parameters.
631    // https://www.rfc-editor.org/rfc/rfc5035#section-4
632    let mut digest = DigestAlgorithm::Implied {
633        algorithm: match version {
634            Version::V1 => ImpliedDigest::EssSha1,
635            Version::V2 => ImpliedDigest::EssV2Sha256,
636        },
637        field,
638        span: element.span(),
639    };
640    if version == Version::V2 && next.tag() == Tag::constructed(universal::SEQUENCE) {
641        let algorithm =
642            structured_pkix::algorithm::read(&inside, &next, field).map_err(Error::pkix)?;
643        inside
644            .charge(
645                1 + algorithm
646                    .algorithm()
647                    .as_bytes()
648                    .len()
649                    .max(oid::SHA256.as_bytes().len()) as u64,
650            )
651            .map_err(|error| Error::encoding(error, field))?;
652        // X.690 §11.5: DER omits only the exact ASN.1 default. Written
653        // SHA-256 with NULL is not this absent-parameter default value;
654        // selected-provider SHA-2 parameter rules remain a separate step.
655        if algorithm.algorithm() == oid::SHA256 && algorithm.parameters().is_none() {
656            record(
657                Error::schema(ErrorCode::DefaultWritten, field, next.span().start),
658                faults,
659                field,
660            )?;
661        }
662        digest = DigestAlgorithm::Written(algorithm);
663        next = required(&mut inside, field)?;
664    } else if version == Version::V2 && next.tag() == Tag::primitive(universal::OID) {
665        let kind = inside
666            .oid(&next)
667            .map_err(|error| Error::encoding(error, field))?;
668        inside
669            .charge(1 + kind.as_bytes().len().max(oid::SHA256.as_bytes().len()) as u64)
670            .map_err(|error| Error::encoding(error, field))?;
671        // Recovery is not specified by ESS. The exact bare default OID has
672        // the same one SHA-256 reading either as a malformed identifier or
673        // extra data beside the default; any other bare OID is ambiguous.
674        if kind != oid::SHA256 {
675            return Err(Error::schema(
676                ErrorCode::AttributeSyntax,
677                field,
678                next.span().start,
679            ));
680        }
681        record(
682            Error::schema(ErrorCode::AttributeSyntax, field, next.span().start),
683            faults,
684            field,
685        )?;
686        next = required(&mut inside, field)?;
687    }
688    let hashed = Octets::read(&inside, &next, field)?;
689    let issuer = match inside
690        .next_element()
691        .map_err(|error| Error::encoding(error, field))?
692    {
693        None => IssuerReading::Absent,
694        Some(issuer) => match read_issuer_serial(&inside, &issuer, field, faults) {
695            Ok(value) => IssuerReading::Present(value),
696            Err(error) => {
697                record(error, faults, field)?;
698                IssuerReading::Unreadable(issuer)
699            }
700        },
701    };
702    if !inside.is_empty() {
703        record(
704            Error::schema(ErrorCode::TrailingData, field, inside.offset()),
705            faults,
706            field,
707        )?;
708    }
709    Ok(CertificateId {
710        digest,
711        hashed,
712        issuer,
713        element: *element,
714        field,
715    })
716}
717
718fn read_issuer_serial<'a, W: Work>(
719    parent: &Reader<'a>,
720    element: &Element<'a>,
721    field: &'static str,
722    faults: &mut OwnedVec<Error, W>,
723) -> Result<IssuerSerial<'a>> {
724    require_sequence(element, field)?;
725    let mut inside = parent
726        .enter(element)
727        .map_err(|error| Error::encoding(error, field))?;
728    let names_element = required(&mut inside, field)?;
729    require_sequence(&names_element, field)?;
730    let serial = inside
731        .read_integer()
732        .map_err(|error| Error::encoding(error, field))?;
733    if !inside.is_empty() {
734        record(
735            Error::schema(ErrorCode::TrailingData, field, inside.offset()),
736            faults,
737            field,
738        )?;
739    }
740    let mut list = inside
741        .enter(&names_element)
742        .map_err(|error| Error::encoding(error, field))?;
743    let mut names = parent.execution().vector();
744    while let Some(name) = list
745        .next_element()
746        .map_err(|error| Error::encoding(error, field))?
747    {
748        let value = general_name::read(&list, &name, field).map_err(Error::pkix)?;
749        names
750            .try_push(value)
751            .map_err(|reason| Error::resource(reason, field, name.span().start))?;
752    }
753    // RFC5035 §4: public-key certificates require exactly their issuer in
754    // the directoryName choice. Other readable names are retained as faults,
755    // never collapsed into a different issuer or an absent optional field.
756    // https://www.rfc-editor.org/rfc/rfc5035#section-4
757    if !matches!(&*names, [GeneralName::Directory(_)]) {
758        record(
759            Error::schema(
760                ErrorCode::AttributeSyntax,
761                field,
762                names_element.span().start,
763            ),
764            faults,
765            field,
766        )?;
767    }
768    Ok(IssuerSerial {
769        names: names.into_owned(),
770        serial,
771        names_element,
772        element: *element,
773    })
774}
775
776fn record<W: Work>(
777    error: Error,
778    faults: &mut OwnedVec<Error, W>,
779    field: &'static str,
780) -> Result<()> {
781    if error.is_terminal() {
782        return Err(error);
783    }
784    let at = error.offset();
785    faults
786        .try_push(error)
787        .map_err(|reason| Error::resource(reason, field, at))
788}
789
790fn require_sequence(element: &Element<'_>, field: &'static str) -> Result<()> {
791    if element.tag() != Tag::constructed(universal::SEQUENCE) {
792        return Err(Error::schema(
793            ErrorCode::UnexpectedTag,
794            field,
795            element.span().start,
796        ));
797    }
798    Ok(())
799}
800
801fn validate_policies(
802    parent: &Reader<'_>,
803    element: &Element<'_>,
804    field: &'static str,
805) -> Result<()> {
806    require_sequence(element, field)?;
807    let mut list = parent
808        .enter(element)
809        .map_err(|error| Error::encoding(error, field))?;
810    while let Some(policy) = list
811        .next_element()
812        .map_err(|error| Error::encoding(error, field))?
813    {
814        require_sequence(&policy, field)?;
815        let mut information = list
816            .enter(&policy)
817            .map_err(|error| Error::encoding(error, field))?;
818        information
819            .read_oid()
820            .map_err(|error| Error::encoding(error, field))?;
821        if let Some(qualifiers) = information
822            .next_element()
823            .map_err(|error| Error::encoding(error, field))?
824        {
825            require_sequence(&qualifiers, field)?;
826            let mut members = information
827                .enter(&qualifiers)
828                .map_err(|error| Error::encoding(error, field))?;
829            if members.is_empty() {
830                return Err(Error::schema(
831                    ErrorCode::AttributeSyntax,
832                    field,
833                    qualifiers.span().start,
834                ));
835            }
836            while let Some(member) = members
837                .next_element()
838                .map_err(|error| Error::encoding(error, field))?
839            {
840                require_sequence(&member, field)?;
841                let mut pair = members
842                    .enter(&member)
843                    .map_err(|error| Error::encoding(error, field))?;
844                let kind = pair
845                    .read_oid()
846                    .map_err(|error| Error::encoding(error, field))?;
847                let qualifier = required(&mut pair, field)?;
848                let work = 2
849                    + kind
850                        .as_bytes()
851                        .len()
852                        .min(structured_pkix::oid::CPS_QUALIFIER.as_bytes().len())
853                        as u64
854                    + kind
855                        .as_bytes()
856                        .len()
857                        .min(structured_pkix::oid::USER_NOTICE_QUALIFIER.as_bytes().len())
858                        as u64;
859                pair.charge(work)
860                    .map_err(|error| Error::encoding(error, field))?;
861                if (kind == structured_pkix::oid::CPS_QUALIFIER
862                    || kind == structured_pkix::oid::USER_NOTICE_QUALIFIER)
863                    && !structured_pkix::policy::qualifier_understood(&pair, kind, &qualifier)
864                        .map_err(Error::pkix)?
865                {
866                    return Err(Error::schema(
867                        ErrorCode::AttributeSyntax,
868                        field,
869                        qualifier.span().start,
870                    ));
871                }
872                if !pair.is_empty() {
873                    return Err(Error::schema(ErrorCode::TrailingData, field, pair.offset()));
874                }
875            }
876        }
877        if !information.is_empty() {
878            return Err(Error::schema(
879                ErrorCode::TrailingData,
880                field,
881                information.offset(),
882            ));
883        }
884    }
885    Ok(())
886}