tasign 0.2.0

TA ELF signing utilities with CMS/PKCS#7 support
//! Shared PKCS#8 PEM → clear DER / SM2 scalar extraction (both backends, `std` only).
//!
//! PEM / PKCS#8 outer structure: `pem` + `pkcs8` crates.
//! SEC1 `ECPrivateKey` inner scalar: `sec1::EcPrivateKey`.
//! GmSSL encrypted PEM (PBKDF2-SM3 + SM4-CBC): per-backend `gmssl_pkcs8` module.

extern crate alloc;

use alloc::string::ToString;
use alloc::vec::Vec;

use num_bigint::BigUint;
use pkcs8::{EncryptedPrivateKeyInfoRef, PrivateKeyInfoRef};
use sec1::der::Decode;
use sec1::EcPrivateKey;

use crate::crypto::CryptoError;

/// Decrypt PEM (plain or encrypted PKCS#8) and return cleartext `PrivateKeyInfo` DER.
pub fn sm2_pkcs8_clear_der_from_pem_with_pass(pem: &str, pass: &str) -> Result<Vec<u8>, CryptoError> {
    for p in pem::parse_many(pem).map_err(|e| CryptoError::Message(e.to_string()))? {
        match p.tag() {
            "PRIVATE KEY" => return Ok(p.contents().to_vec()),
            "ENCRYPTED PRIVATE KEY" => {
                let der = p.contents();
                return decrypt_encrypted_pkcs8_der(der, pass);
            }
            _ => {}
        }
    }
    Err(CryptoError::Message(
        "no PRIVATE KEY or ENCRYPTED PRIVATE KEY block in PEM".into(),
    ))
}

fn decrypt_encrypted_pkcs8_der(der: &[u8], pass: &str) -> Result<Vec<u8>, CryptoError> {
    #[cfg(all(feature = "backend-mbedtls", not(feature = "backend-rustcrypto")))]
    {
        if let Ok(plain) =
            crate::crypto::backend_mbedtls::gmssl_pkcs8::decrypt_gmssl_encrypted_pkcs8_der(der, pass)
        {
            return Ok(plain);
        }
    }
    #[cfg(all(feature = "backend-rustcrypto", not(feature = "backend-mbedtls")))]
    {
        if let Ok(plain) =
            crate::crypto::backend_rustcrypto::gmssl_pkcs8::decrypt_gmssl_encrypted_pkcs8_der(der, pass)
        {
            return Ok(plain);
        }
    }
    let enc = EncryptedPrivateKeyInfoRef::try_from(der)
        .map_err(|e| CryptoError::Message(alloc::format!("encrypted pkcs8: {e}")))?;
    let doc = enc
        .decrypt(pass.as_bytes())
        .map_err(|e| CryptoError::Message(alloc::format!("pkcs8 decrypt: {e}")))?;
    Ok(doc.as_bytes().to_vec())
}

/// Extract the 32-byte SM2 private scalar from cleartext PKCS#8 DER.
pub fn sm2_scalar_from_pkcs8_der(der: &[u8]) -> Result<[u8; 32], CryptoError> {
    let pk = PrivateKeyInfoRef::try_from(der)
        .map_err(|e| CryptoError::Message(alloc::format!("pkcs8: {e}")))?;
    scalar_from_sec1_ec_private_key(pk.private_key.as_bytes())
}

/// Parse PEM and return the SM2 private scalar (for mbedtls `Mpi` construction).
pub fn sm2_secret_from_pkcs8_pem_with_pass(pem: &str, pass: &str) -> Result<BigUint, CryptoError> {
    let der = sm2_pkcs8_clear_der_from_pem_with_pass(pem, pass)?;
    let scalar = sm2_scalar_from_pkcs8_der(&der)?;
    Ok(BigUint::from_bytes_be(&scalar))
}

fn scalar_from_sec1_ec_private_key(sec1: &[u8]) -> Result<[u8; 32], CryptoError> {
    let ec = EcPrivateKey::from_der(sec1)
        .map_err(|e| CryptoError::Message(alloc::format!("sec1: {e}")))?;
    if ec.private_key.len() != 32 {
        return Err(CryptoError::Message(alloc::format!(
            "expected 32-byte SM2 private OCTET STRING, got {}",
            ec.private_key.len()
        )));
    }
    let mut out = [0u8; 32];
    out.copy_from_slice(ec.private_key);
    Ok(out)
}

pub fn sm2_scalar_fixed32_be(n: &BigUint) -> Result<[u8; 32], CryptoError> {
    let b = n.to_bytes_be();
    if b.len() > 32 {
        return Err(CryptoError::InvalidKey);
    }
    let mut out = [0u8; 32];
    out[32 - b.len()..].copy_from_slice(&b);
    Ok(out)
}