Backed out 5 changesets (bug 1890092, bug 1888683) for causing build bustages & crash...
[gecko.git] / third_party / rust / goblin / src / pe / certificate_table.rs
blob353a6c70ce320f62abf1b2606ab65f506c246cc3
1 /// Implements parsing of pe32's Attribute Certificate Table
2 /// See reference:
3 /// https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#the-attribute-certificate-table-image-only
4 /// https://learn.microsoft.com/en-us/windows/win32/api/wintrust/ns-wintrust-win_certificate
5 use crate::error;
6 use scroll::Pread;
8 use alloc::string::ToString;
9 use alloc::vec::Vec;
11 #[repr(u16)]
12 #[derive(Debug, PartialEq, Copy, Clone)]
13 pub enum AttributeCertificateRevision {
14     /// WIN_CERT_REVISION_1_0
15     Revision1_0 = 0x0100,
16     /// WIN_CERT_REVISION_2_0
17     Revision2_0 = 0x0200,
20 impl TryFrom<u16> for AttributeCertificateRevision {
21     type Error = error::Error;
23     fn try_from(value: u16) -> Result<Self, Self::Error> {
24         Ok(match value {
25             x if x == AttributeCertificateRevision::Revision1_0 as u16 => {
26                 AttributeCertificateRevision::Revision1_0
27             }
28             x if x == AttributeCertificateRevision::Revision2_0 as u16 => {
29                 AttributeCertificateRevision::Revision2_0
30             }
31             _ => {
32                 return Err(error::Error::Malformed(
33                     "Invalid certificate attribute revision".to_string(),
34                 ))
35             }
36         })
37     }
40 #[repr(u16)]
41 #[derive(Debug)]
42 pub enum AttributeCertificateType {
43     /// WIN_CERT_TYPE_X509
44     X509 = 0x0001,
45     /// WIN_CERT_TYPE_PKCS_SIGNED_DATA
46     PkcsSignedData = 0x0002,
47     /// WIN_CERT_TYPE_RESERVED_1
48     Reserved1 = 0x0003,
49     /// WIN_CERT_TYPE_TS_STACK_SIGNED
50     TsStackSigned = 0x0004,
53 impl TryFrom<u16> for AttributeCertificateType {
54     type Error = error::Error;
56     fn try_from(value: u16) -> Result<Self, Self::Error> {
57         Ok(match value {
58             x if x == AttributeCertificateType::X509 as u16 => AttributeCertificateType::X509,
59             x if x == AttributeCertificateType::PkcsSignedData as u16 => {
60                 AttributeCertificateType::PkcsSignedData
61             }
62             x if x == AttributeCertificateType::Reserved1 as u16 => {
63                 AttributeCertificateType::Reserved1
64             }
65             x if x == AttributeCertificateType::TsStackSigned as u16 => {
66                 AttributeCertificateType::TsStackSigned
67             }
68             _ => {
69                 return Err(error::Error::Malformed(
70                     "Invalid attribute certificate type".to_string(),
71                 ))
72             }
73         })
74     }
77 #[derive(Clone, Pread)]
78 struct AttributeCertificateHeader {
79     /// dwLength
80     length: u32,
81     revision: u16,
82     certificate_type: u16,
85 const CERTIFICATE_DATA_OFFSET: u32 = 8;
86 #[derive(Debug)]
87 pub struct AttributeCertificate<'a> {
88     pub length: u32,
89     pub revision: AttributeCertificateRevision,
90     pub certificate_type: AttributeCertificateType,
91     pub certificate: &'a [u8],
94 impl<'a> AttributeCertificate<'a> {
95     pub fn parse(
96         bytes: &'a [u8],
97         current_offset: &mut usize,
98     ) -> Result<AttributeCertificate<'a>, error::Error> {
99         // `current_offset` is moved sizeof(AttributeCertificateHeader) = 8 bytes further.
100         let header: AttributeCertificateHeader = bytes.gread_with(current_offset, scroll::LE)?;
101         let cert_size = usize::try_from(header.length.saturating_sub(CERTIFICATE_DATA_OFFSET))
102             .map_err(|_err| {
103                 error::Error::Malformed(
104                     "Attribute certificate size do not fit in usize".to_string(),
105                 )
106             })?;
108         if let Some(bytes) = bytes.get(*current_offset..(*current_offset + cert_size)) {
109             let attr = Self {
110                 length: header.length,
111                 revision: header.revision.try_into()?,
112                 certificate_type: header.certificate_type.try_into()?,
113                 certificate: bytes,
114             };
115             // Moving past the certificate data.
116             // Prevent the current_offset to wrap and ensure current_offset is strictly increasing.
117             *current_offset = current_offset.saturating_add(cert_size);
118             // Round to the next 8-bytes.
119             *current_offset = (*current_offset + 7) & !7;
120             Ok(attr)
121         } else {
122             Err(error::Error::Malformed(format!(
123                 "Unable to extract certificate. Probably cert_size:{} is malformed",
124                 cert_size
125             )))
126         }
127     }
130 pub type CertificateDirectoryTable<'a> = Vec<AttributeCertificate<'a>>;
131 pub(crate) fn enumerate_certificates(
132     bytes: &[u8],
133     table_virtual_address: u32,
134     table_size: u32,
135 ) -> Result<CertificateDirectoryTable, error::Error> {
136     let table_start_offset = usize::try_from(table_virtual_address).map_err(|_err| {
137         error::Error::Malformed("Certificate table RVA do not fit in a usize".to_string())
138     })?;
139     // Here, we do not want wrapping semantics as it means that a too big table size or table start
140     // offset will provide table_end_offset such that table_end_offset < table_start_offset, which
141     // is not desirable at all.
142     let table_end_offset =
143         table_start_offset.saturating_add(usize::try_from(table_size).map_err(|_err| {
144             error::Error::Malformed("Certificate table size do not fit in a usize".to_string())
145         })?);
146     let mut current_offset = table_start_offset;
147     let mut attrs = vec![];
149     // End offset cannot be further than the binary we have at hand.
150     if table_end_offset > bytes.len() {
151         return Err(error::Error::Malformed(
152             "End of attribute certificates table is after the end of the PE binary".to_string(),
153         ));
154     }
156     // This is guaranteed to terminate, either by a malformed error being returned
157     // or because current_offset >= table_end_offset by virtue of current_offset being strictly
158     // increasing through `AttributeCertificate::parse`.
159     while current_offset < table_end_offset {
160         attrs.push(AttributeCertificate::parse(bytes, &mut current_offset)?);
161     }
163     Ok(attrs)