Skip to main content

structured_pkix/
general_name.rs

1//! The several kinds of name an extension can carry.
2//!
3//! Some of these are locations: a distribution point, a status service, a
4//! practice statement. They are read as text and nothing here resolves one. A
5//! parser that fetched a location a document named would be asking a server
6//! chosen by whoever wrote the document to confirm what the document says, and
7//! would tell that server which document is being looked at.
8
9use crate::error::{Error, ErrorCode, Located, Result};
10use crate::extension::{self, Extension};
11use crate::name::{self, Name};
12use core::net::{Ipv4Addr, Ipv6Addr};
13use structured_asn1::memory::{Memory, OwnedVec};
14use structured_asn1::{Class, Element, Oid, ScopedReader as Reader, Tag, universal};
15
16mod equality;
17mod ldap;
18mod uri;
19
20/// One name, in whichever of the forms the schema allows.
21#[derive(Debug)]
22#[non_exhaustive]
23pub enum GeneralName<'a> {
24    /// A form defined elsewhere, kept as its identifier and its value.
25    Other {
26        /// What kind of name this is.
27        type_id: Oid<'a>,
28        /// The value, as it was encoded.
29        value: &'a [u8],
30    },
31    /// An electronic mail address.
32    Email(&'a str),
33    /// A domain name.
34    Dns(&'a str),
35    /// An X.400 address, kept as encoded because nothing here reads one.
36    X400(&'a [u8]),
37    /// A distinguished name, the same kind a certificate's subject is.
38    Directory(Name<'a>),
39    /// An EDI party name, kept as encoded.
40    EdiParty(&'a [u8]),
41    /// A location, which is text and is never fetched.
42    Uri(&'a str),
43    /// An address, as the four or sixteen bytes it is written in. In a name
44    /// constraint this is twice that, the address and then its mask.
45    IpAddress(&'a [u8]),
46    /// A name that is itself an identifier.
47    RegisteredId(Oid<'a>),
48}
49
50impl GeneralName<'_> {
51    /// Compares retained names in the current request's execution account.
52    ///
53    /// DNS and ASCII mailbox identities use RFC5280 §§7.2/7.5 as updated by
54    /// RFC9549; Directory names use [`Name::matches`], and URIs use §7.4
55    /// normalization. Other, X.400 and EDI values have encoded identity only;
56    /// their type-specific semantic matching rules are not implemented here.
57    /// IP addresses and masks compare exactly, without subnet containment.
58    /// This query does not establish that a caller-constructed value was decoded
59    /// successfully. Decode untrusted names with [`read`] first.
60    ///
61    /// # Errors
62    /// Resource refusal remains an error, rather than a mismatch. A retained
63    /// value carries no universal document span, so this query returns an
64    /// unlocated error code. Producing reads use [`matches`] for location.
65    ///
66    /// # Examples
67    /// ```
68    /// use std::sync::Arc;
69    /// use structured_asn1::memory::{Memory, MemoryAccount};
70    /// use structured_pkix::general_name::GeneralName;
71    /// let execution = Memory::new(Arc::new(MemoryAccount::new(1024, 4096)));
72    /// assert!(GeneralName::Dns("EXAMPLE.COM").matches(&GeneralName::Dns("example.com"), &execution)?);
73    /// # Ok::<(), structured_pkix::ErrorCode>(())
74    /// ```
75    pub fn matches(
76        &self,
77        other: &GeneralName<'_>,
78        execution: &Memory,
79    ) -> core::result::Result<bool, ErrorCode> {
80        equality::matches(self, other, execution, None, 0, "generalName")
81            .map_err(|error| error.code())
82    }
83}
84
85/// Compares decoded names as part of their producing parser session.
86///
87/// The same semantic core as [`GeneralName::matches`] admits scratch allocation,
88/// sorting, normalization and comparison against both the original parser-local
89/// work ceiling and current aggregate account. Deferred BER values retain their
90/// original depth. `at` and `field` locate this comparison in the caller's source;
91/// names contain borrowed values rather than a universal outer source span.
92/// No malformed-input recovery or duplicate diagnostics are performed here.
93///
94/// # Errors
95/// Reports resource refusal separately from `Ok(false)`, preserving the supplied
96/// comparison location. Decode both untrusted names successfully with [`read`]
97/// before invoking this comparison.
98///
99/// # Examples
100/// ```
101/// use std::sync::Arc;
102/// use structured_asn1::{Parser, memory::{Memory, MemoryAccount}};
103/// use structured_pkix::general_name::{self, GeneralName};
104/// let parser = Parser::ber(Memory::new(Arc::new(MemoryAccount::new(1024, 4096))));
105/// let reader = parser.read(&[], 400).unwrap();
106/// assert!(general_name::matches(&reader, &GeneralName::Dns("EXAMPLE.COM"), &GeneralName::Dns("example.com"), 777, "tsa")?);
107/// # Ok::<(), structured_pkix::Error>(())
108/// ```
109pub fn matches(
110    parent: &Reader<'_, '_>,
111    left: &GeneralName<'_>,
112    right: &GeneralName<'_>,
113    at: u64,
114    field: &'static str,
115) -> Result<bool> {
116    equality::matches(left, right, &parent.execution(), Some(parent), at, field)
117}
118
119pub(crate) fn matches_at(
120    left: &GeneralName<'_>,
121    right: &GeneralName<'_>,
122    execution: &impl structured_asn1::memory::Work,
123    at: u64,
124    field: &'static str,
125) -> Result<bool> {
126    equality::matches(left, right, execution, None, at, field)
127}
128
129/// Reads a sequence of names.
130pub(crate) fn read_all<'a>(
131    parent: &Reader<'a, '_>,
132    element: &Element<'a>,
133    field: &'static str,
134) -> Result<OwnedVec<GeneralName<'a>>> {
135    let mut inside = parent.enter(element).field(field)?;
136    read_list(&mut inside, field, element.span().start)
137}
138
139/// Reads the names of a container already entered.
140pub(crate) fn read_list<'a>(
141    inside: &mut Reader<'a, '_>,
142    field: &'static str,
143    offset: u64,
144) -> Result<OwnedVec<GeneralName<'a>>> {
145    read_list_as(inside, field, offset, AddressForm::Single)
146}
147
148/// Distribution locations use URI grammar, rather than SAN host identity
149/// rules. RFC5280 §4.2.1.13 permits hostless LDAP URLs with the required DN
150/// and CRL attribute, e.g. `ldap:///cn=CA?certificateRevocationList;binary`.
151pub(crate) fn read_locations<'a>(
152    parent: &Reader<'a, '_>,
153    element: &Element<'a>,
154    field: &'static str,
155) -> Result<OwnedVec<GeneralName<'a>>> {
156    let mut inside = parent.enter(element).field(field)?;
157    read_list_as(
158        &mut inside,
159        field,
160        element.span().start,
161        AddressForm::Location,
162    )
163}
164
165fn read_list_as<'a>(
166    inside: &mut Reader<'a, '_>,
167    field: &'static str,
168    offset: u64,
169    form: AddressForm,
170) -> Result<OwnedVec<GeneralName<'a>>> {
171    let mut names = inside.memory().vector();
172    while let Some(entry) = inside.next_element().field(field)? {
173        let name = read_as(inside, &entry, field, form)?;
174        let work = inside.execution();
175        // Cursor advancement and read_as do not replace this reader's account.
176        // Move the existing keeper into scoped work and back without cloning
177        // an account per name or holding a borrow across the mutable cursor.
178        let Ok(mut scoped) = names.with_execution(&work) else {
179            unreachable!("the name decoder preserves its catalogue account")
180        };
181        scoped
182            .try_push(name)
183            .map_err(|reason| Error::resource(reason, field, entry.span().start))?;
184        names = scoped.into_owned();
185    }
186
187    // A list of names that holds none says nothing while claiming to, and the
188    // schema asks for at least one everywhere this is used.
189    if names.is_empty() {
190        return Err(Error::new(ErrorCode::EmptySequence, field, offset));
191    }
192    Ok(names)
193}
194
195/// Reads the value of an alternative name extension, which is a list of names
196/// and nothing else.
197pub fn read_alternative<'a, W: structured_asn1::memory::Work>(
198    extension: &Extension<'a>,
199    execution: &W,
200) -> Result<OwnedVec<GeneralName<'a>>> {
201    let field = if extension.kind() == crate::oid::ISSUER_ALT_NAME {
202        "extensions.issuerAltName"
203    } else {
204        "extensions.subjectAltName"
205    };
206
207    let mut inside = extension::sequence(extension, field, execution)?;
208    read_list(&mut inside, field, extension.span().start)
209}
210
211/// Where a name sits, which decides which forms its fields take.
212///
213/// The same tags carry different forms in the two positions: an address is
214/// bare in a list of names and paired with its mask in a name-constraint
215/// subtree, and a mail or location constraint may name a host or a domain
216/// where a name must be a mailbox or a URI. Only the position says which.
217#[derive(Debug, Clone, Copy, PartialEq, Eq)]
218pub(crate) enum AddressForm {
219    /// A name in a list of names: a bare address, a mailbox, a URI.
220    Single,
221    /// Distribution location: general URI, not a certificate SAN host.
222    Location,
223    /// The base of a name-constraint subtree: an address with its mask, and
224    /// the host and domain forms of a mail or location constraint.
225    WithMask,
226}
227
228impl AddressForm {
229    fn permits(self, length: usize) -> bool {
230        match self {
231            Self::Single | Self::Location => length == 4 || length == 16,
232            Self::WithMask => length == 8 || length == 32,
233        }
234    }
235}
236
237/// Reads one name from the element whose tag says which kind it is.
238///
239/// The parent must be the reader that produced the element: decoding retains
240/// its shared budget, depth and offsets. This also serves names outside
241/// certificates, such as RFC 3161 §2.4.2's timestamp `tsa` field.
242/// <https://www.rfc-editor.org/rfc/rfc3161#section-2.4.2>
243///
244/// # Errors
245///
246/// Reports malformed name syntax, encoding faults and exhausted limits.
247pub fn read<'a>(
248    parent: &Reader<'a, '_>,
249    element: &Element<'a>,
250    field: &'static str,
251) -> Result<GeneralName<'a>> {
252    read_as(parent, element, field, AddressForm::Single)
253}
254
255/// Whether text is the mail form the position takes.
256///
257/// A name is a mailbox (RFC 5280 4.2.1.6). A constraint may also name every
258/// mailbox at a host, or every mailbox in a domain by a leading period
259/// (4.2.1.10).
260fn mailbox_fits(text: &str, address_form: AddressForm) -> bool {
261    match address_form {
262        AddressForm::Single | AddressForm::Location => is_mailbox(text),
263        AddressForm::WithMask => {
264            is_mailbox(text) || is_domain(text) || text.strip_prefix('.').is_some_and(is_domain)
265        }
266    }
267}
268
269/// Whether text is the domain form the position takes.
270///
271/// A name holds the preferred name syntax (RFC 5280 4.2.1.6, via RFC 1034
272/// 3.5 as modified by RFC 1123): a value that is merely ASCII is not yet a
273/// domain, and reporting one would put an identity in the report the
274/// certificate does not carry.
275///
276/// A subtree base is written the same way, with one addition the profile
277/// does not spell out here. It writes a domain-name restriction as a name
278/// further labels are added to the left of (4.2.1.10), and gives the leading
279/// period to the mail and location forms in the two paragraphs above that
280/// one. It does not say to refuse the period here; issuers write it, and the
281/// extension is critical, so refusing the value refuses the authority and
282/// every certificate under it over a spelling of the same subtree. What the
283/// period means is for the reader of the constraint to decide, and the value
284/// is preserved as written.
285fn domain_fits(text: &str, address_form: AddressForm) -> bool {
286    match address_form {
287        AddressForm::Single | AddressForm::Location => is_domain(text),
288        AddressForm::WithMask => is_domain(text) || text.strip_prefix('.').is_some_and(is_domain),
289    }
290}
291
292pub(crate) fn read_as<'a>(
293    parent: &Reader<'a, '_>,
294    element: &Element<'a>,
295    field: &'static str,
296    address_form: AddressForm,
297) -> Result<GeneralName<'a>> {
298    let fail = |code| Error::new(code, field, element.span().start);
299    let tag = element.tag();
300
301    if tag.class() != Class::ContextSpecific {
302        return Err(fail(ErrorCode::Encoding(
303            structured_asn1::ErrorCode::UnexpectedTag,
304        )));
305    }
306
307    // The tag says which kind of name this is, and whether the value is a
308    // structure or a string. A string written as a structure is the segmented
309    // form, which is a second spelling of one value and is not read here. A
310    // number this reader does not name is none of the nine forms, and falls
311    // to the same refusal as a number it names that this schema does not
312    // have.
313    let Some(number) = tag.number() else {
314        return Err(fail(ErrorCode::Encoding(
315            structured_asn1::ErrorCode::UnexpectedTag,
316        )));
317    };
318    match (number, tag.is_constructed()) {
319        (0, true) => read_other(parent, element, field),
320        (1, false) => {
321            let email = text(parent, element.contents(), field, element)?;
322            if !mailbox_fits(email, address_form) {
323                return Err(fail(ErrorCode::MalformedValue));
324            }
325            Ok(GeneralName::Email(email))
326        }
327        (2, false) => {
328            let domain = text(parent, element.contents(), field, element)?;
329            if !domain_fits(domain, address_form) {
330                return Err(fail(ErrorCode::MalformedValue));
331            }
332            Ok(GeneralName::Dns(domain))
333        }
334        (3, true) => {
335            read_x400(parent, element, field)?;
336            Ok(GeneralName::X400(element.contents()))
337        }
338        (4, true) => {
339            // The name it holds is a choice, which cannot take the tag of its
340            // position, so the tag wraps it rather than replacing it.
341            let mut inside = parent.enter(element).field(field)?;
342            let inner = inside
343                .next_element()
344                .field(field)?
345                .ok_or_else(|| fail(ErrorCode::MissingField))?;
346            let name = name::read(&inside, &inner, field)?;
347            // An empty name is legal as a certificate's subject, where the
348            // alternative name carries the identity instead. As a general
349            // name it IS the identity, and empty it names nobody; as a
350            // subtree base it is the root, under which every directory name
351            // sits, so only the identity-bearing position refuses it. Every
352            // attribute of a kind this crate names must be written inside
353            // its syntax either way: an identity this run never read must
354            // not read as a supplied one. Unknown kinds stay data.
355            //
356            // The walk touches every attribute's contents, and a kind whose
357            // schema leaves the size unbounded has no local ceiling, so the
358            // lengths are charged to the shared work budget before it runs.
359            if address_form != AddressForm::WithMask && name.is_empty() {
360                return Err(fail(ErrorCode::MalformedValue));
361            }
362            for attribute in name.attributes() {
363                if !name::well_formed_in(attribute, &inside, field)? {
364                    return Err(fail(ErrorCode::MalformedValue));
365                }
366            }
367            if !inside.is_empty() {
368                return Err(fail(ErrorCode::TrailingData));
369            }
370            Ok(GeneralName::Directory(name))
371        }
372        (5, true) => {
373            read_edi_party(parent, element, field)?;
374            Ok(GeneralName::EdiParty(element.contents()))
375        }
376        (6, false) => {
377            let location = text(parent, element.contents(), field, element)?;
378            // RFC5280 §4.2.1.6 requires a scheme and forbids relative URIs.
379            // RFC3986 §3's URI includes a fragment; §4.3 absolute-URI is a
380            // distinct, fragment-free production, not the SAN grammar.
381            // https://www.rfc-editor.org/rfc/rfc5280.html#section-4.2.1.6
382            // https://www.rfc-editor.org/rfc/rfc3986.html#section-3
383            // A constraint
384            // applies to the host and is written as a host or a domain
385            // (4.2.1.10).
386            let fits = match address_form {
387                AddressForm::Single => {
388                    // Admit the additional fragment-delimiter scan before
389                    // selecting the existing scheme/authority semantic core.
390                    parent.charge(location.len() as u64).field(field)?;
391                    is_uri(location)
392                }
393                AddressForm::Location => {
394                    // RFC5280 §4.2.1.13 permits an empty LDAP host and leaves
395                    // server selection to the caller. No host is invented.
396                    // https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.13
397                    location_uri(parent, element, location, field)?
398                }
399                AddressForm::WithMask => {
400                    is_domain(location) || location.strip_prefix('.').is_some_and(is_domain)
401                }
402            };
403            if !fits {
404                return Err(fail(ErrorCode::MalformedValue));
405            }
406            Ok(GeneralName::Uri(location))
407        }
408        (7, false) => {
409            let contents = element.contents();
410            if !address_form.permits(contents.len()) {
411                return Err(fail(ErrorCode::MalformedValue));
412            }
413            // A constraint region is CIDR (RFC 5280 4.2.1.10, via RFC 4632):
414            // the mask half is leading one bits and nothing after them. A
415            // gap describes no range this profile defines.
416            if address_form == AddressForm::WithMask
417                && !is_contiguous_mask(&contents[contents.len() / 2..])
418            {
419                return Err(fail(ErrorCode::MalformedValue));
420            }
421            Ok(GeneralName::IpAddress(contents))
422        }
423        (8, false) => parent
424            .oid(element)
425            .field(field)
426            .map(GeneralName::RegisteredId),
427        _ => Err(fail(ErrorCode::Encoding(
428            structured_asn1::ErrorCode::UnexpectedTag,
429        ))),
430    }
431}
432
433/// Validates an X.400 OR-address: the mandatory built-in standard
434/// attributes, then optionally the domain-defined attributes and the
435/// extension attributes (X.411 `ORAddress`, referenced from RFC 5280
436/// appendix A). The members inside each container are validated against
437/// that schema too, because a critical name extension carrying members
438/// outside it must not read as parsed; the address as a whole still stays
439/// encoded, since nothing in this product routes to a messaging system.
440fn read_x400<'a>(
441    parent: &Reader<'a, '_>,
442    element: &Element<'a>,
443    field: &'static str,
444) -> Result<()> {
445    let fail = |code| Error::new(code, field, element.span().start);
446
447    let mut inside = parent.enter(element).field(field)?;
448    let standard = inside
449        .next_element()
450        .field(field)?
451        .ok_or_else(|| fail(ErrorCode::MissingField))?;
452    if standard.tag() != Tag::constructed(universal::SEQUENCE) {
453        return Err(fail(ErrorCode::Encoding(
454            structured_asn1::ErrorCode::UnexpectedTag,
455        )));
456    }
457    read_standard_attributes(&inside, &standard, field)?;
458
459    // The two optional members, in order: a SEQUENCE OF, then a SET OF.
460    let mut next = inside.next_element().field(field)?;
461    if let Some(domain_defined) = next
462        && domain_defined.tag() == Tag::constructed(universal::SEQUENCE)
463    {
464        read_domain_defined(&inside, &domain_defined, field)?;
465        next = inside.next_element().field(field)?;
466    }
467    if let Some(extensions) = next {
468        if extensions.tag() != Tag::constructed(universal::SET) {
469            return Err(fail(ErrorCode::Encoding(
470                structured_asn1::ErrorCode::UnexpectedTag,
471            )));
472        }
473        read_extension_attributes(&inside, &extensions, field)?;
474        if !inside.is_empty() {
475            return Err(fail(ErrorCode::TrailingData));
476        }
477    }
478    Ok(())
479}
480
481/// The tag numbers X.411 string types carry, named here since nothing below
482/// the schema needs them.
483const X400_NUMERIC_STRING: u32 = 18;
484
485/// Requires a universal string member to be a `NumericString` or a
486/// `PrintableString`, inside its repertoire and the size the schema gives
487/// that member (X.411 via RFC 5280 appendix A, whose bounds these are).
488fn x400_string(element: &Element<'_>, field: &'static str, size: (usize, usize)) -> Result<()> {
489    let fail = |code| Error::new(code, field, element.span().start);
490    let contents = element.contents();
491    // The primitive form only: DER has no constructed spelling of these
492    // strings, and the class and number alone would let one through.
493    let fits = match (
494        element.tag().class(),
495        element.tag().number(),
496        element.tag().is_constructed(),
497    ) {
498        (Class::Universal, Some(X400_NUMERIC_STRING), false) => crate::name::is_numeric(contents),
499        (Class::Universal, Some(universal::PRINTABLE_STRING), false) => {
500            crate::name::is_printable(contents)
501        }
502        _ => {
503            return Err(fail(ErrorCode::Encoding(
504                structured_asn1::ErrorCode::UnexpectedTag,
505            )));
506        }
507    };
508    if !fits || contents.len() < size.0 || contents.len() > size.1 {
509        return Err(fail(ErrorCode::MalformedValue));
510    }
511    Ok(())
512}
513
514/// Requires a primitive `PrintableString` of the given size, refusing the
515/// numeric alternative the wider choice permits.
516fn x400_printable(element: &Element<'_>, field: &'static str, size: (usize, usize)) -> Result<()> {
517    let fail = |code| Error::new(code, field, element.span().start);
518    let tag = element.tag();
519    if tag.class() != Class::Universal
520        || tag.number() != Some(universal::PRINTABLE_STRING)
521        || tag.is_constructed()
522    {
523        return Err(fail(ErrorCode::Encoding(
524            structured_asn1::ErrorCode::UnexpectedTag,
525        )));
526    }
527    let contents = element.contents();
528    if !crate::name::is_printable(contents) || contents.len() < size.0 || contents.len() > size.1 {
529        return Err(fail(ErrorCode::MalformedValue));
530    }
531    Ok(())
532}
533
534/// Which repertoire an X.400 string member is held to. Teletex is a
535/// negotiation this crate does not implement, so only its presence and size
536/// are checkable.
537#[derive(Clone, Copy)]
538enum X400Repertoire {
539    Numeric,
540    Printable,
541    Teletex,
542}
543
544/// Requires an implicitly tagged member's contents to be one repertoire
545/// within one size, the tag having replaced the string type.
546fn x400_implicit(
547    element: &Element<'_>,
548    field: &'static str,
549    repertoire: X400Repertoire,
550    most: usize,
551) -> Result<()> {
552    let fail = |code| Error::new(code, field, element.span().start);
553    let contents = element.contents();
554    // The size is decided before the repertoire. These fields are bounded to
555    // a few dozen characters by the schema, while the element they arrive in
556    // may be as long as the parser's length limit permits: walking one to
557    // its end to learn it is digits, only to refuse it for its length, is
558    // work the caller's budget never asked for. Afterwards the walk is
559    // bounded by the schema's own ceiling.
560    if contents.is_empty() || contents.len() > most {
561        return Err(fail(ErrorCode::MalformedValue));
562    }
563    let fits = match repertoire {
564        X400Repertoire::Numeric => crate::name::is_numeric(contents),
565        X400Repertoire::Printable => crate::name::is_printable(contents),
566        X400Repertoire::Teletex => true,
567    };
568    if !fits {
569        return Err(fail(ErrorCode::MalformedValue));
570    }
571    Ok(())
572}
573
574/// Requires a universal `TeletexString` member, primitive, within one size.
575fn x400_teletex(element: &Element<'_>, field: &'static str, size: (usize, usize)) -> Result<()> {
576    let fail = |code| Error::new(code, field, element.span().start);
577    // TeletexString is tag number 20; its repertoire is a negotiation this
578    // crate does not implement, so presence and size are what is checkable.
579    if element.tag() != Tag::primitive(20) {
580        return Err(fail(ErrorCode::Encoding(
581            structured_asn1::ErrorCode::UnexpectedTag,
582        )));
583    }
584    let length = element.contents().len();
585    if length < size.0 || length > size.1 {
586        return Err(fail(ErrorCode::MalformedValue));
587    }
588    Ok(())
589}
590
591/// Requires a bare country choice: an X.121 DCC of exactly three digits, or
592/// an ISO 3166 alpha-2 code of exactly two uppercase letters. The wider
593/// repertoires of the string types name no country.
594fn x400_bare_country(value: &Element<'_>, field: &'static str) -> Result<()> {
595    let fail = |code| Error::new(code, field, value.span().start);
596    let contents = value.contents();
597    let fits = match (
598        value.tag().class(),
599        value.tag().number(),
600        value.tag().is_constructed(),
601    ) {
602        (Class::Universal, Some(X400_NUMERIC_STRING), false) => {
603            contents.len() == 3 && contents.iter().all(u8::is_ascii_digit)
604        }
605        (Class::Universal, Some(universal::PRINTABLE_STRING), false) => {
606            contents.len() == 2 && contents.iter().all(u8::is_ascii_uppercase)
607        }
608        _ => {
609            return Err(fail(ErrorCode::Encoding(
610                structured_asn1::ErrorCode::UnexpectedTag,
611            )));
612        }
613    };
614    if !fits {
615        return Err(fail(ErrorCode::MalformedValue));
616    }
617    Ok(())
618}
619
620/// Requires a wrapper to hold exactly one country choice.
621fn x400_country<'a>(
622    parent: &Reader<'a, '_>,
623    wrapper: &Element<'a>,
624    field: &'static str,
625) -> Result<()> {
626    let fail = |code| Error::new(code, field, wrapper.span().start);
627    let mut inside = parent.enter(wrapper).field(field)?;
628    let value = inside
629        .next_element()
630        .field(field)?
631        .ok_or_else(|| fail(ErrorCode::MissingField))?;
632    if !inside.is_empty() {
633        return Err(fail(ErrorCode::TrailingData));
634    }
635    x400_bare_country(&value, field)
636}
637
638/// Requires a wrapper to hold exactly one string of a two-way choice, sized
639/// as the schema sizes each side of that choice.
640fn x400_choice<'a>(
641    parent: &Reader<'a, '_>,
642    wrapper: &Element<'a>,
643    field: &'static str,
644    numeric: (usize, usize),
645    printable: (usize, usize),
646) -> Result<()> {
647    let fail = |code| Error::new(code, field, wrapper.span().start);
648    let mut inside = parent.enter(wrapper).field(field)?;
649    let value = inside
650        .next_element()
651        .field(field)?
652        .ok_or_else(|| fail(ErrorCode::MissingField))?;
653    if !inside.is_empty() {
654        return Err(fail(ErrorCode::TrailingData));
655    }
656    let size = match (value.tag().class(), value.tag().number()) {
657        (Class::Universal, Some(X400_NUMERIC_STRING)) => numeric,
658        (Class::Universal, Some(universal::PRINTABLE_STRING)) => printable,
659        _ => {
660            return Err(fail(ErrorCode::Encoding(
661                structured_asn1::ErrorCode::UnexpectedTag,
662            )));
663        }
664    };
665    x400_string(&value, field, size)
666}
667
668/// The members X.411 gives the built-in standard attributes, each optional,
669/// each at most once, in schema order, with the strings held to their
670/// repertoires and the appendix's upper bounds.
671fn read_standard_attributes<'a>(
672    parent: &Reader<'a, '_>,
673    element: &Element<'a>,
674    field: &'static str,
675) -> Result<()> {
676    let mut inside = parent.enter(element).field(field)?;
677    let mut last_rank = -1i8;
678    while let Some(member) = inside.next_element().field(field)? {
679        let bad = |code| Error::new(code, field, member.span().start);
680        let tag = member.tag();
681        // A number this reader does not name is no member of the structure,
682        // and falls to the same refusal as any other number it does not have.
683        let Some(number) = tag.number() else {
684            return Err(bad(ErrorCode::Encoding(
685                structured_asn1::ErrorCode::UnexpectedTag,
686            )));
687        };
688        let rank: i8 = match (tag.class(), number, tag.is_constructed()) {
689            // The country: exactly a three-digit or a two-letter code,
690            // wrapped because a choice keeps its own tag.
691            (Class::Application, 1, true) => {
692                x400_country(&inside, &member, field)?;
693                0
694            }
695            // The administration domain: the same choice, up to sixteen
696            // characters, empty naming the default domain.
697            (Class::Application, 2, true) => {
698                x400_choice(&inside, &member, field, (0, 16), (0, 16))?;
699                1
700            }
701            // network-address: an X.121 address of up to sixteen digits.
702            (Class::ContextSpecific, 0, false) => {
703                x400_implicit(&member, field, X400Repertoire::Numeric, 16)?;
704                2
705            }
706            // terminal-identifier.
707            (Class::ContextSpecific, 1, false) => {
708                x400_implicit(&member, field, X400Repertoire::Printable, 24)?;
709                3
710            }
711            // private-domain-name: the numeric-or-printable choice again.
712            (Class::ContextSpecific, 2, true) => {
713                x400_choice(&inside, &member, field, (1, 16), (1, 16))?;
714                4
715            }
716            // organization-name.
717            (Class::ContextSpecific, 3, false) => {
718                x400_implicit(&member, field, X400Repertoire::Printable, 64)?;
719                5
720            }
721            // numeric-user-identifier.
722            (Class::ContextSpecific, 4, false) => {
723                x400_implicit(&member, field, X400Repertoire::Numeric, 32)?;
724                6
725            }
726            // personal-name: a set of up to four parts, the surname required.
727            (Class::ContextSpecific, 5, true) => {
728                read_personal_name(&inside, &member, field, X400Repertoire::Printable)?;
729                7
730            }
731            // organizational-unit-names: one to four unit names, each a
732            // PrintableString alone, unlike the explicit numeric-or-printable
733            // choices elsewhere in the address.
734            (Class::ContextSpecific, 6, true) => {
735                let mut units = inside.enter(&member).field(field)?;
736                let mut count = 0usize;
737                while let Some(unit) = units.next_element().field(field)? {
738                    x400_printable(&unit, field, (1, 32))?;
739                    count += 1;
740                }
741                if !(1..=4).contains(&count) {
742                    return Err(bad(ErrorCode::MalformedValue));
743                }
744                8
745            }
746            _ => {
747                return Err(bad(ErrorCode::Encoding(
748                    structured_asn1::ErrorCode::UnexpectedTag,
749                )));
750            }
751        };
752        // Each member appears at most once, in the order the schema writes
753        // them; a repeat or a step backwards is a second spelling of the
754        // structure.
755        if rank <= last_rank {
756            return Err(bad(ErrorCode::UnexpectedField));
757        }
758        last_rank = rank;
759    }
760    Ok(())
761}
762
763/// The personal name of X.411: up to four parts, each at most once, the
764/// surname required. The built-in form holds printable parts; the teletex
765/// extension attribute holds the same shape in teletex strings.
766fn read_personal_name<'a>(
767    parent: &Reader<'a, '_>,
768    element: &Element<'a>,
769    field: &'static str,
770    repertoire: X400Repertoire,
771) -> Result<()> {
772    let fail = |code| Error::new(code, field, element.span().start);
773    let mut inside = parent.enter(element).field(field)?;
774    let mut seen = [false; 4];
775    let mut last: Option<usize> = None;
776    while let Some(part) = inside.next_element().field(field)? {
777        let bad = |code| Error::new(code, field, part.span().start);
778        let (number, most) = match (
779            part.tag().class(),
780            part.tag().number(),
781            part.tag().is_constructed(),
782        ) {
783            (Class::ContextSpecific, Some(0), false) => (0usize, 40),
784            (Class::ContextSpecific, Some(1), false) => (1, 16),
785            (Class::ContextSpecific, Some(2), false) => (2, 5),
786            (Class::ContextSpecific, Some(3), false) => (3, 3),
787            _ => {
788                return Err(bad(ErrorCode::Encoding(
789                    structured_asn1::ErrorCode::UnexpectedTag,
790                )));
791            }
792        };
793        if seen[number] {
794            return Err(bad(ErrorCode::UnexpectedField));
795        }
796        // A SET's distinct tags come in canonical order under DER (X.690
797        // 10.3): out of order is a second spelling of one value. BER carries
798        // no such rule.
799        if inside.encoding().is_der() && last.is_some_and(|last| number < last) {
800            return Err(bad(ErrorCode::UnexpectedField));
801        }
802        seen[number] = true;
803        last = Some(number);
804        x400_implicit(&part, field, repertoire, most)?;
805    }
806    if !seen[0] {
807        return Err(fail(ErrorCode::MissingField));
808    }
809    Ok(())
810}
811
812/// The domain-defined attributes of X.411: one to four pairs of a type and a
813/// value, each a printable string.
814fn read_domain_defined<'a>(
815    parent: &Reader<'a, '_>,
816    element: &Element<'a>,
817    field: &'static str,
818) -> Result<()> {
819    read_defined_pairs(parent, element, field, false)
820}
821
822/// One to four pairs of a type and a value: printable strings in the
823/// built-in form, teletex strings in the teletex extension attribute.
824fn read_defined_pairs<'a>(
825    parent: &Reader<'a, '_>,
826    element: &Element<'a>,
827    field: &'static str,
828    teletex: bool,
829) -> Result<()> {
830    let fail = |code| Error::new(code, field, element.span().start);
831    if element.tag() != Tag::constructed(universal::SEQUENCE) {
832        return Err(fail(ErrorCode::Encoding(
833            structured_asn1::ErrorCode::UnexpectedTag,
834        )));
835    }
836    let held_string = |element: &Element<'_>, size: (usize, usize)| {
837        if teletex {
838            x400_teletex(element, field, size)
839        } else {
840            // The built-in pair holds PrintableString in both members; the
841            // numeric encoding belongs to other positions of the address.
842            x400_printable(element, field, size)
843        }
844    };
845    let mut inside = parent.enter(element).field(field)?;
846    let mut count = 0usize;
847    while let Some(pair) = inside.next_element().field(field)? {
848        let bad = |code| Error::new(code, field, pair.span().start);
849        if pair.tag() != Tag::constructed(universal::SEQUENCE) {
850            return Err(bad(ErrorCode::Encoding(
851                structured_asn1::ErrorCode::UnexpectedTag,
852            )));
853        }
854        let mut two = inside.enter(&pair).field(field)?;
855        let kind = two
856            .next_element()
857            .field(field)?
858            .ok_or_else(|| bad(ErrorCode::MissingField))?;
859        held_string(&kind, (1, 8))?;
860        let value = two
861            .next_element()
862            .field(field)?
863            .ok_or_else(|| bad(ErrorCode::MissingField))?;
864        held_string(&value, (1, 128))?;
865        if !two.is_empty() {
866            return Err(bad(ErrorCode::TrailingData));
867        }
868        count += 1;
869    }
870    if !(1..=4).contains(&count) {
871        return Err(fail(ErrorCode::MalformedValue));
872    }
873    Ok(())
874}
875
876/// The extension attributes of X.411: each an identifying number and an
877/// explicitly wrapped value that number defines, decoded by that number
878/// below.
879fn read_extension_attributes<'a>(
880    parent: &Reader<'a, '_>,
881    element: &Element<'a>,
882    field: &'static str,
883) -> Result<()> {
884    let fail = |code| Error::new(code, field, element.span().start);
885    let mut inside = parent.enter(element).field(field)?;
886    let mut count = 0usize;
887    let mut previous: Option<&[u8]> = None;
888    while let Some(entry) = inside.next_element().field(field)? {
889        let bad = |code| Error::new(code, field, entry.span().start);
890        if entry.tag() != Tag::constructed(universal::SEQUENCE) {
891            return Err(bad(ErrorCode::Encoding(
892                structured_asn1::ErrorCode::UnexpectedTag,
893            )));
894        }
895        // A SET OF, whose members DER writes in ascending encoded order
896        // (X.690 11.6): out of order is a second spelling of one value. BER
897        // carries no such rule. The comparison walks up to the longer
898        // encoding before either member is decoded, so that length is
899        // charged to the shared work budget first.
900        if inside.encoding().is_der()
901            && let Some(previous) = previous
902        {
903            inside
904                .charge(previous.len().max(entry.as_bytes().len()) as u64)
905                .field(field)?;
906            if entry.as_bytes() < previous {
907                return Err(bad(ErrorCode::UnexpectedField));
908            }
909        }
910        previous = Some(entry.as_bytes());
911        let mut two = inside.enter(&entry).field(field)?;
912        let kind = two
913            .next_element()
914            .field(field)?
915            .ok_or_else(|| bad(ErrorCode::MissingField))?;
916        // The number wears its position's tag; the decoding helper is
917        // tag-agnostic, which is exactly right for an implicit field, but the
918        // position is required first. The schema bounds it to 0..=256.
919        if extension::context_of(kind.tag()) != Some((0, false)) {
920            return Err(bad(ErrorCode::Encoding(
921                structured_asn1::ErrorCode::UnexpectedTag,
922            )));
923        }
924        let number = two.integer(&kind).field(field)?;
925        if number.as_u32().is_none_or(|number| number > 256) {
926            return Err(bad(ErrorCode::MalformedValue));
927        }
928        let value = two
929            .next_element()
930            .field(field)?
931            .ok_or_else(|| bad(ErrorCode::MissingField))?;
932        // The value is wrapped explicitly, because what it is depends on the
933        // number before it. It holds exactly one value, kept encoded.
934        if extension::context_of(value.tag()) != Some((1, true)) {
935            return Err(bad(ErrorCode::Encoding(
936                structured_asn1::ErrorCode::UnexpectedTag,
937            )));
938        }
939        let mut held = two.enter(&value).field(field)?;
940        let Some(declared) = held.next_element().field(field)? else {
941            return Err(bad(ErrorCode::MissingField));
942        };
943        // The number declares the value's syntax, so the value is decoded by
944        // it: an attribute wearing a number whose syntax it does not have was
945        // never decoded, and a critical name built on one must not read as
946        // parsed.
947        let number = number
948            .as_u32()
949            .expect("bounded to 0..=256 above, which fits");
950        read_extension_value(&held, &declared, number, field)?;
951        if !held.is_empty() || !two.is_empty() {
952            return Err(bad(ErrorCode::TrailingData));
953        }
954        count += 1;
955        // ub-extension-attributes bounds the set at 256 members (RFC 5280
956        // appendix A); a 257th puts the address outside the schema.
957        if count > 256 {
958            return Err(bad(ErrorCode::MalformedValue));
959        }
960    }
961    if count == 0 {
962        return Err(fail(ErrorCode::EmptySequence));
963    }
964    Ok(())
965}
966
967/// The value an extension attribute declares, decoded by the number before
968/// it: the syntaxes RFC 5280 appendix A gives the numbers it defines, with
969/// that module's size bounds. A number the appendix does not define is a
970/// value this crate cannot decode, and a name built on one must not read as
971/// parsed.
972fn read_extension_value<'a>(
973    parent: &Reader<'a, '_>,
974    value: &Element<'a>,
975    number: u32,
976    field: &'static str,
977) -> Result<()> {
978    let fail = |code| Error::new(code, field, value.span().start);
979    match number {
980        // common-name: PrintableString alone, unlike the explicit
981        // numeric-or-printable choices elsewhere in the address.
982        1 => x400_printable(value, field, (1, 64)),
983        // teletex-common-name and teletex-organization-name.
984        2 | 3 => x400_teletex(value, field, (1, 64)),
985        // teletex-personal-name: the personal-name shape in teletex strings.
986        4 => {
987            if value.tag() != Tag::constructed(universal::SET) {
988                return Err(fail(ErrorCode::Encoding(
989                    structured_asn1::ErrorCode::UnexpectedTag,
990                )));
991            }
992            read_personal_name(parent, value, field, X400Repertoire::Teletex)
993        }
994        // teletex-organizational-unit-names: one to four unit names.
995        5 => {
996            if value.tag() != Tag::constructed(universal::SEQUENCE) {
997                return Err(fail(ErrorCode::Encoding(
998                    structured_asn1::ErrorCode::UnexpectedTag,
999                )));
1000            }
1001            let mut units = parent.enter(value).field(field)?;
1002            let mut count = 0usize;
1003            while let Some(unit) = units.next_element().field(field)? {
1004                x400_teletex(&unit, field, (1, 32))?;
1005                count += 1;
1006            }
1007            if !(1..=4).contains(&count) {
1008                return Err(fail(ErrorCode::MalformedValue));
1009            }
1010            Ok(())
1011        }
1012        // teletex-domain-defined-attributes: the pairs again, in teletex.
1013        6 => read_defined_pairs(parent, value, field, true),
1014        // pds-name: PrintableString alone.
1015        7 => x400_printable(value, field, (1, 16)),
1016        // physical-delivery-country-name: exactly a three-digit or a
1017        // two-letter code, the choice bare in this position.
1018        8 => x400_bare_country(value, field),
1019        // postal-code.
1020        9 => x400_bare_choice(value, field, (1, 16), (1, 16)),
1021        // The postal-delivery parameters, one shape under many numbers: an
1022        // optional printable and an optional teletex string.
1023        10..=15 | 17..=21 => read_pds_parameter(parent, value, field, 30),
1024        // unformatted-postal-address.
1025        16 => read_unformatted_address(parent, value, field),
1026        // extended-network-address.
1027        22 => read_extended_network_address(parent, value, field),
1028        // terminal-type: an integer of the option table.
1029        23 => {
1030            if value.tag() != Tag::primitive(universal::INTEGER) {
1031                return Err(fail(ErrorCode::Encoding(
1032                    structured_asn1::ErrorCode::UnexpectedTag,
1033                )));
1034            }
1035            let option = parent.integer(value).field(field)?;
1036            if option.as_u32().is_none_or(|option| option > 256) {
1037                return Err(fail(ErrorCode::MalformedValue));
1038            }
1039            Ok(())
1040        }
1041        _ => Err(fail(ErrorCode::MalformedValue)),
1042    }
1043}
1044
1045/// Requires a bare two-way choice: the string stands in the choice's own
1046/// position, so its universal tag says which side it is.
1047fn x400_bare_choice(
1048    value: &Element<'_>,
1049    field: &'static str,
1050    numeric: (usize, usize),
1051    printable: (usize, usize),
1052) -> Result<()> {
1053    let fail = |code| Error::new(code, field, value.span().start);
1054    let size = match (
1055        value.tag().class(),
1056        value.tag().number(),
1057        value.tag().is_constructed(),
1058    ) {
1059        (Class::Universal, Some(X400_NUMERIC_STRING), false) => numeric,
1060        (Class::Universal, Some(universal::PRINTABLE_STRING), false) => printable,
1061        _ => {
1062            return Err(fail(ErrorCode::Encoding(
1063                structured_asn1::ErrorCode::UnexpectedTag,
1064            )));
1065        }
1066    };
1067    x400_string(value, field, size)
1068}
1069
1070/// The postal-delivery parameter of X.411: an optional printable and an
1071/// optional teletex string, each at most once, each within one size.
1072fn read_pds_parameter<'a>(
1073    parent: &Reader<'a, '_>,
1074    value: &Element<'a>,
1075    field: &'static str,
1076    most: usize,
1077) -> Result<()> {
1078    let fail = |code| Error::new(code, field, value.span().start);
1079    if value.tag() != Tag::constructed(universal::SET) {
1080        return Err(fail(ErrorCode::Encoding(
1081            structured_asn1::ErrorCode::UnexpectedTag,
1082        )));
1083    }
1084    let mut inside = parent.enter(value).field(field)?;
1085    let mut seen_printable = false;
1086    let mut seen_teletex = false;
1087    while let Some(member) = inside.next_element().field(field)? {
1088        let bad = |code| Error::new(code, field, member.span().start);
1089        if member.tag() == Tag::primitive(universal::PRINTABLE_STRING) {
1090            // A SET's distinct tags come in canonical order under DER
1091            // (X.690 10.3): the printable tag sorts before the teletex one,
1092            // so arriving after it is a second spelling of one value. BER
1093            // carries no such rule.
1094            if seen_printable || (seen_teletex && inside.encoding().is_der()) {
1095                return Err(bad(ErrorCode::UnexpectedField));
1096            }
1097            seen_printable = true;
1098            x400_string(&member, field, (1, most))?;
1099        } else if member.tag() == Tag::primitive(20) {
1100            if seen_teletex {
1101                return Err(bad(ErrorCode::UnexpectedField));
1102            }
1103            seen_teletex = true;
1104            x400_teletex(&member, field, (1, most))?;
1105        } else {
1106            return Err(bad(ErrorCode::Encoding(
1107                structured_asn1::ErrorCode::UnexpectedTag,
1108            )));
1109        }
1110    }
1111    Ok(())
1112}
1113
1114/// The unformatted postal address of X.411: up to six printable lines and
1115/// an optional teletex rendering, each part at most once.
1116fn read_unformatted_address<'a>(
1117    parent: &Reader<'a, '_>,
1118    value: &Element<'a>,
1119    field: &'static str,
1120) -> Result<()> {
1121    let fail = |code| Error::new(code, field, value.span().start);
1122    if value.tag() != Tag::constructed(universal::SET) {
1123        return Err(fail(ErrorCode::Encoding(
1124            structured_asn1::ErrorCode::UnexpectedTag,
1125        )));
1126    }
1127    let mut inside = parent.enter(value).field(field)?;
1128    let mut seen_lines = false;
1129    let mut seen_teletex = false;
1130    while let Some(member) = inside.next_element().field(field)? {
1131        let bad = |code| Error::new(code, field, member.span().start);
1132        if member.tag() == Tag::constructed(universal::SEQUENCE) {
1133            // A SET's distinct tags come in canonical order under DER
1134            // (X.690 10.3): the SEQUENCE tag sorts before the teletex one,
1135            // so arriving after it is a second spelling of one value. BER
1136            // carries no such rule.
1137            if seen_lines || (seen_teletex && inside.encoding().is_der()) {
1138                return Err(bad(ErrorCode::UnexpectedField));
1139            }
1140            seen_lines = true;
1141            let mut lines = inside.enter(&member).field(field)?;
1142            let mut count = 0usize;
1143            while let Some(line) = lines.next_element().field(field)? {
1144                // Every line of the printable address is a PrintableString;
1145                // the numeric alternative belongs to other positions.
1146                x400_printable(&line, field, (1, 30))?;
1147                count += 1;
1148            }
1149            if !(1..=6).contains(&count) {
1150                return Err(bad(ErrorCode::MalformedValue));
1151            }
1152        } else if member.tag() == Tag::primitive(20) {
1153            if seen_teletex {
1154                return Err(bad(ErrorCode::UnexpectedField));
1155            }
1156            seen_teletex = true;
1157            x400_teletex(&member, field, (1, 180))?;
1158        } else {
1159            return Err(bad(ErrorCode::Encoding(
1160                structured_asn1::ErrorCode::UnexpectedTag,
1161            )));
1162        }
1163    }
1164    Ok(())
1165}
1166
1167/// The E.163/E.164 alternative: a number and an optional sub-address.
1168fn read_e163_address<'a>(
1169    parent: &Reader<'a, '_>,
1170    value: &Element<'a>,
1171    field: &'static str,
1172) -> Result<()> {
1173    let fail = |code| Error::new(code, field, value.span().start);
1174    let mut inside = parent.enter(value).field(field)?;
1175    let number = inside
1176        .next_element()
1177        .field(field)?
1178        .ok_or_else(|| fail(ErrorCode::MissingField))?;
1179    if extension::context_of(number.tag()) != Some((0, false)) {
1180        return Err(fail(ErrorCode::Encoding(
1181            structured_asn1::ErrorCode::UnexpectedTag,
1182        )));
1183    }
1184    x400_implicit(&number, field, X400Repertoire::Numeric, 15)?;
1185    if let Some(sub) = inside.next_element().field(field)? {
1186        if extension::context_of(sub.tag()) != Some((1, false)) {
1187            return Err(fail(ErrorCode::Encoding(
1188                structured_asn1::ErrorCode::UnexpectedTag,
1189            )));
1190        }
1191        x400_implicit(&sub, field, X400Repertoire::Numeric, 40)?;
1192    }
1193    if !inside.is_empty() {
1194        return Err(fail(ErrorCode::TrailingData));
1195    }
1196    Ok(())
1197}
1198
1199/// The extended network address of X.411: an E.163/E.164 number with an
1200/// optional sub-address, or a presentation address of wrapped selectors and
1201/// the mandatory network addresses.
1202fn read_extended_network_address<'a>(
1203    parent: &Reader<'a, '_>,
1204    value: &Element<'a>,
1205    field: &'static str,
1206) -> Result<()> {
1207    let fail = |code| Error::new(code, field, value.span().start);
1208    if value.tag() == Tag::constructed(universal::SEQUENCE) {
1209        read_e163_address(parent, value, field)
1210    } else if extension::context_of(value.tag()) == Some((0, true)) {
1211        // psap-address: three optional wrapped selectors, then the mandatory
1212        // set of network addresses, each member at most once in order.
1213        let mut inside = parent.enter(value).field(field)?;
1214        let mut last: i8 = -1;
1215        let mut saw_network = false;
1216        while let Some(member) = inside.next_element().field(field)? {
1217            let bad = |code| Error::new(code, field, member.span().start);
1218            let Some((number, true)) = extension::context_of(member.tag()) else {
1219                return Err(bad(ErrorCode::Encoding(
1220                    structured_asn1::ErrorCode::UnexpectedTag,
1221                )));
1222            };
1223            let rank = i8::try_from(number).map_err(|_| {
1224                bad(ErrorCode::Encoding(
1225                    structured_asn1::ErrorCode::UnexpectedTag,
1226                ))
1227            })?;
1228            if rank > 3 {
1229                return Err(bad(ErrorCode::Encoding(
1230                    structured_asn1::ErrorCode::UnexpectedTag,
1231                )));
1232            }
1233            if rank <= last {
1234                return Err(bad(ErrorCode::UnexpectedField));
1235            }
1236            last = rank;
1237            let mut held = inside.enter(&member).field(field)?;
1238            if rank == 3 {
1239                // The network addresses: at least one octet string.
1240                let addresses = held
1241                    .next_element()
1242                    .field(field)?
1243                    .ok_or_else(|| bad(ErrorCode::MissingField))?;
1244                if addresses.tag() != Tag::constructed(universal::SET) {
1245                    return Err(bad(ErrorCode::Encoding(
1246                        structured_asn1::ErrorCode::UnexpectedTag,
1247                    )));
1248                }
1249                let mut each = held.enter(&addresses).field(field)?;
1250                let mut count = 0usize;
1251                let mut previous: Option<&[u8]> = None;
1252                while let Some(address) = each.next_element().field(field)? {
1253                    if address.tag() != Tag::primitive(universal::OCTET_STRING) {
1254                        return Err(bad(ErrorCode::Encoding(
1255                            structured_asn1::ErrorCode::UnexpectedTag,
1256                        )));
1257                    }
1258                    // A SET OF, whose members DER writes in ascending
1259                    // encoded order (X.690 11.6): out of order is a second
1260                    // spelling of one value. BER carries no such rule. The
1261                    // comparison walks up to the longer encoding, so that
1262                    // length is charged to the shared work budget first.
1263                    if each.encoding().is_der()
1264                        && let Some(previous) = previous
1265                    {
1266                        each.charge(previous.len().max(address.as_bytes().len()) as u64)
1267                            .field(field)?;
1268                        if address.as_bytes() < previous {
1269                            return Err(bad(ErrorCode::UnexpectedField));
1270                        }
1271                    }
1272                    previous = Some(address.as_bytes());
1273                    count += 1;
1274                }
1275                if count == 0 {
1276                    return Err(bad(ErrorCode::EmptySequence));
1277                }
1278                saw_network = true;
1279            } else {
1280                // A selector: one wrapped octet string.
1281                let selector = held
1282                    .next_element()
1283                    .field(field)?
1284                    .ok_or_else(|| bad(ErrorCode::MissingField))?;
1285                if selector.tag() != Tag::primitive(universal::OCTET_STRING) {
1286                    return Err(bad(ErrorCode::Encoding(
1287                        structured_asn1::ErrorCode::UnexpectedTag,
1288                    )));
1289                }
1290            }
1291            if !held.is_empty() {
1292                return Err(bad(ErrorCode::TrailingData));
1293            }
1294        }
1295        if !saw_network {
1296            return Err(fail(ErrorCode::MissingField));
1297        }
1298        Ok(())
1299    } else {
1300        Err(fail(ErrorCode::Encoding(
1301            structured_asn1::ErrorCode::UnexpectedTag,
1302        )))
1303    }
1304}
1305
1306/// Validates an EDI party name: an optional assigner, then the mandatory
1307/// party, each an explicitly tagged wrapper holding one directory string.
1308/// The tags wrap rather than replace, because a directory string is a choice
1309/// and RFC 5280 appendix A keeps a choice's own tag.
1310fn read_edi_party<'a>(
1311    parent: &Reader<'a, '_>,
1312    element: &Element<'a>,
1313    field: &'static str,
1314) -> Result<()> {
1315    let fail = |code| Error::new(code, field, element.span().start);
1316
1317    let mut inside = parent.enter(element).field(field)?;
1318    let mut next = inside.next_element().field(field)?;
1319
1320    if let Some(assigner) = next
1321        && extension::context_of(assigner.tag()) == Some((0, true))
1322    {
1323        require_directory_string(&inside, &assigner, field)?;
1324        next = inside.next_element().field(field)?;
1325    }
1326
1327    let party = next.ok_or_else(|| fail(ErrorCode::MissingField))?;
1328    if extension::context_of(party.tag()) != Some((1, true)) {
1329        return Err(Error::new(
1330            ErrorCode::UnexpectedField,
1331            field,
1332            party.span().start,
1333        ));
1334    }
1335    require_directory_string(&inside, &party, field)?;
1336
1337    if !inside.is_empty() {
1338        return Err(fail(ErrorCode::TrailingData));
1339    }
1340    Ok(())
1341}
1342
1343/// Requires a wrapper to hold exactly one non-empty directory string.
1344fn require_directory_string<'a>(
1345    parent: &Reader<'a, '_>,
1346    wrapper: &Element<'a>,
1347    field: &'static str,
1348) -> Result<()> {
1349    /// The ub-name character bound both EDI fields carry (RFC 5280 appendix A).
1350    const MOST: usize = 32768;
1351    let fail = |code| Error::new(code, field, wrapper.span().start);
1352
1353    let mut inside = parent.enter(wrapper).field(field)?;
1354    let value = inside
1355        .next_element()
1356        .field(field)?
1357        .ok_or_else(|| fail(ErrorCode::MissingField))?;
1358    if !inside.is_empty() {
1359        return Err(fail(ErrorCode::TrailingData));
1360    }
1361
1362    // The directory string choice of X.520: a primitive universal string of
1363    // one of its five types, holding one to 32768 characters, the ub-name
1364    // bound both EDI fields carry (RFC 5280 appendix A). The tag alone is not
1365    // the type, so the value must also decode under the type chosen.
1366    let tag = value.tag();
1367    let is_directory_string = tag.class() == Class::Universal
1368        && !tag.is_constructed()
1369        && matches!(tag.number(), Some(12 | 19 | 20 | 28 | 30));
1370    // The ceiling is checked on the encoded bytes before any text is
1371    // materialized: each encoding spends a known number of bytes per
1372    // character, so contents past the per-encoding multiple of the bound
1373    // can never fit it and are refused without allocating a copy.
1374    let ceiling = match tag.number() {
1375        Some(12 | 28) => MOST.saturating_mul(4),
1376        Some(30) => MOST.saturating_mul(2),
1377        _ => MOST,
1378    };
1379    if value.contents().len() > ceiling {
1380        return Err(fail(ErrorCode::MalformedValue));
1381    }
1382    // The decode below scans every content byte, and the per-field ceiling
1383    // does not bound the aggregate across many fields: the scan is charged
1384    // to the shared work budget first.
1385    parent.charge(value.contents().len() as u64).field(field)?;
1386    let decodes = match tag.number() {
1387        Some(12) => {
1388            core::str::from_utf8(value.contents()).is_ok_and(|text| text.chars().count() <= MOST)
1389        }
1390        Some(19) => crate::name::is_printable(value.contents()) && value.contents().len() <= MOST,
1391        Some(28) => {
1392            crate::name::wide_char_count(value.contents(), 4).is_some_and(|count| count <= MOST)
1393        }
1394        Some(30) => {
1395            crate::name::wide_char_count(value.contents(), 2).is_some_and(|count| count <= MOST)
1396        }
1397        // TeletexString's repertoire is a negotiation this crate does not
1398        // implement: the bytes being there and inside the bound is what is
1399        // checkable.
1400        _ => value.contents().len() <= MOST,
1401    };
1402    if !is_directory_string || value.contents().is_empty() || !decodes {
1403        return Err(fail(ErrorCode::MalformedValue));
1404    }
1405    Ok(())
1406}
1407
1408fn read_other<'a>(
1409    parent: &Reader<'a, '_>,
1410    element: &Element<'a>,
1411    field: &'static str,
1412) -> Result<GeneralName<'a>> {
1413    let fail = |code| Error::new(code, field, element.span().start);
1414
1415    let mut inside = parent.enter(element).field(field)?;
1416    let type_id = inside.read_oid().field(field)?;
1417    let wrapper = inside
1418        .next_element()
1419        .field(field)?
1420        .ok_or_else(|| fail(ErrorCode::MissingField))?;
1421
1422    // The value is tagged explicitly, because what it is depends on the
1423    // identifier before it and a tag cannot replace one it does not know.
1424    if wrapper.tag() != Tag::context(true, 0) {
1425        return Err(fail(ErrorCode::Encoding(
1426            structured_asn1::ErrorCode::UnexpectedTag,
1427        )));
1428    }
1429    if !inside.is_empty() {
1430        return Err(fail(ErrorCode::TrailingData));
1431    }
1432
1433    // The wrapper holds the one value the identifier describes. Empty it
1434    // names nothing, and with a second element there is no answer to which
1435    // value the name is.
1436    let mut held = inside.enter(&wrapper).field(field)?;
1437    let value = held
1438        .next_element()
1439        .field(field)?
1440        .ok_or_else(|| fail(ErrorCode::MissingField))?;
1441    if !held.is_empty() {
1442        return Err(fail(ErrorCode::TrailingData));
1443    }
1444
1445    // The outer tag is not the whole value: a container this crate does not
1446    // interpret may still hold children that are not DER, an integer written
1447    // a byte longer than it needs or a marker belonging to the indefinite
1448    // lengths DER does not have. A recognizer clears a critical extension on
1449    // the strength of this value, so every element inside it is held to the
1450    // encoding's rules before it is preserved.
1451    crate::name::walk_der(&held, &value, field, crate::name::Strings::Checked)?;
1452
1453    Ok(GeneralName::Other {
1454        type_id,
1455        value: value.as_bytes(),
1456    })
1457}
1458
1459/// Reads the ASCII a schema calls an `IA5String`.
1460/// Whether text is a domain in the preferred name syntax: dot-separated
1461/// labels of letters, digits and inner hyphens, each of one to sixty-three
1462/// characters (RFC 1034 3.5, its leading letter relaxed to a letter or digit
1463/// by RFC 1123 2.1).
1464pub(crate) fn is_domain(text: &str) -> bool {
1465    // The whole name is bounded too: 255 octets in the wire form, which is
1466    // 253 characters of the presentation form (RFC 1035 2.3.4). The
1467    // highest-level label stays alphabetic-bearing (RFC 1123 2.1), which is
1468    // what separates a host name from a dotted-decimal address: an address
1469    // wearing this field would ride under the wrong name-constraint form.
1470    text.len() <= 253
1471        && text.split('.').all(|label| {
1472            (1..=63).contains(&label.len())
1473                && label
1474                    .bytes()
1475                    .all(|byte| byte.is_ascii_alphanumeric() || byte == b'-')
1476                && !label.starts_with('-')
1477                && !label.ends_with('-')
1478        })
1479        && text
1480            .rsplit('.')
1481            .next()
1482            .is_some_and(|top| top.bytes().any(|byte| byte.is_ascii_alphabetic()))
1483}
1484
1485/// Whether text is a mailbox, the form RFC 5280 4.2.1.6 points at: "the
1486/// format of an rfc822Name is a Mailbox as defined in RFC 5321 section
1487/// 4.1.2".
1488///
1489/// The dependency's ASCII SMTP predicate shares its strict envelope grammar,
1490/// including quoted local parts, single-label domains and §4.1.3 literals.
1491/// It performs no allocation or normalization. Both callers admit the bounded
1492/// linear scan first: name decoding through `text()`, and legacy subject
1493/// mailbox formation through the path's current execution account. Header
1494/// comments are outside this grammar. Literal tag registration/deliverability
1495/// is not established by this syntax check.
1496pub(crate) fn is_mailbox(text: &str) -> bool {
1497    structured_email_address::is_smtp_mailbox(text)
1498}
1499
1500/// Whether text is an absolute URI: a scheme of a letter then letters,
1501/// digits, plus, minus and dots (RFC 3986 3.1), a colon, and a non-empty
1502/// remainder inside the URI alphabet, a percent sign spelling a byte only
1503/// with two hexadecimal digits behind it (RFC 3986 2.1). The fragment
1504/// delimiter is not in the alphabet: absolute-URI carries no fragment
1505/// (RFC 3986 4.3), which belongs to URI references. What follows the scheme
1506/// is the scheme's to define, and is never fetched.
1507pub(crate) fn is_absolute_uri(text: &str) -> bool {
1508    absolute_uri::<true>(text)
1509}
1510
1511fn absolute_uri<const CERTIFICATE_NAME: bool>(text: &str) -> bool {
1512    let Some((scheme, rest)) = text.split_once(':') else {
1513        return false;
1514    };
1515    let mut bytes = scheme.bytes();
1516    if !(bytes.next().is_some_and(|byte| byte.is_ascii_alphabetic())
1517        && bytes.all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'+' | b'-' | b'.')))
1518    {
1519        return false;
1520    }
1521    // The remainder is required, which the generic grammar does not require:
1522    // RFC 3986's hier-part admits path-empty, so "urn:" parses there. The
1523    // profile is stricter, and it is the profile this reads under: a name
1524    // "MUST include both a scheme (e.g., "http" or "ftp") and a
1525    // scheme-specific-part" (RFC 5280 4.2.1.6). A scheme alone names no
1526    // place, and accepting one would report a location a report cannot
1527    // explain.
1528    if CERTIFICATE_NAME && rest.is_empty() {
1529        return false;
1530    }
1531
1532    // The authority is [userinfo "@"] host [":" port] (RFC 3986 3.2), and
1533    // it is validated structurally: the user-info delimiter appears at most
1534    // once, the brackets exist only around an IP literal in the host
1535    // (3.2.2), and the port holds digits and nothing else (3.2.3). The
1536    // character walk below covers the remaining free-form segments.
1537    let mut userinfo = "";
1538    let mut bare_host = "";
1539    let mut tail = rest;
1540    if let Some(after) = rest.strip_prefix("//") {
1541        let end = after.find(['/', '?']).unwrap_or(after.len());
1542        let authority = &after[..end];
1543        tail = &after[end..];
1544        let (before, host_port) = match authority.split_once('@') {
1545            Some((before, host_port)) => (before, host_port),
1546            None => ("", authority),
1547        };
1548        // A second raw at-sign belongs to no component: neither user-info
1549        // nor the host may carry one.
1550        if host_port.contains('@') {
1551            return false;
1552        }
1553        userinfo = before;
1554        let port = if let Some(inside) = host_port.strip_prefix('[') {
1555            let Some((literal, past)) = inside.split_once(']') else {
1556                return false;
1557            };
1558            if !is_ip_literal(literal) {
1559                return false;
1560            }
1561            // After the literal only a port may follow within the authority.
1562            if past.is_empty() {
1563                None
1564            } else {
1565                let Some(digits) = past.strip_prefix(':') else {
1566                    return false;
1567                };
1568                Some(digits)
1569            }
1570        } else {
1571            // A colon in a bare host is the port delimiter: reg-names have
1572            // no colon of their own.
1573            let port = if let Some((host, port)) = host_port.split_once(':') {
1574                bare_host = host;
1575                Some(port)
1576            } else {
1577                bare_host = host_port;
1578                None
1579            };
1580            // "URIs that include an authority (RFC 3986 3.2) MUST include a
1581            // fully qualified domain name or IP address as the host"
1582            // (RFC 5280 4.2.1.6). The bracketed form is an address already;
1583            // a bare host is a name or a dotted-quad, and nothing else, so
1584            // an empty authority and a word that is neither are refused
1585            // here rather than passed on as a location.
1586            //
1587            // A single closing period is the absolute spelling of a name,
1588            // which ends at the root (RFC 1034 3.1). It qualifies a name
1589            // rather than leaving it short of qualified, so it is read here
1590            // and kept in the location, which is preserved as written.
1591            let named = bare_host.strip_suffix('.').unwrap_or(bare_host);
1592            if CERTIFICATE_NAME && !is_domain(named) && bare_host.parse::<Ipv4Addr>().is_err() {
1593                return false;
1594            }
1595            port
1596        };
1597        // The port is digits alone, the grammar's *DIGIT; empty included.
1598        if port.is_some_and(|digits| !digits.bytes().all(|byte| byte.is_ascii_digit())) {
1599            return false;
1600        }
1601    }
1602
1603    uri_alphabet(userinfo) && uri_alphabet(bare_host) && uri_alphabet(tail)
1604}
1605
1606/// Whether a segment holds only what a URI is written from: the unreserved
1607/// characters, the sub-delimiters, the two component delimiters that appear
1608/// inside a path or a query, and a percent sign only where two hexadecimal
1609/// digits follow it (RFC 3986 2).
1610fn uri_alphabet(segment: &str) -> bool {
1611    let mut bytes = segment.bytes();
1612    while let Some(byte) = bytes.next() {
1613        let permitted = byte.is_ascii_alphanumeric()
1614            || matches!(
1615                byte,
1616                b'-' | b'.'
1617                    | b'_'
1618                    | b'~'
1619                    | b':'
1620                    | b'/'
1621                    | b'?'
1622                    | b'@'
1623                    | b'!'
1624                    | b'$'
1625                    | b'&'
1626                    | b'\''
1627                    | b'('
1628                    | b')'
1629                    | b'*'
1630                    | b'+'
1631                    | b','
1632                    | b';'
1633                    | b'='
1634            );
1635        if byte == b'%' {
1636            let mut pair = [bytes.next(), bytes.next()].into_iter();
1637            if !pair.all(|digit| digit.is_some_and(|digit| digit.is_ascii_hexdigit())) {
1638                return false;
1639            }
1640        } else if !permitted {
1641            return false;
1642        }
1643    }
1644    true
1645}
1646
1647/// Whether text is a URI, the production that carries an optional fragment
1648/// after the rest of it (RFC 3986 3).
1649///
1650/// Certificate names (RFC5280 §4.2.1.6) additionally require their
1651/// scheme-specific part and host shape. A fragment does not make a
1652/// scheme-bearing URI relative. CPS pointers use the generic branch below.
1653pub(crate) fn is_uri(text: &str) -> bool {
1654    uri::<true>(text)
1655}
1656
1657/// `CPSuri` uses RFC3986 URI grammar (RFC5280 §4.2.1.4), independently of
1658/// §4.2.1.6's certificate-name host and scheme-specific-part requirements.
1659/// <https://www.rfc-editor.org/rfc/rfc5280.html#section-4.2.1.4>
1660pub(crate) fn is_cps_uri(text: &str) -> bool {
1661    uri::<false>(text)
1662}
1663
1664fn location_uri(
1665    parent: &Reader<'_, '_>,
1666    element: &Element<'_>,
1667    text: &str,
1668    field: &'static str,
1669) -> Result<bool> {
1670    // Admit generic syntax plus scheme/authority/host scans through both the
1671    // producing Reader and aggregate domain before any of those scans.
1672    let work = u64::try_from(text.len())
1673        .ok()
1674        .and_then(|n| n.checked_mul(32))
1675        .and_then(|n| n.checked_add(256))
1676        .ok_or_else(|| {
1677            Error::resource(
1678                structured_asn1::memory::MemoryError::WorkExceeded,
1679                field,
1680                element.span().start,
1681            )
1682        })?;
1683    parent.charge(work).field(field)?;
1684    if !is_cps_uri(text) {
1685        return Ok(false);
1686    }
1687    if text
1688        .split_once(':')
1689        .is_some_and(|(scheme, _)| scheme.eq_ignore_ascii_case("ldap"))
1690    {
1691        return ldap::valid(parent, element, text, field);
1692    }
1693    Ok(known_location_authority_fits(text))
1694}
1695
1696fn known_location_authority_fits(text: &str) -> bool {
1697    let (scheme, rest) = text.split_once(':').expect("URI syntax checked");
1698    let ftp = scheme.eq_ignore_ascii_case("ftp");
1699    if !ftp && !scheme.eq_ignore_ascii_case("http") && !scheme.eq_ignore_ascii_case("https") {
1700        // RFC5280 §4.2.1.13 defines CRL locations separately from SAN §4.2.1.6.
1701        // Unknown schemes retain the validated RFC3986 §3 URI grammar, whose
1702        // hier-part admits path-empty (Appendix D.1 explicitly permits just
1703        // "scheme:"). We cannot impose another scheme's retrieval semantics.
1704        // <https://www.rfc-editor.org/rfc/rfc3986#section-3>
1705        return true;
1706    }
1707    // HTTP(S) requires //authority and forbids empty hosts; FTP uses the
1708    // mandatory Internet host syntax. LDAP's empty host is a separate contract.
1709    // RFC9110 §§4.2.1/4.2.2; RFC1738 §§3.1/3.2.
1710    // <https://www.rfc-editor.org/rfc/rfc9110#section-4.2.1>
1711    // <https://www.rfc-editor.org/rfc/rfc1738#section-3.1>
1712    let Some(after) = rest.strip_prefix("//") else {
1713        return false;
1714    };
1715    let end = after.find(['/', '?', '#']).unwrap_or(after.len());
1716    let authority = &after[..end];
1717    let host_port = authority
1718        .rsplit_once('@')
1719        .map_or(authority, |(_, host)| host);
1720    if host_port.starts_with('[') {
1721        // The shared grammar already checked the literal and following port.
1722        return true;
1723    }
1724    let host = host_port
1725        .split_once(':')
1726        .map_or(host_port, |(host, _)| host);
1727    if host.is_empty() {
1728        return false;
1729    }
1730    !ftp || is_domain(host.strip_suffix('.').unwrap_or(host)) || host.parse::<Ipv4Addr>().is_ok()
1731}
1732
1733fn uri<const CERTIFICATE_NAME: bool>(text: &str) -> bool {
1734    match text.split_once('#') {
1735        // The fragment is written from the same alphabet, and the delimiter
1736        // appears once: a second one is inside no production.
1737        Some((rest, fragment)) => absolute_uri::<CERTIFICATE_NAME>(rest) && uri_alphabet(fragment),
1738        None => absolute_uri::<CERTIFICATE_NAME>(text),
1739    }
1740}
1741
1742/// Whether mask bytes are leading one bits and nothing after them.
1743/// Whether bracketed host contents are one of the two IP-literal forms of
1744/// RFC 3986 3.2.2: an IPv6 address, or the versioned future form. A word
1745/// that merely fits the bracket alphabet is neither and names no host.
1746fn is_ip_literal(text: &str) -> bool {
1747    if let Some(rest) = text.strip_prefix(['v', 'V']) {
1748        // IPvFuture: "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ).
1749        let Some((version, suffix)) = rest.split_once('.') else {
1750            return false;
1751        };
1752        return !version.is_empty()
1753            && version.bytes().all(|byte| byte.is_ascii_hexdigit())
1754            && !suffix.is_empty()
1755            && suffix.bytes().all(|byte| {
1756                byte.is_ascii_alphanumeric()
1757                    || matches!(
1758                        byte,
1759                        b'-' | b'.'
1760                            | b'_'
1761                            | b'~'
1762                            | b':'
1763                            | b'!'
1764                            | b'$'
1765                            | b'&'
1766                            | b'\''
1767                            | b'('
1768                            | b')'
1769                            | b'*'
1770                            | b'+'
1771                            | b','
1772                            | b';'
1773                            | b'='
1774                    )
1775            });
1776    }
1777    text.parse::<Ipv6Addr>().is_ok()
1778}
1779
1780fn is_contiguous_mask(mask: &[u8]) -> bool {
1781    let mut still_ones = true;
1782    for &byte in mask {
1783        if still_ones {
1784            if byte == 0xff {
1785                continue;
1786            }
1787            // The one partial byte: leading ones, then zeros to its end.
1788            let ones = byte.leading_ones();
1789            let expected = if ones == 0 { 0 } else { 0xffu8 << (8 - ones) };
1790            if byte != expected {
1791                return false;
1792            }
1793            still_ones = false;
1794        } else if byte != 0 {
1795            return false;
1796        }
1797    }
1798    true
1799}
1800
1801fn text<'a>(
1802    parent: &Reader<'a, '_>,
1803    contents: &'a [u8],
1804    field: &'static str,
1805    element: &Element<'a>,
1806) -> Result<&'a str> {
1807    // The scans behind this text, ASCII, grammar and bounds alike, walk
1808    // every content byte: they are charged to the shared work budget, so a
1809    // caller bounding work sees them on the same meter the reads use.
1810    parent.charge(contents.len() as u64).field(field)?;
1811    // An empty address, domain or location satisfies the shape while naming
1812    // nothing, and a critical alternative-name list carrying only one of
1813    // these would count as having supplied an identity.
1814    if contents.is_empty() {
1815        return Err(Error::new(
1816            ErrorCode::MalformedValue,
1817            field,
1818            element.span().start,
1819        ));
1820    }
1821    if !contents.is_ascii() {
1822        // The schema's alphabet is ASCII, and a byte outside it is a name
1823        // written in an encoding the field does not have. Guessing at one
1824        // would put a domain or an address in the report that the certificate
1825        // does not carry.
1826        return Err(Error::new(
1827            ErrorCode::MalformedValue,
1828            field,
1829            element.span().start,
1830        ));
1831    }
1832    core::str::from_utf8(contents)
1833        .map_err(|_| Error::new(ErrorCode::MalformedValue, field, element.span().start))
1834}
1835
1836#[cfg(test)]
1837mod tests;