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