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::{AttributeType, SignerInfo, Signers};
18 : #[doc(inline)]
19 : pub use x509::{Certificates, KeyUsage, VerificationFlags as CertVerificationFlags, X509};
20 :
21 : use std::io::{Read, Seek};
22 :
23 : use crate::common::into_optional;
24 : use crate::common::FromFFI;
25 : use crate::declare_iterator;
26 : use crate::pe::Algorithms;
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 26 : fn from(value: u32) -> Self {
58 26 : VerificationFlags::from_bits_truncate(value)
59 26 : }
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 26 : fn from(value: VerificationChecks) -> Self {
106 26 : value.bits()
107 26 : }
108 : }
109 :
110 : pub struct Signature<'a> {
111 : ptr: cxx::UniquePtr<ffi::PE_Signature>,
112 : _owner: PhantomData<&'a ()>,
113 : }
114 :
115 : impl<'b, 'a: 'b> From<&'a Signature<'_>> for &'b ffi::PE_Signature {
116 13 : fn from(value: &'a Signature<'_>) -> &'b ffi::PE_Signature {
117 13 : value.ptr.as_ref().unwrap()
118 13 : }
119 : }
120 :
121 : impl std::fmt::Debug for Signature<'_> {
122 208 : fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123 208 : f.debug_struct("Signature").finish()
124 208 : }
125 : }
126 :
127 : impl FromFFI<ffi::PE_Signature> for Signature<'_> {
128 234 : fn from_ffi(ptr: cxx::UniquePtr<ffi::PE_Signature>) -> Self {
129 234 : Signature {
130 234 : ptr,
131 234 : _owner: PhantomData,
132 234 : }
133 234 : }
134 : }
135 :
136 : impl<'a> Signature<'a> {
137 : /// Create a Signature from a PKCS#7 file path
138 65 : pub fn from_file(path: &str) -> Option<Self> {
139 65 : let ffi = ffi::PE_Signature::parse(path);
140 65 : if ffi.is_null() {
141 0 : return None;
142 65 : }
143 65 : Some(Signature::from_ffi(ffi))
144 65 : }
145 :
146 : /// Create a Signature from a PKCS#7 *reader* implementing the `Read + Seek` traits
147 1 : pub fn from<R: Read + Seek>(reader: &mut R) -> Option<Self> {
148 1 : let mut buffer = std::vec::Vec::new();
149 1 : if reader.read_to_end(&mut buffer).is_err() {
150 0 : return None;
151 1 : }
152 1 : let ffi_stream = unsafe { ffi::PE_Signature::from_raw(buffer.as_mut_ptr(), buffer.len()) };
153 1 : Some(Signature::from_ffi(ffi_stream))
154 1 : }
155 :
156 : /// Should be 1
157 0 : pub fn version(&self) -> u32 {
158 0 : self.ptr.version()
159 0 : }
160 :
161 : /// Algorithm used to *digest* the file.
162 : ///
163 : /// It should match [`SignerInfo::digest_algorithm`]
164 0 : pub fn digest_algorithm(&self) -> Algorithms {
165 0 : Algorithms::from(self.ptr.digest_algorithm())
166 0 : }
167 :
168 : /// ContentInfo as described in the RFC2315 <https://tools.ietf.org/html/rfc2315#section-7>
169 416 : pub fn content_info(&'a self) -> ContentInfo<'a> {
170 416 : ContentInfo::from_ffi(self.ptr.content_info())
171 416 : }
172 :
173 : /// Return list of [`X509`] certificates associated with this signature
174 208 : pub fn certificates(&'a self) -> Certificates<'a> {
175 208 : Certificates::new(self.ptr.certificates())
176 208 : }
177 :
178 : /// Iterator over the signer [`SignerInfo`] defined in the PKCS #7 signature
179 208 : pub fn signers(&'a self) -> Signers<'a> {
180 208 : Signers::new(self.ptr.signers())
181 208 : }
182 :
183 : /// The original raw signature as a slice of bytes
184 13 : pub fn raw_der(&self) -> &[u8] {
185 13 : to_slice!(self.ptr.raw_der());
186 13 : }
187 :
188 : /// Find x509 certificate according to its serial number
189 0 : pub fn crt_by_serial(&self, serial: &[u8]) -> Option<X509<'_>> {
190 0 : unsafe { into_optional(self.ptr.find_crt_by_serial(serial.as_ptr(), serial.len())) }
191 0 : }
192 :
193 : /// Find [`X509`] certificate according to its subject
194 0 : pub fn crt_by_subject(&self, subject: &str) -> Option<X509<'_>> {
195 0 : into_optional(self.ptr.find_crt_by_subject(subject))
196 0 : }
197 :
198 : /// Find [`X509`] certificate according to its subject **AND** serial number
199 0 : pub fn crt_by_subject_and_serial(&self, subject: &str, serial: &[u8]) -> Option<X509<'_>> {
200 0 : unsafe {
201 0 : into_optional(self.ptr.find_crt_by_subject_and_serial(
202 0 : subject,
203 0 : serial.as_ptr(),
204 0 : serial.len(),
205 0 : ))
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(
218 0 : issuer,
219 0 : serial.as_ptr(),
220 0 : serial.len(),
221 0 : ))
222 0 : }
223 0 : }
224 :
225 : /// Check if this signature is valid according to the Authenticode/PKCS #7 verification scheme
226 : ///
227 : /// By default, it performs the following verifications:
228 : ///
229 : /// 1. It must contain only **one** signer info
230 : /// 2. [`Signature::digest_algorithm`] must match:
231 : /// * [`ContentInfo::digest_algorithm`]
232 : /// * [`SignerInfo::digest_algorithm`]
233 : /// 3. The x509 certificate specified by [`SignerInfo::serial_number`] **and** [`SignerInfo::issuer`]
234 : /// must exist within [`Signature::certificates`]
235 : /// 4. Given the x509 certificate, compare [`SignerInfo::encrypted_digest`] against either:
236 : /// * hash of authenticated attributes if present
237 : /// * hash of ContentInfo
238 : /// 5. If authenticated attributes are present, check that a `PKCS9_MESSAGE_DIGEST` attribute exists
239 : /// and that its value matches hash of ContentInfo
240 : /// 6. Check the validity of the PKCS #9 counter signature if present
241 : /// 7. If the signature doesn't embed a signing-time in the counter signature, check the certificate
242 : /// validity.
243 : /// (See [`VerificationChecks::LIFETIME_SIGNING`] and [`VerificationChecks::SKIP_CERT_TIME`])
244 : ///
245 : /// See: [`VerificationChecks`] to tweak the behavior
246 0 : pub fn check(&self, checks: VerificationChecks) -> VerificationFlags {
247 0 : VerificationFlags::from(self.ptr.check(checks.into()))
248 0 : }
249 : }
250 :
251 117 : declare_iterator!(
252 117 : Signatures,
253 117 : Signature<'a>,
254 117 : ffi::PE_Signature,
255 117 : ffi::PE_Binary,
256 117 : ffi::PE_Binary_it_signatures
257 117 : );
|