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, 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: &Memory,
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>(
198    extension: &Extension<'a>,
199    execution: &Memory,
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>(parent: &Reader<'a>, element: &Element<'a>, field: &'static str) -> Result<()> {
441    let fail = |code| Error::new(code, field, element.span().start);
442
443    let mut inside = parent.enter(element).field(field)?;
444    let standard = inside
445        .next_element()
446        .field(field)?
447        .ok_or_else(|| fail(ErrorCode::MissingField))?;
448    if standard.tag() != Tag::constructed(universal::SEQUENCE) {
449        return Err(fail(ErrorCode::Encoding(
450            structured_asn1::ErrorCode::UnexpectedTag,
451        )));
452    }
453    read_standard_attributes(&inside, &standard, field)?;
454
455    // The two optional members, in order: a SEQUENCE OF, then a SET OF.
456    let mut next = inside.next_element().field(field)?;
457    if let Some(domain_defined) = next
458        && domain_defined.tag() == Tag::constructed(universal::SEQUENCE)
459    {
460        read_domain_defined(&inside, &domain_defined, field)?;
461        next = inside.next_element().field(field)?;
462    }
463    if let Some(extensions) = next {
464        if extensions.tag() != Tag::constructed(universal::SET) {
465            return Err(fail(ErrorCode::Encoding(
466                structured_asn1::ErrorCode::UnexpectedTag,
467            )));
468        }
469        read_extension_attributes(&inside, &extensions, field)?;
470        if !inside.is_empty() {
471            return Err(fail(ErrorCode::TrailingData));
472        }
473    }
474    Ok(())
475}
476
477/// The tag numbers X.411 string types carry, named here since nothing below
478/// the schema needs them.
479const X400_NUMERIC_STRING: u32 = 18;
480
481/// Requires a universal string member to be a `NumericString` or a
482/// `PrintableString`, inside its repertoire and the size the schema gives
483/// that member (X.411 via RFC 5280 appendix A, whose bounds these are).
484fn x400_string(element: &Element<'_>, field: &'static str, size: (usize, usize)) -> Result<()> {
485    let fail = |code| Error::new(code, field, element.span().start);
486    let contents = element.contents();
487    // The primitive form only: DER has no constructed spelling of these
488    // strings, and the class and number alone would let one through.
489    let fits = match (
490        element.tag().class(),
491        element.tag().number(),
492        element.tag().is_constructed(),
493    ) {
494        (Class::Universal, Some(X400_NUMERIC_STRING), false) => crate::name::is_numeric(contents),
495        (Class::Universal, Some(universal::PRINTABLE_STRING), false) => {
496            crate::name::is_printable(contents)
497        }
498        _ => {
499            return Err(fail(ErrorCode::Encoding(
500                structured_asn1::ErrorCode::UnexpectedTag,
501            )));
502        }
503    };
504    if !fits || contents.len() < size.0 || contents.len() > size.1 {
505        return Err(fail(ErrorCode::MalformedValue));
506    }
507    Ok(())
508}
509
510/// Requires a primitive `PrintableString` of the given size, refusing the
511/// numeric alternative the wider choice permits.
512fn x400_printable(element: &Element<'_>, field: &'static str, size: (usize, usize)) -> Result<()> {
513    let fail = |code| Error::new(code, field, element.span().start);
514    let tag = element.tag();
515    if tag.class() != Class::Universal
516        || tag.number() != Some(universal::PRINTABLE_STRING)
517        || tag.is_constructed()
518    {
519        return Err(fail(ErrorCode::Encoding(
520            structured_asn1::ErrorCode::UnexpectedTag,
521        )));
522    }
523    let contents = element.contents();
524    if !crate::name::is_printable(contents) || contents.len() < size.0 || contents.len() > size.1 {
525        return Err(fail(ErrorCode::MalformedValue));
526    }
527    Ok(())
528}
529
530/// Which repertoire an X.400 string member is held to. Teletex is a
531/// negotiation this crate does not implement, so only its presence and size
532/// are checkable.
533#[derive(Clone, Copy)]
534enum X400Repertoire {
535    Numeric,
536    Printable,
537    Teletex,
538}
539
540/// Requires an implicitly tagged member's contents to be one repertoire
541/// within one size, the tag having replaced the string type.
542fn x400_implicit(
543    element: &Element<'_>,
544    field: &'static str,
545    repertoire: X400Repertoire,
546    most: usize,
547) -> Result<()> {
548    let fail = |code| Error::new(code, field, element.span().start);
549    let contents = element.contents();
550    // The size is decided before the repertoire. These fields are bounded to
551    // a few dozen characters by the schema, while the element they arrive in
552    // may be as long as the parser's length limit permits: walking one to
553    // its end to learn it is digits, only to refuse it for its length, is
554    // work the caller's budget never asked for. Afterwards the walk is
555    // bounded by the schema's own ceiling.
556    if contents.is_empty() || contents.len() > most {
557        return Err(fail(ErrorCode::MalformedValue));
558    }
559    let fits = match repertoire {
560        X400Repertoire::Numeric => crate::name::is_numeric(contents),
561        X400Repertoire::Printable => crate::name::is_printable(contents),
562        X400Repertoire::Teletex => true,
563    };
564    if !fits {
565        return Err(fail(ErrorCode::MalformedValue));
566    }
567    Ok(())
568}
569
570/// Requires a universal `TeletexString` member, primitive, within one size.
571fn x400_teletex(element: &Element<'_>, field: &'static str, size: (usize, usize)) -> Result<()> {
572    let fail = |code| Error::new(code, field, element.span().start);
573    // TeletexString is tag number 20; its repertoire is a negotiation this
574    // crate does not implement, so presence and size are what is checkable.
575    if element.tag() != Tag::primitive(20) {
576        return Err(fail(ErrorCode::Encoding(
577            structured_asn1::ErrorCode::UnexpectedTag,
578        )));
579    }
580    let length = element.contents().len();
581    if length < size.0 || length > size.1 {
582        return Err(fail(ErrorCode::MalformedValue));
583    }
584    Ok(())
585}
586
587/// Requires a bare country choice: an X.121 DCC of exactly three digits, or
588/// an ISO 3166 alpha-2 code of exactly two uppercase letters. The wider
589/// repertoires of the string types name no country.
590fn x400_bare_country(value: &Element<'_>, field: &'static str) -> Result<()> {
591    let fail = |code| Error::new(code, field, value.span().start);
592    let contents = value.contents();
593    let fits = match (
594        value.tag().class(),
595        value.tag().number(),
596        value.tag().is_constructed(),
597    ) {
598        (Class::Universal, Some(X400_NUMERIC_STRING), false) => {
599            contents.len() == 3 && contents.iter().all(u8::is_ascii_digit)
600        }
601        (Class::Universal, Some(universal::PRINTABLE_STRING), false) => {
602            contents.len() == 2 && contents.iter().all(u8::is_ascii_uppercase)
603        }
604        _ => {
605            return Err(fail(ErrorCode::Encoding(
606                structured_asn1::ErrorCode::UnexpectedTag,
607            )));
608        }
609    };
610    if !fits {
611        return Err(fail(ErrorCode::MalformedValue));
612    }
613    Ok(())
614}
615
616/// Requires a wrapper to hold exactly one country choice.
617fn x400_country<'a>(parent: &Reader<'a>, wrapper: &Element<'a>, field: &'static str) -> Result<()> {
618    let fail = |code| Error::new(code, field, wrapper.span().start);
619    let mut inside = parent.enter(wrapper).field(field)?;
620    let value = inside
621        .next_element()
622        .field(field)?
623        .ok_or_else(|| fail(ErrorCode::MissingField))?;
624    if !inside.is_empty() {
625        return Err(fail(ErrorCode::TrailingData));
626    }
627    x400_bare_country(&value, field)
628}
629
630/// Requires a wrapper to hold exactly one string of a two-way choice, sized
631/// as the schema sizes each side of that choice.
632fn x400_choice<'a>(
633    parent: &Reader<'a>,
634    wrapper: &Element<'a>,
635    field: &'static str,
636    numeric: (usize, usize),
637    printable: (usize, usize),
638) -> Result<()> {
639    let fail = |code| Error::new(code, field, wrapper.span().start);
640    let mut inside = parent.enter(wrapper).field(field)?;
641    let value = inside
642        .next_element()
643        .field(field)?
644        .ok_or_else(|| fail(ErrorCode::MissingField))?;
645    if !inside.is_empty() {
646        return Err(fail(ErrorCode::TrailingData));
647    }
648    let size = match (value.tag().class(), value.tag().number()) {
649        (Class::Universal, Some(X400_NUMERIC_STRING)) => numeric,
650        (Class::Universal, Some(universal::PRINTABLE_STRING)) => printable,
651        _ => {
652            return Err(fail(ErrorCode::Encoding(
653                structured_asn1::ErrorCode::UnexpectedTag,
654            )));
655        }
656    };
657    x400_string(&value, field, size)
658}
659
660/// The members X.411 gives the built-in standard attributes, each optional,
661/// each at most once, in schema order, with the strings held to their
662/// repertoires and the appendix's upper bounds.
663fn read_standard_attributes<'a>(
664    parent: &Reader<'a>,
665    element: &Element<'a>,
666    field: &'static str,
667) -> Result<()> {
668    let mut inside = parent.enter(element).field(field)?;
669    let mut last_rank = -1i8;
670    while let Some(member) = inside.next_element().field(field)? {
671        let bad = |code| Error::new(code, field, member.span().start);
672        let tag = member.tag();
673        // A number this reader does not name is no member of the structure,
674        // and falls to the same refusal as any other number it does not have.
675        let Some(number) = tag.number() else {
676            return Err(bad(ErrorCode::Encoding(
677                structured_asn1::ErrorCode::UnexpectedTag,
678            )));
679        };
680        let rank: i8 = match (tag.class(), number, tag.is_constructed()) {
681            // The country: exactly a three-digit or a two-letter code,
682            // wrapped because a choice keeps its own tag.
683            (Class::Application, 1, true) => {
684                x400_country(&inside, &member, field)?;
685                0
686            }
687            // The administration domain: the same choice, up to sixteen
688            // characters, empty naming the default domain.
689            (Class::Application, 2, true) => {
690                x400_choice(&inside, &member, field, (0, 16), (0, 16))?;
691                1
692            }
693            // network-address: an X.121 address of up to sixteen digits.
694            (Class::ContextSpecific, 0, false) => {
695                x400_implicit(&member, field, X400Repertoire::Numeric, 16)?;
696                2
697            }
698            // terminal-identifier.
699            (Class::ContextSpecific, 1, false) => {
700                x400_implicit(&member, field, X400Repertoire::Printable, 24)?;
701                3
702            }
703            // private-domain-name: the numeric-or-printable choice again.
704            (Class::ContextSpecific, 2, true) => {
705                x400_choice(&inside, &member, field, (1, 16), (1, 16))?;
706                4
707            }
708            // organization-name.
709            (Class::ContextSpecific, 3, false) => {
710                x400_implicit(&member, field, X400Repertoire::Printable, 64)?;
711                5
712            }
713            // numeric-user-identifier.
714            (Class::ContextSpecific, 4, false) => {
715                x400_implicit(&member, field, X400Repertoire::Numeric, 32)?;
716                6
717            }
718            // personal-name: a set of up to four parts, the surname required.
719            (Class::ContextSpecific, 5, true) => {
720                read_personal_name(&inside, &member, field, X400Repertoire::Printable)?;
721                7
722            }
723            // organizational-unit-names: one to four unit names, each a
724            // PrintableString alone, unlike the explicit numeric-or-printable
725            // choices elsewhere in the address.
726            (Class::ContextSpecific, 6, true) => {
727                let mut units = inside.enter(&member).field(field)?;
728                let mut count = 0usize;
729                while let Some(unit) = units.next_element().field(field)? {
730                    x400_printable(&unit, field, (1, 32))?;
731                    count += 1;
732                }
733                if !(1..=4).contains(&count) {
734                    return Err(bad(ErrorCode::MalformedValue));
735                }
736                8
737            }
738            _ => {
739                return Err(bad(ErrorCode::Encoding(
740                    structured_asn1::ErrorCode::UnexpectedTag,
741                )));
742            }
743        };
744        // Each member appears at most once, in the order the schema writes
745        // them; a repeat or a step backwards is a second spelling of the
746        // structure.
747        if rank <= last_rank {
748            return Err(bad(ErrorCode::UnexpectedField));
749        }
750        last_rank = rank;
751    }
752    Ok(())
753}
754
755/// The personal name of X.411: up to four parts, each at most once, the
756/// surname required. The built-in form holds printable parts; the teletex
757/// extension attribute holds the same shape in teletex strings.
758fn read_personal_name<'a>(
759    parent: &Reader<'a>,
760    element: &Element<'a>,
761    field: &'static str,
762    repertoire: X400Repertoire,
763) -> Result<()> {
764    let fail = |code| Error::new(code, field, element.span().start);
765    let mut inside = parent.enter(element).field(field)?;
766    let mut seen = [false; 4];
767    let mut last: Option<usize> = None;
768    while let Some(part) = inside.next_element().field(field)? {
769        let bad = |code| Error::new(code, field, part.span().start);
770        let (number, most) = match (
771            part.tag().class(),
772            part.tag().number(),
773            part.tag().is_constructed(),
774        ) {
775            (Class::ContextSpecific, Some(0), false) => (0usize, 40),
776            (Class::ContextSpecific, Some(1), false) => (1, 16),
777            (Class::ContextSpecific, Some(2), false) => (2, 5),
778            (Class::ContextSpecific, Some(3), false) => (3, 3),
779            _ => {
780                return Err(bad(ErrorCode::Encoding(
781                    structured_asn1::ErrorCode::UnexpectedTag,
782                )));
783            }
784        };
785        if seen[number] {
786            return Err(bad(ErrorCode::UnexpectedField));
787        }
788        // A SET's distinct tags come in canonical order under DER (X.690
789        // 10.3): out of order is a second spelling of one value. BER carries
790        // no such rule.
791        if inside.encoding().is_der() && last.is_some_and(|last| number < last) {
792            return Err(bad(ErrorCode::UnexpectedField));
793        }
794        seen[number] = true;
795        last = Some(number);
796        x400_implicit(&part, field, repertoire, most)?;
797    }
798    if !seen[0] {
799        return Err(fail(ErrorCode::MissingField));
800    }
801    Ok(())
802}
803
804/// The domain-defined attributes of X.411: one to four pairs of a type and a
805/// value, each a printable string.
806fn read_domain_defined<'a>(
807    parent: &Reader<'a>,
808    element: &Element<'a>,
809    field: &'static str,
810) -> Result<()> {
811    read_defined_pairs(parent, element, field, false)
812}
813
814/// One to four pairs of a type and a value: printable strings in the
815/// built-in form, teletex strings in the teletex extension attribute.
816fn read_defined_pairs<'a>(
817    parent: &Reader<'a>,
818    element: &Element<'a>,
819    field: &'static str,
820    teletex: bool,
821) -> Result<()> {
822    let fail = |code| Error::new(code, field, element.span().start);
823    if element.tag() != Tag::constructed(universal::SEQUENCE) {
824        return Err(fail(ErrorCode::Encoding(
825            structured_asn1::ErrorCode::UnexpectedTag,
826        )));
827    }
828    let held_string = |element: &Element<'_>, size: (usize, usize)| {
829        if teletex {
830            x400_teletex(element, field, size)
831        } else {
832            // The built-in pair holds PrintableString in both members; the
833            // numeric encoding belongs to other positions of the address.
834            x400_printable(element, field, size)
835        }
836    };
837    let mut inside = parent.enter(element).field(field)?;
838    let mut count = 0usize;
839    while let Some(pair) = inside.next_element().field(field)? {
840        let bad = |code| Error::new(code, field, pair.span().start);
841        if pair.tag() != Tag::constructed(universal::SEQUENCE) {
842            return Err(bad(ErrorCode::Encoding(
843                structured_asn1::ErrorCode::UnexpectedTag,
844            )));
845        }
846        let mut two = inside.enter(&pair).field(field)?;
847        let kind = two
848            .next_element()
849            .field(field)?
850            .ok_or_else(|| bad(ErrorCode::MissingField))?;
851        held_string(&kind, (1, 8))?;
852        let value = two
853            .next_element()
854            .field(field)?
855            .ok_or_else(|| bad(ErrorCode::MissingField))?;
856        held_string(&value, (1, 128))?;
857        if !two.is_empty() {
858            return Err(bad(ErrorCode::TrailingData));
859        }
860        count += 1;
861    }
862    if !(1..=4).contains(&count) {
863        return Err(fail(ErrorCode::MalformedValue));
864    }
865    Ok(())
866}
867
868/// The extension attributes of X.411: each an identifying number and an
869/// explicitly wrapped value that number defines, decoded by that number
870/// below.
871fn read_extension_attributes<'a>(
872    parent: &Reader<'a>,
873    element: &Element<'a>,
874    field: &'static str,
875) -> Result<()> {
876    let fail = |code| Error::new(code, field, element.span().start);
877    let mut inside = parent.enter(element).field(field)?;
878    let mut count = 0usize;
879    let mut previous: Option<&[u8]> = None;
880    while let Some(entry) = inside.next_element().field(field)? {
881        let bad = |code| Error::new(code, field, entry.span().start);
882        if entry.tag() != Tag::constructed(universal::SEQUENCE) {
883            return Err(bad(ErrorCode::Encoding(
884                structured_asn1::ErrorCode::UnexpectedTag,
885            )));
886        }
887        // A SET OF, whose members DER writes in ascending encoded order
888        // (X.690 11.6): out of order is a second spelling of one value. BER
889        // carries no such rule. The comparison walks up to the longer
890        // encoding before either member is decoded, so that length is
891        // charged to the shared work budget first.
892        if inside.encoding().is_der()
893            && let Some(previous) = previous
894        {
895            inside
896                .charge(previous.len().max(entry.as_bytes().len()) as u64)
897                .field(field)?;
898            if entry.as_bytes() < previous {
899                return Err(bad(ErrorCode::UnexpectedField));
900            }
901        }
902        previous = Some(entry.as_bytes());
903        let mut two = inside.enter(&entry).field(field)?;
904        let kind = two
905            .next_element()
906            .field(field)?
907            .ok_or_else(|| bad(ErrorCode::MissingField))?;
908        // The number wears its position's tag; the decoding helper is
909        // tag-agnostic, which is exactly right for an implicit field, but the
910        // position is required first. The schema bounds it to 0..=256.
911        if extension::context_of(kind.tag()) != Some((0, false)) {
912            return Err(bad(ErrorCode::Encoding(
913                structured_asn1::ErrorCode::UnexpectedTag,
914            )));
915        }
916        let number = two.integer(&kind).field(field)?;
917        if number.as_u32().is_none_or(|number| number > 256) {
918            return Err(bad(ErrorCode::MalformedValue));
919        }
920        let value = two
921            .next_element()
922            .field(field)?
923            .ok_or_else(|| bad(ErrorCode::MissingField))?;
924        // The value is wrapped explicitly, because what it is depends on the
925        // number before it. It holds exactly one value, kept encoded.
926        if extension::context_of(value.tag()) != Some((1, true)) {
927            return Err(bad(ErrorCode::Encoding(
928                structured_asn1::ErrorCode::UnexpectedTag,
929            )));
930        }
931        let mut held = two.enter(&value).field(field)?;
932        let Some(declared) = held.next_element().field(field)? else {
933            return Err(bad(ErrorCode::MissingField));
934        };
935        // The number declares the value's syntax, so the value is decoded by
936        // it: an attribute wearing a number whose syntax it does not have was
937        // never decoded, and a critical name built on one must not read as
938        // parsed.
939        let number = number
940            .as_u32()
941            .expect("bounded to 0..=256 above, which fits");
942        read_extension_value(&held, &declared, number, field)?;
943        if !held.is_empty() || !two.is_empty() {
944            return Err(bad(ErrorCode::TrailingData));
945        }
946        count += 1;
947        // ub-extension-attributes bounds the set at 256 members (RFC 5280
948        // appendix A); a 257th puts the address outside the schema.
949        if count > 256 {
950            return Err(bad(ErrorCode::MalformedValue));
951        }
952    }
953    if count == 0 {
954        return Err(fail(ErrorCode::EmptySequence));
955    }
956    Ok(())
957}
958
959/// The value an extension attribute declares, decoded by the number before
960/// it: the syntaxes RFC 5280 appendix A gives the numbers it defines, with
961/// that module's size bounds. A number the appendix does not define is a
962/// value this crate cannot decode, and a name built on one must not read as
963/// parsed.
964fn read_extension_value<'a>(
965    parent: &Reader<'a>,
966    value: &Element<'a>,
967    number: u32,
968    field: &'static str,
969) -> Result<()> {
970    let fail = |code| Error::new(code, field, value.span().start);
971    match number {
972        // common-name: PrintableString alone, unlike the explicit
973        // numeric-or-printable choices elsewhere in the address.
974        1 => x400_printable(value, field, (1, 64)),
975        // teletex-common-name and teletex-organization-name.
976        2 | 3 => x400_teletex(value, field, (1, 64)),
977        // teletex-personal-name: the personal-name shape in teletex strings.
978        4 => {
979            if value.tag() != Tag::constructed(universal::SET) {
980                return Err(fail(ErrorCode::Encoding(
981                    structured_asn1::ErrorCode::UnexpectedTag,
982                )));
983            }
984            read_personal_name(parent, value, field, X400Repertoire::Teletex)
985        }
986        // teletex-organizational-unit-names: one to four unit names.
987        5 => {
988            if value.tag() != Tag::constructed(universal::SEQUENCE) {
989                return Err(fail(ErrorCode::Encoding(
990                    structured_asn1::ErrorCode::UnexpectedTag,
991                )));
992            }
993            let mut units = parent.enter(value).field(field)?;
994            let mut count = 0usize;
995            while let Some(unit) = units.next_element().field(field)? {
996                x400_teletex(&unit, field, (1, 32))?;
997                count += 1;
998            }
999            if !(1..=4).contains(&count) {
1000                return Err(fail(ErrorCode::MalformedValue));
1001            }
1002            Ok(())
1003        }
1004        // teletex-domain-defined-attributes: the pairs again, in teletex.
1005        6 => read_defined_pairs(parent, value, field, true),
1006        // pds-name: PrintableString alone.
1007        7 => x400_printable(value, field, (1, 16)),
1008        // physical-delivery-country-name: exactly a three-digit or a
1009        // two-letter code, the choice bare in this position.
1010        8 => x400_bare_country(value, field),
1011        // postal-code.
1012        9 => x400_bare_choice(value, field, (1, 16), (1, 16)),
1013        // The postal-delivery parameters, one shape under many numbers: an
1014        // optional printable and an optional teletex string.
1015        10..=15 | 17..=21 => read_pds_parameter(parent, value, field, 30),
1016        // unformatted-postal-address.
1017        16 => read_unformatted_address(parent, value, field),
1018        // extended-network-address.
1019        22 => read_extended_network_address(parent, value, field),
1020        // terminal-type: an integer of the option table.
1021        23 => {
1022            if value.tag() != Tag::primitive(universal::INTEGER) {
1023                return Err(fail(ErrorCode::Encoding(
1024                    structured_asn1::ErrorCode::UnexpectedTag,
1025                )));
1026            }
1027            let option = parent.integer(value).field(field)?;
1028            if option.as_u32().is_none_or(|option| option > 256) {
1029                return Err(fail(ErrorCode::MalformedValue));
1030            }
1031            Ok(())
1032        }
1033        _ => Err(fail(ErrorCode::MalformedValue)),
1034    }
1035}
1036
1037/// Requires a bare two-way choice: the string stands in the choice's own
1038/// position, so its universal tag says which side it is.
1039fn x400_bare_choice(
1040    value: &Element<'_>,
1041    field: &'static str,
1042    numeric: (usize, usize),
1043    printable: (usize, usize),
1044) -> Result<()> {
1045    let fail = |code| Error::new(code, field, value.span().start);
1046    let size = match (
1047        value.tag().class(),
1048        value.tag().number(),
1049        value.tag().is_constructed(),
1050    ) {
1051        (Class::Universal, Some(X400_NUMERIC_STRING), false) => numeric,
1052        (Class::Universal, Some(universal::PRINTABLE_STRING), false) => printable,
1053        _ => {
1054            return Err(fail(ErrorCode::Encoding(
1055                structured_asn1::ErrorCode::UnexpectedTag,
1056            )));
1057        }
1058    };
1059    x400_string(value, field, size)
1060}
1061
1062/// The postal-delivery parameter of X.411: an optional printable and an
1063/// optional teletex string, each at most once, each within one size.
1064fn read_pds_parameter<'a>(
1065    parent: &Reader<'a>,
1066    value: &Element<'a>,
1067    field: &'static str,
1068    most: usize,
1069) -> Result<()> {
1070    let fail = |code| Error::new(code, field, value.span().start);
1071    if value.tag() != Tag::constructed(universal::SET) {
1072        return Err(fail(ErrorCode::Encoding(
1073            structured_asn1::ErrorCode::UnexpectedTag,
1074        )));
1075    }
1076    let mut inside = parent.enter(value).field(field)?;
1077    let mut seen_printable = false;
1078    let mut seen_teletex = false;
1079    while let Some(member) = inside.next_element().field(field)? {
1080        let bad = |code| Error::new(code, field, member.span().start);
1081        if member.tag() == Tag::primitive(universal::PRINTABLE_STRING) {
1082            // A SET's distinct tags come in canonical order under DER
1083            // (X.690 10.3): the printable tag sorts before the teletex one,
1084            // so arriving after it is a second spelling of one value. BER
1085            // carries no such rule.
1086            if seen_printable || (seen_teletex && inside.encoding().is_der()) {
1087                return Err(bad(ErrorCode::UnexpectedField));
1088            }
1089            seen_printable = true;
1090            x400_string(&member, field, (1, most))?;
1091        } else if member.tag() == Tag::primitive(20) {
1092            if seen_teletex {
1093                return Err(bad(ErrorCode::UnexpectedField));
1094            }
1095            seen_teletex = true;
1096            x400_teletex(&member, field, (1, most))?;
1097        } else {
1098            return Err(bad(ErrorCode::Encoding(
1099                structured_asn1::ErrorCode::UnexpectedTag,
1100            )));
1101        }
1102    }
1103    Ok(())
1104}
1105
1106/// The unformatted postal address of X.411: up to six printable lines and
1107/// an optional teletex rendering, each part at most once.
1108fn read_unformatted_address<'a>(
1109    parent: &Reader<'a>,
1110    value: &Element<'a>,
1111    field: &'static str,
1112) -> Result<()> {
1113    let fail = |code| Error::new(code, field, value.span().start);
1114    if value.tag() != Tag::constructed(universal::SET) {
1115        return Err(fail(ErrorCode::Encoding(
1116            structured_asn1::ErrorCode::UnexpectedTag,
1117        )));
1118    }
1119    let mut inside = parent.enter(value).field(field)?;
1120    let mut seen_lines = false;
1121    let mut seen_teletex = false;
1122    while let Some(member) = inside.next_element().field(field)? {
1123        let bad = |code| Error::new(code, field, member.span().start);
1124        if member.tag() == Tag::constructed(universal::SEQUENCE) {
1125            // A SET's distinct tags come in canonical order under DER
1126            // (X.690 10.3): the SEQUENCE tag sorts before the teletex one,
1127            // so arriving after it is a second spelling of one value. BER
1128            // carries no such rule.
1129            if seen_lines || (seen_teletex && inside.encoding().is_der()) {
1130                return Err(bad(ErrorCode::UnexpectedField));
1131            }
1132            seen_lines = true;
1133            let mut lines = inside.enter(&member).field(field)?;
1134            let mut count = 0usize;
1135            while let Some(line) = lines.next_element().field(field)? {
1136                // Every line of the printable address is a PrintableString;
1137                // the numeric alternative belongs to other positions.
1138                x400_printable(&line, field, (1, 30))?;
1139                count += 1;
1140            }
1141            if !(1..=6).contains(&count) {
1142                return Err(bad(ErrorCode::MalformedValue));
1143            }
1144        } else if member.tag() == Tag::primitive(20) {
1145            if seen_teletex {
1146                return Err(bad(ErrorCode::UnexpectedField));
1147            }
1148            seen_teletex = true;
1149            x400_teletex(&member, field, (1, 180))?;
1150        } else {
1151            return Err(bad(ErrorCode::Encoding(
1152                structured_asn1::ErrorCode::UnexpectedTag,
1153            )));
1154        }
1155    }
1156    Ok(())
1157}
1158
1159/// The E.163/E.164 alternative: a number and an optional sub-address.
1160fn read_e163_address<'a>(
1161    parent: &Reader<'a>,
1162    value: &Element<'a>,
1163    field: &'static str,
1164) -> Result<()> {
1165    let fail = |code| Error::new(code, field, value.span().start);
1166    let mut inside = parent.enter(value).field(field)?;
1167    let number = inside
1168        .next_element()
1169        .field(field)?
1170        .ok_or_else(|| fail(ErrorCode::MissingField))?;
1171    if extension::context_of(number.tag()) != Some((0, false)) {
1172        return Err(fail(ErrorCode::Encoding(
1173            structured_asn1::ErrorCode::UnexpectedTag,
1174        )));
1175    }
1176    x400_implicit(&number, field, X400Repertoire::Numeric, 15)?;
1177    if let Some(sub) = inside.next_element().field(field)? {
1178        if extension::context_of(sub.tag()) != Some((1, false)) {
1179            return Err(fail(ErrorCode::Encoding(
1180                structured_asn1::ErrorCode::UnexpectedTag,
1181            )));
1182        }
1183        x400_implicit(&sub, field, X400Repertoire::Numeric, 40)?;
1184    }
1185    if !inside.is_empty() {
1186        return Err(fail(ErrorCode::TrailingData));
1187    }
1188    Ok(())
1189}
1190
1191/// The extended network address of X.411: an E.163/E.164 number with an
1192/// optional sub-address, or a presentation address of wrapped selectors and
1193/// the mandatory network addresses.
1194fn read_extended_network_address<'a>(
1195    parent: &Reader<'a>,
1196    value: &Element<'a>,
1197    field: &'static str,
1198) -> Result<()> {
1199    let fail = |code| Error::new(code, field, value.span().start);
1200    if value.tag() == Tag::constructed(universal::SEQUENCE) {
1201        read_e163_address(parent, value, field)
1202    } else if extension::context_of(value.tag()) == Some((0, true)) {
1203        // psap-address: three optional wrapped selectors, then the mandatory
1204        // set of network addresses, each member at most once in order.
1205        let mut inside = parent.enter(value).field(field)?;
1206        let mut last: i8 = -1;
1207        let mut saw_network = false;
1208        while let Some(member) = inside.next_element().field(field)? {
1209            let bad = |code| Error::new(code, field, member.span().start);
1210            let Some((number, true)) = extension::context_of(member.tag()) else {
1211                return Err(bad(ErrorCode::Encoding(
1212                    structured_asn1::ErrorCode::UnexpectedTag,
1213                )));
1214            };
1215            let rank = i8::try_from(number).map_err(|_| {
1216                bad(ErrorCode::Encoding(
1217                    structured_asn1::ErrorCode::UnexpectedTag,
1218                ))
1219            })?;
1220            if rank > 3 {
1221                return Err(bad(ErrorCode::Encoding(
1222                    structured_asn1::ErrorCode::UnexpectedTag,
1223                )));
1224            }
1225            if rank <= last {
1226                return Err(bad(ErrorCode::UnexpectedField));
1227            }
1228            last = rank;
1229            let mut held = inside.enter(&member).field(field)?;
1230            if rank == 3 {
1231                // The network addresses: at least one octet string.
1232                let addresses = held
1233                    .next_element()
1234                    .field(field)?
1235                    .ok_or_else(|| bad(ErrorCode::MissingField))?;
1236                if addresses.tag() != Tag::constructed(universal::SET) {
1237                    return Err(bad(ErrorCode::Encoding(
1238                        structured_asn1::ErrorCode::UnexpectedTag,
1239                    )));
1240                }
1241                let mut each = held.enter(&addresses).field(field)?;
1242                let mut count = 0usize;
1243                let mut previous: Option<&[u8]> = None;
1244                while let Some(address) = each.next_element().field(field)? {
1245                    if address.tag() != Tag::primitive(universal::OCTET_STRING) {
1246                        return Err(bad(ErrorCode::Encoding(
1247                            structured_asn1::ErrorCode::UnexpectedTag,
1248                        )));
1249                    }
1250                    // A SET OF, whose members DER writes in ascending
1251                    // encoded order (X.690 11.6): out of order is a second
1252                    // spelling of one value. BER carries no such rule. The
1253                    // comparison walks up to the longer encoding, so that
1254                    // length is charged to the shared work budget first.
1255                    if each.encoding().is_der()
1256                        && let Some(previous) = previous
1257                    {
1258                        each.charge(previous.len().max(address.as_bytes().len()) as u64)
1259                            .field(field)?;
1260                        if address.as_bytes() < previous {
1261                            return Err(bad(ErrorCode::UnexpectedField));
1262                        }
1263                    }
1264                    previous = Some(address.as_bytes());
1265                    count += 1;
1266                }
1267                if count == 0 {
1268                    return Err(bad(ErrorCode::EmptySequence));
1269                }
1270                saw_network = true;
1271            } else {
1272                // A selector: one wrapped octet string.
1273                let selector = held
1274                    .next_element()
1275                    .field(field)?
1276                    .ok_or_else(|| bad(ErrorCode::MissingField))?;
1277                if selector.tag() != Tag::primitive(universal::OCTET_STRING) {
1278                    return Err(bad(ErrorCode::Encoding(
1279                        structured_asn1::ErrorCode::UnexpectedTag,
1280                    )));
1281                }
1282            }
1283            if !held.is_empty() {
1284                return Err(bad(ErrorCode::TrailingData));
1285            }
1286        }
1287        if !saw_network {
1288            return Err(fail(ErrorCode::MissingField));
1289        }
1290        Ok(())
1291    } else {
1292        Err(fail(ErrorCode::Encoding(
1293            structured_asn1::ErrorCode::UnexpectedTag,
1294        )))
1295    }
1296}
1297
1298/// Validates an EDI party name: an optional assigner, then the mandatory
1299/// party, each an explicitly tagged wrapper holding one directory string.
1300/// The tags wrap rather than replace, because a directory string is a choice
1301/// and RFC 5280 appendix A keeps a choice's own tag.
1302fn read_edi_party<'a>(
1303    parent: &Reader<'a>,
1304    element: &Element<'a>,
1305    field: &'static str,
1306) -> Result<()> {
1307    let fail = |code| Error::new(code, field, element.span().start);
1308
1309    let mut inside = parent.enter(element).field(field)?;
1310    let mut next = inside.next_element().field(field)?;
1311
1312    if let Some(assigner) = next
1313        && extension::context_of(assigner.tag()) == Some((0, true))
1314    {
1315        require_directory_string(&inside, &assigner, field)?;
1316        next = inside.next_element().field(field)?;
1317    }
1318
1319    let party = next.ok_or_else(|| fail(ErrorCode::MissingField))?;
1320    if extension::context_of(party.tag()) != Some((1, true)) {
1321        return Err(Error::new(
1322            ErrorCode::UnexpectedField,
1323            field,
1324            party.span().start,
1325        ));
1326    }
1327    require_directory_string(&inside, &party, field)?;
1328
1329    if !inside.is_empty() {
1330        return Err(fail(ErrorCode::TrailingData));
1331    }
1332    Ok(())
1333}
1334
1335/// Requires a wrapper to hold exactly one non-empty directory string.
1336fn require_directory_string<'a>(
1337    parent: &Reader<'a>,
1338    wrapper: &Element<'a>,
1339    field: &'static str,
1340) -> Result<()> {
1341    /// The ub-name character bound both EDI fields carry (RFC 5280 appendix A).
1342    const MOST: usize = 32768;
1343    let fail = |code| Error::new(code, field, wrapper.span().start);
1344
1345    let mut inside = parent.enter(wrapper).field(field)?;
1346    let value = inside
1347        .next_element()
1348        .field(field)?
1349        .ok_or_else(|| fail(ErrorCode::MissingField))?;
1350    if !inside.is_empty() {
1351        return Err(fail(ErrorCode::TrailingData));
1352    }
1353
1354    // The directory string choice of X.520: a primitive universal string of
1355    // one of its five types, holding one to 32768 characters, the ub-name
1356    // bound both EDI fields carry (RFC 5280 appendix A). The tag alone is not
1357    // the type, so the value must also decode under the type chosen.
1358    let tag = value.tag();
1359    let is_directory_string = tag.class() == Class::Universal
1360        && !tag.is_constructed()
1361        && matches!(tag.number(), Some(12 | 19 | 20 | 28 | 30));
1362    // The ceiling is checked on the encoded bytes before any text is
1363    // materialized: each encoding spends a known number of bytes per
1364    // character, so contents past the per-encoding multiple of the bound
1365    // can never fit it and are refused without allocating a copy.
1366    let ceiling = match tag.number() {
1367        Some(12 | 28) => MOST.saturating_mul(4),
1368        Some(30) => MOST.saturating_mul(2),
1369        _ => MOST,
1370    };
1371    if value.contents().len() > ceiling {
1372        return Err(fail(ErrorCode::MalformedValue));
1373    }
1374    // The decode below scans every content byte, and the per-field ceiling
1375    // does not bound the aggregate across many fields: the scan is charged
1376    // to the shared work budget first.
1377    parent.charge(value.contents().len() as u64).field(field)?;
1378    let decodes = match tag.number() {
1379        Some(12) => {
1380            core::str::from_utf8(value.contents()).is_ok_and(|text| text.chars().count() <= MOST)
1381        }
1382        Some(19) => crate::name::is_printable(value.contents()) && value.contents().len() <= MOST,
1383        Some(28) => {
1384            crate::name::wide_char_count(value.contents(), 4).is_some_and(|count| count <= MOST)
1385        }
1386        Some(30) => {
1387            crate::name::wide_char_count(value.contents(), 2).is_some_and(|count| count <= MOST)
1388        }
1389        // TeletexString's repertoire is a negotiation this crate does not
1390        // implement: the bytes being there and inside the bound is what is
1391        // checkable.
1392        _ => value.contents().len() <= MOST,
1393    };
1394    if !is_directory_string || value.contents().is_empty() || !decodes {
1395        return Err(fail(ErrorCode::MalformedValue));
1396    }
1397    Ok(())
1398}
1399
1400fn read_other<'a>(
1401    parent: &Reader<'a>,
1402    element: &Element<'a>,
1403    field: &'static str,
1404) -> Result<GeneralName<'a>> {
1405    let fail = |code| Error::new(code, field, element.span().start);
1406
1407    let mut inside = parent.enter(element).field(field)?;
1408    let type_id = inside.read_oid().field(field)?;
1409    let wrapper = inside
1410        .next_element()
1411        .field(field)?
1412        .ok_or_else(|| fail(ErrorCode::MissingField))?;
1413
1414    // The value is tagged explicitly, because what it is depends on the
1415    // identifier before it and a tag cannot replace one it does not know.
1416    if wrapper.tag() != Tag::context(true, 0) {
1417        return Err(fail(ErrorCode::Encoding(
1418            structured_asn1::ErrorCode::UnexpectedTag,
1419        )));
1420    }
1421    if !inside.is_empty() {
1422        return Err(fail(ErrorCode::TrailingData));
1423    }
1424
1425    // The wrapper holds the one value the identifier describes. Empty it
1426    // names nothing, and with a second element there is no answer to which
1427    // value the name is.
1428    let mut held = inside.enter(&wrapper).field(field)?;
1429    let value = held
1430        .next_element()
1431        .field(field)?
1432        .ok_or_else(|| fail(ErrorCode::MissingField))?;
1433    if !held.is_empty() {
1434        return Err(fail(ErrorCode::TrailingData));
1435    }
1436
1437    // The outer tag is not the whole value: a container this crate does not
1438    // interpret may still hold children that are not DER, an integer written
1439    // a byte longer than it needs or a marker belonging to the indefinite
1440    // lengths DER does not have. A recognizer clears a critical extension on
1441    // the strength of this value, so every element inside it is held to the
1442    // encoding's rules before it is preserved.
1443    crate::name::walk_der(&held, &value, field, crate::name::Strings::Checked)?;
1444
1445    Ok(GeneralName::Other {
1446        type_id,
1447        value: value.as_bytes(),
1448    })
1449}
1450
1451/// Reads the ASCII a schema calls an `IA5String`.
1452/// Whether text is a domain in the preferred name syntax: dot-separated
1453/// labels of letters, digits and inner hyphens, each of one to sixty-three
1454/// characters (RFC 1034 3.5, its leading letter relaxed to a letter or digit
1455/// by RFC 1123 2.1).
1456pub(crate) fn is_domain(text: &str) -> bool {
1457    // The whole name is bounded too: 255 octets in the wire form, which is
1458    // 253 characters of the presentation form (RFC 1035 2.3.4). The
1459    // highest-level label stays alphabetic-bearing (RFC 1123 2.1), which is
1460    // what separates a host name from a dotted-decimal address: an address
1461    // wearing this field would ride under the wrong name-constraint form.
1462    text.len() <= 253
1463        && text.split('.').all(|label| {
1464            (1..=63).contains(&label.len())
1465                && label
1466                    .bytes()
1467                    .all(|byte| byte.is_ascii_alphanumeric() || byte == b'-')
1468                && !label.starts_with('-')
1469                && !label.ends_with('-')
1470        })
1471        && text
1472            .rsplit('.')
1473            .next()
1474            .is_some_and(|top| top.bytes().any(|byte| byte.is_ascii_alphabetic()))
1475}
1476
1477/// Whether text is a mailbox, the form RFC 5280 4.2.1.6 points at: "the
1478/// format of an rfc822Name is a Mailbox as defined in RFC 5321 section
1479/// 4.1.2".
1480///
1481/// The dependency's ASCII SMTP predicate shares its strict envelope grammar,
1482/// including quoted local parts, single-label domains and §4.1.3 literals.
1483/// It performs no allocation or normalization. Both callers admit the bounded
1484/// linear scan first: name decoding through `text()`, and legacy subject
1485/// mailbox formation through the path's current execution account. Header
1486/// comments are outside this grammar. Literal tag registration/deliverability
1487/// is not established by this syntax check.
1488pub(crate) fn is_mailbox(text: &str) -> bool {
1489    structured_email_address::is_smtp_mailbox(text)
1490}
1491
1492/// Whether text is an absolute URI: a scheme of a letter then letters,
1493/// digits, plus, minus and dots (RFC 3986 3.1), a colon, and a non-empty
1494/// remainder inside the URI alphabet, a percent sign spelling a byte only
1495/// with two hexadecimal digits behind it (RFC 3986 2.1). The fragment
1496/// delimiter is not in the alphabet: absolute-URI carries no fragment
1497/// (RFC 3986 4.3), which belongs to URI references. What follows the scheme
1498/// is the scheme's to define, and is never fetched.
1499pub(crate) fn is_absolute_uri(text: &str) -> bool {
1500    absolute_uri::<true>(text)
1501}
1502
1503fn absolute_uri<const CERTIFICATE_NAME: bool>(text: &str) -> bool {
1504    let Some((scheme, rest)) = text.split_once(':') else {
1505        return false;
1506    };
1507    let mut bytes = scheme.bytes();
1508    if !(bytes.next().is_some_and(|byte| byte.is_ascii_alphabetic())
1509        && bytes.all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'+' | b'-' | b'.')))
1510    {
1511        return false;
1512    }
1513    // The remainder is required, which the generic grammar does not require:
1514    // RFC 3986's hier-part admits path-empty, so "urn:" parses there. The
1515    // profile is stricter, and it is the profile this reads under: a name
1516    // "MUST include both a scheme (e.g., "http" or "ftp") and a
1517    // scheme-specific-part" (RFC 5280 4.2.1.6). A scheme alone names no
1518    // place, and accepting one would report a location a report cannot
1519    // explain.
1520    if CERTIFICATE_NAME && rest.is_empty() {
1521        return false;
1522    }
1523
1524    // The authority is [userinfo "@"] host [":" port] (RFC 3986 3.2), and
1525    // it is validated structurally: the user-info delimiter appears at most
1526    // once, the brackets exist only around an IP literal in the host
1527    // (3.2.2), and the port holds digits and nothing else (3.2.3). The
1528    // character walk below covers the remaining free-form segments.
1529    let mut userinfo = "";
1530    let mut bare_host = "";
1531    let mut tail = rest;
1532    if let Some(after) = rest.strip_prefix("//") {
1533        let end = after.find(['/', '?']).unwrap_or(after.len());
1534        let authority = &after[..end];
1535        tail = &after[end..];
1536        let (before, host_port) = match authority.split_once('@') {
1537            Some((before, host_port)) => (before, host_port),
1538            None => ("", authority),
1539        };
1540        // A second raw at-sign belongs to no component: neither user-info
1541        // nor the host may carry one.
1542        if host_port.contains('@') {
1543            return false;
1544        }
1545        userinfo = before;
1546        let port = if let Some(inside) = host_port.strip_prefix('[') {
1547            let Some((literal, past)) = inside.split_once(']') else {
1548                return false;
1549            };
1550            if !is_ip_literal(literal) {
1551                return false;
1552            }
1553            // After the literal only a port may follow within the authority.
1554            if past.is_empty() {
1555                None
1556            } else {
1557                let Some(digits) = past.strip_prefix(':') else {
1558                    return false;
1559                };
1560                Some(digits)
1561            }
1562        } else {
1563            // A colon in a bare host is the port delimiter: reg-names have
1564            // no colon of their own.
1565            let port = if let Some((host, port)) = host_port.split_once(':') {
1566                bare_host = host;
1567                Some(port)
1568            } else {
1569                bare_host = host_port;
1570                None
1571            };
1572            // "URIs that include an authority (RFC 3986 3.2) MUST include a
1573            // fully qualified domain name or IP address as the host"
1574            // (RFC 5280 4.2.1.6). The bracketed form is an address already;
1575            // a bare host is a name or a dotted-quad, and nothing else, so
1576            // an empty authority and a word that is neither are refused
1577            // here rather than passed on as a location.
1578            //
1579            // A single closing period is the absolute spelling of a name,
1580            // which ends at the root (RFC 1034 3.1). It qualifies a name
1581            // rather than leaving it short of qualified, so it is read here
1582            // and kept in the location, which is preserved as written.
1583            let named = bare_host.strip_suffix('.').unwrap_or(bare_host);
1584            if CERTIFICATE_NAME && !is_domain(named) && bare_host.parse::<Ipv4Addr>().is_err() {
1585                return false;
1586            }
1587            port
1588        };
1589        // The port is digits alone, the grammar's *DIGIT; empty included.
1590        if port.is_some_and(|digits| !digits.bytes().all(|byte| byte.is_ascii_digit())) {
1591            return false;
1592        }
1593    }
1594
1595    uri_alphabet(userinfo) && uri_alphabet(bare_host) && uri_alphabet(tail)
1596}
1597
1598/// Whether a segment holds only what a URI is written from: the unreserved
1599/// characters, the sub-delimiters, the two component delimiters that appear
1600/// inside a path or a query, and a percent sign only where two hexadecimal
1601/// digits follow it (RFC 3986 2).
1602fn uri_alphabet(segment: &str) -> bool {
1603    let mut bytes = segment.bytes();
1604    while let Some(byte) = bytes.next() {
1605        let permitted = byte.is_ascii_alphanumeric()
1606            || matches!(
1607                byte,
1608                b'-' | b'.'
1609                    | b'_'
1610                    | b'~'
1611                    | b':'
1612                    | b'/'
1613                    | b'?'
1614                    | b'@'
1615                    | b'!'
1616                    | b'$'
1617                    | b'&'
1618                    | b'\''
1619                    | b'('
1620                    | b')'
1621                    | b'*'
1622                    | b'+'
1623                    | b','
1624                    | b';'
1625                    | b'='
1626            );
1627        if byte == b'%' {
1628            let mut pair = [bytes.next(), bytes.next()].into_iter();
1629            if !pair.all(|digit| digit.is_some_and(|digit| digit.is_ascii_hexdigit())) {
1630                return false;
1631            }
1632        } else if !permitted {
1633            return false;
1634        }
1635    }
1636    true
1637}
1638
1639/// Whether text is a URI, the production that carries an optional fragment
1640/// after the rest of it (RFC 3986 3).
1641///
1642/// Certificate names (RFC5280 §4.2.1.6) additionally require their
1643/// scheme-specific part and host shape. A fragment does not make a
1644/// scheme-bearing URI relative. CPS pointers use the generic branch below.
1645pub(crate) fn is_uri(text: &str) -> bool {
1646    uri::<true>(text)
1647}
1648
1649/// `CPSuri` uses RFC3986 URI grammar (RFC5280 §4.2.1.4), independently of
1650/// §4.2.1.6's certificate-name host and scheme-specific-part requirements.
1651/// <https://www.rfc-editor.org/rfc/rfc5280.html#section-4.2.1.4>
1652pub(crate) fn is_cps_uri(text: &str) -> bool {
1653    uri::<false>(text)
1654}
1655
1656fn location_uri(
1657    parent: &Reader<'_>,
1658    element: &Element<'_>,
1659    text: &str,
1660    field: &'static str,
1661) -> Result<bool> {
1662    // Admit generic syntax plus scheme/authority/host scans through both the
1663    // producing Reader and aggregate domain before any of those scans.
1664    let work = u64::try_from(text.len())
1665        .ok()
1666        .and_then(|n| n.checked_mul(32))
1667        .and_then(|n| n.checked_add(256))
1668        .ok_or_else(|| {
1669            Error::resource(
1670                structured_asn1::memory::MemoryError::WorkExceeded,
1671                field,
1672                element.span().start,
1673            )
1674        })?;
1675    parent.charge(work).field(field)?;
1676    if !is_cps_uri(text) {
1677        return Ok(false);
1678    }
1679    if text
1680        .split_once(':')
1681        .is_some_and(|(scheme, _)| scheme.eq_ignore_ascii_case("ldap"))
1682    {
1683        return ldap::valid(parent, element, text, field);
1684    }
1685    Ok(known_location_authority_fits(text))
1686}
1687
1688fn known_location_authority_fits(text: &str) -> bool {
1689    let (scheme, rest) = text.split_once(':').expect("URI syntax checked");
1690    let ftp = scheme.eq_ignore_ascii_case("ftp");
1691    if !ftp && !scheme.eq_ignore_ascii_case("http") && !scheme.eq_ignore_ascii_case("https") {
1692        // RFC5280 §4.2.1.13 defines CRL locations separately from SAN §4.2.1.6.
1693        // Unknown schemes retain the validated RFC3986 §3 URI grammar, whose
1694        // hier-part admits path-empty (Appendix D.1 explicitly permits just
1695        // "scheme:"). We cannot impose another scheme's retrieval semantics.
1696        // <https://www.rfc-editor.org/rfc/rfc3986#section-3>
1697        return true;
1698    }
1699    // HTTP(S) requires //authority and forbids empty hosts; FTP uses the
1700    // mandatory Internet host syntax. LDAP's empty host is a separate contract.
1701    // RFC9110 §§4.2.1/4.2.2; RFC1738 §§3.1/3.2.
1702    // <https://www.rfc-editor.org/rfc/rfc9110#section-4.2.1>
1703    // <https://www.rfc-editor.org/rfc/rfc1738#section-3.1>
1704    let Some(after) = rest.strip_prefix("//") else {
1705        return false;
1706    };
1707    let end = after.find(['/', '?', '#']).unwrap_or(after.len());
1708    let authority = &after[..end];
1709    let host_port = authority
1710        .rsplit_once('@')
1711        .map_or(authority, |(_, host)| host);
1712    if host_port.starts_with('[') {
1713        // The shared grammar already checked the literal and following port.
1714        return true;
1715    }
1716    let host = host_port
1717        .split_once(':')
1718        .map_or(host_port, |(host, _)| host);
1719    if host.is_empty() {
1720        return false;
1721    }
1722    !ftp || is_domain(host.strip_suffix('.').unwrap_or(host)) || host.parse::<Ipv4Addr>().is_ok()
1723}
1724
1725fn uri<const CERTIFICATE_NAME: bool>(text: &str) -> bool {
1726    match text.split_once('#') {
1727        // The fragment is written from the same alphabet, and the delimiter
1728        // appears once: a second one is inside no production.
1729        Some((rest, fragment)) => absolute_uri::<CERTIFICATE_NAME>(rest) && uri_alphabet(fragment),
1730        None => absolute_uri::<CERTIFICATE_NAME>(text),
1731    }
1732}
1733
1734/// Whether mask bytes are leading one bits and nothing after them.
1735/// Whether bracketed host contents are one of the two IP-literal forms of
1736/// RFC 3986 3.2.2: an IPv6 address, or the versioned future form. A word
1737/// that merely fits the bracket alphabet is neither and names no host.
1738fn is_ip_literal(text: &str) -> bool {
1739    if let Some(rest) = text.strip_prefix(['v', 'V']) {
1740        // IPvFuture: "v" 1*HEXDIG "." 1*( unreserved / sub-delims / ":" ).
1741        let Some((version, suffix)) = rest.split_once('.') else {
1742            return false;
1743        };
1744        return !version.is_empty()
1745            && version.bytes().all(|byte| byte.is_ascii_hexdigit())
1746            && !suffix.is_empty()
1747            && suffix.bytes().all(|byte| {
1748                byte.is_ascii_alphanumeric()
1749                    || matches!(
1750                        byte,
1751                        b'-' | b'.'
1752                            | b'_'
1753                            | b'~'
1754                            | b':'
1755                            | b'!'
1756                            | b'$'
1757                            | b'&'
1758                            | b'\''
1759                            | b'('
1760                            | b')'
1761                            | b'*'
1762                            | b'+'
1763                            | b','
1764                            | b';'
1765                            | b'='
1766                    )
1767            });
1768    }
1769    text.parse::<Ipv6Addr>().is_ok()
1770}
1771
1772fn is_contiguous_mask(mask: &[u8]) -> bool {
1773    let mut still_ones = true;
1774    for &byte in mask {
1775        if still_ones {
1776            if byte == 0xff {
1777                continue;
1778            }
1779            // The one partial byte: leading ones, then zeros to its end.
1780            let ones = byte.leading_ones();
1781            let expected = if ones == 0 { 0 } else { 0xffu8 << (8 - ones) };
1782            if byte != expected {
1783                return false;
1784            }
1785            still_ones = false;
1786        } else if byte != 0 {
1787            return false;
1788        }
1789    }
1790    true
1791}
1792
1793fn text<'a>(
1794    parent: &Reader<'a>,
1795    contents: &'a [u8],
1796    field: &'static str,
1797    element: &Element<'a>,
1798) -> Result<&'a str> {
1799    // The scans behind this text, ASCII, grammar and bounds alike, walk
1800    // every content byte: they are charged to the shared work budget, so a
1801    // caller bounding work sees them on the same meter the reads use.
1802    parent.charge(contents.len() as u64).field(field)?;
1803    // An empty address, domain or location satisfies the shape while naming
1804    // nothing, and a critical alternative-name list carrying only one of
1805    // these would count as having supplied an identity.
1806    if contents.is_empty() {
1807        return Err(Error::new(
1808            ErrorCode::MalformedValue,
1809            field,
1810            element.span().start,
1811        ));
1812    }
1813    if !contents.is_ascii() {
1814        // The schema's alphabet is ASCII, and a byte outside it is a name
1815        // written in an encoding the field does not have. Guessing at one
1816        // would put a domain or an address in the report that the certificate
1817        // does not carry.
1818        return Err(Error::new(
1819            ErrorCode::MalformedValue,
1820            field,
1821            element.span().start,
1822        ));
1823    }
1824    core::str::from_utf8(contents)
1825        .map_err(|_| Error::new(ErrorCode::MalformedValue, field, element.span().start))
1826}
1827
1828#[cfg(test)]
1829mod tests;