#[cfg(target_os = "macos")]
mod macos;
#[cfg(windows)]
mod windows;
#[cfg(target_os = "macos")]
use macos::{Context, Verifier};
#[cfg(windows)]
use windows::{Context, Verifier};
pub struct CodeSignVerifier(Verifier);
pub struct SignatureContext(Context);
#[derive(Debug)]
pub struct Name {
pub common_name: Option<String>, pub organization: Option<String>, pub organization_unit: Option<String>, pub country: Option<String>, }
#[derive(Debug)]
pub enum Error {
Unsigned, OsError(i32), InvalidPath, LeafCertNotFound, #[cfg(target_os = "macos")]
CFError(String),
#[cfg(windows)]
IoError(std::io::Error),
}
impl CodeSignVerifier {
pub fn for_file<P: AsRef<std::path::Path>>(path: P) -> Result<Self, Error> {
Verifier::for_file(path).map(|v| CodeSignVerifier(v))
}
pub fn for_pid(pid: i32) -> Result<Self, Error> {
Verifier::for_pid(pid).map(|v| CodeSignVerifier(v))
}
pub fn verify(self) -> Result<SignatureContext, Error> {
self.0.verify().map(|c| SignatureContext(c))
}
}
impl SignatureContext {
pub fn subject_name(&self) -> Name {
self.0.subject_name()
}
pub fn issuer_name(&self) -> Name {
self.0.issuer_name()
}
pub fn sha1_thumbprint(&self) -> String {
self.0.sha1_thumbprint()
}
pub fn sha256_thumbprint(&self) -> String {
self.0.sha256_thumbprint()
}
pub fn serial(&self) -> Option<String> {
self.0.serial()
}
}
#[cfg(test)]
mod tests {
use crate::Error;
#[test]
#[cfg(target_os = "macos")]
fn test_signed() {
let verifier = super::CodeSignVerifier::for_file("/sbin/ping").unwrap(); let ctx = verifier.verify().unwrap();
assert_eq!(
ctx.subject_name().organization.as_deref(),
Some("Apple Inc.")
);
assert_eq!(
ctx.issuer_name().organization_unit.as_deref(),
Some("Apple Certification Authority")
);
assert_eq!(
ctx.sha1_thumbprint(),
"013e2787748a74103d62d2cdbf77a1345517c482"
);
}
#[test]
#[cfg(windows)]
fn test_signed() {
let path = format!("{}/explorer.exe", std::env::var("windir").unwrap()); let verifier = super::CodeSignVerifier::for_file(path).unwrap();
let ctx = verifier.verify().unwrap();
assert_eq!(
ctx.subject_name().organization.as_deref(),
Some("Microsoft Corporation")
);
assert_eq!(
ctx.issuer_name().common_name.as_deref(),
Some("Microsoft Windows Production PCA 2011")
);
assert_eq!(
ctx.sha1_thumbprint(),
"a4341b9fd50fb9964283220a36a1ef6f6faa7840"
);
assert_eq!(
ctx.serial().as_deref(),
Some("3300000266bd1580efa75cd6d3000000000266")
);
}
#[test]
fn test_unsigned() {
let path = std::env::args().next().unwrap();
assert!(matches!(
super::CodeSignVerifier::for_file(path).unwrap().verify(),
Err(Error::Unsigned)
));
}
}