Skip to main content

structured_pkix/
name.rs

1//! Distinguished names, kept as they were encoded.
2//!
3//! A name has two jobs here and they pull in opposite directions. One is
4//! identity: an issuer name in one certificate has to be recognized as the
5//! subject name of another, or no path is ever built. The other is
6//! explanation: somebody has to read who this is.
7//!
8//! They are kept apart. Identity is the bytes, and the comparison below is the
9//! profile's rule over the encoded values. Explanation is [`Attribute::text`],
10//! which decodes for a person to read and is never what two names are compared
11//! on. A parser that folded the two would let a name that renders identically
12//! stand in for the one it imitates.
13
14use crate::error::{Error, ErrorCode, Located, Result};
15use crate::oid;
16#[cfg(test)]
17use alloc::vec::Vec;
18use structured_asn1::memory::{Memory, OwnedString, OwnedVec, Work};
19use structured_asn1::{Class, Element, Oid, ScopedReader as Reader, Tag, universal};
20use unicode_bidi::BidiClass;
21
22mod fold;
23mod normalize;
24mod prepared;
25mod render;
26pub(crate) mod segments;
27mod selector;
28pub use selector::Selector;
29pub(crate) use selector::attribute_type_syntax_fits;
30pub(crate) use selector::syntax_fits as dn_syntax_fits;
31mod unassigned;
32
33/// A distinguished name, as the sequence of parts the certificate holds.
34#[derive(Debug)]
35pub struct Name<'a> {
36    der: &'a [u8],
37    parts: OwnedVec<RelativeName<'a>>,
38    offset: u64,
39    field: &'static str,
40}
41
42/// One part of a name: usually one attribute, occasionally several that are
43/// unordered among themselves.
44#[derive(Debug)]
45pub struct RelativeName<'a> {
46    attributes: OwnedVec<Attribute<'a>>,
47}
48
49/// One attribute of a name: what kind of thing it is, and the value.
50#[derive(Debug, Clone, Copy)]
51pub struct Attribute<'a> {
52    kind: Oid<'a>,
53    value: AttributeValue<'a>,
54}
55
56/// The value of an attribute, as it was encoded.
57#[derive(Debug, Clone, Copy)]
58pub struct AttributeValue<'a> {
59    tag: Tag,
60    contents: &'a [u8],
61    der: &'a [u8],
62    offset: u64,
63    // Original container depth for deferred constructed-string preparation.
64    depth: u32,
65}
66
67/// Which string encoding a value was written in, where it is a string at all.
68#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69#[non_exhaustive]
70pub enum Encoding {
71    /// `UTF8String`.
72    Utf8,
73    /// `PrintableString`, a restricted set of ASCII.
74    Printable,
75    /// `IA5String`, ASCII.
76    Ia5,
77    /// `VisibleString`, printable ASCII.
78    Visible,
79    /// `NumericString`, digits and spaces.
80    Numeric,
81    /// `TeletexString`, from before the others, and read here as an
82    /// approximation.
83    Teletex,
84    /// `BMPString`, sixteen bits a character.
85    Bmp,
86    /// `UniversalString`, thirty-two bits a character.
87    Universal,
88    /// Not a string type this crate reads as text.
89    Other,
90}
91
92impl<'a> Name<'a> {
93    /// The name exactly as it was encoded, which is what identity is.
94    #[must_use]
95    pub fn as_bytes(&self) -> &'a [u8] {
96        self.der
97    }
98
99    /// The parts, outermost first, as the certificate wrote them.
100    #[must_use]
101    pub fn parts(&self) -> &[RelativeName<'a>] {
102        &self.parts
103    }
104
105    /// Whether the name has no parts at all, which a certificate is allowed to
106    /// do when the subject is named somewhere else instead.
107    #[must_use]
108    pub fn is_empty(&self) -> bool {
109        self.parts.is_empty()
110    }
111
112    /// Copies descriptors into the current domain; document bytes remain borrowed.
113    ///
114    /// # Errors
115    /// Returns a located resource error before unfunded copying or allocation.
116    pub fn try_copy(&self, execution: &Memory) -> Result<Self> {
117        let mut parts = execution.vector();
118        for part in &self.parts {
119            execution
120                .charge_work(1)
121                .map_err(|reason| Error::resource(reason, self.field, self.offset))?;
122            parts
123                .try_push(part.try_copy(execution)?)
124                .map_err(|reason| Error::resource(reason, self.field, self.offset))?;
125        }
126        Ok(Self {
127            der: self.der,
128            parts,
129            offset: self.offset,
130            field: self.field,
131        })
132    }
133
134    /// Every attribute, in the order the name holds them.
135    /// The caller admits its own traversal of this borrowed view.
136    pub fn attributes(&self) -> impl Iterator<Item = &Attribute<'a>> {
137        self.parts.iter().flat_map(RelativeName::attributes)
138    }
139
140    /// The attributes of one kind, in order. Several is not an error: a name
141    /// can carry more than one organizational unit, and which one matters is
142    /// not this layer's decision.
143    /// Each search step is admitted in the current execution domain. A refusal
144    /// is yielded once, after which repeated `next()` calls do no further work.
145    /// Returned references borrow the name, independently of the budget borrow.
146    pub fn attributes_of<'n, 'q, W: structured_asn1::memory::Work>(
147        &'n self,
148        kind: Oid<'q>,
149        execution: &'q W,
150    ) -> impl Iterator<Item = Result<&'n Attribute<'a>>> + 'q
151    where
152        'n: 'q,
153    {
154        let mut attributes = self.attributes();
155        let mut failed = false;
156        core::iter::from_fn(move || {
157            if failed {
158                return None;
159            }
160            for attribute in attributes.by_ref() {
161                let work = 1 + attribute.kind.as_bytes().len().max(kind.as_bytes().len()) as u64;
162                if let Err(reason) = execution.charge_work(work) {
163                    failed = true;
164                    return Some(Err(self.resource(reason)));
165                }
166                if attribute.kind == kind {
167                    return Some(Ok(attribute));
168                }
169            }
170            None
171        })
172    }
173
174    /// The first attribute of one kind.
175    pub fn attribute<W: structured_asn1::memory::Work>(
176        &self,
177        kind: Oid<'_>,
178        execution: &W,
179    ) -> Result<Option<&Attribute<'a>>> {
180        self.attributes_of(kind, execution).next().transpose()
181    }
182
183    /// The common name, which describes the subject and does not identify it.
184    pub fn common_name<W: structured_asn1::memory::Work>(
185        &self,
186        execution: &W,
187    ) -> Result<Option<&Attribute<'a>>> {
188        self.attribute(oid::COMMON_NAME, execution)
189    }
190
191    /// The organization identifier, which is how a legal entity is identified
192    /// rather than described, and is the attribute an attribution decision may
193    /// rest on where the common name is not.
194    pub fn organization_identifier<W: structured_asn1::memory::Work>(
195        &self,
196        execution: &W,
197    ) -> Result<Option<&Attribute<'a>>> {
198        self.attribute(oid::ORGANIZATION_IDENTIFIER, execution)
199    }
200
201    /// Whether two names are the same name, by the profile's rule: the same
202    /// parts in the same order, each holding the same attributes, with strings
203    /// compared under their declared equality rule.
204    ///
205    /// Directory strings are compared after the preparation the profile requires
206    /// (RFC 5280 7.1, over RFC 4518): the characters that carry no meaning
207    /// are dropped, case is folded in full, the value is normalized to form
208    /// KC, and runs of space count as one between words and none at the
209    /// edges. Unsupported types and nonconforming stored values retain their
210    /// encoded identity; that preservation does not validate an LDAP assertion.
211    /// Valid supported values use the schema's size bounds and the current
212    /// work/storage budget, independently of display truncation.
213    /// IA5 emailAddress and domainComponent values instead use their exact
214    /// ASCII case-insensitive rules without whitespace compression; the DN
215    /// emailAddress rule covers the whole value, unlike rfc822Name mailboxes.
216    /// Pseudonym uses caseExactMatch (RFC2985 §5.2.9): the same preparation
217    /// and insignificant-space handling, with case preserved.
218    ///
219    /// # Errors
220    /// Returns a located resource error before unfunded comparison work.
221    pub fn matches<W: structured_asn1::memory::Work>(
222        &self,
223        other: &Name<'_>,
224        execution: &W,
225    ) -> Result<bool> {
226        self.matches_with(other, execution, None)
227    }
228
229    /// Compares retained names under the current query reader's execution.
230    ///
231    /// Names may come from independently parsed certificates. Their storage
232    /// stays on its original account; normalization/comparison scratch and
233    /// deferred string decoding use the query's local and aggregate ceilings.
234    /// This is a comparison, not permission to reparse a foreign certificate.
235    pub fn matches_in(&self, other: &Name<'_>, parent: &Reader<'_, '_>) -> Result<bool> {
236        self.matches_with(other, &parent.execution(), Some(parent))
237    }
238
239    pub(crate) fn matches_with<W: structured_asn1::memory::Work>(
240        &self,
241        other: &Name<'_>,
242        execution: &W,
243        parent: Option<&Reader<'_, '_>>,
244    ) -> Result<bool> {
245        if self.parts.len() != other.parts.len() {
246            return Ok(false);
247        }
248        // Identical original encodings have the same retained wire identity.
249        // This fast path does not validate a nonconforming stored value.
250        if self.der.len() == other.der.len() {
251            execution
252                .charge_work(self.der.len() as u64)
253                .map_err(|reason| self.resource(reason))?;
254            if self.der == other.der {
255                return Ok(true);
256            }
257        }
258        Ok(self.order_with(other, execution, parent)? == core::cmp::Ordering::Equal)
259    }
260
261    pub(crate) fn offset(&self) -> u64 {
262        self.offset
263    }
264
265    /// Compare two original names with independently supplied source owners.
266    /// Each Parser must be the actual producing parser of its corresponding
267    /// name; retained Name alone deliberately carries no parser ownership.
268    /// Source ceilings and each Work's current operation fund preparation,
269    /// sorting scratch and comparison. The per-RDN semantic kernel is shared
270    /// with retained matching; neither name is re-encoded or normalized twice.
271    pub fn matches_between<
272        'b,
273        L: structured_asn1::memory::Work,
274        R: structured_asn1::memory::Work,
275    >(
276        &self,
277        other: &Name<'b>,
278        left_source: (&L, &'a structured_asn1::Parser),
279        right_source: (&R, &'b structured_asn1::Parser),
280    ) -> Result<bool> {
281        let left_work = left_source.0.execution_for(left_source.1);
282        let right_work = right_source.0.execution_for(right_source.1);
283        let left_source = (&left_work, left_source.1);
284        let right_source = (&right_work, right_source.1);
285        left_work.charge_work(1).map_err(|e| self.resource(e))?;
286        right_work.charge_work(1).map_err(|e| other.resource(e))?;
287        if self.parts.len() != other.parts.len() {
288            return Ok(false);
289        }
290        if self.der.len() == other.der.len() {
291            left_work
292                .charge_work(self.der.len() as u64)
293                .map_err(|e| self.resource(e))?;
294            right_work
295                .charge_work(other.der.len() as u64)
296                .map_err(|e| other.resource(e))?;
297            if self.der == other.der {
298                return Ok(true);
299            }
300        }
301        for index in 0..self.parts.len() {
302            left_work.charge_work(1).map_err(|e| self.resource(e))?;
303            right_work.charge_work(1).map_err(|e| other.resource(e))?;
304            if prepared::compare_parts_between(
305                &self.parts[index],
306                &other.parts[index],
307                left_source,
308                right_source,
309            )? != core::cmp::Ordering::Equal
310            {
311                return Ok(false);
312            }
313        }
314        Ok(true)
315    }
316
317    pub(crate) fn field(&self) -> &'static str {
318        self.field
319    }
320
321    pub(crate) fn resource(&self, reason: structured_asn1::memory::MemoryError) -> Error {
322        Error::resource(reason, self.field, self.offset)
323    }
324
325    /// A total order over names that calls two of them equal exactly when
326    /// [`Name::matches`] does.
327    ///
328    /// It exists so that a set of names can be searched rather than walked. A
329    /// caller holding many certificates and asking which one a name belongs to
330    /// otherwise compares against every one of them, and comparing names is
331    /// where the profile's folding lives; ordering them once turns that into a
332    /// search. Answering the same question two ways is only safe because this
333    /// is the same comparison: it is built from the ordering the multiset
334    /// matching already uses, not from a second reading of what identity is.
335    ///
336    /// Preparation and comparison use the current execution domain. Unicode
337    /// scratch storage is released when this comparison returns.
338    ///
339    /// # Errors
340    /// Returns a located resource error before unfunded preparation or comparison.
341    pub fn order<W: structured_asn1::memory::Work>(
342        &self,
343        other: &Name<'_>,
344        execution: &W,
345    ) -> Result<core::cmp::Ordering> {
346        self.order_with(other, execution, None)
347    }
348
349    fn order_with<W: structured_asn1::memory::Work>(
350        &self,
351        other: &Name<'_>,
352        execution: &W,
353        parent: Option<&Reader<'_, '_>>,
354    ) -> Result<core::cmp::Ordering> {
355        let order = self.parts.len().cmp(&other.parts.len());
356        if order != core::cmp::Ordering::Equal {
357            return Ok(order);
358        }
359        // Stop at the first distinguishing RDN: unrelated tail values must
360        // not pay for normalization or scratch storage that cannot affect order.
361        for (left, right) in self.parts.iter().zip(&other.parts) {
362            execution
363                .charge_work(1)
364                .map_err(|reason| Error::resource(reason, self.field, self.offset))?;
365            let order = prepared::compare_parts_in(left, right, execution, parent)?;
366            if order != core::cmp::Ordering::Equal {
367                return Ok(order);
368            }
369        }
370        Ok(core::cmp::Ordering::Equal)
371    }
372
373    /// Prepares this name once for repeated comparisons.
374    ///
375    /// Encoded values remain borrowed. Sorted descriptors and any Unicode
376    /// canonical text retain their storage charge until this result is dropped.
377    /// Comparisons subsequently use the execution domain passed to them.
378    ///
379    /// # Errors
380    /// Returns a located resource error if preparation cannot be financed.
381    pub fn ordered<W: structured_asn1::memory::Work>(
382        &self,
383        execution: &W,
384    ) -> Result<Ordered<'a, W>> {
385        Ok(Ordered {
386            prepared: prepared::PreparedName::of(&self.parts, execution)?,
387            offset: self.offset,
388        })
389    }
390
391    /// Prepares a cache inside the current producing operation's work ceilings.
392    ///
393    /// Original document values and their retained nesting depths remain intact.
394    /// Deferred BER strings use the supplied reader's session/account instead
395    /// of a new Parser. Cache storage retains its current account; its work
396    /// owner borrows the operation independently of the document's lifetime.
397    /// This is the same semantic order as [`Name::ordered`], with scoped funding.
398    pub fn ordered_in<'p>(
399        &self,
400        parent: &'p Reader<'_, '_>,
401    ) -> Result<Ordered<'a, structured_asn1::memory::Execution<'p>>> {
402        Ok(Ordered {
403            prepared: prepared::PreparedName::of_in(
404                &self.parts,
405                &parent.execution(),
406                Some(parent),
407            )?,
408            offset: self.offset,
409        })
410    }
411
412    /// Whether this name sits below another one: the parts of the other, in
413    /// order, from the top down, and anything at all after them.
414    ///
415    /// This is the containment a name-constraint subtree asks about, where
416    /// the base "specifies a subtree of the X.500 directory tree" and a name
417    /// is within it when it begins with the base (RFC 5280 4.2.1.10). A base
418    /// of no parts is the root of the tree and holds every name. Parts are
419    /// compared by the rule identity uses, so a name sits below itself.
420    ///
421    /// # Errors
422    /// Returns a located resource error before unfunded prefix preparation.
423    pub fn starts_with<W: structured_asn1::memory::Work>(
424        &self,
425        base: &Name<'_>,
426        execution: &W,
427    ) -> Result<bool> {
428        if base.parts.len() > self.parts.len() {
429            return Ok(false);
430        }
431        for (left, right) in self.parts.iter().zip(&base.parts) {
432            execution
433                .charge_work(1)
434                .map_err(|reason| self.resource(reason))?;
435            let order = prepared::compare_parts(left, right, execution)?;
436            if order != core::cmp::Ordering::Equal {
437                return Ok(false);
438            }
439        }
440        Ok(true)
441    }
442}
443
444/// A prepared name with independently accounted descriptors and canonical text.
445///
446/// Encoded values borrow the source document. Storage charges survive the
447/// parser; every comparison is paid by its explicit current execution domain.
448#[derive(Debug)]
449pub struct Ordered<'a, W: structured_asn1::memory::Work = Memory> {
450    prepared: prepared::PreparedName<'a, W>,
451    offset: u64,
452}
453
454impl<W: structured_asn1::memory::Work> Ordered<'_, W> {
455    /// The same order [`Name::order`] gives, using already prepared values.
456    ///
457    /// # Errors
458    /// Returns a located resource error before unfunded comparison work.
459    pub fn order<V: structured_asn1::memory::Work, E: structured_asn1::memory::Work>(
460        &self,
461        other: &Ordered<'_, V>,
462        execution: &E,
463    ) -> Result<core::cmp::Ordering> {
464        self.prepared
465            .order(&other.prepared, execution)
466            .map_err(|reason| Error::resource(reason, "name", self.offset))
467    }
468}
469
470/// Match virtual names without inventing an encoding for the appended RDN.
471/// CRL distribution names append relative fragments to their own issuer bases.
472/// The existing prepared-part core owns all attribute and SET semantics.
473pub(crate) fn matches_composed<const ENCODED: bool>(
474    left: &Name<'_>,
475    left_suffix: Option<&RelativeName<'_>>,
476    right: &Name<'_>,
477    right_suffix: Option<&RelativeName<'_>>,
478    execution: &impl structured_asn1::memory::Work,
479) -> Result<bool> {
480    if left_suffix.is_none() && right_suffix.is_none() {
481        if ENCODED {
482            return encoded_bytes_match(left.der, right.der, left, execution);
483        }
484        return left.matches(right, execution);
485    }
486    if ENCODED && let (Some(left_part), Some(right_part)) = (left_suffix, right_suffix) {
487        // The original DP fields encode the relative RDN, not their issuer
488        // bases. RFC5280 §5.2.5 fixes that RDN encoding; §6.3.3(b)(1) retains
489        // ordinary issuer-name equality for the separately encoded bases.
490        if !left.matches(right, execution)? {
491            return Ok(false);
492        }
493        return encoded_parts_match(left_part, right_part, left, execution);
494    }
495    let left_len = left
496        .parts
497        .len()
498        .checked_add(usize::from(left_suffix.is_some()))
499        .ok_or_else(|| left.resource(structured_asn1::memory::MemoryError::WorkExceeded))?;
500    let right_len = right
501        .parts
502        .len()
503        .checked_add(usize::from(right_suffix.is_some()))
504        .ok_or_else(|| right.resource(structured_asn1::memory::MemoryError::WorkExceeded))?;
505    if left_len != right_len {
506        return Ok(false);
507    }
508    for index in 0..left_len {
509        execution
510            .charge_work(1)
511            .map_err(|reason| left.resource(reason))?;
512        let left_part = if index < left.parts.len() {
513            &left.parts[index]
514        } else {
515            left_suffix.expect("suffix was counted")
516        };
517        let right_part = if index < right.parts.len() {
518            &right.parts[index]
519        } else {
520            right_suffix.expect("suffix was counted")
521        };
522        let matches = if ENCODED {
523            encoded_parts_match(left_part, right_part, left, execution)?
524        } else {
525            prepared::compare_parts(left_part, right_part, execution)? == core::cmp::Ordering::Equal
526        };
527        if !matches {
528            return Ok(false);
529        }
530    }
531    Ok(true)
532}
533
534fn encoded_bytes_match(
535    left: &[u8],
536    right: &[u8],
537    source: &Name<'_>,
538    execution: &impl structured_asn1::memory::Work,
539) -> Result<bool> {
540    let work = u64::try_from(left.len().max(right.len()))
541        .ok()
542        .and_then(|n| n.checked_mul(2))
543        .and_then(|n| n.checked_add(1))
544        .ok_or_else(|| source.resource(structured_asn1::memory::MemoryError::WorkExceeded))?;
545    execution
546        .charge_work(work)
547        .map_err(|reason| source.resource(reason))?;
548    Ok(left == right)
549}
550
551fn encoded_parts_match(
552    left: &RelativeName<'_>,
553    right: &RelativeName<'_>,
554    source: &Name<'_>,
555    execution: &impl structured_asn1::memory::Work,
556) -> Result<bool> {
557    if left.attributes.len() != right.attributes.len() {
558        return Ok(false);
559    }
560    // Original DER SET order and canonical tag/length encoding make exact OID
561    // plus complete value TLVs sufficient for exact RDN encoding, without an
562    // encode/reparse bridge. No attribute preparation or reordering occurs.
563    for (left, right) in left.attributes.iter().zip(right.attributes.iter()) {
564        execution
565            .charge_work(1)
566            .map_err(|reason| source.resource(reason))?;
567        if !encoded_bytes_match(
568            left.kind.as_bytes(),
569            right.kind.as_bytes(),
570            source,
571            execution,
572        )? || !encoded_bytes_match(left.value.der, right.value.der, source, execution)?
573        {
574            return Ok(false);
575        }
576    }
577    Ok(true)
578}
579
580impl<'a> RelativeName<'a> {
581    /// The attributes of this part.
582    #[must_use]
583    pub fn attributes(&self) -> &[Attribute<'a>] {
584        &self.attributes
585    }
586
587    /// Copies borrowed descriptors into independently accounted storage.
588    ///
589    /// # Errors
590    /// Returns a located resource error before unfunded copying or allocation.
591    pub fn try_copy(&self, execution: &Memory) -> Result<Self> {
592        let mut attributes = execution.vector();
593        attributes
594            .try_extend_from_slice(&self.attributes)
595            .map_err(|reason| {
596                Error::resource(reason, "name.relative", self.attributes[0].value.offset)
597            })?;
598        Ok(Self { attributes })
599    }
600
601    /// Whether two parts hold the same attributes. They are a set, so order
602    /// within a part is not part of what they say, and the correspondence is
603    /// one to one: a part written with a duplicate does not match a part
604    /// without one.
605    ///
606    /// Both sides are ordered by a total order that agrees with attribute
607    /// equality, then compared in step. Ordering is streamed: nothing is
608    /// materialized, so a part holding a value the element limit permits, up
609    /// to four gibibytes, costs a list of references and not a copy of the
610    /// value. The alternative, one canonical key per attribute, is a copy the
611    /// size of the name; scanning one list per attribute of the other is work
612    /// proportional to the square of a count the parse budget never sees.
613    #[must_use]
614    #[cfg(test)]
615    fn matches(&self, other: &RelativeName<'_>) -> bool {
616        if self.attributes.len() != other.attributes.len() {
617            return false;
618        }
619        // One attribute is the common case, and it needs no ordering at all.
620        if let ([left], [right]) = (&self.attributes[..], &other.attributes[..]) {
621            return left.matches(right);
622        }
623        let mut ours: Vec<&Attribute<'a>> = self.attributes.iter().collect();
624        let mut theirs: Vec<&Attribute<'_>> = other.attributes.iter().collect();
625        ours.sort_unstable_by(|left, right| left.order(right));
626        theirs.sort_unstable_by(|left, right| left.order(right));
627        ours.iter()
628            .zip(&theirs)
629            .all(|(left, right)| left.matches(right))
630    }
631
632    /// A total order over parts that calls two of them equal exactly when
633    /// [`RelativeName::matches`] does, built the same way: both sides ordered
634    /// by the attribute order, then compared in step.
635    #[cfg(test)]
636    fn order(&self, other: &RelativeName<'_>) -> core::cmp::Ordering {
637        self.attributes
638            .len()
639            .cmp(&other.attributes.len())
640            .then_with(|| {
641                if let ([left], [right]) = (&self.attributes[..], &other.attributes[..]) {
642                    return left.order(right);
643                }
644                let mut ours: Vec<&Attribute<'a>> = self.attributes.iter().collect();
645                let mut theirs: Vec<&Attribute<'_>> = other.attributes.iter().collect();
646                ours.sort_unstable_by(|left, right| left.order(right));
647                theirs.sort_unstable_by(|left, right| left.order(right));
648                ours.iter()
649                    .zip(&theirs)
650                    .map(|(left, right)| left.order(right))
651                    .find(|ordering| ordering.is_ne())
652                    .unwrap_or(core::cmp::Ordering::Equal)
653            })
654    }
655}
656
657/// The value syntax of an attribute type this crate names, or `None` for a
658/// kind it does not.
659fn syntax_of(kind: Oid<'_>) -> Option<Syntax> {
660    // The X.520 attributes whose value is a DirectoryString.
661    const DIRECTORY: &[Oid<'static>] = &[
662        oid::COMMON_NAME,
663        oid::SURNAME,
664        oid::LOCALITY,
665        oid::STATE_OR_PROVINCE,
666        oid::STREET_ADDRESS,
667        oid::ORGANIZATION,
668        oid::ORGANIZATIONAL_UNIT,
669        oid::TITLE,
670        oid::GIVEN_NAME,
671        oid::INITIALS,
672        oid::GENERATION_QUALIFIER,
673        oid::PSEUDONYM,
674        oid::ORGANIZATION_IDENTIFIER,
675        oid::USER_ID,
676    ];
677    if DIRECTORY.contains(&kind) {
678        Some(Syntax::Directory)
679    } else if kind == oid::COUNTRY || kind == oid::SERIAL_NUMBER || kind == oid::DN_QUALIFIER {
680        // X.520 gives these a PrintableString.
681        Some(Syntax::Printable)
682    } else if kind == oid::EMAIL_ADDRESS || kind == oid::DOMAIN_COMPONENT {
683        // And these an IA5String.
684        Some(Syntax::Ia5)
685    } else {
686        None
687    }
688}
689
690/// The value syntaxes the named attribute types take.
691#[derive(Clone, Copy)]
692enum Syntax {
693    /// The `DirectoryString` choice of X.520.
694    Directory,
695    /// A `PrintableString`.
696    Printable,
697    /// An `IA5String`.
698    Ia5,
699}
700
701impl Syntax {
702    fn permits(self, encoding: Encoding) -> bool {
703        match self {
704            Self::Directory => matches!(
705                encoding,
706                Encoding::Utf8
707                    | Encoding::Printable
708                    | Encoding::Teletex
709                    | Encoding::Bmp
710                    | Encoding::Universal
711            ),
712            Self::Printable => encoding == Encoding::Printable,
713            Self::Ia5 => encoding == Encoding::Ia5,
714        }
715    }
716}
717
718/// Whether an attribute type's schema permits folding a value in one string
719/// encoding. A value outside its type's choices takes part in identity as
720/// bytes only: text folded out of an encoding the type does not have would
721/// let a malformed value match a genuine one it never equals. And a kind
722/// this crate does not name may define case-sensitive equality, so folding
723/// under a rule the type may not have would match names that differ: its
724/// values too equal themselves and nothing else.
725fn permits(kind: Oid<'_>, encoding: Encoding) -> bool {
726    syntax_of(kind).is_some_and(|syntax| syntax.permits(encoding))
727}
728
729/// The size an attribute kind's schema puts on its value, in characters:
730/// the country is exactly two, and the rest follow the upper bounds of
731/// RFC 5280 appendix A. A kind the appendix does not bound stays unbounded.
732fn size_bounds(kind: Oid<'_>) -> (usize, usize) {
733    if kind == oid::COUNTRY {
734        (2, 2)
735    } else if kind == oid::COMMON_NAME
736        || kind == oid::ORGANIZATION
737        || kind == oid::ORGANIZATIONAL_UNIT
738        || kind == oid::TITLE
739        || kind == oid::SERIAL_NUMBER
740    {
741        (1, 64)
742    } else if kind == oid::LOCALITY || kind == oid::STATE_OR_PROVINCE {
743        (1, 128)
744    } else if kind == oid::EMAIL_ADDRESS {
745        (1, 255)
746    } else if kind == oid::PSEUDONYM {
747        (1, 128)
748    } else if kind == oid::STREET_ADDRESS {
749        // ub-street-address (X.520).
750        (1, 128)
751    } else if kind == oid::SURNAME
752        || kind == oid::GIVEN_NAME
753        || kind == oid::INITIALS
754        || kind == oid::GENERATION_QUALIFIER
755    {
756        // The name components share ub-name, 32768.
757        (1, 32768)
758    } else {
759        (1, usize::MAX)
760    }
761}
762
763/// Whether an attribute of a kind this crate names is written inside that
764/// kind's syntax: the encoding is one its type permits, the value decodes
765/// under it, and the decoded text fits the size the schema gives the kind.
766/// An unknown kind answers yes, having no schema here to hold it to; where
767/// an identity must have been read entirely, this is the question each
768/// attribute is asked.
769pub(crate) fn well_formed(attribute: &Attribute<'_>) -> bool {
770    inside_syntax(attribute, Asking::Shape, |_| true)
771}
772
773/// Schema validation of retained string bytes, admitted before scanning.
774pub(crate) fn well_formed_in(
775    attribute: &Attribute<'_>,
776    parent: &Reader<'_, '_>,
777    field: &'static str,
778) -> Result<bool> {
779    let work = (attribute.kind.as_bytes().len() as u64)
780        .checked_mul(64)
781        .and_then(|kinds| {
782            (attribute.value.contents.len() as u64)
783                .checked_mul(2)
784                .and_then(|contents| kinds.checked_add(contents))
785        })
786        .and_then(|work| work.checked_add(1))
787        .ok_or_else(|| {
788            Error::resource(
789                structured_asn1::memory::MemoryError::WorkExceeded,
790                field,
791                attribute.value.offset,
792            )
793        })?;
794    parent.charge(work).field(field)?;
795    let value = attribute.value;
796    if value.tag.is_constructed()
797        && value.tag.class() == Class::Universal
798        && value.tag.number().is_some_and(segmented_universal_type)
799        && value.tag.number() != Some(universal::BIT_STRING)
800        && syntax_of(attribute.kind).is_some()
801    {
802        // X.690 §8.23: the BER restricted-string spelling has OCTET STRING
803        // fragments. Validate the joined logical value under the same schema
804        // rather than treating its constructed tag as unsupported text.
805        // https://www.itu.int/rec/T-REC-X.690-202102-I/en
806        debug_assert!(value.depth > 0);
807        let mut reader = parent
808            .parser()
809            .read_nested_query_in(
810                parent,
811                value.der,
812                value.offset,
813                structured_asn1::Encoding::Ber,
814                value.depth - 1,
815            )
816            .field(field)?;
817        let element = reader
818            .next_element()
819            .field(field)?
820            .expect("retained complete attribute value");
821        let execution = parent.execution();
822        let mut contents = execution.vector();
823        segments::walk_in(
824            &reader,
825            &element,
826            field,
827            false,
828            Some(&mut contents),
829            &execution,
830        )?;
831        parent
832            .charge((contents.len() as u64).checked_mul(2).ok_or_else(|| {
833                Error::resource(
834                    structured_asn1::memory::MemoryError::WorkExceeded,
835                    field,
836                    value.offset,
837                )
838            })?)
839            .field(field)?;
840        return Ok(well_formed(&Attribute {
841            kind: attribute.kind,
842            value: AttributeValue {
843                tag: Tag::primitive(value.tag.number().expect("universal string")),
844                contents: &contents,
845                ..value
846            },
847        }));
848    }
849    Ok(well_formed(attribute))
850}
851
852/// Display text may truncate at this bound. Identity preparation instead uses
853/// the schema bound and the operation's admitted work/storage: RFC5280 §7.1
854/// requires `StringPrep` for valid `UTF8String`/`PrintableString` values.
855/// A display limit must never silently substitute binary equality.
856/// <https://www.rfc-editor.org/rfc/rfc5280.html#section-7.1>
857const DISPLAY_CEILING: usize = 32_768;
858
859/// Whether the preparation can run on a value at all.
860///
861/// Unsupported encodings, malformed stored values and prohibited code points
862/// retain only their encoded identity. That preservation does not validate
863/// an LDAP assertion. Valid values have no display-derived preparation limit.
864#[cfg(test)]
865fn preparable(attribute: &Attribute<'_>) -> bool {
866    if !inside_syntax(attribute, Asking::Folding, |c| !prohibited(c)) {
867        return false;
868    }
869    // The direction step reads the value the normalizing step leaves, which
870    // is the order the profile puts them in (RFC 4518 2, steps 3 then 5).
871    // The order decides the answer: a compatibility form can expand into
872    // letters of a direction the character it replaces did not have, U+2121
873    // into TEL, so a value read before normalizing can look right-to-left
874    // and be mixed once prepared. The walk is bounded by the step above,
875    // which already held the value to the size its schema gives it.
876    let mut direction = Direction::default();
877    for c in normalized(attribute.value.decoded_chars()) {
878        direction.see(c);
879    }
880    direction.settled()
881}
882
883/// The direction step of the preparation (RFC 4518 2.5, over RFC 3454 6),
884/// read as the value is scanned.
885///
886/// A value holding a right-to-left letter may hold no left-to-right one, and
887/// must begin and end with a right-to-left one. Failing either leaves the
888/// value unprepared, which here means compared as bytes.
889///
890/// The edges are decided on the characters that are still there when the
891/// preparation ends. The step that drops the spaces at the edges is part of
892/// the same preparation and comes after this one, so holding a value to an
893/// edge its own preparation deletes would refuse it over its spacing rather
894/// than over its direction.
895///
896/// The steps before this one decide what it sees, so it reads the value as
897/// they leave it: mapped and normalized. The mapping sends a set of
898/// characters to nothing, and the direction marks are among them: a value is
899/// written right to left because of its letters, not because it carries a
900/// mark saying so, and counting a mark the preparation deletes would refuse
901/// a name over a character that is not there by the time anything compares
902/// it. Normalizing matters here too, and in the other direction: a
903/// compatibility form can expand into letters that carry a direction the
904/// character it replaces did not have, U+2121 into TEL, so a value read
905/// before that step can look settled and be mixed after it.
906#[derive(Default)]
907struct Direction {
908    /// The first character that is not a space, and whether it is
909    /// right-to-left.
910    first: Option<bool>,
911    /// The same for the last one seen so far.
912    last: bool,
913    any_right: bool,
914    any_left: bool,
915}
916
917impl Direction {
918    fn see(&mut self, c: char) {
919        if mapped_to_nothing(c) || c.is_whitespace() {
920            return;
921        }
922        let class = unicode_bidi::bidi_class(c);
923        let right = matches!(class, BidiClass::R | BidiClass::AL);
924        if self.first.is_none() {
925            self.first = Some(right);
926        }
927        self.last = right;
928        self.any_right |= right;
929        self.any_left |= class == BidiClass::L;
930    }
931
932    fn settled(&self) -> bool {
933        !self.any_right || (!self.any_left && self.first == Some(true) && self.last)
934    }
935}
936
937/// Whether the profile prohibits a code point (RFC 4518 2.4).
938///
939/// Private use, the non-characters, and the replacement character a decoder
940/// writes when it could not read something: nobody agrees what any of them
941/// says, so nothing downstream can be defined over them. None of the three
942/// is touched by the mapping or the normalization, so asking about them
943/// before those steps is asking about the same characters they ask about.
944/// Surrogates are prohibited too and cannot arrive: nothing here produces
945/// one, because a character is a scalar value.
946///
947/// The step also prohibits the deprecated code points and the ones that
948/// change display properties, and none of those can reach it. The mapping
949/// sends the display controls to nothing two steps earlier, and the
950/// normalization takes the two deprecated tone marks to the ordinary marks
951/// they are equivalent to. Testing for them here would refuse a value the
952/// profile prepares, which is a name that stops matching itself.
953///
954/// The step's table of code points unassigned in Unicode 3.2 is applied,
955/// which costs the folded comparison for a character assigned since. That is
956/// the direction to lose in. The preparation is defined over one repertoire,
957/// and every step after this one is a table this crate reaches at its
958/// current version: a character the profile never saw carries a case folding
959/// and a compatibility decomposition today, so folding one makes two names
960/// equal by a rule the profile does not contain. Refusing it leaves the two
961/// compared as bytes, which reports a path as unproven; folding it can
962/// report an issuer as a subject it is not.
963fn prohibited(c: char) -> bool {
964    let point = u32::from(c);
965    unassigned::in_unicode_3_2(c)
966        || matches!(c,
967        // Private use (RFC 3454 C.3).
968        '\u{e000}'..='\u{f8ff}' | '\u{f0000}'..='\u{ffffd}' | '\u{100000}'..='\u{10fffd}'
969        // The non-characters of the arabic presentation forms (C.4).
970        | '\u{fdd0}'..='\u{fdef}'
971        // The replacement character.
972        | '\u{fffd}')
973        // The last two code points of every plane, which are the rest of C.4.
974        || point & 0xfffe == 0xfffe
975}
976
977/// Which of the two questions about a value is being asked.
978///
979/// They differ where a value can be kept without being read. A value whose
980/// shape its schema names is not refused, and it is what the two callers of
981/// this share; whether the preparation can then run on it is a narrower
982/// question, and answering no to it costs a folded comparison rather than a
983/// certificate.
984#[derive(Clone, Copy, PartialEq, Eq)]
985enum Asking {
986    /// Whether the value is written inside its kind's syntax at all. A no
987    /// here refuses the value where an identity must have been read whole.
988    Shape,
989    /// Whether the preparation the comparison runs can run on this value.
990    /// A no here leaves it compared as bytes.
991    Folding,
992}
993
994/// The shared reading of an attribute against its kind's schema, with a
995/// further test on each character the caller supplies.
996fn inside_syntax(
997    attribute: &Attribute<'_>,
998    asking: Asking,
999    mut keep: impl FnMut(char) -> bool,
1000) -> bool {
1001    match syntax_of(attribute.kind) {
1002        Some(syntax) => {
1003            let (least, schema) = size_bounds(attribute.kind);
1004            let most = schema;
1005            // The ceiling is checked on the encoded bytes first: no encoding
1006            // here spends fewer than one byte per character or more than
1007            // four, so a value whose bytes exceed four times the character
1008            // bound can never fit it. The saturation is deliberate: an
1009            // unbounded kind saturates the ceiling to the type's maximum,
1010            // and the streaming scan below still touches it one character
1011            // at a time without materializing a copy the size of the value.
1012            if attribute.value.contents().len() > most.saturating_mul(4) {
1013                return false;
1014            }
1015            if !syntax.permits(attribute.value.encoding()) {
1016                return false;
1017            }
1018            // TeletexString is T.61, and its repertoire is a negotiation this
1019            // crate does not implement: the bytes being there and inside the
1020            // size the schema gives the kind is what is checkable. Asking it
1021            // to decode as anything would call a legacy value malformed, and
1022            // where a name must be written inside its syntax that refuses a
1023            // certificate over an encoding the schema names. It is counted
1024            // in bytes here and not folded at all, which is the other half
1025            // of the same answer: a value nobody has agreed how to read
1026            // takes part in identity as its bytes.
1027            if attribute.value.encoding() == Encoding::Teletex && asking == Asking::Shape {
1028                return (least..=most).contains(&attribute.value.contents().len());
1029            }
1030            let country = attribute.kind == oid::COUNTRY;
1031            attribute
1032                .value
1033                // The country is a two-letter ISO 3166 code: digits and
1034                // punctuation fit the printable repertoire and the size
1035                // without naming any country. Case is left to folding.
1036                .validated_count(|c| (!country || c.is_ascii_alphabetic()) && keep(c))
1037                .is_some_and(|count| (least..=most).contains(&count))
1038        }
1039        None => true,
1040    }
1041}
1042
1043/// Whether the preparation this comparison runs sends a character to nothing.
1044///
1045/// The Map step of the string preparation the profile requires (RFC 5280 7.1,
1046/// which requires RFC 4518) sends soft hyphen, the grapheme joiner, the
1047/// variation selectors, the object replacement character, the zero width
1048/// space and "all other control code (e.g., Cc) points or code points with a
1049/// control function (e.g., Cf)" to nothing. The list below is the one that
1050/// section writes out. What it does NOT contain is tab, line feed, vertical
1051/// tab, form feed, carriage return and next line: those are mapped to SPACE,
1052/// which is why the whitespace fold beside this covers them.
1053fn mapped_to_nothing(c: char) -> bool {
1054    matches!(
1055        c,
1056        '\u{00ad}'
1057            | '\u{1806}'
1058            | '\u{034f}'
1059            | '\u{180b}'..='\u{180d}'
1060            | '\u{fe00}'..='\u{fe0f}'
1061            | '\u{fffc}'
1062            | '\u{0000}'..='\u{0008}'
1063            | '\u{000e}'..='\u{001f}'
1064            | '\u{007f}'..='\u{0084}'
1065            | '\u{0086}'..='\u{009f}'
1066            | '\u{06dd}'
1067            | '\u{070f}'
1068            | '\u{180e}'
1069            | '\u{200b}'..='\u{200f}'
1070            | '\u{202a}'..='\u{202e}'
1071            | '\u{2060}'..='\u{2063}'
1072            | '\u{206a}'..='\u{206f}'
1073            | '\u{feff}'
1074            | '\u{fff9}'..='\u{fffb}'
1075            | '\u{1d173}'..='\u{1d17a}'
1076            | '\u{e0001}'
1077            | '\u{e0020}'..='\u{e007f}'
1078    )
1079}
1080
1081/// Streams the canonical form of a character stream: the preparation's steps
1082/// in the order it gives them, each one an adapter over the one before, so
1083/// that no step ever holds the value.
1084///
1085/// Map (RFC 4518 2.2), then Normalize (2.3), then the insignificant space
1086/// handling (2.6.1). The order is the profile's and it is not free to
1087/// change: normalization can produce a space of its own, out of a character
1088/// whose compatibility form begins with one, and a space it produces has to
1089/// reach the step that decides which spaces count.
1090///
1091/// The mapping runs on both sides of the normalization, which is what makes
1092/// it the profile's mapping rather than a case fold. The table the profile
1093/// names is B.2 of RFC 3454, a fold that carries the compatibility mappings
1094/// with it: `2102; 0063; Additional folding` takes the double-struck
1095/// capital C straight to a lowercase c. This crate's fold is the published
1096/// case folding, which leaves that character alone, and the normalization
1097/// after it produces a capital. Folding what the normalization produces
1098/// arrives where the profile's table does, and costs a second walk of a
1099/// stream already bounded.
1100///
1101/// What bounds the work is upstream of here: only an attribute type this
1102/// crate names is folded at all, and only inside the size its schema gives
1103/// it, so the longest stream this sees is a few tens of thousands of
1104/// characters rather than a document's worth.
1105#[cfg(test)]
1106type Canonical<I> = Spaced<Mapped<Recomposed<Mapped<I>>>>;
1107
1108/// What the normalization step hands on, named so the pipeline above reads
1109/// as the steps it is.
1110#[cfg(test)]
1111type Recomposed<I> = unicode_normalization::Recompositions<I>;
1112
1113/// Assembles the pipeline over one decoded value.
1114#[cfg(test)]
1115fn canonical<I: Iterator<Item = char>>(inner: I) -> Canonical<I> {
1116    Spaced::over(Mapped::over(normalized(inner)))
1117}
1118
1119/// The value as the two steps before the direction one leave it: mapped
1120/// (RFC 4518 2.2), then normalized to form KC (2.3).
1121///
1122/// The steps that read a value rather than produce one, Prohibit and the
1123/// direction check, come after these two (2.4 and 2.5), so this is the
1124/// stream they are defined over.
1125#[cfg(test)]
1126fn normalized<I: Iterator<Item = char>>(inner: I) -> Recomposed<Mapped<I>> {
1127    use unicode_normalization::UnicodeNormalization;
1128    Mapped::over(inner).nfkc()
1129}
1130
1131/// The Map step: what the preparation sends to nothing is dropped, what it
1132/// sends to SPACE becomes one, and case is folded. One held expansion is the
1133/// whole state.
1134///
1135/// The characters mapped to a space are exactly the ones RFC 4518 names: tab,
1136/// line feed, vertical tab, form feed, carriage return and next line, plus
1137/// the separator code points, which is what `is_whitespace` names.
1138struct Mapped<I, const FOLD_CASE: bool = true> {
1139    inner: I,
1140    /// What one folded character expands to, which is at most a few.
1141    folded: Option<Folded>,
1142}
1143
1144/// The tail of one character's fold, after its first character has been
1145/// handed out.
1146enum Folded {
1147    Mapped(core::str::Chars<'static>),
1148    Lowered(core::char::ToLowercase),
1149}
1150
1151impl Iterator for Folded {
1152    type Item = char;
1153
1154    fn next(&mut self) -> Option<char> {
1155        match self {
1156            Self::Mapped(rest) => rest.next(),
1157            Self::Lowered(rest) => rest.next(),
1158        }
1159    }
1160}
1161
1162impl<I: Iterator<Item = char>, const FOLD_CASE: bool> Mapped<I, FOLD_CASE> {
1163    fn over(inner: I) -> Self {
1164        Self {
1165            inner,
1166            folded: None,
1167        }
1168    }
1169
1170    /// Folds one character's case and hands back its first output, keeping
1171    /// the rest for the next call.
1172    ///
1173    /// The fold is the one the preparation names (RFC 4518 2.2 folds case by
1174    /// the table of RFC 3454 B.2): a letter may fold to a different letter
1175    /// or to more than one, and an issuer written in one case is the same
1176    /// name as the subject written in another. See [`fold`] for what the map
1177    /// is and what it still leaves out.
1178    fn fold(&mut self, c: char) -> Option<char> {
1179        match fold::of(c) {
1180            fold::Fold::Mapped(mapped) => {
1181                let mut rest = mapped.chars();
1182                let first = rest.next();
1183                self.folded = Some(Folded::Mapped(rest));
1184                first
1185            }
1186            fold::Fold::One(one) => {
1187                self.folded = None;
1188                Some(one)
1189            }
1190            fold::Fold::Lowercase => {
1191                let mut lowered = c.to_lowercase();
1192                let first = lowered.next();
1193                self.folded = Some(Folded::Lowered(lowered));
1194                first
1195            }
1196        }
1197    }
1198}
1199
1200impl<I: Iterator<Item = char>, const FOLD_CASE: bool> Iterator for Mapped<I, FOLD_CASE> {
1201    type Item = char;
1202
1203    fn next(&mut self) -> Option<char> {
1204        if let Some(rest) = self.folded.as_mut()
1205            && let Some(c) = rest.next()
1206        {
1207            return Some(c);
1208        }
1209        self.folded = None;
1210        for c in self.inner.by_ref() {
1211            // Mapped to nothing, so it takes no part in what the value says
1212            // and does not separate words either.
1213            if mapped_to_nothing(c) {
1214                continue;
1215            }
1216            if c.is_whitespace() {
1217                return Some(' ');
1218            }
1219            return if FOLD_CASE { self.fold(c) } else { Some(c) };
1220        }
1221        None
1222    }
1223}
1224
1225/// The insignificant space handling: a run of spaces between words counts as
1226/// one and the runs at either end count as none.
1227///
1228/// The profile writes the same rule as padding rather than trimming, one
1229/// space at each end and two between words (RFC 4518 2.6.1), which is what
1230/// makes a substring assertion's edges line up. For deciding whether two
1231/// whole values are equal the two forms put exactly the same values
1232/// together, and this one holds one character of state instead of emitting
1233/// an edge the value does not have.
1234struct Spaced<I> {
1235    inner: I,
1236    held: Option<char>,
1237    gap: bool,
1238    started: bool,
1239}
1240
1241impl<I: Iterator<Item = char>> Spaced<I> {
1242    fn over(inner: I) -> Self {
1243        Self {
1244            inner,
1245            held: None,
1246            gap: false,
1247            started: false,
1248        }
1249    }
1250}
1251
1252impl<I: Iterator<Item = char>> Iterator for Spaced<I> {
1253    type Item = char;
1254
1255    fn next(&mut self) -> Option<char> {
1256        if let Some(c) = self.held.take() {
1257            return Some(c);
1258        }
1259        for c in self.inner.by_ref() {
1260            if c == ' ' {
1261                // A gap counts only between words: a leading run has no word
1262                // before it, and a trailing run meets the end instead.
1263                self.gap = self.started;
1264                continue;
1265            }
1266            self.started = true;
1267            if self.gap {
1268                self.gap = false;
1269                self.held = Some(c);
1270                return Some(' ');
1271            }
1272            return Some(c);
1273        }
1274        None
1275    }
1276}
1277
1278impl<'a> Attribute<'a> {
1279    /// What kind of attribute this is.
1280    #[must_use]
1281    pub fn kind(self) -> Oid<'a> {
1282        self.kind
1283    }
1284
1285    /// The value, as encoded.
1286    #[must_use]
1287    pub fn value(self) -> AttributeValue<'a> {
1288        self.value
1289    }
1290
1291    /// The value as text for a person to read, where it is text at all.
1292    ///
1293    /// For display. Never for deciding that two names are the same one: see
1294    /// [`Name::matches`].
1295    ///
1296    /// # Errors
1297    /// Returns a located resource error if the execution or storage budget is spent.
1298    pub fn text(self, execution: &Memory) -> Result<Option<OwnedString>> {
1299        self.value.text(execution)
1300    }
1301
1302    #[cfg(test)]
1303    fn matches(self, other: &Attribute<'_>) -> bool {
1304        if self.kind != other.kind {
1305            return false;
1306        }
1307        if self.value.der == other.value.der {
1308            return true;
1309        }
1310        // Both are strings the type permits and that decode: the profile
1311        // compares what they say, ignoring case and the width of the gaps.
1312        // Streamed, so two long differently encoded values cost no copy.
1313        // Anything else is compared as it was encoded, because this layer has
1314        // no rule for what it means, and such bytes only ever equal
1315        // themselves.
1316        match (self.folding_chars(), other.folding_chars()) {
1317            (Some(left), Some(right)) => canonical(left).eq(canonical(right)),
1318            _ => false,
1319        }
1320    }
1321
1322    /// A total order over attributes that calls two of them equal exactly
1323    /// when [`Attribute::matches`] does, so ordering both sides of a part and
1324    /// walking them in step answers the multiset question.
1325    ///
1326    /// Kind first, then whether the value folds, then the values themselves:
1327    /// folded ones by their canonical characters, the rest by their bytes. A
1328    /// value that folds can never share its encoding with one that does not,
1329    /// the two questions being asked of the same kind and bytes, so no pair
1330    /// this separates is a pair `matches` would join.
1331    #[cfg(test)]
1332    fn order(&self, other: &Attribute<'_>) -> core::cmp::Ordering {
1333        self.kind
1334            .as_bytes()
1335            .cmp(other.kind.as_bytes())
1336            .then_with(|| match (self.folding_chars(), other.folding_chars()) {
1337                (Some(left), Some(right)) => canonical(left).cmp(canonical(right)),
1338                (None, None) => self.value.der.cmp(other.value.der),
1339                (Some(_), None) => core::cmp::Ordering::Less,
1340                (None, Some(_)) => core::cmp::Ordering::Greater,
1341            })
1342    }
1343
1344    /// Whether this value takes part in the preparation the profile describes,
1345    /// or is compared only as the bytes it was written in.
1346    ///
1347    /// A caller explaining why a name did not match needs the same answer this
1348    /// comparison uses, not a guess at it: a value can be excluded for its
1349    /// encoding, for a code point the type prohibits, or for failing the
1350    /// direction step, and only one of those is visible from the outside.
1351    ///
1352    /// # Errors
1353    /// Returns a located resource error if preparation cannot be financed.
1354    pub fn folds(self, execution: &Memory) -> Result<bool> {
1355        prepared::folds(self, execution)
1356    }
1357
1358    /// Whether this value prepares under the current producing query's limits.
1359    ///
1360    /// Uses the same preparation as [`Self::folds`], funding scans and scratch
1361    /// through `parent.execution()` instead of only its aggregate account.
1362    /// Deferred BER strings retain this attribute's original offset and depth
1363    /// and use the supplied reader's session, just as [`Name::ordered_in`].
1364    /// A query over another source supplies its original-source reader rebound
1365    /// to the current operation; retained Name storage is neither copied nor
1366    /// charged again. Resource refusal is an error, never a byte-only answer.
1367    pub fn folds_in(self, parent: &Reader<'_, '_>) -> Result<bool> {
1368        prepared::folds_in(self, parent)
1369    }
1370
1371    /// The decoded characters of a value that takes part in folding: the
1372    /// type's schema permits the encoding it was written in, and it sits
1373    /// inside the schema's size bounds. A known attribute outside them is not
1374    /// a value the schema names, and folding one would let a malformed issuer
1375    /// match a genuine subject; it stays byte-only equality.
1376    #[cfg(test)]
1377    fn folding_chars(self) -> Option<DecodedChars<'a>> {
1378        (permits(self.kind, self.value.encoding()) && preparable(&self))
1379            .then(|| self.value.decoded_chars())
1380    }
1381}
1382
1383impl<'a> AttributeValue<'a> {
1384    /// The value exactly as it was encoded, tag included.
1385    #[must_use]
1386    pub fn as_bytes(self) -> &'a [u8] {
1387        self.der
1388    }
1389
1390    /// The contents, without the tag and length.
1391    #[must_use]
1392    pub fn contents(self) -> &'a [u8] {
1393        self.contents
1394    }
1395
1396    /// Which string encoding this was written in.
1397    #[must_use]
1398    pub fn encoding(self) -> Encoding {
1399        if self.tag.class() != Class::Universal || self.tag.is_constructed() {
1400            return Encoding::Other;
1401        }
1402        match self.tag.number() {
1403            Some(universal::UTF8_STRING) => Encoding::Utf8,
1404            Some(universal::PRINTABLE_STRING) => Encoding::Printable,
1405            Some(universal::IA5_STRING) => Encoding::Ia5,
1406            Some(NUMERIC_STRING) => Encoding::Numeric,
1407            Some(TELETEX_STRING) => Encoding::Teletex,
1408            Some(VISIBLE_STRING) => Encoding::Visible,
1409            Some(BMP_STRING) => Encoding::Bmp,
1410            Some(UNIVERSAL_STRING) => Encoding::Universal,
1411            _ => Encoding::Other,
1412        }
1413    }
1414
1415    /// The value as text for a person to read, where it is text at all.
1416    ///
1417    /// Bounded, because this is the public reading path and a value may be
1418    /// as wide as the element limit permits while several attribute types
1419    /// have no ceiling in the schema. What is shown is as much of the value
1420    /// as the widest name component the profile names, and a mark saying the
1421    /// rest is there. Identity is the bytes and is never this.
1422    ///
1423    /// The returned string retains its storage charge independently of the parser.
1424    /// # Errors
1425    /// Returns a located resource error before unfunded decoding or allocation.
1426    pub fn text(self, execution: &Memory) -> Result<Option<OwnedString>> {
1427        let fail = |reason| Error::resource(reason, "name.attribute", self.offset);
1428        let width = match self.encoding() {
1429            Encoding::Other => return Ok(None),
1430            Encoding::Bmp => Some(2),
1431            Encoding::Universal => Some(4),
1432            _ => None,
1433        };
1434        // UTF-8 validation and subsequent decoding each visit the input at most
1435        // once. Admit both before entering either scan.
1436        execution
1437            .charge_work((self.contents.len() as u64) * 2)
1438            .map_err(fail)?;
1439        match width {
1440            Some(width) => {
1441                if !self.contents.len().is_multiple_of(width) {
1442                    return Ok(None);
1443                }
1444                let bound = ((DISPLAY_CEILING + 1) * width).min(self.contents.len());
1445                display_text(wide_chars(&self.contents[..bound], width), execution).map_err(fail)
1446            }
1447            // Every remaining type is a subset of ASCII by its definition, and
1448            // an issuer writing outside it has written something. Where those
1449            // bytes are UTF-8 they are read as UTF-8, which is what the issuer
1450            // meant; where they are not, they are read as one byte a
1451            // character. Both are approximations for a person to read, and
1452            // neither is what two names are compared on.
1453            None => match core::str::from_utf8(self.contents) {
1454                Ok(text) => display_text(text.chars().map(Some), execution).map_err(fail),
1455                Err(_) => display_text(
1456                    self.contents.iter().map(|byte| Some(char::from(*byte))),
1457                    execution,
1458                )
1459                .map_err(fail),
1460            },
1461        }
1462    }
1463
1464    /// Streams the value's characters through its declared alphabet and
1465    /// `keep`, counting them, without materializing the text: a value can be
1466    /// as long as the element limit permits, and validating one must not
1467    /// allocate a copy its size. `None` is a value that does not decode or a
1468    /// character either test refuses.
1469    fn validated_count(self, mut keep: impl FnMut(char) -> bool) -> Option<usize> {
1470        let encoding = self.encoding();
1471        let mut count = 0usize;
1472        match encoding {
1473            Encoding::Other => return None,
1474            Encoding::Bmp | Encoding::Universal => {
1475                let width = if encoding == Encoding::Bmp { 2 } else { 4 };
1476                if !self.contents.len().is_multiple_of(width) {
1477                    return None;
1478                }
1479                for c in wide_chars(self.contents, width) {
1480                    if !keep(c?) {
1481                        return None;
1482                    }
1483                    count += 1;
1484                }
1485            }
1486            _ => {
1487                let text = core::str::from_utf8(self.contents).ok()?;
1488                for c in text.chars() {
1489                    if !in_alphabet(encoding, c) || !keep(c) {
1490                        return None;
1491                    }
1492                    count += 1;
1493                }
1494            }
1495        }
1496        Some(count)
1497    }
1498
1499    /// The decoded characters of a value [`inside_syntax`]
1500    /// has already validated: total and allocation free, for folding without a copy.
1501    fn decoded_chars(self) -> DecodedChars<'a> {
1502        match self.encoding() {
1503            Encoding::Bmp => DecodedChars::Wide {
1504                contents: self.contents,
1505                width: 2,
1506            },
1507            Encoding::Universal => DecodedChars::Wide {
1508                contents: self.contents,
1509                width: 4,
1510            },
1511            _ => DecodedChars::Narrow(
1512                core::str::from_utf8(self.contents)
1513                    .expect("the value was decoded before this iterator was built")
1514                    .chars(),
1515            ),
1516        }
1517    }
1518}
1519
1520/// Whether a character sits inside the alphabet a narrow string type
1521/// declares. IA5 is ASCII by definition. The teletex repertoire is a
1522/// negotiation this crate does not implement; its invariant subset is ASCII,
1523/// and only that subset takes part in matching.
1524fn in_alphabet(encoding: Encoding, c: char) -> bool {
1525    match encoding {
1526        Encoding::Printable => is_printable_string(c),
1527        Encoding::Numeric => c.is_ascii_digit() || c == ' ',
1528        Encoding::Visible => matches!(c, '\x20'..='\x7e'),
1529        Encoding::Ia5 | Encoding::Teletex => c.is_ascii(),
1530        _ => true,
1531    }
1532}
1533
1534/// The decoded characters of a wide string, one unit at a time: a unit that
1535/// is not a character, a surrogate included, is `None` (X.680, 41.16 puts
1536/// `BMPString` in the basic plane, and `char::from_u32` refuses surrogates).
1537fn wide_chars(contents: &[u8], width: usize) -> impl Iterator<Item = Option<char>> + '_ {
1538    contents.chunks_exact(width).map(|chunk| {
1539        char::from_u32(
1540            chunk
1541                .iter()
1542                .fold(0u32, |value, byte| (value << 8) | u32::from(*byte)),
1543        )
1544    })
1545}
1546
1547/// The characters of an already validated value, streamed without an
1548/// allocation.
1549enum DecodedChars<'a> {
1550    Narrow(core::str::Chars<'a>),
1551    Wide { contents: &'a [u8], width: usize },
1552}
1553
1554impl Iterator for DecodedChars<'_> {
1555    type Item = char;
1556
1557    fn next(&mut self) -> Option<char> {
1558        match self {
1559            Self::Narrow(chars) => chars.next(),
1560            Self::Wide { contents, width } => {
1561                if contents.len() < *width {
1562                    return None;
1563                }
1564                let (unit, rest) = contents.split_at(*width);
1565                *contents = rest;
1566                // A substitute character here would make two values that do
1567                // not decode compare equal, which is the forged match this
1568                // module exists to prevent.
1569                Some(
1570                    char::from_u32(
1571                        unit.iter()
1572                            .fold(0u32, |value, byte| (value << 8) | u32::from(*byte)),
1573                    )
1574                    .expect("the value was decoded before this iterator was built"),
1575                )
1576            }
1577        }
1578    }
1579}
1580
1581/// Tag numbers X.680 gives the types this crate reads past the ones
1582/// `sig-asn1` names, which are the ones nothing below the schema needs.
1583const REAL: u32 = 9;
1584const ENUMERATED: u32 = 10;
1585const RELATIVE_OID: u32 = 13;
1586const NUMERIC_STRING: u32 = 18;
1587const TELETEX_STRING: u32 = 20;
1588const VISIBLE_STRING: u32 = 26;
1589const UNIVERSAL_STRING: u32 = 28;
1590const BMP_STRING: u32 = 30;
1591const EXTERNAL: u32 = 8;
1592const EMBEDDED_PDV: u32 = 11;
1593const OBJECT_DESCRIPTOR: u32 = 7;
1594const TIME: u32 = 14;
1595const DATE: u32 = 31;
1596const TIME_OF_DAY: u32 = 32;
1597const DATE_TIME: u32 = 33;
1598const DURATION: u32 = 34;
1599const OID_IRI: u32 = 35;
1600const RELATIVE_OID_IRI: u32 = 36;
1601
1602/// Whether contents are the value notation of an identifier written as a
1603/// resource name: the arcs in UTF-8, no arc empty and none carrying white
1604/// space, the object form opening with the separator and the relative one
1605/// not (X.680 34.3 and 35.3, X.690 8.21.2 and 8.22.2).
1606///
1607/// What an arc may say beyond that is a registry's business, so nothing here
1608/// reads one: this is the shape of the value, which is what deciding it is a
1609/// value of the type asks for.
1610fn iri_fits(contents: &[u8], rooted: bool) -> bool {
1611    let Ok(text) = core::str::from_utf8(contents) else {
1612        return false;
1613    };
1614    let arcs = match text.strip_prefix('/') {
1615        Some(rest) if rooted => rest,
1616        None if !rooted => text,
1617        _ => return false,
1618    };
1619    !arcs.is_empty() && arcs.split('/').all(arc_fits)
1620}
1621
1622/// Whether one arc is a Unicode label: either the digits of an arc's own
1623/// number, or a name written from the characters X.660 7.5.2 lists.
1624///
1625/// The number opens with a zero only where it is that one digit (7.3). The
1626/// name holds at least one character that is not a digit, which is what
1627/// tells the two apart (7.5.1), carries no hyphen at either end and no pair
1628/// of them in the third and fourth places, where a normalized form would
1629/// read them as a prefix (7.5.4).
1630///
1631/// What is not applied is 7.5.3, which excludes the positions ISO/IEC 10646
1632/// marks as not to be used: that marking is not stable across editions, and
1633/// the clause's own note tells a reader to be tolerant of it. Everything
1634/// else here is the label as the standard writes it.
1635fn arc_fits(arc: &str) -> bool {
1636    if arc.is_empty() {
1637        return false;
1638    }
1639    if arc.bytes().all(|byte| byte.is_ascii_digit()) {
1640        return arc == "0" || !arc.starts_with('0');
1641    }
1642    if arc.starts_with('-') || arc.ends_with('-') {
1643        return false;
1644    }
1645    let hyphenated = arc
1646        .chars()
1647        .nth(2)
1648        .zip(arc.chars().nth(3))
1649        .is_some_and(|(third, fourth)| third == '-' && fourth == '-');
1650    !hyphenated && arc.chars().all(label_character)
1651}
1652
1653/// Whether a character is one a Unicode label is written from (X.660 7.5.2).
1654fn label_character(c: char) -> bool {
1655    matches!(c, '-' | '.' | '_' | '~' | '0'..='9' | 'A'..='Z' | 'a'..='z')
1656        || matches!(u32::from(c),
1657            0xa0..=0xdffe
1658            | 0xf900..=0xfdcf
1659            | 0xfdf0..=0xffef
1660            | 0x1_0000..=0x1_fffd
1661            | 0x2_0000..=0x2_fffd
1662            | 0x3_0000..=0x3_fffd
1663            | 0x4_0000..=0x4_fffd
1664            | 0x5_0000..=0x5_fffd
1665            | 0x6_0000..=0x6_fffd
1666            | 0x7_0000..=0x7_fffd
1667            | 0x8_0000..=0x8_fffd
1668            | 0x9_0000..=0x9_fffd
1669            | 0xa_0000..=0xa_fffd
1670            | 0xb_0000..=0xb_fffd
1671            | 0xc_0000..=0xc_fffd
1672            | 0xd_0000..=0xd_fffd
1673            | 0xe_1000..=0xe_fffd)
1674}
1675
1676/// Whether a universal type is one DER writes constructed, so finding it that
1677/// way is the type rather than a second spelling of a primitive one.
1678///
1679/// The containers, and the three types whose definition is an associated
1680/// sequence: external and instance-of under tag 8 (X.690 8.16.1, 8.18.1),
1681/// embedded-pdv under 11 (8.17.1), and the unrestricted character string
1682/// under 29 (8.24.1). Everything else universal is primitive here (10.2).
1683pub(crate) fn constructed_universal_type(number: u32) -> bool {
1684    matches!(
1685        number,
1686        universal::SEQUENCE | universal::SET | 8 | 11 | CHARACTER_STRING
1687    )
1688}
1689
1690/// `CHARACTER STRING`, the unrestricted one, which this crate names itself
1691/// because nothing below the schema needs it.
1692const CHARACTER_STRING: u32 = 29;
1693
1694/// Whether the members of one of the three types whose encoding is a
1695/// sequence defined for it are the components that definition names.
1696///
1697/// The external type is a sequence of three optional members, an identifier,
1698/// a reference and a descriptor, and then the encoding it carries, which is
1699/// one of three choices and is not optional (X.690 8.18.1). The embedded-pdv
1700/// type and the unrestricted character string are the same shape as each
1701/// other: how the value is identified, then the value, both required and
1702/// tagged in order, with the descriptor between them absent by the
1703/// definition's own constraint (X.680 36.5 and 44.5).
1704///
1705/// What each member says is somebody else's business, and nothing here reads
1706/// one. This is the shape, which is what deciding it is a value of the type
1707/// asks for. The walk that follows holds each member to its own rules.
1708fn associated_sequence_fits<'a>(
1709    parent: &Reader<'a, '_>,
1710    element: &Element<'a>,
1711    field: &'static str,
1712    number: u32,
1713) -> Result<bool> {
1714    let mut inside = parent.enter(element).field(field)?;
1715    if number == EXTERNAL {
1716        // The optional members come in this order and each appears once.
1717        let mut left: &[u32] = &[universal::OID, universal::INTEGER, OBJECT_DESCRIPTOR];
1718        while let Some(member) = inside.next_element().field(field)? {
1719            let tag = member.tag();
1720            if tag.class() == Class::ContextSpecific {
1721                // The encoding closes the sequence, and each of its three
1722                // ways says what its contents are: one whole value carried
1723                // inside the tag, a value in octets, which any bytes are, or
1724                // a value in bits, which opens with the count of the unused
1725                // ones.
1726                let carried = match tag.number() {
1727                    Some(0) => {
1728                        let mut held = inside.enter(&member).field(field)?;
1729                        held.next_element().field(field)?.is_some() && held.is_empty()
1730                    }
1731                    Some(1) => segments::implicit(&inside, &member, field, false)?,
1732                    Some(2) => segments::implicit(&inside, &member, field, true)?,
1733                    _ => false,
1734                };
1735                return Ok(carried && inside.is_empty());
1736            }
1737            let Some(at) = tag
1738                .number()
1739                .filter(|_| tag.class() == Class::Universal)
1740                .and_then(|number| left.iter().position(|kind| *kind == number))
1741            else {
1742                return Ok(false);
1743            };
1744            left = &left[at + 1..];
1745        }
1746        // Nothing carried, which is the one member the definition requires.
1747        return Ok(false);
1748    }
1749
1750    // How the value is identified: a choice, so its member wears an explicit
1751    // tag over whichever of the six alternatives was written.
1752    let Some(identification) = inside.next_element().field(field)? else {
1753        return Ok(false);
1754    };
1755    if identification.tag() != Tag::context(true, 0) {
1756        return Ok(false);
1757    }
1758    let mut chosen = inside.enter(&identification).field(field)?;
1759    let Some(alternative) = chosen.next_element().field(field)? else {
1760        return Ok(false);
1761    };
1762    if alternative.tag().class() != Class::ContextSpecific
1763        || !chosen.is_empty()
1764        || !identification_fits(&chosen, &alternative, field)?
1765    {
1766        return Ok(false);
1767    }
1768    // Then the value itself, which is the third component of the definition
1769    // and keeps that number whether or not the second one is written. It is
1770    // an octet string under its tag, and the distinguished rules write a
1771    // string in one piece (X.690 10.2), so the segmented form is a second
1772    // spelling of it there. The basic rules have both, and a document read
1773    // under them keeps what it was written with.
1774    let Some(value) = inside.next_element().field(field)? else {
1775        return Ok(false);
1776    };
1777    Ok(value.tag().class() == Class::ContextSpecific
1778        && value.tag().number() == Some(2)
1779        && segments::implicit(&inside, &value, field, false)?
1780        && inside.is_empty())
1781}
1782
1783/// Whether a member read as the type it is, with a spent budget kept as
1784/// what it is.
1785///
1786/// A value that is not the type is an answer about the document, and this
1787/// says no. A budget that ran out is this run giving up, and it says
1788/// nothing about the document, so it travels on rather than being folded
1789/// into the no beside it: a report must not blame a certificate for a
1790/// device that stopped.
1791fn reads<T>(outcome: structured_asn1::Result<T>, field: &'static str) -> Result<bool> {
1792    match outcome.field(field) {
1793        Ok(_) => Ok(true),
1794        Err(error) if error.is_resource() => Err(error),
1795        Err(_) => Ok(false),
1796    }
1797}
1798
1799/// Whether one alternative of the identification choice is the type that
1800/// alternative is (X.680 36.5, and 44.5 for the same choice again).
1801///
1802/// The alternatives are tagged over their types rather than beside them, so
1803/// what arrives is the type's contents under a context tag, and the contents
1804/// are the type's: the pair of identifiers, one identifier, a context named
1805/// by a number, that number with an identifier beside it, the transfer
1806/// syntax as an identifier, and the fixed alternative, which is nothing at
1807/// all. A tag with the wrong contents under it is the same defect as a
1808/// container with the wrong members, one level down.
1809fn identification_fits<'a>(
1810    parent: &Reader<'a, '_>,
1811    alternative: &Element<'a>,
1812    field: &'static str,
1813) -> Result<bool> {
1814    /// The two members of the alternatives that hold a pair, as the types
1815    /// they are: the syntaxes are two identifiers, the negotiation is a
1816    /// context and the syntax to encode it in.
1817    fn pair<'a>(
1818        parent: &Reader<'a, '_>,
1819        alternative: &Element<'a>,
1820        field: &'static str,
1821        numbered: bool,
1822    ) -> Result<bool> {
1823        let mut inside = parent.enter(alternative).field(field)?;
1824        let (Some(first), Some(second)) = (
1825            inside.next_element().field(field)?,
1826            inside.next_element().field(field)?,
1827        ) else {
1828            return Ok(false);
1829        };
1830        let opens = if numbered {
1831            reads(inside.integer(&first), field)?
1832        } else {
1833            reads(inside.oid(&first), field)?
1834        };
1835        Ok(opens
1836            && first.tag() == Tag::context(false, 0)
1837            && second.tag() == Tag::context(false, 1)
1838            && reads(inside.oid(&second), field)?
1839            && inside.is_empty())
1840    }
1841
1842    let constructed = alternative.tag().is_constructed();
1843    Ok(match alternative.tag().number() {
1844        Some(0) => constructed && pair(parent, alternative, field, false)?,
1845        Some(1 | 4) => reads(parent.oid(alternative), field)?,
1846        Some(2) => reads(parent.integer(alternative), field)?,
1847        Some(3) => constructed && pair(parent, alternative, field, true)?,
1848        // The fixed alternative is a null, which says what it says by being
1849        // there and carries nothing.
1850        Some(5) => !constructed && alternative.contents().is_empty(),
1851        _ => false,
1852    })
1853}
1854
1855/// Whether a universal number belongs to a type at all.
1856///
1857/// Table 1 of X.680 assigns them. Zero is kept by the encoding rules,
1858/// fifteen is reserved for future editions, and thirty-seven and up are
1859/// reserved for addenda: nothing between the standard's covers gives any of
1860/// them a meaning, so bytes wearing one are a value of no type.
1861fn assigned_universal_type(number: u32) -> bool {
1862    !matches!(number, 0 | 15) && number <= RELATIVE_OID_IRI
1863}
1864
1865/// Whether a restricted string's contents are held to the alphabet its type
1866/// names.
1867///
1868/// The two answers are the two things a preserved value goes on to do. A
1869/// value handed to a scheme that clears a critical extension has to be a
1870/// value of the type it claims, or the extension is cleared on something
1871/// that is not one. A value that takes part in a name is kept as written:
1872/// refusing it would block a certificate over a spelling, and the profile
1873/// names binary comparison as a sufficient matching rule (RFC 5280 7.1), so
1874/// a malformed value still has an identity and is still shown to a person.
1875/// It is only withheld from folding, which is defined over values the schema
1876/// names.
1877#[derive(Clone, Copy, PartialEq, Eq)]
1878pub(crate) enum Strings {
1879    Checked,
1880    AsWritten,
1881}
1882
1883/// Reads every element under one, holding each to the rules the encoding
1884/// names even where nothing here interprets the value.
1885///
1886/// The places that preserve an opaque value share this: an attribute of a
1887/// name, the value of an other-name, the information of a qualified
1888/// statement, an algorithm's parameter and the value of a policy qualifier.
1889/// Each is handed to somebody who decides an identity, names the service that
1890/// issued the certificate, or clears a critical extension on the strength of
1891/// it, so what is preserved has to be the encoding the document claims, all
1892/// the way down. Checking the outer tag
1893/// alone leaves the same second spelling of a value one level lower, where it
1894/// arrives at the same reader by the same route.
1895pub(crate) fn walk_der<'a>(
1896    parent: &Reader<'a, '_>,
1897    element: &Element<'a>,
1898    field: &'static str,
1899    strings: Strings,
1900) -> Result<()> {
1901    if !check_der(parent, element, field, strings)? {
1902        return Ok(());
1903    }
1904    // The descent keeps its own stack rather than the machine's. Depth is
1905    // the reader's to bound and a caller may raise that bound, so spending a
1906    // machine stack frame per element would turn a limit into a crash for
1907    // whoever raised it. On this stack the same input is the refusal the
1908    // limit describes.
1909    let mut open = parent.execution().vector::<Open<'a, '_>>();
1910    open.try_push(Open::of(parent.enter(element).field(field)?, element.tag()))
1911        .map_err(|reason| Error::resource(reason, field, element.span().start))?;
1912    while let Some(level) = open.last_mut() {
1913        let Some(child) = level.reader.next_element().field(field)? else {
1914            open.pop();
1915            continue;
1916        };
1917        // The members of a set appear in ascending order of their encodings
1918        // (X.690 11.6), and a set's components in the tag order 10.3 names,
1919        // which the same comparison decides because a tag leads its own
1920        // encoding. Deciding it walks up to the longer member, so that
1921        // length is charged before the walk: a caller bounding work sees
1922        // this on the same meter the reads use.
1923        if level.ordered
1924            && let Some(previous) = level.previous
1925        {
1926            level
1927                .reader
1928                .charge(previous.len().max(child.as_bytes().len()) as u64)
1929                .field(field)?;
1930            if !der_set_ordered(previous, child.as_bytes()) {
1931                return Err(Error::new(
1932                    ErrorCode::MalformedValue,
1933                    field,
1934                    child.span().start,
1935                ));
1936            }
1937        }
1938        level.previous = Some(child.as_bytes());
1939
1940        let inside = &open
1941            .last()
1942            .expect("the reader the child was read from")
1943            .reader;
1944        if check_der(inside, &child, field, strings)? {
1945            let entered = inside.enter(&child).field(field)?;
1946            let tag = child.tag();
1947            open.try_push(Open::of(entered, tag))
1948                .map_err(|reason| Error::resource(reason, field, child.span().start))?;
1949        }
1950    }
1951    Ok(())
1952}
1953
1954/// One container the walk is inside, and what deciding its members needs.
1955struct Open<'a: 'operation, 'operation> {
1956    reader: Reader<'a, 'operation>,
1957    /// Whether this container's members are ordered by their encodings,
1958    /// which is the case for a set and for nothing else.
1959    ordered: bool,
1960    /// The member read before this one, for that comparison.
1961    previous: Option<&'a [u8]>,
1962}
1963
1964impl<'a: 'operation, 'operation> Open<'a, 'operation> {
1965    fn of(reader: Reader<'a, 'operation>, tag: Tag) -> Self {
1966        let ordered = reader.encoding().is_der()
1967            && tag.class() == Class::Universal
1968            && tag.number() == Some(universal::SET);
1969        Self {
1970            reader,
1971            ordered,
1972            previous: None,
1973        }
1974    }
1975}
1976
1977/// Holds one element to the rules its tag names, and says whether it has
1978/// children the caller still has to walk.
1979fn check_der<'a>(
1980    parent: &Reader<'a, '_>,
1981    element: &Element<'a>,
1982    field: &'static str,
1983    strings: Strings,
1984) -> Result<bool> {
1985    let fail = |code| Error::new(code, field, element.span().start);
1986    let tag = element.tag();
1987    // The construction rule runs both ways, and only one of the two is
1988    // about DER. A string written constructed is a second spelling of one
1989    // value, which the distinguished rules forbid (X.690 10.2). A container
1990    // written primitive is not the type at all: a sequence and a set are
1991    // constructed by definition (8.9.1, 8.11.1), as are the three types
1992    // whose definition is an associated sequence (8.17.1, 8.18.1, 8.24.1),
1993    // and that holds in either encoding.
1994    if tag.class() == Class::Universal {
1995        // Before the form, the number itself. Table 1 of X.680 assigns the
1996        // universal numbers, and three ranges belong to no type: zero, which
1997        // the encoding rules keep for themselves, fifteen, and everything
1998        // from thirty-seven up. Bytes wearing one of those claim a type the
1999        // standard has not defined, in either encoding, so preserving them
2000        // would hand a scheme a value of nothing.
2001        if !tag.number().is_some_and(assigned_universal_type) {
2002            return Err(fail(ErrorCode::Encoding(
2003                structured_asn1::ErrorCode::UnexpectedTag,
2004            )));
2005        }
2006        // A number this reader does not name is not one of the containers,
2007        // and universal numbers that wide are not assigned at all.
2008        let container = tag.number().is_some_and(constructed_universal_type);
2009        let wrong = if tag.is_constructed() {
2010            !container
2011                && (parent.encoding().is_der()
2012                    || !tag.number().is_some_and(segmented_universal_type))
2013        } else {
2014            container
2015        };
2016        if wrong {
2017            return Err(fail(ErrorCode::Encoding(
2018                structured_asn1::ErrorCode::UnexpectedTag,
2019            )));
2020        }
2021        if tag.is_constructed() && tag.number().is_some_and(segmented_universal_type) {
2022            segments::validate(parent, element, field, strings)?;
2023            return Ok(false);
2024        }
2025        // Three of the containers are not containers of anything: their
2026        // encoding is the encoding of a sequence defined for them, whose
2027        // components are the type. A tag with the wrong members under it
2028        // wears the type without being a value of it.
2029        if let Some(number @ (EXTERNAL | EMBEDDED_PDV | CHARACTER_STRING)) = tag.number()
2030            && !associated_sequence_fits(parent, element, field, number)?
2031        {
2032            return Err(fail(ErrorCode::MalformedValue));
2033        }
2034    }
2035    if !tag.is_constructed() {
2036        // The types whose contents the encoding settles: an integer written
2037        // a byte longer than it needs, a boolean spelled with a byte other
2038        // than the two DER allows, a bit string whose padding carries data.
2039        // Nothing here means anything by the value; what is checked is that
2040        // it is written the one way DER writes it.
2041        if tag.class() == Class::Universal {
2042            match tag.number() {
2043                Some(universal::INTEGER) => parent.integer(element).field(field).map(|_| ())?,
2044                Some(universal::BIT_STRING) => {
2045                    parent.bit_string(element).field(field).map(|_| ())?;
2046                }
2047                Some(universal::BOOLEAN) => parent.boolean(element).field(field).map(|_| ())?,
2048                Some(universal::NULL) => parent.null(element).field(field)?,
2049                Some(universal::OID) => parent.oid(element).field(field).map(|_| ())?,
2050                // An enumerated value is encoded as the integer it is
2051                // associated with (X.690 8.4), so the integer's rules are
2052                // its rules.
2053                Some(ENUMERATED) => parent.integer(element).field(field).map(|_| ())?,
2054                // A real's octets are settled to the last one (8.5), and the
2055                // distinguished rules settle them further (11.3). Nothing
2056                // here reads the number.
2057                Some(REAL) => parent.real(element).field(field)?,
2058                // A relative identifier's sub-identifiers are written the
2059                // way an identifier's are: in the fewest octets, so none
2060                // begins with 0x80, and the last of each series closes it
2061                // (X.690 8.20.2). That is what the identifier reader checks,
2062                // the joint first arc aside, which a relative one does not
2063                // have.
2064                Some(RELATIVE_OID) => parent.oid(element).field(field).map(|_| ())?,
2065                // Under the encoding's rule and not the profile's: a
2066                // certificate's validity dates are written without a
2067                // fraction because the profile says so (RFC 5280 4.1.2.5.2),
2068                // and a value preserved inside somebody else's payload is
2069                // not a validity date.
2070                Some(universal::UTC_TIME | universal::GENERALIZED_TIME) => {
2071                    // A fraction is held to digits by walking it, and it is
2072                    // as long as the element permits: the length goes on the
2073                    // shared meter first, as it does for the identifier and
2074                    // the strings below.
2075                    useful_time_fits(parent, element, field)?;
2076                }
2077                // The newer time types, whose contents are the value notation
2078                // with the characters X.690 8.26 names taken out. What each
2079                // one writes is settled by its property settings (X.680
2080                // 38.4), so a value that is not that notation is not a value
2081                // of the type, whatever the tag says.
2082                Some(kind @ (TIME | DATE | TIME_OF_DAY | DATE_TIME | DURATION)) => {
2083                    parent
2084                        .charge(element.contents().len() as u64)
2085                        .field(field)?;
2086                    let defined = match kind {
2087                        DATE => crate::time::Defined::Date,
2088                        TIME_OF_DAY => crate::time::Defined::TimeOfDay,
2089                        DATE_TIME => crate::time::Defined::DateTime,
2090                        DURATION => crate::time::Defined::Duration,
2091                        _ => crate::time::Defined::Any,
2092                    };
2093                    if !crate::time::defined_fits(defined, element.contents()) {
2094                        return Err(fail(ErrorCode::MalformedValue));
2095                    }
2096                }
2097                // The two identifiers written as resource names: their
2098                // contents are the arcs in UTF-8 with no white space between
2099                // the lexical items (X.690 8.21.2, 8.22.2), the object form
2100                // beginning at the root and the relative one not (X.680 34.3
2101                // and 35.3).
2102                Some(kind @ (OID_IRI | RELATIVE_OID_IRI)) => {
2103                    parent
2104                        .charge(element.contents().len() as u64)
2105                        .field(field)?;
2106                    if !iri_fits(element.contents(), kind == OID_IRI) {
2107                        return Err(fail(ErrorCode::MalformedValue));
2108                    }
2109                }
2110                // A restricted string's contents are settled by its type as
2111                // much as an integer's are by DER: bytes outside the type's
2112                // alphabet, a width that is not whole, or a unit that is not
2113                // a character wear the tag without being a value of it
2114                // (X.680 41). The scan streams and counts rather than
2115                // decoding into a copy, so a value the element limit permits
2116                // costs its length and not its size in memory.
2117                _ if strings == Strings::Checked => {
2118                    // The scan walks every character, and a preserved value
2119                    // has no ceiling of its own: the length is charged to
2120                    // the shared work budget first, so a caller who bounded
2121                    // the work gets the bound here as everywhere else.
2122                    parent
2123                        .charge(element.contents().len() as u64)
2124                        .field(field)?;
2125                    let value = AttributeValue {
2126                        tag,
2127                        contents: element.contents(),
2128                        der: element.as_bytes(),
2129                        offset: element.span().start,
2130                        depth: parent.depth(),
2131                    };
2132                    // TeletexString is left out of this, as it is left out
2133                    // of the shape question a name asks: its repertoire is
2134                    // T.61, a negotiation this crate does not implement, so
2135                    // the bytes being there is what is checkable. What the
2136                    // alphabet below says about it is the approximation
2137                    // this crate reads it as for display, and holding a
2138                    // preserved value to that would refuse a legacy value
2139                    // the schema names.
2140                    let settled = !matches!(value.encoding(), Encoding::Other | Encoding::Teletex);
2141                    if settled && value.validated_count(|_| true).is_none() {
2142                        return Err(fail(ErrorCode::MalformedValue));
2143                    }
2144                }
2145                // What is left is what the encoding does not settle, and it
2146                // is left on purpose. The graphic and general strings and
2147                // the object descriptor carry a repertoire that is
2148                // negotiated rather than fixed (X.680 41, and the same
2149                // reasoning as the teletex string a name may hold). The
2150                // types that write a time as characters, and the two that
2151                // write an identifier as an internationalized resource
2152                // name, are settled by a syntax rather than by the octets,
2153                // and none of them has a reader here because no schema this
2154                // product parses names one; checking them would mean
2155                // writing a parser whose only caller is this check.
2156                _ => (),
2157            }
2158        }
2159        return Ok(false);
2160    }
2161    Ok(true)
2162}
2163
2164fn useful_time_fits(
2165    parent: &Reader<'_, '_>,
2166    element: &Element<'_>,
2167    field: &'static str,
2168) -> Result<()> {
2169    let generalized = element.tag().number() == Some(universal::GENERALIZED_TIME);
2170    let bytes = element.contents();
2171    if parent.encoding().is_der() {
2172        parent.charge(bytes.len() as u64).field(field)?;
2173        if generalized {
2174            crate::temporal::admit_generalized(parent, element, field)?;
2175        }
2176        crate::time::read_as(element, field, crate::time::Fraction::Written)?;
2177    } else {
2178        let work = (bytes.len() as u64).checked_mul(3).ok_or_else(|| {
2179            Error::resource(
2180                structured_asn1::memory::MemoryError::WorkExceeded,
2181                field,
2182                element.span().start,
2183            )
2184        })?;
2185        parent.charge(work).field(field)?;
2186        if !crate::time::ber_fits(bytes, generalized) {
2187            return Err(Error::new(
2188                ErrorCode::MalformedTime,
2189                field,
2190                element.span().start,
2191            ));
2192        }
2193    }
2194    Ok(())
2195}
2196
2197/// BER permits segmentation only for bit/octet strings and the restricted
2198/// character strings, including the useful time types implicitly based on
2199/// `VisibleString`. It does not change primitive-only scalar encodings.
2200/// X.690 §§8.6, 8.7, 8.23; X.680 §§46.3, 47.3, 48.3.
2201/// <https://www.itu.int/rec/T-REC-X.690-202102-I/en>
2202fn segmented_universal_type(number: u32) -> bool {
2203    matches!(number, 3 | 4 | 7 | 12 | 18..=28 | 30)
2204}
2205
2206/// Whether the bytes stay inside the `IA5String` alphabet, the 128-character
2207/// IRV (X.680, 41.4): a byte above 0x7f wears the tag but is not the type.
2208pub(crate) fn is_ia5(bytes: &[u8]) -> bool {
2209    bytes.iter().all(u8::is_ascii)
2210}
2211
2212/// Whether the bytes stay inside the `NumericString` alphabet, digits and
2213/// space (X.680, 41.2).
2214pub(crate) fn is_numeric(bytes: &[u8]) -> bool {
2215    bytes
2216        .iter()
2217        .all(|&byte| byte.is_ascii_digit() || byte == b' ')
2218}
2219
2220/// Whether the bytes stay inside the closed `PrintableString` alphabet of
2221/// X.680, 41.4.
2222pub(crate) fn is_printable(bytes: &[u8]) -> bool {
2223    bytes
2224        .iter()
2225        .all(|&byte| byte.is_ascii() && is_printable_string(char::from(byte)))
2226}
2227
2228/// Whether a character belongs to the `PrintableString` alphabet, which X.680
2229/// closes over letters, digits, space and a short list of punctuation.
2230pub(crate) fn is_printable_string(c: char) -> bool {
2231    c.is_ascii_alphanumeric()
2232        || matches!(
2233            c,
2234            ' ' | '\'' | '(' | ')' | '+' | ',' | '-' | '.' | '/' | ':' | '=' | '?'
2235        )
2236}
2237
2238/// Whether two adjacent members of a set are in the order DER requires:
2239/// ascending as octet strings, the shorter compared as if padded at its end
2240/// with zeroes. Equal members are permitted, which is how a genuine duplicate
2241/// is encoded.
2242pub(crate) fn der_set_ordered(previous: &[u8], next: &[u8]) -> bool {
2243    let length = previous.len().max(next.len());
2244    for position in 0..length {
2245        let left = previous.get(position).copied().unwrap_or(0);
2246        let right = next.get(position).copied().unwrap_or(0);
2247        if left != right {
2248            return left < right;
2249        }
2250    }
2251    true
2252}
2253
2254/// Decodes a string of characters `width` bytes each.
2255/// Counts the characters of a wide string without building one: the size
2256/// constraint on such a value is in characters, and answering it is all a
2257/// gate needs. `None` is a value that does not decode.
2258pub(crate) fn wide_char_count(contents: &[u8], width: usize) -> Option<usize> {
2259    if !contents.len().is_multiple_of(width) {
2260        return None;
2261    }
2262    let mut count = 0usize;
2263    for character in wide_chars(contents, width) {
2264        character?;
2265        count += 1;
2266    }
2267    Some(count)
2268}
2269
2270/// The same reading, stopped one character past the bound a report shows.
2271///
2272/// A unit that is not a character still refuses the whole value, as it does
2273/// when the value is read entire; the difference is only that the units past
2274/// the bound are not looked at, because they are not going to be shown.
2275fn display_text(
2276    mut characters: impl Iterator<Item = Option<char>>,
2277    execution: &Memory,
2278) -> core::result::Result<Option<OwnedString>, structured_asn1::memory::MemoryError> {
2279    let mut text = execution.string();
2280    for index in 0..=DISPLAY_CEILING {
2281        execution.charge_work(1)?;
2282        let Some(character) = characters.next() else {
2283            break;
2284        };
2285        let Some(character) = character else {
2286            return Ok(None);
2287        };
2288        if index == DISPLAY_CEILING {
2289            text.try_push('…')?;
2290            break;
2291        }
2292        text.try_push(character)?;
2293    }
2294    Ok(Some(text))
2295}
2296
2297/// Reads a name from an element a schema said is one.
2298///
2299/// It is public for the same reason as [`crate::algorithm::read`]: a signed
2300/// message names a signer by the issuer name of its certificate, and comparing
2301/// that against a certificate requires both to have been read by the same
2302/// rules. A second reader would be a second answer to which encodings of a
2303/// name are the same name.
2304/// Catalogue allocation and copying use the producing reader's local work
2305/// ceiling and current aggregate account. A successful result retains its
2306/// capacity charge until it is dropped; the borrowed input lifetime is unchanged.
2307///
2308/// # Errors
2309///
2310/// Reports encoding faults or resource refusals with the original field and
2311/// source position.
2312pub fn read<'a>(
2313    parent: &Reader<'a, '_>,
2314    element: &Element<'a>,
2315    field: &'static str,
2316) -> Result<Name<'a>> {
2317    let fail = |code| Error::new(code, field, element.span().start);
2318
2319    if element.tag() != Tag::constructed(universal::SEQUENCE) {
2320        return Err(fail(ErrorCode::Encoding(
2321            structured_asn1::ErrorCode::UnexpectedTag,
2322        )));
2323    }
2324
2325    let mut sequence = parent.enter(element).field(field)?;
2326    let mut parts = parent.execution().vector();
2327
2328    while let Some(part) = sequence.next_element().field(field)? {
2329        if part.tag() != Tag::constructed(universal::SET) {
2330            return Err(Error::new(
2331                ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag),
2332                field,
2333                part.span().start,
2334            ));
2335        }
2336        parts
2337            .try_push(read_relative(&sequence, &part, field)?)
2338            .map_err(|reason| Error::resource(reason, field, part.span().start))?;
2339    }
2340
2341    Ok(Name {
2342        der: element.as_bytes(),
2343        parts: parts.into_owned(),
2344        offset: element.span().start,
2345        field,
2346    })
2347}
2348
2349/// Reads one part of a name, which a distribution point carries on its own.
2350///
2351/// The tag belongs to the caller: here it is the set the schema names, and in
2352/// a distribution point it is the number that position gives it.
2353pub(crate) fn read_relative<'a>(
2354    parent: &Reader<'a, '_>,
2355    element: &Element<'a>,
2356    field: &'static str,
2357) -> Result<RelativeName<'a>> {
2358    let fail = |code| Error::new(code, field, element.span().start);
2359
2360    let mut inside = parent.enter(element).field(field)?;
2361    let mut attributes = parent.execution().vector();
2362    let mut previous: Option<&[u8]> = None;
2363    while let Some(pair) = inside.next_element().field(field)? {
2364        // DER orders the members of a set by their encodings, ascending, and
2365        // the comparison of names deliberately ignores order. Without this
2366        // check an unsorted part would match a correctly sorted one, and a
2367        // malformed issuer could take part in a path. The comparison walks
2368        // up to the longer encoding, so that length is charged to the
2369        // shared work budget before the walk: a caller bounding work sees
2370        // two huge common-prefix members on the same meter the reads use.
2371        if inside.encoding().is_der()
2372            && let Some(previous) = previous
2373            && {
2374                inside
2375                    .charge(previous.len().max(pair.as_bytes().len()) as u64)
2376                    .field(field)?;
2377                !der_set_ordered(previous, pair.as_bytes())
2378            }
2379        {
2380            return Err(Error::new(
2381                ErrorCode::MalformedValue,
2382                field,
2383                pair.span().start,
2384            ));
2385        }
2386        previous = Some(pair.as_bytes());
2387        attributes
2388            .try_push(read_attribute(&inside, &pair, field)?)
2389            .map_err(|reason| Error::resource(reason, field, pair.span().start))?;
2390    }
2391
2392    // A part with nothing in it names nothing, and two names that differ only
2393    // by one of these would compare as different names.
2394    if attributes.is_empty() {
2395        return Err(fail(ErrorCode::EmptySequence));
2396    }
2397
2398    Ok(RelativeName {
2399        attributes: attributes.into_owned(),
2400    })
2401}
2402
2403fn read_attribute<'a>(
2404    parent: &Reader<'a, '_>,
2405    element: &Element<'a>,
2406    field: &'static str,
2407) -> Result<Attribute<'a>> {
2408    let fail = |code| Error::new(code, field, element.span().start);
2409
2410    if element.tag() != Tag::constructed(universal::SEQUENCE) {
2411        return Err(fail(ErrorCode::Encoding(
2412            structured_asn1::ErrorCode::UnexpectedTag,
2413        )));
2414    }
2415
2416    let mut pair = parent.enter(element).field(field)?;
2417    let kind = pair.read_oid().field(field)?;
2418    let value = pair
2419        .next_element()
2420        .field(field)?
2421        .ok_or_else(|| fail(ErrorCode::MissingField))?;
2422
2423    // Even an unknown kind's opaque value must be DER before it is
2424    // preserved: what is kept here is compared against the same attribute in
2425    // another certificate, and a second spelling of one value would be a
2426    // different name for the same holder.
2427    walk_der(&pair, &value, field, Strings::AsWritten)?;
2428
2429    if !pair.is_empty() {
2430        return Err(fail(ErrorCode::TrailingData));
2431    }
2432
2433    Ok(Attribute {
2434        kind,
2435        value: AttributeValue {
2436            tag: value.tag(),
2437            contents: value.contents(),
2438            der: value.as_bytes(),
2439            offset: value.span().start,
2440            depth: pair.depth(),
2441        },
2442    })
2443}
2444
2445#[cfg(test)]
2446mod tests;