1use super::{Attribute, AttributeValue, Name, Strings, Syntax, prepared, segments};
4use crate::error::{Error, ErrorCode, Located, Result};
5use crate::oid;
6use core::cmp::Ordering;
7use core::ops::Range;
8use structured_asn1::memory::{Memory, OwnedVec};
9use structured_asn1::{Class, Oid, OwnedOid, Parser, Tag, universal};
10
11const FIELD: &str = "name.selector";
12
13#[derive(Debug)]
21pub struct Selector {
22 parts: OwnedVec<OwnedVec<Selected>>,
23}
24
25#[derive(Debug)]
26struct Selected {
27 kind: Kind,
28 bytes: OwnedVec<u8>,
29 value: Value,
30 offset: u64,
31}
32
33#[derive(Debug)]
34enum Kind {
35 Known(Oid<'static>),
36 Numeric(OwnedOid),
37}
38
39impl Kind {
40 fn as_oid(&self) -> Oid<'_> {
41 match self {
42 Self::Known(kind) => *kind,
43 Self::Numeric(kind) => kind.as_oid(),
44 }
45 }
46}
47
48#[derive(Debug)]
49enum Value {
50 Assertion,
51 Ber {
52 tag: Tag,
53 contents: Range<usize>,
54 joined: Option<OwnedVec<u8>>,
55 },
56}
57
58impl Selector {
59 pub fn parse(text: &str, execution: &Memory) -> Result<Self> {
75 let work = (text.len() as u64)
78 .checked_mul(64)
79 .and_then(|n| n.checked_add(1))
80 .ok_or_else(|| {
81 Error::resource(structured_asn1::memory::MemoryError::WorkExceeded, FIELD, 0)
82 })?;
83 execution
84 .charge_work(work)
85 .map_err(|reason| Error::resource(reason, FIELD, 0))?;
86 scan(text, |_, _, _, _| Ok(()))?;
87 let parser = Parser::ber(execution.clone());
88 let mut parts = execution.vector();
89 let mut current = execution.vector();
90 scan(text, |label, value, offset, last| {
91 current
92 .try_push(selected(label, value, offset, &parser, execution)?)
93 .map_err(|reason| Error::resource(reason, FIELD, offset))?;
94 if last {
95 parts
96 .try_push(core::mem::replace(&mut current, execution.vector()))
97 .map_err(|reason| Error::resource(reason, FIELD, offset))?;
98 }
99 Ok(())
100 })?;
101 execution
102 .charge_work(parts.len() as u64)
103 .map_err(|reason| Error::resource(reason, FIELD, 0))?;
104 parts.reverse(); Ok(Self { parts })
106 }
107
108 pub fn matches_name(&self, name: &Name<'_>, execution: &Memory) -> Result<bool> {
112 if self.parts.len() != name.parts().len() {
113 return Ok(false);
114 }
115 for (selected, part) in self.parts.iter().zip(name.parts()) {
116 execution
117 .charge_work(1)
118 .map_err(|reason| Error::resource(reason, FIELD, 0))?;
119 let fail = |reason| Error::resource(reason, FIELD, 0);
120 let order = prepared::compare_views(
121 selected.iter().map(|item| project(item, execution)),
122 part.attributes()
123 .iter()
124 .copied()
125 .map(|attribute| prepared::Prepared::of(attribute, execution)),
126 execution,
127 fail,
128 )?;
129 if order != Ordering::Equal {
130 return Ok(false);
131 }
132 }
133 Ok(true)
134 }
135
136 pub fn matches(&self, other: &Self, execution: &Memory) -> Result<bool> {
138 if self.parts.len() != other.parts.len() {
139 return Ok(false);
140 }
141 for (left, right) in self.parts.iter().zip(&other.parts) {
142 execution
143 .charge_work(1)
144 .map_err(|reason| Error::resource(reason, FIELD, 0))?;
145 if prepared::compare_views(
146 left.iter().map(|item| project(item, execution)),
147 right.iter().map(|item| project(item, execution)),
148 execution,
149 |reason| Error::resource(reason, FIELD, 0),
150 )? != Ordering::Equal
151 {
152 return Ok(false);
153 }
154 }
155 Ok(true)
156 }
157}
158
159fn selected(
160 label: &str,
161 value: &str,
162 offset: u64,
163 parser: &Parser,
164 execution: &Memory,
165) -> Result<Selected> {
166 let kind = match descriptor(label) {
167 Some(kind) => Kind::Known(kind),
168 None => Kind::Numeric(OwnedOid::parse(label, execution).map_err(|error| {
169 Error::new(
170 ErrorCode::Encoding(error.code()),
171 FIELD,
172 offset + error.offset(),
173 )
174 })?),
175 };
176 let mut bytes = execution.vector();
177 let value = if let Some(hex) = value.strip_prefix('#') {
178 for pair in hex.as_bytes().as_chunks::<2>().0 {
179 bytes
180 .try_push(
181 (digit(pair[0]).expect("validated hex") << 4)
182 | digit(pair[1]).expect("validated hex"),
183 )
184 .map_err(|reason| Error::resource(reason, FIELD, offset))?;
185 }
186 ber(&bytes, kind.as_oid(), parser, execution).map_err(|error| {
187 Error::new(
188 error.code(),
189 FIELD,
190 offset + label.len() as u64 + 2 + 2 * error.offset(),
191 )
192 })?
193 } else {
194 let mut at = 0;
195 let input = value.as_bytes();
196 while at < input.len() {
197 let byte = if input[at] == b'\\' {
198 at += 1;
199 if let Some(high) = digit(input[at]) {
200 at += 1;
201 (high << 4) | digit(input[at]).expect("validated escape")
202 } else {
203 input[at]
204 }
205 } else {
206 input[at]
207 };
208 bytes
209 .try_push(byte)
210 .map_err(|reason| Error::resource(reason, FIELD, offset))?;
211 at += 1;
212 }
213 Value::Assertion
214 };
215 Ok(Selected {
216 kind,
217 bytes,
218 value,
219 offset,
220 })
221}
222
223fn ber(bytes: &[u8], kind: Oid<'_>, parser: &Parser, execution: &Memory) -> Result<Value> {
224 let mut reader = parser.read(bytes, 0).field(FIELD)?;
225 let element = reader
226 .next_element()
227 .field(FIELD)?
228 .ok_or_else(|| malformed(0))?;
229 if !reader.is_empty() {
230 return Err(malformed(element.span().end()));
231 }
232 super::walk_der(&reader, &element, FIELD, Strings::AsWritten)?;
237 let tag = element.tag();
238 let start = element.as_bytes().len()
239 - element.contents().len()
240 - if element.is_indefinite() { 2 } else { 0 };
241 let contents = start..start + element.contents().len();
242 execution
243 .charge_work(
244 (kind.as_bytes().len() as u64)
245 .checked_mul(40)
246 .ok_or_else(|| {
247 Error::resource(structured_asn1::memory::MemoryError::WorkExceeded, FIELD, 0)
248 })?,
249 )
250 .map_err(|reason| Error::resource(reason, FIELD, 0))?;
251 let logical_encoding = match tag.number() {
252 Some(number) => super::AttributeValue {
253 tag: Tag::primitive(number),
254 contents: &[],
255 der: &[],
256 offset: 0,
257 }
258 .encoding(),
259 None => super::Encoding::Other,
260 };
261 let joined = if tag.class() == Class::Universal
262 && tag.is_constructed()
263 && tag.number().is_some_and(super::segmented_universal_type)
264 && tag.number() != Some(universal::BIT_STRING)
265 && super::permits(kind, logical_encoding)
266 {
267 let mut joined = execution.vector();
268 segments::walk(&reader, &element, FIELD, false, Some(&mut joined))?;
269 Some(joined)
270 } else {
271 None
272 };
273 Ok(Value::Ber {
274 tag,
275 contents,
276 joined,
277 })
278}
279
280fn project<'a>(item: &'a Selected, execution: &Memory) -> Result<prepared::Prepared<'a>> {
281 let kind = item.kind.as_oid();
282 let value = match &item.value {
283 Value::Assertion => {
284 execution
285 .charge_work((kind.as_bytes().len() as u64) * 40)
286 .map_err(|reason| Error::resource(reason, FIELD, item.offset))?;
287 let tag = match super::syntax_of(kind) {
288 Some(Syntax::Directory) => universal::UTF8_STRING,
289 Some(Syntax::Printable) => universal::PRINTABLE_STRING,
290 Some(Syntax::Ia5) => universal::IA5_STRING,
291 None => {
292 return Err(Error::new(
293 ErrorCode::UnsupportedNameSyntax,
294 FIELD,
295 item.offset,
296 ));
297 }
298 };
299 AttributeValue {
300 tag: Tag::primitive(tag),
301 contents: &item.bytes,
302 der: &[],
303 offset: item.offset,
304 }
305 }
306 Value::Ber {
307 tag,
308 contents,
309 joined,
310 } => AttributeValue {
311 tag: if joined.is_some() {
312 Tag::primitive(tag.number().expect("universal string"))
313 } else {
314 *tag
315 },
316 contents: joined.as_deref().unwrap_or(&item.bytes[contents.clone()]),
317 der: &item.bytes,
318 offset: item.offset,
319 },
320 };
321 let attribute = Attribute { kind, value };
322 if matches!(item.value, Value::Assertion) {
323 let work = (kind.as_bytes().len() as u64)
324 .checked_mul(64)
325 .and_then(|kinds| {
326 (value.contents.len() as u64)
327 .checked_mul(2)
328 .and_then(|contents| kinds.checked_add(contents))
329 })
330 .and_then(|work| work.checked_add(1))
331 .ok_or_else(|| {
332 Error::resource(
333 structured_asn1::memory::MemoryError::WorkExceeded,
334 FIELD,
335 item.offset,
336 )
337 })?;
338 execution
339 .charge_work(work)
340 .map_err(|reason| Error::resource(reason, FIELD, item.offset))?;
341 if !super::well_formed(&attribute) {
342 return Err(Error::new(ErrorCode::MalformedValue, FIELD, item.offset));
343 }
344 }
345 let prepared = prepared::Prepared::of(attribute, execution)?;
346 if matches!(item.value, Value::Assertion) && !prepared.is_prepared() {
347 return Err(Error::new(
348 ErrorCode::UnsupportedNameSyntax,
349 FIELD,
350 item.offset,
351 ));
352 }
353 Ok(prepared)
354}
355
356fn malformed(offset: u64) -> Error {
357 Error::new(ErrorCode::MalformedValue, FIELD, offset)
358}
359
360fn digit(byte: u8) -> Option<u8> {
361 match byte {
362 b'0'..=b'9' => Some(byte - b'0'),
363 b'a'..=b'f' => Some(byte - b'a' + 10),
364 b'A'..=b'F' => Some(byte - b'A' + 10),
365 _ => None,
366 }
367}
368
369fn label_fits(label: &str, offset: u64) -> Result<()> {
370 let Some(first) = label.as_bytes().first() else {
371 return Err(malformed(offset));
372 };
373 if first.is_ascii_digit() {
374 let mut count = 0;
375 for component in label.split('.') {
376 if component.is_empty()
377 || !component.bytes().all(|b| b.is_ascii_digit())
378 || (component.len() > 1 && component.starts_with('0'))
379 {
380 return Err(malformed(offset));
381 }
382 count += 1;
383 }
384 if count < 2 {
385 return Err(malformed(offset));
386 }
387 } else if !first.is_ascii_alphabetic()
388 || !label
389 .bytes()
390 .all(|b| b.is_ascii_alphanumeric() || b == b'-')
391 {
392 return Err(malformed(offset));
393 } else if descriptor(label).is_none() {
394 return Err(Error::new(ErrorCode::UnsupportedNameSyntax, FIELD, offset));
395 }
396 Ok(())
397}
398
399fn scan(text: &str, mut visit: impl FnMut(&str, &str, u64, bool) -> Result<()>) -> Result<()> {
402 if text.is_empty() {
403 return Ok(());
404 }
405 let bytes = text.as_bytes();
406 let mut at = 0;
407 loop {
408 let start = at;
409 while at < bytes.len() && bytes[at] != b'=' {
410 at += 1;
411 }
412 if at == bytes.len() {
413 return Err(malformed(start as u64));
414 }
415 let label = &text[start..at];
416 label_fits(label, start as u64)?;
417 at += 1;
418 let value = at;
419 if bytes.get(at) == Some(&b'#') {
420 at += 1;
421 let hex = at;
422 while at < bytes.len() && !matches!(bytes[at], b',' | b'+') {
423 if digit(bytes[at]).is_none() {
424 return Err(malformed(at as u64));
425 }
426 at += 1;
427 }
428 if at == hex || !(at - hex).is_multiple_of(2) {
429 return Err(malformed(hex as u64));
430 }
431 } else {
432 let mut last_space = false;
433 while at < bytes.len() && !matches!(bytes[at], b',' | b'+') {
434 let byte = bytes[at];
435 if byte == b'\\' {
436 at += 1;
437 let Some(&escaped) = bytes.get(at) else {
438 return Err(malformed(at as u64));
439 };
440 if digit(escaped).is_some() {
441 at += 1;
442 if bytes.get(at).copied().and_then(digit).is_none() {
443 return Err(malformed(at as u64));
444 }
445 } else if !matches!(
446 escaped,
447 b' ' | b'"' | b'#' | b'+' | b',' | b';' | b'<' | b'=' | b'>' | b'\\'
448 ) {
449 return Err(malformed(at as u64));
450 }
451 last_space = false;
452 } else {
453 if matches!(byte, 0 | b'"' | b';' | b'<' | b'>')
454 || (at == value && matches!(byte, b' ' | b'#'))
455 {
456 return Err(malformed(at as u64));
457 }
458 last_space = byte == b' ';
459 }
460 at += 1;
461 }
462 if last_space {
463 return Err(malformed((at - 1) as u64));
464 }
465 }
466 let last = bytes.get(at) != Some(&b'+');
467 visit(label, &text[value..at], start as u64, last)?;
468 if at == bytes.len() {
469 return Ok(());
470 }
471 at += 1;
472 if at == bytes.len() {
473 return Err(malformed(at as u64));
474 }
475 }
476}
477
478const DESCRIPTORS: &[(&str, Oid<'static>)] = &[
479 ("CN", oid::COMMON_NAME),
480 ("L", oid::LOCALITY),
481 ("ST", oid::STATE_OR_PROVINCE),
482 ("O", oid::ORGANIZATION),
483 ("OU", oid::ORGANIZATIONAL_UNIT),
484 ("C", oid::COUNTRY),
485 ("STREET", oid::STREET_ADDRESS),
486 ("DC", oid::DOMAIN_COMPONENT),
487 ("UID", oid::USER_ID),
488 ("emailAddress", oid::EMAIL_ADDRESS),
489 ("serialNumber", oid::SERIAL_NUMBER),
490 ("surname", oid::SURNAME),
491 ("givenName", oid::GIVEN_NAME),
492 ("initials", oid::INITIALS),
493 ("generationQualifier", oid::GENERATION_QUALIFIER),
494 ("title", oid::TITLE),
495 ("dnQualifier", oid::DN_QUALIFIER),
496 ("pseudonym", oid::PSEUDONYM),
497 ("organizationIdentifier", oid::ORGANIZATION_IDENTIFIER),
498];
499
500pub(super) const REGISTERED_DESCRIPTORS: usize = 18;
505
506pub(super) fn registered_name(kind: Oid<'_>) -> Option<&'static str> {
507 DESCRIPTORS[..REGISTERED_DESCRIPTORS]
508 .iter()
509 .find(|(_, oid)| *oid == kind)
510 .map(|(name, _)| *name)
511}
512
513fn descriptor(text: &str) -> Option<Oid<'static>> {
514 if text.eq_ignore_ascii_case("sn") {
515 return Some(oid::SURNAME);
516 }
517 DESCRIPTORS
518 .iter()
519 .find(|(name, _)| name.eq_ignore_ascii_case(text))
520 .map(|(_, oid)| *oid)
521}