authenticode/
signature.rs

1// Copyright 2023 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use cms::content_info::CmsVersion;
10use cms::content_info::ContentInfo;
11use cms::signed_data::{SignedData, SignerInfo};
12use core::fmt::{self, Display, Formatter};
13use der::asn1::{ObjectIdentifier, OctetString};
14use der::Decode;
15use der::{Sequence, SliceReader};
16use x509_cert::Certificate;
17
18/// OID for [`SpcIndirectDataContent`].
19pub const SPC_INDIRECT_DATA_OBJID: ObjectIdentifier =
20    ObjectIdentifier::new_unwrap("1.3.6.1.4.1.311.2.1.4");
21
22/// Authenticode ASN.1 image and digest data.
23#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
24pub struct SpcIndirectDataContent {
25    /// Image data.
26    pub data: SpcAttributeTypeAndOptionalValue,
27
28    /// Authenticode digest.
29    pub message_digest: DigestInfo,
30}
31
32/// Authenticode ASN.1 image data.
33#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
34pub struct SpcAttributeTypeAndOptionalValue {
35    /// Type of data stored in the `value` field.
36    pub value_type: ObjectIdentifier,
37
38    /// Image data.
39    //TODO(nicholasbishop): implement SpcPeImageData.
40    pub value: der::Any,
41}
42
43/// Authenticode ASN.1 digest data.
44#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
45pub struct DigestInfo {
46    /// Authenticode digest algorithm.
47    pub digest_algorithm: spki::AlgorithmIdentifierOwned,
48
49    /// Authenticode digest.
50    pub digest: OctetString,
51}
52
53/// Error returned by [`AuthenticodeSignature::from_bytes`].
54#[derive(Clone, Copy, Debug, Eq, PartialEq)]
55pub enum AuthenticodeSignatureParseError {
56    /// The signature data is empty.
57    Empty,
58
59    /// The signature data is not valid [`ContentInfo`].
60    InvalidContentInfo(der::Error),
61
62    /// The content type does not match [`const_oid::db::rfc6268::ID_SIGNED_DATA`].
63    InvalidContentType(ObjectIdentifier),
64
65    /// The content info is not valid [`SignedData`].
66    InvalidSignedData(der::Error),
67
68    /// The version of [`SignedData`] is not 1.
69    InvalidSignedDataVersion(CmsVersion),
70
71    /// The number of digest algorithms is not 1.
72    InvalidNumDigestAlgorithms(usize),
73
74    /// The encapsulated content type does not match [`SPC_INDIRECT_DATA_OBJID`].
75    InvalidEncapsulatedContentType(ObjectIdentifier),
76
77    /// The encapsulated content is empty.
78    EmptyEncapsulatedContent,
79
80    /// The encapsulated content is not valid [`SpcIndirectDataContent`].
81    InvalidSpcIndirectDataContent(der::Error),
82
83    /// The number of signer infos is not 1.
84    InvalidNumSignerInfo(usize),
85
86    /// The version of [`SignerInfo`] is not 1.
87    InvalidSignerInfoVersion(CmsVersion),
88
89    /// The digest algorithm is not internally consistent.
90    AlgorithmMismatch,
91
92    /// No authenticated attributes are present.
93    EmptyAuthenticatedAttributes,
94
95    /// The `contentType` authenticated attribute is missing.
96    MissingContentTypeAuthenticatedAttribute,
97
98    /// The `messageDigest` authenticated attribute is missing.
99    MissingMessageDigestAuthenticatedAttribute,
100}
101
102impl Display for AuthenticodeSignatureParseError {
103    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
104        // TODO(nicholasbishop): better error message.
105        write!(f, "authenticode signature parse error: {:?}", self)
106    }
107}
108
109#[cfg(feature = "std")]
110impl std::error::Error for AuthenticodeSignatureParseError {}
111
112/// Parsed authenticode signature.
113#[derive(Clone, Debug, Eq, PartialEq)]
114pub struct AuthenticodeSignature {
115    signed_data: SignedData,
116    indirect_data: SpcIndirectDataContent,
117}
118
119impl AuthenticodeSignature {
120    /// Parse an `AuthenticodeSignature` from DER-encoded bytes.
121    ///
122    /// Note that while many aspects of the data are validated, this
123    /// does not constitute actual signature verification.
124    pub fn from_bytes(
125        bytes: &[u8],
126    ) -> Result<Self, AuthenticodeSignatureParseError> {
127        // Construct a reader manually here rather than using
128        // `Decode::from_der`, because there may be unused trailing data
129        // in `bytes`, which causes a `TrailingData` error.
130        let mut reader = SliceReader::new(bytes)
131            .map_err(|_| AuthenticodeSignatureParseError::Empty)?;
132        let content_info = ContentInfo::decode(&mut reader)
133            .map_err(AuthenticodeSignatureParseError::InvalidContentInfo)?;
134
135        if content_info.content_type != const_oid::db::rfc6268::ID_SIGNED_DATA {
136            return Err(AuthenticodeSignatureParseError::InvalidContentType(
137                content_info.content_type,
138            ));
139        }
140        let signed_data = content_info
141            .content
142            .decode_as::<SignedData>()
143            .map_err(AuthenticodeSignatureParseError::InvalidSignedData)?;
144
145        if signed_data.version != CmsVersion::V1 {
146            return Err(
147                AuthenticodeSignatureParseError::InvalidSignedDataVersion(
148                    signed_data.version,
149                ),
150            );
151        }
152
153        // Exactly one is required per the spec.
154        if signed_data.digest_algorithms.len() != 1 {
155            return Err(
156                AuthenticodeSignatureParseError::InvalidNumDigestAlgorithms(
157                    signed_data.digest_algorithms.len(),
158                ),
159            );
160        }
161
162        if signed_data.encap_content_info.econtent_type
163            != SPC_INDIRECT_DATA_OBJID
164        {
165            return Err(
166                AuthenticodeSignatureParseError::InvalidEncapsulatedContentType(
167                    signed_data.encap_content_info.econtent_type,
168                ),
169            );
170        }
171        let indirect_data = signed_data
172            .clone()
173            .encap_content_info
174            .econtent
175            .ok_or(AuthenticodeSignatureParseError::EmptyEncapsulatedContent)?
176            .decode_as::<SpcIndirectDataContent>()
177            .map_err(
178                AuthenticodeSignatureParseError::InvalidSpcIndirectDataContent,
179            )?;
180
181        // Exactly one is required per the spec.
182        if signed_data.signer_infos.0.len() != 1 {
183            return Err(AuthenticodeSignatureParseError::InvalidNumSignerInfo(
184                signed_data.signer_infos.0.len(),
185            ));
186        }
187        let signer_info = &signed_data.signer_infos.0.as_slice()[0];
188
189        if signer_info.version != CmsVersion::V1 {
190            return Err(
191                AuthenticodeSignatureParseError::InvalidSignerInfoVersion(
192                    signer_info.version,
193                ),
194            );
195        }
196
197        if signer_info.digest_alg != signed_data.digest_algorithms.as_slice()[0]
198        {
199            return Err(AuthenticodeSignatureParseError::AlgorithmMismatch);
200        }
201
202        let signed_attrs = if let Some(signed_attrs) = &signer_info.signed_attrs
203        {
204            signed_attrs
205        } else {
206            return Err(
207                AuthenticodeSignatureParseError::EmptyAuthenticatedAttributes,
208            );
209        };
210
211        if !signed_attrs
212            .iter()
213            .any(|a| a.oid == const_oid::db::rfc6268::ID_CONTENT_TYPE)
214        {
215            return Err(AuthenticodeSignatureParseError::MissingContentTypeAuthenticatedAttribute);
216        }
217
218        if !signed_attrs
219            .iter()
220            .any(|a| a.oid == const_oid::db::rfc6268::ID_MESSAGE_DIGEST)
221        {
222            return Err(AuthenticodeSignatureParseError::MissingMessageDigestAuthenticatedAttribute);
223        }
224
225        Ok(Self {
226            signed_data: signed_data.clone(),
227            indirect_data,
228        })
229    }
230
231    /// Get [`SignerInfo`].
232    pub fn signer_info(&self) -> &SignerInfo {
233        // The constructor validates that exactly one signer info is
234        // present, so this won't panic.
235        &self.signed_data.signer_infos.0.as_slice()[0]
236    }
237
238    /// Get the authenticode digest.
239    ///
240    /// This is the digest value embedded in the signature; it is not
241    /// guaranteed to be correct.
242    pub fn digest(&self) -> &[u8] {
243        self.indirect_data.message_digest.digest.as_bytes()
244    }
245
246    /// Get the authenticode signature.
247    ///
248    /// This is the `encryptedDigest` value embedded in the signature;
249    /// it is not guaranteed to be correct.
250    pub fn signature(&self) -> &[u8] {
251        self.signer_info().signature.as_bytes()
252    }
253
254    /// Get the encapsulated content.
255    pub fn encapsulated_content(&self) -> Option<&[u8]> {
256        self.signed_data
257            .encap_content_info
258            .econtent
259            .as_ref()
260            .map(|c| c.value())
261    }
262
263    /// Get the certificate chain.
264    pub fn certificates(&self) -> impl Iterator<Item = &Certificate> {
265        self.signed_data
266            .certificates
267            .as_ref()
268            .unwrap()
269            .0
270            .iter()
271            .map(|cert| {
272                if let cms::cert::CertificateChoices::Certificate(cert) = cert {
273                    cert
274                } else {
275                    panic!()
276                }
277            })
278    }
279}