diff --git a/crates/bitwarden-core/src/auth/auth_request.rs b/crates/bitwarden-core/src/auth/auth_request.rs index 1d6ce18..792877e 100644 --- a/crates/bitwarden-core/src/auth/auth_request.rs +++ b/crates/bitwarden-core/src/auth/auth_request.rs @@ -8,7 +8,7 @@ use bitwarden_crypto::{EncString, KeyDecryptable, SymmetricCryptoKey}; #[cfg(feature = "internal")] use crate::client::encryption_settings::EncryptionSettingsError; -use crate::{error::Error, Client}; +use crate::{error::Error, key_management::SymmetricKeyRef, Client}; #[cfg_attr(feature = "uniffi", derive(uniffi::Record))] pub struct AuthRequestResponse { @@ -83,8 +83,11 @@ pub(crate) fn approve_auth_request( ) -> Result { let public_key = AsymmetricPublicCryptoKey::from_der(&STANDARD.decode(public_key)?)?; - let enc = client.internal.get_encryption_settings()?; - let key = enc.get_key(&None)?; + let crypto_service = client.internal.get_crypto_service(); + let ctx = crypto_service.context(); + + #[allow(deprecated)] + let key = ctx.dangerous_get_symmetric_key(SymmetricKeyRef::User)?; Ok(AsymmetricEncString::encrypt_rsa2048_oaep_sha1( &key.to_vec(), @@ -235,21 +238,22 @@ mod tests { // We can validate that the vault is unlocked correctly by confirming the user key is the // same - assert_eq!( - existing_device - .internal - .get_encryption_settings() - .unwrap() - .get_key(&None) + let existing_key_b64 = { + let ctx = existing_device.internal.get_crypto_service().context(); + #[allow(deprecated)] + ctx.dangerous_get_symmetric_key(SymmetricKeyRef::User) .unwrap() - .to_base64(), - new_device - .internal - .get_encryption_settings() - .unwrap() - .get_key(&None) + .to_base64() + }; + + let new_key_b64 = { + let ctx = new_device.internal.get_crypto_service().context(); + #[allow(deprecated)] + ctx.dangerous_get_symmetric_key(SymmetricKeyRef::User) .unwrap() .to_base64() - ); + }; + + assert_eq!(existing_key_b64, new_key_b64,); } } diff --git a/crates/bitwarden-core/src/auth/client_auth.rs b/crates/bitwarden-core/src/auth/client_auth.rs index 8298685..85e6c94 100644 --- a/crates/bitwarden-core/src/auth/client_auth.rs +++ b/crates/bitwarden-core/src/auth/client_auth.rs @@ -162,9 +162,11 @@ impl<'a> ClientAuth<'a> { #[cfg(feature = "internal")] fn trust_device(client: &Client) -> Result { - let enc = client.internal.get_encryption_settings()?; + use crate::key_management::SymmetricKeyRef; - let user_key = enc.get_key(&None)?; + let ctx = client.internal.get_crypto_service().context(); + #[allow(deprecated)] + let user_key = ctx.dangerous_get_symmetric_key(SymmetricKeyRef::User)?; Ok(DeviceKey::trust_device(user_key)?) } diff --git a/crates/bitwarden-core/src/auth/password/validate.rs b/crates/bitwarden-core/src/auth/password/validate.rs index b5f9ccf..83156cd 100644 --- a/crates/bitwarden-core/src/auth/password/validate.rs +++ b/crates/bitwarden-core/src/auth/password/validate.rs @@ -44,6 +44,8 @@ pub(crate) fn validate_password_user_key( password: String, encrypted_user_key: String, ) -> Result { + use crate::key_management::SymmetricKeyRef; + let login_method = client .internal .get_login_method() @@ -59,9 +61,10 @@ pub(crate) fn validate_password_user_key( .decrypt_user_key(encrypted_user_key.parse()?) .map_err(|_| "wrong password")?; - let enc = client.internal.get_encryption_settings()?; - - let existing_key = enc.get_key(&None)?; + let crypto_service = client.internal.get_crypto_service(); + let ctx = crypto_service.context(); + #[allow(deprecated)] + let existing_key = ctx.dangerous_get_symmetric_key(SymmetricKeyRef::User)?; if user_key.to_vec() != existing_key.to_vec() { return Err("wrong user key".into()); diff --git a/crates/bitwarden-core/src/auth/pin.rs b/crates/bitwarden-core/src/auth/pin.rs index ee093bf..89160f7 100644 --- a/crates/bitwarden-core/src/auth/pin.rs +++ b/crates/bitwarden-core/src/auth/pin.rs @@ -3,6 +3,7 @@ use bitwarden_crypto::{EncString, PinKey}; use crate::{ client::{LoginMethod, UserLoginMethod}, error::{Error, Result}, + key_management::SymmetricKeyRef, Client, }; @@ -24,8 +25,9 @@ pub(crate) fn validate_pin( match login_method { UserLoginMethod::Username { email, kdf, .. } | UserLoginMethod::ApiKey { email, kdf, .. } => { - let enc = client.internal.get_encryption_settings()?; - let user_key = enc.get_key(&None)?; + let ctx = client.internal.get_crypto_service().context(); + #[allow(deprecated)] + let user_key = ctx.dangerous_get_symmetric_key(SymmetricKeyRef::User)?; let pin_key = PinKey::derive(pin.as_bytes(), email.as_bytes(), kdf)?; diff --git a/crates/bitwarden-core/src/auth/renew.rs b/crates/bitwarden-core/src/auth/renew.rs index b35d71d..cf37f21 100644 --- a/crates/bitwarden-core/src/auth/renew.rs +++ b/crates/bitwarden-core/src/auth/renew.rs @@ -4,6 +4,7 @@ use chrono::Utc; use crate::{ auth::api::request::AccessTokenRequest, client::ServiceAccountLoginMethod, + key_management::SymmetricKeyRef, secrets_manager::state::{self, ClientState}, }; use crate::{ @@ -70,10 +71,14 @@ pub(crate) async fn renew_token(client: &InternalClient) -> Result<()> { .send(&config) .await?; - if let (IdentityTokenResponse::Payload(r), Some(state_file), Ok(enc_settings)) = - (&result, state_file, client.get_encryption_settings()) + if let (IdentityTokenResponse::Payload(r), Some(state_file)) = + (&result, state_file) { - if let Ok(enc_key) = enc_settings.get_key(&None) { + let ctx = client.get_crypto_service().context(); + + #[allow(deprecated)] + if let Ok(enc_key) = ctx.dangerous_get_symmetric_key(SymmetricKeyRef::User) + { let state = ClientState::new(r.access_token.clone(), enc_key.to_base64()); _ = state::set(state_file, access_token, state); diff --git a/crates/bitwarden-core/src/client/client.rs b/crates/bitwarden-core/src/client/client.rs index b9bf4c5..d82dd85 100644 --- a/crates/bitwarden-core/src/client/client.rs +++ b/crates/bitwarden-core/src/client/client.rs @@ -1,5 +1,6 @@ use std::sync::{Arc, RwLock}; +use bitwarden_crypto::service::CryptoService; use reqwest::header::{self, HeaderValue}; use super::internal::InternalClient; @@ -78,7 +79,7 @@ impl Client { device_type: settings.device_type, })), external_client, - encryption_settings: RwLock::new(None), + crypto_service: CryptoService::new(), }, } } diff --git a/crates/bitwarden-core/src/client/encryption_settings.rs b/crates/bitwarden-core/src/client/encryption_settings.rs index 9d95490..10ea985 100644 --- a/crates/bitwarden-core/src/client/encryption_settings.rs +++ b/crates/bitwarden-core/src/client/encryption_settings.rs @@ -1,14 +1,15 @@ -use std::collections::HashMap; - -use bitwarden_crypto::{AsymmetricCryptoKey, CryptoError, KeyContainer, SymmetricCryptoKey}; +use bitwarden_crypto::{service::CryptoService, AsymmetricCryptoKey, SymmetricCryptoKey}; #[cfg(feature = "internal")] -use bitwarden_crypto::{AsymmetricEncString, EncString, MasterKey}; +use bitwarden_crypto::{AsymmetricEncString, EncString}; use thiserror::Error; use uuid::Uuid; #[cfg(feature = "internal")] use crate::error::Result; -use crate::VaultLocked; +use crate::{ + key_management::{AsymmetricKeyRef, SymmetricKeyRef}, + VaultLocked, +}; #[derive(Debug, Error)] pub enum EncryptionSettingsError { @@ -28,32 +29,9 @@ pub enum EncryptionSettingsError { MissingPrivateKey, } -#[derive(Clone)] -pub struct EncryptionSettings { - user_key: SymmetricCryptoKey, - pub(crate) private_key: Option, - org_keys: HashMap, -} - -impl std::fmt::Debug for EncryptionSettings { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("EncryptionSettings").finish() - } -} +pub struct EncryptionSettings {} impl EncryptionSettings { - /// Initialize the encryption settings with the master key and the encrypted user keys - #[cfg(feature = "internal")] - pub(crate) fn new( - master_key: MasterKey, - user_key: EncString, - private_key: EncString, - ) -> Result { - // Decrypt the user key - let user_key = master_key.decrypt_user_key(user_key)?; - Self::new_decrypted_key(user_key, private_key) - } - /// Initialize the encryption settings with the decrypted user key and the encrypted user /// private key This should only be used when unlocking the vault via biometrics or when the /// vault is set to lock: "never" Otherwise handling the decrypted user key is dangerous and @@ -62,7 +40,8 @@ impl EncryptionSettings { pub(crate) fn new_decrypted_key( user_key: SymmetricCryptoKey, private_key: EncString, - ) -> Result { + crypto_service: &CryptoService, + ) -> Result<(), EncryptionSettingsError> { use bitwarden_crypto::KeyDecryptable; use log::warn; @@ -83,76 +62,61 @@ impl EncryptionSettings { // ) }; - Ok(EncryptionSettings { - user_key, - private_key, - org_keys: HashMap::new(), - }) + #[allow(deprecated)] + { + let mut ctx = crypto_service.context_mut(); + ctx.set_symmetric_key(SymmetricKeyRef::User, user_key)?; + if let Some(private_key) = private_key { + ctx.set_asymmetric_key(AsymmetricKeyRef::UserPrivateKey, private_key)?; + } + } + + Ok(()) } /// Initialize the encryption settings with only a single decrypted key. /// This is used only for logging in Secrets Manager with an access token #[cfg(feature = "secrets")] - pub(crate) fn new_single_key(key: SymmetricCryptoKey) -> Self { - EncryptionSettings { - user_key: key, - private_key: None, - org_keys: HashMap::new(), - } + pub(crate) fn new_single_key( + key: SymmetricCryptoKey, + crypto_service: &CryptoService, + ) { + #[allow(deprecated)] + crypto_service + .context_mut() + .set_symmetric_key(SymmetricKeyRef::User, key) + .expect("Mutable context"); } #[cfg(feature = "internal")] pub(crate) fn set_org_keys( - &mut self, org_enc_keys: Vec<(Uuid, AsymmetricEncString)>, - ) -> Result<&Self, EncryptionSettingsError> { - use bitwarden_crypto::KeyDecryptable; + crypto_service: &CryptoService, + ) -> Result<(), EncryptionSettingsError> { + let mut ctx = crypto_service.context_mut(); + + if !ctx.has_asymmetric_key(AsymmetricKeyRef::UserPrivateKey) { + return Err(VaultLocked.into()); + } // Make sure we only keep the keys given in the arguments and not any of the previous // ones, which might be from organizations that the user is no longer a part of anymore - self.org_keys.clear(); + ctx.retain_symmetric_keys(|key_ref| !matches!(key_ref, SymmetricKeyRef::Organization(_))); // FIXME: [PM-11690] - Early abort to handle private key being corrupt if org_enc_keys.is_empty() { - return Ok(self); + return Ok(()); } - let private_key = self - .private_key - .as_ref() - .ok_or(EncryptionSettingsError::MissingPrivateKey)?; - // Decrypt the org keys with the private key for (org_id, org_enc_key) in org_enc_keys { - let mut dec: Vec = org_enc_key.decrypt_with_key(private_key)?; - - let org_key = SymmetricCryptoKey::try_from(dec.as_mut_slice())?; - - self.org_keys.insert(org_id, org_key); + ctx.decrypt_symmetric_key_with_asymmetric_key( + AsymmetricKeyRef::UserPrivateKey, + SymmetricKeyRef::Organization(org_id), + &org_enc_key, + )?; } - Ok(self) - } - - pub fn get_key(&self, org_id: &Option) -> Result<&SymmetricCryptoKey, CryptoError> { - // If we don't have a private key set (to decode multiple org keys), we just use the main - // user key - if self.private_key.is_none() { - return Ok(&self.user_key); - } - - match org_id { - Some(org_id) => self - .org_keys - .get(org_id) - .ok_or(CryptoError::MissingKey(*org_id)), - None => Ok(&self.user_key), - } - } -} - -impl KeyContainer for EncryptionSettings { - fn get_key(&self, org_id: &Option) -> Result<&SymmetricCryptoKey, CryptoError> { - EncryptionSettings::get_key(self, org_id) + Ok(()) } } diff --git a/crates/bitwarden-core/src/client/internal.rs b/crates/bitwarden-core/src/client/internal.rs index d64ac75..382c4e9 100644 --- a/crates/bitwarden-core/src/client/internal.rs +++ b/crates/bitwarden-core/src/client/internal.rs @@ -1,5 +1,6 @@ use std::sync::{Arc, RwLock}; +use bitwarden_crypto::service::CryptoService; #[cfg(any(feature = "internal", feature = "secrets"))] use bitwarden_crypto::SymmetricCryptoKey; #[cfg(feature = "internal")] @@ -12,7 +13,8 @@ use super::login_method::ServiceAccountLoginMethod; use crate::{ auth::renew::renew_token, client::{encryption_settings::EncryptionSettings, login_method::LoginMethod}, - error::{Result, VaultLocked}, + error::Result, + key_management::{AsymmetricKeyRef, SymmetricKeyRef}, DeviceType, }; #[cfg(feature = "internal")] @@ -58,7 +60,7 @@ pub struct InternalClient { #[allow(unused)] pub(crate) external_client: reqwest::Client, - pub(super) encryption_settings: RwLock>>, + pub(super) crypto_service: CryptoService, } impl InternalClient { @@ -167,12 +169,8 @@ impl InternalClient { &self.external_client } - pub fn get_encryption_settings(&self) -> Result, VaultLocked> { - self.encryption_settings - .read() - .expect("RwLock is not poisoned") - .clone() - .ok_or(VaultLocked) + pub fn get_crypto_service(&self) -> &CryptoService { + &self.crypto_service } #[cfg(feature = "internal")] @@ -182,14 +180,8 @@ impl InternalClient { user_key: EncString, private_key: EncString, ) -> Result<(), EncryptionSettingsError> { - *self - .encryption_settings - .write() - .expect("RwLock is not poisoned") = Some(Arc::new(EncryptionSettings::new( - master_key, - user_key, - private_key, - )?)); + let user_key = master_key.decrypt_user_key(user_key)?; + EncryptionSettings::new_decrypted_key(user_key, private_key, &self.crypto_service)?; Ok(()) } @@ -200,12 +192,7 @@ impl InternalClient { user_key: SymmetricCryptoKey, private_key: EncString, ) -> Result<(), EncryptionSettingsError> { - *self - .encryption_settings - .write() - .expect("RwLock is not poisoned") = Some(Arc::new( - EncryptionSettings::new_decrypted_key(user_key, private_key)?, - )); + EncryptionSettings::new_decrypted_key(user_key, private_key, &self.crypto_service)?; Ok(()) } @@ -223,30 +210,15 @@ impl InternalClient { #[cfg(feature = "secrets")] pub(crate) fn initialize_crypto_single_key(&self, key: SymmetricCryptoKey) { - *self - .encryption_settings - .write() - .expect("RwLock is not poisoned") = - Some(Arc::new(EncryptionSettings::new_single_key(key))); + EncryptionSettings::new_single_key(key, &self.crypto_service); } #[cfg(feature = "internal")] pub fn initialize_org_crypto( &self, org_keys: Vec<(Uuid, AsymmetricEncString)>, - ) -> Result, EncryptionSettingsError> { - let mut guard = self - .encryption_settings - .write() - .expect("RwLock is not poisoned"); - - let Some(enc) = guard.as_mut() else { - return Err(VaultLocked.into()); - }; - - let inner = Arc::make_mut(enc); - inner.set_org_keys(org_keys)?; - - Ok(enc.clone()) + ) -> Result<(), EncryptionSettingsError> { + EncryptionSettings::set_org_keys(org_keys, &self.crypto_service)?; + Ok(()) } } diff --git a/crates/bitwarden-core/src/key_management/mod.rs b/crates/bitwarden-core/src/key_management/mod.rs new file mode 100644 index 0000000..c877742 --- /dev/null +++ b/crates/bitwarden-core/src/key_management/mod.rs @@ -0,0 +1,55 @@ +use bitwarden_crypto::{key_refs, service::CryptoService, SymmetricCryptoKey}; + +key_refs! { + #[symmetric] + pub enum SymmetricKeyRef { + Master, + User, + Organization(uuid::Uuid), + #[local] + Local(&'static str), + } + + #[asymmetric] + pub enum AsymmetricKeyRef { + UserPrivateKey, + #[local] + Local(&'static str), + } +} + +pub fn create_test_crypto_with_user_key( + key: SymmetricCryptoKey, +) -> CryptoService { + let service = CryptoService::new(); + + #[allow(deprecated)] + service + .context_mut() + .set_symmetric_key(SymmetricKeyRef::User, key.clone()) + .expect("Mutable context"); + + service +} + +pub fn create_test_crypto_with_user_and_org_key( + key: SymmetricCryptoKey, + org_id: uuid::Uuid, + org_key: SymmetricCryptoKey, +) -> CryptoService { + let service = CryptoService::new(); + + #[allow(deprecated)] + service + .context_mut() + .set_symmetric_key(SymmetricKeyRef::User, key.clone()) + .expect("Mutable context"); + + #[allow(deprecated)] + service + .context_mut() + .set_symmetric_key(SymmetricKeyRef::Organization(org_id), org_key.clone()) + .expect("Mutable context"); + + service +} diff --git a/crates/bitwarden-core/src/lib.rs b/crates/bitwarden-core/src/lib.rs index 12b0df3..bc46a02 100644 --- a/crates/bitwarden-core/src/lib.rs +++ b/crates/bitwarden-core/src/lib.rs @@ -8,6 +8,7 @@ pub mod admin_console; pub mod auth; pub mod client; mod error; +pub mod key_management; pub use error::{validate_only_whitespaces, Error, MissingFieldError, VaultLocked}; #[cfg(feature = "internal")] pub mod mobile; diff --git a/crates/bitwarden-core/src/mobile/crypto.rs b/crates/bitwarden-core/src/mobile/crypto.rs index 1b027df..a09098f 100644 --- a/crates/bitwarden-core/src/mobile/crypto.rs +++ b/crates/bitwarden-core/src/mobile/crypto.rs @@ -1,8 +1,7 @@ use std::collections::HashMap; use bitwarden_crypto::{ - AsymmetricEncString, EncString, Kdf, KeyDecryptable, KeyEncryptable, MasterKey, - SymmetricCryptoKey, + AsymmetricEncString, Decryptable, EncString, Encryptable, Kdf, MasterKey, SymmetricCryptoKey, }; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -12,6 +11,7 @@ use {tsify_next::Tsify, wasm_bindgen::prelude::*}; use crate::{ client::{encryption_settings::EncryptionSettingsError, LoginMethod, UserLoginMethod}, error::{Error, Result}, + key_management::SymmetricKeyRef, Client, }; @@ -206,8 +206,9 @@ pub async fn initialize_org_crypto( } pub async fn get_user_encryption_key(client: &Client) -> Result { - let enc = client.internal.get_encryption_settings()?; - let user_key = enc.get_key(&None)?; + let ctx = client.internal.get_crypto_service().context(); + #[allow(deprecated)] + let user_key = ctx.dangerous_get_symmetric_key(SymmetricKeyRef::User)?; Ok(user_key.to_base64()) } @@ -223,8 +224,9 @@ pub struct UpdatePasswordResponse { } pub fn update_password(client: &Client, new_password: String) -> Result { - let enc = client.internal.get_encryption_settings()?; - let user_key = enc.get_key(&None)?; + let ctx = client.internal.get_crypto_service().context(); + #[allow(deprecated)] + let user_key = ctx.dangerous_get_symmetric_key(SymmetricKeyRef::User)?; let login_method = client .internal @@ -265,8 +267,9 @@ pub struct DerivePinKeyResponse { } pub fn derive_pin_key(client: &Client, pin: String) -> Result { - let enc = client.internal.get_encryption_settings()?; - let user_key = enc.get_key(&None)?; + let mut ctx = client.internal.get_crypto_service().context(); + #[allow(deprecated)] + let user_key = ctx.dangerous_get_symmetric_key(SymmetricKeyRef::User)?; let login_method = client .internal @@ -277,20 +280,21 @@ pub fn derive_pin_key(client: &Client, pin: String) -> Result Result { - let enc = client.internal.get_encryption_settings()?; - let user_key = enc.get_key(&None)?; + let mut ctx = client.internal.get_crypto_service().context(); - let pin: String = encrypted_pin.decrypt_with_key(user_key)?; + let pin: String = encrypted_pin.decrypt(&mut ctx, SymmetricKeyRef::User)?; let login_method = client .internal .get_login_method() .ok_or(Error::NotAuthenticated)?; + #[allow(deprecated)] + let user_key = ctx.dangerous_get_symmetric_key(SymmetricKeyRef::User)?; derive_pin_protected_user_key(&pin, &login_method, user_key) } @@ -321,8 +325,10 @@ pub(super) fn enroll_admin_password_reset( use bitwarden_crypto::AsymmetricPublicCryptoKey; let public_key = AsymmetricPublicCryptoKey::from_der(&STANDARD.decode(public_key)?)?; - let enc = client.internal.get_encryption_settings()?; - let key = enc.get_key(&None)?; + + let ctx = client.internal.get_crypto_service().context(); + #[allow(deprecated)] + let key = ctx.dangerous_get_symmetric_key(SymmetricKeyRef::User)?; Ok(AsymmetricEncString::encrypt_rsa2048_oaep_sha1( &key.to_vec(), @@ -414,22 +420,23 @@ mod tests { assert_eq!(new_hash, new_password_response.password_hash); - assert_eq!( - client - .internal - .get_encryption_settings() - .unwrap() - .get_key(&None) - .unwrap() - .to_base64(), - client2 - .internal - .get_encryption_settings() - .unwrap() - .get_key(&None) - .unwrap() - .to_base64() - ); + #[allow(deprecated)] + let key1 = client + .internal + .get_crypto_service() + .context() + .dangerous_get_symmetric_key(SymmetricKeyRef::User) + .unwrap() + .to_base64(); + #[allow(deprecated)] + let key2 = client2 + .internal + .get_crypto_service() + .context() + .dangerous_get_symmetric_key(SymmetricKeyRef::User) + .unwrap() + .to_base64(); + assert_eq!(key1, key2); } #[tokio::test] @@ -476,22 +483,23 @@ mod tests { .await .unwrap(); - assert_eq!( - client - .internal - .get_encryption_settings() - .unwrap() - .get_key(&None) - .unwrap() - .to_base64(), - client2 - .internal - .get_encryption_settings() - .unwrap() - .get_key(&None) - .unwrap() - .to_base64() - ); + #[allow(deprecated)] + let key1 = client + .internal + .get_crypto_service() + .context() + .dangerous_get_symmetric_key(SymmetricKeyRef::User) + .unwrap() + .to_base64(); + #[allow(deprecated)] + let key2 = client2 + .internal + .get_crypto_service() + .context() + .dangerous_get_symmetric_key(SymmetricKeyRef::User) + .unwrap() + .to_base64(); + assert_eq!(key1, key2); // Verify we can derive the pin protected user key from the encrypted pin let pin_protected_user_key = derive_pin_user_key(&client, pin_key.encrypted_pin).unwrap(); @@ -515,22 +523,15 @@ mod tests { .await .unwrap(); - assert_eq!( - client - .internal - .get_encryption_settings() - .unwrap() - .get_key(&None) - .unwrap() - .to_base64(), - client3 - .internal - .get_encryption_settings() - .unwrap() - .get_key(&None) - .unwrap() - .to_base64() - ); + #[allow(deprecated)] + let key3 = client3 + .internal + .get_crypto_service() + .context() + .dangerous_get_symmetric_key(SymmetricKeyRef::User) + .unwrap() + .to_base64(); + assert_eq!(key1, key3); } #[test] @@ -563,11 +564,22 @@ mod tests { let private_key = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCzLtEUdxfcLxDj84yaGFsVF5hZ8Hjlb08NMQDy1RnBma06I3ZESshLYzVz4r/gegMn9OOltfV/Yxlyvida8oW6qdlfJ7AVz6Oa8pV7BiL40C7b76+oqraQpyYw2HChANB1AhXL9SqWngKmLZwjA7qiCrmcc0kZHeOb4KnKtp9iVvPVs+8veFvKgYO4ba2AAOHKFdR0W55/agXfAy+fWUAkC8mc9ikyJdQWaPV6OZvC2XFkOseBQm9Rynudh3BQpoWiL6w620efe7t5k+02/EyOFJL9f/XEEjM/+Yo0t3LAfkuhHGeKiRST59Xc9hTEmyJTeVXROtz+0fjqOp3xkaObAgMBAAECggEACs4xhnO0HaZhh1/iH7zORMIRXKeyxP2LQiTR8xwN5JJ9wRWmGAR9VasS7EZFTDidIGVME2u/h4s5EqXnhxfO+0gGksVvgNXJ/qw87E8K2216g6ZNo6vSGA7H1GH2voWwejJ4/k/cJug6dz2S402rRAKh2Wong1arYHSkVlQp3diiMa5FHAOSE+Cy09O2ZsaF9IXQYUtlW6AVXFrBEPYH2kvkaPXchh8VETMijo6tbvoKLnUHe+wTaDMls7hy8exjtVyI59r3DNzjy1lNGaGb5QSnFMXR+eHhPZc844Wv02MxC15zKABADrl58gpJyjTl6XpDdHCYGsmGpVGH3X9TQQKBgQDz/9beFjzq59ve6rGwn+EtnQfSsyYT+jr7GN8lNEXb3YOFXBgPhfFIcHRh2R00Vm9w2ApfAx2cd8xm2I6HuvQ1Os7g26LWazvuWY0Qzb+KaCLQTEGH1RnTq6CCG+BTRq/a3J8M4t38GV5TWlzv8wr9U4dl6FR4efjb65HXs1GQ4QKBgQC7/uHfrOTEHrLeIeqEuSl0vWNqEotFKdKLV6xpOvNuxDGbgW4/r/zaxDqt0YBOXmRbQYSEhmO3oy9J6XfE1SUln0gbavZeW0HESCAmUIC88bDnspUwS9RxauqT5aF8ODKN/bNCWCnBM1xyonPOs1oT1nyparJVdQoG//Y7vkB3+wKBgBqLqPq8fKAp3XfhHLfUjREDVoiLyQa/YI9U42IOz9LdxKNLo6p8rgVthpvmnRDGnpUuS+KOWjhdqDVANjF6G3t3DG7WNl8Rh5Gk2H4NhFswfSkgQrjebFLlBy9gjQVCWXt8KSmjvPbiY6q52Aaa8IUjA0YJAregvXxfopxO+/7BAoGARicvEtDp7WWnSc1OPoj6N14VIxgYcI7SyrzE0d/1x3ffKzB5e7qomNpxKzvqrVP8DzG7ydh8jaKPmv1MfF8tpYRy3AhmN3/GYwCnPqT75YYrhcrWcVdax5gmQVqHkFtIQkRSCIftzPLlpMGKha/YBV8c1fvC4LD0NPh/Ynv0gtECgYEAyOZg95/kte0jpgUEgwuMrzkhY/AaUJULFuR5MkyvReEbtSBQwV5tx60+T95PHNiFooWWVXiLMsAgyI2IbkxVR1Pzdri3gWK5CTfqb7kLuaj/B7SGvBa2Sxo478KS5K8tBBBWkITqo+wLC0mn3uZi1dyMWO1zopTA+KtEGF2dtGQ="; let private_key = AsymmetricCryptoKey::from_der(&STANDARD.decode(private_key).unwrap()).unwrap(); - let decrypted: Vec = encrypted.decrypt_with_key(&private_key).unwrap(); - let enc = client.internal.get_encryption_settings().unwrap(); - let expected = enc.get_key(&None).unwrap(); - assert_eq!(&decrypted, &expected.to_vec()); + let key = crate::key_management::AsymmetricKeyRef::Local("test"); + let mut ctx = client.internal.get_crypto_service().context(); + #[allow(deprecated)] + ctx.set_asymmetric_key(key, private_key).unwrap(); + let decrypted: Vec = encrypted.decrypt(&mut ctx, key).unwrap(); + + #[allow(deprecated)] + let expected = client + .internal + .get_crypto_service() + .context() + .dangerous_get_symmetric_key(SymmetricKeyRef::User) + .unwrap() + .to_vec(); + assert_eq!(&decrypted, &expected); } #[test] diff --git a/crates/bitwarden-core/src/platform/generate_fingerprint.rs b/crates/bitwarden-core/src/platform/generate_fingerprint.rs index 5d31a1a..f196eff 100644 --- a/crates/bitwarden-core/src/platform/generate_fingerprint.rs +++ b/crates/bitwarden-core/src/platform/generate_fingerprint.rs @@ -4,7 +4,7 @@ use log::info; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -use crate::error::Result; +use crate::{error::Result, key_management::AsymmetricKeyRef}; #[derive(Serialize, Deserialize, Debug, JsonSchema)] #[serde(rename_all = "camelCase", deny_unknown_fields)] @@ -38,11 +38,9 @@ pub(crate) fn generate_user_fingerprint( ) -> Result { info!("Generating fingerprint"); - let enc_settings = client.internal.get_encryption_settings()?; - let private_key = enc_settings - .private_key - .as_ref() - .ok_or("Missing private key")?; + let ctx = client.internal.get_crypto_service().context(); + #[allow(deprecated)] + let private_key = ctx.dangerous_get_asymmetric_key(AsymmetricKeyRef::UserPrivateKey)?; let public_key = private_key.to_public_der()?; let fingerprint = fingerprint(&fingerprint_material, &public_key)?; diff --git a/crates/bitwarden-crypto/src/enc_string/symmetric.rs b/crates/bitwarden-crypto/src/enc_string/symmetric.rs index 69711f7..c8598b0 100644 --- a/crates/bitwarden-crypto/src/enc_string/symmetric.rs +++ b/crates/bitwarden-crypto/src/enc_string/symmetric.rs @@ -8,7 +8,7 @@ use serde::Deserialize; use super::{check_length, from_b64, from_b64_vec, split_enc_string}; use crate::{ error::{CryptoError, EncStringParseError, Result}, - KeyDecryptable, KeyEncryptable, LocateKey, SymmetricCryptoKey, + KeyDecryptable, KeyEncryptable, SymmetricCryptoKey, }; #[cfg(feature = "wasm")] @@ -228,7 +228,6 @@ impl EncString { } } -impl LocateKey for EncString {} impl KeyEncryptable for &[u8] { fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { EncString::encrypt_aes256_hmac( diff --git a/crates/bitwarden-crypto/src/error.rs b/crates/bitwarden-crypto/src/error.rs index 1fcd967..21569a0 100644 --- a/crates/bitwarden-crypto/src/error.rs +++ b/crates/bitwarden-crypto/src/error.rs @@ -1,7 +1,6 @@ use std::fmt::Debug; use thiserror::Error; -use uuid::Uuid; use crate::fingerprint::FingerprintError; @@ -19,12 +18,10 @@ pub enum CryptoError { InvalidKeyLen, #[error("The value is not a valid UTF8 String")] InvalidUtf8String, - #[error("Missing Key for organization with ID {0}")] - MissingKey(Uuid), + #[error("Missing Key for Ref. {0}")] + MissingKey(String), #[error("The item was missing a required field: {0}")] MissingField(&'static str), - #[error("Missing Key for Ref. {0}")] - MissingKey2(String), #[error("Crypto store is read-only")] ReadOnlyCryptoStore, diff --git a/crates/bitwarden-crypto/src/keys/key_encryptable.rs b/crates/bitwarden-crypto/src/keys/key_encryptable.rs index 044be9f..7ddf689 100644 --- a/crates/bitwarden-crypto/src/keys/key_encryptable.rs +++ b/crates/bitwarden-crypto/src/keys/key_encryptable.rs @@ -15,16 +15,6 @@ impl KeyContainer for Arc { } } -pub trait LocateKey { - fn locate_key<'a>( - &self, - enc: &'a dyn KeyContainer, - org_id: &Option, - ) -> Result<&'a SymmetricCryptoKey, CryptoError> { - enc.get_key(org_id) - } -} - pub trait CryptoKey {} pub trait KeyEncryptable { diff --git a/crates/bitwarden-crypto/src/keys/mod.rs b/crates/bitwarden-crypto/src/keys/mod.rs index a75d9db..d36c9fb 100644 --- a/crates/bitwarden-crypto/src/keys/mod.rs +++ b/crates/bitwarden-crypto/src/keys/mod.rs @@ -1,5 +1,5 @@ mod key_encryptable; -pub use key_encryptable::{CryptoKey, KeyContainer, KeyDecryptable, KeyEncryptable, LocateKey}; +pub use key_encryptable::{CryptoKey, KeyContainer, KeyDecryptable, KeyEncryptable}; mod encryptable; pub use encryptable::{Decryptable, Encryptable, UsesKey}; pub mod key_ref; diff --git a/crates/bitwarden-crypto/src/service/context.rs b/crates/bitwarden-crypto/src/service/context.rs index 3e966cc..7aa10f1 100644 --- a/crates/bitwarden-crypto/src/service/context.rs +++ b/crates/bitwarden-crypto/src/service/context.rs @@ -239,7 +239,7 @@ impl< } else { self.global.get().symmetric_keys.get(key_ref) } - .ok_or_else(|| crate::CryptoError::MissingKey2(format!("{key_ref:?}"))) + .ok_or_else(|| crate::CryptoError::MissingKey(format!("{key_ref:?}"))) } fn get_asymmetric_key(&self, key_ref: AsymmKeyRef) -> Result<&AsymmetricCryptoKey> { @@ -248,7 +248,7 @@ impl< } else { self.global.get().asymmetric_keys.get(key_ref) } - .ok_or_else(|| crate::CryptoError::MissingKey2(format!("{key_ref:?}"))) + .ok_or_else(|| crate::CryptoError::MissingKey(format!("{key_ref:?}"))) } #[deprecated(note = "This function should ideally never be used outside this crate")] diff --git a/crates/bitwarden-exporters/src/export.rs b/crates/bitwarden-exporters/src/export.rs index 7fa0541..8819113 100644 --- a/crates/bitwarden-exporters/src/export.rs +++ b/crates/bitwarden-exporters/src/export.rs @@ -1,5 +1,4 @@ use bitwarden_core::Client; -use bitwarden_crypto::KeyDecryptable; use bitwarden_vault::{Cipher, CipherView, Collection, Folder, FolderView}; use crate::{ @@ -13,13 +12,12 @@ pub(crate) fn export_vault( ciphers: Vec, format: ExportFormat, ) -> Result { - let enc = client.internal.get_encryption_settings()?; - let key = enc.get_key(&None)?; + let crypto = client.internal.get_crypto_service(); - let folders: Vec = folders.decrypt_with_key(key)?; + let folders: Vec = crypto.decrypt_list(&folders)?; let folders: Vec = folders.into_iter().flat_map(|f| f.try_into()).collect(); - let ciphers: Vec = ciphers.decrypt_with_key(key)?; + let ciphers: Vec = crypto.decrypt_list(&ciphers)?; let ciphers: Vec = ciphers.into_iter().flat_map(|c| c.try_into()).collect(); match format { diff --git a/crates/bitwarden-fido/src/authenticator.rs b/crates/bitwarden-fido/src/authenticator.rs index cbddaf9..4617b41 100644 --- a/crates/bitwarden-fido/src/authenticator.rs +++ b/crates/bitwarden-fido/src/authenticator.rs @@ -1,7 +1,7 @@ use std::sync::Mutex; use bitwarden_core::{Client, VaultLocked}; -use bitwarden_crypto::{CryptoError, KeyContainer, KeyEncryptable}; +use bitwarden_crypto::CryptoError; use bitwarden_vault::{CipherError, CipherView}; use itertools::Itertools; use log::error; @@ -249,14 +249,14 @@ impl<'a> Fido2Authenticator<'a> { &mut self, rp_id: String, ) -> Result, SilentlyDiscoverCredentialsError> { - let enc = self.client.internal.get_encryption_settings()?; + let crypto = self.client.internal.get_crypto_service(); let result = self.credential_store.find_credentials(None, rp_id).await?; result .into_iter() .map( |cipher| -> Result, SilentlyDiscoverCredentialsError> { - Ok(Fido2CredentialAutofillView::from_cipher_view(&cipher, &*enc)?) + Ok(Fido2CredentialAutofillView::from_cipher_view(&cipher, &mut crypto.context())?) }, ) .flatten_ok() @@ -268,7 +268,7 @@ impl<'a> Fido2Authenticator<'a> { pub async fn credentials_for_autofill( &mut self, ) -> Result, CredentialsForAutofillError> { - let enc = self.client.internal.get_encryption_settings()?; + let crypto = self.client.internal.get_crypto_service(); let all_credentials = self.credential_store.all_credentials().await?; all_credentials @@ -276,7 +276,8 @@ impl<'a> Fido2Authenticator<'a> { .map( |cipher| -> Result, CredentialsForAutofillError> { Ok(Fido2CredentialAutofillView::from_cipher_view( - &cipher, &*enc, + &cipher, + &mut crypto.context(), )?) }, ) @@ -313,7 +314,7 @@ impl<'a> Fido2Authenticator<'a> { pub(super) fn get_selected_credential( &self, ) -> Result { - let enc = self.client.internal.get_encryption_settings()?; + let crypto = self.client.internal.get_crypto_service(); let cipher = self .selected_cipher @@ -322,7 +323,7 @@ impl<'a> Fido2Authenticator<'a> { .clone() .ok_or(GetSelectedCredentialError::NoSelectedCredential)?; - let creds = cipher.decrypt_fido2_credentials(&*enc)?; + let creds = cipher.decrypt_fido2_credentials(&mut crypto.context())?; let credential = creds .first() @@ -376,11 +377,7 @@ impl passkey::authenticator::CredentialStore for CredentialStoreImpl<'_> { .find_credentials(ids, rp_id.to_string()) .await?; - let enc = this - .authenticator - .client - .internal - .get_encryption_settings()?; + let crypto = this.authenticator.client.internal.get_crypto_service(); // Remove any that don't have Fido2 credentials let creds: Vec<_> = ciphers @@ -397,7 +394,7 @@ impl passkey::authenticator::CredentialStore for CredentialStoreImpl<'_> { if this.create_credential { Ok(creds .into_iter() - .map(|c| CipherViewContainer::new(c, &*enc)) + .map(|c| CipherViewContainer::new(c, &mut crypto.context())) .collect::>()?) } else { let picked = this @@ -413,7 +410,10 @@ impl passkey::authenticator::CredentialStore for CredentialStoreImpl<'_> { .expect("Mutex is not poisoned") .replace(picked.clone()); - Ok(vec![CipherViewContainer::new(picked, &*enc)?]) + Ok(vec![CipherViewContainer::new( + picked, + &mut crypto.context(), + )?]) } } @@ -457,12 +457,6 @@ impl passkey::authenticator::CredentialStore for CredentialStoreImpl<'_> { rp: passkey::types::ctap2::make_credential::PublicKeyCredentialRpEntity, options: passkey::types::ctap2::get_assertion::Options, ) -> Result<(), InnerError> { - let enc = this - .authenticator - .client - .internal - .get_encryption_settings()?; - let cred = try_from_credential_full(cred, user, rp, options)?; // Get the previously selected cipher and add the new credential to it @@ -474,7 +468,9 @@ impl passkey::authenticator::CredentialStore for CredentialStoreImpl<'_> { .clone() .ok_or(InnerError::NoSelectedCredential)?; - selected.set_new_fido2_credentials(&*enc, vec![cred])?; + let crypto = this.authenticator.client.internal.get_crypto_service(); + + selected.set_new_fido2_credentials(&mut crypto.context(), vec![cred])?; // Store the updated credential for later use this.authenticator @@ -484,8 +480,7 @@ impl passkey::authenticator::CredentialStore for CredentialStoreImpl<'_> { .replace(selected.clone()); // Encrypt the updated cipher before sending it to the clients to be stored - let key = enc.get_key(&selected.organization_id)?; - let encrypted = selected.encrypt_with_key(key)?; + let encrypted = crypto.encrypt(selected)?; this.authenticator .credential_store @@ -529,11 +524,7 @@ impl passkey::authenticator::CredentialStore for CredentialStoreImpl<'_> { this: &mut CredentialStoreImpl<'_>, cred: Passkey, ) -> Result<(), InnerError> { - let enc = this - .authenticator - .client - .internal - .get_encryption_settings()?; + let crypto = this.authenticator.client.internal.get_crypto_service(); // Get the previously selected cipher and update the credential let selected = this.authenticator.get_selected_credential()?; @@ -548,7 +539,7 @@ impl passkey::authenticator::CredentialStore for CredentialStoreImpl<'_> { let cred = fill_with_credential(&selected.credential, cred)?; let mut selected = selected.cipher; - selected.set_new_fido2_credentials(&*enc, vec![cred])?; + selected.set_new_fido2_credentials(&mut crypto.context(), vec![cred])?; // Store the updated credential for later use this.authenticator @@ -558,8 +549,7 @@ impl passkey::authenticator::CredentialStore for CredentialStoreImpl<'_> { .replace(selected.clone()); // Encrypt the updated cipher before sending it to the clients to be stored - let key = enc.get_key(&selected.organization_id)?; - let encrypted = selected.encrypt_with_key(key)?; + let encrypted = crypto.encrypt(selected)?; this.authenticator .credential_store diff --git a/crates/bitwarden-fido/src/client_fido.rs b/crates/bitwarden-fido/src/client_fido.rs index 84b3de6..482c124 100644 --- a/crates/bitwarden-fido/src/client_fido.rs +++ b/crates/bitwarden-fido/src/client_fido.rs @@ -47,11 +47,11 @@ impl<'a> ClientFido2<'a> { &'a self, cipher_view: CipherView, ) -> Result, DecryptFido2AutofillCredentialsError> { - let enc = self.client.internal.get_encryption_settings()?; + let crypto = self.client.internal.get_crypto_service(); Ok(Fido2CredentialAutofillView::from_cipher_view( &cipher_view, - &*enc, + &mut crypto.context(), )?) } } diff --git a/crates/bitwarden-fido/src/lib.rs b/crates/bitwarden-fido/src/lib.rs index be1dfdb..a5a322a 100644 --- a/crates/bitwarden-fido/src/lib.rs +++ b/crates/bitwarden-fido/src/lib.rs @@ -1,5 +1,6 @@ use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; -use bitwarden_crypto::KeyContainer; +use bitwarden_core::key_management::{AsymmetricKeyRef, SymmetricKeyRef}; +use bitwarden_crypto::service::CryptoServiceContext; use bitwarden_vault::{ CipherError, CipherView, Fido2CredentialFullView, Fido2CredentialNewView, Fido2CredentialView, }; @@ -62,8 +63,11 @@ pub(crate) struct CipherViewContainer { } impl CipherViewContainer { - fn new(cipher: CipherView, enc: &dyn KeyContainer) -> Result { - let fido2_credentials = cipher.get_fido2_credentials(enc)?; + fn new( + cipher: CipherView, + ctx: &mut CryptoServiceContext, + ) -> Result { + let fido2_credentials = cipher.get_fido2_credentials(ctx)?; Ok(Self { cipher, fido2_credentials, diff --git a/crates/bitwarden-fido/src/types.rs b/crates/bitwarden-fido/src/types.rs index 409db7a..fd3ee94 100644 --- a/crates/bitwarden-fido/src/types.rs +++ b/crates/bitwarden-fido/src/types.rs @@ -1,7 +1,8 @@ use std::borrow::Cow; use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine}; -use bitwarden_crypto::KeyContainer; +use bitwarden_core::key_management::{AsymmetricKeyRef, SymmetricKeyRef}; +use bitwarden_crypto::service::CryptoServiceContext; use bitwarden_vault::{CipherError, CipherView}; use passkey::types::webauthn::UserVerificationRequirement; use reqwest::Url; @@ -65,9 +66,9 @@ pub enum Fido2CredentialAutofillViewError { impl Fido2CredentialAutofillView { pub fn from_cipher_view( cipher: &CipherView, - enc: &dyn KeyContainer, + ctx: &mut CryptoServiceContext, ) -> Result, Fido2CredentialAutofillViewError> { - let credentials = cipher.decrypt_fido2_credentials(enc)?; + let credentials = cipher.decrypt_fido2_credentials(ctx)?; credentials .into_iter() diff --git a/crates/bitwarden-send/src/client_sends.rs b/crates/bitwarden-send/src/client_sends.rs index bf496e0..58b1871 100644 --- a/crates/bitwarden-send/src/client_sends.rs +++ b/crates/bitwarden-send/src/client_sends.rs @@ -1,7 +1,7 @@ use std::path::Path; use bitwarden_core::{Client, Error}; -use bitwarden_crypto::{EncString, KeyDecryptable, KeyEncryptable}; +use bitwarden_crypto::{Decryptable, EncString, Encryptable, UsesKey}; use crate::{Send, SendListView, SendView}; @@ -15,19 +15,16 @@ impl<'a> ClientSends<'a> { } pub fn decrypt(&self, send: Send) -> Result { - let enc = self.client.internal.get_encryption_settings()?; - let key = enc.get_key(&None)?; - - let send_view = send.decrypt_with_key(key)?; + let crypto = self.client.internal.get_crypto_service(); + let send_view = crypto.decrypt(&send)?; Ok(send_view) } pub fn decrypt_list(&self, sends: Vec) -> Result, Error> { - let enc = self.client.internal.get_encryption_settings()?; - let key = enc.get_key(&None)?; + let crypto = self.client.internal.get_crypto_service(); - let send_views = sends.decrypt_with_key(key)?; + let send_views = crypto.decrypt_list(&sends)?; Ok(send_views) } @@ -45,19 +42,19 @@ impl<'a> ClientSends<'a> { } pub fn decrypt_buffer(&self, send: Send, encrypted_buffer: &[u8]) -> Result, Error> { - let enc = self.client.internal.get_encryption_settings()?; - let key = enc.get_key(&None)?; - let key = Send::get_key(&send.key, key)?; + let crypto = self.client.internal.get_crypto_service(); + + let mut ctx = crypto.context(); + let key = Send::decrypt_key(&mut ctx, &send.key, send.uses_key())?; let buf = EncString::from_buffer(encrypted_buffer)?; - Ok(buf.decrypt_with_key(&key)?) + Ok(buf.decrypt(&mut ctx, key)?) } pub fn encrypt(&self, send_view: SendView) -> Result { - let enc = self.client.internal.get_encryption_settings()?; - let key = enc.get_key(&None)?; + let crypto = self.client.internal.get_crypto_service(); - let send = send_view.encrypt_with_key(key)?; + let send = crypto.encrypt(send_view)?; Ok(send) } @@ -75,11 +72,12 @@ impl<'a> ClientSends<'a> { } pub fn encrypt_buffer(&self, send: Send, buffer: &[u8]) -> Result, Error> { - let enc = self.client.internal.get_encryption_settings()?; - let key = enc.get_key(&None)?; - let key = Send::get_key(&send.key, key)?; + let crypto = self.client.internal.get_crypto_service(); + + let mut ctx = crypto.context(); + let key = Send::decrypt_key(&mut ctx, &send.key, send.uses_key())?; - let encrypted = buffer.encrypt_with_key(&key)?; + let encrypted = buffer.encrypt(&mut ctx, key)?; Ok(encrypted.to_buffer()?) } } diff --git a/crates/bitwarden-send/src/send.rs b/crates/bitwarden-send/src/send.rs index 3222ec1..bfd220a 100644 --- a/crates/bitwarden-send/src/send.rs +++ b/crates/bitwarden-send/src/send.rs @@ -3,10 +3,13 @@ use base64::{ Engine, }; use bitwarden_api_api::models::{SendFileModel, SendResponseModel, SendTextModel}; -use bitwarden_core::require; +use bitwarden_core::{ + key_management::{AsymmetricKeyRef, SymmetricKeyRef}, + require, +}; use bitwarden_crypto::{ - derive_shareable_key, generate_random_bytes, CryptoError, EncString, KeyDecryptable, - KeyEncryptable, SymmetricCryptoKey, + generate_random_bytes, service::CryptoServiceContext, CryptoError, Decryptable, EncString, + Encryptable, UsesKey, }; use chrono::{DateTime, Utc}; use schemars::JsonSchema; @@ -141,82 +144,121 @@ pub struct SendListView { pub expiration_date: Option>, } +const SEND_KEY: SymmetricKeyRef = SymmetricKeyRef::Local("send_key"); + impl Send { - pub fn get_key( + pub fn decrypt_key( + ctx: &mut CryptoServiceContext, send_key: &EncString, - enc_key: &SymmetricCryptoKey, - ) -> Result { - let key: Vec = send_key.decrypt_with_key(enc_key)?; - Self::derive_shareable_key(&key) + enc_key: SymmetricKeyRef, + ) -> Result { + let key: Vec = send_key.decrypt(ctx, enc_key)?; + Self::derive_shareable_key(ctx, &key) } - fn derive_shareable_key(key: &[u8]) -> Result { + fn derive_shareable_key( + ctx: &mut CryptoServiceContext, + key: &[u8], + ) -> Result { let key = Zeroizing::new(key.try_into().map_err(|_| CryptoError::InvalidKeyLen)?); - Ok(derive_shareable_key(key, "send", Some("send"))) + ctx.derive_shareable_key(SEND_KEY, key, "send", Some("send")) + } +} + +impl UsesKey for Send { + fn uses_key(&self) -> SymmetricKeyRef { + SymmetricKeyRef::User } } -impl KeyDecryptable for SendText { - fn decrypt_with_key(&self, key: &SymmetricCryptoKey) -> Result { +impl UsesKey for SendView { + fn uses_key(&self) -> SymmetricKeyRef { + SymmetricKeyRef::User + } +} + +impl Decryptable for SendText { + fn decrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(SendTextView { - text: self.text.decrypt_with_key(key)?, + text: self.text.decrypt(ctx, key)?, hidden: self.hidden, }) } } -impl KeyEncryptable for SendTextView { - fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { +impl Encryptable for SendTextView { + fn encrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(SendText { - text: self.text.encrypt_with_key(key)?, + text: self.text.encrypt(ctx, key)?, hidden: self.hidden, }) } } -impl KeyDecryptable for SendFile { - fn decrypt_with_key(&self, key: &SymmetricCryptoKey) -> Result { +impl Decryptable for SendFile { + fn decrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(SendFileView { id: self.id.clone(), - file_name: self.file_name.decrypt_with_key(key)?, + file_name: self.file_name.decrypt(ctx, key)?, size: self.size.clone(), size_name: self.size_name.clone(), }) } } -impl KeyEncryptable for SendFileView { - fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { +impl Encryptable for SendFileView { + fn encrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(SendFile { id: self.id.clone(), - file_name: self.file_name.encrypt_with_key(key)?, + file_name: self.file_name.encrypt(ctx, key)?, size: self.size.clone(), size_name: self.size_name.clone(), }) } } -impl KeyDecryptable for Send { - fn decrypt_with_key(&self, key: &SymmetricCryptoKey) -> Result { +impl Decryptable for Send { + fn decrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { // For sends, we first decrypt the send key with the user key, and stretch it to it's full // size For the rest of the fields, we ignore the provided SymmetricCryptoKey and // the stretched key - let k: Vec = self.key.decrypt_with_key(key)?; - let key = Send::derive_shareable_key(&k)?; + + let k: Vec = self.key.decrypt(ctx, key)?; + let key = Send::derive_shareable_key(ctx, &k)?; Ok(SendView { id: self.id, access_id: self.access_id.clone(), - name: self.name.decrypt_with_key(&key).ok().unwrap_or_default(), - notes: self.notes.decrypt_with_key(&key).ok().flatten(), + name: self.name.decrypt(ctx, key).ok().unwrap_or_default(), + notes: self.notes.decrypt(ctx, key).ok().flatten(), key: Some(URL_SAFE_NO_PAD.encode(k)), new_password: None, has_password: self.password.is_some(), r#type: self.r#type, - file: self.file.decrypt_with_key(&key).ok().flatten(), - text: self.text.decrypt_with_key(&key).ok().flatten(), + file: self.file.decrypt(ctx, key).ok().flatten(), + text: self.text.decrypt(ctx, key).ok().flatten(), max_access_count: self.max_access_count, access_count: self.access_count, @@ -230,18 +272,24 @@ impl KeyDecryptable for Send { } } -impl KeyDecryptable for Send { - fn decrypt_with_key(&self, key: &SymmetricCryptoKey) -> Result { +impl Decryptable for Send { + fn decrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { // For sends, we first decrypt the send key with the user key, and stretch it to it's full // size For the rest of the fields, we ignore the provided SymmetricCryptoKey and // the stretched key - let key = Send::get_key(&self.key, key)?; + + let k: Vec = self.key.decrypt(ctx, key)?; + let key = Send::derive_shareable_key(ctx, &k)?; Ok(SendListView { id: self.id, access_id: self.access_id.clone(), - name: self.name.decrypt_with_key(&key)?, + name: self.name.decrypt(ctx, key).ok().unwrap_or_default(), r#type: self.r#type, disabled: self.disabled, @@ -253,12 +301,16 @@ impl KeyDecryptable for Send { } } -impl KeyEncryptable for SendView { - fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { +impl Encryptable for SendView { + fn encrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { // For sends, we first decrypt the send key with the user key, and stretch it to it's full // size For the rest of the fields, we ignore the provided SymmetricCryptoKey and // the stretched key - let k = match (self.key, self.id) { + let k = match (&self.key, &self.id) { // Existing send, decrypt key (Some(k), _) => URL_SAFE_NO_PAD .decode(k) @@ -271,23 +323,23 @@ impl KeyEncryptable for SendView { // Existing send without key _ => return Err(CryptoError::InvalidKey), }; - let send_key = Send::derive_shareable_key(&k)?; + let send_key = Send::derive_shareable_key(ctx, &k)?; Ok(Send { id: self.id, - access_id: self.access_id, + access_id: self.access_id.clone(), - name: self.name.encrypt_with_key(&send_key)?, - notes: self.notes.encrypt_with_key(&send_key)?, - key: k.encrypt_with_key(key)?, - password: self.new_password.map(|password| { + name: self.name.encrypt(ctx, send_key)?, + notes: self.notes.encrypt(ctx, send_key)?, + key: k.as_slice().encrypt(ctx, key)?, + password: self.new_password.as_ref().map(|password| { let password = bitwarden_crypto::pbkdf2(password.as_bytes(), &k, SEND_ITERATIONS); STANDARD.encode(password) }), r#type: self.r#type, - file: self.file.encrypt_with_key(&send_key)?, - text: self.text.encrypt_with_key(&send_key)?, + file: self.file.encrypt(ctx, send_key)?, + text: self.text.encrypt(ctx, send_key)?, max_access_count: self.max_access_count, access_count: self.access_count, @@ -361,78 +413,36 @@ impl TryFrom for SendText { #[cfg(test)] mod tests { - use std::collections::HashMap; - - use bitwarden_crypto::{Kdf, KeyContainer, KeyDecryptable, KeyEncryptable, MasterKey}; + use bitwarden_core::key_management::create_test_crypto_with_user_key; + use bitwarden_crypto::SymmetricCryptoKey; use super::*; - struct MockKeyContainer(HashMap, SymmetricCryptoKey>); - impl MockKeyContainer { - fn new(master_key: MasterKey, user_key: EncString) -> Result { - let user_key = master_key.decrypt_user_key(user_key)?; - Ok(Self(HashMap::from([(None, user_key)]))) - } - } - impl KeyContainer for MockKeyContainer { - fn get_key<'a>( - &'a self, - org_id: &Option, - ) -> Result<&'a SymmetricCryptoKey, CryptoError> { - self.0 - .get(org_id) - .ok_or(CryptoError::MissingKey(org_id.unwrap_or_default())) - } - } - #[test] fn test_get_send_key() { // Initialize user encryption with some test data - let master_key = MasterKey::derive( - "asdfasdfasdf", - "test@bitwarden.com", - &Kdf::PBKDF2 { - iterations: 345123.try_into().unwrap(), - }, - ) - .unwrap(); - let enc = MockKeyContainer::new( - master_key, - "2.majkL1/hNz9yptLqNAUSnw==|RiOzMTTJMG948qu8O3Zm1EQUO2E8BuTwFKnO9LWQjMzxMWJM5GbyOq2/A+tumPbTERt4JWur/FKfgHb+gXuYiEYlXPMuVBvT7nv4LPytJuM=|IVqMxHJeR1ZXY0sGngTC0x+WqbG8p6V+BTrdgBbQXjM=".parse().unwrap(), - ).unwrap(); - - let k = enc.get_key(&None).unwrap(); + let user_key: SymmetricCryptoKey = "w2LO+nwV4oxwswVYCxlOfRUseXfvU03VzvKQHrqeklPgiMZrspUe6sOBToCnDn9Ay0tuCBn8ykVVRb7PWhub2Q==".to_string().try_into().unwrap(); + let crypto = create_test_crypto_with_user_key(user_key); + let mut ctx = crypto.context(); let send_key = "2.+1KUfOX8A83Xkwk1bumo/w==|Nczvv+DTkeP466cP/wMDnGK6W9zEIg5iHLhcuQG6s+M=|SZGsfuIAIaGZ7/kzygaVUau3LeOvJUlolENBOU+LX7g=" .parse() .unwrap(); // Get the send key - let send_key = Send::get_key(&send_key, k).unwrap(); + let send_key = Send::decrypt_key(&mut ctx, &send_key, SymmetricKeyRef::User).unwrap(); + + #[allow(deprecated)] + let send_key = ctx.dangerous_get_symmetric_key(send_key).unwrap(); + let send_key_b64 = send_key.to_base64(); assert_eq!(send_key_b64, "IR9ImHGm6rRuIjiN7csj94bcZR5WYTJj5GtNfx33zm6tJCHUl+QZlpNPba8g2yn70KnOHsAODLcR0um6E3MAlg=="); } - fn build_encryption_settings() -> MockKeyContainer { - let master_key = MasterKey::derive( - "asdfasdfasdf", - "test@bitwarden.com", - &Kdf::PBKDF2 { - iterations: 600_000.try_into().unwrap(), - }, - ) - .unwrap(); - - MockKeyContainer::new( - master_key, - "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=".parse().unwrap(), - ).unwrap() - } - #[test] pub fn test_decrypt() { - let enc = build_encryption_settings(); - let key = enc.get_key(&None).unwrap(); + let user_key: SymmetricCryptoKey = "bYCsk857hl8QJJtxyRK65tjUrbxKC4aDifJpsml+NIv4W9cVgFvi3qVD+yJTUU2T4UwNKWYtt9pqWf7Q+2WCCg==".to_string().try_into().unwrap(); + let crypto = create_test_crypto_with_user_key(user_key); let send = Send { id: "3d80dd72-2d14-4f26-812c-b0f0018aa144".parse().ok(), @@ -457,7 +467,7 @@ mod tests { hide_email: false, }; - let view: SendView = send.decrypt_with_key(key).unwrap(); + let view: SendView = crypto.decrypt(&send).unwrap(); let expected = SendView { id: "3d80dd72-2d14-4f26-812c-b0f0018aa144".parse().ok(), @@ -487,8 +497,8 @@ mod tests { #[test] pub fn test_encrypt() { - let enc = build_encryption_settings(); - let key = enc.get_key(&None).unwrap(); + let user_key: SymmetricCryptoKey = "bYCsk857hl8QJJtxyRK65tjUrbxKC4aDifJpsml+NIv4W9cVgFvi3qVD+yJTUU2T4UwNKWYtt9pqWf7Q+2WCCg==".to_string().try_into().unwrap(); + let crypto = create_test_crypto_with_user_key(user_key); let view = SendView { id: "3d80dd72-2d14-4f26-812c-b0f0018aa144".parse().ok(), @@ -514,19 +524,16 @@ mod tests { }; // Re-encrypt and decrypt again to ensure encrypt works - let v: SendView = view - .clone() - .encrypt_with_key(key) - .unwrap() - .decrypt_with_key(key) + let v: SendView = crypto + .decrypt(&crypto.encrypt(view.clone()).unwrap()) .unwrap(); assert_eq!(v, view); } #[test] pub fn test_create() { - let enc = build_encryption_settings(); - let key = enc.get_key(&None).unwrap(); + let user_key: SymmetricCryptoKey = "bYCsk857hl8QJJtxyRK65tjUrbxKC4aDifJpsml+NIv4W9cVgFvi3qVD+yJTUU2T4UwNKWYtt9pqWf7Q+2WCCg==".to_string().try_into().unwrap(); + let crypto = create_test_crypto_with_user_key(user_key); let view = SendView { id: None, @@ -552,11 +559,8 @@ mod tests { }; // Re-encrypt and decrypt again to ensure encrypt works - let v: SendView = view - .clone() - .encrypt_with_key(key) - .unwrap() - .decrypt_with_key(key) + let v: SendView = crypto + .decrypt(&crypto.encrypt(view.clone()).unwrap()) .unwrap(); // Ignore key when comparing @@ -566,8 +570,8 @@ mod tests { #[test] pub fn test_create_password() { - let enc = build_encryption_settings(); - let key = enc.get_key(&None).unwrap(); + let user_key: SymmetricCryptoKey = "bYCsk857hl8QJJtxyRK65tjUrbxKC4aDifJpsml+NIv4W9cVgFvi3qVD+yJTUU2T4UwNKWYtt9pqWf7Q+2WCCg==".to_string().try_into().unwrap(); + let crypto = create_test_crypto_with_user_key(user_key); let view = SendView { id: None, @@ -592,14 +596,14 @@ mod tests { expiration_date: None, }; - let send: Send = view.encrypt_with_key(key).unwrap(); + let send: Send = crypto.encrypt(view).unwrap(); assert_eq!( send.password, Some("vTIDfdj3FTDbejmMf+mJWpYdMXsxfeSd1Sma3sjCtiQ=".to_owned()) ); - let v: SendView = send.decrypt_with_key(key).unwrap(); + let v: SendView = crypto.decrypt(&send).unwrap(); assert_eq!(v.new_password, None); assert!(v.has_password); } diff --git a/crates/bitwarden-sm/src/projects/create.rs b/crates/bitwarden-sm/src/projects/create.rs index bb377e2..91154ec 100644 --- a/crates/bitwarden-sm/src/projects/create.rs +++ b/crates/bitwarden-sm/src/projects/create.rs @@ -1,6 +1,6 @@ use bitwarden_api_api::models::ProjectCreateRequestModel; -use bitwarden_core::{validate_only_whitespaces, Client, Error}; -use bitwarden_crypto::KeyEncryptable; +use bitwarden_core::{key_management::SymmetricKeyRef, validate_only_whitespaces, Client, Error}; +use bitwarden_crypto::Encryptable; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -23,12 +23,20 @@ pub(crate) async fn create_project( ) -> Result { input.validate()?; - let enc = client.internal.get_encryption_settings()?; - let key = enc.get_key(&Some(input.organization_id))?; - - let project = Some(ProjectCreateRequestModel { - name: input.name.clone().trim().encrypt_with_key(key)?.to_string(), - }); + let project = { + // Context is not Send, so we can't use it across an await point + let mut ctx = client.internal.get_crypto_service().context(); + let key = SymmetricKeyRef::Organization(input.organization_id); + + Some(ProjectCreateRequestModel { + name: input + .name + .clone() + .trim() + .encrypt(&mut ctx, key)? + .to_string(), + }) + }; let config = client.internal.get_api_configurations().await; let res = bitwarden_api_api::apis::projects_api::organizations_organization_id_projects_post( @@ -38,7 +46,8 @@ pub(crate) async fn create_project( ) .await?; - ProjectResponse::process_response(res, &enc) + let mut ctx = client.internal.get_crypto_service().context(); + ProjectResponse::process_response(res, &mut ctx) } #[cfg(test)] diff --git a/crates/bitwarden-sm/src/projects/get.rs b/crates/bitwarden-sm/src/projects/get.rs index 81a6697..fe2e3d1 100644 --- a/crates/bitwarden-sm/src/projects/get.rs +++ b/crates/bitwarden-sm/src/projects/get.rs @@ -20,7 +20,6 @@ pub(crate) async fn get_project( let res = bitwarden_api_api::apis::projects_api::projects_id_get(&config.api, input.id).await?; - let enc = client.internal.get_encryption_settings()?; - - ProjectResponse::process_response(res, &enc) + let mut ctx = client.internal.get_crypto_service().context(); + ProjectResponse::process_response(res, &mut ctx) } diff --git a/crates/bitwarden-sm/src/projects/list.rs b/crates/bitwarden-sm/src/projects/list.rs index 334e060..05435e6 100644 --- a/crates/bitwarden-sm/src/projects/list.rs +++ b/crates/bitwarden-sm/src/projects/list.rs @@ -1,8 +1,10 @@ use bitwarden_api_api::models::ProjectResponseModelListResponseModel; use bitwarden_core::{ - client::{encryption_settings::EncryptionSettings, Client}, + client::Client, + key_management::{AsymmetricKeyRef, SymmetricKeyRef}, Error, }; +use bitwarden_crypto::service::CryptoServiceContext; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -27,9 +29,9 @@ pub(crate) async fn list_projects( ) .await?; - let enc = client.internal.get_encryption_settings()?; + let mut ctx = client.internal.get_crypto_service().context(); - ProjectsResponse::process_response(res, &enc) + ProjectsResponse::process_response(res, &mut ctx) } #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -41,14 +43,14 @@ pub struct ProjectsResponse { impl ProjectsResponse { pub(crate) fn process_response( response: ProjectResponseModelListResponseModel, - enc: &EncryptionSettings, + ctx: &mut CryptoServiceContext, ) -> Result { let data = response.data.unwrap_or_default(); Ok(ProjectsResponse { data: data .into_iter() - .map(|r| ProjectResponse::process_response(r, enc)) + .map(|r| ProjectResponse::process_response(r, ctx)) .collect::>()?, }) } diff --git a/crates/bitwarden-sm/src/projects/project_response.rs b/crates/bitwarden-sm/src/projects/project_response.rs index a70a3fd..0898b18 100644 --- a/crates/bitwarden-sm/src/projects/project_response.rs +++ b/crates/bitwarden-sm/src/projects/project_response.rs @@ -1,6 +1,9 @@ use bitwarden_api_api::models::ProjectResponseModel; -use bitwarden_core::{client::encryption_settings::EncryptionSettings, require, Error}; -use bitwarden_crypto::{EncString, KeyDecryptable}; +use bitwarden_core::{ + key_management::{AsymmetricKeyRef, SymmetricKeyRef}, + require, Error, +}; +use bitwarden_crypto::{service::CryptoServiceContext, Decryptable, EncString}; use chrono::{DateTime, Utc}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -19,14 +22,14 @@ pub struct ProjectResponse { impl ProjectResponse { pub(crate) fn process_response( response: ProjectResponseModel, - enc: &EncryptionSettings, + ctx: &mut CryptoServiceContext, ) -> Result { let organization_id = require!(response.organization_id); - let enc_key = enc.get_key(&Some(organization_id))?; + let enc_key = SymmetricKeyRef::Organization(organization_id); let name = require!(response.name) .parse::()? - .decrypt_with_key(enc_key)?; + .decrypt(ctx, enc_key)?; Ok(ProjectResponse { id: require!(response.id), diff --git a/crates/bitwarden-sm/src/projects/update.rs b/crates/bitwarden-sm/src/projects/update.rs index 359b669..d173942 100644 --- a/crates/bitwarden-sm/src/projects/update.rs +++ b/crates/bitwarden-sm/src/projects/update.rs @@ -1,6 +1,6 @@ use bitwarden_api_api::models::ProjectUpdateRequestModel; -use bitwarden_core::{validate_only_whitespaces, Client, Error}; -use bitwarden_crypto::KeyEncryptable; +use bitwarden_core::{key_management::SymmetricKeyRef, validate_only_whitespaces, Client, Error}; +use bitwarden_crypto::Encryptable; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -25,19 +25,28 @@ pub(crate) async fn update_project( ) -> Result { input.validate()?; - let enc = client.internal.get_encryption_settings()?; - let key = enc.get_key(&Some(input.organization_id))?; - - let project = Some(ProjectUpdateRequestModel { - name: input.name.clone().trim().encrypt_with_key(key)?.to_string(), - }); + let project = { + // Context is not Send, so we can't use it across an await point + let mut ctx = client.internal.get_crypto_service().context(); + let key = SymmetricKeyRef::Organization(input.organization_id); + + Some(ProjectUpdateRequestModel { + name: input + .name + .clone() + .trim() + .encrypt(&mut ctx, key)? + .to_string(), + }) + }; let config = client.internal.get_api_configurations().await; let res = bitwarden_api_api::apis::projects_api::projects_id_put(&config.api, input.id, project) .await?; - ProjectResponse::process_response(res, &enc) + let mut ctx = client.internal.get_crypto_service().context(); + ProjectResponse::process_response(res, &mut ctx) } #[cfg(test)] diff --git a/crates/bitwarden-sm/src/secrets/create.rs b/crates/bitwarden-sm/src/secrets/create.rs index efd8138..2414516 100644 --- a/crates/bitwarden-sm/src/secrets/create.rs +++ b/crates/bitwarden-sm/src/secrets/create.rs @@ -1,6 +1,6 @@ use bitwarden_api_api::models::SecretCreateRequestModel; -use bitwarden_core::{validate_only_whitespaces, Client, Error}; -use bitwarden_crypto::KeyEncryptable; +use bitwarden_core::{key_management::SymmetricKeyRef, validate_only_whitespaces, Client, Error}; +use bitwarden_crypto::Encryptable; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -31,16 +31,24 @@ pub(crate) async fn create_secret( ) -> Result { input.validate()?; - let enc = client.internal.get_encryption_settings()?; - let key = enc.get_key(&Some(input.organization_id))?; - - let secret = Some(SecretCreateRequestModel { - key: input.key.clone().trim().encrypt_with_key(key)?.to_string(), - value: input.value.clone().encrypt_with_key(key)?.to_string(), - note: input.note.clone().trim().encrypt_with_key(key)?.to_string(), - project_ids: input.project_ids.clone(), - access_policies_requests: None, - }); + let secret = { + // Context is not Send, so we can't use it across an await point + let mut ctx = client.internal.get_crypto_service().context(); + let key = SymmetricKeyRef::Organization(input.organization_id); + + Some(SecretCreateRequestModel { + key: input.key.clone().trim().encrypt(&mut ctx, key)?.to_string(), + value: input.value.clone().encrypt(&mut ctx, key)?.to_string(), + note: input + .note + .clone() + .trim() + .encrypt(&mut ctx, key)? + .to_string(), + project_ids: input.project_ids.clone(), + access_policies_requests: None, + }) + }; let config = client.internal.get_api_configurations().await; let res = bitwarden_api_api::apis::secrets_api::organizations_organization_id_secrets_post( @@ -50,7 +58,8 @@ pub(crate) async fn create_secret( ) .await?; - SecretResponse::process_response(res, &enc) + let mut ctx = client.internal.get_crypto_service().context(); + SecretResponse::process_response(res, &mut ctx) } #[cfg(test)] diff --git a/crates/bitwarden-sm/src/secrets/get.rs b/crates/bitwarden-sm/src/secrets/get.rs index d0964df..0dabe45 100644 --- a/crates/bitwarden-sm/src/secrets/get.rs +++ b/crates/bitwarden-sm/src/secrets/get.rs @@ -19,7 +19,7 @@ pub(crate) async fn get_secret( let config = client.internal.get_api_configurations().await; let res = bitwarden_api_api::apis::secrets_api::secrets_id_get(&config.api, input.id).await?; - let enc = client.internal.get_encryption_settings()?; + let mut ctx = client.internal.get_crypto_service().context(); - SecretResponse::process_response(res, &enc) + SecretResponse::process_response(res, &mut ctx) } diff --git a/crates/bitwarden-sm/src/secrets/get_by_ids.rs b/crates/bitwarden-sm/src/secrets/get_by_ids.rs index b5714c4..579e04b 100644 --- a/crates/bitwarden-sm/src/secrets/get_by_ids.rs +++ b/crates/bitwarden-sm/src/secrets/get_by_ids.rs @@ -24,7 +24,7 @@ pub(crate) async fn get_secrets_by_ids( let res = bitwarden_api_api::apis::secrets_api::secrets_get_by_ids_post(&config.api, request).await?; - let enc = client.internal.get_encryption_settings()?; + let mut ctx = client.internal.get_crypto_service().context(); - SecretsResponse::process_response(res, &enc) + SecretsResponse::process_response(res, &mut ctx) } diff --git a/crates/bitwarden-sm/src/secrets/list.rs b/crates/bitwarden-sm/src/secrets/list.rs index 60a5c97..9b51808 100644 --- a/crates/bitwarden-sm/src/secrets/list.rs +++ b/crates/bitwarden-sm/src/secrets/list.rs @@ -2,10 +2,11 @@ use bitwarden_api_api::models::{ SecretWithProjectsListResponseModel, SecretsWithProjectsInnerSecret, }; use bitwarden_core::{ - client::{encryption_settings::EncryptionSettings, Client}, + client::Client, + key_management::{AsymmetricKeyRef, SymmetricKeyRef}, require, Error, }; -use bitwarden_crypto::{EncString, KeyDecryptable}; +use bitwarden_crypto::{service::CryptoServiceContext, Decryptable, EncString}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -28,9 +29,9 @@ pub(crate) async fn list_secrets( ) .await?; - let enc = client.internal.get_encryption_settings()?; + let mut ctx = client.internal.get_crypto_service().context(); - SecretIdentifiersResponse::process_response(res, &enc) + SecretIdentifiersResponse::process_response(res, &mut ctx) } #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -51,9 +52,9 @@ pub(crate) async fn list_secrets_by_project( ) .await?; - let enc = client.internal.get_encryption_settings()?; + let mut ctx = client.internal.get_crypto_service().context(); - SecretIdentifiersResponse::process_response(res, &enc) + SecretIdentifiersResponse::process_response(res, &mut ctx) } #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -65,14 +66,14 @@ pub struct SecretIdentifiersResponse { impl SecretIdentifiersResponse { pub(crate) fn process_response( response: SecretWithProjectsListResponseModel, - enc: &EncryptionSettings, + ctx: &mut CryptoServiceContext, ) -> Result { Ok(SecretIdentifiersResponse { data: response .secrets .unwrap_or_default() .into_iter() - .map(|r| SecretIdentifierResponse::process_response(r, enc)) + .map(|r| SecretIdentifierResponse::process_response(r, ctx)) .collect::>()?, }) } @@ -90,14 +91,14 @@ pub struct SecretIdentifierResponse { impl SecretIdentifierResponse { pub(crate) fn process_response( response: SecretsWithProjectsInnerSecret, - enc: &EncryptionSettings, + ctx: &mut CryptoServiceContext, ) -> Result { let organization_id = require!(response.organization_id); - let enc_key = enc.get_key(&Some(organization_id))?; + let enc_key = SymmetricKeyRef::Organization(organization_id); let key = require!(response.key) .parse::()? - .decrypt_with_key(enc_key)?; + .decrypt(ctx, enc_key)?; Ok(SecretIdentifierResponse { id: require!(response.id), diff --git a/crates/bitwarden-sm/src/secrets/secret_response.rs b/crates/bitwarden-sm/src/secrets/secret_response.rs index e480ac3..8419a61 100644 --- a/crates/bitwarden-sm/src/secrets/secret_response.rs +++ b/crates/bitwarden-sm/src/secrets/secret_response.rs @@ -1,8 +1,11 @@ use bitwarden_api_api::models::{ BaseSecretResponseModel, BaseSecretResponseModelListResponseModel, SecretResponseModel, }; -use bitwarden_core::{client::encryption_settings::EncryptionSettings, require, Error}; -use bitwarden_crypto::{EncString, KeyDecryptable}; +use bitwarden_core::{ + key_management::{AsymmetricKeyRef, SymmetricKeyRef}, + require, Error, +}; +use bitwarden_crypto::{service::CryptoServiceContext, Decryptable, EncString}; use chrono::{DateTime, Utc}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -26,7 +29,7 @@ pub struct SecretResponse { impl SecretResponse { pub(crate) fn process_response( response: SecretResponseModel, - enc: &EncryptionSettings, + ctx: &mut CryptoServiceContext, ) -> Result { let base = BaseSecretResponseModel { object: response.object, @@ -39,24 +42,24 @@ impl SecretResponse { revision_date: response.revision_date, projects: response.projects, }; - Self::process_base_response(base, enc) + Self::process_base_response(base, ctx) } pub(crate) fn process_base_response( response: BaseSecretResponseModel, - enc: &EncryptionSettings, + ctx: &mut CryptoServiceContext, ) -> Result { - let org_id = response.organization_id; - let enc_key = enc.get_key(&org_id)?; + let organization_id = require!(response.organization_id); + let enc_key = SymmetricKeyRef::Organization(organization_id); let key = require!(response.key) .parse::()? - .decrypt_with_key(enc_key)?; + .decrypt(ctx, enc_key)?; let value = require!(response.value) .parse::()? - .decrypt_with_key(enc_key)?; + .decrypt(ctx, enc_key)?; let note = require!(response.note) .parse::()? - .decrypt_with_key(enc_key)?; + .decrypt(ctx, enc_key)?; let project = response .projects @@ -65,7 +68,7 @@ impl SecretResponse { Ok(SecretResponse { id: require!(response.id), - organization_id: require!(org_id), + organization_id, project_id: project, key, value, @@ -86,14 +89,14 @@ pub struct SecretsResponse { impl SecretsResponse { pub(crate) fn process_response( response: BaseSecretResponseModelListResponseModel, - enc: &EncryptionSettings, + ctx: &mut CryptoServiceContext, ) -> Result { Ok(SecretsResponse { data: response .data .unwrap_or_default() .into_iter() - .map(|r| SecretResponse::process_base_response(r, enc)) + .map(|r| SecretResponse::process_base_response(r, ctx)) .collect::>()?, }) } diff --git a/crates/bitwarden-sm/src/secrets/sync.rs b/crates/bitwarden-sm/src/secrets/sync.rs index 9c922a0..7c6f094 100644 --- a/crates/bitwarden-sm/src/secrets/sync.rs +++ b/crates/bitwarden-sm/src/secrets/sync.rs @@ -1,5 +1,9 @@ use bitwarden_api_api::models::SecretsSyncResponseModel; -use bitwarden_core::{client::encryption_settings::EncryptionSettings, require, Client, Error}; +use bitwarden_core::{ + key_management::{AsymmetricKeyRef, SymmetricKeyRef}, + require, Client, Error, +}; +use bitwarden_crypto::service::CryptoServiceContext; use chrono::{DateTime, Utc}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -30,9 +34,9 @@ pub(crate) async fn sync_secrets( ) .await?; - let enc = client.internal.get_encryption_settings()?; + let mut ctx = client.internal.get_crypto_service().context(); - SecretsSyncResponse::process_response(res, &enc) + SecretsSyncResponse::process_response(res, &mut ctx) } #[derive(Serialize, Deserialize, Debug, JsonSchema)] @@ -45,7 +49,7 @@ pub struct SecretsSyncResponse { impl SecretsSyncResponse { pub(crate) fn process_response( response: SecretsSyncResponseModel, - enc: &EncryptionSettings, + ctx: &mut CryptoServiceContext, ) -> Result { let has_changes = require!(response.has_changes); @@ -54,7 +58,7 @@ impl SecretsSyncResponse { .data .unwrap_or_default() .into_iter() - .map(|r| SecretResponse::process_base_response(r, enc)) + .map(|r| SecretResponse::process_base_response(r, ctx)) .collect::>()?; return Ok(SecretsSyncResponse { has_changes, diff --git a/crates/bitwarden-sm/src/secrets/update.rs b/crates/bitwarden-sm/src/secrets/update.rs index b80c572..b68f7ae 100644 --- a/crates/bitwarden-sm/src/secrets/update.rs +++ b/crates/bitwarden-sm/src/secrets/update.rs @@ -1,6 +1,6 @@ use bitwarden_api_api::models::SecretUpdateRequestModel; -use bitwarden_core::{validate_only_whitespaces, Client, Error}; -use bitwarden_crypto::KeyEncryptable; +use bitwarden_core::{key_management::SymmetricKeyRef, validate_only_whitespaces, Client, Error}; +use bitwarden_crypto::Encryptable; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -30,22 +30,31 @@ pub(crate) async fn update_secret( ) -> Result { input.validate()?; - let enc = client.internal.get_encryption_settings()?; - let key = enc.get_key(&Some(input.organization_id))?; - - let secret = Some(SecretUpdateRequestModel { - key: input.key.clone().trim().encrypt_with_key(key)?.to_string(), - value: input.value.clone().encrypt_with_key(key)?.to_string(), - note: input.note.clone().trim().encrypt_with_key(key)?.to_string(), - project_ids: input.project_ids.clone(), - access_policies_requests: None, - }); + let secret = { + // Context is not Send, so we can't use it across an await point + let mut ctx = client.internal.get_crypto_service().context(); + let key = SymmetricKeyRef::Organization(input.organization_id); + + Some(SecretUpdateRequestModel { + key: input.key.clone().trim().encrypt(&mut ctx, key)?.to_string(), + value: input.value.clone().encrypt(&mut ctx, key)?.to_string(), + note: input + .note + .clone() + .trim() + .encrypt(&mut ctx, key)? + .to_string(), + project_ids: input.project_ids.clone(), + access_policies_requests: None, + }) + }; let config = client.internal.get_api_configurations().await; let res = bitwarden_api_api::apis::secrets_api::secrets_id_put(&config.api, input.id, secret).await?; - SecretResponse::process_response(res, &enc) + let mut ctx = client.internal.get_crypto_service().context(); + SecretResponse::process_response(res, &mut ctx) } #[cfg(test)] diff --git a/crates/bitwarden-vault/src/cipher/attachment.rs b/crates/bitwarden-vault/src/cipher/attachment.rs index bf58987..b24759d 100644 --- a/crates/bitwarden-vault/src/cipher/attachment.rs +++ b/crates/bitwarden-vault/src/cipher/attachment.rs @@ -1,5 +1,6 @@ +use bitwarden_core::key_management::{AsymmetricKeyRef, SymmetricKeyRef}; use bitwarden_crypto::{ - CryptoError, EncString, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey, + service::CryptoServiceContext, CryptoError, Decryptable, EncString, Encryptable, UsesKey, }; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -57,21 +58,38 @@ pub struct AttachmentFileView<'a> { pub contents: &'a [u8], } -impl<'a> KeyEncryptable for AttachmentFileView<'a> { - fn encrypt_with_key( - self, - key: &SymmetricCryptoKey, +pub(super) const ATTACHMENT_KEY: SymmetricKeyRef = SymmetricKeyRef::Local("attachment_key"); + +impl<'a> UsesKey for AttachmentFileView<'a> { + fn uses_key(&self) -> SymmetricKeyRef { + self.cipher.uses_key() + } +} + +impl UsesKey for AttachmentFile { + fn uses_key(&self) -> SymmetricKeyRef { + self.cipher.uses_key() + } +} + +impl<'a> Encryptable + for AttachmentFileView<'a> +{ + fn encrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, ) -> Result { - let ciphers_key = Cipher::get_cipher_key(key, &self.cipher.key)?; - let ciphers_key = ciphers_key.as_ref().unwrap_or(key); + let ciphers_key = Cipher::get_cipher_key(ctx, key, &self.cipher.key)?; - let mut attachment = self.attachment; + let mut attachment = self.attachment.clone(); // Because this is a new attachment, we have to generate a key for it, encrypt the contents // with it, and then encrypt the key with the cipher key - let attachment_key = SymmetricCryptoKey::generate(rand::thread_rng()); - let encrypted_contents = self.contents.encrypt_with_key(&attachment_key)?; - attachment.key = Some(attachment_key.to_vec().encrypt_with_key(ciphers_key)?); + ctx.generate_symmetric_key(ATTACHMENT_KEY)?; + let encrypted_contents = self.contents.encrypt(ctx, ATTACHMENT_KEY)?; + attachment.key = + Some(ctx.encrypt_symmetric_key_with_symmetric_key(ciphers_key, ATTACHMENT_KEY)?); let contents = encrypted_contents.to_buffer()?; @@ -80,7 +98,7 @@ impl<'a> KeyEncryptable for Attachm attachment.size_name = Some(size_name(contents.len())); Ok(AttachmentEncryptResult { - attachment: attachment.encrypt_with_key(ciphers_key)?, + attachment: attachment.encrypt(ctx, ciphers_key)?, contents, }) } @@ -96,45 +114,63 @@ fn size_name(size: usize) -> String { format!("{} {}", size_round, units[unit]) } -impl KeyDecryptable> for AttachmentFile { - fn decrypt_with_key(&self, key: &SymmetricCryptoKey) -> Result, CryptoError> { - let ciphers_key = Cipher::get_cipher_key(key, &self.cipher.key)?; - let ciphers_key = ciphers_key.as_ref().unwrap_or(key); +impl Decryptable> for AttachmentFile { + fn decrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result, CryptoError> { + let ciphers_key = Cipher::get_cipher_key(ctx, key, &self.cipher.key)?; // Version 2 or 3, `AttachmentKey` or `CipherKey(AttachmentKey)` if let Some(attachment_key) = &self.attachment.key { - let mut content_key: Vec = attachment_key.decrypt_with_key(ciphers_key)?; - let content_key = SymmetricCryptoKey::try_from(content_key.as_mut_slice())?; + ctx.decrypt_symmetric_key_with_symmetric_key( + ciphers_key, + ATTACHMENT_KEY, + attachment_key, + )?; - self.contents.decrypt_with_key(&content_key) + self.contents.decrypt(ctx, ATTACHMENT_KEY) } else { // Legacy attachment version 1, use user/org key - self.contents.decrypt_with_key(key) + self.contents.decrypt(ctx, key) } } } -impl KeyEncryptable for AttachmentView { - fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { +impl Encryptable + for AttachmentView +{ + fn encrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(Attachment { - id: self.id, - url: self.url, - size: self.size, - size_name: self.size_name, - file_name: self.file_name.encrypt_with_key(key)?, - key: self.key, + id: self.id.clone(), + url: self.url.clone(), + size: self.size.clone(), + size_name: self.size_name.clone(), + file_name: self.file_name.encrypt(ctx, key)?, + key: self.key.clone(), }) } } -impl KeyDecryptable for Attachment { - fn decrypt_with_key(&self, key: &SymmetricCryptoKey) -> Result { +impl Decryptable + for Attachment +{ + fn decrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(AttachmentView { id: self.id.clone(), url: self.url.clone(), size: self.size.clone(), size_name: self.size_name.clone(), - file_name: self.file_name.decrypt_with_key(key)?, + file_name: self.file_name.decrypt(ctx, key)?, key: self.key.clone(), }) } @@ -160,7 +196,8 @@ impl TryFrom for Attachment #[cfg(test)] mod tests { use base64::{engine::general_purpose::STANDARD, Engine}; - use bitwarden_crypto::{EncString, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey}; + use bitwarden_core::key_management::{create_test_crypto_with_user_key, SymmetricKeyRef}; + use bitwarden_crypto::{Decryptable, EncString, Encryptable, SymmetricCryptoKey}; use crate::{ cipher::cipher::{CipherRepromptType, CipherType}, @@ -182,6 +219,7 @@ mod tests { #[test] fn test_encrypt_attachment() { let user_key: SymmetricCryptoKey = "w2LO+nwV4oxwswVYCxlOfRUseXfvU03VzvKQHrqeklPgiMZrspUe6sOBToCnDn9Ay0tuCBn8ykVVRb7PWhub2Q==".to_string().try_into().unwrap(); + let crypto = create_test_crypto_with_user_key(user_key); let attachment = AttachmentView { id: None, @@ -226,7 +264,9 @@ mod tests { contents: contents.as_slice(), }; - let result = attachment_file.encrypt_with_key(&user_key).unwrap(); + let result = attachment_file + .encrypt(&mut crypto.context(), SymmetricKeyRef::User) + .unwrap(); assert_eq!(result.contents.len(), 161); assert_eq!(result.attachment.size, Some("161".into())); @@ -236,6 +276,7 @@ mod tests { #[test] fn test_attachment_key() { let user_key: SymmetricCryptoKey = "w2LO+nwV4oxwswVYCxlOfRUseXfvU03VzvKQHrqeklPgiMZrspUe6sOBToCnDn9Ay0tuCBn8ykVVRb7PWhub2Q==".to_string().try_into().unwrap(); + let crypto = create_test_crypto_with_user_key(user_key); let attachment = Attachment { id: None, @@ -282,7 +323,7 @@ mod tests { attachment, contents: EncString::from_buffer(&enc_file).unwrap(), } - .decrypt_with_key(&user_key) + .decrypt(&mut crypto.context(), SymmetricKeyRef::User) .unwrap(); assert_eq!(dec, original); @@ -291,6 +332,7 @@ mod tests { #[test] fn test_attachment_without_key() { let user_key: SymmetricCryptoKey = "w2LO+nwV4oxwswVYCxlOfRUseXfvU03VzvKQHrqeklPgiMZrspUe6sOBToCnDn9Ay0tuCBn8ykVVRb7PWhub2Q==".to_string().try_into().unwrap(); + let crypto = create_test_crypto_with_user_key(user_key); let attachment = Attachment { id: None, @@ -337,7 +379,7 @@ mod tests { attachment, contents: EncString::from_buffer(&enc_file).unwrap(), } - .decrypt_with_key(&user_key) + .decrypt(&mut crypto.context(), SymmetricKeyRef::User) .unwrap(); assert_eq!(dec, original); diff --git a/crates/bitwarden-vault/src/cipher/card.rs b/crates/bitwarden-vault/src/cipher/card.rs index 5a2396d..96cf94a 100644 --- a/crates/bitwarden-vault/src/cipher/card.rs +++ b/crates/bitwarden-vault/src/cipher/card.rs @@ -1,6 +1,7 @@ use bitwarden_api_api::models::CipherCardModel; +use bitwarden_core::key_management::{AsymmetricKeyRef, SymmetricKeyRef}; use bitwarden_crypto::{ - CryptoError, EncString, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey, + service::CryptoServiceContext, CryptoError, Decryptable, EncString, Encryptable, }; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -31,28 +32,36 @@ pub struct CardView { pub number: Option, } -impl KeyEncryptable for CardView { - fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { +impl Encryptable for CardView { + fn encrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(Card { - cardholder_name: self.cardholder_name.encrypt_with_key(key)?, - exp_month: self.exp_month.encrypt_with_key(key)?, - exp_year: self.exp_year.encrypt_with_key(key)?, - code: self.code.encrypt_with_key(key)?, - brand: self.brand.encrypt_with_key(key)?, - number: self.number.encrypt_with_key(key)?, + cardholder_name: self.cardholder_name.encrypt(ctx, key)?, + exp_month: self.exp_month.encrypt(ctx, key)?, + exp_year: self.exp_year.encrypt(ctx, key)?, + code: self.code.encrypt(ctx, key)?, + brand: self.brand.encrypt(ctx, key)?, + number: self.number.encrypt(ctx, key)?, }) } } -impl KeyDecryptable for Card { - fn decrypt_with_key(&self, key: &SymmetricCryptoKey) -> Result { +impl Decryptable for Card { + fn decrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(CardView { - cardholder_name: self.cardholder_name.decrypt_with_key(key).ok().flatten(), - exp_month: self.exp_month.decrypt_with_key(key).ok().flatten(), - exp_year: self.exp_year.decrypt_with_key(key).ok().flatten(), - code: self.code.decrypt_with_key(key).ok().flatten(), - brand: self.brand.decrypt_with_key(key).ok().flatten(), - number: self.number.decrypt_with_key(key).ok().flatten(), + cardholder_name: self.cardholder_name.decrypt(ctx, key).ok().flatten(), + exp_month: self.exp_month.decrypt(ctx, key).ok().flatten(), + exp_year: self.exp_year.decrypt(ctx, key).ok().flatten(), + code: self.code.decrypt(ctx, key).ok().flatten(), + brand: self.brand.decrypt(ctx, key).ok().flatten(), + number: self.number.decrypt(ctx, key).ok().flatten(), }) } } diff --git a/crates/bitwarden-vault/src/cipher/cipher.rs b/crates/bitwarden-vault/src/cipher/cipher.rs index 846d332..f326bf5 100644 --- a/crates/bitwarden-vault/src/cipher/cipher.rs +++ b/crates/bitwarden-vault/src/cipher/cipher.rs @@ -1,8 +1,10 @@ use bitwarden_api_api::models::CipherDetailsResponseModel; -use bitwarden_core::{require, MissingFieldError, VaultLocked}; +use bitwarden_core::{ + key_management::{AsymmetricKeyRef, SymmetricKeyRef}, + require, MissingFieldError, VaultLocked, +}; use bitwarden_crypto::{ - CryptoError, EncString, KeyContainer, KeyDecryptable, KeyEncryptable, LocateKey, - SymmetricCryptoKey, + service::CryptoServiceContext, CryptoError, Decryptable, EncString, Encryptable, UsesKey, }; use chrono::{DateTime, Utc}; use schemars::JsonSchema; @@ -12,7 +14,8 @@ use thiserror::Error; use uuid::Uuid; use super::{ - attachment, card, field, identity, + attachment::{self, ATTACHMENT_KEY}, + card, field, identity, local_data::{LocalData, LocalDataView}, secure_note, ssh_key, }; @@ -173,17 +176,20 @@ pub struct CipherListView { pub revision_date: DateTime, } +const CIPHER_KEY: SymmetricKeyRef = SymmetricKeyRef::Local("cipher_key"); +const NEW_CIPHER_KEY: SymmetricKeyRef = SymmetricKeyRef::Local("new_cipher_key"); + impl CipherListView { + // TODO: Don't return the TOTP key directly, store it in the context pub(crate) fn get_totp_key( self, - enc: &dyn KeyContainer, + ctx: &mut CryptoServiceContext, ) -> Result, CryptoError> { - let key = self.locate_key(enc, &None)?; - let cipher_key = Cipher::get_cipher_key(key, &self.key)?; - let key = cipher_key.as_ref().unwrap_or(key); + let key = self.uses_key(); + let cipher_key = Cipher::get_cipher_key(ctx, key, &self.key)?; let totp = if let CipherListViewType::Login { totp, .. } = self.r#type { - totp.decrypt_with_key(key)? + totp.decrypt(ctx, cipher_key)? } else { None }; @@ -192,50 +198,58 @@ impl CipherListView { } } -impl KeyEncryptable for CipherView { - fn encrypt_with_key(mut self, key: &SymmetricCryptoKey) -> Result { - let ciphers_key = Cipher::get_cipher_key(key, &self.key)?; - let key = ciphers_key.as_ref().unwrap_or(key); +impl Encryptable for CipherView { + fn encrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { + let key: SymmetricKeyRef = Cipher::get_cipher_key(ctx, key, &self.key)?; + + let mut cipher_view = self.clone(); // For compatibility reasons, we only create checksums for ciphers that have a key - if ciphers_key.is_some() { - self.generate_checksums(); + if cipher_view.key.is_some() { + cipher_view.generate_checksums(); } Ok(Cipher { - id: self.id, - organization_id: self.organization_id, - folder_id: self.folder_id, - collection_ids: self.collection_ids, - key: self.key, - name: self.name.encrypt_with_key(key)?, - notes: self.notes.encrypt_with_key(key)?, - r#type: self.r#type, - login: self.login.encrypt_with_key(key)?, - identity: self.identity.encrypt_with_key(key)?, - card: self.card.encrypt_with_key(key)?, - secure_note: self.secure_note.encrypt_with_key(key)?, - ssh_key: self.ssh_key.encrypt_with_key(key)?, - favorite: self.favorite, - reprompt: self.reprompt, - organization_use_totp: self.organization_use_totp, - edit: self.edit, - view_password: self.view_password, - local_data: self.local_data.encrypt_with_key(key)?, - attachments: self.attachments.encrypt_with_key(key)?, - fields: self.fields.encrypt_with_key(key)?, - password_history: self.password_history.encrypt_with_key(key)?, - creation_date: self.creation_date, - deleted_date: self.deleted_date, - revision_date: self.revision_date, + id: cipher_view.id, + organization_id: cipher_view.organization_id, + folder_id: cipher_view.folder_id, + collection_ids: cipher_view.collection_ids.clone(), + key: cipher_view.key.clone(), + name: cipher_view.name.encrypt(ctx, key)?, + notes: cipher_view.notes.encrypt(ctx, key)?, + r#type: cipher_view.r#type, + login: cipher_view.login.encrypt(ctx, key)?, + identity: cipher_view.identity.encrypt(ctx, key)?, + card: cipher_view.card.encrypt(ctx, key)?, + secure_note: cipher_view.secure_note.encrypt(ctx, key)?, + ssh_key: cipher_view.ssh_key.encrypt(ctx, key)?, + favorite: cipher_view.favorite, + reprompt: cipher_view.reprompt, + organization_use_totp: cipher_view.organization_use_totp, + edit: cipher_view.edit, + view_password: cipher_view.view_password, + local_data: cipher_view.local_data.encrypt(ctx, key)?, + attachments: cipher_view.attachments.encrypt(ctx, key)?, + fields: cipher_view.fields.encrypt(ctx, key)?, + password_history: cipher_view.password_history.encrypt(ctx, key)?, + creation_date: cipher_view.creation_date, + deleted_date: cipher_view.deleted_date, + revision_date: cipher_view.revision_date, }) } } -impl KeyDecryptable for Cipher { - fn decrypt_with_key(&self, key: &SymmetricCryptoKey) -> Result { - let ciphers_key = Cipher::get_cipher_key(key, &self.key)?; - let key = ciphers_key.as_ref().unwrap_or(key); +impl Decryptable for Cipher { + fn decrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { + let key: SymmetricKeyRef = Cipher::get_cipher_key(ctx, key, &self.key)?; let mut cipher = CipherView { id: self.id, @@ -243,30 +257,30 @@ impl KeyDecryptable for Cipher { folder_id: self.folder_id, collection_ids: self.collection_ids.clone(), key: self.key.clone(), - name: self.name.decrypt_with_key(key).ok().unwrap_or_default(), - notes: self.notes.decrypt_with_key(key).ok().flatten(), + name: self.name.decrypt(ctx, key).ok().unwrap_or_default(), + notes: self.notes.decrypt(ctx, key).ok().flatten(), r#type: self.r#type, - login: self.login.decrypt_with_key(key).ok().flatten(), - identity: self.identity.decrypt_with_key(key).ok().flatten(), - card: self.card.decrypt_with_key(key).ok().flatten(), - secure_note: self.secure_note.decrypt_with_key(key).ok().flatten(), - ssh_key: self.ssh_key.decrypt_with_key(key).ok().flatten(), + login: self.login.decrypt(ctx, key).ok().flatten(), + identity: self.identity.decrypt(ctx, key).ok().flatten(), + card: self.card.decrypt(ctx, key).ok().flatten(), + secure_note: self.secure_note.decrypt(ctx, key).ok().flatten(), + ssh_key: self.ssh_key.decrypt(ctx, key).ok().flatten(), favorite: self.favorite, reprompt: self.reprompt, organization_use_totp: self.organization_use_totp, edit: self.edit, view_password: self.view_password, - local_data: self.local_data.decrypt_with_key(key).ok().flatten(), - attachments: self.attachments.decrypt_with_key(key).ok().flatten(), - fields: self.fields.decrypt_with_key(key).ok().flatten(), - password_history: self.password_history.decrypt_with_key(key).ok().flatten(), + local_data: self.local_data.decrypt(ctx, key).ok().flatten(), + attachments: self.attachments.decrypt(ctx, key).ok().flatten(), + fields: self.fields.decrypt(ctx, key).ok().flatten(), + password_history: self.password_history.decrypt(ctx, key).ok().flatten(), creation_date: self.creation_date, deleted_date: self.deleted_date, revision_date: self.revision_date, }; // For compatibility we only remove URLs with invalid checksums if the cipher has a key - if ciphers_key.is_some() { + if self.key.is_some() { cipher.remove_invalid_checksums(); } @@ -280,25 +294,30 @@ impl Cipher { /// in which case this will return Ok(None) and the key associated /// with this cipher's user or organization must be used instead pub(super) fn get_cipher_key( - key: &SymmetricCryptoKey, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, ciphers_key: &Option, - ) -> Result, CryptoError> { - ciphers_key - .as_ref() - .map(|k| { - let mut key: Vec = k.decrypt_with_key(key)?; - SymmetricCryptoKey::try_from(key.as_mut_slice()) - }) - .transpose() + ) -> Result { + match ciphers_key { + Some(ciphers_key) => { + ctx.decrypt_symmetric_key_with_symmetric_key(key, CIPHER_KEY, ciphers_key)?; + Ok(CIPHER_KEY) + } + None => Ok(key), + } } - fn get_decrypted_subtitle(&self, key: &SymmetricCryptoKey) -> Result { + fn get_decrypted_subtitle( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(match self.r#type { CipherType::Login => { let Some(login) = &self.login else { return Ok(String::new()); }; - login.username.decrypt_with_key(key)?.unwrap_or_default() + login.username.decrypt(ctx, key)?.unwrap_or_default() } CipherType::SecureNote => String::new(), CipherType::Card => { @@ -309,11 +328,11 @@ impl Cipher { build_subtitle_card( card.brand .as_ref() - .map(|b| b.decrypt_with_key(key)) + .map(|b| b.decrypt(ctx, key)) .transpose()?, card.number .as_ref() - .map(|n| n.decrypt_with_key(key)) + .map(|n| n.decrypt(ctx, key)) .transpose()?, ) } @@ -326,12 +345,12 @@ impl Cipher { identity .first_name .as_ref() - .map(|f| f.decrypt_with_key(key)) + .map(|f| f.decrypt(ctx, key)) .transpose()?, identity .last_name .as_ref() - .map(|l| l.decrypt_with_key(key)) + .map(|l| l.decrypt(ctx, key)) .transpose()?, ) } @@ -343,7 +362,7 @@ impl Cipher { ssh_key .fingerprint .as_ref() - .map(|c| c.decrypt_with_key(key)) + .map(|c| c.decrypt(ctx, key)) .transpose()? .unwrap_or_default() } @@ -408,16 +427,18 @@ fn build_subtitle_identity(first_name: Option, last_name: Option } impl CipherView { - pub fn generate_cipher_key(&mut self, key: &SymmetricCryptoKey) -> Result<(), CryptoError> { - let old_ciphers_key = Cipher::get_cipher_key(key, &self.key)?; - let old_key = old_ciphers_key.as_ref().unwrap_or(key); - - let new_key = SymmetricCryptoKey::generate(rand::thread_rng()); + pub fn generate_cipher_key( + &mut self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result<(), CryptoError> { + let old_key = Cipher::get_cipher_key(ctx, key, &self.key)?; + let new_key = ctx.generate_symmetric_key(NEW_CIPHER_KEY)?; - self.reencrypt_attachment_keys(old_key, &new_key)?; - self.reencrypt_fido2_credentials(old_key, &new_key)?; + self.reencrypt_attachment_keys(ctx, old_key, new_key)?; + self.reencrypt_fido2_credentials(ctx, old_key, new_key)?; - self.key = Some(new_key.to_vec().encrypt_with_key(key)?); + self.key = Some(ctx.encrypt_symmetric_key_with_symmetric_key(key, NEW_CIPHER_KEY)?); Ok(()) } @@ -437,14 +458,20 @@ impl CipherView { fn reencrypt_attachment_keys( &mut self, - old_key: &SymmetricCryptoKey, - new_key: &SymmetricCryptoKey, + ctx: &mut CryptoServiceContext, + old_key: SymmetricKeyRef, + new_key: SymmetricKeyRef, ) -> Result<(), CryptoError> { if let Some(attachments) = &mut self.attachments { for attachment in attachments { if let Some(attachment_key) = &mut attachment.key { - let dec_attachment_key: Vec = attachment_key.decrypt_with_key(old_key)?; - *attachment_key = dec_attachment_key.encrypt_with_key(new_key)?; + ctx.decrypt_symmetric_key_with_symmetric_key( + old_key, + ATTACHMENT_KEY, + attachment_key, + )?; + *attachment_key = + ctx.encrypt_symmetric_key_with_symmetric_key(new_key, ATTACHMENT_KEY)?; } } } @@ -453,32 +480,31 @@ impl CipherView { pub fn decrypt_fido2_credentials( &self, - enc: &dyn KeyContainer, + ctx: &mut CryptoServiceContext, ) -> Result, CipherError> { - let key = self.locate_key(enc, &None)?; - let cipher_key = Cipher::get_cipher_key(key, &self.key)?; - - let key = cipher_key.as_ref().unwrap_or(key); + let key = self.uses_key(); + let cipher_key = Cipher::get_cipher_key(ctx, key, &self.key)?; Ok(self .login .as_ref() .and_then(|l| l.fido2_credentials.as_ref()) - .map(|f| f.decrypt_with_key(key)) + .map(|f| f.decrypt(ctx, cipher_key)) .transpose()? .unwrap_or_default()) } fn reencrypt_fido2_credentials( &mut self, - old_key: &SymmetricCryptoKey, - new_key: &SymmetricCryptoKey, + ctx: &mut CryptoServiceContext, + old_key: SymmetricKeyRef, + new_key: SymmetricKeyRef, ) -> Result<(), CryptoError> { if let Some(login) = self.login.as_mut() { if let Some(fido2_credentials) = &mut login.fido2_credentials { let dec_fido2_credentials: Vec = - fido2_credentials.decrypt_with_key(old_key)?; - *fido2_credentials = dec_fido2_credentials.encrypt_with_key(new_key)?; + fido2_credentials.decrypt(ctx, old_key)?; + *fido2_credentials = dec_fido2_credentials.encrypt(ctx, new_key)?; } } Ok(()) @@ -486,12 +512,12 @@ impl CipherView { pub fn move_to_organization( &mut self, - enc: &dyn KeyContainer, + ctx: &mut CryptoServiceContext, organization_id: Uuid, ) -> Result<(), CipherError> { - let old_key = enc.get_key(&self.organization_id)?; + let old_key = self.uses_key(); - let new_key = enc.get_key(&Some(organization_id))?; + let new_key = SymmetricKeyRef::Organization(organization_id); // If any attachment is missing a key we can't reencrypt the attachment keys if self.attachments.iter().flatten().any(|a| a.key.is_none()) { @@ -500,12 +526,12 @@ impl CipherView { // If the cipher has a key, we need to re-encrypt it with the new organization key if let Some(cipher_key) = &mut self.key { - let dec_cipher_key: Vec = cipher_key.decrypt_with_key(old_key)?; - *cipher_key = dec_cipher_key.encrypt_with_key(new_key)?; + ctx.decrypt_symmetric_key_with_symmetric_key(old_key, CIPHER_KEY, cipher_key)?; + *cipher_key = ctx.encrypt_symmetric_key_with_symmetric_key(new_key, CIPHER_KEY)?; } else { // If the cipher does not have a key, we need to reencrypt all attachment keys - self.reencrypt_attachment_keys(old_key, new_key)?; - self.reencrypt_fido2_credentials(old_key, new_key)?; + self.reencrypt_attachment_keys(ctx, old_key, new_key)?; + self.reencrypt_fido2_credentials(ctx, old_key, new_key)?; } self.organization_id = Some(organization_id); @@ -514,40 +540,38 @@ impl CipherView { pub fn set_new_fido2_credentials( &mut self, - enc: &dyn KeyContainer, + ctx: &mut CryptoServiceContext, creds: Vec, ) -> Result<(), CipherError> { - let key = enc.get_key(&self.organization_id)?; + let key = self.uses_key(); + let ciphers_key = Cipher::get_cipher_key(ctx, key, &self.key)?; - let ciphers_key = Cipher::get_cipher_key(key, &self.key)?; - let ciphers_key = ciphers_key.as_ref().unwrap_or(key); - - require!(self.login.as_mut()).fido2_credentials = - Some(creds.encrypt_with_key(ciphers_key)?); + require!(self.login.as_mut()).fido2_credentials = Some(creds.encrypt(ctx, ciphers_key)?); Ok(()) } pub fn get_fido2_credentials( &self, - enc: &dyn KeyContainer, + ctx: &mut CryptoServiceContext, ) -> Result, CipherError> { - let key = enc.get_key(&self.organization_id)?; - - let ciphers_key = Cipher::get_cipher_key(key, &self.key)?; - let ciphers_key = ciphers_key.as_ref().unwrap_or(key); + let key = self.uses_key(); + let ciphers_key = Cipher::get_cipher_key(ctx, key, &self.key)?; let login = require!(self.login.as_ref()); let creds = require!(login.fido2_credentials.as_ref()); - let res = creds.decrypt_with_key(ciphers_key)?; + let res = creds.decrypt(ctx, ciphers_key)?; Ok(res) } } -impl KeyDecryptable for Cipher { - fn decrypt_with_key(&self, key: &SymmetricCryptoKey) -> Result { - let ciphers_key = Cipher::get_cipher_key(key, &self.key)?; - let key = ciphers_key.as_ref().unwrap_or(key); +impl Decryptable for Cipher { + fn decrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { + let key = Cipher::get_cipher_key(ctx, key, &self.key)?; Ok(CipherListView { id: self.id, @@ -555,8 +579,11 @@ impl KeyDecryptable for Cipher { folder_id: self.folder_id, collection_ids: self.collection_ids.clone(), key: self.key.clone(), - name: self.name.decrypt_with_key(key).ok().unwrap_or_default(), - sub_title: self.get_decrypted_subtitle(key).ok().unwrap_or_default(), + name: self.name.decrypt(ctx, key).ok().unwrap_or_default(), + sub_title: self + .get_decrypted_subtitle(ctx, key) + .ok() + .unwrap_or_default(), r#type: match self.r#type { CipherType::Login => { let login = self @@ -589,31 +616,30 @@ impl KeyDecryptable for Cipher { } } -impl LocateKey for Cipher { - fn locate_key<'a>( - &self, - enc: &'a dyn KeyContainer, - _: &Option, - ) -> Result<&'a SymmetricCryptoKey, CryptoError> { - enc.get_key(&self.organization_id) +impl UsesKey for Cipher { + fn uses_key(&self) -> SymmetricKeyRef { + match self.organization_id { + Some(organization_id) => SymmetricKeyRef::Organization(organization_id), + None => SymmetricKeyRef::User, + } } } -impl LocateKey for CipherView { - fn locate_key<'a>( - &self, - enc: &'a dyn KeyContainer, - _: &Option, - ) -> Result<&'a SymmetricCryptoKey, CryptoError> { - enc.get_key(&self.organization_id) + +impl UsesKey for CipherView { + fn uses_key(&self) -> SymmetricKeyRef { + match self.organization_id { + Some(organization_id) => SymmetricKeyRef::Organization(organization_id), + None => SymmetricKeyRef::User, + } } } -impl LocateKey for CipherListView { - fn locate_key<'a>( - &self, - enc: &'a dyn KeyContainer, - _: &Option, - ) -> Result<&'a SymmetricCryptoKey, CryptoError> { - enc.get_key(&self.organization_id) + +impl UsesKey for CipherListView { + fn uses_key(&self) -> SymmetricKeyRef { + match self.organization_id { + Some(organization_id) => SymmetricKeyRef::Organization(organization_id), + None => SymmetricKeyRef::User, + } } } @@ -687,10 +713,11 @@ impl From for CipherRepromptType #[cfg(test)] mod tests { - - use std::collections::HashMap; - use attachment::AttachmentView; + use bitwarden_core::key_management::{ + create_test_crypto_with_user_and_org_key, create_test_crypto_with_user_key, + }; + use bitwarden_crypto::SymmetricCryptoKey; use ssh_key::SshKey; use super::*; @@ -734,20 +761,23 @@ mod tests { } } - fn generate_fido2(key: &SymmetricCryptoKey) -> Fido2Credential { + fn generate_fido2( + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Fido2Credential { Fido2Credential { - credential_id: "123".to_string().encrypt_with_key(key).unwrap(), - key_type: "public-key".to_string().encrypt_with_key(key).unwrap(), - key_algorithm: "ECDSA".to_string().encrypt_with_key(key).unwrap(), - key_curve: "P-256".to_string().encrypt_with_key(key).unwrap(), - key_value: "123".to_string().encrypt_with_key(key).unwrap(), - rp_id: "123".to_string().encrypt_with_key(key).unwrap(), + credential_id: "123".to_string().encrypt(ctx, key).unwrap(), + key_type: "public-key".to_string().encrypt(ctx, key).unwrap(), + key_algorithm: "ECDSA".to_string().encrypt(ctx, key).unwrap(), + key_curve: "P-256".to_string().encrypt(ctx, key).unwrap(), + key_value: "123".to_string().encrypt(ctx, key).unwrap(), + rp_id: "123".to_string().encrypt(ctx, key).unwrap(), user_handle: None, user_name: None, - counter: "123".to_string().encrypt_with_key(key).unwrap(), + counter: "123".to_string().encrypt(ctx, key).unwrap(), rp_name: None, user_display_name: None, - discoverable: "true".to_string().encrypt_with_key(key).unwrap(), + discoverable: "true".to_string().encrypt(ctx, key).unwrap(), creation_date: "2024-06-07T14:12:36.150Z".parse().unwrap(), } } @@ -755,6 +785,8 @@ mod tests { #[test] fn test_decrypt_cipher_list_view() { let key: SymmetricCryptoKey = "w2LO+nwV4oxwswVYCxlOfRUseXfvU03VzvKQHrqeklPgiMZrspUe6sOBToCnDn9Ay0tuCBn8ykVVRb7PWhub2Q==".to_string().try_into().unwrap(); + let crypto = create_test_crypto_with_user_key(key.clone()); + let mut ctx = crypto.context(); let cipher = Cipher { id: Some("090c19ea-a61a-4df6-8963-262b97bc6266".parse().unwrap()), @@ -772,7 +804,7 @@ mod tests { uris: None, totp: Some("2.hqdioUAc81FsKQmO1XuLQg==|oDRdsJrQjoFu9NrFVy8tcJBAFKBx95gHaXZnWdXbKpsxWnOr2sKipIG43pKKUFuq|3gKZMiboceIB5SLVOULKg2iuyu6xzos22dfJbvx0EHk=".parse().unwrap()), autofill_on_page_load: None, - fido2_credentials: Some(vec![generate_fido2(&key)]), + fido2_credentials: Some(vec![generate_fido2(&mut ctx, SymmetricKeyRef::User)]), }), identity: None, card: None, @@ -792,7 +824,7 @@ mod tests { revision_date: "2024-01-30T17:55:36.150Z".parse().unwrap(), }; - let view: CipherListView = cipher.decrypt_with_key(&key).unwrap(); + let view: CipherListView = cipher.decrypt(&mut ctx, SymmetricKeyRef::User).unwrap(); assert_eq!( view, @@ -823,22 +855,30 @@ mod tests { #[test] fn test_generate_cipher_key() { let key = SymmetricCryptoKey::generate(rand::thread_rng()); + let crypto = create_test_crypto_with_user_key(key.clone()); + let mut ctx = crypto.context(); let original_cipher = generate_cipher(); // Check that the cipher gets encrypted correctly without it's own key let cipher = generate_cipher(); - let no_key_cipher_enc = cipher.encrypt_with_key(&key).unwrap(); - let no_key_cipher_dec: CipherView = no_key_cipher_enc.decrypt_with_key(&key).unwrap(); + let no_key_cipher_enc = cipher.encrypt(&mut ctx, SymmetricKeyRef::User).unwrap(); + let no_key_cipher_dec: CipherView = no_key_cipher_enc + .decrypt(&mut ctx, SymmetricKeyRef::User) + .unwrap(); assert!(no_key_cipher_dec.key.is_none()); assert_eq!(no_key_cipher_dec.name, original_cipher.name); let mut cipher = generate_cipher(); - cipher.generate_cipher_key(&key).unwrap(); + cipher + .generate_cipher_key(&mut ctx, SymmetricKeyRef::User) + .unwrap(); // Check that the cipher gets encrypted correctly when it's assigned it's own key - let key_cipher_enc = cipher.encrypt_with_key(&key).unwrap(); - let key_cipher_dec: CipherView = key_cipher_enc.decrypt_with_key(&key).unwrap(); + let key_cipher_enc = cipher.encrypt(&mut ctx, SymmetricKeyRef::User).unwrap(); + let key_cipher_dec: CipherView = key_cipher_enc + .decrypt(&mut ctx, SymmetricKeyRef::User) + .unwrap(); assert!(key_cipher_dec.key.is_some()); assert_eq!(key_cipher_dec.name, original_cipher.name); } @@ -846,22 +886,36 @@ mod tests { #[test] fn test_generate_cipher_key_when_a_cipher_key_already_exists() { let key = SymmetricCryptoKey::generate(rand::thread_rng()); + let crypto = create_test_crypto_with_user_key(key.clone()); + let mut ctx = crypto.context(); let cipher_key = SymmetricCryptoKey::generate(rand::thread_rng()); - let cipher_key = cipher_key.to_vec().encrypt_with_key(&key).unwrap(); + let cipher_key = cipher_key + .to_vec() + .as_slice() + .encrypt(&mut ctx, SymmetricKeyRef::User) + .unwrap(); let mut original_cipher = generate_cipher(); original_cipher.key = Some(cipher_key.clone()); - original_cipher.generate_cipher_key(&key).unwrap(); + original_cipher + .generate_cipher_key(&mut ctx, SymmetricKeyRef::User) + .unwrap(); // Make sure that the cipher key is decryptable - let _: Vec = original_cipher.key.unwrap().decrypt_with_key(&key).unwrap(); + let _: Vec = original_cipher + .key + .unwrap() + .decrypt(&mut ctx, SymmetricKeyRef::User) + .unwrap(); } #[test] fn test_generate_cipher_key_ignores_attachments_without_key() { let key = SymmetricCryptoKey::generate(rand::thread_rng()); + let crypto = create_test_crypto_with_user_key(key.clone()); + let mut ctx = crypto.context(); let mut cipher = generate_cipher(); let attachment = AttachmentView { @@ -874,44 +928,39 @@ mod tests { }; cipher.attachments = Some(vec![attachment]); - cipher.generate_cipher_key(&key).unwrap(); + cipher + .generate_cipher_key(&mut ctx, SymmetricKeyRef::User) + .unwrap(); assert!(cipher.attachments.unwrap()[0].key.is_none()); } - struct MockKeyContainer(HashMap, SymmetricCryptoKey>); - impl KeyContainer for MockKeyContainer { - fn get_key<'a>( - &'a self, - org_id: &Option, - ) -> Result<&'a SymmetricCryptoKey, CryptoError> { - self.0 - .get(org_id) - .ok_or(CryptoError::MissingKey(org_id.unwrap_or_default())) - } - } - #[test] fn test_move_user_cipher_to_org() { let org = uuid::Uuid::new_v4(); - let enc = MockKeyContainer(HashMap::from([ - (None, SymmetricCryptoKey::generate(rand::thread_rng())), - (Some(org), SymmetricCryptoKey::generate(rand::thread_rng())), - ])); + let crypto = create_test_crypto_with_user_and_org_key( + SymmetricCryptoKey::generate(rand::thread_rng()), + org, + SymmetricCryptoKey::generate(rand::thread_rng()), + ); + let mut ctx = crypto.context(); // Create a cipher with a user key let mut cipher = generate_cipher(); cipher - .generate_cipher_key(enc.get_key(&None).unwrap()) + .generate_cipher_key(&mut ctx, SymmetricKeyRef::User) .unwrap(); - cipher.move_to_organization(&enc, org).unwrap(); + cipher.move_to_organization(&mut ctx, org).unwrap(); assert_eq!(cipher.organization_id, Some(org)); // Check that the cipher can be encrypted/decrypted with the new org key - let org_key = enc.get_key(&Some(org)).unwrap(); - let cipher_enc = cipher.encrypt_with_key(org_key).unwrap(); - let cipher_dec: CipherView = cipher_enc.decrypt_with_key(org_key).unwrap(); + let cipher_enc = cipher + .encrypt(&mut ctx, SymmetricKeyRef::Organization(org)) + .unwrap(); + let cipher_dec: CipherView = cipher_enc + .decrypt(&mut ctx, SymmetricKeyRef::Organization(org)) + .unwrap(); assert_eq!(cipher_dec.name, "My test login"); } @@ -920,33 +969,38 @@ mod tests { fn test_move_user_cipher_to_org_manually() { let org = uuid::Uuid::new_v4(); - let enc = MockKeyContainer(HashMap::from([ - (None, SymmetricCryptoKey::generate(rand::thread_rng())), - (Some(org), SymmetricCryptoKey::generate(rand::thread_rng())), - ])); + let crypto = create_test_crypto_with_user_and_org_key( + SymmetricCryptoKey::generate(rand::thread_rng()), + org, + SymmetricCryptoKey::generate(rand::thread_rng()), + ); + let mut ctx = crypto.context(); // Create a cipher with a user key let mut cipher = generate_cipher(); cipher - .generate_cipher_key(enc.get_key(&None).unwrap()) + .generate_cipher_key(&mut ctx, SymmetricKeyRef::User) .unwrap(); cipher.organization_id = Some(org); // Check that the cipher can not be encrypted, as the // cipher key is tied to the user key and not the org key - let org_key = enc.get_key(&Some(org)).unwrap(); - assert!(cipher.encrypt_with_key(org_key).is_err()); + assert!(cipher + .encrypt(&mut ctx, SymmetricKeyRef::Organization(org)) + .is_err()); } #[test] fn test_move_user_cipher_with_attachment_without_key_to_org() { let org = uuid::Uuid::new_v4(); - let enc = MockKeyContainer(HashMap::from([ - (None, SymmetricCryptoKey::generate(rand::thread_rng())), - (Some(org), SymmetricCryptoKey::generate(rand::thread_rng())), - ])); + let crypto = create_test_crypto_with_user_and_org_key( + SymmetricCryptoKey::generate(rand::thread_rng()), + org, + SymmetricCryptoKey::generate(rand::thread_rng()), + ); + let mut ctx = crypto.context(); let mut cipher = generate_cipher(); let attachment = AttachmentView { @@ -960,23 +1014,26 @@ mod tests { cipher.attachments = Some(vec![attachment]); // Neither cipher nor attachment have keys, so the cipher can't be moved - assert!(cipher.move_to_organization(&enc, org).is_err()); + assert!(cipher.move_to_organization(&mut ctx, org).is_err()); } #[test] fn test_move_user_cipher_with_attachment_with_key_to_org() { let org = uuid::Uuid::new_v4(); - let enc = MockKeyContainer(HashMap::from([ - (None, SymmetricCryptoKey::generate(rand::thread_rng())), - (Some(org), SymmetricCryptoKey::generate(rand::thread_rng())), - ])); + let crypto = create_test_crypto_with_user_and_org_key( + SymmetricCryptoKey::generate(rand::thread_rng()), + org, + SymmetricCryptoKey::generate(rand::thread_rng()), + ); + let mut ctx = crypto.context(); // Attachment has a key that is encrypted with the user key, as the cipher has no key itself let attachment_key = SymmetricCryptoKey::generate(rand::thread_rng()); let attachment_key_enc = attachment_key .to_vec() - .encrypt_with_key(enc.get_key(&None).unwrap()) + .as_slice() + .encrypt(&mut ctx, SymmetricKeyRef::User) .unwrap(); let mut cipher = generate_cipher(); @@ -989,10 +1046,10 @@ mod tests { key: Some(attachment_key_enc), }; cipher.attachments = Some(vec![attachment]); - let cred = generate_fido2(enc.get_key(&None).unwrap()); + let cred = generate_fido2(&mut ctx, SymmetricKeyRef::User); cipher.login.as_mut().unwrap().fido2_credentials = Some(vec![cred]); - cipher.move_to_organization(&enc, org).unwrap(); + cipher.move_to_organization(&mut ctx, org).unwrap(); assert!(cipher.key.is_none()); @@ -1000,7 +1057,7 @@ mod tests { // and the value matches with the original attachment key let new_attachment_key = cipher.attachments.unwrap()[0].key.clone().unwrap(); let new_attachment_key_dec: Vec<_> = new_attachment_key - .decrypt_with_key(enc.get_key(&Some(org)).unwrap()) + .decrypt(&mut ctx, SymmetricKeyRef::Organization(org)) .unwrap(); let new_attachment_key_dec: SymmetricCryptoKey = new_attachment_key_dec.try_into().unwrap(); assert_eq!(new_attachment_key_dec.to_vec(), attachment_key.to_vec()); @@ -1012,7 +1069,7 @@ mod tests { .unwrap() .first() .unwrap() - .decrypt_with_key(enc.get_key(&Some(org)).unwrap()) + .decrypt(&mut ctx, SymmetricKeyRef::Organization(org)) .unwrap(); assert_eq!(cred2.credential_id, "123"); @@ -1022,22 +1079,25 @@ mod tests { fn test_move_user_cipher_with_key_with_attachment_with_key_to_org() { let org = uuid::Uuid::new_v4(); - let enc = MockKeyContainer(HashMap::from([ - (None, SymmetricCryptoKey::generate(rand::thread_rng())), - (Some(org), SymmetricCryptoKey::generate(rand::thread_rng())), - ])); + let crypto = create_test_crypto_with_user_and_org_key( + SymmetricCryptoKey::generate(rand::thread_rng()), + org, + SymmetricCryptoKey::generate(rand::thread_rng()), + ); + let mut ctx = crypto.context(); - let cipher_key = SymmetricCryptoKey::generate(rand::thread_rng()); - let cipher_key_enc = cipher_key - .to_vec() - .encrypt_with_key(enc.get_key(&None).unwrap()) + let cipher_key = SymmetricKeyRef::Local("test_cipher_key"); + ctx.generate_symmetric_key(cipher_key).unwrap(); + + let cipher_key_enc = ctx + .encrypt_symmetric_key_with_symmetric_key(SymmetricKeyRef::User, cipher_key) .unwrap(); - // Attachment has a key that is encrypted with the cipher key - let attachment_key = SymmetricCryptoKey::generate(rand::thread_rng()); - let attachment_key_enc = attachment_key - .to_vec() - .encrypt_with_key(&cipher_key) + let attachment_key = SymmetricKeyRef::Local("test_attachment_key"); + ctx.generate_symmetric_key(attachment_key).unwrap(); + + let attachment_key_enc = ctx + .encrypt_symmetric_key_with_symmetric_key(cipher_key, attachment_key) .unwrap(); let mut cipher = generate_cipher(); @@ -1053,21 +1113,23 @@ mod tests { }; cipher.attachments = Some(vec![attachment]); - let cred = generate_fido2(&cipher_key); + let cred = generate_fido2(&mut ctx, cipher_key); cipher.login.as_mut().unwrap().fido2_credentials = Some(vec![cred.clone()]); - cipher.move_to_organization(&enc, org).unwrap(); + cipher.move_to_organization(&mut ctx, org).unwrap(); // Check that the cipher key has been re-encrypted with the org key, let new_cipher_key_dec: Vec<_> = cipher .key .clone() .unwrap() - .decrypt_with_key(enc.get_key(&Some(org)).unwrap()) + .decrypt(&mut ctx, SymmetricKeyRef::Organization(org)) .unwrap(); let new_cipher_key_dec: SymmetricCryptoKey = new_cipher_key_dec.try_into().unwrap(); + #[allow(deprecated)] + let cipher_key = ctx.dangerous_get_symmetric_key(cipher_key).unwrap(); assert_eq!(new_cipher_key_dec.to_vec(), cipher_key.to_vec()); // Check that the attachment key hasn't changed @@ -1187,10 +1249,15 @@ mod tests { #[test] fn test_subtitle_ssh_key() { - let key = "hvBMMb1t79YssFZkpetYsM3deyVuQv4r88Uj9gvYe0+G8EwxvW3v1iywVmSl61iwzd17JW5C/ivzxSP2C9h7Tw==".to_string(); - let key = SymmetricCryptoKey::try_from(key).unwrap(); + let key: SymmetricCryptoKey = "hvBMMb1t79YssFZkpetYsM3deyVuQv4r88Uj9gvYe0+G8EwxvW3v1iywVmSl61iwzd17JW5C/ivzxSP2C9h7Tw==".to_string().try_into().unwrap(); + let crypto = create_test_crypto_with_user_key(key.clone()); + let mut ctx = crypto.context(); + let original_subtitle = "SHA256:1JjFjvPRkj1Gbf2qRP1dgHiIzEuNAEvp+92x99jw3K0".to_string(); - let fingerprint_encrypted = original_subtitle.to_owned().encrypt_with_key(&key).unwrap(); + let fingerprint_encrypted = original_subtitle + .to_owned() + .encrypt(&mut ctx, SymmetricKeyRef::User) + .unwrap(); let ssh_key_cipher = Cipher { id: Some("090c19ea-a61a-4df6-8963-262b97bc6266".parse().unwrap()), organization_id: None, @@ -1200,7 +1267,7 @@ mod tests { key: None, name: "My test ssh key" .to_string() - .encrypt_with_key(&key) + .encrypt(&mut ctx, SymmetricKeyRef::User) .unwrap(), notes: None, login: None, @@ -1225,7 +1292,9 @@ mod tests { deleted_date: None, revision_date: "2024-01-01T00:00:00.000Z".parse().unwrap(), }; - let subtitle = ssh_key_cipher.get_decrypted_subtitle(&key).unwrap(); + let subtitle = ssh_key_cipher + .get_decrypted_subtitle(&mut ctx, SymmetricKeyRef::User) + .unwrap(); assert_eq!(subtitle, original_subtitle); } } diff --git a/crates/bitwarden-vault/src/cipher/field.rs b/crates/bitwarden-vault/src/cipher/field.rs index 6c826d4..cbda150 100644 --- a/crates/bitwarden-vault/src/cipher/field.rs +++ b/crates/bitwarden-vault/src/cipher/field.rs @@ -1,7 +1,10 @@ use bitwarden_api_api::models::CipherFieldModel; -use bitwarden_core::require; +use bitwarden_core::{ + key_management::{AsymmetricKeyRef, SymmetricKeyRef}, + require, +}; use bitwarden_crypto::{ - CryptoError, EncString, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey, + service::CryptoServiceContext, CryptoError, Decryptable, EncString, Encryptable, }; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -42,22 +45,30 @@ pub struct FieldView { pub linked_id: Option, } -impl KeyEncryptable for FieldView { - fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { +impl Encryptable for FieldView { + fn encrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(Field { - name: self.name.encrypt_with_key(key)?, - value: self.value.encrypt_with_key(key)?, + name: self.name.encrypt(ctx, key)?, + value: self.value.encrypt(ctx, key)?, r#type: self.r#type, linked_id: self.linked_id, }) } } -impl KeyDecryptable for Field { - fn decrypt_with_key(&self, key: &SymmetricCryptoKey) -> Result { +impl Decryptable for Field { + fn decrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(FieldView { - name: self.name.decrypt_with_key(key).ok().flatten(), - value: self.value.decrypt_with_key(key).ok().flatten(), + name: self.name.decrypt(ctx, key).ok().flatten(), + value: self.value.decrypt(ctx, key).ok().flatten(), r#type: self.r#type, linked_id: self.linked_id, }) diff --git a/crates/bitwarden-vault/src/cipher/identity.rs b/crates/bitwarden-vault/src/cipher/identity.rs index f01274d..a5d56c9 100644 --- a/crates/bitwarden-vault/src/cipher/identity.rs +++ b/crates/bitwarden-vault/src/cipher/identity.rs @@ -1,6 +1,7 @@ use bitwarden_api_api::models::CipherIdentityModel; +use bitwarden_core::key_management::{AsymmetricKeyRef, SymmetricKeyRef}; use bitwarden_crypto::{ - CryptoError, EncString, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey, + service::CryptoServiceContext, CryptoError, Decryptable, EncString, Encryptable, }; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -55,52 +56,60 @@ pub struct IdentityView { pub license_number: Option, } -impl KeyEncryptable for IdentityView { - fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { +impl Encryptable for IdentityView { + fn encrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(Identity { - title: self.title.encrypt_with_key(key)?, - first_name: self.first_name.encrypt_with_key(key)?, - middle_name: self.middle_name.encrypt_with_key(key)?, - last_name: self.last_name.encrypt_with_key(key)?, - address1: self.address1.encrypt_with_key(key)?, - address2: self.address2.encrypt_with_key(key)?, - address3: self.address3.encrypt_with_key(key)?, - city: self.city.encrypt_with_key(key)?, - state: self.state.encrypt_with_key(key)?, - postal_code: self.postal_code.encrypt_with_key(key)?, - country: self.country.encrypt_with_key(key)?, - company: self.company.encrypt_with_key(key)?, - email: self.email.encrypt_with_key(key)?, - phone: self.phone.encrypt_with_key(key)?, - ssn: self.ssn.encrypt_with_key(key)?, - username: self.username.encrypt_with_key(key)?, - passport_number: self.passport_number.encrypt_with_key(key)?, - license_number: self.license_number.encrypt_with_key(key)?, + title: self.title.encrypt(ctx, key)?, + first_name: self.first_name.encrypt(ctx, key)?, + middle_name: self.middle_name.encrypt(ctx, key)?, + last_name: self.last_name.encrypt(ctx, key)?, + address1: self.address1.encrypt(ctx, key)?, + address2: self.address2.encrypt(ctx, key)?, + address3: self.address3.encrypt(ctx, key)?, + city: self.city.encrypt(ctx, key)?, + state: self.state.encrypt(ctx, key)?, + postal_code: self.postal_code.encrypt(ctx, key)?, + country: self.country.encrypt(ctx, key)?, + company: self.company.encrypt(ctx, key)?, + email: self.email.encrypt(ctx, key)?, + phone: self.phone.encrypt(ctx, key)?, + ssn: self.ssn.encrypt(ctx, key)?, + username: self.username.encrypt(ctx, key)?, + passport_number: self.passport_number.encrypt(ctx, key)?, + license_number: self.license_number.encrypt(ctx, key)?, }) } } -impl KeyDecryptable for Identity { - fn decrypt_with_key(&self, key: &SymmetricCryptoKey) -> Result { +impl Decryptable for Identity { + fn decrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(IdentityView { - title: self.title.decrypt_with_key(key).ok().flatten(), - first_name: self.first_name.decrypt_with_key(key).ok().flatten(), - middle_name: self.middle_name.decrypt_with_key(key).ok().flatten(), - last_name: self.last_name.decrypt_with_key(key).ok().flatten(), - address1: self.address1.decrypt_with_key(key).ok().flatten(), - address2: self.address2.decrypt_with_key(key).ok().flatten(), - address3: self.address3.decrypt_with_key(key).ok().flatten(), - city: self.city.decrypt_with_key(key).ok().flatten(), - state: self.state.decrypt_with_key(key).ok().flatten(), - postal_code: self.postal_code.decrypt_with_key(key).ok().flatten(), - country: self.country.decrypt_with_key(key).ok().flatten(), - company: self.company.decrypt_with_key(key).ok().flatten(), - email: self.email.decrypt_with_key(key).ok().flatten(), - phone: self.phone.decrypt_with_key(key).ok().flatten(), - ssn: self.ssn.decrypt_with_key(key).ok().flatten(), - username: self.username.decrypt_with_key(key).ok().flatten(), - passport_number: self.passport_number.decrypt_with_key(key).ok().flatten(), - license_number: self.license_number.decrypt_with_key(key).ok().flatten(), + title: self.title.decrypt(ctx, key).ok().flatten(), + first_name: self.first_name.decrypt(ctx, key).ok().flatten(), + middle_name: self.middle_name.decrypt(ctx, key).ok().flatten(), + last_name: self.last_name.decrypt(ctx, key).ok().flatten(), + address1: self.address1.decrypt(ctx, key).ok().flatten(), + address2: self.address2.decrypt(ctx, key).ok().flatten(), + address3: self.address3.decrypt(ctx, key).ok().flatten(), + city: self.city.decrypt(ctx, key).ok().flatten(), + state: self.state.decrypt(ctx, key).ok().flatten(), + postal_code: self.postal_code.decrypt(ctx, key).ok().flatten(), + country: self.country.decrypt(ctx, key).ok().flatten(), + company: self.company.decrypt(ctx, key).ok().flatten(), + email: self.email.decrypt(ctx, key).ok().flatten(), + phone: self.phone.decrypt(ctx, key).ok().flatten(), + ssn: self.ssn.decrypt(ctx, key).ok().flatten(), + username: self.username.decrypt(ctx, key).ok().flatten(), + passport_number: self.passport_number.decrypt(ctx, key).ok().flatten(), + license_number: self.license_number.decrypt(ctx, key).ok().flatten(), }) } } diff --git a/crates/bitwarden-vault/src/cipher/local_data.rs b/crates/bitwarden-vault/src/cipher/local_data.rs index 1c1c766..39a17ed 100644 --- a/crates/bitwarden-vault/src/cipher/local_data.rs +++ b/crates/bitwarden-vault/src/cipher/local_data.rs @@ -1,4 +1,5 @@ -use bitwarden_crypto::{CryptoError, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey}; +use bitwarden_core::key_management::{AsymmetricKeyRef, SymmetricKeyRef}; +use bitwarden_crypto::{service::CryptoServiceContext, CryptoError, Decryptable, Encryptable}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -18,8 +19,12 @@ pub struct LocalDataView { last_launched: Option, } -impl KeyEncryptable for LocalDataView { - fn encrypt_with_key(self, _key: &SymmetricCryptoKey) -> Result { +impl Encryptable for LocalDataView { + fn encrypt( + &self, + _ctx: &mut CryptoServiceContext, + _key: SymmetricKeyRef, + ) -> Result { Ok(LocalData { last_used_date: self.last_used_date, last_launched: self.last_launched, @@ -27,8 +32,12 @@ impl KeyEncryptable for LocalDataView { } } -impl KeyDecryptable for LocalData { - fn decrypt_with_key(&self, _key: &SymmetricCryptoKey) -> Result { +impl Decryptable for LocalData { + fn decrypt( + &self, + _ctx: &mut CryptoServiceContext, + _key: SymmetricKeyRef, + ) -> Result { Ok(LocalDataView { last_used_date: self.last_used_date, last_launched: self.last_launched, diff --git a/crates/bitwarden-vault/src/cipher/login.rs b/crates/bitwarden-vault/src/cipher/login.rs index 4b476b0..0203f03 100644 --- a/crates/bitwarden-vault/src/cipher/login.rs +++ b/crates/bitwarden-vault/src/cipher/login.rs @@ -1,8 +1,11 @@ use base64::{engine::general_purpose::STANDARD, Engine}; use bitwarden_api_api::models::{CipherLoginModel, CipherLoginUriModel}; -use bitwarden_core::require; +use bitwarden_core::{ + key_management::{AsymmetricKeyRef, SymmetricKeyRef}, + require, +}; use bitwarden_crypto::{ - CryptoError, EncString, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey, + service::CryptoServiceContext, CryptoError, Decryptable, EncString, Encryptable, }; use chrono::{DateTime, Utc}; use schemars::JsonSchema; @@ -168,63 +171,72 @@ impl From for Fido2CredentialNewView { } } -impl KeyEncryptable for Fido2CredentialFullView { - fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { +impl Encryptable + for Fido2CredentialFullView +{ + fn encrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(Fido2Credential { - credential_id: self.credential_id.encrypt_with_key(key)?, - key_type: self.key_type.encrypt_with_key(key)?, - key_algorithm: self.key_algorithm.encrypt_with_key(key)?, - key_curve: self.key_curve.encrypt_with_key(key)?, - key_value: self.key_value.encrypt_with_key(key)?, - rp_id: self.rp_id.encrypt_with_key(key)?, - user_handle: self - .user_handle - .map(|h| h.encrypt_with_key(key)) - .transpose()?, - user_name: self.user_name.encrypt_with_key(key)?, - counter: self.counter.encrypt_with_key(key)?, - rp_name: self.rp_name.encrypt_with_key(key)?, - user_display_name: self.user_display_name.encrypt_with_key(key)?, - discoverable: self.discoverable.encrypt_with_key(key)?, + credential_id: self.credential_id.encrypt(ctx, key)?, + key_type: self.key_type.encrypt(ctx, key)?, + key_algorithm: self.key_algorithm.encrypt(ctx, key)?, + key_curve: self.key_curve.encrypt(ctx, key)?, + key_value: self.key_value.encrypt(ctx, key)?, + rp_id: self.rp_id.encrypt(ctx, key)?, + user_handle: self.user_handle.encrypt(ctx, key)?, + user_name: self.user_name.encrypt(ctx, key)?, + counter: self.counter.encrypt(ctx, key)?, + rp_name: self.rp_name.encrypt(ctx, key)?, + user_display_name: self.user_display_name.encrypt(ctx, key)?, + discoverable: self.discoverable.encrypt(ctx, key)?, creation_date: self.creation_date, }) } } -impl KeyDecryptable for Fido2Credential { - fn decrypt_with_key( +impl Decryptable + for Fido2Credential +{ + fn decrypt( &self, - key: &SymmetricCryptoKey, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, ) -> Result { Ok(Fido2CredentialFullView { - credential_id: self.credential_id.decrypt_with_key(key)?, - key_type: self.key_type.decrypt_with_key(key)?, - key_algorithm: self.key_algorithm.decrypt_with_key(key)?, - key_curve: self.key_curve.decrypt_with_key(key)?, - key_value: self.key_value.decrypt_with_key(key)?, - rp_id: self.rp_id.decrypt_with_key(key)?, - user_handle: self.user_handle.decrypt_with_key(key)?, - user_name: self.user_name.decrypt_with_key(key)?, - counter: self.counter.decrypt_with_key(key)?, - rp_name: self.rp_name.decrypt_with_key(key)?, - user_display_name: self.user_display_name.decrypt_with_key(key)?, - discoverable: self.discoverable.decrypt_with_key(key)?, + credential_id: self.credential_id.decrypt(ctx, key)?, + key_type: self.key_type.decrypt(ctx, key)?, + key_algorithm: self.key_algorithm.decrypt(ctx, key)?, + key_curve: self.key_curve.decrypt(ctx, key)?, + key_value: self.key_value.decrypt(ctx, key)?, + rp_id: self.rp_id.decrypt(ctx, key)?, + user_handle: self.user_handle.decrypt(ctx, key)?, + user_name: self.user_name.decrypt(ctx, key)?, + counter: self.counter.decrypt(ctx, key)?, + rp_name: self.rp_name.decrypt(ctx, key)?, + user_display_name: self.user_display_name.decrypt(ctx, key)?, + discoverable: self.discoverable.decrypt(ctx, key)?, creation_date: self.creation_date, }) } } -impl KeyDecryptable for Fido2CredentialView { - fn decrypt_with_key( +impl Decryptable + for Fido2CredentialView +{ + fn decrypt( &self, - key: &SymmetricCryptoKey, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, ) -> Result { Ok(Fido2CredentialFullView { credential_id: self.credential_id.clone(), key_type: self.key_type.clone(), key_algorithm: self.key_algorithm.clone(), key_curve: self.key_curve.clone(), - key_value: self.key_value.decrypt_with_key(key)?, + key_value: self.key_value.decrypt(ctx, key)?, rp_id: self.rp_id.clone(), user_handle: self.user_handle.clone(), user_name: self.user_name.clone(), @@ -268,98 +280,117 @@ pub struct LoginView { pub fido2_credentials: Option>, } -impl KeyEncryptable for LoginUriView { - fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { +impl Encryptable for LoginUriView { + fn encrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(LoginUri { - uri: self.uri.encrypt_with_key(key)?, + uri: self.uri.encrypt(ctx, key)?, r#match: self.r#match, - uri_checksum: self.uri_checksum.encrypt_with_key(key)?, + uri_checksum: self.uri_checksum.encrypt(ctx, key)?, }) } } -impl KeyEncryptable for LoginView { - fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { +impl Encryptable for LoginView { + fn encrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(Login { - username: self.username.encrypt_with_key(key)?, - password: self.password.encrypt_with_key(key)?, + username: self.username.encrypt(ctx, key)?, + password: self.password.encrypt(ctx, key)?, password_revision_date: self.password_revision_date, - uris: self.uris.encrypt_with_key(key)?, - totp: self.totp.encrypt_with_key(key)?, + uris: self.uris.encrypt(ctx, key)?, + totp: self.totp.encrypt(ctx, key)?, autofill_on_page_load: self.autofill_on_page_load, - fido2_credentials: self.fido2_credentials, + fido2_credentials: self.fido2_credentials.clone(), }) } } -impl KeyDecryptable for LoginUri { - fn decrypt_with_key(&self, key: &SymmetricCryptoKey) -> Result { +impl Decryptable for LoginUri { + fn decrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(LoginUriView { - uri: self.uri.decrypt_with_key(key)?, + uri: self.uri.decrypt(ctx, key)?, r#match: self.r#match, - uri_checksum: self.uri_checksum.decrypt_with_key(key)?, + uri_checksum: self.uri_checksum.decrypt(ctx, key)?, }) } } -impl KeyDecryptable for Login { - fn decrypt_with_key(&self, key: &SymmetricCryptoKey) -> Result { +impl Decryptable for Login { + fn decrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(LoginView { - username: self.username.decrypt_with_key(key).ok().flatten(), - password: self.password.decrypt_with_key(key).ok().flatten(), + username: self.username.decrypt(ctx, key).ok().flatten(), + password: self.password.decrypt(ctx, key).ok().flatten(), password_revision_date: self.password_revision_date, - uris: self.uris.decrypt_with_key(key).ok().flatten(), - totp: self.totp.decrypt_with_key(key).ok().flatten(), + uris: self.uris.decrypt(ctx, key).ok().flatten(), + totp: self.totp.decrypt(ctx, key).ok().flatten(), autofill_on_page_load: self.autofill_on_page_load, fido2_credentials: self.fido2_credentials.clone(), }) } } -impl KeyEncryptable for Fido2CredentialView { - fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { +impl Encryptable + for Fido2CredentialView +{ + fn encrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(Fido2Credential { - credential_id: self.credential_id.encrypt_with_key(key)?, - key_type: self.key_type.encrypt_with_key(key)?, - key_algorithm: self.key_algorithm.encrypt_with_key(key)?, - key_curve: self.key_curve.encrypt_with_key(key)?, - key_value: self.key_value, - rp_id: self.rp_id.encrypt_with_key(key)?, - user_handle: self - .user_handle - .map(|h| h.encrypt_with_key(key)) - .transpose()?, - user_name: self - .user_name - .map(|n| n.encrypt_with_key(key)) - .transpose()?, - counter: self.counter.encrypt_with_key(key)?, - rp_name: self.rp_name.encrypt_with_key(key)?, - user_display_name: self.user_display_name.encrypt_with_key(key)?, - discoverable: self.discoverable.encrypt_with_key(key)?, + credential_id: self.credential_id.encrypt(ctx, key)?, + key_type: self.key_type.encrypt(ctx, key)?, + key_algorithm: self.key_algorithm.encrypt(ctx, key)?, + key_curve: self.key_curve.encrypt(ctx, key)?, + key_value: self.key_value.clone(), + rp_id: self.rp_id.encrypt(ctx, key)?, + user_handle: self.user_handle.encrypt(ctx, key)?, + user_name: self.user_name.encrypt(ctx, key)?, + counter: self.counter.encrypt(ctx, key)?, + rp_name: self.rp_name.encrypt(ctx, key)?, + user_display_name: self.user_display_name.encrypt(ctx, key)?, + discoverable: self.discoverable.encrypt(ctx, key)?, creation_date: self.creation_date, }) } } -impl KeyDecryptable for Fido2Credential { - fn decrypt_with_key( +impl Decryptable + for Fido2Credential +{ + fn decrypt( &self, - key: &SymmetricCryptoKey, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, ) -> Result { Ok(Fido2CredentialView { - credential_id: self.credential_id.decrypt_with_key(key)?, - key_type: self.key_type.decrypt_with_key(key)?, - key_algorithm: self.key_algorithm.decrypt_with_key(key)?, - key_curve: self.key_curve.decrypt_with_key(key)?, + credential_id: self.credential_id.decrypt(ctx, key)?, + key_type: self.key_type.decrypt(ctx, key)?, + key_algorithm: self.key_algorithm.decrypt(ctx, key)?, + key_curve: self.key_curve.decrypt(ctx, key)?, key_value: self.key_value.clone(), - rp_id: self.rp_id.decrypt_with_key(key)?, - user_handle: self.user_handle.decrypt_with_key(key)?, - user_name: self.user_name.decrypt_with_key(key)?, - counter: self.counter.decrypt_with_key(key)?, - rp_name: self.rp_name.decrypt_with_key(key)?, - user_display_name: self.user_display_name.decrypt_with_key(key)?, - discoverable: self.discoverable.decrypt_with_key(key)?, + rp_id: self.rp_id.decrypt(ctx, key)?, + user_handle: self.user_handle.decrypt(ctx, key)?, + user_name: self.user_name.decrypt(ctx, key)?, + counter: self.counter.decrypt(ctx, key)?, + rp_name: self.rp_name.decrypt(ctx, key)?, + user_display_name: self.user_display_name.decrypt(ctx, key)?, + discoverable: self.discoverable.decrypt(ctx, key)?, creation_date: self.creation_date, }) } diff --git a/crates/bitwarden-vault/src/cipher/secure_note.rs b/crates/bitwarden-vault/src/cipher/secure_note.rs index 2563160..2123cde 100644 --- a/crates/bitwarden-vault/src/cipher/secure_note.rs +++ b/crates/bitwarden-vault/src/cipher/secure_note.rs @@ -1,6 +1,9 @@ use bitwarden_api_api::models::CipherSecureNoteModel; -use bitwarden_core::require; -use bitwarden_crypto::{CryptoError, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey}; +use bitwarden_core::{ + key_management::{AsymmetricKeyRef, SymmetricKeyRef}, + require, +}; +use bitwarden_crypto::{service::CryptoServiceContext, CryptoError, Decryptable, Encryptable}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_repr::{Deserialize_repr, Serialize_repr}; @@ -28,16 +31,28 @@ pub struct SecureNoteView { pub r#type: SecureNoteType, } -impl KeyEncryptable for SecureNoteView { - fn encrypt_with_key(self, _key: &SymmetricCryptoKey) -> Result { +impl Encryptable + for SecureNoteView +{ + fn encrypt( + &self, + _ctx: &mut CryptoServiceContext, + _key: SymmetricKeyRef, + ) -> Result { Ok(SecureNote { r#type: self.r#type, }) } } -impl KeyDecryptable for SecureNote { - fn decrypt_with_key(&self, _key: &SymmetricCryptoKey) -> Result { +impl Decryptable + for SecureNote +{ + fn decrypt( + &self, + _ctx: &mut CryptoServiceContext, + _key: SymmetricKeyRef, + ) -> Result { Ok(SecureNoteView { r#type: self.r#type, }) diff --git a/crates/bitwarden-vault/src/cipher/ssh_key.rs b/crates/bitwarden-vault/src/cipher/ssh_key.rs index 6d4977c..6ba0c13 100644 --- a/crates/bitwarden-vault/src/cipher/ssh_key.rs +++ b/crates/bitwarden-vault/src/cipher/ssh_key.rs @@ -1,5 +1,6 @@ +use bitwarden_core::key_management::{AsymmetricKeyRef, SymmetricKeyRef}; use bitwarden_crypto::{ - CryptoError, EncString, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey, + service::CryptoServiceContext, CryptoError, Decryptable, EncString, Encryptable, }; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -28,22 +29,30 @@ pub struct SshKeyView { pub fingerprint: Option, } -impl KeyEncryptable for SshKeyView { - fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { +impl Encryptable for SshKeyView { + fn encrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(SshKey { - private_key: self.private_key.encrypt_with_key(key)?, - public_key: self.public_key.encrypt_with_key(key)?, - fingerprint: self.fingerprint.encrypt_with_key(key)?, + private_key: self.private_key.encrypt(ctx, key).ok().flatten(), + public_key: self.public_key.encrypt(ctx, key).ok().flatten(), + fingerprint: self.fingerprint.encrypt(ctx, key).ok().flatten(), }) } } -impl KeyDecryptable for SshKey { - fn decrypt_with_key(&self, key: &SymmetricCryptoKey) -> Result { +impl Decryptable for SshKey { + fn decrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(SshKeyView { - private_key: self.private_key.decrypt_with_key(key).ok().flatten(), - public_key: self.public_key.decrypt_with_key(key).ok().flatten(), - fingerprint: self.fingerprint.decrypt_with_key(key).ok().flatten(), + private_key: self.private_key.decrypt(ctx, key).ok().flatten(), + public_key: self.public_key.decrypt(ctx, key).ok().flatten(), + fingerprint: self.fingerprint.decrypt(ctx, key).ok().flatten(), }) } } diff --git a/crates/bitwarden-vault/src/client_totp.rs b/crates/bitwarden-vault/src/client_totp.rs index 6425b72..d4af00b 100644 --- a/crates/bitwarden-vault/src/client_totp.rs +++ b/crates/bitwarden-vault/src/client_totp.rs @@ -25,8 +25,8 @@ impl<'a> ClientVault<'a> { view: CipherListView, time: Option>, ) -> Result { - let enc = self.client.internal.get_encryption_settings()?; + let mut ctx = self.client.internal.get_crypto_service().context(); - generate_totp_cipher_view(&enc, view, time) + generate_totp_cipher_view(&mut ctx, view, time) } } diff --git a/crates/bitwarden-vault/src/collection.rs b/crates/bitwarden-vault/src/collection.rs index ce798d9..e0d6e97 100644 --- a/crates/bitwarden-vault/src/collection.rs +++ b/crates/bitwarden-vault/src/collection.rs @@ -1,7 +1,10 @@ use bitwarden_api_api::models::CollectionDetailsResponseModel; -use bitwarden_core::require; +use bitwarden_core::{ + key_management::{AsymmetricKeyRef, SymmetricKeyRef}, + require, +}; use bitwarden_crypto::{ - CryptoError, EncString, KeyContainer, KeyDecryptable, LocateKey, SymmetricCryptoKey, + service::CryptoServiceContext, CryptoError, Decryptable, EncString, UsesKey, }; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -39,22 +42,25 @@ pub struct CollectionView { pub manage: bool, } -impl LocateKey for Collection { - fn locate_key<'a>( - &self, - enc: &'a dyn KeyContainer, - _: &Option, - ) -> Result<&'a SymmetricCryptoKey, CryptoError> { - enc.get_key(&Some(self.organization_id)) +impl UsesKey for Collection { + fn uses_key(&self) -> SymmetricKeyRef { + SymmetricKeyRef::Organization(self.organization_id) } } -impl KeyDecryptable for Collection { - fn decrypt_with_key(&self, key: &SymmetricCryptoKey) -> Result { + +impl Decryptable + for Collection +{ + fn decrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(CollectionView { id: self.id, organization_id: self.organization_id, - name: self.name.decrypt_with_key(key).ok().unwrap_or_default(), + name: self.name.decrypt(ctx, key).ok().unwrap_or_default(), external_id: self.external_id.clone(), hide_passwords: self.hide_passwords, diff --git a/crates/bitwarden-vault/src/folder.rs b/crates/bitwarden-vault/src/folder.rs index f57b8ee..22810cb 100644 --- a/crates/bitwarden-vault/src/folder.rs +++ b/crates/bitwarden-vault/src/folder.rs @@ -1,7 +1,10 @@ use bitwarden_api_api::models::FolderResponseModel; -use bitwarden_core::require; +use bitwarden_core::{ + key_management::{AsymmetricKeyRef, SymmetricKeyRef}, + require, +}; use bitwarden_crypto::{ - CryptoError, EncString, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey, + service::CryptoServiceContext, CryptoError, Decryptable, EncString, Encryptable, UsesKey, }; use chrono::{DateTime, Utc}; use schemars::JsonSchema; @@ -32,26 +35,46 @@ pub struct FolderView { pub revision_date: DateTime, } -impl KeyEncryptable for FolderView { - fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { +impl Encryptable for FolderView { + fn encrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(Folder { id: self.id, - name: self.name.encrypt_with_key(key)?, + name: self.name.encrypt(ctx, key)?, revision_date: self.revision_date, }) } } -impl KeyDecryptable for Folder { - fn decrypt_with_key(&self, key: &SymmetricCryptoKey) -> Result { +impl Decryptable for Folder { + fn decrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(FolderView { id: self.id, - name: self.name.decrypt_with_key(key).ok().unwrap_or_default(), + name: self.name.decrypt(ctx, key).ok().unwrap_or_default(), revision_date: self.revision_date, }) } } +impl UsesKey for Folder { + fn uses_key(&self) -> SymmetricKeyRef { + SymmetricKeyRef::User + } +} + +impl UsesKey for FolderView { + fn uses_key(&self) -> SymmetricKeyRef { + SymmetricKeyRef::User + } +} + impl TryFrom for Folder { type Error = VaultParseError; diff --git a/crates/bitwarden-vault/src/mobile/client_attachments.rs b/crates/bitwarden-vault/src/mobile/client_attachments.rs index a98a7d1..b36f242 100644 --- a/crates/bitwarden-vault/src/mobile/client_attachments.rs +++ b/crates/bitwarden-vault/src/mobile/client_attachments.rs @@ -1,7 +1,7 @@ use std::path::Path; use bitwarden_core::{Client, Error}; -use bitwarden_crypto::{EncString, KeyDecryptable, KeyEncryptable, LocateKey}; +use bitwarden_crypto::EncString; use crate::{ Attachment, AttachmentEncryptResult, AttachmentFile, AttachmentFileView, AttachmentView, @@ -19,15 +19,13 @@ impl<'a> ClientAttachments<'a> { attachment: AttachmentView, buffer: &[u8], ) -> Result { - let enc = self.client.internal.get_encryption_settings()?; - let key = cipher.locate_key(&enc, &None)?; + let crypto = self.client.internal.get_crypto_service(); - Ok(AttachmentFileView { + Ok(crypto.encrypt(AttachmentFileView { cipher, attachment, contents: buffer, - } - .encrypt_with_key(key)?) + })?) } pub fn encrypt_file( &self, @@ -51,16 +49,13 @@ impl<'a> ClientAttachments<'a> { attachment: Attachment, encrypted_buffer: &[u8], ) -> Result, Error> { - let enc = self.client.internal.get_encryption_settings()?; - let key = cipher.locate_key(&enc, &None)?; + let crypto = self.client.internal.get_crypto_service(); - AttachmentFile { + Ok(crypto.decrypt(&AttachmentFile { cipher, attachment, contents: EncString::from_buffer(encrypted_buffer)?, - } - .decrypt_with_key(key) - .map_err(Error::Crypto) + })?) } pub fn decrypt_file( &self, diff --git a/crates/bitwarden-vault/src/mobile/client_ciphers.rs b/crates/bitwarden-vault/src/mobile/client_ciphers.rs index 063fb4b..15638ae 100644 --- a/crates/bitwarden-vault/src/mobile/client_ciphers.rs +++ b/crates/bitwarden-vault/src/mobile/client_ciphers.rs @@ -1,5 +1,5 @@ use bitwarden_core::{Client, Error}; -use bitwarden_crypto::{KeyDecryptable, KeyEncryptable, LocateKey}; +use bitwarden_crypto::{Encryptable, UsesKey}; use uuid::Uuid; use crate::{Cipher, CipherError, CipherListView, CipherView, ClientVault}; @@ -10,7 +10,10 @@ pub struct ClientCiphers<'a> { impl<'a> ClientCiphers<'a> { pub fn encrypt(&self, mut cipher_view: CipherView) -> Result { - let enc = self.client.internal.get_encryption_settings()?; + let crypto = self.client.internal.get_crypto_service(); + let mut ctx = crypto.context(); + + let key = cipher_view.uses_key(); // TODO: Once this flag is removed, the key generation logic should // be moved directly into the KeyEncryptable implementation @@ -21,37 +24,28 @@ impl<'a> ClientCiphers<'a> { .get_flags() .enable_cipher_key_encryption { - let key = cipher_view.locate_key(&enc, &None)?; - cipher_view.generate_cipher_key(key)?; + cipher_view.generate_cipher_key(&mut ctx, key)?; } - let key = cipher_view.locate_key(&enc, &None)?; - let cipher = cipher_view.encrypt_with_key(key)?; + let cipher = cipher_view.encrypt(&mut ctx, key)?; Ok(cipher) } pub fn decrypt(&self, cipher: Cipher) -> Result { - let enc = self.client.internal.get_encryption_settings()?; - let key = cipher.locate_key(&enc, &None)?; + let crypto = self.client.internal.get_crypto_service(); - let cipher_view = cipher.decrypt_with_key(key)?; + let cipher_view = crypto.decrypt(&cipher)?; Ok(cipher_view) } pub fn decrypt_list(&self, ciphers: Vec) -> Result, Error> { - let enc = self.client.internal.get_encryption_settings()?; + let crypto = self.client.internal.get_crypto_service(); - let cipher_views: Result, _> = ciphers - .iter() - .map(|c| -> Result { - let key = c.locate_key(&enc, &None)?; - Ok(c.decrypt_with_key(key)?) - }) - .collect(); + let cipher_views = crypto.decrypt_list(&ciphers)?; - cipher_views + Ok(cipher_views) } #[cfg(feature = "uniffi")] @@ -59,10 +53,11 @@ impl<'a> ClientCiphers<'a> { &self, cipher_view: CipherView, ) -> Result, Error> { - let enc = self.client.internal.get_encryption_settings()?; + let crypto = self.client.internal.get_crypto_service(); + let mut ctx = crypto.context(); let credentials = cipher_view - .decrypt_fido2_credentials(&enc) + .decrypt_fido2_credentials(&mut ctx) .map_err(|e| e.to_string())?; Ok(credentials) @@ -73,8 +68,10 @@ impl<'a> ClientCiphers<'a> { mut cipher_view: CipherView, organization_id: Uuid, ) -> Result { - let enc = self.client.internal.get_encryption_settings()?; - cipher_view.move_to_organization(&enc, organization_id)?; + let crypto = self.client.internal.get_crypto_service(); + let mut ctx = crypto.context(); + + cipher_view.move_to_organization(&mut ctx, organization_id)?; Ok(cipher_view) } } diff --git a/crates/bitwarden-vault/src/mobile/client_collection.rs b/crates/bitwarden-vault/src/mobile/client_collection.rs index 0c007f9..87e479d 100644 --- a/crates/bitwarden-vault/src/mobile/client_collection.rs +++ b/crates/bitwarden-vault/src/mobile/client_collection.rs @@ -1,5 +1,4 @@ use bitwarden_core::{Client, Error}; -use bitwarden_crypto::{KeyDecryptable, LocateKey}; use crate::{ClientVault, Collection, CollectionView}; @@ -9,26 +8,23 @@ pub struct ClientCollections<'a> { impl<'a> ClientCollections<'a> { pub fn decrypt(&self, collection: Collection) -> Result { - let enc = self.client.internal.get_encryption_settings()?; - let key = collection.locate_key(&enc, &None)?; - - let view = collection.decrypt_with_key(key)?; + let view = self + .client + .internal + .get_crypto_service() + .decrypt(&collection)?; Ok(view) } pub fn decrypt_list(&self, collections: Vec) -> Result, Error> { - let enc = self.client.internal.get_encryption_settings()?; - - let views: Result, _> = collections - .iter() - .map(|c| -> Result { - let key = c.locate_key(&enc, &None)?; - Ok(c.decrypt_with_key(key)?) - }) - .collect(); + let views = self + .client + .internal + .get_crypto_service() + .decrypt_list(&collections)?; - views + Ok(views) } } diff --git a/crates/bitwarden-vault/src/mobile/client_folders.rs b/crates/bitwarden-vault/src/mobile/client_folders.rs index d8ebb76..a559979 100644 --- a/crates/bitwarden-vault/src/mobile/client_folders.rs +++ b/crates/bitwarden-vault/src/mobile/client_folders.rs @@ -1,5 +1,4 @@ use bitwarden_core::{Client, Error}; -use bitwarden_crypto::{KeyDecryptable, KeyEncryptable}; use crate::{ClientVault, Folder, FolderView}; @@ -9,28 +8,25 @@ pub struct ClientFolders<'a> { impl<'a> ClientFolders<'a> { pub fn encrypt(&self, folder_view: FolderView) -> Result { - let enc = self.client.internal.get_encryption_settings()?; - let key = enc.get_key(&None)?; + let crypto = self.client.internal.get_crypto_service(); - let folder = folder_view.encrypt_with_key(key)?; + let folder = crypto.encrypt(folder_view)?; Ok(folder) } pub fn decrypt(&self, folder: Folder) -> Result { - let enc = self.client.internal.get_encryption_settings()?; - let key = enc.get_key(&None)?; + let crypto = self.client.internal.get_crypto_service(); - let folder_view = folder.decrypt_with_key(key)?; + let folder_view = crypto.decrypt(&folder)?; Ok(folder_view) } pub fn decrypt_list(&self, folders: Vec) -> Result, Error> { - let enc = self.client.internal.get_encryption_settings()?; - let key = enc.get_key(&None)?; + let crypto = self.client.internal.get_crypto_service(); - let views = folders.decrypt_with_key(key)?; + let views = crypto.decrypt_list(&folders)?; Ok(views) } diff --git a/crates/bitwarden-vault/src/mobile/client_password_history.rs b/crates/bitwarden-vault/src/mobile/client_password_history.rs index 98d989d..3a3c155 100644 --- a/crates/bitwarden-vault/src/mobile/client_password_history.rs +++ b/crates/bitwarden-vault/src/mobile/client_password_history.rs @@ -1,5 +1,4 @@ use bitwarden_core::{Client, Error}; -use bitwarden_crypto::{KeyDecryptable, KeyEncryptable}; use crate::{ClientVault, PasswordHistory, PasswordHistoryView}; @@ -9,10 +8,11 @@ pub struct ClientPasswordHistory<'a> { impl<'a> ClientPasswordHistory<'a> { pub fn encrypt(&self, history_view: PasswordHistoryView) -> Result { - let enc = self.client.internal.get_encryption_settings()?; - let key = enc.get_key(&None)?; - - let history = history_view.encrypt_with_key(key)?; + let history = self + .client + .internal + .get_crypto_service() + .encrypt(history_view)?; Ok(history) } @@ -21,10 +21,11 @@ impl<'a> ClientPasswordHistory<'a> { &self, history: Vec, ) -> Result, Error> { - let enc = self.client.internal.get_encryption_settings()?; - let key = enc.get_key(&None)?; - - let history_view = history.decrypt_with_key(key)?; + let history_view = self + .client + .internal + .get_crypto_service() + .decrypt_list(&history)?; Ok(history_view) } diff --git a/crates/bitwarden-vault/src/password_history.rs b/crates/bitwarden-vault/src/password_history.rs index 5e2075e..b2b7cdf 100644 --- a/crates/bitwarden-vault/src/password_history.rs +++ b/crates/bitwarden-vault/src/password_history.rs @@ -1,6 +1,7 @@ use bitwarden_api_api::models::CipherPasswordHistoryModel; +use bitwarden_core::key_management::{AsymmetricKeyRef, SymmetricKeyRef}; use bitwarden_crypto::{ - CryptoError, EncString, KeyDecryptable, KeyEncryptable, SymmetricCryptoKey, + service::CryptoServiceContext, CryptoError, Decryptable, EncString, Encryptable, UsesKey, }; use chrono::{DateTime, Utc}; use schemars::JsonSchema; @@ -24,22 +25,43 @@ pub struct PasswordHistoryView { last_used_date: DateTime, } -impl KeyEncryptable for PasswordHistoryView { - fn encrypt_with_key(self, key: &SymmetricCryptoKey) -> Result { +impl UsesKey for PasswordHistory { + fn uses_key(&self) -> SymmetricKeyRef { + SymmetricKeyRef::User + } +} + +impl UsesKey for PasswordHistoryView { + fn uses_key(&self) -> SymmetricKeyRef { + SymmetricKeyRef::User + } +} + +impl Encryptable + for PasswordHistoryView +{ + fn encrypt( + &self, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, + ) -> Result { Ok(PasswordHistory { - password: self.password.encrypt_with_key(key)?, + password: self.password.encrypt(ctx, key)?, last_used_date: self.last_used_date, }) } } -impl KeyDecryptable for PasswordHistory { - fn decrypt_with_key( +impl Decryptable + for PasswordHistory +{ + fn decrypt( &self, - key: &SymmetricCryptoKey, + ctx: &mut CryptoServiceContext, + key: SymmetricKeyRef, ) -> Result { Ok(PasswordHistoryView { - password: self.password.decrypt_with_key(key).ok().unwrap_or_default(), + password: self.password.decrypt(ctx, key).ok().unwrap_or_default(), last_used_date: self.last_used_date, }) } diff --git a/crates/bitwarden-vault/src/sync.rs b/crates/bitwarden-vault/src/sync.rs index be4c3b1..527b0c0 100644 --- a/crates/bitwarden-vault/src/sync.rs +++ b/crates/bitwarden-vault/src/sync.rs @@ -2,8 +2,10 @@ use bitwarden_api_api::models::{ DomainsResponseModel, ProfileOrganizationResponseModel, ProfileResponseModel, SyncResponseModel, }; use bitwarden_core::{ - client::encryption_settings::EncryptionSettings, require, Client, Error, MissingFieldError, + key_management::{AsymmetricKeyRef, SymmetricKeyRef}, + require, Client, Error, MissingFieldError, }; +use bitwarden_crypto::service::CryptoServiceContext; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use thiserror::Error; @@ -44,12 +46,14 @@ pub(crate) async fn sync(client: &Client, input: &SyncRequest) -> Result, ) -> Result { let profile = require!(response.profile); let ciphers = require!(response.ciphers); @@ -110,7 +114,7 @@ impl SyncResponse { } Ok(SyncResponse { - profile: ProfileResponse::process_response(*profile, enc)?, + profile: ProfileResponse::process_response(*profile, ctx)?, folders: try_into_iter(require!(response.folders))?, collections: try_into_iter(require!(response.collections))?, ciphers: try_into_iter(ciphers)?, @@ -134,7 +138,7 @@ impl ProfileOrganizationResponse { impl ProfileResponse { fn process_response( response: ProfileResponseModel, - _enc: &EncryptionSettings, + _ctx: &mut CryptoServiceContext, ) -> Result { Ok(ProfileResponse { id: require!(response.id), diff --git a/crates/bitwarden-vault/src/totp.rs b/crates/bitwarden-vault/src/totp.rs index d6167a1..7b6bbd1 100644 --- a/crates/bitwarden-vault/src/totp.rs +++ b/crates/bitwarden-vault/src/totp.rs @@ -1,7 +1,10 @@ use std::{collections::HashMap, str::FromStr}; -use bitwarden_core::VaultLocked; -use bitwarden_crypto::{CryptoError, KeyContainer}; +use bitwarden_core::{ + key_management::{AsymmetricKeyRef, SymmetricKeyRef}, + VaultLocked, +}; +use bitwarden_crypto::{service::CryptoServiceContext, CryptoError}; use chrono::{DateTime, Utc}; use hmac::{Hmac, Mac}; use reqwest::Url; @@ -76,11 +79,11 @@ pub fn generate_totp(key: String, time: Option>) -> Result, view: CipherListView, time: Option>, ) -> Result { - let key = view.get_totp_key(enc)?.ok_or(TotpError::MissingSecret)?; + let key = view.get_totp_key(ctx)?.ok_or(TotpError::MissingSecret)?; generate_totp(key, time) } @@ -261,9 +264,8 @@ fn decode_b32(s: &str) -> Vec { #[cfg(test)] mod tests { - use bitwarden_crypto::{CryptoError, SymmetricCryptoKey}; + use bitwarden_core::key_management::create_test_crypto_with_user_key; use chrono::Utc; - use uuid::Uuid; use super::*; use crate::{cipher::cipher::CipherListViewType, CipherRepromptType}; @@ -391,22 +393,14 @@ mod tests { revision_date: "2024-01-30T17:55:36.150Z".parse().unwrap(), }; - struct MockKeyContainer(SymmetricCryptoKey); - impl KeyContainer for MockKeyContainer { - fn get_key<'a>( - &'a self, - _: &Option, - ) -> Result<&'a SymmetricCryptoKey, CryptoError> { - Ok(&self.0) - } - } + let key= "w2LO+nwV4oxwswVYCxlOfRUseXfvU03VzvKQHrqeklPgiMZrspUe6sOBToCnDn9Ay0tuCBn8ykVVRb7PWhub2Q==".to_string().try_into().unwrap(); + let crypto = create_test_crypto_with_user_key(key); - let enc = MockKeyContainer("w2LO+nwV4oxwswVYCxlOfRUseXfvU03VzvKQHrqeklPgiMZrspUe6sOBToCnDn9Ay0tuCBn8ykVVRb7PWhub2Q==".to_string().try_into().unwrap()); let time = DateTime::parse_from_rfc3339("2023-01-01T00:00:00.000Z") .unwrap() .with_timezone(&Utc); - let response = generate_totp_cipher_view(&enc, view, Some(time)).unwrap(); + let response = generate_totp_cipher_view(&mut crypto.context(), view, Some(time)).unwrap(); assert_eq!(response.code, "559388".to_string()); assert_eq!(response.period, 30); }