bitwarden_core/client/
encryption_settings.rs

1#[cfg(feature = "internal")]
2use std::sync::RwLock;
3
4#[cfg(feature = "internal")]
5use bitwarden_crypto::{
6    Aes256CbcHmacKey, AsymmetricCryptoKey, CoseKeyBytes, CoseSerializable, EncString,
7    KeyDecryptable, Pkcs8PrivateKeyBytes, SigningKey, UnsignedSharedKey, XChaCha20Poly1305Key,
8};
9#[cfg(any(feature = "internal", feature = "secrets"))]
10use bitwarden_crypto::{KeyStore, SymmetricCryptoKey};
11use bitwarden_error::bitwarden_error;
12#[cfg(feature = "internal")]
13use log::warn;
14use thiserror::Error;
15
16#[cfg(feature = "internal")]
17use crate::key_management::{AsymmetricKeyId, SecurityState, SignedSecurityState, SigningKeyId};
18#[cfg(any(feature = "internal", feature = "secrets"))]
19use crate::key_management::{KeyIds, SymmetricKeyId};
20#[cfg(any(feature = "secrets", feature = "internal"))]
21use crate::OrganizationId;
22use crate::{error::UserIdAlreadySetError, MissingPrivateKeyError};
23
24#[allow(missing_docs)]
25#[bitwarden_error(flat)]
26#[derive(Debug, Error)]
27pub enum EncryptionSettingsError {
28    #[error("Cryptography error, {0}")]
29    Crypto(#[from] bitwarden_crypto::CryptoError),
30
31    #[error("Invalid private key")]
32    InvalidPrivateKey,
33
34    #[error("Invalid signing key")]
35    InvalidSigningKey,
36
37    #[error("Invalid security state")]
38    InvalidSecurityState,
39
40    #[error(transparent)]
41    MissingPrivateKey(#[from] MissingPrivateKeyError),
42
43    #[error(transparent)]
44    UserIdAlreadySet(#[from] UserIdAlreadySetError),
45
46    #[error("Wrong Pin")]
47    WrongPin,
48}
49
50#[allow(clippy::large_enum_variant)]
51#[cfg(feature = "internal")]
52pub(crate) enum AccountEncryptionKeys {
53    V1 {
54        user_key: Aes256CbcHmacKey,
55        private_key: EncString,
56    },
57    V2 {
58        user_key: XChaCha20Poly1305Key,
59        private_key: EncString,
60        signing_key: EncString,
61        security_state: SignedSecurityState,
62    },
63}
64
65#[allow(missing_docs)]
66pub struct EncryptionSettings {}
67
68impl EncryptionSettings {
69    /// Initialize the encryption settings with the decrypted user key and the encrypted user
70    /// private key This should only be used when unlocking the vault via biometrics or when the
71    /// vault is set to lock: "never" Otherwise handling the decrypted user key is dangerous and
72    /// discouraged
73    #[cfg(feature = "internal")]
74    pub(crate) fn new_decrypted_key(
75        encryption_keys: AccountEncryptionKeys,
76        store: &KeyStore<KeyIds>,
77        security_state_rwlock: &RwLock<Option<SecurityState>>,
78    ) -> Result<(), EncryptionSettingsError> {
79        // This is an all-or-nothing check. The server cannot pretend a signing key or security
80        // state to be missing, because they are *always* present when the user key is an
81        // XChaCha20Poly1305Key. Thus, the server or network cannot lie about the presence of these,
82        // because otherwise the entire user account will fail to decrypt.
83        match encryption_keys {
84            AccountEncryptionKeys::V1 {
85                user_key,
86                private_key,
87            } => {
88                Self::init_v1(user_key, private_key, store)?;
89            }
90            AccountEncryptionKeys::V2 {
91                user_key,
92                private_key,
93                signing_key,
94                security_state,
95            } => {
96                Self::init_v2(
97                    user_key,
98                    private_key,
99                    signing_key,
100                    security_state,
101                    store,
102                    security_state_rwlock,
103                )?;
104            }
105        }
106
107        Ok(())
108    }
109
110    #[cfg(feature = "internal")]
111    fn init_v1(
112        user_key: Aes256CbcHmacKey,
113        private_key: EncString,
114        store: &KeyStore<KeyIds>,
115    ) -> Result<(), EncryptionSettingsError> {
116        let user_key = SymmetricCryptoKey::Aes256CbcHmacKey(user_key);
117
118        let private_key = {
119            let dec: Vec<u8> = private_key.decrypt_with_key(&user_key)?;
120
121            // FIXME: [PM-11690] - Temporarily ignore invalid private keys until we have a
122            // recovery process in place.
123            AsymmetricCryptoKey::from_der(&Pkcs8PrivateKeyBytes::from(dec))
124                .map_err(|_| {
125                    warn!("Invalid private key");
126                })
127                .ok()
128
129            // Some(
130            //     AsymmetricCryptoKey::from_der(&dec)
131            //         .map_err(|_| EncryptionSettingsError::InvalidPrivateKey)?,
132            // )
133        };
134
135        // FIXME: [PM-18098] When this is part of crypto we won't need to use deprecated methods
136        #[allow(deprecated)]
137        {
138            let mut ctx = store.context_mut();
139            ctx.set_symmetric_key(SymmetricKeyId::User, user_key)?;
140            if let Some(private_key) = private_key {
141                ctx.set_asymmetric_key(AsymmetricKeyId::UserPrivateKey, private_key)?;
142            }
143        }
144
145        Ok(())
146    }
147
148    #[cfg(feature = "internal")]
149    fn init_v2(
150        user_key: XChaCha20Poly1305Key,
151        private_key: EncString,
152        signing_key: EncString,
153        security_state: SignedSecurityState,
154        store: &KeyStore<KeyIds>,
155        sdk_security_state: &RwLock<Option<SecurityState>>,
156    ) -> Result<(), EncryptionSettingsError> {
157        use crate::key_management::SecurityState;
158
159        let user_key = SymmetricCryptoKey::XChaCha20Poly1305Key(user_key);
160
161        // For v2 users, we mandate the signing key and security state and the private key to be
162        // present and valid Everything MUST decrypt.
163        let signing_key: Vec<u8> = signing_key.decrypt_with_key(&user_key)?;
164        let signing_key = SigningKey::from_cose(&CoseKeyBytes::from(signing_key))
165            .map_err(|_| EncryptionSettingsError::InvalidSigningKey)?;
166        let private_key: Vec<u8> = private_key.decrypt_with_key(&user_key)?;
167        let private_key = AsymmetricCryptoKey::from_der(&Pkcs8PrivateKeyBytes::from(private_key))
168            .map_err(|_| EncryptionSettingsError::InvalidPrivateKey)?;
169
170        let security_state: SecurityState = security_state
171            .verify_and_unwrap(&signing_key.to_verifying_key())
172            .map_err(|_| EncryptionSettingsError::InvalidSecurityState)?;
173        *sdk_security_state.write().expect("RwLock not poisoned") = Some(security_state);
174
175        #[allow(deprecated)]
176        {
177            let mut ctx = store.context_mut();
178            ctx.set_symmetric_key(SymmetricKeyId::User, user_key)?;
179            ctx.set_asymmetric_key(AsymmetricKeyId::UserPrivateKey, private_key)?;
180            ctx.set_signing_key(SigningKeyId::UserSigningKey, signing_key)?;
181        }
182
183        Ok(())
184    }
185
186    /// Initialize the encryption settings with only a single decrypted organization key.
187    /// This is used only for logging in Secrets Manager with an access token
188    #[cfg(feature = "secrets")]
189    pub(crate) fn new_single_org_key(
190        organization_id: OrganizationId,
191        key: SymmetricCryptoKey,
192        store: &KeyStore<KeyIds>,
193    ) {
194        // FIXME: [PM-18098] When this is part of crypto we won't need to use deprecated methods
195        #[allow(deprecated)]
196        store
197            .context_mut()
198            .set_symmetric_key(SymmetricKeyId::Organization(organization_id), key)
199            .expect("Mutable context");
200    }
201
202    #[cfg(feature = "internal")]
203    pub(crate) fn set_org_keys(
204        org_enc_keys: Vec<(OrganizationId, UnsignedSharedKey)>,
205        store: &KeyStore<KeyIds>,
206    ) -> Result<(), EncryptionSettingsError> {
207        use crate::key_management::AsymmetricKeyId;
208
209        let mut ctx = store.context_mut();
210
211        // FIXME: [PM-11690] - Early abort to handle private key being corrupt
212        if org_enc_keys.is_empty() {
213            return Ok(());
214        }
215
216        if !ctx.has_asymmetric_key(AsymmetricKeyId::UserPrivateKey) {
217            return Err(MissingPrivateKeyError.into());
218        }
219
220        // Make sure we only keep the keys given in the arguments and not any of the previous
221        // ones, which might be from organizations that the user is no longer a part of anymore
222        ctx.retain_symmetric_keys(|key_ref| !matches!(key_ref, SymmetricKeyId::Organization(_)));
223
224        // Decrypt the org keys with the private key
225        for (org_id, org_enc_key) in org_enc_keys {
226            ctx.decapsulate_key_unsigned(
227                AsymmetricKeyId::UserPrivateKey,
228                SymmetricKeyId::Organization(org_id),
229                &org_enc_key,
230            )?;
231        }
232
233        Ok(())
234    }
235}