diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index 98a8cef4..536564d4 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -75,12 +75,16 @@ pub fn routes() -> Vec { ] } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, Eq, PartialEq)] #[serde(rename_all = "camelCase")] pub struct KDFData { + #[serde(alias = "kdfType")] kdf: i32, + #[serde(alias = "iterations")] kdf_iterations: i32, + #[serde(alias = "memory")] kdf_memory: Option, + #[serde(alias = "parallelism")] kdf_parallelism: Option, } @@ -545,17 +549,6 @@ async fn post_password(data: Json, headers: Headers, conn: DbCon save_result } -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct ChangeKdfData { - #[serde(flatten)] - kdf: KDFData, - - master_password_hash: String, - new_master_password_hash: String, - key: String, -} - fn set_kdf_data(user: &mut User, data: &KDFData) -> EmptyResult { if data.kdf == UserKdfType::Pbkdf2 as i32 && data.kdf_iterations < 100_000 { err!("PBKDF2 KDF iterations must be at least 100000.") @@ -591,18 +584,61 @@ fn set_kdf_data(user: &mut User, data: &KDFData) -> EmptyResult { Ok(()) } +#[allow(dead_code)] +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct AuthenticationData { + salt: String, + kdf: KDFData, + master_password_authentication_hash: String, +} + +#[allow(dead_code)] +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct UnlockData { + salt: String, + kdf: KDFData, + master_key_wrapped_user_key: String, +} + +#[allow(dead_code)] +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +struct ChangeKdfData { + new_master_password_hash: String, + key: String, + authentication_data: AuthenticationData, + unlock_data: UnlockData, + master_password_hash: String, +} + #[post("/accounts/kdf", data = "")] async fn post_kdf(data: Json, headers: Headers, conn: DbConn, nt: Notify<'_>) -> EmptyResult { let data: ChangeKdfData = data.into_inner(); - let mut user = headers.user; - if !user.check_valid_password(&data.master_password_hash) { + if !headers.user.check_valid_password(&data.master_password_hash) { err!("Invalid password") } - set_kdf_data(&mut user, &data.kdf)?; + if data.authentication_data.kdf != data.unlock_data.kdf { + err!("KDF settings must be equal for authentication and unlock") + } - user.set_password(&data.new_master_password_hash, Some(data.key), true, None); + if headers.user.email != data.authentication_data.salt || headers.user.email != data.unlock_data.salt { + err!("Invalid master password salt") + } + + let mut user = headers.user; + + set_kdf_data(&mut user, &data.unlock_data.kdf)?; + + user.set_password( + &data.authentication_data.master_password_authentication_hash, + Some(data.unlock_data.master_key_wrapped_user_key), + true, + None, + ); let save_result = user.save(&conn).await; nt.send_logout(&user, Some(headers.device.uuid.clone()), &conn).await;