diff --git a/src/api/core/accounts.rs b/src/api/core/accounts.rs index 2b14a269..556ba1f4 100644 --- a/src/api/core/accounts.rs +++ b/src/api/core/accounts.rs @@ -708,18 +708,32 @@ fn set_kdf_data(user: &mut User, data: &KDFData) -> EmptyResult { #[derive(Deserialize)] #[serde(rename_all = "camelCase")] -struct AuthenticationData { +pub(super) struct AuthenticationData { salt: String, - kdf: KDFData, - master_password_authentication_hash: String, + pub(super) kdf: KDFData, + pub(super) master_password_authentication_hash: String, +} + +impl AuthenticationData { + pub(super) fn check(&self, user: &User, unlock: &UnlockData) -> EmptyResult { + if self.kdf != unlock.kdf { + err!("KDF settings must be equal for authentication and unlock") + } + + if self.salt != user.master_password_salt() || self.salt != unlock.salt { + err!("Invalid master password salt") + } + + Ok(()) + } } #[derive(Deserialize)] #[serde(rename_all = "camelCase")] -struct UnlockData { +pub(super) struct UnlockData { salt: String, kdf: KDFData, - master_key_wrapped_user_key: String, + pub(super) master_key_wrapped_user_key: String, } #[derive(Deserialize)] @@ -738,13 +752,7 @@ async fn post_kdf(data: Json, headers: Headers, conn: DbConn, nt: err!("Invalid password") } - if data.authentication_data.kdf != data.unlock_data.kdf { - err!("KDF settings must be equal for authentication and unlock") - } - - if headers.user.email != data.authentication_data.salt || headers.user.email != data.unlock_data.salt { - err!("Invalid master password salt") - } + data.authentication_data.check(&headers.user, &data.unlock_data)?; let mut user = headers.user; diff --git a/src/api/core/emergency_access.rs b/src/api/core/emergency_access.rs index beb340ba..293ff2e0 100644 --- a/src/api/core/emergency_access.rs +++ b/src/api/core/emergency_access.rs @@ -6,7 +6,10 @@ use crate::{ CONFIG, api::{ EmptyResult, JsonResult, - core::{CipherSyncData, CipherSyncType, accounts::KDFData}, + core::{ + CipherSyncData, CipherSyncType, + accounts::{AuthenticationData, UnlockData}, + }, }, auth::{Headers, decode_emergency_access_invite}, db::{ @@ -615,29 +618,13 @@ async fn takeover_emergency_access(emer_id: EmergencyAccessId, headers: Headers, "kdfMemory": grantor_user.client_kdf_memory, "kdfParallelism": grantor_user.client_kdf_parallelism, "keyEncrypted": &emergency_access.key_encrypted, - "salt": master_password_salt(&grantor_user), + "salt": grantor_user.master_password_salt(), "object": "emergencyAccessTakeover", }); Ok(Json(result)) } -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct EmergencyAccessAuthenticationData { - salt: String, - kdf: KDFData, - master_password_authentication_hash: String, -} - -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -struct EmergencyAccessUnlockData { - salt: String, - kdf: KDFData, - master_key_wrapped_user_key: String, -} - #[derive(Deserialize)] #[serde(rename_all = "camelCase")] struct EmergencyAccessPasswordData { @@ -646,8 +633,8 @@ struct EmergencyAccessPasswordData { key: Option, // Current payload - authentication_data: Option, - unlock_data: Option, + authentication_data: Option, + unlock_data: Option, } #[post("/emergency-access//password", data = "")] @@ -678,22 +665,12 @@ async fn password_emergency_access( let (new_master_password_hash, new_key) = if let (Some(authentication_data), Some(unlock_data)) = (data.authentication_data, data.unlock_data) { - if authentication_data.kdf != unlock_data.kdf { - err!("KDF settings must be equal for authentication and unlock") - } - - if authentication_data.salt != unlock_data.salt { - err!("Invalid master password salt") - } + authentication_data.check(&grantor_user, &unlock_data)?; if !authentication_data.kdf.matches_user(&grantor_user) { err!("KDF settings do not match the grantor account") } - if authentication_data.salt != master_password_salt(&grantor_user) { - err!("Invalid master password salt") - } - (authentication_data.master_password_authentication_hash, unlock_data.master_key_wrapped_user_key) } else if let (Some(new_master_password_hash), Some(new_key)) = (data.new_master_password_hash, data.key) { (new_master_password_hash, new_key) @@ -746,10 +723,6 @@ async fn policies_emergency_access(emer_id: EmergencyAccessId, headers: Headers, }))) } -fn master_password_salt(user: &User) -> String { - user.email.trim().to_lowercase() -} - fn is_valid_request( emergency_access: &EmergencyAccess, requesting_user_id: &UserId, diff --git a/src/db/models/user.rs b/src/db/models/user.rs index 81cb8d84..17bfb425 100644 --- a/src/db/models/user.rs +++ b/src/db/models/user.rs @@ -166,6 +166,10 @@ impl User { ) } + pub fn master_password_salt(&self) -> String { + self.email.trim().to_lowercase() + } + pub fn check_valid_recovery_code(&self, recovery_code: &str) -> bool { if let Some(ref totp_recover) = self.totp_recover { crypto::ct_eq(recovery_code, totp_recover.to_lowercase())