tasign 0.2.0

TA ELF signing utilities with CMS/PKCS#7 support
//! RustCrypto-backed `Pk` with **mbedtls-compatible** method signatures.
//!
//! `PkInner` enum dispatch mirrors mbedtls `Pk` / `PkType` (migration plan D4) — not
//! the idiomatic RustCrypto `Signer` trait design. New algorithms require enum variants
//! and match arms rather than trait extension.
//!
//! ## SM2 signing modes
//!
//! - [`Pk::sm2_sign`] / [`Pk::sm2_verify`]: message mode (ZA‖M preprocessing).
//! - [`Pk::sign`] / [`Pk::verify`] with `MdType::SM3`: digest mode (pre-hashed *e*).
//!
//! These produce **non-interchangeable** signatures. Use message mode for CMS detached;
//! digest mode for GmSSL attached CMS.

extern crate alloc;

#[cfg(feature = "std")]
use sm2::dsa::signature::hazmat::RandomizedPrehashSigner;
#[cfg(feature = "std")]
use sm2::dsa::signature::RandomizedSigner;
#[cfg(feature = "std")]
use sm2::dsa::SigningKey;
use sm2::dsa::VerifyingKey;

use super::hash::MdType;
#[cfg(feature = "std")]
use super::rng::{CryptoRng, DynCryptoRng};
use super::rsa_ecdsa;
use super::sm2_raw;
use crate::crypto::CryptoError;

/// Maximum ECDSA/SM2 signature buffer size (aligned with mbedtls `ECDSA_MAX_LEN`).
pub const ECDSA_MAX_LEN: usize = 256;

/// Public key algorithm type (subset used by tasign SM2 paths).
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PkType {
    SM2,
    Rsa,
    Eckey,
    Ecdsa,
}

enum PkInner {
    #[cfg(feature = "std")]
    Sm2Sign {
        key: SigningKey,
        scalar: [u8; 32],
    },
    Sm2Verify(VerifyingKey),
    #[cfg(feature = "std")]
    RsaSign(rsa::RsaPrivateKey),
    RsaVerify(rsa::RsaPublicKey),
    #[cfg(feature = "std")]
    EcdsaSign(p256::ecdsa::SigningKey),
    EcdsaVerify(p256::ecdsa::VerifyingKey),
}

/// Signing or verifying key (RustCrypto backend).
pub struct Pk {
    inner: PkInner,
}

impl Pk {
    pub fn pk_type(&self) -> PkType {
        match &self.inner {
            #[cfg(feature = "std")]
            PkInner::Sm2Sign { .. } => PkType::SM2,
            PkInner::Sm2Verify(_) => PkType::SM2,
            #[cfg(feature = "std")]
            PkInner::RsaSign(_) => PkType::Rsa,
            PkInner::RsaVerify(_) => PkType::Rsa,
            #[cfg(feature = "std")]
            PkInner::EcdsaSign(_) => PkType::Ecdsa,
            PkInner::EcdsaVerify(_) => PkType::Ecdsa,
        }
    }

    /// Key size in bits. SM2 / P-256 ECDSA paths return 256; update when `p384` is enabled.
    pub fn len(&self) -> usize {
        match &self.inner {
            #[cfg(feature = "std")]
            PkInner::Sm2Sign { .. } | PkInner::EcdsaSign(_) => 256,
            PkInner::Sm2Verify(_) | PkInner::EcdsaVerify(_) => 256,
            #[cfg(feature = "std")]
            PkInner::RsaSign(k) => rsa_ecdsa::rsa_key_bits(k),
            PkInner::RsaVerify(k) => rsa_ecdsa::rsa_pub_key_bits(k),
        }
    }

    /// SM2 sign on a message (ZA||M preprocessing).
    #[cfg(feature = "std")]
    pub fn sm2_sign(
        &mut self,
        md: MdType,
        msg: &[u8],
        sig: &mut [u8],
        rng: &mut dyn CryptoRng,
    ) -> Result<usize, CryptoError> {
        if md != MdType::SM3 {
            return Err(CryptoError::UnsupportedAlgorithm);
        }
        let signing_key = match &self.inner {
            PkInner::Sm2Sign { key, .. } => key,
            _ => return Err(CryptoError::InvalidKey),
        };
        let mut drng = DynCryptoRng(rng);
        let sig_obj = signing_key
            .try_sign_with_rng(&mut drng, msg)
            .map_err(|_| CryptoError::InternalError)?;
        let der_bytes = sig_obj.to_der();
        let der = der_bytes.as_bytes();
        if der.len() > sig.len() {
            return Err(CryptoError::InvalidLength);
        }
        sig[..der.len()].copy_from_slice(der);
        Ok(der.len())
    }

    /// SM2 verify on a message (ZA||M preprocessing).
    pub fn sm2_verify(&mut self, md: MdType, msg: &[u8], sig: &[u8]) -> Result<(), CryptoError> {
        if md != MdType::SM3 {
            return Err(CryptoError::UnsupportedAlgorithm);
        }
        let verifying_key = match &self.inner {
            PkInner::Sm2Verify(k) => k,
            _ => return Err(CryptoError::InvalidKey),
        };
        sm2_raw::verify_message_with_key(verifying_key, msg, sig)
    }

    /// Sign a pre-computed 32-byte digest (CMS RSA/ECDSA detached, or SM2 digest mode).
    #[cfg(feature = "std")]
    pub fn sign(
        &mut self,
        md: MdType,
        digest: &[u8],
        sig: &mut [u8],
        rng: &mut dyn CryptoRng,
    ) -> Result<usize, CryptoError> {
        let digest: [u8; 32] = digest
            .try_into()
            .map_err(|_| CryptoError::InvalidLength)?;
        let out = match &self.inner {
            PkInner::Sm2Sign { key, .. } => {
                if md != MdType::SM3 {
                    return Err(CryptoError::UnsupportedAlgorithm);
                }
                let mut drng = DynCryptoRng(rng);
                let sig_obj = key
                    .sign_prehash_with_rng(&mut drng, &digest)
                    .map_err(|_| CryptoError::InternalError)?;
                sig_obj.to_der().as_bytes().to_vec()
            }
            PkInner::RsaSign(key) => {
                if md != MdType::Sha256 {
                    return Err(CryptoError::UnsupportedAlgorithm);
                }
                rsa_ecdsa::rsa_sign_digest(key, &digest, rng)?
            }
            PkInner::EcdsaSign(key) => {
                if md != MdType::Sha256 {
                    return Err(CryptoError::UnsupportedAlgorithm);
                }
                rsa_ecdsa::ecdsa_sign_digest(key, &digest, rng)?
            }
            _ => return Err(CryptoError::InvalidKey),
        };
        if out.len() > sig.len() {
            return Err(CryptoError::InvalidLength);
        }
        sig[..out.len()].copy_from_slice(&out);
        Ok(out.len())
    }

    /// Verify a pre-computed 32-byte digest.
    pub fn verify(&mut self, md: MdType, digest: &[u8], sig: &[u8]) -> Result<(), CryptoError> {
        let digest: [u8; 32] = digest
            .try_into()
            .map_err(|_| CryptoError::InvalidLength)?;
        match &self.inner {
            PkInner::Sm2Verify(k) => {
                if md != MdType::SM3 {
                    return Err(CryptoError::UnsupportedAlgorithm);
                }
                sm2_raw::verify_digest_with_key(k, &digest, sig)
            }
            PkInner::RsaVerify(k) => {
                if md != MdType::Sha256 {
                    return Err(CryptoError::UnsupportedAlgorithm);
                }
                rsa_ecdsa::rsa_verify_digest(k, &digest, sig)
            }
            PkInner::EcdsaVerify(k) => {
                if md != MdType::Sha256 {
                    return Err(CryptoError::UnsupportedAlgorithm);
                }
                rsa_ecdsa::ecdsa_verify_digest(k, &digest, sig)
            }
            #[cfg(feature = "std")]
            _ => Err(CryptoError::InvalidKey),
        }
    }
}

impl Pk {
    #[cfg(feature = "std")]
    pub(crate) fn from_signing_scalar(scalar: [u8; 32]) -> Result<Self, CryptoError> {
        let key = sm2_raw::signing_key_from_scalar(&scalar)?;
        Ok(Self {
            inner: PkInner::Sm2Sign { key, scalar },
        })
    }

    pub(crate) fn from_verifying_key(key: VerifyingKey) -> Self {
        Self {
            inner: PkInner::Sm2Verify(key),
        }
    }

    #[cfg(feature = "std")]
    pub(crate) fn from_rsa_sign(key: rsa::RsaPrivateKey) -> Self {
        Self {
            inner: PkInner::RsaSign(key),
        }
    }

    pub(crate) fn from_rsa_verify(key: rsa::RsaPublicKey) -> Self {
        Self {
            inner: PkInner::RsaVerify(key),
        }
    }

    #[cfg(feature = "std")]
    pub(crate) fn from_ecdsa_sign(key: p256::ecdsa::SigningKey) -> Self {
        Self {
            inner: PkInner::EcdsaSign(key),
        }
    }

    pub(crate) fn from_ecdsa_verify(key: p256::ecdsa::VerifyingKey) -> Self {
        Self {
            inner: PkInner::EcdsaVerify(key),
        }
    }

    #[cfg(feature = "std")]
    #[allow(dead_code)]
    pub(crate) fn secret_scalar32(&self) -> Result<[u8; 32], CryptoError> {
        match &self.inner {
            PkInner::Sm2Sign { scalar, .. } => Ok(*scalar),
            _ => Err(CryptoError::InvalidKey),
        }
    }
}