signalscreen_checker/
lib.rs1pub mod cert;
6pub mod checks;
7pub mod pe;
8pub mod report;
9pub mod score;
10pub mod signature;
11pub mod timestamp;
12
13use anyhow::Result;
14use checks::Facts;
15use report::{CertSummary, FileInfo, Report, SigSummary, TimestampSummary};
16use sha2::{Digest, Sha256};
17
18pub fn analyze(name: &str, data: &[u8], now_unix: i64) -> Result<Report> {
21 let sha256 = hex::encode(Sha256::digest(data));
22
23 let (facts, signature, certificate) = match pe::extract(data)? {
24 None => (
25 Facts {
26 signed: false,
27 signature_valid: false,
28 digest_algo_oid: String::new(),
29 has_timestamp: false,
30 self_signed: false,
31 not_after_unix: 0,
32 now_unix,
33 image_hash: signature::ImageHash::Unverified,
34 },
35 SigSummary {
36 present: false,
37 valid: false,
38 image_hash: "none",
39 digest_algo: "none".into(),
40 timestamp_present: false,
41 timestamp: None,
42 },
43 None,
44 ),
45 Some(si) => {
46 let ci = si
47 .leaf_cert
48 .as_ref()
49 .map(cert::from_certificate)
50 .transpose()?;
51 let self_signed = ci.as_ref().is_some_and(|c| c.self_signed);
52 let not_after_unix = ci.as_ref().map_or(0, |c| c.not_after_unix);
53 let valid =
57 si.leaf_cert.is_some() && si.image_hash != signature::ImageHash::Mismatch;
58 (
59 Facts {
60 signed: true,
61 signature_valid: valid,
62 digest_algo_oid: si.digest_algo_oid.clone(),
63 has_timestamp: si.has_timestamp,
64 self_signed,
65 not_after_unix,
66 now_unix,
67 image_hash: si.image_hash,
68 },
69 SigSummary {
70 present: true,
71 valid,
72 image_hash: image_hash_str(si.image_hash),
73 digest_algo: oid_name(&si.digest_algo_oid),
74 timestamp_present: si.has_timestamp,
75 timestamp: si.timestamp.as_ref().map(|t| TimestampSummary {
76 signed_at_unix: t.signed_at_unix,
77 authority: t.authority.clone(),
78 kind: match t.kind {
79 crate::timestamp::TimestampKind::Rfc3161 => "rfc3161",
80 crate::timestamp::TimestampKind::Countersignature => {
81 "countersignature"
82 }
83 },
84 }),
85 },
86 ci.map(|c| CertSummary {
87 subject_o: c.subject_o,
88 issuer: c.issuer,
89 not_after_unix: c.not_after_unix,
90 ev_hint: c.ev_hint,
91 self_signed: c.self_signed,
92 }),
93 )
94 }
95 };
96
97 let results = checks::run(&facts);
98 let sc = score::score(&results);
99 Ok(Report {
100 grade: score::grade(sc),
101 score: sc,
102 file: FileInfo {
103 name: name.into(),
104 sha256,
105 },
106 signature,
107 certificate,
108 checks: results,
109 })
110}
111
112fn image_hash_str(h: signature::ImageHash) -> &'static str {
114 match h {
115 signature::ImageHash::Match => "verified",
116 signature::ImageHash::Mismatch => "mismatch",
117 signature::ImageHash::Unverified => "unverified",
118 }
119}
120
121fn oid_name(oid: &str) -> String {
122 match oid {
123 "2.16.840.1.101.3.4.2.1" => "SHA-256".into(),
124 "1.3.14.3.2.26" => "SHA-1".into(),
125 other => other.into(),
126 }
127}