bitwarden_core/auth/password/
validate.rs1use bitwarden_crypto::{HashPurpose, MasterKey};
2
3use crate::{
4 auth::{password::determine_password_hash, AuthValidateError},
5 client::{LoginMethod, UserLoginMethod},
6 Client, NotAuthenticatedError, WrongPasswordError,
7};
8
9pub(crate) fn validate_password(
11 client: &Client,
12 password: String,
13 password_hash: String,
14) -> Result<bool, AuthValidateError> {
15 let login_method = client
16 .internal
17 .get_login_method()
18 .ok_or(NotAuthenticatedError)?;
19
20 #[allow(irrefutable_let_patterns)]
21 if let LoginMethod::User(login_method) = login_method.as_ref() {
22 match login_method {
23 UserLoginMethod::Username { email, kdf, .. }
24 | UserLoginMethod::ApiKey { email, kdf, .. } => {
25 let hash = determine_password_hash(
26 email,
27 kdf,
28 &password,
29 HashPurpose::LocalAuthorization,
30 )?;
31
32 Ok(hash == password_hash)
33 }
34 }
35 } else {
36 Err(NotAuthenticatedError)?
37 }
38}
39
40pub(crate) fn validate_password_user_key(
41 client: &Client,
42 password: String,
43 encrypted_user_key: String,
44) -> Result<String, AuthValidateError> {
45 use crate::key_management::SymmetricKeyId;
46
47 let login_method = client
48 .internal
49 .get_login_method()
50 .ok_or(NotAuthenticatedError)?;
51
52 #[allow(irrefutable_let_patterns)]
53 if let LoginMethod::User(login_method) = login_method.as_ref() {
54 match login_method {
55 UserLoginMethod::Username { email, kdf, .. }
56 | UserLoginMethod::ApiKey { email, kdf, .. } => {
57 let master_key = MasterKey::derive(&password, email, kdf)?;
58 let user_key = master_key
59 .decrypt_user_key(encrypted_user_key.parse()?)
60 .map_err(|_| WrongPasswordError)?;
61
62 let key_store = client.internal.get_key_store();
63 let ctx = key_store.context();
64 #[allow(deprecated)]
66 let existing_key = ctx.dangerous_get_symmetric_key(SymmetricKeyId::User)?;
67
68 if user_key != *existing_key {
69 return Err(AuthValidateError::WrongUserKey);
70 }
71
72 Ok(master_key
73 .derive_master_key_hash(password.as_bytes(), HashPurpose::LocalAuthorization)?)
74 }
75 }
76 } else {
77 Err(NotAuthenticatedError)?
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use bitwarden_crypto::Kdf;
84
85 use crate::auth::password::{validate::validate_password_user_key, validate_password};
86
87 #[test]
88 fn test_validate_password() {
89 use std::num::NonZeroU32;
90
91 use crate::client::{Client, LoginMethod, UserLoginMethod};
92
93 let client = Client::new(None);
94 client
95 .internal
96 .set_login_method(LoginMethod::User(UserLoginMethod::Username {
97 email: "[email protected]".to_string(),
98 kdf: Kdf::PBKDF2 {
99 iterations: NonZeroU32::new(100_000).unwrap(),
100 },
101 client_id: "1".to_string(),
102 }));
103
104 let password = "password123".to_string();
105 let password_hash = "7kTqkF1pY/3JeOu73N9kR99fDDe9O1JOZaVc7KH3lsU=".to_string();
106
107 let result = validate_password(&client, password, password_hash);
108
109 assert!(result.unwrap());
110 }
111
112 #[test]
113 fn test_validate_password_user_key() {
114 use std::num::NonZeroU32;
115
116 use bitwarden_crypto::{Kdf, MasterKey};
117
118 use crate::client::{Client, LoginMethod, UserLoginMethod};
119
120 let client = Client::new(None);
121
122 let password = "asdfasdfasdf";
123 let email = "[email protected]";
124 let kdf = Kdf::PBKDF2 {
125 iterations: NonZeroU32::new(600_000).unwrap(),
126 };
127
128 client
129 .internal
130 .set_login_method(LoginMethod::User(UserLoginMethod::Username {
131 email: email.to_string(),
132 kdf: kdf.clone(),
133 client_id: "1".to_string(),
134 }));
135
136 let master_key = MasterKey::derive(password, email, &kdf).unwrap();
137
138 let user_key = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=";
139 let private_key = "2.yN7l00BOlUE0Sb0M//Q53w==|EwKG/BduQRQ33Izqc/ogoBROIoI5dmgrxSo82sgzgAMIBt3A2FZ9vPRMY+GWT85JiqytDitGR3TqwnFUBhKUpRRAq4x7rA6A1arHrFp5Tp1p21O3SfjtvB3quiOKbqWk6ZaU1Np9HwqwAecddFcB0YyBEiRX3VwF2pgpAdiPbSMuvo2qIgyob0CUoC/h4Bz1be7Qa7B0Xw9/fMKkB1LpOm925lzqosyMQM62YpMGkjMsbZz0uPopu32fxzDWSPr+kekNNyLt9InGhTpxLmq1go/pXR2uw5dfpXc5yuta7DB0EGBwnQ8Vl5HPdDooqOTD9I1jE0mRyuBpWTTI3FRnu3JUh3rIyGBJhUmHqGZvw2CKdqHCIrQeQkkEYqOeJRJVdBjhv5KGJifqT3BFRwX/YFJIChAQpebNQKXe/0kPivWokHWwXlDB7S7mBZzhaAPidZvnuIhalE2qmTypDwHy22FyqV58T8MGGMchcASDi/QXI6kcdpJzPXSeU9o+NC68QDlOIrMVxKFeE7w7PvVmAaxEo0YwmuAzzKy9QpdlK0aab/xEi8V4iXj4hGepqAvHkXIQd+r3FNeiLfllkb61p6WTjr5urcmDQMR94/wYoilpG5OlybHdbhsYHvIzYoLrC7fzl630gcO6t4nM24vdB6Ymg9BVpEgKRAxSbE62Tqacxqnz9AcmgItb48NiR/He3n3ydGjPYuKk/ihZMgEwAEZvSlNxYONSbYrIGDtOY+8Nbt6KiH3l06wjZW8tcmFeVlWv+tWotnTY9IqlAfvNVTjtsobqtQnvsiDjdEVtNy/s2ci5TH+NdZluca2OVEr91Wayxh70kpM6ib4UGbfdmGgCo74gtKvKSJU0rTHakQ5L9JlaSDD5FamBRyI0qfL43Ad9qOUZ8DaffDCyuaVyuqk7cz9HwmEmvWU3VQ+5t06n/5kRDXttcw8w+3qClEEdGo1KeENcnXCB32dQe3tDTFpuAIMLqwXs6FhpawfZ5kPYvLPczGWaqftIs/RXJ/EltGc0ugw2dmTLpoQhCqrcKEBDoYVk0LDZKsnzitOGdi9mOWse7Se8798ib1UsHFUjGzISEt6upestxOeupSTOh0v4+AjXbDzRUyogHww3V+Bqg71bkcMxtB+WM+pn1XNbVTyl9NR040nhP7KEf6e9ruXAtmrBC2ah5cFEpLIot77VFZ9ilLuitSz+7T8n1yAh1IEG6xxXxninAZIzi2qGbH69O5RSpOJuJTv17zTLJQIIc781JwQ2TTwTGnx5wZLbffhCasowJKd2EVcyMJyhz6ru0PvXWJ4hUdkARJs3Xu8dus9a86N8Xk6aAPzBDqzYb1vyFIfBxP0oO8xFHgd30Cgmz8UrSE3qeWRrF8ftrI6xQnFjHBGWD/JWSvd6YMcQED0aVuQkuNW9ST/DzQThPzRfPUoiL10yAmV7Ytu4fR3x2sF0Yfi87YhHFuCMpV/DsqxmUizyiJuD938eRcH8hzR/VO53Qo3UIsqOLcyXtTv6THjSlTopQ+JOLOnHm1w8dzYbLN44OG44rRsbihMUQp+wUZ6bsI8rrOnm9WErzkbQFbrfAINdoCiNa6cimYIjvvnMTaFWNymqY1vZxGztQiMiHiHYwTfwHTXrb9j0uPM=|09J28iXv9oWzYtzK2LBT6Yht4IT4MijEkk0fwFdrVQ4=".parse().unwrap();
140
141 client
142 .internal
143 .initialize_user_crypto_master_key(master_key, user_key.parse().unwrap(), private_key)
144 .unwrap();
145
146 let result =
147 validate_password_user_key(&client, "asdfasdfasdf".to_owned(), user_key.to_string())
148 .unwrap();
149
150 assert_eq!(result, "aOvkBXFhSdgrBWR3hZCMRoML9+h5yRblU3lFphCdkeA=");
151 assert!(validate_password(&client, password.to_owned(), result).unwrap())
152 }
153
154 #[cfg(feature = "internal")]
155 #[test]
156 fn test_validate_password_user_key_wrong_password() {
157 use std::num::NonZeroU32;
158
159 use bitwarden_crypto::{Kdf, MasterKey};
160
161 use crate::client::{Client, LoginMethod, UserLoginMethod};
162
163 let client = Client::new(None);
164
165 let password = "asdfasdfasdf";
166 let email = "[email protected]";
167 let kdf = Kdf::PBKDF2 {
168 iterations: NonZeroU32::new(600_000).unwrap(),
169 };
170
171 client
172 .internal
173 .set_login_method(LoginMethod::User(UserLoginMethod::Username {
174 email: email.to_string(),
175 kdf: kdf.clone(),
176 client_id: "1".to_string(),
177 }));
178
179 let master_key = MasterKey::derive(password, email, &kdf).unwrap();
180
181 let user_key = "2.Q/2PhzcC7GdeiMHhWguYAQ==|GpqzVdr0go0ug5cZh1n+uixeBC3oC90CIe0hd/HWA/pTRDZ8ane4fmsEIcuc8eMKUt55Y2q/fbNzsYu41YTZzzsJUSeqVjT8/iTQtgnNdpo=|dwI+uyvZ1h/iZ03VQ+/wrGEFYVewBUUl/syYgjsNMbE=";
182 let private_key = "2.yN7l00BOlUE0Sb0M//Q53w==|EwKG/BduQRQ33Izqc/ogoBROIoI5dmgrxSo82sgzgAMIBt3A2FZ9vPRMY+GWT85JiqytDitGR3TqwnFUBhKUpRRAq4x7rA6A1arHrFp5Tp1p21O3SfjtvB3quiOKbqWk6ZaU1Np9HwqwAecddFcB0YyBEiRX3VwF2pgpAdiPbSMuvo2qIgyob0CUoC/h4Bz1be7Qa7B0Xw9/fMKkB1LpOm925lzqosyMQM62YpMGkjMsbZz0uPopu32fxzDWSPr+kekNNyLt9InGhTpxLmq1go/pXR2uw5dfpXc5yuta7DB0EGBwnQ8Vl5HPdDooqOTD9I1jE0mRyuBpWTTI3FRnu3JUh3rIyGBJhUmHqGZvw2CKdqHCIrQeQkkEYqOeJRJVdBjhv5KGJifqT3BFRwX/YFJIChAQpebNQKXe/0kPivWokHWwXlDB7S7mBZzhaAPidZvnuIhalE2qmTypDwHy22FyqV58T8MGGMchcASDi/QXI6kcdpJzPXSeU9o+NC68QDlOIrMVxKFeE7w7PvVmAaxEo0YwmuAzzKy9QpdlK0aab/xEi8V4iXj4hGepqAvHkXIQd+r3FNeiLfllkb61p6WTjr5urcmDQMR94/wYoilpG5OlybHdbhsYHvIzYoLrC7fzl630gcO6t4nM24vdB6Ymg9BVpEgKRAxSbE62Tqacxqnz9AcmgItb48NiR/He3n3ydGjPYuKk/ihZMgEwAEZvSlNxYONSbYrIGDtOY+8Nbt6KiH3l06wjZW8tcmFeVlWv+tWotnTY9IqlAfvNVTjtsobqtQnvsiDjdEVtNy/s2ci5TH+NdZluca2OVEr91Wayxh70kpM6ib4UGbfdmGgCo74gtKvKSJU0rTHakQ5L9JlaSDD5FamBRyI0qfL43Ad9qOUZ8DaffDCyuaVyuqk7cz9HwmEmvWU3VQ+5t06n/5kRDXttcw8w+3qClEEdGo1KeENcnXCB32dQe3tDTFpuAIMLqwXs6FhpawfZ5kPYvLPczGWaqftIs/RXJ/EltGc0ugw2dmTLpoQhCqrcKEBDoYVk0LDZKsnzitOGdi9mOWse7Se8798ib1UsHFUjGzISEt6upestxOeupSTOh0v4+AjXbDzRUyogHww3V+Bqg71bkcMxtB+WM+pn1XNbVTyl9NR040nhP7KEf6e9ruXAtmrBC2ah5cFEpLIot77VFZ9ilLuitSz+7T8n1yAh1IEG6xxXxninAZIzi2qGbH69O5RSpOJuJTv17zTLJQIIc781JwQ2TTwTGnx5wZLbffhCasowJKd2EVcyMJyhz6ru0PvXWJ4hUdkARJs3Xu8dus9a86N8Xk6aAPzBDqzYb1vyFIfBxP0oO8xFHgd30Cgmz8UrSE3qeWRrF8ftrI6xQnFjHBGWD/JWSvd6YMcQED0aVuQkuNW9ST/DzQThPzRfPUoiL10yAmV7Ytu4fR3x2sF0Yfi87YhHFuCMpV/DsqxmUizyiJuD938eRcH8hzR/VO53Qo3UIsqOLcyXtTv6THjSlTopQ+JOLOnHm1w8dzYbLN44OG44rRsbihMUQp+wUZ6bsI8rrOnm9WErzkbQFbrfAINdoCiNa6cimYIjvvnMTaFWNymqY1vZxGztQiMiHiHYwTfwHTXrb9j0uPM=|09J28iXv9oWzYtzK2LBT6Yht4IT4MijEkk0fwFdrVQ4=".parse().unwrap();
183
184 client
185 .internal
186 .initialize_user_crypto_master_key(master_key, user_key.parse().unwrap(), private_key)
187 .unwrap();
188
189 let result =
190 validate_password_user_key(&client, "asdfasdfasdf".to_string(), user_key.to_string())
191 .unwrap();
192
193 assert_eq!(result, "aOvkBXFhSdgrBWR3hZCMRoML9+h5yRblU3lFphCdkeA=");
194 assert!(validate_password(&client, "asdfasdfasdf".to_string(), result.to_string()).unwrap())
195 }
196}