authenticode/
authenticode_digest.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 crate::usize_from_u32;
10use crate::{PeOffsetError, PeTrait};
11use alloc::vec::Vec;
12use digest::Update;
13
14fn authenticode_digest_impl(
15    pe: &dyn PeTrait,
16    digest: &mut dyn Update,
17) -> Option<()> {
18    let offsets = pe.offsets().ok()?;
19
20    // Hash from beginning to checksum.
21    let bytes = &pe.data().get(..offsets.check_sum)?;
22    digest.update(bytes);
23
24    // Hash from checksum to the security data directory.
25    let bytes = &pe
26        .data()
27        .get(offsets.after_check_sum..offsets.security_data_dir)?;
28    digest.update(bytes);
29
30    // Hash from the security data directory to the end of the header.
31    let bytes = &pe
32        .data()
33        .get(offsets.after_security_data_dir..offsets.after_header)?;
34    digest.update(bytes);
35
36    // Track offset as sections are hashed. This is used to hash data
37    // after the sections.
38    let mut sum_of_bytes_hashed = usize_from_u32(offsets.after_header as u32);
39
40    // First sort the sections.
41    let mut sections = (1..=pe.num_sections())
42        .map(|i| pe.section_data_range(i))
43        .collect::<Result<Vec<_>, PeOffsetError>>()
44        .ok()?;
45    sections.sort_unstable_by_key(|r| r.start);
46
47    // Then hash each section's data.
48    for section_range in sections {
49        let bytes = &pe.data().get(section_range)?;
50
51        digest.update(bytes);
52        sum_of_bytes_hashed = sum_of_bytes_hashed.checked_add(bytes.len())?;
53    }
54
55    let mut extra_hash_len =
56        pe.data().len().checked_sub(sum_of_bytes_hashed)?;
57
58    // The certificate table is not included in the hash.
59    if let Some(security_data_dir) = pe.certificate_table_range().ok()? {
60        let size =
61            security_data_dir.end.checked_sub(security_data_dir.start)?;
62        extra_hash_len = extra_hash_len.checked_sub(size)?;
63    }
64
65    digest.update(pe.data().get(
66        sum_of_bytes_hashed..sum_of_bytes_hashed.checked_add(extra_hash_len)?,
67    )?);
68
69    Some(())
70}
71
72/// Calculate an authenticode digest.
73pub fn authenticode_digest(
74    pe: &dyn PeTrait,
75    digest: &mut dyn Update,
76) -> Result<(), PeOffsetError> {
77    authenticode_digest_impl(pe, digest).ok_or(PeOffsetError)
78}