cmail_rpgp/crypto/
rsa.rs

1use std::ops::Deref;
2
3use digest::{const_oid::AssociatedOid, Digest};
4use md5::Md5;
5use num_bigint::traits::ModInverse;
6use num_bigint::BigUint;
7use rand::{CryptoRng, Rng};
8use ripemd::Ripemd160;
9use rsa::pkcs1v15::{Pkcs1v15Encrypt, Signature as RsaSignature, SigningKey, VerifyingKey};
10use rsa::{
11    traits::{PrivateKeyParts, PublicKeyParts},
12    RsaPrivateKey, RsaPublicKey,
13};
14use sha1_checked::Sha1; // not used for hashing, just as a source of the OID
15use sha2::{Sha224, Sha256, Sha384, Sha512};
16use sha3::{Sha3_256, Sha3_512};
17use signature::hazmat::{PrehashSigner, PrehashVerifier};
18use signature::SignatureEncoding;
19use zeroize::ZeroizeOnDrop;
20
21use crate::crypto::{hash::HashAlgorithm, Decryptor, KeyParams, Signer};
22use crate::errors::Result;
23use crate::types::{Mpi, PkeskBytes, PlainSecretParams, PublicParams};
24
25const MAX_KEY_SIZE: usize = 16384;
26
27/// Private Key for RSA.
28#[derive(derive_more::Debug, ZeroizeOnDrop)]
29pub struct PrivateKey(#[debug("..")] RsaPrivateKey);
30
31impl Deref for PrivateKey {
32    type Target = RsaPrivateKey;
33
34    fn deref(&self) -> &Self::Target {
35        &self.0
36    }
37}
38
39impl KeyParams for PrivateKey {
40    type KeyParams = ();
41
42    #[allow(clippy::unused_unit)]
43    fn key_params(&self) -> Self::KeyParams {
44        ()
45    }
46}
47
48impl Decryptor for PrivateKey {
49    type EncryptionFields<'a> = &'a Mpi;
50
51    /// RSA decryption using PKCS1v15 padding.
52    fn decrypt(&self, mpi: Self::EncryptionFields<'_>) -> Result<Vec<u8>> {
53        let m = self.0.decrypt(Pkcs1v15Encrypt, mpi.as_bytes())?;
54
55        Ok(m)
56    }
57}
58
59impl Signer for PrivateKey {
60    /// Sign using RSA, with PKCS1v15 padding.
61    fn sign(
62        &self,
63        hash: HashAlgorithm,
64        digest: &[u8],
65        pub_params: &PublicParams,
66    ) -> Result<Vec<Vec<u8>>> {
67        ensure!(
68            matches!(pub_params, PublicParams::RSA { .. }),
69            "invalid public params"
70        );
71
72        let sig = match hash {
73            HashAlgorithm::None => return Err(format_err!("none")),
74            HashAlgorithm::MD5 => sign_int::<Md5>(self.0.clone(), digest),
75            HashAlgorithm::RIPEMD160 => sign_int::<Ripemd160>(self.0.clone(), digest),
76            HashAlgorithm::SHA1 => sign_int::<Sha1>(self.0.clone(), digest),
77            HashAlgorithm::SHA2_224 => sign_int::<Sha224>(self.0.clone(), digest),
78            HashAlgorithm::SHA2_256 => sign_int::<Sha256>(self.0.clone(), digest),
79            HashAlgorithm::SHA2_384 => sign_int::<Sha384>(self.0.clone(), digest),
80            HashAlgorithm::SHA2_512 => sign_int::<Sha512>(self.0.clone(), digest),
81            HashAlgorithm::SHA3_256 => sign_int::<Sha3_256>(self.0.clone(), digest),
82            HashAlgorithm::SHA3_512 => sign_int::<Sha3_512>(self.0.clone(), digest),
83            HashAlgorithm::Private10 => unsupported_err!("Private10 should not be used"),
84            HashAlgorithm::Other(o) => unsupported_err!("Hash algorithm {} is unsupported", o),
85        }?;
86
87        Ok(vec![sig.to_vec()])
88    }
89}
90
91impl From<RsaPrivateKey> for PrivateKey {
92    fn from(key: RsaPrivateKey) -> Self {
93        Self(key)
94    }
95}
96
97/// RSA encryption using PKCS1v15 padding.
98pub fn encrypt<R: CryptoRng + Rng>(
99    mut rng: R,
100    n: &[u8],
101    e: &[u8],
102    plaintext: &[u8],
103) -> Result<PkeskBytes> {
104    let key = RsaPublicKey::new_with_max_size(
105        BigUint::from_bytes_be(n),
106        BigUint::from_bytes_be(e),
107        MAX_KEY_SIZE,
108    )?;
109    let data = key.encrypt(&mut rng, Pkcs1v15Encrypt, plaintext)?;
110
111    Ok(PkeskBytes::Rsa {
112        mpi: Mpi::from_slice(&data[..]),
113    })
114}
115
116/// Generate an RSA KeyPair.
117pub fn generate_key<R: Rng + CryptoRng>(
118    mut rng: R,
119    bit_size: usize,
120) -> Result<(PublicParams, PlainSecretParams)> {
121    let key = RsaPrivateKey::new(&mut rng, bit_size)?;
122
123    let p = &key.primes()[0];
124    let q = &key.primes()[1];
125    let u = p
126        .clone()
127        .mod_inverse(q)
128        .expect("invalid prime")
129        .to_biguint()
130        .expect("invalid prime");
131
132    Ok((
133        PublicParams::RSA {
134            n: key.n().into(),
135            e: key.e().into(),
136        },
137        PlainSecretParams::RSA {
138            d: key.d().into(),
139            p: p.into(),
140            q: q.into(),
141            u: u.into(),
142        },
143    ))
144}
145
146fn verify_int<D>(key: RsaPublicKey, hashed: &[u8], signature: &RsaSignature) -> Result<()>
147where
148    D: Digest + AssociatedOid,
149{
150    VerifyingKey::<D>::new(key)
151        .verify_prehash(hashed, signature)
152        .map_err(Into::into)
153}
154
155fn sign_int<D>(key: RsaPrivateKey, digest: &[u8]) -> Result<RsaSignature>
156where
157    D: Digest + AssociatedOid,
158{
159    SigningKey::<D>::new(key)
160        .sign_prehash(digest)
161        .map_err(Into::into)
162}
163
164/// Verify a RSA, PKCS1v15 padded signature.
165pub fn verify(
166    n: &[u8],
167    e: &[u8],
168    hash: HashAlgorithm,
169    hashed: &[u8],
170    signature: &[u8],
171) -> Result<()> {
172    let key = RsaPublicKey::new_with_max_size(
173        BigUint::from_bytes_be(n),
174        BigUint::from_bytes_be(e),
175        MAX_KEY_SIZE,
176    )?;
177
178    let signature = if signature.len() < key.size() {
179        // RSA short signatures are allowed by PGP, but not by the RSA crate.
180        // So we pad out the signature if we encounter a short one.
181        let mut signature_padded = vec![0u8; key.size()];
182        let diff = key.size() - signature.len();
183        signature_padded[diff..].copy_from_slice(signature);
184        RsaSignature::try_from(&signature_padded[..])?
185    } else {
186        RsaSignature::try_from(signature)?
187    };
188
189    match hash {
190        HashAlgorithm::None => Err(format_err!("none")),
191        HashAlgorithm::MD5 => verify_int::<Md5>(key, hashed, &signature),
192        HashAlgorithm::RIPEMD160 => verify_int::<Ripemd160>(key, hashed, &signature),
193        HashAlgorithm::SHA1 => verify_int::<Sha1>(key, hashed, &signature),
194        HashAlgorithm::SHA2_224 => verify_int::<Sha224>(key, hashed, &signature),
195        HashAlgorithm::SHA2_256 => verify_int::<Sha256>(key, hashed, &signature),
196        HashAlgorithm::SHA2_384 => verify_int::<Sha384>(key, hashed, &signature),
197        HashAlgorithm::SHA2_512 => verify_int::<Sha512>(key, hashed, &signature),
198        HashAlgorithm::SHA3_256 => verify_int::<Sha3_256>(key, hashed, &signature),
199        HashAlgorithm::SHA3_512 => verify_int::<Sha3_512>(key, hashed, &signature),
200        HashAlgorithm::Private10 => unsupported_err!("Private10 should not be used"),
201        HashAlgorithm::Other(o) => unsupported_err!("Hash algorithm {} is unsupported", o),
202    }
203    .map_err(Into::into)
204}