ts_crypto/
rsa.rs

1//! RSA keys
2
3use rsa::{BoxedUint, RsaPrivateKey, RsaPublicKey, traits::PublicKeyParts};
4use sha2::{Digest as _, Sha256};
5
6pub use rsa::{Pkcs1v15Sign as Pkcs1v15, Pss};
7
8use crate::Digest;
9
10/// Trait providing a unified interface to construct RSA signature schemes.
11pub trait SignatureScheme: rsa::traits::SignatureScheme {
12    /// Create a new instance of the signature scheme for the given digest algorithm.
13    fn new<D: Digest>() -> Self;
14}
15impl SignatureScheme for Pkcs1v15 {
16    fn new<D: Digest>() -> Self {
17        Self::new::<D>()
18    }
19}
20impl SignatureScheme for Pss {
21    fn new<D: Digest>() -> Self {
22        Self::new::<D>()
23    }
24}
25
26/// An RSA verifying key.
27pub struct RsaVerifyingKey(pub RsaPublicKey);
28
29/// An RSA signing key.
30pub struct RsaSigningKey(pub RsaPrivateKey);
31
32impl RsaVerifyingKey {
33    /// Get the modulus bytes.
34    pub fn modulus(&self) -> Vec<u8> {
35        self.0.n_bytes().to_vec()
36    }
37
38    /// Get the exponent bytes.
39    pub fn exponent(&self) -> Vec<u8> {
40        self.0.e_bytes().to_vec()
41    }
42
43    /// Create an RSA key from its parameters.
44    pub fn from_parameters(modulus: &[u8], exponent: &[u8]) -> Option<Self> {
45        let modulus = BoxedUint::from_be_slice_vartime(modulus);
46        let exponent = BoxedUint::from_be_slice_vartime(exponent);
47        let key = RsaPublicKey::new(modulus, exponent).ok()?;
48        Some(Self(key))
49    }
50
51    /// Returns if this key verifies the signature to match the message.
52    pub fn verifies<S: SignatureScheme, D: Digest>(
53        &self,
54        signature: &[u8],
55        message: &[u8],
56    ) -> bool {
57        let hash = D::digest(message);
58        let scheme = S::new::<D>();
59        self.0.verify(scheme, &hash, signature).is_ok()
60    }
61
62    /// Returns the ID for this key.
63    pub fn key_id(&self) -> Vec<u8> {
64        let modulus = self.modulus();
65        let exponent = self.exponent();
66        let digest = Sha256::new()
67            .chain_update(modulus)
68            .chain_update(exponent)
69            .finalize();
70        digest.to_vec()
71    }
72}
73
74impl RsaSigningKey {
75    /// Sign the message using this key, the digest algorithm, and the signature scheme provided.
76    ///
77    /// ## Panics
78    /// * If the provided signature scheme or digest would produce and invalid output.
79    pub fn sign<S: SignatureScheme, D: Digest>(&self, message: &[u8]) -> Vec<u8> {
80        let digest = D::digest(message);
81        let scheme = S::new::<D>();
82        self.0
83            .sign_with_rng(&mut rand::rng(), scheme, digest.as_slice())
84            .expect("RSA signing with valid parameters should not fail")
85    }
86
87    /// Get the verifying key for this signing key.
88    pub fn verifying_key(&self) -> RsaVerifyingKey {
89        RsaVerifyingKey(self.0.to_public_key())
90    }
91}