tasign 0.1.3

TA ELF signing utilities with CMS/PKCS#7 support
Documentation
//! TA ELF 签名与验签库。
//!
//! 提供两种使用模式:
//!
//! - **`std`(默认)**:完整签名工具链,包括 ELF 写入(`objcopy`)、CMS 签名(mbedtls)、
//!   GmSSL 兼容模式等。供 `tasign-tool` CLI 使用。
//! - **`kernel-verify`(`no_std` + `alloc`)**:仅验签路径,供 Rust 内核直接调用
//!   [`verify_elf_signature`] 对已嵌入 `.ta_signature` 的 ELF 做证书验签。
//!
//! 密码学运算统一由 `mbedtls-smx` 完成(SM2/SM3、RSA/SHA256、ECDSA/SHA256)。
//! GmSSL 可执行文件仅在脚本层面用于证书生成与 `cms-compat=gmssl` 兼容验证。
//!
//! # 日志(`log` 门面)
//!
//! 本 crate 使用 `log` 的 `info!` / `warn!` / `debug!` 等宏,**不**依赖具体实现:
//! - 在 **x-kernel** 中,由入口/内核侧已注册的 logger(例如 `klogger`,实现 `log::Log`)输出。
//! - **独立使用**(含 `tasign-tool`):在进程入口初始化 `env_logger`(或其它 `log` 后端);
//!   单元测试可使用 `env_logger::Builder::...::try_init()`。
#![cfg_attr(not(feature = "std"), no_std)]
#[cfg(not(feature = "std"))]
extern crate alloc;

#[cfg(any(feature = "std", feature = "kernel-verify"))]
use log::{debug, error, info};

#[cfg(all(feature = "std", feature = "bjca"))]
pub mod bjca;
pub mod cms;
#[cfg(feature = "std")]
pub mod cert;
#[cfg(feature = "std")]
pub mod elf;
pub mod error;
#[cfg(feature = "std")]
pub mod gmssl_cli;
#[cfg(feature = "std")]
mod gmssl_pkcs8_decrypt;
#[cfg(feature = "std")]
pub mod key;
#[cfg(any(feature = "std", feature = "kernel-verify"))]
pub mod limits;
#[cfg(any(feature = "std", feature = "kernel-verify"))]
pub mod plain;

#[cfg(feature = "std")]
pub use cms::{
    extract_cms_detached_parts, sign_gmssl_cms_attached_native, sign_sm2_cms, CmsSignAlgorithm,
    SignInputs,
};
pub use cms::{
    verify_cms_signature_with_ca_opt, verify_cms_signature_with_ca_opt_and_limits, verify_sm2_cms,
    verify_sm2_cms_with_ca_opt, verify_sm2_cms_with_ca_pem,
};
#[cfg(feature = "std")]
pub use elf::{
    append_ta_signature, append_ta_signature_bytes, append_ta_signature_objcopy,
    append_ta_signature_object, normalize_relocatable_elf_bytes,
    strip_ta_signature_relocatable_bytes, write_outputs,
};
pub use error::Error;
#[cfg(feature = "std")]
pub use gmssl_cli::{find_gmssl_in_path, resolve_gmssl_path, DEFAULT_GMSSL};
#[cfg(any(feature = "std", feature = "kernel-verify"))]
pub use limits::{LimitsError, VerifyLimits};
#[cfg(any(feature = "std", feature = "kernel-verify"))]
pub use plain::{
    build_plain_bin, build_plain_bin_with_limits, has_ta_signature_section,
    plain_bytes_from_signed_elf, plain_bytes_from_signed_elf_with_limits,
    ta_signature_section_bytes, ta_signature_section_bytes_with_limits, PlainError,
    TA_SIGNATURE_SECTION,
};

/// 兼容旧错误类型名。
#[cfg(any(feature = "std", feature = "kernel-verify"))]
pub type TasignError = PlainError;

/// 对已嵌入 `.ta_signature` 的整颗 ELF 验签。
///
/// 内部流程:
/// 1. 从 ELF 中提取 `.ta_signature` 节(PKCS#7 DER)
/// 2. 按 plain 协议重算签名原数据(忽略 `.ta_signature` 本身)
/// 3. 调用 CMS 验签(摘要比对 + 签名验证 + 可选证书链校验)
///
/// # 参数
/// - `elf_bytes`: 完整的已签名 ELF 文件字节
/// - `ca_pem`: 信任锚 PEM(`Some` 时校验证书链到根;`None` 时仅校验 CMS 签名)
///
/// # Feature
/// 在 `std` 与 `kernel-verify` 下均可用。
#[cfg(any(feature = "std", feature = "kernel-verify"))]
pub fn verify_elf_signature(elf_bytes: &[u8], ca_pem: Option<&[u8]>) -> Result<(), Error> {
    verify_elf_signature_with_limits(elf_bytes, ca_pem, &VerifyLimits::default())
}

/// 与 [`verify_elf_signature`] 相同,但使用调用方提供的资源上限。
#[cfg(any(feature = "std", feature = "kernel-verify"))]
pub fn verify_elf_signature_with_limits(
    elf_bytes: &[u8],
    ca_pem: Option<&[u8]>,
    limits: &VerifyLimits,
) -> Result<(), Error> {
    info!(
        target: "tasign",
        "verify_elf_signature_with_limits: elf_bytes.len: {}",
        elf_bytes.len()
    );
    limits.check_elf_input_len(elf_bytes.len()).map_err(|e| {
        error!(target: "tasign", "verify_elf: ELF 输入长度超过上限: {e}");
        Error::from(e)
    })?;
    let pkcs7 = ta_signature_section_bytes_with_limits(elf_bytes, limits).map_err(|e| {
        error!(
            target: "tasign",
            "verify_elf: 提取 `.ta_signature`(PKCS#7 DER)失败: {e}"
        );
        Error::Plain(e)
    })?;
    limits.check_cms_der_len(pkcs7.len()).map_err(|e| {
        error!(target: "tasign", "verify_elf: `.ta_signature` DER 长度超过上限: {e}");
        Error::from(e)
    })?;
    let plain = plain_bytes_from_signed_elf_with_limits(elf_bytes, limits).map_err(|e| {
        error!(
            target: "tasign",
            "verify_elf: 重算签名原数据 plain 失败: {e}"
        );
        Error::Plain(e)
    })?;
    limits.check_plain_buffer_len(plain.len()).map_err(|e| {
        error!(target: "tasign", "verify_elf: plain 输出长度超过上限: {e}");
        Error::from(e)
    })?;
    debug!(target: "tasign", "verify: cms_der.len={}, plain.len={}", pkcs7.len(), plain.len());
    verify_cms_signature_with_ca_opt_and_limits(pkcs7, &plain, ca_pem, limits).map_err(|e| {
        error!(
            target: "tasign",
            "verify_elf: CMS/PKCS#7 验签失败: {e}"
        );
        e
    })
}

/// 分离式验签:调用方自行提供 `plain` 与 PKCS#7 DER。
///
/// 适用于签名数据与 ELF 分开存储的场景。行为等价于
/// [`verify_cms_signature_with_ca_opt`]。
///
/// # Feature
/// 在 `std` 与 `kernel-verify` 下均可用。
#[cfg(any(feature = "std", feature = "kernel-verify"))]
pub fn verify_elf_signature_from_parts(
    plain_elf_sections: &[u8],
    signature_der: &[u8],
    ca_pem: Option<&[u8]>,
) -> Result<(), Error> {
    verify_elf_signature_from_parts_with_limits(
        plain_elf_sections,
        signature_der,
        ca_pem,
        &VerifyLimits::default(),
    )
}

/// 分离式验签,并套用资源上限(分别约束 `plain` 与 PKCS#7 DER 长度)。
#[cfg(any(feature = "std", feature = "kernel-verify"))]
pub fn verify_elf_signature_from_parts_with_limits(
    plain_elf_sections: &[u8],
    signature_der: &[u8],
    ca_pem: Option<&[u8]>,
    limits: &VerifyLimits,
) -> Result<(), Error> {
    limits
        .check_plain_buffer_len(plain_elf_sections.len())
        .map_err(|e| {
            error!(target: "tasign", "verify_elf_from_parts: plain 长度超过上限: {e}");
            Error::from(e)
        })?;
    limits.check_cms_der_len(signature_der.len()).map_err(|e| {
        error!(target: "tasign", "verify_elf_from_parts: PKCS#7 DER 长度超过上限: {e}");
        Error::from(e)
    })?;
    verify_cms_signature_with_ca_opt_and_limits(signature_der, plain_elf_sections, ca_pem, limits)
        .map_err(|e| {
            error!(
                target: "tasign",
                "verify_elf_from_parts: CMS/PKCS#7 验签失败(plain 与签名不匹配等): {e}"
            );
            e
        })
}

#[cfg(all(test, feature = "std"))]
mod tests {
    use super::*;

    fn init_log() {
        let _ = env_logger::builder().is_test(true).try_init();
    }

    #[test]
    fn plain_ok_without_ta_signature_fixture() {
        init_log();
        static ELF: &[u8] = include_bytes!("../tests/fixtures/trivial_elf64.o");
        let p = build_plain_bin(ELF).expect("fixture has no .ta_signature");
        assert!(!p.is_empty());
    }

    #[test]
    fn rejects_when_ta_signature_present_fixture() {
        init_log();
        static ELF: &[u8] = include_bytes!("../tests/fixtures/trivial_with_ta_sig.o");
        let err = build_plain_bin(ELF).unwrap_err();
        match err {
            PlainError::TaSignatureSectionExists(_) => {}
            e => panic!("unexpected: {e:?}"),
        }
    }
}