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, Work};
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 work = inside.execution_in(execution);
330 let mut statements = work.vector();
331 let mut kinds = work.map::<Oid<'a>, Option<&'a [u8]>>();
332
333 while let Some(entry) = inside.next_element().field(FIELD)? {
334 let at = |code| Error::new(code, FIELD, entry.span().start);
335 if entry.tag() != Tag::constructed(universal::SEQUENCE) {
336 return Err(at(ErrorCode::Encoding(
337 structured_asn1::ErrorCode::UnexpectedTag,
338 )));
339 }
340
341 let mut pair = inside.enter(&entry).field(FIELD)?;
342 let kind = pair.read_oid().field(FIELD)?;
343 let info_element = pair.next_element().field(FIELD)?;
344 let info = info_element.map(structured_asn1::Element::as_bytes);
345 let info_at =
346 info_element.map_or_else(|| entry.span().start, |element| element.span().start);
347 // Exactly one element. This check is what lets every decoder above
348 // re-parse the information knowing it holds one value and nothing
349 // after it: a statement written as a structure plus trailing bytes
350 // is refused here, not misread there.
351 if !pair.is_empty() {
352 return Err(at(ErrorCode::TrailingData));
353 }
354
355 // The payload is preserved for a scheme that knows the
356 // statement, and that scheme clears a critical extension on the
357 // strength of it, so the whole of it has to be the encoding the
358 // document claims and not only its outer tag.
359 if let Some(element) = info_element {
360 crate::name::walk_der(&pair, &element, FIELD, crate::name::Strings::Checked)?;
361 }
362
363 // One statement written twice with different information leaves
364 // no answer to what the issuer claimed. Written twice with the
365 // same information it claims one thing twice: the schema is a
366 // SEQUENCE OF with no uniqueness constraint, so the repeat is
367 // folded rather than refused. A map rather than a scan, so a
368 // long list costs the work its size says and not its square.
369 if let Some(existing) = kinds
370 .try_insert(kind, info)
371 .map_err(|reason| Error::resource(reason, FIELD, entry.span().start))?
372 {
373 // Deciding which of the two it is walks both payloads, which
374 // are opaque and have no local ceiling, so the longer of them
375 // is charged to the shared work budget first.
376 let compared = existing
377 .map_or(0, <[u8]>::len)
378 .max(info.map_or(0, <[u8]>::len));
379 inside.charge(compared as u64).field(FIELD)?;
380 if existing != info {
381 return Err(at(ErrorCode::MalformedValue));
382 }
383 continue;
384 }
385
386 // The compliance and qualified-device statements are bare claims
387 // whose identifiers define no information value. A value there is
388 // malformed statement data, and a reader that kept the claim
389 // while carrying it would report a qualified certificate on the
390 // strength of a statement that is not one.
391 if info.is_some() && (kind == oid::QC_COMPLIANCE || kind == oid::QC_SSCD) {
392 return Err(at(ErrorCode::MalformedValue));
393 }
394
395 statements
396 .try_push(QcStatement {
397 kind,
398 info,
399 at: info_at,
400 outer: inside.depth(),
401 })
402 .map_err(|reason| Error::resource(reason, FIELD, entry.span().start))?;
403 }
404
405 // No minimum size: QCStatements is a bare SEQUENCE OF (RFC 3739
406 // 3.2.6), unlike the lists this crate refuses empty, whose schema
407 // writes SIZE (1..MAX). An empty one makes no qualified claim, which
408 // is an answer a report can give.
409 Ok(Self {
410 statements: statements.into_owned(),
411 parser: extension.parser(),
412 })
413 }
414}
415
416impl<'a> QcStatements<'a> {
417 /// Whether every statement was actually understood, for the gate that
418 /// decides what a critical extension blocked: each identifier is one
419 /// this crate names, and each structured payload has its shape. An error
420 /// is a spent budget and only that.
421 pub(crate) fn understood<E>(
422 &self,
423 recognizer: &impl crate::extension::Recognizer<Error = E>,
424 execution: &structured_asn1::memory::Memory,
425 ) -> crate::error::CallbackResult<bool, E> {
426 if gate(self.types(execution))?.is_none()
427 || gate(self.retention_period(execution))?.is_none()
428 {
429 return Ok(false);
430 }
431 // Two syntax statements that name different semantics leave the
432 // subject's identifiers with two readings and the certificate with
433 // no answer between them. Each statement is well formed on its own,
434 // so this is asked once over the pair rather than inside the walk
435 // below.
436 if gate(self.semantics(execution))?.is_none() {
437 return Ok(false);
438 }
439 for statement in &self.statements {
440 let work = (statement.kind.as_bytes().len() as u64)
441 .checked_mul(8)
442 .and_then(|work| work.checked_add(1))
443 .ok_or_else(|| {
444 Error::resource(
445 structured_asn1::memory::MemoryError::WorkExceeded,
446 STATEMENT_FIELD,
447 statement.at,
448 )
449 })?;
450 execution
451 .charge_work(work)
452 .map_err(|reason| Error::resource(reason, STATEMENT_FIELD, statement.at))?;
453 let kind = statement.kind;
454 let statement_understood = if kind == oid::QC_TYPE {
455 self.qc_types_understood(statement, execution)?
456 } else if kind == oid::QC_LIMIT_VALUE {
457 self.limit_value_understood(statement, execution)?
458 } else if kind == oid::QC_PDS {
459 self.pds_understood(statement, execution)?
460 } else if kind == oid::QC_CC_LEGISLATION {
461 self.legislation_understood(statement, execution)?
462 } else if kind == oid::PKIX_QC_SYNTAX_V1 || kind == oid::PKIX_QC_SYNTAX_V2 {
463 // Each syntax statement on its own, not just the one the
464 // accessor prefers: with both present, a well-formed
465 // preferred statement must not hide the other's garbage.
466 // An identifier decides how the subject's identifiers are
467 // read; one this crate does not name is a reading this run
468 // cannot perform. And the registration authorities answer
469 // the other-name question every list of names answers: a
470 // type nobody claims was never interpreted. The accessor
471 // still reports both.
472 match gate(self.semantics_parts(statement, execution))? {
473 None => false,
474 Some((identifier, authorities)) => {
475 identifier.is_none_or(|identifier| {
476 identifier == oid::SEMANTICS_NATURAL_PERSON
477 || identifier == oid::SEMANTICS_LEGAL_PERSON
478 }) && {
479 let mut known = true;
480 for name in &authorities {
481 execution.charge_work(1).map_err(|reason| {
482 Error::resource(reason, "qcStatements", statement.at)
483 })?;
484 if !crate::extension::other_name_known(name, recognizer, execution)?
485 {
486 known = false;
487 break;
488 }
489 }
490 known
491 }
492 }
493 }
494 } else {
495 // A statement this crate does not name may still be one the
496 // caller's scheme carries: a national statement is the
497 // scheme's knowledge, and only with neither does a critical
498 // list hold a claim nobody read.
499 is_known_statement(kind)
500 || recognizer
501 .recognizes_statement(kind, execution)
502 .map_err(crate::error::CallbackError::Callback)?
503 };
504 if !statement_understood {
505 return Ok(false);
506 }
507 }
508 Ok(true)
509 }
510
511 /// The type statement of ETSI EN 319 412-5, read for the gate: every
512 /// identifier is one of the three purposes this crate names.
513 ///
514 /// The accessor skips an unknown identifier so a report can still show
515 /// the rest; this gate cannot, because the payload's identifiers are what
516 /// carry the meaning, and a critical list declaring a purpose nobody here
517 /// can name was not understood (RFC 3739, 3.2.6: applications that do not
518 /// understand a statement in a critical extension reject the certificate).
519 fn qc_types_understood(&self, statement: &QcStatement<'_>, execution: &Memory) -> Result<bool> {
520 let Some(mut inside) = self.payload_sequence(statement, execution)? else {
521 return Ok(false);
522 };
523 // read_oid rather than the tag-agnostic decoding helper: the schema
524 // says SEQUENCE OF OBJECT IDENTIFIER, and this gate must not depend
525 // on another accessor happening to check the tag first.
526 let mut any = false;
527 while !inside.is_empty() {
528 let Some(identifier) = gate(inside.read_oid().field(STATEMENT_FIELD))? else {
529 return Ok(false);
530 };
531 if ![oid::QCT_ESIGN, oid::QCT_ESEAL, oid::QCT_WEB].contains(&identifier) {
532 return Ok(false);
533 }
534 any = true;
535 }
536 // The statement declares which purpose the certificate is issued for
537 // (ETSI EN 319 412-5, clause 4.2.3): zero purposes declares nothing
538 // while wearing the statement, which is not a statement understood.
539 Ok(any)
540 }
541
542 /// The monetary limit of ETSI EN 319 412-5: a currency as a three-letter
543 /// code or a number, then the amount and the exponent.
544 fn limit_value_understood(
545 &self,
546 statement: &QcStatement<'_>,
547 execution: &Memory,
548 ) -> Result<bool> {
549 let Some(mut inside) = self.payload_sequence(statement, execution)? else {
550 return Ok(false);
551 };
552 let Some(Some(currency)) = gate(inside.next_element().field(STATEMENT_FIELD))? else {
553 return Ok(false);
554 };
555 // The alphabetic choice is an ISO 4217 code, three uppercase
556 // letters; the numeric choice is INTEGER (1..999), that standard's
557 // numeric code space (ETSI EN 319 412-5, clause 4.3.2,
558 // Iso4217CurrencyCode). Either outside its form wears the type but
559 // names no currency. The universal tag is required first, because
560 // the decoding helper is deliberately tag-agnostic for implicitly
561 // tagged fields.
562 let currency_ok = (currency.tag() == Tag::primitive(universal::PRINTABLE_STRING)
563 && currency.contents().len() == 3
564 && currency.contents().iter().all(u8::is_ascii_uppercase))
565 || (currency.tag() == Tag::primitive(universal::INTEGER)
566 && gate(inside.integer(¤cy).field(STATEMENT_FIELD))?.is_some_and(|code| {
567 code.as_u32().is_some_and(|code| (1..=999).contains(&code))
568 }));
569 // The amount and the exponent are written `INTEGER`, with no range
570 // (ETSI EN 319 412-5, clause 4.3.2: `MonetaryValue ::= SEQUENCE {
571 // currency Iso4217CurrencyCode, amount INTEGER, exponent INTEGER }`,
572 // the currency being the only member the schema constrains). Neither
573 // is held to a sign here: a negative amount is inside the type, and
574 // refusing one would leave a critical extension unhandled over a
575 // value the schema names. What the issuer meant by it is the
576 // issuer's to answer, and this layer reports the claim rather than
577 // grading it.
578 Ok(currency_ok
579 && gate(inside.read_integer().field(STATEMENT_FIELD))?.is_some()
580 && gate(inside.read_integer().field(STATEMENT_FIELD))?.is_some()
581 && inside.is_empty())
582 }
583
584 /// The disclosure locations of ETSI EN 319 412-5: at least one pair of a
585 /// location and a two-letter language. Locations are read, never fetched.
586 fn pds_understood(&self, statement: &QcStatement<'_>, execution: &Memory) -> Result<bool> {
587 let Some(mut inside) = self.payload_sequence(statement, execution)? else {
588 return Ok(false);
589 };
590 let mut any = false;
591 loop {
592 match gate(inside.next_element().field(STATEMENT_FIELD))? {
593 Some(Some(location)) => {
594 if location.tag() != Tag::constructed(universal::SEQUENCE) {
595 return Ok(false);
596 }
597 let Some(mut pair) = gate(inside.enter(&location).field(STATEMENT_FIELD))?
598 else {
599 return Ok(false);
600 };
601 let Some(Some(url)) = gate(pair.next_element().field(STATEMENT_FIELD))? else {
602 return Ok(false);
603 };
604 let Some(Some(language)) = gate(pair.next_element().field(STATEMENT_FIELD))?
605 else {
606 return Ok(false);
607 };
608 // The location is a URL, so it answers the same
609 // absolute-URI question every location field answers;
610 // the language is an ISO 639-1 code, two lowercase
611 // letters, the form the pair's schema names. The URL
612 // scans touch every content byte and the field has no
613 // local size bound, so the length is charged to the
614 // shared work budget first.
615 pair.charge(url.contents().len() as u64)
616 .field(STATEMENT_FIELD)?;
617 if url.tag() != Tag::primitive(universal::IA5_STRING)
618 || !is_ia5(url.contents())
619 || !core::str::from_utf8(url.contents())
620 .is_ok_and(crate::general_name::is_absolute_uri)
621 || language.tag() != Tag::primitive(universal::PRINTABLE_STRING)
622 || language.contents().len() != 2
623 || !language.contents().iter().all(u8::is_ascii_lowercase)
624 || !pair.is_empty()
625 {
626 return Ok(false);
627 }
628 any = true;
629 }
630 Some(None) => return Ok(any && inside.is_empty()),
631 None => return Ok(false),
632 }
633 }
634 }
635
636 /// The legislation countries of ETSI EN 319 412-5: at least one
637 /// two-letter country code.
638 fn legislation_understood(
639 &self,
640 statement: &QcStatement<'_>,
641 execution: &Memory,
642 ) -> Result<bool> {
643 let Some(mut inside) = self.payload_sequence(statement, execution)? else {
644 return Ok(false);
645 };
646 let mut any = false;
647 loop {
648 match gate(inside.next_element().field(STATEMENT_FIELD))? {
649 Some(Some(country)) => {
650 // An ISO 3166-1 alpha-2 code, the form the entries are
651 // defined as: two uppercase letters. A digit or
652 // punctuation in the pair names no country.
653 if country.tag() != Tag::primitive(universal::PRINTABLE_STRING)
654 || country.contents().len() != 2
655 || !country.contents().iter().all(u8::is_ascii_uppercase)
656 {
657 return Ok(false);
658 }
659 any = true;
660 }
661 Some(None) => return Ok(any),
662 None => return Ok(false),
663 }
664 }
665 }
666
667 /// The one sequence a structured payload holds, or `None` where the
668 /// information is absent or not that. An error is a spent budget.
669 fn payload_sequence(
670 &self,
671 statement: &QcStatement<'a>,
672 execution: &Memory,
673 ) -> Result<Option<structured_asn1::Reader<'a>>> {
674 let Some(info) = statement.info else {
675 return Ok(None);
676 };
677 let Some(mut reader) = gate(
678 self.info_reader(info, statement, execution)
679 .field(STATEMENT_FIELD),
680 )?
681 else {
682 return Ok(None);
683 };
684 let Some(Some(sequence)) = gate(reader.next_element().field(STATEMENT_FIELD))? else {
685 return Ok(None);
686 };
687 if sequence.tag() != Tag::constructed(universal::SEQUENCE) || !reader.is_empty() {
688 return Ok(None);
689 }
690 gate(reader.enter(&sequence).field(STATEMENT_FIELD))
691 }
692}
693
694/// The field the statement gates above report a spent budget in.
695const STATEMENT_FIELD: &str = "extensions.qcStatements";
696
697/// Whether a qualified statement identifier is one this crate names.
698fn is_known_statement(kind: Oid<'_>) -> bool {
699 [
700 oid::QC_COMPLIANCE,
701 oid::QC_RETENTION_PERIOD,
702 oid::QC_SSCD,
703 oid::QC_TYPE,
704 oid::PKIX_QC_SYNTAX_V1,
705 oid::PKIX_QC_SYNTAX_V2,
706 ]
707 .contains(&kind)
708}
709
710/// Requires an element to be the list of registration authorities: a
711/// non-empty sequence of names. The names are validated as names, since a
712/// value that merely wears the sequence tag is the same malformed statement.
713fn require_authorities<'a>(
714 parent: &structured_asn1::Reader<'a>,
715 element: &structured_asn1::Element<'a>,
716) -> Result<OwnedVec<crate::general_name::GeneralName<'a>>> {
717 const FIELD: &str = "extensions.qcStatements.semanticsInformation";
718 if element.tag() != Tag::constructed(universal::SEQUENCE) {
719 return Err(Error::new(
720 ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag),
721 FIELD,
722 element.span().start,
723 ));
724 }
725 crate::general_name::read_all(parent, element, FIELD)
726}
727
728#[cfg(test)]
729mod tests;