Skip to main content

structured_cms/
signed.rs

1//! `SignedData` catalogues and version facts, without application trust decisions.
2
3use crate::content::{ContentInfo, Encapsulated};
4use crate::signer::{SignerInfo, required};
5use crate::{Error, ErrorCode, Result, oid};
6use structured_asn1::memory::{OwnedVec, Work};
7use structured_asn1::{Element, Encoding, Oid, Parser, Reader, Span, Tag, universal};
8use structured_pkix::{AlgorithmIdentifier, Certificate};
9pub mod revocation;
10
11/// One original certificate CHOICE (RFC5652 §10.2.2).
12///
13/// X.509 values live in one flat accounted catalogue, avoiding one Box
14/// allocation per certificate and avoiding a large enum slot for opaque kinds.
15#[derive(Debug, Clone, Copy)]
16pub enum CertificateChoice<'a> {
17    /// Index into this message's [`SignedData::certificates`] catalogue.
18    X509(usize),
19    /// Obsolete PKCS6 extended certificate, preserved without interpreting it.
20    Extended(Element<'a>),
21    /// Obsolete version-1 attribute certificate, preserved as encoded.
22    AttributeV1(Element<'a>),
23    /// Version-2 attribute certificate, preserved as encoded.
24    AttributeV2(Element<'a>),
25    /// Other format with its original format identifier and value.
26    Other(Element<'a>),
27}
28
29/// A schema-selected original `[0] IMPLICIT CertificateSet`, without a
30/// fabricated `SignedData` envelope. The same field kernel handles `PoE` extras.
31#[derive(Debug)]
32pub struct CertificateSet<'a> {
33    source: crate::signer::FieldsSource<'a>,
34    choices: OwnedVec<CertificateChoice<'a>>,
35    certificates: OwnedVec<Certificate<'a>>,
36}
37impl<'a> CertificateSet<'a> {
38    /// Reads all choices with producing/current admission before growth.
39    pub fn read(parent: &Reader<'a>, element: &Element<'a>, field: &'static str) -> Result<Self> {
40        let mut version = 1;
41        let (choices, certificates) = read_certificates(parent, element, &mut version, field)?;
42        Ok(Self {
43            source: crate::signer::FieldsSource::entered(parent, *element, field),
44            choices,
45            certificates,
46        })
47    }
48    /// Every original choice; X509 indices select this set's certificate pool.
49    #[must_use]
50    pub fn choices(&self) -> &[CertificateChoice<'a>] {
51        &self.choices
52    }
53    /// First-read X509 models, without cloning or reparsing certificate values.
54    #[must_use]
55    pub fn certificates(&self) -> &[Certificate<'a>] {
56        &self.certificates
57    }
58    /// Original choice cursor/depth, reusable under current query Work.
59    #[must_use]
60    pub fn fields_source(&self) -> crate::signer::FieldsSource<'a> {
61        self.source
62    }
63}
64
65/// Original `[1] IMPLICIT RevocationInfoChoices` from a schema-selected field.
66/// Framing/format validation is shared with `SignedData`; authentication and
67/// decoding the real CRL/OCSP wire formats remain separate operations.
68#[derive(Debug)]
69pub struct RevocationSet<'a> {
70    source: crate::signer::FieldsSource<'a>,
71    values: OwnedVec<Element<'a>>,
72}
73impl<'a> RevocationSet<'a> {
74    /// Reads the original complete choices without constructing an envelope.
75    pub fn read(parent: &Reader<'a>, element: &Element<'a>, field: &'static str) -> Result<Self> {
76        let mut version = 1;
77        let values = read_revocations(parent, element, &mut version, field)?;
78        Ok(Self {
79            source: crate::signer::FieldsSource::entered(parent, *element, field),
80            values,
81        })
82    }
83    /// Whole untouched CRL/other-format values in original catalogue order.
84    #[must_use]
85    pub fn values(&self) -> &[Element<'a>] {
86        &self.values
87    }
88    /// Original choice cursor/depth, reusable under current query Work.
89    #[must_use]
90    pub fn fields_source(&self) -> crate::signer::FieldsSource<'a> {
91        self.source
92    }
93}
94
95/// Original CMS other-revocation format and its untouched ANY value. Decoding
96/// a recognized format establishes no signature, authorization or freshness.
97#[derive(Debug)]
98pub struct OtherRevocationInfo<'a> {
99    reader: Reader<'a>,
100    kind: Oid<'a>,
101    value: Element<'a>,
102}
103
104impl<'a> OtherRevocationInfo<'a> {
105    /// Reads an original context \[1\] IMPLICIT `OtherRevocationInfoFormat` using
106    /// the same field kernel as `SignedData`'s structural catalogue validation.
107    pub fn read(parent: &Reader<'a>, element: &Element<'a>) -> Result<Self> {
108        const FIELD: &str = "signedData.crls.other";
109        if element.tag() != Tag::context(true, 1) {
110            return Err(Error::schema(
111                ErrorCode::UnexpectedTag,
112                FIELD,
113                element.span().start,
114            ));
115        }
116        let (reader, kind, value) = other_fields(parent, element, FIELD)?;
117        Ok(Self {
118            reader,
119            kind,
120            value,
121        })
122    }
123
124    /// Original open-format identifier, without provider/default normalization.
125    #[must_use]
126    pub fn kind(&self) -> Oid<'a> {
127        self.kind
128    }
129
130    /// Whole original ANY element, not its contents or a re-encoded value.
131    #[must_use]
132    pub fn value(&self) -> Element<'a> {
133        self.value
134    }
135
136    /// Decodes the successful whole `OCSPResponse` required by RFC5940 §3.
137    /// An unknown format returns None; a known malformed response or unsigned
138    /// protocol failure returns an error. Unsupported inner response syntax is
139    /// preserved by the shared envelope, rather than promoted to a basic body.
140    /// <https://www.rfc-editor.org/rfc/rfc5940#section-3>
141    pub fn ocsp(self) -> Result<Option<structured_pkix::ocsp::ResponseBytes<'a>>> {
142        const FIELD: &str = "signedData.crls.other";
143        self.reader
144            .charge(9)
145            .map_err(|error| Error::encoding(error, FIELD))?;
146        if self.kind != oid::OCSP_RESPONSE {
147            return Ok(None);
148        }
149        match structured_pkix::ocsp::Response::read_element(&self.reader, &self.value)
150            .map_err(Error::pkix)?
151        {
152            structured_pkix::ocsp::Response::Successful(response) => Ok(Some(response)),
153            structured_pkix::ocsp::Response::Failure { span, .. } => {
154                Err(Error::schema(ErrorCode::MalformedValue, FIELD, span.start))
155            }
156        }
157    }
158}
159
160/// Original CMS `SignedData` with independently accounted metadata and faults.
161#[derive(Debug)]
162pub struct SignedData<'a> {
163    parser: &'a Parser,
164    encoding: Encoding,
165    outer: u32,
166    version: u64,
167    required_version: u64,
168    algorithms: OwnedVec<AlgorithmIdentifier<'a>>,
169    content: Encapsulated<'a>,
170    choices: OwnedVec<CertificateChoice<'a>>,
171    certificates: OwnedVec<Certificate<'a>>,
172    certificate_field: Option<Span>,
173    revocations: OwnedVec<Element<'a>>,
174    revocation_field: Option<Span>,
175    signers: OwnedVec<SignerInfo<'a>>,
176    signer_set: Element<'a>,
177    faults: OwnedVec<Error>,
178    element: Element<'a>,
179}
180
181impl<'a> SignedData<'a> {
182    /// Reads one entire CMS `ContentInfo` and its `SignedData` using the same parser.
183    pub fn read(parser: &'a Parser, encoded: &'a [u8], base: u64) -> Result<Self> {
184        let mut root = parser
185            .read(encoded, base)
186            .map_err(|error| Error::encoding(error, "contentInfo"))?;
187        let (envelope, parent) = ContentInfo::read(&mut root)?;
188        parent
189            .execution()
190            .charge_work(envelope.kind().as_bytes().len() as u64)
191            .map_err(|reason| {
192                Error::resource(reason, "contentInfo.contentType", envelope.span().start)
193            })?;
194        if envelope.kind() != oid::SIGNED_DATA {
195            return Err(Error::schema(
196                ErrorCode::UnexpectedContentType,
197                "contentInfo.contentType",
198                envelope.span().start,
199            ));
200        }
201        Self::read_element(&parent, envelope.content())
202    }
203
204    /// Reads an original embedded `SignedData`, preserving enclosing depth/work.
205    pub fn read_element(parent: &Reader<'a>, element: &Element<'a>) -> Result<Self> {
206        const FIELD: &str = "signedData";
207        if element.tag() != Tag::constructed(universal::SEQUENCE) {
208            return Err(Error::schema(
209                ErrorCode::UnexpectedTag,
210                FIELD,
211                element.span().start,
212            ));
213        }
214        let mut inside = parent
215            .enter(element)
216            .map_err(|error| Error::encoding(error, FIELD))?;
217        let execution = parent.execution();
218        let (version, version_element) = read_version(&mut inside)?;
219        let set = required(&mut inside, "signedData.digestAlgorithms")?;
220        let algorithms = catalogue(
221            &inside,
222            &set,
223            Tag::constructed(universal::SET),
224            "signedData.digestAlgorithms",
225            |members, entry| {
226                structured_pkix::algorithm::read(members, entry, "signedData.digestAlgorithms")
227                    .map_err(Error::pkix)
228            },
229        )?;
230        let content_element = required(&mut inside, "signedData.encapContentInfo")?;
231        let content = Encapsulated::read(&inside, &content_element)?;
232        execution
233            .charge_work(content.kind().as_bytes().len() as u64)
234            .map_err(|reason| {
235                Error::resource(
236                    reason,
237                    "signedData.encapContentInfo.eContentType",
238                    content.span().start,
239                )
240            })?;
241        // RFC5652 §5.1 ladder, computed during the admitted schema traversal:
242        // 5 (other), 4 (v2 AC), 3 (v1 AC/v3 signer/non-data), otherwise 1.
243        // https://www.rfc-editor.org/rfc/rfc5652#section-5.1
244        let mut required_version = if content.kind() == oid::DATA { 1 } else { 3 };
245        let mut choices = parent.memory().vector();
246        let mut certificates = parent.memory().vector();
247        let mut revocations = parent.memory().vector();
248        let mut certificate_field = None;
249        let mut revocation_field = None;
250        let mut next = required(&mut inside, "signedData.signerInfos")?;
251        if next.tag() == Tag::context(true, 0) {
252            certificate_field = Some(next.span());
253            (choices, certificates) = read_certificates(
254                &inside,
255                &next,
256                &mut required_version,
257                "signedData.certificates",
258            )?;
259            next = required(&mut inside, "signedData.signerInfos")?;
260        }
261        if next.tag() == Tag::context(true, 1) {
262            revocation_field = Some(next.span());
263            revocations =
264                read_revocations(&inside, &next, &mut required_version, "signedData.crls")?;
265            next = required(&mut inside, "signedData.signerInfos")?;
266        }
267        let signers = catalogue(
268            &inside,
269            &next,
270            Tag::constructed(universal::SET),
271            "signedData.signerInfos",
272            |members, entry| {
273                let signer = SignerInfo::read(members, entry)?;
274                if signer.version() == 3 {
275                    required_version = required_version.max(3);
276                }
277                Ok(signer)
278            },
279        )?;
280        if !inside.is_empty() {
281            return Err(Error::schema(
282                ErrorCode::TrailingData,
283                FIELD,
284                inside.offset(),
285            ));
286        }
287        let faults = version_fault(&execution, version, required_version, &version_element)?;
288        Ok(Self {
289            parser: parent.parser(),
290            encoding: parent.encoding(),
291            outer: inside.depth(),
292            version,
293            required_version,
294            algorithms,
295            content,
296            choices,
297            certificates,
298            certificate_field,
299            revocations,
300            revocation_field,
301            signers,
302            signer_set: next,
303            faults,
304            element: *element,
305        })
306    }
307
308    /// Declared version.
309    #[must_use]
310    pub fn version(&self) -> u64 {
311        self.version
312    }
313    /// Original complete `SignedData` fields, with their actual first-read
314    /// depth and source identity. Opening applies current and original Work.
315    #[must_use]
316    pub fn fields_source(&self) -> crate::signer::FieldsSource<'a> {
317        crate::signer::FieldsSource {
318            parser: self.parser,
319            data: self.element.contents(),
320            base: self.element.contents_span().start,
321            encoding: self.encoding,
322            depth: self.outer,
323            field: "signedData",
324        }
325    }
326    /// Version independently required by the encoded choices.
327    #[must_use]
328    pub fn required_version(&self) -> u64 {
329        self.required_version
330    }
331    /// Original advertised digest algorithms; provider resolution is separate.
332    /// RFC5652 §§5.1, 5.3 permit zero entries and do not require recipients to
333    /// refuse an unlisted signer digest. These advertisements never replace the
334    /// original signer algorithm; stricter catalogue acceptance is caller policy.
335    /// <https://www.rfc-editor.org/rfc/rfc5652#section-5.1>
336    #[must_use]
337    pub fn digest_algorithms(&self) -> &[AlgorithmIdentifier<'a>] {
338        &self.algorithms
339    }
340    /// First-read original `SignerInfos` SET, preserving BER lengths/EOC for
341    /// complete-object evidence inputs. Its members remain this same model.
342    #[must_use]
343    pub fn signer_set_element(&self) -> Element<'a> {
344        self.signer_set
345    }
346    /// Original attached or absent content.
347    #[must_use]
348    pub fn content(&self) -> &Encapsulated<'a> {
349        &self.content
350    }
351    /// Whole original `encapContentInfo` TLV, including BER framing/EOC.
352    /// No element is reopened, normalized or copied. Historical archive inputs
353    /// distinguish this from the logical eContent octets (RFC5126 §6.4.1).
354    #[must_use]
355    pub fn encap_content_encoding(&self) -> &'a [u8] {
356        self.field_encoding(self.content.span())
357    }
358    /// Certificate choices in their original order.
359    #[must_use]
360    pub fn certificate_choices(&self) -> &[CertificateChoice<'a>] {
361        &self.choices
362    }
363    /// Parsed X.509 certificates, in the order referenced by X509 choices.
364    #[must_use]
365    pub fn certificates(&self) -> &[Certificate<'a>] {
366        &self.certificates
367    }
368    /// Original presence of the certificate field, including an empty written set.
369    #[must_use]
370    pub fn certificate_field(&self) -> Option<Span> {
371        self.certificate_field
372    }
373    /// Whole original optional certificate field, including its context tag.
374    /// An absent field differs from a written empty set; bytes are borrowed.
375    #[must_use]
376    pub fn certificate_encoding(&self) -> Option<&'a [u8]> {
377        self.certificate_field.map(|span| self.field_encoding(span))
378    }
379    /// Opens an original non-X.509 certificate CHOICE under the current query.
380    ///
381    /// Preserves the original parser, encoding, location and certificate-set
382    /// depth. The current operation funds new work while original ceilings
383    /// remain active. An absent ordinal or X.509 index returns `None` after
384    /// metadata admission; parsed X.509 certificates have their own accessors.
385    /// No certificate is implicitly trusted or decoded by opening this reader.
386    pub fn certificate_choice_reader_in<'query>(
387        &self,
388        query: &Reader<'query>,
389        ordinal: usize,
390    ) -> Result<Option<Reader<'query>>>
391    where
392        'a: 'query,
393    {
394        const FIELD: &str = "signedData.certificates";
395        let at = self.certificate_field.unwrap_or(self.element.span()).start;
396        query
397            .execution()
398            .charge_work(1)
399            .map_err(|reason| Error::resource(reason, FIELD, at))?;
400        let entry = match self.choices.get(ordinal) {
401            Some(
402                CertificateChoice::Extended(entry)
403                | CertificateChoice::AttributeV1(entry)
404                | CertificateChoice::AttributeV2(entry)
405                | CertificateChoice::Other(entry),
406            ) => entry,
407            Some(CertificateChoice::X509(_)) | None => return Ok(None),
408        };
409        self.parser
410            .read_nested_query_in(
411                query,
412                entry.as_bytes(),
413                entry.span().start,
414                self.encoding,
415                self.outer,
416            )
417            .map(Some)
418            .map_err(|error| Error::encoding(error, FIELD))
419    }
420    /// Original presence of the revocation field, including an empty written set.
421    #[must_use]
422    pub fn revocation_field(&self) -> Option<Span> {
423        self.revocation_field
424    }
425    /// Whole original optional revocation field, not assembled member bytes.
426    #[must_use]
427    pub fn revocation_encoding(&self) -> Option<&'a [u8]> {
428        self.revocation_field.map(|span| self.field_encoding(span))
429    }
430    /// Original revocation choices, not implicitly authenticated or fetched.
431    #[must_use]
432    pub fn revocations(&self) -> &[Element<'a>] {
433        &self.revocations
434    }
435    /// Opens one original revocation CHOICE under the current query operation.
436    ///
437    /// The reader contains the whole original CRL or other-format CHOICE, not
438    /// its contents. Original parser, encoding, location and set-member depth
439    /// are preserved; the query may come from another document and pays all
440    /// new work/storage while both source and operation ceilings remain active.
441    /// No bytes are copied and no new parser or authentication is introduced.
442    /// An absent ordinal returns `None` only after paid metadata admission.
443    pub fn revocation_reader_in<'query>(
444        &self,
445        query: &Reader<'query>,
446        ordinal: usize,
447    ) -> Result<Option<Reader<'query>>>
448    where
449        'a: 'query,
450    {
451        const FIELD: &str = "signedData.crls";
452        let at = self.revocation_field.unwrap_or(self.element.span()).start;
453        query
454            .execution()
455            .charge_work(1)
456            .map_err(|reason| Error::resource(reason, FIELD, at))?;
457        let Some(entry) = self.revocations.get(ordinal) else {
458            return Ok(None);
459        };
460        self.parser
461            .read_nested_query_in(
462                query,
463                entry.as_bytes(),
464                entry.span().start,
465                self.encoding,
466                self.outer,
467            )
468            .map(Some)
469            .map_err(|error| Error::encoding(error, FIELD))
470    }
471    /// Independent signers in their original order, without an all-signers verdict.
472    #[must_use]
473    pub fn signers(&self) -> &[SignerInfo<'a>] {
474        &self.signers
475    }
476    /// Refuses a reader of another parser session.
477    pub fn require_session(&self, parent: &Reader<'_>, field: &'static str) -> Result<()> {
478        if !core::ptr::eq(self.parser, parent.parser()) {
479            return Err(Error::pkix(structured_pkix::Error::new(
480                structured_pkix::ErrorCode::ForeignReader,
481                field,
482                self.element.span().start,
483            )));
484        }
485        Ok(())
486    }
487    /// Refuses a `SignerInfo` that is not one of these signers, by identity.
488    ///
489    /// Constant work: a reference to one of these signers lies inside their
490    /// storage, so an address range test replaces a scan of every signer.
491    pub fn require_signer(&self, signer: &SignerInfo<'_>, field: &'static str) -> Result<()> {
492        let storage = self.signers.as_ptr_range();
493        let address = core::ptr::from_ref(signer).cast::<SignerInfo<'a>>();
494        if !storage.contains(&address) {
495            return Err(Error::schema(
496                ErrorCode::InvalidLocation,
497                field,
498                signer.span().start,
499            ));
500        }
501        Ok(())
502    }
503    /// Readable document faults, retaining their allocation lifetime.
504    #[must_use]
505    pub fn faults(&self) -> &[Error] {
506        &self.faults
507    }
508    /// Original `SignedData` source range.
509    #[must_use]
510    pub fn span(&self) -> Span {
511        self.element.span()
512    }
513    /// Original encoding without serialization.
514    #[must_use]
515    pub fn as_bytes(&self) -> &'a [u8] {
516        self.element.as_bytes()
517    }
518
519    fn field_encoding(&self, span: Span) -> &'a [u8] {
520        // Each saved field was produced inside this exact element. Its
521        // differences therefore fit the borrowed encoding's usize length.
522        let start = usize::try_from(span.start - self.element.span().start)
523            .expect("original field starts inside SignedData");
524        let length = usize::try_from(span.length).expect("original field length fits SignedData");
525        &self.element.as_bytes()[start..start + length]
526    }
527}
528
529fn version_fault(
530    execution: &impl Work,
531    version: u64,
532    required: u64,
533    element: &Element<'_>,
534) -> Result<OwnedVec<Error>> {
535    let mut faults = execution.vector();
536    if version != required {
537        faults
538            .try_push(Error::schema(
539                ErrorCode::VersionMismatch,
540                "signedData.version",
541                element.span().start,
542            ))
543            .map_err(|reason| {
544                Error::resource(reason, "signedData.version", element.span().start)
545            })?;
546    }
547    Ok(faults.into_owned())
548}
549
550fn ordered<'a>(
551    parent: &Reader<'a>,
552    previous: &mut Option<&'a [u8]>,
553    element: &Element<'a>,
554    field: &'static str,
555) -> Result<()> {
556    if parent.encoding().is_der() {
557        if let Some(earlier) = *previous
558            && !structured_pkix::der::set_ordered(parent, earlier, element, field)
559                .map_err(Error::pkix)?
560        {
561            return Err(Error::schema(
562                ErrorCode::SetOrder,
563                field,
564                element.span().start,
565            ));
566        }
567        *previous = Some(element.as_bytes());
568    }
569    Ok(())
570}
571
572fn other_format(parent: &Reader<'_>, element: &Element<'_>, field: &'static str) -> Result<()> {
573    other_fields(parent, element, field).map(|_| ())
574}
575
576fn other_fields<'a>(
577    parent: &Reader<'a>,
578    element: &Element<'a>,
579    field: &'static str,
580) -> Result<(Reader<'a>, Oid<'a>, Element<'a>)> {
581    let mut inside = parent
582        .enter(element)
583        .map_err(|error| Error::encoding(error, field))?;
584    let kind = inside
585        .read_oid()
586        .map_err(|error| Error::encoding(error, field))?;
587    let value = required(&mut inside, field)?;
588    if !inside.is_empty() {
589        return Err(Error::schema(
590            ErrorCode::TrailingData,
591            field,
592            inside.offset(),
593        ));
594    }
595    Ok((inside, kind, value))
596}
597
598pub(crate) fn catalogue<'a, T>(
599    parent: &Reader<'a>,
600    set: &Element<'a>,
601    tag: Tag,
602    field: &'static str,
603    mut read: impl FnMut(&Reader<'a>, &Element<'a>) -> Result<T>,
604) -> Result<OwnedVec<T>> {
605    if set.tag() != tag {
606        return Err(Error::schema(
607            ErrorCode::UnexpectedTag,
608            field,
609            set.span().start,
610        ));
611    }
612    let mut members = parent
613        .enter(set)
614        .map_err(|error| Error::encoding(error, field))?;
615    let execution = parent.execution();
616    let mut values = execution.vector();
617    let mut previous = None;
618    while let Some(entry) = members
619        .next_element()
620        .map_err(|error| Error::encoding(error, field))?
621    {
622        ordered(&members, &mut previous, &entry, field)?;
623        values
624            .try_reserve(1)
625            .map_err(|reason| Error::resource(reason, field, entry.span().start))?;
626        let value = read(&members, &entry)?;
627        values
628            .try_push(value)
629            .map_err(|reason| Error::resource(reason, field, entry.span().start))?;
630    }
631    Ok(values.into_owned())
632}
633
634pub(crate) fn read_certificates<'a>(
635    parent: &Reader<'a>,
636    set: &Element<'a>,
637    version: &mut u64,
638    field: &'static str,
639) -> Result<(OwnedVec<CertificateChoice<'a>>, OwnedVec<Certificate<'a>>)> {
640    let execution = parent.execution();
641    let mut certificates = execution.vector();
642    let choices = catalogue(
643        parent,
644        set,
645        Tag::context(true, 0),
646        field,
647        |members, entry| {
648            Ok(match entry.tag() {
649                tag if tag == Tag::constructed(universal::SEQUENCE) => {
650                    certificates
651                        .try_reserve(1)
652                        .map_err(|reason| Error::resource(reason, field, entry.span().start))?;
653                    let certificate =
654                        Certificate::read_element(members, entry).map_err(Error::pkix)?;
655                    let index = certificates.len();
656                    certificates
657                        .try_push(certificate)
658                        .map_err(|reason| Error::resource(reason, field, entry.span().start))?;
659                    CertificateChoice::X509(index)
660                }
661                tag if tag == Tag::context(true, 0) => CertificateChoice::Extended(*entry),
662                tag if tag == Tag::context(true, 1) => {
663                    *version = (*version).max(3);
664                    CertificateChoice::AttributeV1(*entry)
665                }
666                tag if tag == Tag::context(true, 2) => {
667                    *version = (*version).max(4);
668                    CertificateChoice::AttributeV2(*entry)
669                }
670                tag if tag == Tag::context(true, 3) => {
671                    other_format(members, entry, field)?;
672                    *version = 5;
673                    CertificateChoice::Other(*entry)
674                }
675                _ => {
676                    return Err(Error::schema(
677                        ErrorCode::UnexpectedTag,
678                        field,
679                        entry.span().start,
680                    ));
681                }
682            })
683        },
684    )?;
685    Ok((choices, certificates.into_owned()))
686}
687
688fn read_version<'a>(inside: &mut Reader<'a>) -> Result<(u64, Element<'a>)> {
689    const FIELD: &str = "signedData.version";
690    let element = inside
691        .expect(Tag::primitive(universal::INTEGER))
692        .map_err(|error| Error::encoding(error, FIELD))?;
693    let version = inside
694        .integer(&element)
695        .map_err(|error| Error::encoding(error, FIELD))?
696        .as_u64()
697        .ok_or_else(|| Error::schema(ErrorCode::ValueTooLarge, FIELD, element.span().start))?;
698    Ok((version, element))
699}
700
701pub(crate) fn read_revocations<'a>(
702    parent: &Reader<'a>,
703    set: &Element<'a>,
704    version: &mut u64,
705    field: &'static str,
706) -> Result<OwnedVec<Element<'a>>> {
707    catalogue(
708        parent,
709        set,
710        Tag::context(true, 1),
711        field,
712        |members, entry| {
713            if entry.tag() == Tag::context(true, 1) {
714                other_format(members, entry, field)?;
715                *version = 5;
716            } else if entry.tag() != Tag::constructed(universal::SEQUENCE) {
717                return Err(Error::schema(
718                    ErrorCode::UnexpectedTag,
719                    field,
720                    entry.span().start,
721                ));
722            }
723            Ok(*entry)
724        },
725    )
726}