structured_pkix/qualified.rs
1//! What a certificate claims about being a qualified certificate.
2//!
3//! These statements are claims the issuer makes, not facts this crate can
4//! confirm. Whether a certificate is qualified is decided by the trusted list,
5//! against the service that issued it, at the moment in question. A statement
6//! saying so is evidence about what the issuer intended, and the report has to
7//! keep the two apart.
8
9use crate::error::{Error, ErrorCode, Located, Result, gate};
10use crate::extension::{self, Extension};
11use crate::name::is_ia5;
12use crate::oid;
13use structured_asn1::memory::{Memory, OwnedVec};
14use structured_asn1::{Oid, Parser, Tag, universal};
15
16/// One statement: what it is, and whatever was written with it.
17#[derive(Debug, Clone, Copy)]
18pub struct QcStatement<'a> {
19 kind: Oid<'a>,
20 info: Option<&'a [u8]>,
21 /// Where the information sits in the document, so an error found while
22 /// re-parsing it points at the statement rather than at the front of the
23 /// certificate.
24 at: u64,
25 /// Depth above the statement pair, retained for information decoders.
26 outer: u32,
27}
28
29impl<'a> QcStatement<'a> {
30 /// Which statement this is.
31 #[must_use]
32 pub fn kind(self) -> Oid<'a> {
33 self.kind
34 }
35
36 /// What was written with it, as encoded. What it means depends on the
37 /// statement, and the ones this crate reads are below.
38 #[must_use]
39 pub fn info(self) -> Option<&'a [u8]> {
40 self.info
41 }
42}
43
44/// How long the issuer says it keeps what is needed to check a certificate.
45///
46/// The statement is a bare `INTEGER` of years (ETSI TS 101 862, annex B), so
47/// a period wider than the counter is inside the schema. It is kept as what
48/// it is, longer than any interval a question will be asked over, rather
49/// than refused as malformed or folded into a number nobody wrote.
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub enum RetentionPeriod {
52 /// The number of years the statement names.
53 Years(u32),
54 /// A period past any interval a report covers.
55 BeyondAnyInterval,
56}
57
58impl RetentionPeriod {
59 /// The years where an interval could reach them, and `None` where none
60 /// can.
61 #[must_use]
62 pub fn years(self) -> Option<u32> {
63 match self {
64 Self::Years(years) => Some(years),
65 Self::BeyondAnyInterval => None,
66 }
67 }
68}
69
70/// Which kind of qualified certificate this claims to be.
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72#[non_exhaustive]
73pub enum QualifiedType {
74 /// For an electronic signature, which a natural person makes.
75 ElectronicSignature,
76 /// For an electronic seal, which a legal person makes.
77 ElectronicSeal,
78 /// For website authentication.
79 WebsiteAuthentication,
80}
81
82/// The statements a certificate carries.
83#[derive(Debug)]
84pub struct QcStatements<'a> {
85 statements: OwnedVec<QcStatement<'a>>,
86 parser: &'a Parser,
87}
88
89impl<'a> QcStatements<'a> {
90 /// Account retaining the statement collection's storage. Deferred
91 /// decoding uses the explicitly supplied current execution domain.
92 #[must_use]
93 pub fn memory(&self) -> &Memory {
94 self.statements.memory()
95 }
96
97 /// Re-read information at its original offset and depth under the same
98 /// document budget. Every lazy decoder goes through this boundary.
99 fn info_reader(
100 &self,
101 info: &'a [u8],
102 statement: &QcStatement<'a>,
103 execution: &Memory,
104 ) -> structured_asn1::Result<structured_asn1::Reader<'a>> {
105 self.parser.read_nested_in(
106 info,
107 statement.at,
108 structured_asn1::Encoding::Der,
109 statement.outer,
110 execution,
111 )
112 }
113
114 /// Every statement, in order.
115 #[must_use]
116 pub fn statements(&self) -> &[QcStatement<'a>] {
117 &self.statements
118 }
119
120 /// One statement by identifier, for the ones this crate does not read.
121 pub fn get(&self, kind: Oid<'_>, execution: &Memory) -> Result<Option<QcStatement<'a>>> {
122 for statement in &self.statements {
123 let work = statement.kind.as_bytes().len().max(kind.as_bytes().len()) as u64;
124 execution
125 .charge_work(1 + work)
126 .map_err(|reason| Error::resource(reason, STATEMENT_FIELD, statement.at))?;
127 if statement.kind == kind {
128 return Ok(Some(*statement));
129 }
130 }
131 Ok(None)
132 }
133
134 /// Whether the issuer claims this is a qualified certificate.
135 ///
136 /// A claim, not a finding. The trusted list decides.
137 pub fn claims_compliance(&self, execution: &Memory) -> Result<bool> {
138 Ok(self.get(oid::QC_COMPLIANCE, execution)?.is_some())
139 }
140
141 /// Whether the issuer claims the private key is held in a qualified
142 /// device.
143 pub fn claims_qualified_device(&self, execution: &Memory) -> Result<bool> {
144 Ok(self.get(oid::QC_SSCD, execution)?.is_some())
145 }
146
147 /// Which kinds of qualified certificate this claims to be.
148 ///
149 /// This is what separates a signature made by a person from a seal made by
150 /// an organization, which is a difference the report must never collapse.
151 pub fn types(&self, execution: &Memory) -> Result<OwnedVec<QualifiedType>> {
152 const FIELD: &str = "extensions.qcStatements.qcType";
153 // Statement information lives inside the DER certificate like every
154 // extension value, so its re-parses below are DER whatever parser
155 // carries the budget.
156 let Some(statement) = self.get(oid::QC_TYPE, execution)? else {
157 return Ok(execution.vector());
158 };
159 let info = statement
160 .info
161 .ok_or_else(|| Error::new(ErrorCode::MissingField, FIELD, statement.at))?;
162
163 // Re-parsed from where it sits, so every offset below is an offset in
164 // the document, which is what the error type promises.
165 let mut reader = self.info_reader(info, &statement, execution).field(FIELD)?;
166 let sequence = reader
167 .next_element()
168 .field(FIELD)?
169 .ok_or_else(|| Error::new(ErrorCode::MissingField, FIELD, statement.at))?;
170 if sequence.tag() != Tag::constructed(universal::SEQUENCE) {
171 return Err(Error::new(
172 ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag),
173 FIELD,
174 sequence.span().start,
175 ));
176 }
177 let mut inside = reader.enter(&sequence).field(FIELD)?;
178 let mut types = execution.vector();
179 while !inside.is_empty() {
180 let identifier = inside.read_oid().field(FIELD)?;
181 // An identifier this crate does not know is a kind defined
182 // elsewhere. It is skipped rather than refused, and stays visible
183 // through the statement it was written in.
184 if identifier == oid::QCT_ESIGN {
185 types
186 .try_push(QualifiedType::ElectronicSignature)
187 .map_err(|reason| Error::resource(reason, FIELD, inside.offset()))?;
188 } else if identifier == oid::QCT_ESEAL {
189 types
190 .try_push(QualifiedType::ElectronicSeal)
191 .map_err(|reason| Error::resource(reason, FIELD, inside.offset()))?;
192 } else if identifier == oid::QCT_WEB {
193 types
194 .try_push(QualifiedType::WebsiteAuthentication)
195 .map_err(|reason| Error::resource(reason, FIELD, inside.offset()))?;
196 }
197 }
198
199 Ok(types)
200 }
201
202 /// How long the issuer says it keeps what is needed to check this
203 /// certificate, in years.
204 pub fn retention_period(&self, execution: &Memory) -> Result<Option<RetentionPeriod>> {
205 const FIELD: &str = "extensions.qcStatements.qcRetentionPeriod";
206 let Some(statement) = self.get(oid::QC_RETENTION_PERIOD, execution)? else {
207 return Ok(None);
208 };
209 let info = statement
210 .info
211 .ok_or_else(|| Error::new(ErrorCode::MissingField, FIELD, statement.at))?;
212
213 let mut reader = self.info_reader(info, &statement, execution).field(FIELD)?;
214 let years = reader.read_integer().field(FIELD)?;
215 if !reader.is_empty() {
216 return Err(Error::new(ErrorCode::TrailingData, FIELD, statement.at));
217 }
218
219 // Below zero is not a duration at all. Above the counter is still a
220 // duration, and what it says is that nothing asks over that long.
221 if years.is_negative() {
222 return Err(Error::new(ErrorCode::MalformedValue, FIELD, statement.at));
223 }
224 Ok(Some(years.as_u32().map_or(
225 RetentionPeriod::BeyondAnyInterval,
226 RetentionPeriod::Years,
227 )))
228 }
229
230 /// Which kind of person the subject's identifiers are to be read as.
231 ///
232 /// Says how to read an organization identifier or a serial number in the
233 /// subject's name, and getting it wrong turns an organization into a
234 /// person or the reverse.
235 pub fn semantics(&self, execution: &Memory) -> Result<Option<Oid<'a>>> {
236 // Both versions may be present, each saying which profile the
237 // certificate claims conformance with (RFC 3739 3.2.6), and each may
238 // carry an identifier. Two that differ say the subject's identifiers
239 // are to be read two ways, and the certificate does not say which:
240 // answering with either would put one of them in a report as though
241 // the document had settled it.
242 let mut said: Option<(Oid<'a>, u64)> = None;
243 for kind in [oid::PKIX_QC_SYNTAX_V2, oid::PKIX_QC_SYNTAX_V1] {
244 let Some(statement) = self.get(kind, execution)? else {
245 continue;
246 };
247 let Some(identifier) = self.semantics_parts(&statement, execution)?.0 else {
248 continue;
249 };
250 if said.is_some_and(|(said, _)| said != identifier) {
251 return Err(Error::new(
252 ErrorCode::MalformedValue,
253 "extensions.qcStatements.semanticsInformation",
254 statement.at,
255 ));
256 }
257 said = Some((identifier, statement.at));
258 }
259 Ok(said.map(|(identifier, _)| identifier))
260 }
261
262 /// Reads one syntax statement's semantics information: the identifier if
263 /// one was written, and the registration authorities. The accessor above
264 /// prefers the newer statement, but where both appear each is validated on
265 /// its own: a well-formed preferred statement must not vouch for the other.
266 #[allow(clippy::type_complexity)]
267 fn semantics_parts(
268 &self,
269 statement: &QcStatement<'a>,
270 execution: &Memory,
271 ) -> Result<(
272 Option<Oid<'a>>,
273 OwnedVec<crate::general_name::GeneralName<'a>>,
274 )> {
275 const FIELD: &str = "extensions.qcStatements.semanticsInformation";
276 // The information is optional even where the statement is present.
277 let Some(info) = statement.info else {
278 return Ok((None, execution.vector()));
279 };
280
281 let mut reader = self.info_reader(info, statement, execution).field(FIELD)?;
282 let sequence = reader
283 .next_element()
284 .field(FIELD)?
285 .ok_or_else(|| Error::new(ErrorCode::MissingField, FIELD, statement.at))?;
286 if sequence.tag() != Tag::constructed(universal::SEQUENCE) {
287 return Err(Error::new(
288 ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag),
289 FIELD,
290 sequence.span().start,
291 ));
292 }
293 let mut inside = reader.enter(&sequence).field(FIELD)?;
294 // Both members are optional, but at least one must be present
295 // (RFC 3739, 3.2.6.1): empty, the structure says nothing while
296 // wearing the statement.
297 let Some(first) = inside.next_element().field(FIELD)? else {
298 return Err(Error::new(ErrorCode::MissingField, FIELD, statement.at));
299 };
300
301 // Both members are optional, but the structure is closed: after the
302 // identifier only the list of registration authorities may follow,
303 // and anything else riding along under a value that decides person
304 // versus organization is a malformed statement, not extra context.
305 let parts = if first.tag() == Tag::primitive(universal::OID) {
306 let identifier = inside.oid(&first).field(FIELD)?;
307 match inside.next_element().field(FIELD)? {
308 Some(authorities) => (
309 Some(identifier),
310 require_authorities(&inside, &authorities)?,
311 ),
312 None => return Ok((Some(identifier), execution.vector())),
313 }
314 } else {
315 (None, require_authorities(&inside, &first)?)
316 };
317
318 if !inside.is_empty() {
319 return Err(Error::new(ErrorCode::TrailingData, FIELD, statement.at));
320 }
321 Ok(parts)
322 }
323
324 /// Reads the value of a qualified statements extension.
325 pub fn read(extension: &Extension<'a>, execution: &Memory) -> Result<Self> {
326 const FIELD: &str = "extensions.qcStatements";
327
328 let mut inside = extension::sequence(extension, FIELD, execution)?;
329 let mut statements = inside.memory().vector();
330 let mut kinds = inside.memory().map::<Oid<'a>, Option<&'a [u8]>>();
331
332 while let Some(entry) = inside.next_element().field(FIELD)? {
333 let at = |code| Error::new(code, FIELD, entry.span().start);
334 if entry.tag() != Tag::constructed(universal::SEQUENCE) {
335 return Err(at(ErrorCode::Encoding(
336 structured_asn1::ErrorCode::UnexpectedTag,
337 )));
338 }
339
340 let mut pair = inside.enter(&entry).field(FIELD)?;
341 let kind = pair.read_oid().field(FIELD)?;
342 let info_element = pair.next_element().field(FIELD)?;
343 let info = info_element.map(structured_asn1::Element::as_bytes);
344 let info_at =
345 info_element.map_or_else(|| entry.span().start, |element| element.span().start);
346 // Exactly one element. This check is what lets every decoder above
347 // re-parse the information knowing it holds one value and nothing
348 // after it: a statement written as a structure plus trailing bytes
349 // is refused here, not misread there.
350 if !pair.is_empty() {
351 return Err(at(ErrorCode::TrailingData));
352 }
353
354 // The payload is preserved for a scheme that knows the
355 // statement, and that scheme clears a critical extension on the
356 // strength of it, so the whole of it has to be the encoding the
357 // document claims and not only its outer tag.
358 if let Some(element) = info_element {
359 crate::name::walk_der(&pair, &element, FIELD, crate::name::Strings::Checked)?;
360 }
361
362 // One statement written twice with different information leaves
363 // no answer to what the issuer claimed. Written twice with the
364 // same information it claims one thing twice: the schema is a
365 // SEQUENCE OF with no uniqueness constraint, so the repeat is
366 // folded rather than refused. A map rather than a scan, so a
367 // long list costs the work its size says and not its square.
368 if let Some(existing) = kinds
369 .try_insert(kind, info)
370 .map_err(|reason| Error::resource(reason, FIELD, entry.span().start))?
371 {
372 // Deciding which of the two it is walks both payloads, which
373 // are opaque and have no local ceiling, so the longer of them
374 // is charged to the shared work budget first.
375 let compared = existing
376 .map_or(0, <[u8]>::len)
377 .max(info.map_or(0, <[u8]>::len));
378 inside.charge(compared as u64).field(FIELD)?;
379 if existing != info {
380 return Err(at(ErrorCode::MalformedValue));
381 }
382 continue;
383 }
384
385 // The compliance and qualified-device statements are bare claims
386 // whose identifiers define no information value. A value there is
387 // malformed statement data, and a reader that kept the claim
388 // while carrying it would report a qualified certificate on the
389 // strength of a statement that is not one.
390 if info.is_some() && (kind == oid::QC_COMPLIANCE || kind == oid::QC_SSCD) {
391 return Err(at(ErrorCode::MalformedValue));
392 }
393
394 statements
395 .try_push(QcStatement {
396 kind,
397 info,
398 at: info_at,
399 outer: inside.depth(),
400 })
401 .map_err(|reason| Error::resource(reason, FIELD, entry.span().start))?;
402 }
403
404 // No minimum size: QCStatements is a bare SEQUENCE OF (RFC 3739
405 // 3.2.6), unlike the lists this crate refuses empty, whose schema
406 // writes SIZE (1..MAX). An empty one makes no qualified claim, which
407 // is an answer a report can give.
408 Ok(Self {
409 statements,
410 parser: extension.parser(),
411 })
412 }
413}
414
415impl<'a> QcStatements<'a> {
416 /// Whether every statement was actually understood, for the gate that
417 /// decides what a critical extension blocked: each identifier is one
418 /// this crate names, and each structured payload has its shape. An error
419 /// is a spent budget and only that.
420 pub(crate) fn understood<E>(
421 &self,
422 recognizer: &impl crate::extension::Recognizer<Error = E>,
423 execution: &structured_asn1::memory::Memory,
424 ) -> crate::error::CallbackResult<bool, E> {
425 if gate(self.types(execution))?.is_none()
426 || gate(self.retention_period(execution))?.is_none()
427 {
428 return Ok(false);
429 }
430 // Two syntax statements that name different semantics leave the
431 // subject's identifiers with two readings and the certificate with
432 // no answer between them. Each statement is well formed on its own,
433 // so this is asked once over the pair rather than inside the walk
434 // below.
435 if gate(self.semantics(execution))?.is_none() {
436 return Ok(false);
437 }
438 for statement in &self.statements {
439 let work = (statement.kind.as_bytes().len() as u64)
440 .checked_mul(8)
441 .and_then(|work| work.checked_add(1))
442 .ok_or_else(|| {
443 Error::resource(
444 structured_asn1::memory::MemoryError::WorkExceeded,
445 STATEMENT_FIELD,
446 statement.at,
447 )
448 })?;
449 execution
450 .charge_work(work)
451 .map_err(|reason| Error::resource(reason, STATEMENT_FIELD, statement.at))?;
452 let kind = statement.kind;
453 let statement_understood = if kind == oid::QC_TYPE {
454 self.qc_types_understood(statement, execution)?
455 } else if kind == oid::QC_LIMIT_VALUE {
456 self.limit_value_understood(statement, execution)?
457 } else if kind == oid::QC_PDS {
458 self.pds_understood(statement, execution)?
459 } else if kind == oid::QC_CC_LEGISLATION {
460 self.legislation_understood(statement, execution)?
461 } else if kind == oid::PKIX_QC_SYNTAX_V1 || kind == oid::PKIX_QC_SYNTAX_V2 {
462 // Each syntax statement on its own, not just the one the
463 // accessor prefers: with both present, a well-formed
464 // preferred statement must not hide the other's garbage.
465 // An identifier decides how the subject's identifiers are
466 // read; one this crate does not name is a reading this run
467 // cannot perform. And the registration authorities answer
468 // the other-name question every list of names answers: a
469 // type nobody claims was never interpreted. The accessor
470 // still reports both.
471 match gate(self.semantics_parts(statement, execution))? {
472 None => false,
473 Some((identifier, authorities)) => {
474 identifier.is_none_or(|identifier| {
475 identifier == oid::SEMANTICS_NATURAL_PERSON
476 || identifier == oid::SEMANTICS_LEGAL_PERSON
477 }) && {
478 let mut known = true;
479 for name in &authorities {
480 execution.charge_work(1).map_err(|reason| {
481 Error::resource(reason, "qcStatements", statement.at)
482 })?;
483 if !crate::extension::other_name_known(name, recognizer, execution)?
484 {
485 known = false;
486 break;
487 }
488 }
489 known
490 }
491 }
492 }
493 } else {
494 // A statement this crate does not name may still be one the
495 // caller's scheme carries: a national statement is the
496 // scheme's knowledge, and only with neither does a critical
497 // list hold a claim nobody read.
498 is_known_statement(kind)
499 || recognizer
500 .recognizes_statement(kind, execution)
501 .map_err(crate::error::CallbackError::Callback)?
502 };
503 if !statement_understood {
504 return Ok(false);
505 }
506 }
507 Ok(true)
508 }
509
510 /// The type statement of ETSI EN 319 412-5, read for the gate: every
511 /// identifier is one of the three purposes this crate names.
512 ///
513 /// The accessor skips an unknown identifier so a report can still show
514 /// the rest; this gate cannot, because the payload's identifiers are what
515 /// carry the meaning, and a critical list declaring a purpose nobody here
516 /// can name was not understood (RFC 3739, 3.2.6: applications that do not
517 /// understand a statement in a critical extension reject the certificate).
518 fn qc_types_understood(&self, statement: &QcStatement<'_>, execution: &Memory) -> Result<bool> {
519 let Some(mut inside) = self.payload_sequence(statement, execution)? else {
520 return Ok(false);
521 };
522 // read_oid rather than the tag-agnostic decoding helper: the schema
523 // says SEQUENCE OF OBJECT IDENTIFIER, and this gate must not depend
524 // on another accessor happening to check the tag first.
525 let mut any = false;
526 while !inside.is_empty() {
527 let Some(identifier) = gate(inside.read_oid().field(STATEMENT_FIELD))? else {
528 return Ok(false);
529 };
530 if ![oid::QCT_ESIGN, oid::QCT_ESEAL, oid::QCT_WEB].contains(&identifier) {
531 return Ok(false);
532 }
533 any = true;
534 }
535 // The statement declares which purpose the certificate is issued for
536 // (ETSI EN 319 412-5, clause 4.2.3): zero purposes declares nothing
537 // while wearing the statement, which is not a statement understood.
538 Ok(any)
539 }
540
541 /// The monetary limit of ETSI EN 319 412-5: a currency as a three-letter
542 /// code or a number, then the amount and the exponent.
543 fn limit_value_understood(
544 &self,
545 statement: &QcStatement<'_>,
546 execution: &Memory,
547 ) -> Result<bool> {
548 let Some(mut inside) = self.payload_sequence(statement, execution)? else {
549 return Ok(false);
550 };
551 let Some(Some(currency)) = gate(inside.next_element().field(STATEMENT_FIELD))? else {
552 return Ok(false);
553 };
554 // The alphabetic choice is an ISO 4217 code, three uppercase
555 // letters; the numeric choice is INTEGER (1..999), that standard's
556 // numeric code space (ETSI EN 319 412-5, clause 4.3.2,
557 // Iso4217CurrencyCode). Either outside its form wears the type but
558 // names no currency. The universal tag is required first, because
559 // the decoding helper is deliberately tag-agnostic for implicitly
560 // tagged fields.
561 let currency_ok = (currency.tag() == Tag::primitive(universal::PRINTABLE_STRING)
562 && currency.contents().len() == 3
563 && currency.contents().iter().all(u8::is_ascii_uppercase))
564 || (currency.tag() == Tag::primitive(universal::INTEGER)
565 && gate(inside.integer(¤cy).field(STATEMENT_FIELD))?.is_some_and(|code| {
566 code.as_u32().is_some_and(|code| (1..=999).contains(&code))
567 }));
568 // The amount and the exponent are written `INTEGER`, with no range
569 // (ETSI EN 319 412-5, clause 4.3.2: `MonetaryValue ::= SEQUENCE {
570 // currency Iso4217CurrencyCode, amount INTEGER, exponent INTEGER }`,
571 // the currency being the only member the schema constrains). Neither
572 // is held to a sign here: a negative amount is inside the type, and
573 // refusing one would leave a critical extension unhandled over a
574 // value the schema names. What the issuer meant by it is the
575 // issuer's to answer, and this layer reports the claim rather than
576 // grading it.
577 Ok(currency_ok
578 && gate(inside.read_integer().field(STATEMENT_FIELD))?.is_some()
579 && gate(inside.read_integer().field(STATEMENT_FIELD))?.is_some()
580 && inside.is_empty())
581 }
582
583 /// The disclosure locations of ETSI EN 319 412-5: at least one pair of a
584 /// location and a two-letter language. Locations are read, never fetched.
585 fn pds_understood(&self, statement: &QcStatement<'_>, execution: &Memory) -> Result<bool> {
586 let Some(mut inside) = self.payload_sequence(statement, execution)? else {
587 return Ok(false);
588 };
589 let mut any = false;
590 loop {
591 match gate(inside.next_element().field(STATEMENT_FIELD))? {
592 Some(Some(location)) => {
593 if location.tag() != Tag::constructed(universal::SEQUENCE) {
594 return Ok(false);
595 }
596 let Some(mut pair) = gate(inside.enter(&location).field(STATEMENT_FIELD))?
597 else {
598 return Ok(false);
599 };
600 let Some(Some(url)) = gate(pair.next_element().field(STATEMENT_FIELD))? else {
601 return Ok(false);
602 };
603 let Some(Some(language)) = gate(pair.next_element().field(STATEMENT_FIELD))?
604 else {
605 return Ok(false);
606 };
607 // The location is a URL, so it answers the same
608 // absolute-URI question every location field answers;
609 // the language is an ISO 639-1 code, two lowercase
610 // letters, the form the pair's schema names. The URL
611 // scans touch every content byte and the field has no
612 // local size bound, so the length is charged to the
613 // shared work budget first.
614 pair.charge(url.contents().len() as u64)
615 .field(STATEMENT_FIELD)?;
616 if url.tag() != Tag::primitive(universal::IA5_STRING)
617 || !is_ia5(url.contents())
618 || !core::str::from_utf8(url.contents())
619 .is_ok_and(crate::general_name::is_absolute_uri)
620 || language.tag() != Tag::primitive(universal::PRINTABLE_STRING)
621 || language.contents().len() != 2
622 || !language.contents().iter().all(u8::is_ascii_lowercase)
623 || !pair.is_empty()
624 {
625 return Ok(false);
626 }
627 any = true;
628 }
629 Some(None) => return Ok(any && inside.is_empty()),
630 None => return Ok(false),
631 }
632 }
633 }
634
635 /// The legislation countries of ETSI EN 319 412-5: at least one
636 /// two-letter country code.
637 fn legislation_understood(
638 &self,
639 statement: &QcStatement<'_>,
640 execution: &Memory,
641 ) -> Result<bool> {
642 let Some(mut inside) = self.payload_sequence(statement, execution)? else {
643 return Ok(false);
644 };
645 let mut any = false;
646 loop {
647 match gate(inside.next_element().field(STATEMENT_FIELD))? {
648 Some(Some(country)) => {
649 // An ISO 3166-1 alpha-2 code, the form the entries are
650 // defined as: two uppercase letters. A digit or
651 // punctuation in the pair names no country.
652 if country.tag() != Tag::primitive(universal::PRINTABLE_STRING)
653 || country.contents().len() != 2
654 || !country.contents().iter().all(u8::is_ascii_uppercase)
655 {
656 return Ok(false);
657 }
658 any = true;
659 }
660 Some(None) => return Ok(any),
661 None => return Ok(false),
662 }
663 }
664 }
665
666 /// The one sequence a structured payload holds, or `None` where the
667 /// information is absent or not that. An error is a spent budget.
668 fn payload_sequence(
669 &self,
670 statement: &QcStatement<'a>,
671 execution: &Memory,
672 ) -> Result<Option<structured_asn1::Reader<'a>>> {
673 let Some(info) = statement.info else {
674 return Ok(None);
675 };
676 let Some(mut reader) = gate(
677 self.info_reader(info, statement, execution)
678 .field(STATEMENT_FIELD),
679 )?
680 else {
681 return Ok(None);
682 };
683 let Some(Some(sequence)) = gate(reader.next_element().field(STATEMENT_FIELD))? else {
684 return Ok(None);
685 };
686 if sequence.tag() != Tag::constructed(universal::SEQUENCE) || !reader.is_empty() {
687 return Ok(None);
688 }
689 gate(reader.enter(&sequence).field(STATEMENT_FIELD))
690 }
691}
692
693/// The field the statement gates above report a spent budget in.
694const STATEMENT_FIELD: &str = "extensions.qcStatements";
695
696/// Whether a qualified statement identifier is one this crate names.
697fn is_known_statement(kind: Oid<'_>) -> bool {
698 [
699 oid::QC_COMPLIANCE,
700 oid::QC_RETENTION_PERIOD,
701 oid::QC_SSCD,
702 oid::QC_TYPE,
703 oid::PKIX_QC_SYNTAX_V1,
704 oid::PKIX_QC_SYNTAX_V2,
705 ]
706 .contains(&kind)
707}
708
709/// Requires an element to be the list of registration authorities: a
710/// non-empty sequence of names. The names are validated as names, since a
711/// value that merely wears the sequence tag is the same malformed statement.
712fn require_authorities<'a>(
713 parent: &structured_asn1::Reader<'a>,
714 element: &structured_asn1::Element<'a>,
715) -> Result<OwnedVec<crate::general_name::GeneralName<'a>>> {
716 const FIELD: &str = "extensions.qcStatements.semanticsInformation";
717 if element.tag() != Tag::constructed(universal::SEQUENCE) {
718 return Err(Error::new(
719 ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag),
720 FIELD,
721 element.span().start,
722 ));
723 }
724 crate::general_name::read_all(parent, element, FIELD)
725}
726
727#[cfg(test)]
728mod tests;