Line data Source code
1 : //! This module wraps the PKCS #7 PE authenticode signature
2 :
3 : use bitflags::bitflags;
4 : use lief_ffi as ffi;
5 :
6 : pub mod attributes;
7 : pub mod content_info;
8 : pub mod rsa_info;
9 : pub mod signer_info;
10 : pub mod x509;
11 :
12 : #[doc(inline)]
13 : pub use content_info::ContentInfo;
14 : #[doc(inline)]
15 : pub use rsa_info::RsaInfo;
16 : #[doc(inline)]
17 : pub use signer_info::{SignerInfo, Signers};
18 : #[doc(inline)]
19 : pub use x509::{Certificates, X509};
20 :
21 : use std::io::{Read, Seek};
22 :
23 : use crate::pe::Algorithms;
24 : use crate::common::into_optional;
25 : use crate::common::FromFFI;
26 : use crate::declare_iterator;
27 : use crate::to_slice;
28 :
29 : use std::marker::PhantomData;
30 :
31 0 : bitflags! {
32 2 : #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
33 0 : pub struct VerificationFlags: u32 {
34 0 : const OK = 0;
35 0 : const INVALID_SIGNER = 1 << 0;
36 0 : const UNSUPPORTED_ALGORITHM = 1 << 1;
37 0 : const INCONSISTENT_DIGEST_ALGORITHM = 1 << 2;
38 0 : const CERT_NOT_FOUND = 1 << 3;
39 0 : const CORRUPTED_CONTENT_INFO = 1 << 4;
40 0 : const CORRUPTED_AUTH_DATA = 1 << 5;
41 0 : const MISSING_PKCS9_MESSAGE_DIGEST = 1 << 6;
42 0 : const BAD_DIGEST = 1 << 7;
43 0 : const BAD_SIGNATURE = 1 << 8;
44 0 : const NO_SIGNATURE = 1 << 9;
45 0 : const CERT_EXPIRED = 1 << 10;
46 0 : const CERT_FUTURE = 1 << 11;
47 0 : }
48 0 : }
49 :
50 : impl std::fmt::Display for VerificationFlags {
51 0 : fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
52 0 : bitflags::parser::to_writer(self, f)
53 0 : }
54 : }
55 :
56 : impl From<u32> for VerificationFlags {
57 16 : fn from(value: u32) -> Self {
58 16 : VerificationFlags::from_bits_truncate(value)
59 16 : }
60 : }
61 :
62 : impl From<VerificationFlags> for u32 {
63 0 : fn from(value: VerificationFlags) -> Self {
64 0 : value.bits()
65 0 : }
66 : }
67 :
68 : impl VerificationFlags {
69 0 : pub fn is_ok(self) -> bool {
70 0 : self == VerificationFlags::OK
71 0 : }
72 : }
73 :
74 0 : bitflags! {
75 0 : #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
76 0 : /// Flags to tweak the verification process of the signature
77 0 : ///
78 0 : /// See [`Signature::check`] and [`crate::pe::Binary::verify_signature`]
79 0 : pub struct VerificationChecks: u32 {
80 0 : /// Default behavior that tries to follow the Microsoft verification process as close as
81 0 : /// possible
82 0 : const DEFAULT = 1 << 0;
83 0 :
84 0 : /// Only check that [`crate::pe::Binary::authentihash`] matches
85 0 : /// [`ContentInfo::digest`] regardless of the signature's validity
86 0 : const HASH_ONLY = 1 << 1;
87 0 :
88 0 : /// Same semantic as
89 0 : /// [WTD_LIFETIME_SIGNING_FLAG](https://docs.microsoft.com/en-us/windows/win32/api/wintrust/ns-wintrust-wintrust_data#WTD_LIFETIME_SIGNING_FLAG)
90 0 : const LIFETIME_SIGNING = 1 << 2;
91 0 :
92 0 : /// Skip the verification of the certificates time validities so that even though a
93 0 : /// certificate expired, it returns [`VerificationFlags::OK`]
94 0 : const SKIP_CERT_TIME = 1 << 3;
95 0 : }
96 0 : }
97 :
98 : impl From<u32> for VerificationChecks {
99 0 : fn from(value: u32) -> Self {
100 0 : VerificationChecks::from_bits_truncate(value)
101 0 : }
102 : }
103 :
104 : impl From<VerificationChecks> for u32 {
105 16 : fn from(value: VerificationChecks) -> Self {
106 16 : value.bits()
107 16 : }
108 : }
109 :
110 :
111 : pub struct Signature<'a> {
112 : ptr: cxx::UniquePtr<ffi::PE_Signature>,
113 : _owner: PhantomData<&'a ()>,
114 : }
115 :
116 : impl<'b, 'a: 'b> From<&'a Signature<'_>> for &'b ffi::PE_Signature {
117 8 : fn from(value: &'a Signature<'_>) -> &'b ffi::PE_Signature {
118 8 : value.ptr.as_ref().unwrap()
119 8 : }
120 : }
121 :
122 : impl std::fmt::Debug for Signature<'_> {
123 104 : fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124 104 : f.debug_struct("Signature").finish()
125 104 : }
126 : }
127 :
128 : impl FromFFI<ffi::PE_Signature> for Signature<'_> {
129 120 : fn from_ffi(ptr: cxx::UniquePtr<ffi::PE_Signature>) -> Self {
130 120 : Signature {
131 120 : ptr,
132 120 : _owner: PhantomData,
133 120 : }
134 120 : }
135 : }
136 :
137 : impl<'a> Signature<'a> {
138 : /// Create a Signature from a PKCS#7 file path
139 40 : pub fn from_file(path: &str) -> Option<Self> {
140 40 : let ffi = ffi::PE_Signature::parse(path);
141 40 : if ffi.is_null() {
142 0 : return None;
143 40 : }
144 40 : Some(Signature::from_ffi(ffi))
145 40 : }
146 :
147 : /// Create a Signature from a PKCS#7 *reader* implementing the `Read + Seek` traits
148 1 : pub fn from<R: Read + Seek>(reader: &mut R) -> Option<Self> {
149 1 : let mut buffer = std::vec::Vec::new();
150 1 : if reader.read_to_end(&mut buffer).is_err() {
151 0 : return None;
152 1 : }
153 1 : let ffi_stream =
154 1 : unsafe { ffi::PE_Signature::from_raw(buffer.as_mut_ptr(), buffer.len()) };
155 1 : Some(Signature::from_ffi(ffi_stream))
156 1 : }
157 :
158 : /// Should be 1
159 0 : pub fn version(&self) -> u32 {
160 0 : self.ptr.version()
161 0 : }
162 :
163 : /// Algorithm used to *digest* the file.
164 : ///
165 : /// It should match [`SignerInfo::digest_algorithm`]
166 0 : pub fn digest_algorithm(&self) -> Algorithms {
167 0 : Algorithms::from(self.ptr.digest_algorithm())
168 0 : }
169 :
170 : /// ContentInfo as described in the RFC2315 <https://tools.ietf.org/html/rfc2315#section-7>
171 208 : pub fn content_info(&'a self) -> ContentInfo<'a> {
172 208 : ContentInfo::from_ffi(self.ptr.content_info())
173 208 : }
174 :
175 : /// Return list of [`X509`] certificates associated with this signature
176 104 : pub fn certificates(&'a self) -> Certificates<'a> {
177 104 : Certificates::new(self.ptr.certificates())
178 104 : }
179 :
180 : /// Iterator over the signer [`SignerInfo`] defined in the PKCS #7 signature
181 104 : pub fn signers(&'a self) -> Signers<'a> {
182 104 : Signers::new(self.ptr.signers())
183 104 : }
184 :
185 : /// The original raw signature as a slice of bytes
186 8 : pub fn raw_der(&'a self) -> &[u8] {
187 8 : to_slice!(self.ptr.raw_der());
188 8 : }
189 :
190 : /// Find x509 certificate according to its serial number
191 0 : pub fn crt_by_serial(&self, serial: &[u8]) -> Option<X509> {
192 0 : unsafe {
193 0 : into_optional(self.ptr.find_crt_by_serial(serial.as_ptr(), serial.len()))
194 0 : }
195 0 : }
196 :
197 : /// Find [`X509`] certificate according to its subject
198 0 : pub fn crt_by_subject(&self, subject: &str) -> Option<X509> {
199 0 : into_optional(self.ptr.find_crt_by_subject(subject))
200 0 : }
201 :
202 : /// Find [`X509`] certificate according to its subject **AND** serial number
203 0 : pub fn crt_by_subject_and_serial(&self, subject: &str, serial: &[u8]) -> Option<X509> {
204 0 : unsafe {
205 0 : into_optional(self.ptr.find_crt_by_subject_and_serial(subject, serial.as_ptr(), serial.len()))
206 0 : }
207 0 : }
208 :
209 : /// Find [`X509`] certificate according to its issuer
210 0 : pub fn crt_by_issuer(&self, issuer: &str) -> Option<X509> {
211 0 : into_optional(self.ptr.find_crt_by_issuer(issuer))
212 0 : }
213 :
214 : /// Find [`X509`] certificate according to its issuer **AND** serial number
215 0 : pub fn find_crt_by_issuer_and_serial(&self, issuer: &str, serial: &[u8]) -> Option<X509> {
216 0 : unsafe {
217 0 : into_optional(self.ptr.find_crt_by_issuer_and_serial(issuer, serial.as_ptr(), serial.len()))
218 0 : }
219 0 : }
220 :
221 : /// Check if this signature is valid according to the Authenticode/PKCS #7 verification scheme
222 : ///
223 : /// By default, it performs the following verifications:
224 : ///
225 : /// 1. It must contain only **one** signer info
226 : /// 2. [`Signature::digest_algorithm`] must match:
227 : /// * [`ContentInfo::digest_algorithm`]
228 : /// * [`SignerInfo::digest_algorithm`]
229 : /// 3. The x509 certificate specified by [`SignerInfo::serial_number`] **and** [`SignerInfo::issuer`]
230 : /// must exist within [`Signature::certificates`]
231 : /// 4. Given the x509 certificate, compare [`SignerInfo::encrypted_digest`] against either:
232 : /// * hash of authenticated attributes if present
233 : /// * hash of ContentInfo
234 : /// 5. If authenticated attributes are present, check that a `PKCS9_MESSAGE_DIGEST` attribute exists
235 : /// and that its value matches hash of ContentInfo
236 : /// 6. Check the validity of the PKCS #9 counter signature if present
237 : /// 7. If the signature doesn't embed a signing-time in the counter signature, check the certificate
238 : /// validity.
239 : /// (See [`VerificationChecks::LIFETIME_SIGNING`] and [`VerificationChecks::SKIP_CERT_TIME`])
240 : ///
241 : /// See: [`VerificationChecks`] to tweak the behavior
242 0 : pub fn check(&self, checks: VerificationChecks) -> VerificationFlags {
243 0 : VerificationFlags::from(self.ptr.check(checks.into()))
244 0 : }
245 : }
246 :
247 48 : declare_iterator!(
248 48 : Signatures,
249 48 : Signature<'a>,
250 48 : ffi::PE_Signature,
251 48 : ffi::PE_Binary,
252 48 : ffi::PE_Binary_it_signatures
253 48 : );
|