Skip to content

Commit

Permalink
Remove AuthSettings
Browse files Browse the repository at this point in the history
  • Loading branch information
Hinton committed Sep 18, 2023
1 parent 7684f02 commit 03412b9
Show file tree
Hide file tree
Showing 19 changed files with 139 additions and 125 deletions.
2 changes: 1 addition & 1 deletion crates/bitwarden-uniffi/src/auth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::sync::Arc;

use bitwarden::{
auth::{password::MasterPasswordPolicyOptions, RegisterKeyResponse},
client::auth_settings::Kdf,
client::kdf::Kdf,
};

use crate::{error::Result, Client};
Expand Down
2 changes: 1 addition & 1 deletion crates/bitwarden/src/auth/client_auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use super::{
register::{make_register_keys, register},
RegisterKeyResponse, RegisterRequest,
};
use crate::{client::auth_settings::Kdf, error::Result, Client};
use crate::{client::kdf::Kdf, error::Result, Client};

pub struct ClientAuth<'a> {
pub(crate) client: &'a mut crate::Client,
Expand Down
5 changes: 4 additions & 1 deletion crates/bitwarden/src/auth/login/api_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ use crate::{
Client,
};

use super::request_prelogin;

pub(crate) async fn api_key_login(
client: &mut Client,
input: &ApiKeyLoginRequest,
Expand Down Expand Up @@ -45,7 +47,8 @@ pub(crate) async fn api_key_login(
.email
.ok_or(Error::Internal("Access token doesn't contain email"))?;

let _ = determine_password_hash(client, &email, &input.password).await?;
let kdf = request_prelogin(client, email.clone()).await?.try_into()?;
let _ = determine_password_hash(&email, &kdf, &input.password).await?;

let user_key = EncString::from_str(r.key.as_deref().unwrap()).unwrap();
let private_key = EncString::from_str(r.private_key.as_deref().unwrap()).unwrap();
Expand Down
16 changes: 7 additions & 9 deletions crates/bitwarden/src/auth/login/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#[cfg(feature = "internal")]
use {
crate::{
client::{auth_settings::AuthSettings, Client},
client::{kdf::Kdf, Client},
error::Result,
},
bitwarden_api_identity::{
Expand Down Expand Up @@ -41,20 +41,18 @@ pub use access_token::{AccessTokenLoginRequest, AccessTokenLoginResponse};

#[cfg(feature = "internal")]
async fn determine_password_hash(
client: &mut Client,
email: &str,
kdf: &Kdf,
password: &str,
) -> Result<String> {
let pre_login = request_prelogin(client, email.to_owned()).await?;
let auth_settings = AuthSettings::new(pre_login, email.to_owned());
let password_hash = auth_settings.derive_user_password_hash(password)?;
client.set_auth_settings(auth_settings);

Ok(password_hash)
use crate::crypto::{MasterKey, HashPurpose};

let master_key = MasterKey::derive(password.as_bytes(), email.as_bytes(), kdf)?;
master_key.derive_master_key_hash(password.as_bytes(), HashPurpose::ServerAuthorization)
}

#[cfg(feature = "internal")]
async fn request_prelogin(client: &mut Client, email: String) -> Result<PreloginResponseModel> {
pub(crate) async fn request_prelogin(client: &mut Client, email: String) -> Result<PreloginResponseModel> {
let request_model = PreloginRequestModel::new(email);
let config = client.get_api_configurations().await;
Ok(accounts_prelogin_post(&config.identity, Some(request_model)).await?)
Expand Down
11 changes: 8 additions & 3 deletions crates/bitwarden/src/auth/login/password.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,21 @@ use crate::{
api::response::IdentityTokenResponse,
login::response::{captcha_response::CaptchaResponse, two_factor::TwoFactorProviders},
},
error::Result,
error::Result, client::kdf::Kdf,
};

#[cfg(feature = "internal")]
pub(crate) async fn password_login(
client: &mut Client,
input: &PasswordLoginRequest,
) -> Result<PasswordLoginResponse> {
use crate::client::UserLoginMethod;
use crate::{client::UserLoginMethod, auth::login::request_prelogin};

info!("password logging in");
debug!("{:#?}, {:#?}", client, input);

let password_hash = determine_password_hash(client, &input.email, &input.password).await?;
let kdf = request_prelogin(client, input.email.clone()).await?.try_into()?;
let password_hash = determine_password_hash(&input.email, &kdf, &input.password).await?;
let response = request_identity_tokens(client, input, &password_hash).await?;

if let IdentityTokenResponse::Authenticated(r) = &response {
Expand All @@ -44,6 +45,8 @@ pub(crate) async fn password_login(
r.expires_in,
LoginMethod::User(UserLoginMethod::Username {
client_id: "web".to_owned(),
email: input.email.to_owned(),
kdf: input.kdf.to_owned(),
}),
);

Expand Down Expand Up @@ -79,6 +82,8 @@ pub struct PasswordLoginRequest {
pub password: String,
// Two-factor authentication
pub two_factor: Option<TwoFactorRequest>,
/// Kdf from prelogin
pub kdf: Kdf,
}

#[derive(Serialize, Deserialize, Debug, JsonSchema)]
Expand Down
5 changes: 3 additions & 2 deletions crates/bitwarden/src/auth/login/two_factor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};

use super::determine_password_hash;
use super::{determine_password_hash, request_prelogin};
use crate::{error::Result, Client};

#[derive(Serialize, Deserialize, Debug, JsonSchema)]
Expand All @@ -19,7 +19,8 @@ pub(crate) async fn send_two_factor_email(
client: &mut Client,
input: &TwoFactorEmailRequest,
) -> Result<()> {
let password_hash = determine_password_hash(client, &input.email, &input.password).await?;
let kdf = request_prelogin(client, input.email.clone()).await?.try_into()?;
let password_hash = determine_password_hash(&input.email, &kdf, &input.password).await?;

let config = client.get_api_configurations().await;
bitwarden_api_api::apis::two_factor_api::two_factor_send_email_login_post(
Expand Down
2 changes: 1 addition & 1 deletion crates/bitwarden/src/auth/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::{
client::auth_settings::Kdf,
client::kdf::Kdf,
crypto::{HashPurpose, MasterKey, RsaKeyPair},
error::Result,
util::default_pbkdf2_iterations,
Expand Down
2 changes: 1 addition & 1 deletion crates/bitwarden/src/auth/renew.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub(crate) async fn renew_token(client: &mut Client) -> Result<()> {
let res = match login_method {
#[cfg(feature = "internal")]
LoginMethod::User(u) => match u {
UserLoginMethod::Username { client_id } => {
UserLoginMethod::Username { client_id, .. } => {
let refresh = client
.refresh_token
.as_deref()
Expand Down
36 changes: 22 additions & 14 deletions crates/bitwarden/src/client/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use {
ApiKeyLoginResponse, PasswordLoginRequest, PasswordLoginResponse,
TwoFactorEmailRequest,
},
client::auth_settings::AuthSettings,
crypto::EncString,
platform::{
generate_fingerprint, get_user_api_key, sync, FingerprintRequest, FingerprintResponse,
Expand All @@ -32,6 +31,8 @@ use crate::{
error::{Error, Result},
};

use super::kdf::Kdf;

#[derive(Debug)]
pub(crate) struct ApiConfigurations {
pub identity: bitwarden_api_identity::apis::configuration::Configuration,
Expand All @@ -52,6 +53,8 @@ pub(crate) enum LoginMethod {
pub(crate) enum UserLoginMethod {
Username {
client_id: String,
email: String,
kdf: Kdf,
},
ApiKey {
client_id: String,
Expand Down Expand Up @@ -80,9 +83,6 @@ pub struct Client {
#[doc(hidden)]
pub(crate) __api_configurations: ApiConfigurations,

#[cfg(feature = "internal")]
auth_settings: Option<AuthSettings>,

encryption_settings: Option<EncryptionSettings>,
}

Expand Down Expand Up @@ -127,8 +127,6 @@ impl Client {
api,
device_type: settings.device_type,
},
#[cfg(feature = "internal")]
auth_settings: None,
encryption_settings: None,
}
}
Expand All @@ -140,6 +138,16 @@ impl Client {
&self.__api_configurations
}

#[cfg(feature = "internal")]
pub async fn prelogin(
&mut self,
email: String,
) -> Result<Kdf> {
use crate::auth::login::request_prelogin;

request_prelogin(self, email).await?.try_into()
}

#[cfg(feature = "internal")]
pub async fn password_login(
&mut self,
Expand Down Expand Up @@ -178,8 +186,8 @@ impl Client {
}

#[cfg(feature = "internal")]
pub(crate) fn get_auth_settings(&self) -> &Option<AuthSettings> {
&self.auth_settings
pub(crate) fn get_login_method(&self) -> &Option<LoginMethod> {
&self.login_method
}

pub fn get_access_token_organization(&self) -> Option<Uuid> {
Expand All @@ -197,9 +205,9 @@ impl Client {
}

#[cfg(feature = "internal")]
pub(crate) fn set_auth_settings(&mut self, auth_settings: AuthSettings) {
debug! {"setting auth settings: {:#?}", auth_settings}
self.auth_settings = Some(auth_settings);
pub(crate) fn set_login_method(&mut self, login_method: LoginMethod) {
debug! {"setting login method: {:#?}", login_method}
self.login_method = Some(login_method);
}

pub(crate) fn set_tokens(
Expand All @@ -223,7 +231,7 @@ impl Client {

#[cfg(feature = "internal")]
pub fn is_authed(&self) -> bool {
self.token.is_some() || self.auth_settings.is_some()
self.token.is_some() || self.login_method.is_some()
}

#[cfg(feature = "internal")]
Expand All @@ -233,13 +241,13 @@ impl Client {
user_key: EncString,
private_key: EncString,
) -> Result<&EncryptionSettings> {
let auth = match &self.auth_settings {
let login_method = match &self.login_method {
Some(a) => a,
None => return Err(Error::NotAuthenticated),
};

self.encryption_settings = Some(EncryptionSettings::new(
auth,
login_method,
password,
user_key,
private_key,
Expand Down
51 changes: 30 additions & 21 deletions crates/bitwarden/src/client/encryption_settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use rsa::RsaPrivateKey;
use uuid::Uuid;
#[cfg(feature = "internal")]
use {
crate::client::auth_settings::AuthSettings,
crate::client::LoginMethod,
rsa::{pkcs8::DecodePrivateKey, Oaep},
};

Expand All @@ -28,30 +28,39 @@ impl std::fmt::Debug for EncryptionSettings {
impl EncryptionSettings {
#[cfg(feature = "internal")]
pub(crate) fn new(
auth: &AuthSettings,
login_method: &LoginMethod,
password: &str,
user_key: EncString,
private_key: EncString,
) -> Result<Self> {
use crate::crypto::MasterKey;

// Derive master key from password
let master_key = MasterKey::derive(password.as_bytes(), auth.email.as_bytes(), &auth.kdf)?;

// Decrypt the user key
let user_key = master_key.decrypt_user_key(user_key)?;

// Decrypt the private key with the user key
let private_key = {
let dec = private_key.decrypt_with_key(&user_key)?;
Some(rsa::RsaPrivateKey::from_pkcs8_der(&dec).map_err(|_| CryptoError::InvalidKey)?)
};

Ok(EncryptionSettings {
user_key,
private_key,
org_keys: HashMap::new(),
})
use crate::{client::UserLoginMethod, crypto::MasterKey};

match login_method {
LoginMethod::User(UserLoginMethod::Username { email, kdf, .. }) => {
// Derive master key from password
let master_key = MasterKey::derive(password.as_bytes(), email.as_bytes(), kdf)?;

// Decrypt the user key
let user_key = master_key.decrypt_user_key(user_key)?;

// Decrypt the private key with the user key
let private_key = {
let dec = private_key.decrypt_with_key(&user_key)?;
Some(
rsa::RsaPrivateKey::from_pkcs8_der(&dec)
.map_err(|_| CryptoError::InvalidKey)?,
)
};

Ok(EncryptionSettings {
user_key,
private_key,
org_keys: HashMap::new(),
})
}
LoginMethod::User(UserLoginMethod::ApiKey { .. }) => todo!(),
LoginMethod::ServiceAccount(_) => todo!(),
}
}

pub(crate) fn new_single_key(key: SymmetricCryptoKey) -> Self {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,11 @@ use bitwarden_api_identity::models::{KdfType, PreloginResponseModel};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::error::Error;
#[cfg(feature = "internal")]
use crate::{
crypto::{HashPurpose, MasterKey},
error::Result,
};
use crate::error::Result;

#[derive(Debug)]
pub(crate) struct AuthSettings {
#[cfg(feature = "internal")]
pub email: String,
#[cfg(feature = "internal")]
pub(crate) kdf: Kdf,
}

#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[derive(Serialize, Deserialize, Debug, JsonSchema, Clone)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[cfg_attr(feature = "mobile", derive(uniffi::Enum))]
pub enum Kdf {
Expand All @@ -33,15 +23,19 @@ pub enum Kdf {
},
}

impl AuthSettings {
#[cfg(feature = "internal")]
pub fn new(response: PreloginResponseModel, email: String) -> Self {
#[cfg(feature = "internal")]
impl TryFrom<PreloginResponseModel> for Kdf {
type Error = Error;

fn try_from(response: PreloginResponseModel) -> Result<Kdf> {
use crate::util::{
default_argon2_iterations, default_argon2_memory, default_argon2_parallelism,
default_pbkdf2_iterations,
};

let kdf = match response.kdf.unwrap_or_default() {
let kdf = response.kdf.ok_or(Error::Internal("KDF not found"))?;

Ok(match kdf {
KdfType::Variant0 => Kdf::PBKDF2 {
iterations: response
.kdf_iterations
Expand All @@ -62,14 +56,6 @@ impl AuthSettings {
.and_then(|e| NonZeroU32::new(e as u32))
.unwrap_or_else(default_argon2_parallelism),
},
};

Self { email, kdf }
}

#[cfg(feature = "internal")]
pub fn derive_user_password_hash(&self, password: &str) -> Result<String> {
let master_key = MasterKey::derive(password.as_bytes(), self.email.as_bytes(), &self.kdf)?;
master_key.derive_master_key_hash(password.as_bytes(), HashPurpose::ServerAuthorization)
})
}
}
Loading

0 comments on commit 03412b9

Please sign in to comment.