use clap::Parser;
use directories::BaseDirs;
use duct::cmd;
use serde::{Deserialize, Serialize};
use std::fs;
use trauma::{download::Download, downloader::DownloaderBuilder};
use zip_extensions::zip_extract;
#[derive(Serialize, Deserialize, Debug)]
pub struct Metadata {
#[serde(rename = "Endpoint")]
pub endpoint: String,
#[serde(rename = "CodeSigningAccountName")]
pub code_signing_account_name: String,
#[serde(rename = "CertificateProfileName")]
pub certificate_profile: String,
}
#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
#[arg(required = true, value_name = "FILE(S)", num_args = 1..=99)]
file: Vec<String>,
#[arg(long, env = "AZURE_CLIENT_SECRET")]
azure_client_secret: String,
#[arg(long, env = "AZURE_CLIENT_ID")]
azure_client_id: String,
#[arg(long, env = "AZURE_TENANT_ID")]
azure_tenant_id: String,
#[arg(
long,
env = "AZURE_CLI_PATH",
default_value = r"C:\Program Files\Microsoft SDKs\Azure\CLI2\wbin\az.cmd"
)]
azure_cli_path: String,
#[arg(
long,
env = "SIGNTOOL_PATH",
default_value = r"C:\Program Files (x86)\Windows Kits\10\bin\10.0.22000.0\x64\signtool.exe"
)]
sing_tool_path: String,
#[arg(long, short = 'e', verbatim_doc_comment)]
endpoint: String,
#[arg(long, short = 'a')]
account: String,
#[arg(long, short = 'c')]
certificate: String,
}
#[tokio::main]
async fn main() {
let args = Args::parse();
let base = BaseDirs::new().unwrap();
let home = base.home_dir();
let config_dir = home.join(".trusted-signing-cli");
if !config_dir.exists() {
std::fs::create_dir_all(&config_dir).unwrap();
}
let lib_path = config_dir
.join("lib")
.join("bin")
.join("x64")
.join("Azure.CodeSigning.Dlib.dll");
if !lib_path.exists() {
let link = "https://www.nuget.org/api/v2/package/Microsoft.Trusted.Signing.Client/1.0.52";
let downloads = vec![Download::try_from(link).unwrap()];
let downloader = DownloaderBuilder::new()
.directory(config_dir.clone())
.build();
downloader.download(&downloads).await;
let archive = config_dir.join("1.0.52");
let target_dir = config_dir.join("lib");
zip_extract(&archive, &target_dir).unwrap();
}
let metadata_path = config_dir.join("metadata.json");
if !metadata_path.exists() {
let data = Metadata {
certificate_profile: args.certificate,
code_signing_account_name: args.account,
endpoint: args.endpoint,
};
fs::write(
config_dir.join("metadata.json"),
serde_json::to_string(&data).unwrap(),
)
.unwrap();
}
cmd!(
args.azure_cli_path,
"login",
"--service-principal",
"-t",
args.azure_tenant_id,
"-u",
args.azure_client_id,
"-p",
args.azure_client_secret
)
.run()
.unwrap();
for file in args.file {
cmd!(
&args.sing_tool_path,
"sign",
"/v",
"/fd",
"SHA256",
"/tr",
"http://timestamp.acs.microsoft.com",
"/td",
"SHA256",
"/dlib",
&lib_path,
"/dmdf",
&metadata_path,
file
)
.run()
.unwrap();
}
}