tasign 0.2.0

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

extern crate alloc;

use alloc::format;
use alloc::vec;
use alloc::vec::Vec;
use mbedtls::cipher::raw::{CipherId, CipherMode};
use mbedtls::cipher::{Cipher, Decryption, Traditional};
use mbedtls::hash::pbkdf2_hmac;
use mbedtls::hash::Type as MdType;
use pkcs8::PrivateKeyInfoRef;

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

fn map_cipher_err(e: mbedtls::Error) -> CryptoError {
    CryptoError::Message(alloc::format!("mbedtls cipher: {e:?}"))
}

fn pbkdf2_hmac_sm3(
    password: &[u8],
    salt: &[u8],
    iterations: usize,
    out_len: usize,
) -> Result<Vec<u8>, CryptoError> {
    if iterations > u32::MAX as usize {
        return Err(CryptoError::InvalidInput);
    }
    let mut out = vec![0u8; out_len];
    pbkdf2_hmac(MdType::SM3, password, salt, iterations as u32, &mut out)
        .map_err(map_cipher_err)?;
    Ok(out)
}

/// 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 cipher = Cipher::<Decryption, Traditional, _>::new(CipherId::SM4, CipherMode::CBC, 128)
        .map_err(map_cipher_err)?;
    let c = cipher
        .set_key_iv(&dk, params.iv)
        .map_err(map_cipher_err)?;
    let mut plain = vec![0u8; params.enc_data.len() + c.block_size()];
    let (len, _) = c
        .decrypt(params.enc_data, &mut plain)
        .map_err(map_cipher_err)?;
    plain.truncate(len);

    PrivateKeyInfoRef::try_from(plain.as_slice())
        .map_err(|e| CryptoError::Message(format!("pkcs8: {e}")))?;
    Ok(plain)
}