Skip to main content

structured_cms/
signer.rs

1//! Original signer identities, algorithms, attributes and signature ranges.
2
3use crate::attribute::{Attributes, Placement};
4use crate::{Error, ErrorCode, Octets, Result};
5use structured_asn1::memory::{OwnedVec, Work};
6use structured_asn1::{Element, Encoding, Integer, Reader, Span, Tag, universal};
7use structured_pkix::{AlgorithmIdentifier, Name};
8
9/// The identity actually selected by the document, without certificate selection.
10#[derive(Debug)]
11pub enum SignerIdentifier<'a> {
12    /// The issuer's distinguished name and original certificate serial.
13    IssuerAndSerial {
14        /// Original issuer name, interpreted by the common PKIX name core.
15        issuer: Name<'a>,
16        /// Original minimal two's-complement serial; no certificate policy is inferred.
17        serial: Integer<'a>,
18    },
19    /// Logical identifier octets, preserving any BER segmentation.
20    SubjectKeyIdentifier(Octets<'a>),
21}
22
23impl<'a> SignerIdentifier<'a> {
24    fn read(parent: &Reader<'a>, element: &Element<'a>) -> Result<Self> {
25        const FIELD: &str = "signerInfo.sid";
26        if element.tag() == Tag::context(false, 0) || element.tag() == Tag::context(true, 0) {
27            return Octets::read_identifier(parent, element, FIELD).map(Self::SubjectKeyIdentifier);
28        }
29        if element.tag() != Tag::constructed(universal::SEQUENCE) {
30            return Err(Error::schema(
31                ErrorCode::UnexpectedTag,
32                FIELD,
33                element.span().start,
34            ));
35        }
36        let mut inside = parent
37            .enter(element)
38            .map_err(|error| Error::encoding(error, FIELD))?;
39        let name = inside
40            .expect(Tag::constructed(universal::SEQUENCE))
41            .map_err(|error| Error::encoding(error, FIELD))?;
42        let issuer = structured_pkix::name::read(&inside, &name, FIELD).map_err(Error::pkix)?;
43        let serial = inside
44            .read_integer()
45            .map_err(|error| Error::encoding(error, FIELD))?;
46        if !inside.is_empty() {
47            return Err(Error::schema(
48                ErrorCode::TrailingData,
49                FIELD,
50                inside.offset(),
51            ));
52        }
53        Ok(Self::IssuerAndSerial { issuer, serial })
54    }
55
56    /// The version required by this CHOICE (RFC5652 §5.3).
57    #[must_use]
58    pub fn required_version(&self) -> u64 {
59        match self {
60            Self::IssuerAndSerial { .. } => 1,
61            Self::SubjectKeyIdentifier(_) => 3,
62        }
63    }
64}
65
66/// One parsed CMS signer, retaining original byte identity and diagnostic storage.
67#[derive(Debug)]
68pub struct SignerInfo<'a> {
69    version: u64,
70    sid: SignerIdentifier<'a>,
71    digest_algorithm: AlgorithmIdentifier<'a>,
72    signed_attributes: Option<Attributes<'a>>,
73    signature_algorithm: AlgorithmIdentifier<'a>,
74    signature: Octets<'a>,
75    unsigned_attributes: Option<Attributes<'a>>,
76    encoding: Encoding,
77    element: Element<'a>,
78    faults: OwnedVec<Error>,
79}
80
81impl<'a> SignerInfo<'a> {
82    pub(crate) fn require_session(&self, parent: &Reader<'_>, field: &'static str) -> Result<()> {
83        if !self.digest_algorithm.same_session(parent) {
84            return Err(Error::pkix(structured_pkix::Error::new(
85                structured_pkix::ErrorCode::ForeignReader,
86                field,
87                self.span().start,
88            )));
89        }
90        Ok(())
91    }
92    /// Reads a signer at the original depth with both producing work ceilings.
93    ///
94    /// A uniquely readable version mismatch is retained as a fault, never a
95    /// successful conformance claim. Signed attributes remain DER and their
96    /// original octets are never repaired (RFC5652 §§5.3–5.4).
97    /// <https://www.rfc-editor.org/rfc/rfc5652#section-5.3>
98    pub fn read(parent: &Reader<'a>, element: &Element<'a>) -> Result<Self> {
99        const FIELD: &str = "signerInfo";
100        if element.tag() != Tag::constructed(universal::SEQUENCE) {
101            return Err(Error::schema(
102                ErrorCode::UnexpectedTag,
103                FIELD,
104                element.span().start,
105            ));
106        }
107        let mut inside = parent
108            .enter(element)
109            .map_err(|error| Error::encoding(error, FIELD))?;
110        let execution = parent.execution();
111        let mut faults = execution.vector();
112        let version_element = inside
113            .expect(Tag::primitive(universal::INTEGER))
114            .map_err(|error| Error::encoding(error, "signerInfo.version"))?;
115        let version = inside
116            .integer(&version_element)
117            .map_err(|error| Error::encoding(error, "signerInfo.version"))?
118            .as_u64()
119            .ok_or_else(|| {
120                Error::schema(
121                    ErrorCode::ValueTooLarge,
122                    "signerInfo.version",
123                    version_element.span().start,
124                )
125            })?;
126        let named = required(&mut inside, "signerInfo.sid")?;
127        let sid = SignerIdentifier::read(&inside, &named)?;
128        if version != sid.required_version() {
129            faults
130                .try_push(Error::schema(
131                    ErrorCode::VersionMismatch,
132                    "signerInfo.version",
133                    version_element.span().start,
134                ))
135                .map_err(|reason| {
136                    Error::resource(reason, "signerInfo.version", version_element.span().start)
137                })?;
138        }
139        let digest = required(&mut inside, "signerInfo.digestAlgorithm")?;
140        let digest_algorithm =
141            structured_pkix::algorithm::read(&inside, &digest, "signerInfo.digestAlgorithm")
142                .map_err(Error::pkix)?;
143        let mut next = required(&mut inside, "signerInfo.signatureAlgorithm")?;
144        let signed_attributes = if next.tag() == Tag::context(true, 0) {
145            let attributes = Attributes::read(&inside, &next, Placement::Signed)?;
146            next = required(&mut inside, "signerInfo.signatureAlgorithm")?;
147            Some(attributes)
148        } else {
149            None
150        };
151        let signature_algorithm =
152            structured_pkix::algorithm::read(&inside, &next, "signerInfo.signatureAlgorithm")
153                .map_err(Error::pkix)?;
154        let signature_element = required(&mut inside, "signerInfo.signature")?;
155        let signature = Octets::read(&inside, &signature_element, "signerInfo.signature")?;
156        let unsigned_attributes = match inside
157            .next_element()
158            .map_err(|error| Error::encoding(error, "signerInfo.unsignedAttrs"))?
159        {
160            Some(attributes) => Some(Attributes::read(&inside, &attributes, Placement::Unsigned)?),
161            None => None,
162        };
163        if !inside.is_empty() {
164            return Err(Error::schema(
165                ErrorCode::TrailingData,
166                FIELD,
167                inside.offset(),
168            ));
169        }
170        Ok(Self {
171            version,
172            sid,
173            digest_algorithm,
174            signed_attributes,
175            signature_algorithm,
176            signature,
177            unsigned_attributes,
178            encoding: inside.encoding(),
179            element: *element,
180            faults: faults.into_owned(),
181        })
182    }
183
184    /// Declared numeric version, independently of any retained mismatch.
185    #[must_use]
186    pub fn version(&self) -> u64 {
187        self.version
188    }
189    /// The document's signer identity CHOICE.
190    #[must_use]
191    pub fn sid(&self) -> &SignerIdentifier<'a> {
192        &self.sid
193    }
194    /// Original digest algorithm and opaque parameters.
195    #[must_use]
196    pub fn digest_algorithm(&self) -> AlgorithmIdentifier<'a> {
197        self.digest_algorithm
198    }
199    /// Original signed attributes, when present.
200    #[must_use]
201    pub fn signed_attributes(&self) -> Option<&Attributes<'a>> {
202        self.signed_attributes.as_ref()
203    }
204    /// Original signature algorithm and opaque parameters.
205    #[must_use]
206    pub fn signature_algorithm(&self) -> AlgorithmIdentifier<'a> {
207        self.signature_algorithm
208    }
209    /// Logical signature octets without joining.
210    #[must_use]
211    pub fn signature(&self) -> &Octets<'a> {
212        &self.signature
213    }
214    /// Original attributes outside the signature's coverage.
215    #[must_use]
216    pub fn unsigned_attributes(&self) -> Option<&Attributes<'a>> {
217        self.unsigned_attributes.as_ref()
218    }
219
220    /// Reads direct unsigned countersignatures lazily through original provenance.
221    /// Malformed values remain independent answers; resource refusal stops once.
222    pub fn countersignatures_in<'s>(
223        &'s self,
224        parent: &'s Reader<'a>,
225    ) -> crate::counter::Countersignatures<'s, 'a> {
226        crate::counter::Countersignatures::new(self, parent)
227    }
228    /// Encoding contract of the envelope, not that of the signed attributes.
229    #[must_use]
230    pub fn encoding(&self) -> Encoding {
231        self.encoding
232    }
233    /// Original signer range.
234    #[must_use]
235    pub fn span(&self) -> Span {
236        self.element.span()
237    }
238    /// Entire original encoding, including non-DER envelope spelling.
239    #[must_use]
240    pub fn as_bytes(&self) -> &'a [u8] {
241        self.element.as_bytes()
242    }
243    /// Retained document faults; resource refusals never enter this list.
244    #[must_use]
245    pub fn faults(&self) -> &[Error] {
246        &self.faults
247    }
248
249    /// Narrow parsed-field identity for duplicate recovery, not PKIX equivalence.
250    ///
251    /// BER outer headers and octet segmentation are ignored. Issuer/serial,
252    /// opaque parameters and attribute sets retain encoded identity. Two
253    /// present NULL parameters compare by their parsed NULL value, so its
254    /// root length spelling cannot create a different reading. Signed
255    /// attributes therefore remain byte-identical. RFC5754 §2 SHA-2 digest
256    /// absence/NULL defaults share the PKIX comparer; other algorithm defaults
257    /// and selected-provider acceptance remain separate operations.
258    pub fn same_value(&self, other: &SignerInfo<'_>, parent: &Reader<'_>) -> Result<bool> {
259        if !self.same_fields(other, parent)? {
260            return Ok(false);
261        }
262        // Resource refusal belongs to this duplicate query, not a prior
263        // retained algorithm's location. Syntax/caller errors keep provenance.
264        let comparison_error = |source: structured_pkix::Error| {
265            if source.is_resource() {
266                Error::pkix(structured_pkix::Error::new(
267                    source.code(),
268                    "signedData.signerInfos",
269                    other.span().start,
270                ))
271            } else {
272                Error::pkix(source)
273            }
274        };
275        // RFC5754 §2 requires SHA224/256/384/512 absent/NULL equivalence.
276        // Written identity is checked first so identical opaque malformed
277        // parameters remain inspectable, without claiming algorithm acceptance.
278        // https://www.rfc-editor.org/rfc/rfc5754#section-2
279        if !self
280            .digest_algorithm
281            .same_written_in(&other.digest_algorithm, parent)
282            .map_err(comparison_error)?
283            && !self
284                .digest_algorithm
285                .digest_matches_in(&other.digest_algorithm, parent)
286                .map_err(comparison_error)?
287        {
288            return Ok(false);
289        }
290        self.signature_algorithm
291            .same_written_in(&other.signature_algorithm, parent)
292            .map_err(comparison_error)
293    }
294
295    // Shared document-field identity for encoded and selected-provider paths.
296    // Provider identities replace only algorithm comparison; original signed
297    // octets and signature/SID value checks cannot be bypassed by either path.
298    pub(crate) fn same_fields(&self, other: &SignerInfo<'_>, parent: &Reader<'_>) -> Result<bool> {
299        let field = "signedData.signerInfos";
300        let at = other.span().start;
301        self.require_session(parent, field)?;
302        other.require_session(parent, field)?;
303        parent
304            .execution()
305            .charge_work(1)
306            .map_err(|reason| Error::resource(reason, field, at))?;
307        if self.version != other.version {
308            return Ok(false);
309        }
310        let same_sid = match (&self.sid, &other.sid) {
311            (
312                SignerIdentifier::IssuerAndSerial { issuer, serial },
313                SignerIdentifier::IssuerAndSerial {
314                    issuer: right,
315                    serial: number,
316                },
317            ) => {
318                same_bytes(parent, issuer.as_bytes(), right.as_bytes(), field, at)?
319                    && same_bytes(parent, serial.as_bytes(), number.as_bytes(), field, at)?
320            }
321            (
322                SignerIdentifier::SubjectKeyIdentifier(left),
323                SignerIdentifier::SubjectKeyIdentifier(right),
324            ) => left.same_value(right, parent, field, at)?,
325            _ => false,
326        };
327        if !same_sid {
328            return Ok(false);
329        }
330        for (left, right) in [
331            (&self.signed_attributes, &other.signed_attributes),
332            (&self.unsigned_attributes, &other.unsigned_attributes),
333        ] {
334            if !same_optional(
335                parent,
336                left.as_ref().map(|attrs| attrs.element().as_bytes()),
337                right.as_ref().map(|attrs| attrs.element().as_bytes()),
338                field,
339                at,
340            )? {
341                return Ok(false);
342            }
343        }
344        self.signature
345            .same_value(&other.signature, parent, field, at)
346    }
347}
348
349pub(crate) fn required<'a>(reader: &mut Reader<'a>, field: &'static str) -> Result<Element<'a>> {
350    reader
351        .next_element()
352        .map_err(|error| Error::encoding(error, field))?
353        .ok_or_else(|| Error::schema(ErrorCode::MissingField, field, reader.offset()))
354}
355
356fn same_bytes(
357    parent: &Reader<'_>,
358    left: &[u8],
359    right: &[u8],
360    field: &'static str,
361    at: u64,
362) -> Result<bool> {
363    let execution = parent.execution();
364    execution
365        .charge_work(1)
366        .map_err(|reason| Error::resource(reason, field, at))?;
367    if left.len() != right.len() {
368        return Ok(false);
369    }
370    execution
371        .charge_work(left.len() as u64)
372        .map_err(|reason| Error::resource(reason, field, at))?;
373    Ok(left == right)
374}
375
376fn same_optional(
377    parent: &Reader<'_>,
378    left: Option<&[u8]>,
379    right: Option<&[u8]>,
380    field: &'static str,
381    at: u64,
382) -> Result<bool> {
383    parent
384        .execution()
385        .charge_work(1)
386        .map_err(|reason| Error::resource(reason, field, at))?;
387    match (left, right) {
388        (None, None) => Ok(true),
389        (Some(left), Some(right)) => same_bytes(parent, left, right, field, at),
390        _ => Ok(false),
391    }
392}