|  | @ -49,6 +49,8 @@ pub struct RegisterData { | 
			
		
	
		
		
			
				
					|  |  |     Email: String, |  |  |     Email: String, | 
			
		
	
		
		
			
				
					|  |  |     Kdf: Option<i32>, |  |  |     Kdf: Option<i32>, | 
			
		
	
		
		
			
				
					|  |  |     KdfIterations: Option<i32>, |  |  |     KdfIterations: Option<i32>, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     KdfMemory: Option<i32>, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     KdfParallelism: Option<i32>, | 
			
		
	
		
		
			
				
					|  |  |     Key: String, |  |  |     Key: String, | 
			
		
	
		
		
			
				
					|  |  |     Keys: Option<KeysData>, |  |  |     Keys: Option<KeysData>, | 
			
		
	
		
		
			
				
					|  |  |     MasterPasswordHash: String, |  |  |     MasterPasswordHash: String, | 
			
		
	
	
		
		
			
				
					|  | @ -153,13 +155,16 @@ pub async fn _register(data: JsonUpcase<RegisterData>, mut conn: DbConn) -> Json | 
			
		
	
		
		
			
				
					|  |  |     // Make sure we don't leave a lingering invitation.
 |  |  |     // Make sure we don't leave a lingering invitation.
 | 
			
		
	
		
		
			
				
					|  |  |     Invitation::take(&email, &mut conn).await; |  |  |     Invitation::take(&email, &mut conn).await; | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     if let Some(client_kdf_type) = data.Kdf { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         user.client_kdf_type = client_kdf_type; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     if let Some(client_kdf_iter) = data.KdfIterations { |  |  |     if let Some(client_kdf_iter) = data.KdfIterations { | 
			
		
	
		
		
			
				
					|  |  |         user.client_kdf_iter = client_kdf_iter; |  |  |         user.client_kdf_iter = client_kdf_iter; | 
			
		
	
		
		
			
				
					|  |  |     } |  |  |     } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |     if let Some(client_kdf_type) = data.Kdf { |  |  |     user.client_kdf_parallelism = data.KdfMemory; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |         user.client_kdf_type = client_kdf_type; |  |  |     user.client_kdf_memory = data.KdfParallelism; | 
			
				
				
			
		
	
		
		
			
				
					|  |  |     } |  |  |  | 
			
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     user.set_password(&data.MasterPasswordHash, Some(data.Key), true, None); |  |  |     user.set_password(&data.MasterPasswordHash, Some(data.Key), true, None); | 
			
		
	
		
		
			
				
					|  |  |     user.password_hint = password_hint; |  |  |     user.password_hint = password_hint; | 
			
		
	
	
		
		
			
				
					|  | @ -337,6 +342,8 @@ async fn post_password( | 
			
		
	
		
		
			
				
					|  |  | struct ChangeKdfData { |  |  | struct ChangeKdfData { | 
			
		
	
		
		
			
				
					|  |  |     Kdf: i32, |  |  |     Kdf: i32, | 
			
		
	
		
		
			
				
					|  |  |     KdfIterations: i32, |  |  |     KdfIterations: i32, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     KdfMemory: Option<i32>, | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     KdfParallelism: Option<i32>, | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |     MasterPasswordHash: String, |  |  |     MasterPasswordHash: String, | 
			
		
	
		
		
			
				
					|  |  |     NewMasterPasswordHash: String, |  |  |     NewMasterPasswordHash: String, | 
			
		
	
	
		
		
			
				
					|  | @ -352,10 +359,31 @@ async fn post_kdf(data: JsonUpcase<ChangeKdfData>, headers: Headers, mut conn: D | 
			
		
	
		
		
			
				
					|  |  |         err!("Invalid password") |  |  |         err!("Invalid password") | 
			
		
	
		
		
			
				
					|  |  |     } |  |  |     } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |     if data.KdfIterations < 100_000 { |  |  |     if data.Kdf == UserKdfType::Pbkdf2 as i32 && data.KdfIterations < 100_000 { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |         err!("KDF iterations lower then 100000 are not allowed.") |  |  |         err!("PBKDF2 KDF iterations must be at least 100000.") | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  |     } |  |  |     } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     if data.Kdf == UserKdfType::Argon2id as i32 { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         if data.KdfIterations < 1 { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             err!("Argon2 KDF iterations must be at least 1.") | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         } | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         if let Some(m) = data.KdfMemory { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             if !(15..=1024).contains(&m) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                 err!("Argon2 memory must be between 15 MB and 1024 MB.") | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             } | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             user.client_kdf_memory = data.KdfMemory; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         } else { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             err!("Argon2 memory parameter is required.") | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         } | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         if let Some(p) = data.KdfParallelism { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             if !(1..=16).contains(&p) { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |                 err!("Argon2 parallelism must be between 1 and 16.") | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             } | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             user.client_kdf_parallelism = data.KdfParallelism; | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         } else { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |             err!("Argon2 parallelism parameter is required.") | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         } | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |     user.client_kdf_iter = data.KdfIterations; |  |  |     user.client_kdf_iter = data.KdfIterations; | 
			
		
	
		
		
			
				
					|  |  |     user.client_kdf_type = data.Kdf; |  |  |     user.client_kdf_type = data.Kdf; | 
			
		
	
		
		
			
				
					|  |  |     user.set_password(&data.NewMasterPasswordHash, Some(data.Key), true, None); |  |  |     user.set_password(&data.NewMasterPasswordHash, Some(data.Key), true, None); | 
			
		
	
	
		
		
			
				
					|  | @ -770,15 +798,22 @@ async fn prelogin(data: JsonUpcase<PreloginData>, conn: DbConn) -> Json<Value> { | 
			
		
	
		
		
			
				
					|  |  | pub async fn _prelogin(data: JsonUpcase<PreloginData>, mut conn: DbConn) -> Json<Value> { |  |  | pub async fn _prelogin(data: JsonUpcase<PreloginData>, mut conn: DbConn) -> Json<Value> { | 
			
		
	
		
		
			
				
					|  |  |     let data: PreloginData = data.into_inner().data; |  |  |     let data: PreloginData = data.into_inner().data; | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |     let (kdf_type, kdf_iter) = match User::find_by_mail(&data.Email, &mut conn).await { |  |  |     let (kdf_type, kdf_iter, kdf_mem, kdf_para) = match User::find_by_mail(&data.Email, &mut conn).await { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |         Some(user) => (user.client_kdf_type, user.client_kdf_iter), |  |  |         Some(user) => (user.client_kdf_type, user.client_kdf_iter, user.client_kdf_memory, user.client_kdf_parallelism), | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |         None => (User::CLIENT_KDF_TYPE_DEFAULT, User::CLIENT_KDF_ITER_DEFAULT), |  |  |         None => (User::CLIENT_KDF_TYPE_DEFAULT, User::CLIENT_KDF_ITER_DEFAULT, None, None), | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  |     }; |  |  |     }; | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |     Json(json!({ |  |  |     let mut result = json!({ | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					|  |  |         "Kdf": kdf_type, |  |  |         "Kdf": kdf_type, | 
			
		
	
		
		
			
				
					
					|  |  |         "KdfIterations": kdf_iter |  |  |         "KdfIterations": kdf_iter, | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |     })) |  |  |     }); | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     if kdf_type == UserKdfType::Argon2id as i32 { | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         result["KdfMemory"] = Value::Number(kdf_mem.expect("Argon2 memory parameter is required.").into()); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |         result["KdfParallelism"] = Value::Number(kdf_para.expect("Argon2 parallelism parameter is required.").into()); | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     } | 
			
		
	
		
		
			
				
					|  |  |  |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  |  |  |  |     Json(result) | 
			
		
	
		
		
			
				
					|  |  | } |  |  | } | 
			
		
	
		
		
			
				
					|  |  | 
 |  |  | 
 | 
			
		
	
		
		
			
				
					|  |  | // https://github.com/bitwarden/server/blob/master/src/Api/Models/Request/Accounts/SecretVerificationRequestModel.cs
 |  |  | // https://github.com/bitwarden/server/blob/master/src/Api/Models/Request/Accounts/SecretVerificationRequestModel.cs
 | 
			
		
	
	
		
		
			
				
					|  | 
 |