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, 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/// Original CMS `SignedData` with independently accounted metadata and faults.
29#[derive(Debug)]
30pub struct SignedData<'a> {
31    version: u64,
32    required_version: u64,
33    algorithms: OwnedVec<AlgorithmIdentifier<'a>>,
34    content: Encapsulated<'a>,
35    choices: OwnedVec<CertificateChoice<'a>>,
36    certificates: OwnedVec<Certificate<'a>>,
37    certificate_field: Option<Span>,
38    revocations: OwnedVec<Element<'a>>,
39    revocation_field: Option<Span>,
40    signers: OwnedVec<SignerInfo<'a>>,
41    faults: OwnedVec<Error>,
42    element: Element<'a>,
43}
44
45impl<'a> SignedData<'a> {
46    /// Reads one entire CMS `ContentInfo` and its `SignedData` using the same parser.
47    pub fn read(parser: &'a Parser, encoded: &'a [u8], base: u64) -> Result<Self> {
48        let mut root = parser
49            .read(encoded, base)
50            .map_err(|error| Error::encoding(error, "contentInfo"))?;
51        let (envelope, parent) = ContentInfo::read(&mut root)?;
52        parent
53            .execution()
54            .charge_work(envelope.kind().as_bytes().len() as u64)
55            .map_err(|reason| {
56                Error::resource(reason, "contentInfo.contentType", envelope.span().start)
57            })?;
58        if envelope.kind() != oid::SIGNED_DATA {
59            return Err(Error::schema(
60                ErrorCode::UnexpectedContentType,
61                "contentInfo.contentType",
62                envelope.span().start,
63            ));
64        }
65        Self::read_element(&parent, envelope.content())
66    }
67
68    /// Reads an original embedded `SignedData`, preserving enclosing depth/work.
69    pub fn read_element(parent: &Reader<'a>, element: &Element<'a>) -> Result<Self> {
70        const FIELD: &str = "signedData";
71        if element.tag() != Tag::constructed(universal::SEQUENCE) {
72            return Err(Error::schema(
73                ErrorCode::UnexpectedTag,
74                FIELD,
75                element.span().start,
76            ));
77        }
78        let mut inside = parent
79            .enter(element)
80            .map_err(|error| Error::encoding(error, FIELD))?;
81        let execution = parent.execution();
82        let (version, version_element) = read_version(&mut inside)?;
83        let set = required(&mut inside, "signedData.digestAlgorithms")?;
84        let algorithms = catalogue(
85            &inside,
86            &set,
87            Tag::constructed(universal::SET),
88            "signedData.digestAlgorithms",
89            |members, entry| {
90                structured_pkix::algorithm::read(members, entry, "signedData.digestAlgorithms")
91                    .map_err(Error::pkix)
92            },
93        )?;
94        let content_element = required(&mut inside, "signedData.encapContentInfo")?;
95        let content = Encapsulated::read(&inside, &content_element)?;
96        execution
97            .charge_work(content.kind().as_bytes().len() as u64)
98            .map_err(|reason| {
99                Error::resource(
100                    reason,
101                    "signedData.encapContentInfo.eContentType",
102                    content.span().start,
103                )
104            })?;
105        // RFC5652 §5.1 ladder, computed during the admitted schema traversal:
106        // 5 (other), 4 (v2 AC), 3 (v1 AC/v3 signer/non-data), otherwise 1.
107        // https://www.rfc-editor.org/rfc/rfc5652#section-5.1
108        let mut required_version = if content.kind() == oid::DATA { 1 } else { 3 };
109        let mut choices = parent.memory().vector();
110        let mut certificates = parent.memory().vector();
111        let mut revocations = parent.memory().vector();
112        let mut certificate_field = None;
113        let mut revocation_field = None;
114        let mut next = required(&mut inside, "signedData.signerInfos")?;
115        if next.tag() == Tag::context(true, 0) {
116            certificate_field = Some(next.span());
117            (choices, certificates) = read_certificates(&inside, &next, &mut required_version)?;
118            next = required(&mut inside, "signedData.signerInfos")?;
119        }
120        if next.tag() == Tag::context(true, 1) {
121            revocation_field = Some(next.span());
122            revocations = read_revocations(&inside, &next, &mut required_version)?;
123            next = required(&mut inside, "signedData.signerInfos")?;
124        }
125        let signers = catalogue(
126            &inside,
127            &next,
128            Tag::constructed(universal::SET),
129            "signedData.signerInfos",
130            |members, entry| {
131                let signer = SignerInfo::read(members, entry)?;
132                if signer.version() == 3 {
133                    required_version = required_version.max(3);
134                }
135                Ok(signer)
136            },
137        )?;
138        if !inside.is_empty() {
139            return Err(Error::schema(
140                ErrorCode::TrailingData,
141                FIELD,
142                inside.offset(),
143            ));
144        }
145        let mut faults = execution.vector();
146        if version != required_version {
147            faults
148                .try_push(Error::schema(
149                    ErrorCode::VersionMismatch,
150                    "signedData.version",
151                    version_element.span().start,
152                ))
153                .map_err(|reason| {
154                    Error::resource(reason, "signedData.version", version_element.span().start)
155                })?;
156        }
157        Ok(Self {
158            version,
159            required_version,
160            algorithms,
161            content,
162            choices,
163            certificates,
164            certificate_field,
165            revocations,
166            revocation_field,
167            signers,
168            faults: faults.into_owned(),
169            element: *element,
170        })
171    }
172
173    /// Declared version.
174    #[must_use]
175    pub fn version(&self) -> u64 {
176        self.version
177    }
178    /// Version independently required by the encoded choices.
179    #[must_use]
180    pub fn required_version(&self) -> u64 {
181        self.required_version
182    }
183    /// Original advertised digest algorithms; provider resolution is separate.
184    /// RFC5652 §§5.1, 5.3 permit zero entries and do not require recipients to
185    /// refuse an unlisted signer digest. These advertisements never replace the
186    /// original signer algorithm; stricter catalogue acceptance is caller policy.
187    /// <https://www.rfc-editor.org/rfc/rfc5652#section-5.1>
188    #[must_use]
189    pub fn digest_algorithms(&self) -> &[AlgorithmIdentifier<'a>] {
190        &self.algorithms
191    }
192    /// Original attached or absent content.
193    #[must_use]
194    pub fn content(&self) -> &Encapsulated<'a> {
195        &self.content
196    }
197    /// Certificate choices in their original order.
198    #[must_use]
199    pub fn certificate_choices(&self) -> &[CertificateChoice<'a>] {
200        &self.choices
201    }
202    /// Parsed X.509 certificates, in the order referenced by X509 choices.
203    #[must_use]
204    pub fn certificates(&self) -> &[Certificate<'a>] {
205        &self.certificates
206    }
207    /// Original presence of the certificate field, including an empty written set.
208    #[must_use]
209    pub fn certificate_field(&self) -> Option<Span> {
210        self.certificate_field
211    }
212    /// Original presence of the revocation field, including an empty written set.
213    #[must_use]
214    pub fn revocation_field(&self) -> Option<Span> {
215        self.revocation_field
216    }
217    /// Original revocation choices, not implicitly authenticated or fetched.
218    #[must_use]
219    pub fn revocations(&self) -> &[Element<'a>] {
220        &self.revocations
221    }
222    /// Independent signers in their original order, without an all-signers verdict.
223    #[must_use]
224    pub fn signers(&self) -> &[SignerInfo<'a>] {
225        &self.signers
226    }
227    /// Readable document faults, retaining their allocation lifetime.
228    #[must_use]
229    pub fn faults(&self) -> &[Error] {
230        &self.faults
231    }
232    /// Original `SignedData` source range.
233    #[must_use]
234    pub fn span(&self) -> Span {
235        self.element.span()
236    }
237    /// Original encoding without serialization.
238    #[must_use]
239    pub fn as_bytes(&self) -> &'a [u8] {
240        self.element.as_bytes()
241    }
242}
243
244fn ordered<'a>(
245    parent: &Reader<'a>,
246    previous: &mut Option<&'a [u8]>,
247    element: &Element<'a>,
248    field: &'static str,
249) -> Result<()> {
250    if parent.encoding().is_der() {
251        if let Some(earlier) = *previous
252            && !structured_pkix::der::set_ordered(parent, earlier, element, field)
253                .map_err(Error::pkix)?
254        {
255            return Err(Error::schema(
256                ErrorCode::SetOrder,
257                field,
258                element.span().start,
259            ));
260        }
261        *previous = Some(element.as_bytes());
262    }
263    Ok(())
264}
265
266fn other_format(parent: &Reader<'_>, element: &Element<'_>, field: &'static str) -> Result<()> {
267    let mut inside = parent
268        .enter(element)
269        .map_err(|error| Error::encoding(error, field))?;
270    inside
271        .read_oid()
272        .map_err(|error| Error::encoding(error, field))?;
273    required(&mut inside, field)?;
274    if !inside.is_empty() {
275        return Err(Error::schema(
276            ErrorCode::TrailingData,
277            field,
278            inside.offset(),
279        ));
280    }
281    Ok(())
282}
283
284fn catalogue<'a, T>(
285    parent: &Reader<'a>,
286    set: &Element<'a>,
287    tag: Tag,
288    field: &'static str,
289    mut read: impl FnMut(&Reader<'a>, &Element<'a>) -> Result<T>,
290) -> Result<OwnedVec<T>> {
291    if set.tag() != tag {
292        return Err(Error::schema(
293            ErrorCode::UnexpectedTag,
294            field,
295            set.span().start,
296        ));
297    }
298    let mut members = parent
299        .enter(set)
300        .map_err(|error| Error::encoding(error, field))?;
301    let execution = parent.execution();
302    let mut values = execution.vector();
303    let mut previous = None;
304    while let Some(entry) = members
305        .next_element()
306        .map_err(|error| Error::encoding(error, field))?
307    {
308        ordered(&members, &mut previous, &entry, field)?;
309        values
310            .try_reserve(1)
311            .map_err(|reason| Error::resource(reason, field, entry.span().start))?;
312        let value = read(&members, &entry)?;
313        values
314            .try_push(value)
315            .map_err(|reason| Error::resource(reason, field, entry.span().start))?;
316    }
317    Ok(values.into_owned())
318}
319
320fn read_certificates<'a>(
321    parent: &Reader<'a>,
322    set: &Element<'a>,
323    version: &mut u64,
324) -> Result<(OwnedVec<CertificateChoice<'a>>, OwnedVec<Certificate<'a>>)> {
325    const FIELD: &str = "signedData.certificates";
326    let execution = parent.execution();
327    let mut certificates = execution.vector();
328    let choices = catalogue(
329        parent,
330        set,
331        Tag::context(true, 0),
332        FIELD,
333        |members, entry| {
334            Ok(match entry.tag() {
335                tag if tag == Tag::constructed(universal::SEQUENCE) => {
336                    certificates
337                        .try_reserve(1)
338                        .map_err(|reason| Error::resource(reason, FIELD, entry.span().start))?;
339                    let certificate =
340                        Certificate::read_element(members, entry).map_err(Error::pkix)?;
341                    let index = certificates.len();
342                    certificates
343                        .try_push(certificate)
344                        .map_err(|reason| Error::resource(reason, FIELD, entry.span().start))?;
345                    CertificateChoice::X509(index)
346                }
347                tag if tag == Tag::context(true, 0) => CertificateChoice::Extended(*entry),
348                tag if tag == Tag::context(true, 1) => {
349                    *version = (*version).max(3);
350                    CertificateChoice::AttributeV1(*entry)
351                }
352                tag if tag == Tag::context(true, 2) => {
353                    *version = (*version).max(4);
354                    CertificateChoice::AttributeV2(*entry)
355                }
356                tag if tag == Tag::context(true, 3) => {
357                    other_format(members, entry, FIELD)?;
358                    *version = 5;
359                    CertificateChoice::Other(*entry)
360                }
361                _ => {
362                    return Err(Error::schema(
363                        ErrorCode::UnexpectedTag,
364                        FIELD,
365                        entry.span().start,
366                    ));
367                }
368            })
369        },
370    )?;
371    Ok((choices, certificates.into_owned()))
372}
373
374fn read_version<'a>(inside: &mut Reader<'a>) -> Result<(u64, Element<'a>)> {
375    const FIELD: &str = "signedData.version";
376    let element = inside
377        .expect(Tag::primitive(universal::INTEGER))
378        .map_err(|error| Error::encoding(error, FIELD))?;
379    let version = inside
380        .integer(&element)
381        .map_err(|error| Error::encoding(error, FIELD))?
382        .as_u64()
383        .ok_or_else(|| Error::schema(ErrorCode::ValueTooLarge, FIELD, element.span().start))?;
384    Ok((version, element))
385}
386
387fn read_revocations<'a>(
388    parent: &Reader<'a>,
389    set: &Element<'a>,
390    version: &mut u64,
391) -> Result<OwnedVec<Element<'a>>> {
392    const FIELD: &str = "signedData.crls";
393    catalogue(
394        parent,
395        set,
396        Tag::context(true, 1),
397        FIELD,
398        |members, entry| {
399            if entry.tag() == Tag::context(true, 1) {
400                other_format(members, entry, FIELD)?;
401                *version = 5;
402            } else if entry.tag() != Tag::constructed(universal::SEQUENCE) {
403                return Err(Error::schema(
404                    ErrorCode::UnexpectedTag,
405                    FIELD,
406                    entry.span().start,
407                ));
408            }
409            Ok(*entry)
410        },
411    )
412}