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