pub mod cert;
pub mod checks;
pub mod pe;
pub mod report;
pub mod score;
pub mod signature;
pub mod timestamp;
use anyhow::Result;
use checks::Facts;
use report::{CertSummary, FileInfo, Report, SigSummary, TimestampSummary};
use sha2::{Digest, Sha256};
pub fn analyze(name: &str, data: &[u8], now_unix: i64) -> Result<Report> {
let sha256 = hex::encode(Sha256::digest(data));
let (facts, signature, certificate) = match pe::extract(data)? {
None => (
Facts {
signed: false,
signature_valid: false,
digest_algo_oid: String::new(),
has_timestamp: false,
self_signed: false,
not_after_unix: 0,
now_unix,
image_hash: signature::ImageHash::Unverified,
},
SigSummary {
present: false,
valid: false,
image_hash: "none",
digest_algo: "none".into(),
timestamp_present: false,
timestamp: None,
},
None,
),
Some(si) => {
let ci = si
.leaf_cert
.as_ref()
.map(cert::from_certificate)
.transpose()?;
let self_signed = ci.as_ref().is_some_and(|c| c.self_signed);
let not_after_unix = ci.as_ref().map_or(0, |c| c.not_after_unix);
let valid =
si.leaf_cert.is_some() && si.image_hash != signature::ImageHash::Mismatch;
(
Facts {
signed: true,
signature_valid: valid,
digest_algo_oid: si.digest_algo_oid.clone(),
has_timestamp: si.has_timestamp,
self_signed,
not_after_unix,
now_unix,
image_hash: si.image_hash,
},
SigSummary {
present: true,
valid,
image_hash: image_hash_str(si.image_hash),
digest_algo: oid_name(&si.digest_algo_oid),
timestamp_present: si.has_timestamp,
timestamp: si.timestamp.as_ref().map(|t| TimestampSummary {
signed_at_unix: t.signed_at_unix,
authority: t.authority.clone(),
kind: match t.kind {
crate::timestamp::TimestampKind::Rfc3161 => "rfc3161",
crate::timestamp::TimestampKind::Countersignature => {
"countersignature"
}
},
}),
},
ci.map(|c| CertSummary {
subject_o: c.subject_o,
issuer: c.issuer,
not_after_unix: c.not_after_unix,
ev_hint: c.ev_hint,
self_signed: c.self_signed,
}),
)
}
};
let results = checks::run(&facts);
let sc = score::score(&results);
Ok(Report {
grade: score::grade(sc),
score: sc,
file: FileInfo {
name: name.into(),
sha256,
},
signature,
certificate,
checks: results,
})
}
fn image_hash_str(h: signature::ImageHash) -> &'static str {
match h {
signature::ImageHash::Match => "verified",
signature::ImageHash::Mismatch => "mismatch",
signature::ImageHash::Unverified => "unverified",
}
}
fn oid_name(oid: &str) -> String {
match oid {
"2.16.840.1.101.3.4.2.1" => "SHA-256".into(),
"1.3.14.3.2.26" => "SHA-1".into(),
other => other.into(),
}
}