tasign 0.2.0

TA ELF signing utilities with CMS/PKCS#7 support
//! GmSSL `ENCRYPTED PRIVATE KEY` decryption via RustCrypto (PBKDF2-HMAC-SM3 + SM4-CBC).

extern crate alloc;

use alloc::format;
use alloc::vec::Vec;

use cipher::block_padding::Pkcs7;
use cipher::{BlockModeDecrypt, KeyIvInit};
use hmac::Hmac;
use pbkdf2::pbkdf2;
use sm3::Sm3;

use crate::crypto::gmssl_pkcs8_parse::parse_gmssl_encrypted_pkcs8_der;
use crate::crypto::CryptoError;

type Sm4CbcDec = cbc::Decryptor<sm4::Sm4>;
type HmacSm3 = Hmac<Sm3>;

fn pbkdf2_hmac_sm3(
    password: &[u8],
    salt: &[u8],
    iterations: usize,
    dk_len: usize,
) -> Result<Vec<u8>, CryptoError> {
    if iterations == 0 || dk_len == 0 {
        return Err(CryptoError::InvalidInput);
    }
    let iterations: u32 = iterations
        .try_into()
        .map_err(|_| CryptoError::InvalidInput)?;
    let mut dk = vec![0u8; dk_len];
    pbkdf2::<HmacSm3>(password, salt, iterations, &mut dk)
        .map_err(|_| CryptoError::InvalidInput)?;
    Ok(dk)
}

fn sm4_cbc_decrypt(key: &[u8], iv: &[u8], enc_data: &[u8]) -> Result<Vec<u8>, CryptoError> {
    let mut buf = enc_data.to_vec();
    let cipher = Sm4CbcDec::new_from_slices(key, iv)
        .map_err(|_| CryptoError::InvalidInput)?;
    let plain = cipher
        .decrypt_padded::<Pkcs7>(&mut buf)
        .map_err(|_| CryptoError::InvalidInput)?;
    let plain_len = plain.len();
    Ok(buf[..plain_len].to_vec())
}

/// Decrypt GmSSL-encrypted PKCS#8 DER to plaintext `PrivateKeyInfo` DER.
pub fn decrypt_gmssl_encrypted_pkcs8_der(enc_der: &[u8], pass: &str) -> Result<Vec<u8>, CryptoError> {
    let params = parse_gmssl_encrypted_pkcs8_der(enc_der)?;
    let dk = pbkdf2_hmac_sm3(
        pass.as_bytes(),
        params.salt,
        params.iterations,
        params.dk_len,
    )?;
    let plain = sm4_cbc_decrypt(&dk, params.iv, params.enc_data)?;
    // Sanity-check cleartext is valid PKCS#8; `pk_from_pkcs8_der` parses again upstream.
    pkcs8::PrivateKeyInfoRef::try_from(plain.as_slice())
        .map_err(|e| CryptoError::Message(format!("pkcs8: {e}")))?;
    Ok(plain)
}