signalscreen-checker 0.2.1

Windows code-signing hygiene checker. Reads the Authenticode signature in a PE file and grades it A-F. Pure Rust, no Windows dependency.
Documentation
//! Extract owned facts from a parsed Authenticode signature.
use crate::timestamp::{self, TimestampInfo};
use authenticode::AuthenticodeSignature;
use cms::signed_data::SignerIdentifier;
use x509_cert::Certificate;

/// Owned summary of the signature (no borrows from the PE bytes).
#[derive(Debug, Clone)]
pub struct SignatureInfo {
    pub digest_algo_oid: String,
    /// Whether a timestamp attribute is present. Deliberately OID
    /// presence, not "the token parsed": grading depends on it, and
    /// a malformed token would cost a file points for a defect in
    /// this parser rather than one in the signature.
    pub has_timestamp: bool,
    /// The parsed detail, when it parses. `None` alongside
    /// `has_timestamp == true` means one is there and could not be
    /// read. Report the fact without the date.
    pub timestamp: Option<TimestampInfo>,
    /// The signer (leaf) certificate, selected by SignerInfo.sid — NOT by position
    /// in the chain bundle. `None` if it couldn't be matched (e.g. SubjectKeyId sid).
    pub leaf_cert: Option<Certificate>,
}

// Timestamp markers found in the signer's unsigned attributes.
const OID_MS_TIMESTAMP: &str = "1.3.6.1.4.1.311.3.3.1"; // Microsoft RFC-3161 token
const OID_COUNTERSIGNATURE: &str = "1.2.840.113549.1.9.6"; // PKCS#9 legacy timestamp

pub fn from_authenticode(sig: &AuthenticodeSignature) -> SignatureInfo {
    let si = sig.signer_info();
    let digest_algo_oid = si.digest_alg.oid.to_string();

    let has_timestamp = si.unsigned_attrs.as_ref().is_some_and(|attrs| {
        attrs.iter().any(|a| {
            let oid = a.oid.to_string();
            oid == OID_MS_TIMESTAMP || oid == OID_COUNTERSIGNATURE
        })
    });

    let leaf_cert = match &si.sid {
        SignerIdentifier::IssuerAndSerialNumber(isn) => sig
            .certificates()
            .find(|c| {
                c.tbs_certificate.issuer == isn.issuer
                    && c.tbs_certificate.serial_number == isn.serial_number
            })
            .cloned(),
        SignerIdentifier::SubjectKeyIdentifier(_) => None,
    };

    SignatureInfo {
        digest_algo_oid,
        has_timestamp,
        timestamp: timestamp::from_unsigned_attrs(si.unsigned_attrs.as_ref()),
        leaf_cert,
    }
}