Skip to main content

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(&currency).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;