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