summaryrefslogtreecommitdiffstats
path: root/third_party/rust/goblin/src/pe/authenticode.rs
blob: e16b7997cddbf2882846674db07ae893a3efcf4c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// Reference:
//   https://learn.microsoft.com/en-us/windows-hardware/drivers/install/authenticode
//   https://download.microsoft.com/download/9/c/5/9c5b2167-8017-4bae-9fde-d599bac8184a/Authenticode_PE.docx

// Authenticode works by omiting sections of the PE binary from the digest
// those sections are:
//   - checksum
//   - data directory entry for certtable
//   - certtable

use core::ops::Range;

use super::PE;

impl PE<'_> {
    /// [`authenticode_ranges`] returns the various ranges of the binary that are relevant for
    /// signature.
    pub fn authenticode_ranges(&self) -> ExcludedSectionsIter<'_> {
        ExcludedSectionsIter {
            pe: self,
            state: IterState::default(),
        }
    }
}

/// [`ExcludedSections`] holds the various ranges of the binary that are expected to be
/// excluded from the authenticode computation.
#[derive(Debug, Clone, Default)]
pub(super) struct ExcludedSections {
    checksum: Range<usize>,
    datadir_entry_certtable: Range<usize>,
    certtable: Option<Range<usize>>,
}

impl ExcludedSections {
    pub(super) fn new(
        checksum: Range<usize>,
        datadir_entry_certtable: Range<usize>,
        certtable: Option<Range<usize>>,
    ) -> Self {
        Self {
            checksum,
            datadir_entry_certtable,
            certtable,
        }
    }
}

pub struct ExcludedSectionsIter<'s> {
    pe: &'s PE<'s>,
    state: IterState,
}

#[derive(Debug, PartialEq)]
enum IterState {
    Initial,
    DatadirEntry(usize),
    CertTable(usize),
    Final(usize),
    Done,
}

impl Default for IterState {
    fn default() -> Self {
        Self::Initial
    }
}

impl<'s> Iterator for ExcludedSectionsIter<'s> {
    type Item = &'s [u8];

    fn next(&mut self) -> Option<Self::Item> {
        let bytes = &self.pe.bytes;

        if let Some(sections) = self.pe.authenticode_excluded_sections.as_ref() {
            loop {
                match self.state {
                    IterState::Initial => {
                        self.state = IterState::DatadirEntry(sections.checksum.end);
                        return Some(&bytes[..sections.checksum.start]);
                    }
                    IterState::DatadirEntry(start) => {
                        self.state = IterState::CertTable(sections.datadir_entry_certtable.end);
                        return Some(&bytes[start..sections.datadir_entry_certtable.start]);
                    }
                    IterState::CertTable(start) => {
                        if let Some(certtable) = sections.certtable.as_ref() {
                            self.state = IterState::Final(certtable.end);
                            return Some(&bytes[start..certtable.start]);
                        } else {
                            self.state = IterState::Final(start)
                        }
                    }
                    IterState::Final(start) => {
                        self.state = IterState::Done;
                        return Some(&bytes[start..]);
                    }
                    IterState::Done => return None,
                }
            }
        } else {
            loop {
                match self.state {
                    IterState::Initial => {
                        self.state = IterState::Done;
                        return Some(bytes);
                    }
                    IterState::Done => return None,
                    _ => {
                        self.state = IterState::Done;
                    }
                }
            }
        }
    }
}