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