bitwarden_crypto/
rsa.rs

1use base64::{engine::general_purpose::STANDARD, Engine};
2use rsa::{
3    pkcs8::{EncodePrivateKey, EncodePublicKey},
4    Oaep, RsaPrivateKey, RsaPublicKey,
5};
6use sha1::Sha1;
7
8use crate::{
9    error::{Result, RsaError, UnsupportedOperation},
10    CryptoError, EncString, SymmetricCryptoKey,
11};
12
13/// RSA Key Pair
14///
15/// Consists of a public key and an encrypted private key.
16#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
17pub struct RsaKeyPair {
18    /// Base64 encoded DER representation of the public key
19    pub public: String,
20    /// Encrypted PKCS8 private key
21    pub private: EncString,
22}
23
24/// Generate a new RSA key pair of 2048 bits
25pub(crate) fn make_key_pair(key: &SymmetricCryptoKey) -> Result<RsaKeyPair> {
26    let mut rng = rand::thread_rng();
27    let bits = 2048;
28    let priv_key = RsaPrivateKey::new(&mut rng, bits).expect("failed to generate a key");
29    let pub_key = RsaPublicKey::from(&priv_key);
30
31    let spki = pub_key
32        .to_public_key_der()
33        .map_err(|_| RsaError::CreatePublicKey)?;
34
35    let b64 = STANDARD.encode(spki.as_bytes());
36    let pkcs = priv_key
37        .to_pkcs8_der()
38        .map_err(|_| RsaError::CreatePrivateKey)?;
39
40    let protected = match key {
41        SymmetricCryptoKey::Aes256CbcHmacKey(key) => {
42            EncString::encrypt_aes256_hmac(pkcs.as_bytes(), key)
43        }
44        SymmetricCryptoKey::XChaCha20Poly1305Key(_) => Err(CryptoError::OperationNotSupported(
45            UnsupportedOperation::EncryptionNotImplementedForKey,
46        )),
47        SymmetricCryptoKey::Aes256CbcKey(_) => Err(CryptoError::OperationNotSupported(
48            UnsupportedOperation::EncryptionNotImplementedForKey,
49        )),
50    }?;
51
52    Ok(RsaKeyPair {
53        public: b64,
54        private: protected,
55    })
56}
57
58/// Encrypt data using RSA-OAEP-SHA1 with a 2048 bit key
59pub(super) fn encrypt_rsa2048_oaep_sha1(public_key: &RsaPublicKey, data: &[u8]) -> Result<Vec<u8>> {
60    let mut rng = rand::thread_rng();
61
62    let padding = Oaep::new::<Sha1>();
63    public_key
64        .encrypt(&mut rng, padding, data)
65        .map_err(|e| CryptoError::RsaError(e.into()))
66}