cross_authenticode/
authenticode_info.rs1use crate::win_certificate::WinCertificate;
2use crate::{authenticode_certificate::AuthenticodeCertificate, error::AuthenticodeError};
3use cms::{
4 cert::{
5 CertificateChoices,
6 x509::der::{Decode, SliceReader},
7 },
8 content_info::ContentInfo,
9 signed_data::SignedData,
10};
11use object::{
12 LittleEndian, pe::IMAGE_DIRECTORY_ENTRY_SECURITY, read::pe::PeFile32, read::pe::PeFile64,
13};
14
15#[derive(Debug)]
17pub struct AuthenticodeInfo {
18 pub certificates: Vec<AuthenticodeCertificate>,
20}
21
22impl AuthenticodeInfo {
23 fn create(data: &[u8]) -> Result<AuthenticodeInfo, AuthenticodeError> {
24 let win_certificate = Self::win_certificate(data)?;
25 let signed_data = Self::signed_data(&win_certificate)?;
26 let authenticode_certificates = Self::certificates(signed_data)?;
27
28 Ok(AuthenticodeInfo {
29 certificates: authenticode_certificates,
30 })
31 }
32
33 fn signed_data(win_certificate: &WinCertificate) -> Result<SignedData, AuthenticodeError> {
34 let mut reader = SliceReader::new(win_certificate.certificate)?;
35 let content_info = ContentInfo::decode(&mut reader)?;
36 let signed_data = content_info.content.decode_as::<SignedData>()?;
37 Ok(signed_data)
38 }
39
40 fn win_certificate(data: &[u8]) -> Result<WinCertificate, AuthenticodeError> {
41 let security_dir = match PeFile64::parse(data) {
42 Ok(pe) => pe
43 .data_directory(IMAGE_DIRECTORY_ENTRY_SECURITY)
44 .ok_or(AuthenticodeError::NoWinCertificate)?,
45 Err(_) => PeFile32::parse(data)?
46 .data_directory(IMAGE_DIRECTORY_ENTRY_SECURITY)
47 .ok_or(AuthenticodeError::NoWinCertificate)?,
48 };
49
50 let win_certificate =
51 WinCertificate::new(data, security_dir.virtual_address.get(LittleEndian))?;
52
53 Ok(win_certificate)
54 }
55
56 fn certificates(
57 signed_data: SignedData,
58 ) -> Result<Vec<AuthenticodeCertificate>, AuthenticodeError> {
59 let authenticode_certificates = signed_data
60 .certificates
61 .as_ref()
62 .ok_or(AuthenticodeError::NoCertificates)?
63 .0
64 .iter()
65 .filter_map(|cert| match cert {
66 CertificateChoices::Certificate(cert) => Some(cert),
67 _ => None,
68 })
69 .map(|cert| AuthenticodeCertificate::try_from(cert.to_owned()))
70 .collect::<Result<Vec<_>, _>>()?;
71
72 Ok(authenticode_certificates)
73 }
74}
75
76impl TryFrom<&[u8]> for AuthenticodeInfo {
78 type Error = AuthenticodeError;
79
80 fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
81 Self::create(data)
82 }
83}
84
85impl TryFrom<&Vec<u8>> for AuthenticodeInfo {
87 type Error = AuthenticodeError;
88
89 fn try_from(data: &Vec<u8>) -> Result<Self, Self::Error> {
90 Self::create(data)
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97 use std::path::PathBuf;
98
99 #[test]
100 fn sha1_thumbprints_signed_64() {
101 let pe_path = PathBuf::from("test-pe/test-signed-64.bin");
102 let pe_file = std::fs::read(pe_path).unwrap();
103
104 let ai = AuthenticodeInfo::try_from(&pe_file).unwrap();
105
106 assert_eq!(ai.certificates.len(), 2);
107 assert_eq!(
108 ai.certificates[0].sha1,
109 "f55115d2439ce0a7529ffaaea654be2c71dce955"
110 );
111 assert_eq!(
112 ai.certificates[1].sha1,
113 "580a6f4cc4e4b669b9ebdc1b2b3e087b80d0678d"
114 );
115 }
116
117 #[test]
118 fn sha256_thumbprints_signed_64() {
119 let pe_path = PathBuf::from("test-pe/test-signed-64.bin");
120 let pe_file = std::fs::read(pe_path).unwrap();
121
122 let ai = AuthenticodeInfo::try_from(&pe_file).unwrap();
123
124 assert_eq!(ai.certificates.len(), 2);
125 assert_eq!(
126 ai.certificates[0].sha256,
127 "9267a08c9fc07b6ab194dc4df3121b264e825330a39ffc42cdb0942f5115eb97"
128 );
129 assert_eq!(
130 ai.certificates[1].sha256,
131 "e8e95f0733a55e8bad7be0a1413ee23c51fcea64b3c8fa6a786935fddcc71961"
132 );
133 }
134
135 #[test]
136 fn sha1_thumbprints_signed_32() {
137 let pe_path = PathBuf::from("test-pe/test-signed-32.bin");
138 let pe_file = std::fs::read(pe_path).unwrap();
139
140 let ai = AuthenticodeInfo::try_from(&pe_file).unwrap();
141
142 assert_eq!(ai.certificates.len(), 2);
143 assert_eq!(
144 ai.certificates[0].sha1,
145 "aeb9b61e47d91c42fff213992b7810a3d562fb12"
146 );
147 assert_eq!(
148 ai.certificates[1].sha1,
149 "580a6f4cc4e4b669b9ebdc1b2b3e087b80d0678d"
150 );
151 }
152
153 #[test]
154 fn sha256_thumbprints_signed_32() {
155 let pe_path = PathBuf::from("test-pe/test-signed-32.bin");
156 let pe_file = std::fs::read(pe_path).unwrap();
157
158 let ai = AuthenticodeInfo::try_from(&pe_file).unwrap();
159
160 assert_eq!(ai.certificates.len(), 2);
161 assert_eq!(
162 ai.certificates[0].sha256,
163 "bb91b9f1a11556a6556a804d0b5c984c3d1281a04dc918ab7b0a90d8b0747fde"
164 );
165 assert_eq!(
166 ai.certificates[1].sha256,
167 "e8e95f0733a55e8bad7be0a1413ee23c51fcea64b3c8fa6a786935fddcc71961"
168 );
169 }
170
171 #[test]
172 fn no_cert_unsigned_32() {
173 let pe_path = PathBuf::from("test-pe/test-unsigned-32.bin");
174 let pe_file = std::fs::read(pe_path).unwrap();
175
176 let error = AuthenticodeInfo::try_from(&pe_file).err().unwrap();
177
178 assert_eq!(error, AuthenticodeError::NoWinCertificate);
179 }
180
181 #[test]
182 fn no_cert_unsigned_64() {
183 let pe_path = PathBuf::from("test-pe/test-unsigned-64.bin");
184 let pe_file = std::fs::read(pe_path).unwrap();
185
186 let error = AuthenticodeInfo::try_from(&pe_file).err().unwrap();
187
188 assert_eq!(error, AuthenticodeError::NoWinCertificate);
189 }
190
191 #[test]
192 fn not_a_pe_file() {
193 let pe_path = PathBuf::from("test-pe/test-no-pe.bin");
194 let pe_file = std::fs::read(pe_path).unwrap();
195
196 let error = AuthenticodeInfo::try_from(&pe_file).err().unwrap();
197
198 assert_eq!(
199 error,
200 AuthenticodeError::ParsePe("Invalid DOS header size or alignment".to_string())
201 );
202 }
203}