Skip to main content

pers/pe_file/
authenticode.rs

1//! Authenticode accessors and the Authenticode hash of the file.
2
3use alloc::vec::Vec;
4
5use x509_cert::Certificate;
6
7use super::PeFile;
8use crate::authenticode::{AuthenticodeInfo, CrlUrlList, HashAlgorithm};
9use crate::bytes::Bytes;
10use crate::error::{Result, malformed};
11use crate::lazy::cached;
12use crate::pe::{DataDirectoryType, field_offset};
13
14impl<'a> PeFile<'a> {
15    /// The Authenticode signature, `Ok(None)` for unsigned files and an error if the certificate
16    /// data cannot be decoded.
17    pub fn authenticode_info(&self) -> Result<Option<&AuthenticodeInfo<'a>>> {
18        cached(&self.cache.authenticode, || AuthenticodeInfo::parse(self)).map(Option::as_ref)
19    }
20
21    /// The certificate the file was signed with.
22    pub fn signing_certificate(&self) -> Result<Option<&Certificate>> {
23        Ok(self
24            .authenticode_info()?
25            .and_then(|i| i.signing_certificate.as_ref()))
26    }
27
28    /// True if the file is signed. The signature is not validated.
29    pub fn is_signed(&self) -> bool {
30        self.signing_certificate().is_ok_and(|c| c.is_some())
31    }
32
33    /// True if the signature is valid. The trust of the certificate chain is not checked.
34    pub fn has_valid_signature(&self) -> bool {
35        self.authenticode_info()
36            .is_ok_and(|i| i.is_some_and(|i| i.is_valid))
37    }
38
39    /// The CRL URLs of the signing certificate, `Ok(None)` if the file is not signed.
40    pub fn crl_url_list(&self) -> Result<Option<CrlUrlList>> {
41        self.signing_certificate()?
42            .map(CrlUrlList::from_certificate)
43            .transpose()
44    }
45
46    /// The Authenticode hash: the hash of the file without the checksum, the certificate table
47    /// entry and the certificate data.
48    pub fn authenticode_hash(&self, algorithm: HashAlgorithm) -> Result<Vec<u8>> {
49        let nt_headers = &self.nt_headers;
50        let certificate_table_entry = nt_headers.data_directory_offset(DataDirectoryType::Security);
51        let size_of_headers = nt_headers.optional_header.size_of_headers() as usize;
52        let check_sum = nt_headers.optional_header_offset() + field_offset::CHECK_SUM;
53        let bytes = Bytes(self.data);
54        let range = |start: usize, end: usize| {
55            end.checked_sub(start)
56                .ok_or(malformed("Authenticode hash", "overlapping ranges"))
57                .and_then(|length| bytes.slice(start, length))
58        };
59
60        let mut hasher = algorithm.hasher();
61        hasher.update(range(0, check_sum)?);
62        hasher.update(range(check_sum + 4, certificate_table_entry)?);
63        hasher.update(range(certificate_table_entry + 8, size_of_headers)?);
64        let mut end_of_hashed_data = size_of_headers;
65        if let Some(certificate) = self.win_certificate()? {
66            hasher.update(range(size_of_headers, certificate.offset)?);
67            let size = self.data_directory(DataDirectoryType::Security).size() as usize;
68            end_of_hashed_data = certificate.offset + size;
69        }
70        if self.data.len() > end_of_hashed_data {
71            hasher.update(range(end_of_hashed_data, self.data.len())?);
72        }
73        Ok(hasher.finalize())
74    }
75}