tasign 0.1.3

TA ELF signing utilities with CMS/PKCS#7 support
Documentation
//! GmSSL 可执行路径解析;SM2 签名/验签由 **mbedtls-smx** 实现(与 `gmssl sm2sign` / `sm2verify`、默认 ID `1234567812345678` 一致:`pk_md_sm2` + 签名)。
//!
//! [`resolve_gmssl_path`] 解析顺序:
//! 1. 调用方显式传入的路径(例如 CLI `--gmssl`);
//! 2. 环境变量 `GMSSL`(非空则作为可执行路径);
//! 3. `PATH` 中第一个名为 `gmssl` 且可执行的文件;
//! 4. 当前目录下的 [`DEFAULT_GMSSL`],若不存在则沿父目录查找 `GmSSL/build/bin/gmssl`,
//!    仍不存在则返回 [`DEFAULT_GMSSL`] 字面路径(由调用方处理「不存在」)。

use std::fs;
use std::path::{Path, PathBuf};
use std::sync::Arc;

use mbedtls::hash::Type as MdType;
use mbedtls::pk::ECDSA_MAX_LEN;
use mbedtls::rng::{CtrDrbg, OsEntropy};
use mbedtls::x509::Certificate;

use crate::error::Error;

pub const DEFAULT_GMSSL: &str = "./GmSSL/build/bin/gmssl";
pub const DEFAULT_SM2_ID: &str = "1234567812345678";

/// 在 `PATH` 目录中查找名为 `program` 的可执行文件(Unix 检查可执行位;Windows 尝试 `.exe`)。
pub fn find_gmssl_in_path(program: &str) -> Option<PathBuf> {
    #[cfg(unix)]
    {
        use std::os::unix::fs::PermissionsExt;
        std::env::var_os("PATH").and_then(|paths| {
            std::env::split_paths(&paths).find_map(|dir| {
                let full = dir.join(program);
                if full.is_file() {
                    if let Ok(m) = fs::metadata(&full) {
                        if m.permissions().mode() & 0o111 != 0 {
                            return Some(full);
                        }
                    }
                }
                None
            })
        })
    }
    #[cfg(windows)]
    {
        std::env::var_os("PATH").and_then(|paths| {
            std::env::split_paths(&paths).find_map(|dir| {
                let with_exe = dir.join(format!("{program}.exe"));
                if with_exe.is_file() {
                    return Some(with_exe);
                }
                let full = dir.join(program);
                if full.is_file() {
                    return Some(full);
                }
                None
            })
        })
    }
    #[cfg(not(any(unix, windows)))]
    {
        let _ = program;
        None
    }
}

/// 按模块文档顺序解析 GmSSL 可执行路径。
pub fn resolve_gmssl_path(cli_override: Option<&Path>) -> PathBuf {
    if let Some(explicit) = cli_override {
        return explicit.to_path_buf();
    }
    if let Ok(s) = std::env::var("GMSSL") {
        let t = s.trim();
        if !t.is_empty() {
            return PathBuf::from(t);
        }
    }
    if let Some(p) = find_gmssl_in_path("gmssl") {
        return p;
    }
    let candidate = PathBuf::from(DEFAULT_GMSSL);
    if candidate.exists() {
        return candidate;
    }
    let mut cwd = std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
    loop {
        let c = cwd.join("GmSSL/build/bin/gmssl");
        if c.exists() {
            return c;
        }
        if !cwd.pop() {
            break;
        }
    }
    PathBuf::from(DEFAULT_GMSSL)
}

/// 对 `input` 文件内容做 SM2 签名(SM3 预处理 `pk_md_sm2`,默认用户 ID 与 GmSSL 一致)。
/// `gmssl` 参数保留以兼容旧调用,已忽略。
pub fn sm2_sign(
    _gmssl: &str,
    key_pem: &Path,
    pass: &str,
    input: &Path,
    out_sig_der: &Path,
) -> Result<(), Error> {
    let pem = fs::read_to_string(key_pem)?;
    let mut pk = crate::key::sm2_pk_from_pkcs8_pem_with_pass(&pem, pass)?;
    let data = fs::read(input)?;
    let entropy = Arc::new(OsEntropy::new());
    let mut rng = CtrDrbg::new(entropy, None)?;
    let mut sig = vec![0u8; ECDSA_MAX_LEN];
    let n = pk.sm2_sign(MdType::SM3, &data, &mut sig, &mut rng)?;
    sig.truncate(n);
    fs::write(out_sig_der, &sig)?;
    Ok(())
}

/// 使用证书公钥验证 SM2 签名(与 `gmssl sm2verify -cert … -id …` 一致)。
/// `gmssl` 参数保留以兼容旧调用,已忽略。
pub fn sm2_verify(
    _gmssl: &str,
    cert_pem: &Path,
    input: &Path,
    sig_der: &Path,
) -> Result<(), Error> {
    let mut pem = fs::read(cert_pem)?;
    pem.push(0);
    let mut cert = Certificate::from_pem(&pem)?;
    let data = fs::read(input)?;
    let sig = fs::read(sig_der)?;
    cert.public_key_mut().sm2_verify(MdType::SM3, &data, &sig)?;
    Ok(())
}