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