Skip to content

Commit

Permalink
Migrate SDK to CryptoService
Browse files Browse the repository at this point in the history
# Conflicts:
#	crates/bitwarden-crypto/src/error.rs
#	crates/bitwarden-crypto/src/service/context.rs
  • Loading branch information
dani-garcia committed Oct 8, 2024
1 parent f4ca816 commit 0c74d80
Show file tree
Hide file tree
Showing 55 changed files with 1,327 additions and 1,057 deletions.
36 changes: 20 additions & 16 deletions crates/bitwarden-core/src/auth/auth_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -83,8 +83,11 @@ pub(crate) fn approve_auth_request(
) -> Result<AsymmetricEncString, Error> {
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(),
Expand Down Expand Up @@ -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,);
}
}
6 changes: 4 additions & 2 deletions crates/bitwarden-core/src/auth/client_auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,11 @@ impl<'a> ClientAuth<'a> {

#[cfg(feature = "internal")]
fn trust_device(client: &Client) -> Result<TrustDeviceResponse> {
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();

Check warning on line 167 in crates/bitwarden-core/src/auth/client_auth.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/auth/client_auth.rs#L167

Added line #L167 was not covered by tests
#[allow(deprecated)]
let user_key = ctx.dangerous_get_symmetric_key(SymmetricKeyRef::User)?;

Check warning on line 169 in crates/bitwarden-core/src/auth/client_auth.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/auth/client_auth.rs#L169

Added line #L169 was not covered by tests

Ok(DeviceKey::trust_device(user_key)?)
}
Expand Down
9 changes: 6 additions & 3 deletions crates/bitwarden-core/src/auth/password/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ pub(crate) fn validate_password_user_key(
password: String,
encrypted_user_key: String,
) -> Result<String> {
use crate::key_management::SymmetricKeyRef;

let login_method = client
.internal
.get_login_method()
Expand All @@ -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());
Expand Down
6 changes: 4 additions & 2 deletions crates/bitwarden-core/src/auth/pin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use bitwarden_crypto::{EncString, PinKey};
use crate::{
client::{LoginMethod, UserLoginMethod},
error::{Error, Result},
key_management::SymmetricKeyRef,
Client,
};

Expand All @@ -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)?;

Expand Down
11 changes: 8 additions & 3 deletions crates/bitwarden-core/src/auth/renew.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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)

Check warning on line 75 in crates/bitwarden-core/src/auth/renew.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/auth/renew.rs#L74-L75

Added lines #L74 - L75 were not covered by tests
{
if let Ok(enc_key) = enc_settings.get_key(&None) {
let ctx = client.get_crypto_service().context();

Check warning on line 77 in crates/bitwarden-core/src/auth/renew.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/auth/renew.rs#L77

Added line #L77 was not covered by tests

#[allow(deprecated)]
if let Ok(enc_key) = ctx.dangerous_get_symmetric_key(SymmetricKeyRef::User)
{

Check warning on line 81 in crates/bitwarden-core/src/auth/renew.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/auth/renew.rs#L80-L81

Added lines #L80 - L81 were not covered by tests
let state =
ClientState::new(r.access_token.clone(), enc_key.to_base64());
_ = state::set(state_file, access_token, state);
Expand Down
3 changes: 2 additions & 1 deletion crates/bitwarden-core/src/client/client.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::sync::{Arc, RwLock};

use bitwarden_crypto::service::CryptoService;
use reqwest::header::{self, HeaderValue};

use super::internal::InternalClient;
Expand Down Expand Up @@ -78,7 +79,7 @@ impl Client {
device_type: settings.device_type,
})),
external_client,
encryption_settings: RwLock::new(None),
crypto_service: CryptoService::new(),
},
}
}
Expand Down
122 changes: 43 additions & 79 deletions crates/bitwarden-core/src/client/encryption_settings.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -28,32 +29,9 @@ pub enum EncryptionSettingsError {
MissingPrivateKey,
}

#[derive(Clone)]
pub struct EncryptionSettings {
user_key: SymmetricCryptoKey,
pub(crate) private_key: Option<AsymmetricCryptoKey>,
org_keys: HashMap<Uuid, SymmetricCryptoKey>,
}

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<Self, EncryptionSettingsError> {
// 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
Expand All @@ -62,7 +40,8 @@ impl EncryptionSettings {
pub(crate) fn new_decrypted_key(
user_key: SymmetricCryptoKey,
private_key: EncString,
) -> Result<Self, EncryptionSettingsError> {
crypto_service: &CryptoService<SymmetricKeyRef, AsymmetricKeyRef>,
) -> Result<(), EncryptionSettingsError> {
use bitwarden_crypto::KeyDecryptable;
use log::warn;

Expand All @@ -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)?;
}

Check warning on line 71 in crates/bitwarden-core/src/client/encryption_settings.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/client/encryption_settings.rs#L71

Added line #L71 was not covered by tests
}

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<SymmetricKeyRef, AsymmetricKeyRef>,
) {
#[allow(deprecated)]
crypto_service
.context_mut()
.set_symmetric_key(SymmetricKeyRef::User, key)
.expect("Mutable context");

Check warning on line 88 in crates/bitwarden-core/src/client/encryption_settings.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/client/encryption_settings.rs#L80-L88

Added lines #L80 - L88 were not covered by tests
}

#[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<SymmetricKeyRef, AsymmetricKeyRef>,
) -> Result<(), EncryptionSettingsError> {
let mut ctx = crypto_service.context_mut();

if !ctx.has_asymmetric_key(AsymmetricKeyRef::UserPrivateKey) {
return Err(VaultLocked.into());

Check warning on line 99 in crates/bitwarden-core/src/client/encryption_settings.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/client/encryption_settings.rs#L99

Added line #L99 was not covered by tests
}

// 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(());

Check warning on line 108 in crates/bitwarden-core/src/client/encryption_settings.rs

View check run for this annotation

Codecov / codecov/patch

crates/bitwarden-core/src/client/encryption_settings.rs#L108

Added line #L108 was not covered by tests
}

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<u8> = 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<Uuid>) -> 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<Uuid>) -> Result<&SymmetricCryptoKey, CryptoError> {
EncryptionSettings::get_key(self, org_id)
Ok(())
}
}
Loading

0 comments on commit 0c74d80

Please sign in to comment.