ts-crypto 0.4.0

Cryptography abstraction for my projects
Documentation
//! RSA keys

use rsa::{BoxedUint, RsaPrivateKey, RsaPublicKey, traits::PublicKeyParts};
use sha2::{Digest as _, Sha256};

pub use rsa::{Pkcs1v15Sign as Pkcs1v15, Pss};

use crate::Digest;

/// Trait providing a unified interface to construct RSA signature schemes.
pub trait SignatureScheme: rsa::traits::SignatureScheme {
    /// Create a new instance of the signature scheme for the given digest algorithm.
    fn new<D: Digest>() -> Self;
}
impl SignatureScheme for Pkcs1v15 {
    fn new<D: Digest>() -> Self {
        Self::new::<D>()
    }
}
impl SignatureScheme for Pss {
    fn new<D: Digest>() -> Self {
        Self::new::<D>()
    }
}

/// An RSA verifying key.
pub struct RsaVerifyingKey(pub RsaPublicKey);

/// An RSA signing key.
pub struct RsaSigningKey(pub RsaPrivateKey);

impl RsaVerifyingKey {
    /// Get the modulus bytes.
    pub fn modulus(&self) -> Vec<u8> {
        self.0.n_bytes().to_vec()
    }

    /// Get the exponent bytes.
    pub fn exponent(&self) -> Vec<u8> {
        self.0.e_bytes().to_vec()
    }

    /// Create an RSA key from its parameters.
    pub fn from_parameters(modulus: &[u8], exponent: &[u8]) -> Option<Self> {
        let modulus = BoxedUint::from_be_slice_vartime(modulus);
        let exponent = BoxedUint::from_be_slice_vartime(exponent);
        let key = RsaPublicKey::new(modulus, exponent).ok()?;
        Some(Self(key))
    }

    /// Returns if this key verifies the signature to match the message.
    pub fn verifies<S: SignatureScheme, D: Digest>(
        &self,
        signature: &[u8],
        message: &[u8],
    ) -> bool {
        let hash = D::digest(message);
        let scheme = S::new::<D>();
        self.0.verify(scheme, &hash, signature).is_ok()
    }

    /// Returns the ID for this key.
    pub fn key_id(&self) -> Vec<u8> {
        let modulus = self.modulus();
        let exponent = self.exponent();
        let digest = Sha256::new()
            .chain_update(modulus)
            .chain_update(exponent)
            .finalize();
        digest.to_vec()
    }
}

impl RsaSigningKey {
    /// Sign the message using this key, the digest algorithm, and the signature scheme provided.
    ///
    /// ## Panics
    /// * If the provided signature scheme or digest would produce and invalid output.
    pub fn sign<S: SignatureScheme, D: Digest>(&self, message: &[u8]) -> Vec<u8> {
        let digest = D::digest(message);
        let scheme = S::new::<D>();
        self.0
            .sign_with_rng(&mut rand::rng(), scheme, digest.as_slice())
            .expect("RSA signing with valid parameters should not fail")
    }

    /// Get the verifying key for this signing key.
    pub fn verifying_key(&self) -> RsaVerifyingKey {
        RsaVerifyingKey(self.0.to_public_key())
    }
}