Skip to main content

bitwarden_crypto/
rsa.rs

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