1 //
2 // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2021
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 #include "td/telegram/PasswordManager.h"
8 
9 #include "td/telegram/ConfigManager.h"
10 #include "td/telegram/ConfigShared.h"
11 #include "td/telegram/DhCache.h"
12 #include "td/telegram/Global.h"
13 #include "td/telegram/logevent/LogEvent.h"
14 #include "td/telegram/net/NetQueryDispatcher.h"
15 #include "td/telegram/SuggestedAction.h"
16 #include "td/telegram/TdDb.h"
17 
18 #include "td/mtproto/DhHandshake.h"
19 
20 #include "td/utils/BigNum.h"
21 #include "td/utils/buffer.h"
22 #include "td/utils/crypto.h"
23 #include "td/utils/format.h"
24 #include "td/utils/logging.h"
25 #include "td/utils/misc.h"
26 #include "td/utils/Random.h"
27 #include "td/utils/Slice.h"
28 #include "td/utils/SliceBuilder.h"
29 #include "td/utils/Time.h"
30 
31 namespace td {
32 
get_temporary_password_state_object() const33 tl_object_ptr<td_api::temporaryPasswordState> TempPasswordState::get_temporary_password_state_object() const {
34   if (!has_temp_password || valid_until <= G()->unix_time()) {
35     return make_tl_object<td_api::temporaryPasswordState>(false, 0);
36   }
37   return make_tl_object<td_api::temporaryPasswordState>(true, valid_until - G()->unix_time_cached());
38 }
39 
hash_sha256(Slice data,Slice salt,MutableSlice dest)40 static void hash_sha256(Slice data, Slice salt, MutableSlice dest) {
41   sha256(PSLICE() << salt << data << salt, dest);
42 }
43 
calc_password_hash(Slice password,Slice client_salt,Slice server_salt)44 BufferSlice PasswordManager::calc_password_hash(Slice password, Slice client_salt, Slice server_salt) {
45   LOG(INFO) << "Begin password hash calculation";
46   BufferSlice buf(32);
47   hash_sha256(password, client_salt, buf.as_slice());
48   hash_sha256(buf.as_slice(), server_salt, buf.as_slice());
49   BufferSlice hash(64);
50   pbkdf2_sha512(buf.as_slice(), client_salt, 100000, hash.as_slice());
51   hash_sha256(hash.as_slice(), server_salt, buf.as_slice());
52   LOG(INFO) << "End password hash calculation";
53   return buf;
54 }
55 
calc_password_srp_hash(Slice password,Slice client_salt,Slice server_salt,int32 g,Slice p)56 Result<BufferSlice> PasswordManager::calc_password_srp_hash(Slice password, Slice client_salt, Slice server_salt,
57                                                             int32 g, Slice p) {
58   LOG(INFO) << "Begin password SRP hash calculation";
59   TRY_STATUS(mtproto::DhHandshake::check_config(g, p, DhCache::instance()));
60 
61   auto hash = calc_password_hash(password, client_salt, server_salt);
62   auto p_bn = BigNum::from_binary(p);
63   BigNum g_bn;
64   g_bn.set_value(g);
65   auto x_bn = BigNum::from_binary(hash.as_slice());
66 
67   BigNumContext ctx;
68   BigNum v_bn;
69   BigNum::mod_exp(v_bn, g_bn, x_bn, p_bn, ctx);
70 
71   BufferSlice result(v_bn.to_binary(256));
72   LOG(INFO) << "End password SRP hash calculation";
73   return std::move(result);
74 }
75 
get_input_check_password(Slice password,Slice client_salt,Slice server_salt,int32 g,Slice p,Slice B,int64 id)76 tl_object_ptr<telegram_api::InputCheckPasswordSRP> PasswordManager::get_input_check_password(
77     Slice password, Slice client_salt, Slice server_salt, int32 g, Slice p, Slice B, int64 id) {
78   if (password.empty()) {
79     return make_tl_object<telegram_api::inputCheckPasswordEmpty>();
80   }
81 
82   if (mtproto::DhHandshake::check_config(g, p, DhCache::instance()).is_error()) {
83     LOG(ERROR) << "Receive invalid config " << g << " " << format::escaped(p);
84     return make_tl_object<telegram_api::inputCheckPasswordEmpty>();
85   }
86 
87   auto p_bn = BigNum::from_binary(p);
88   auto B_bn = BigNum::from_binary(B);
89   auto zero = BigNum::from_decimal("0").move_as_ok();
90   if (BigNum::compare(zero, B_bn) != -1 || BigNum::compare(B_bn, p_bn) != -1 || B.size() < 248 || B.size() > 256) {
91     LOG(ERROR) << "Receive invalid value of B(" << B.size() << "): " << B_bn << " " << p_bn;
92     return make_tl_object<telegram_api::inputCheckPasswordEmpty>();
93   }
94 
95   LOG(INFO) << "Begin input password SRP hash calculation";
96   BigNum g_bn;
97   g_bn.set_value(g);
98   auto g_padded = g_bn.to_binary(256);
99 
100   auto x = calc_password_hash(password, client_salt, server_salt);
101   auto x_bn = BigNum::from_binary(x.as_slice());
102 
103   BufferSlice a(2048 / 8);
104   Random::secure_bytes(a.as_slice());
105   auto a_bn = BigNum::from_binary(a.as_slice());
106 
107   BigNumContext ctx;
108   BigNum A_bn;
109   BigNum::mod_exp(A_bn, g_bn, a_bn, p_bn, ctx);
110   string A = A_bn.to_binary(256);
111 
112   string B_pad(256 - B.size(), '\0');
113   string u = sha256(PSLICE() << A << B_pad << B);
114   auto u_bn = BigNum::from_binary(u);
115   string k = sha256(PSLICE() << p << g_padded);
116   auto k_bn = BigNum::from_binary(k);
117 
118   BigNum v_bn;
119   BigNum::mod_exp(v_bn, g_bn, x_bn, p_bn, ctx);
120   BigNum kv_bn;
121   BigNum::mod_mul(kv_bn, k_bn, v_bn, p_bn, ctx);
122   BigNum t_bn;
123   BigNum::sub(t_bn, B_bn, kv_bn);
124   if (BigNum::compare(t_bn, zero) == -1) {
125     BigNum::add(t_bn, t_bn, p_bn);
126   }
127   BigNum exp_bn;
128   BigNum::mul(exp_bn, u_bn, x_bn, ctx);
129   BigNum::add(exp_bn, exp_bn, a_bn);
130 
131   BigNum S_bn;
132   BigNum::mod_exp(S_bn, t_bn, exp_bn, p_bn, ctx);
133   string S = S_bn.to_binary(256);
134   auto K = sha256(S);
135 
136   auto h1 = sha256(p);
137   auto h2 = sha256(g_padded);
138   for (size_t i = 0; i < h1.size(); i++) {
139     h1[i] = static_cast<char>(static_cast<unsigned char>(h1[i]) ^ static_cast<unsigned char>(h2[i]));
140   }
141   auto M = sha256(PSLICE() << h1 << sha256(client_salt) << sha256(server_salt) << A << B_pad << B << K);
142 
143   LOG(INFO) << "End input password SRP hash calculation";
144   return make_tl_object<telegram_api::inputCheckPasswordSRP>(id, BufferSlice(A), BufferSlice(M));
145 }
146 
get_input_check_password(Slice password,const PasswordState & state)147 tl_object_ptr<telegram_api::InputCheckPasswordSRP> PasswordManager::get_input_check_password(
148     Slice password, const PasswordState &state) {
149   return get_input_check_password(password, state.current_client_salt, state.current_server_salt, state.current_srp_g,
150                                   state.current_srp_p, state.current_srp_B, state.current_srp_id);
151 }
152 
get_input_check_password_srp(string password,Promise<tl_object_ptr<telegram_api::InputCheckPasswordSRP>> && promise)153 void PasswordManager::get_input_check_password_srp(
154     string password, Promise<tl_object_ptr<telegram_api::InputCheckPasswordSRP>> &&promise) {
155   do_get_state(PromiseCreator::lambda(
156       [promise = std::move(promise), password = std::move(password)](Result<PasswordState> r_state) mutable {
157         if (r_state.is_error()) {
158           return promise.set_error(r_state.move_as_error());
159         }
160         promise.set_value(PasswordManager::get_input_check_password(password, r_state.move_as_ok()));
161       }));
162 }
163 
set_password(string current_password,string new_password,string new_hint,bool set_recovery_email_address,string recovery_email_address,Promise<State> promise)164 void PasswordManager::set_password(string current_password, string new_password, string new_hint,
165                                    bool set_recovery_email_address, string recovery_email_address,
166                                    Promise<State> promise) {
167   UpdateSettings update_settings;
168 
169   update_settings.current_password = std::move(current_password);
170   update_settings.update_password = true;
171   //update_settings.update_secure_secret = true;
172   update_settings.new_password = std::move(new_password);
173   update_settings.new_hint = std::move(new_hint);
174 
175   if (set_recovery_email_address) {
176     update_settings.update_recovery_email_address = true;
177     update_settings.recovery_email_address = std::move(recovery_email_address);
178   }
179 
180   update_password_settings(std::move(update_settings), std::move(promise));
181 }
182 
set_recovery_email_address(string password,string new_recovery_email_address,Promise<State> promise)183 void PasswordManager::set_recovery_email_address(string password, string new_recovery_email_address,
184                                                  Promise<State> promise) {
185   UpdateSettings update_settings;
186   update_settings.current_password = std::move(password);
187   update_settings.update_recovery_email_address = true;
188   update_settings.recovery_email_address = std::move(new_recovery_email_address);
189 
190   update_password_settings(std::move(update_settings), std::move(promise));
191 }
192 
get_secure_secret(string password,Promise<secure_storage::Secret> promise)193 void PasswordManager::get_secure_secret(string password, Promise<secure_storage::Secret> promise) {
194   return do_get_secure_secret(true, std::move(password), std::move(promise));
195 }
196 
do_get_secure_secret(bool allow_recursive,string password,Promise<secure_storage::Secret> promise)197 void PasswordManager::do_get_secure_secret(bool allow_recursive, string password,
198                                            Promise<secure_storage::Secret> promise) {
199   if (secret_) {
200     return promise.set_value(secret_.value().clone());
201   }
202   if (password.empty()) {
203     return promise.set_error(Status::Error(400, "PASSWORD_HASH_INVALID"));
204   }
205   get_full_state(
206       password, PromiseCreator::lambda([password, allow_recursive, promise = std::move(promise),
207                                         actor_id = actor_id(this)](Result<PasswordFullState> r_state) mutable {
208         if (r_state.is_error()) {
209           return promise.set_error(r_state.move_as_error());
210         }
211         auto state = r_state.move_as_ok();
212         if (!state.state.has_password) {
213           return promise.set_error(Status::Error(400, "2-step verification is disabled"));
214         }
215         if (state.private_state.secret) {
216           send_closure(actor_id, &PasswordManager::cache_secret, state.private_state.secret.value().clone());
217           return promise.set_value(std::move(state.private_state.secret.value()));
218         }
219         if (!allow_recursive) {
220           return promise.set_error(Status::Error(400, "Failed to get Telegram Passport secret"));
221         }
222 
223         auto new_promise =
224             PromiseCreator::lambda([password, promise = std::move(promise), actor_id](Result<bool> r_ok) mutable {
225               if (r_ok.is_error()) {
226                 return promise.set_error(r_ok.move_as_error());
227               }
228               send_closure(actor_id, &PasswordManager::do_get_secure_secret, false, std::move(password),
229                            std::move(promise));
230             });
231 
232         UpdateSettings update_settings;
233         update_settings.current_password = password;
234         update_settings.update_secure_secret = true;
235         send_closure(actor_id, &PasswordManager::do_update_password_settings, std::move(update_settings),
236                      std::move(state), std::move(new_promise));
237       }));
238 }
239 
get_temp_password_state(Promise<TempState> promise)240 void PasswordManager::get_temp_password_state(Promise<TempState> promise) /*const*/ {
241   promise.set_value(temp_password_state_.get_temporary_password_state_object());
242 }
243 
get_temp_password_state_sync()244 TempPasswordState PasswordManager::get_temp_password_state_sync() {
245   auto temp_password_str = G()->td_db()->get_binlog_pmc()->get("temp_password");
246   TempPasswordState res;
247   auto status = log_event_parse(res, temp_password_str);
248   if (status.is_error() || res.valid_until <= G()->unix_time()) {
249     res = TempPasswordState();
250   }
251   return res;
252 }
253 
create_temp_password(string password,int32 timeout,Promise<TempState> promise)254 void PasswordManager::create_temp_password(string password, int32 timeout, Promise<TempState> promise) {
255   if (create_temp_password_promise_) {
256     return promise.set_error(Status::Error(400, "Another create_temp_password query is active"));
257   }
258   create_temp_password_promise_ = std::move(promise);
259 
260   auto new_promise = PromiseCreator::lambda([actor_id = actor_id(this)](Result<TempPasswordState> result) {
261     send_closure(actor_id, &PasswordManager::on_finish_create_temp_password, std::move(result), false);
262   });
263 
264   do_get_state(PromiseCreator::lambda([password = std::move(password), timeout, promise = std::move(new_promise),
265                                        actor_id = actor_id(this)](Result<PasswordState> r_state) mutable {
266     if (r_state.is_error()) {
267       return promise.set_error(r_state.move_as_error());
268     }
269     send_closure(actor_id, &PasswordManager::do_create_temp_password, std::move(password), timeout,
270                  r_state.move_as_ok(), std::move(promise));
271   }));
272 }
273 
drop_temp_password()274 void PasswordManager::drop_temp_password() {
275   G()->td_db()->get_binlog_pmc()->erase("temp_password");
276   temp_password_state_ = TempPasswordState();
277 }
278 
do_create_temp_password(string password,int32 timeout,PasswordState && password_state,Promise<TempPasswordState> promise)279 void PasswordManager::do_create_temp_password(string password, int32 timeout, PasswordState &&password_state,
280                                               Promise<TempPasswordState> promise) {
281   auto hash = get_input_check_password(password, password_state);
282   send_with_promise(G()->net_query_creator().create(telegram_api::account_getTmpPassword(std::move(hash), timeout)),
283                     PromiseCreator::lambda([promise = std::move(promise)](Result<NetQueryPtr> r_query) mutable {
284                       auto r_result = fetch_result<telegram_api::account_getTmpPassword>(std::move(r_query));
285                       if (r_result.is_error()) {
286                         return promise.set_error(r_result.move_as_error());
287                       }
288                       auto result = r_result.move_as_ok();
289                       TempPasswordState res;
290                       res.has_temp_password = true;
291                       res.temp_password = result->tmp_password_.as_slice().str();
292                       res.valid_until = result->valid_until_;
293                       promise.set_value(std::move(res));
294                     }));
295 }
296 
on_finish_create_temp_password(Result<TempPasswordState> result,bool)297 void PasswordManager::on_finish_create_temp_password(Result<TempPasswordState> result, bool /*dummy*/) {
298   CHECK(create_temp_password_promise_);
299   if (result.is_error()) {
300     drop_temp_password();
301     return create_temp_password_promise_.set_error(result.move_as_error());
302   }
303   temp_password_state_ = result.move_as_ok();
304   G()->td_db()->get_binlog_pmc()->set("temp_password", log_event_store(temp_password_state_).as_slice().str());
305   create_temp_password_promise_.set_value(temp_password_state_.get_temporary_password_state_object());
306 }
307 
get_full_state(string password,Promise<PasswordFullState> promise)308 void PasswordManager::get_full_state(string password, Promise<PasswordFullState> promise) {
309   send_closure(G()->config_manager(), &ConfigManager::hide_suggested_action,
310                SuggestedAction{SuggestedAction::Type::CheckPassword});
311 
312   do_get_state(PromiseCreator::lambda([password = std::move(password), promise = std::move(promise),
313                                        actor_id = actor_id(this)](Result<PasswordState> r_state) mutable {
314     if (r_state.is_error()) {
315       return promise.set_error(r_state.move_as_error());
316     }
317     send_closure(actor_id, &PasswordManager::do_get_full_state, std::move(password), r_state.move_as_ok(),
318                  std::move(promise));
319   }));
320 }
321 
decrypt_secure_secret(Slice password,tl_object_ptr<telegram_api::SecurePasswordKdfAlgo> algo_ptr,Slice secret,int64 secret_id)322 Result<secure_storage::Secret> PasswordManager::decrypt_secure_secret(
323     Slice password, tl_object_ptr<telegram_api::SecurePasswordKdfAlgo> algo_ptr, Slice secret, int64 secret_id) {
324   TRY_RESULT(encrypted_secret, secure_storage::EncryptedSecret::create(secret));
325 
326   CHECK(algo_ptr != nullptr);
327   BufferSlice salt;
328   secure_storage::EnryptionAlgorithm algorithm = secure_storage::EnryptionAlgorithm::Pbkdf2;
329   switch (algo_ptr->get_id()) {
330     case telegram_api::securePasswordKdfAlgoUnknown::ID:
331       return Status::Error(400, "Unsupported algorithm");
332     case telegram_api::securePasswordKdfAlgoSHA512::ID: {
333       auto algo = move_tl_object_as<telegram_api::securePasswordKdfAlgoSHA512>(algo_ptr);
334       salt = std::move(algo->salt_);
335       algorithm = secure_storage::EnryptionAlgorithm::Sha512;
336       break;
337     }
338     case telegram_api::securePasswordKdfAlgoPBKDF2HMACSHA512iter100000::ID: {
339       auto algo = move_tl_object_as<telegram_api::securePasswordKdfAlgoPBKDF2HMACSHA512iter100000>(algo_ptr);
340       salt = std::move(algo->salt_);
341       break;
342     }
343     default:
344       UNREACHABLE();
345   }
346   TRY_RESULT(result, encrypted_secret.decrypt(password, salt.as_slice(), algorithm));
347   if (secret_id != result.get_hash()) {
348     return Status::Error("Secret hash mismatch");
349   }
350   return result;
351 }
352 
do_get_full_state(string password,PasswordState state,Promise<PasswordFullState> promise)353 void PasswordManager::do_get_full_state(string password, PasswordState state, Promise<PasswordFullState> promise) {
354   if (!state.has_password) {
355     PasswordFullState result;
356     result.state = std::move(state);
357     return promise.set_value(std::move(result));
358   }
359 
360   auto hash = get_input_check_password(password, state);
361   send_with_promise(G()->net_query_creator().create(telegram_api::account_getPasswordSettings(std::move(hash))),
362                     PromiseCreator::lambda([promise = std::move(promise), state = std::move(state),
363                                             password](Result<NetQueryPtr> r_query) mutable {
364                       promise.set_result([&]() -> Result<PasswordFullState> {
365                         TRY_RESULT(result, fetch_result<telegram_api::account_getPasswordSettings>(std::move(r_query)));
366                         LOG(INFO) << "Receive password settings: " << to_string(result);
367                         PasswordPrivateState private_state;
368                         private_state.email = std::move(result->email_);
369 
370                         if (result->secure_settings_ != nullptr) {
371                           auto r_secret =
372                               decrypt_secure_secret(password, std::move(result->secure_settings_->secure_algo_),
373                                                     result->secure_settings_->secure_secret_.as_slice(),
374                                                     result->secure_settings_->secure_secret_id_);
375                           if (r_secret.is_ok()) {
376                             private_state.secret = r_secret.move_as_ok();
377                           }
378                         }
379 
380                         return PasswordFullState{std::move(state), std::move(private_state)};
381                       }());
382                     }));
383 }
384 
get_recovery_email_address(string password,Promise<tl_object_ptr<td_api::recoveryEmailAddress>> promise)385 void PasswordManager::get_recovery_email_address(string password,
386                                                  Promise<tl_object_ptr<td_api::recoveryEmailAddress>> promise) {
387   get_full_state(
388       password,
389       PromiseCreator::lambda([password, promise = std::move(promise)](Result<PasswordFullState> r_state) mutable {
390         if (r_state.is_error()) {
391           return promise.set_error(r_state.move_as_error());
392         }
393         auto state = r_state.move_as_ok();
394         return promise.set_value(make_tl_object<td_api::recoveryEmailAddress>(state.private_state.email));
395       }));
396 }
397 
check_recovery_email_address_code(string code,Promise<State> promise)398 void PasswordManager::check_recovery_email_address_code(string code, Promise<State> promise) {
399   auto query = G()->net_query_creator().create(telegram_api::account_confirmPasswordEmail(std::move(code)));
400   send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this), promise = std::move(promise)](
401                                                                  Result<NetQueryPtr> r_query) mutable {
402                       auto r_result = fetch_result<telegram_api::account_confirmPasswordEmail>(std::move(r_query));
403                       if (r_result.is_error() && r_result.error().message() != "EMAIL_HASH_EXPIRED" &&
404                           r_result.error().message() != "CODE_INVALID") {
405                         return promise.set_error(r_result.move_as_error());
406                       }
407                       send_closure(actor_id, &PasswordManager::get_state, std::move(promise));
408                     }));
409 }
410 
resend_recovery_email_address_code(Promise<State> promise)411 void PasswordManager::resend_recovery_email_address_code(Promise<State> promise) {
412   auto query = G()->net_query_creator().create(telegram_api::account_resendPasswordEmail());
413   send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this), promise = std::move(promise)](
414                                                                  Result<NetQueryPtr> r_query) mutable {
415                       auto r_result = fetch_result<telegram_api::account_resendPasswordEmail>(std::move(r_query));
416                       if (r_result.is_error() && r_result.error().message() != "EMAIL_HASH_EXPIRED") {
417                         return promise.set_error(r_result.move_as_error());
418                       }
419                       send_closure(actor_id, &PasswordManager::get_state, std::move(promise));
420                     }));
421 }
422 
send_email_address_verification_code(string email,Promise<td_api::object_ptr<td_api::emailAddressAuthenticationCodeInfo>> promise)423 void PasswordManager::send_email_address_verification_code(
424     string email, Promise<td_api::object_ptr<td_api::emailAddressAuthenticationCodeInfo>> promise) {
425   last_verified_email_address_ = email;
426   auto query = G()->net_query_creator().create(telegram_api::account_sendVerifyEmailCode(std::move(email)));
427   send_with_promise(
428       std::move(query), PromiseCreator::lambda([promise = std::move(promise)](Result<NetQueryPtr> r_query) mutable {
429         auto r_result = fetch_result<telegram_api::account_sendVerifyEmailCode>(std::move(r_query));
430         if (r_result.is_error()) {
431           return promise.set_error(r_result.move_as_error());
432         }
433         auto result = r_result.move_as_ok();
434         if (result->length_ < 0 || result->length_ >= 100) {
435           LOG(ERROR) << "Receive wrong code length " << result->length_;
436           result->length_ = 0;
437         }
438         return promise.set_value(
439             make_tl_object<td_api::emailAddressAuthenticationCodeInfo>(result->email_pattern_, result->length_));
440       }));
441 }
442 
resend_email_address_verification_code(Promise<td_api::object_ptr<td_api::emailAddressAuthenticationCodeInfo>> promise)443 void PasswordManager::resend_email_address_verification_code(
444     Promise<td_api::object_ptr<td_api::emailAddressAuthenticationCodeInfo>> promise) {
445   if (last_verified_email_address_.empty()) {
446     return promise.set_error(Status::Error(400, "No email address verification was sent"));
447   }
448   send_email_address_verification_code(last_verified_email_address_, std::move(promise));
449 }
450 
check_email_address_verification_code(string code,Promise<Unit> promise)451 void PasswordManager::check_email_address_verification_code(string code, Promise<Unit> promise) {
452   if (last_verified_email_address_.empty()) {
453     return promise.set_error(Status::Error(400, "No email address verification was sent"));
454   }
455   auto query =
456       G()->net_query_creator().create(telegram_api::account_verifyEmail(last_verified_email_address_, std::move(code)));
457   send_with_promise(std::move(query),
458                     PromiseCreator::lambda([promise = std::move(promise)](Result<NetQueryPtr> r_query) mutable {
459                       auto r_result = fetch_result<telegram_api::account_verifyEmail>(std::move(r_query));
460                       if (r_result.is_error()) {
461                         return promise.set_error(r_result.move_as_error());
462                       }
463                       return promise.set_value(Unit());
464                     }));
465 }
466 
request_password_recovery(Promise<td_api::object_ptr<td_api::emailAddressAuthenticationCodeInfo>> promise)467 void PasswordManager::request_password_recovery(
468     Promise<td_api::object_ptr<td_api::emailAddressAuthenticationCodeInfo>> promise) {
469   // is called only after authorization
470   send_with_promise(
471       G()->net_query_creator().create(telegram_api::auth_requestPasswordRecovery()),
472       PromiseCreator::lambda([promise = std::move(promise)](Result<NetQueryPtr> r_query) mutable {
473         auto r_result = fetch_result<telegram_api::auth_requestPasswordRecovery>(std::move(r_query));
474         if (r_result.is_error()) {
475           return promise.set_error(r_result.move_as_error());
476         }
477         auto result = r_result.move_as_ok();
478         return promise.set_value(make_tl_object<td_api::emailAddressAuthenticationCodeInfo>(result->email_pattern_, 0));
479       }));
480 }
481 
check_password_recovery_code(string code,Promise<Unit> promise)482 void PasswordManager::check_password_recovery_code(string code, Promise<Unit> promise) {
483   // is called only after authorization
484   send_with_promise(G()->net_query_creator().create(telegram_api::auth_checkRecoveryPassword(code)),
485                     PromiseCreator::lambda([promise = std::move(promise)](Result<NetQueryPtr> r_query) mutable {
486                       auto r_result = fetch_result<telegram_api::auth_checkRecoveryPassword>(std::move(r_query));
487                       if (r_result.is_error()) {
488                         return promise.set_error(r_result.move_as_error());
489                       }
490                       if (!r_result.ok()) {
491                         return promise.set_error(Status::Error(400, "Invalid recovery code"));
492                       }
493                       return promise.set_value(Unit());
494                     }));
495 }
496 
recover_password(string code,string new_password,string new_hint,Promise<State> promise)497 void PasswordManager::recover_password(string code, string new_password, string new_hint, Promise<State> promise) {
498   // is called only after authorization
499   if (new_password.empty()) {
500     return do_recover_password(std::move(code), nullptr, std::move(promise));
501   }
502 
503   UpdateSettings update_settings;
504   update_settings.update_password = true;
505   update_settings.new_password = std::move(new_password);
506   update_settings.new_hint = std::move(new_hint);
507 
508   do_get_state(PromiseCreator::lambda([actor_id = actor_id(this), code = std::move(code),
509                                        update_settings = std::move(update_settings),
510                                        promise = std::move(promise)](Result<PasswordState> r_state) mutable {
511     if (r_state.is_error()) {
512       return promise.set_error(r_state.move_as_error());
513     }
514 
515     TRY_RESULT_PROMISE(
516         promise, new_settings,
517         get_password_input_settings(update_settings, r_state.ok().has_password, r_state.ok().new_state, nullptr));
518 
519     send_closure(actor_id, &PasswordManager::do_recover_password, std::move(code), std::move(new_settings),
520                  std::move(promise));
521   }));
522 }
523 
do_recover_password(string code,PasswordInputSettings && new_settings,Promise<State> && promise)524 void PasswordManager::do_recover_password(string code, PasswordInputSettings &&new_settings, Promise<State> &&promise) {
525   int32 flags = 0;
526   if (new_settings != nullptr) {
527     flags |= telegram_api::auth_recoverPassword::NEW_SETTINGS_MASK;
528   }
529   send_with_promise(G()->net_query_creator().create(
530                         telegram_api::auth_recoverPassword(flags, std::move(code), std::move(new_settings))),
531                     PromiseCreator::lambda(
532                         [actor_id = actor_id(this), promise = std::move(promise)](Result<NetQueryPtr> r_query) mutable {
533                           auto r_result = fetch_result<telegram_api::auth_recoverPassword>(std::move(r_query));
534                           if (r_result.is_error()) {
535                             return promise.set_error(r_result.move_as_error());
536                           }
537                           send_closure(actor_id, &PasswordManager::get_state, std::move(promise));
538                         }));
539 }
540 
reset_password(Promise<ResetPasswordResult> promise)541 void PasswordManager::reset_password(Promise<ResetPasswordResult> promise) {
542   send_with_promise(
543       G()->net_query_creator().create(telegram_api::account_resetPassword()),
544       PromiseCreator::lambda([promise = std::move(promise)](Result<NetQueryPtr> r_query) mutable {
545         auto r_result = fetch_result<telegram_api::account_resetPassword>(std::move(r_query));
546         if (r_result.is_error()) {
547           return promise.set_error(r_result.move_as_error());
548         }
549         auto result_ptr = r_result.move_as_ok();
550         switch (result_ptr->get_id()) {
551           case telegram_api::account_resetPasswordOk::ID:
552             return promise.set_value(td_api::make_object<td_api::resetPasswordResultOk>());
553           case telegram_api::account_resetPasswordRequestedWait::ID: {
554             auto result = move_tl_object_as<telegram_api::account_resetPasswordRequestedWait>(result_ptr);
555             return promise.set_value(td_api::make_object<td_api::resetPasswordResultPending>(result->until_date_));
556           }
557           case telegram_api::account_resetPasswordFailedWait::ID: {
558             auto result = move_tl_object_as<telegram_api::account_resetPasswordFailedWait>(result_ptr);
559             return promise.set_value(td_api::make_object<td_api::resetPasswordResultDeclined>(result->retry_date_));
560           }
561           default:
562             UNREACHABLE();
563             break;
564         }
565       }));
566 }
567 
cancel_password_reset(Promise<Unit> promise)568 void PasswordManager::cancel_password_reset(Promise<Unit> promise) {
569   send_with_promise(G()->net_query_creator().create(telegram_api::account_declinePasswordReset()),
570                     PromiseCreator::lambda([promise = std::move(promise)](Result<NetQueryPtr> r_query) mutable {
571                       auto r_result = fetch_result<telegram_api::account_declinePasswordReset>(std::move(r_query));
572                       if (r_result.is_error() && r_result.error().message() != "RESET_REQUEST_MISSING") {
573                         return promise.set_error(r_result.move_as_error());
574                       }
575                       return promise.set_value(Unit());
576                     }));
577 }
578 
update_password_settings(UpdateSettings update_settings,Promise<State> promise)579 void PasswordManager::update_password_settings(UpdateSettings update_settings, Promise<State> promise) {
580   auto result_promise = PromiseCreator::lambda(
581       [actor_id = actor_id(this), promise = std::move(promise)](Result<bool> r_update_settings) mutable {
582         if (r_update_settings.is_error()) {
583           return promise.set_error(r_update_settings.move_as_error());
584         }
585         if (!r_update_settings.ok()) {
586           return promise.set_error(Status::Error(400, "account_updatePasswordSettings returned false"));
587         }
588         send_closure(actor_id, &PasswordManager::get_state, std::move(promise));
589       });
590 
591   auto password = update_settings.current_password;
592   get_full_state(
593       std::move(password),
594       PromiseCreator::lambda([actor_id = actor_id(this), result_promise = std::move(result_promise),
595                               update_settings = std::move(update_settings)](Result<PasswordFullState> r_state) mutable {
596         if (r_state.is_error()) {
597           return result_promise.set_error(r_state.move_as_error());
598         }
599         send_closure(actor_id, &PasswordManager::do_update_password_settings, std::move(update_settings),
600                      r_state.move_as_ok(), std::move(result_promise));
601       }));
602 }
603 
create_salt(Slice salt_prefix)604 static BufferSlice create_salt(Slice salt_prefix) {
605   static constexpr size_t ADDED_SALT_SIZE = 32;
606   BufferSlice new_salt(salt_prefix.size() + ADDED_SALT_SIZE);
607   new_salt.as_slice().copy_from(salt_prefix);
608   Random::secure_bytes(new_salt.as_slice().substr(salt_prefix.size()));
609   return new_salt;
610 }
611 
do_update_password_settings(UpdateSettings update_settings,PasswordFullState full_state,Promise<bool> promise)612 void PasswordManager::do_update_password_settings(UpdateSettings update_settings, PasswordFullState full_state,
613                                                   Promise<bool> promise) {
614   // PasswordState has already been used to get PasswordPrivateState and need to be reget
615   do_get_state(PromiseCreator::lambda([actor_id = actor_id(this), update_settings = std::move(update_settings),
616                                        private_state = std::move(full_state.private_state),
617                                        promise = std::move(promise)](Result<PasswordState> r_state) mutable {
618     if (r_state.is_error()) {
619       return promise.set_error(r_state.move_as_error());
620     }
621     send_closure(actor_id, &PasswordManager::do_update_password_settings_impl, std::move(update_settings),
622                  r_state.move_as_ok(), std::move(private_state), std::move(promise));
623   }));
624 }
625 
get_password_input_settings(string new_password,string new_hint,const NewPasswordState & state)626 Result<PasswordManager::PasswordInputSettings> PasswordManager::get_password_input_settings(
627     string new_password, string new_hint, const NewPasswordState &state) {
628   UpdateSettings update_settings;
629   update_settings.update_password = true;
630   update_settings.new_password = std::move(new_password);
631   update_settings.new_hint = std::move(new_hint);
632 
633   return get_password_input_settings(update_settings, true, state, nullptr);
634 }
635 
get_password_input_settings(const UpdateSettings & update_settings,bool has_password,const NewPasswordState & state,const PasswordPrivateState * private_state)636 Result<PasswordManager::PasswordInputSettings> PasswordManager::get_password_input_settings(
637     const UpdateSettings &update_settings, bool has_password, const NewPasswordState &state,
638     const PasswordPrivateState *private_state) {
639   bool have_secret = private_state != nullptr && private_state->secret;
640   auto update_secure_secret = update_settings.update_secure_secret;
641   int32 flags = 0;
642   BufferSlice new_password_hash;
643   tl_object_ptr<telegram_api::PasswordKdfAlgo> new_algo;
644   string new_hint;
645   if (update_settings.update_password) {
646     flags |= telegram_api::account_passwordInputSettings::NEW_PASSWORD_HASH_MASK;
647     flags |= telegram_api::account_passwordInputSettings::NEW_ALGO_MASK;
648     flags |= telegram_api::account_passwordInputSettings::HINT_MASK;
649     if (!update_settings.new_password.empty()) {
650       auto new_client_salt = create_salt(state.client_salt);
651 
652       auto new_hash = calc_password_srp_hash(update_settings.new_password, new_client_salt.as_slice(),
653                                              state.server_salt, state.srp_g, state.srp_p);
654       if (new_hash.is_error()) {
655         return Status::Error(400, "Unable to change password, because it may be unsafe");
656       }
657       new_password_hash = new_hash.move_as_ok();
658       new_algo = make_tl_object<telegram_api::passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow>(
659           std::move(new_client_salt), BufferSlice(state.server_salt), state.srp_g, BufferSlice(state.srp_p));
660       new_hint = update_settings.new_hint;
661       if (have_secret) {
662         update_secure_secret = true;
663       }
664     } else {
665       new_algo = make_tl_object<telegram_api::passwordKdfAlgoUnknown>();
666     }
667   }
668 
669   // have no password and not setting one
670   if (!update_settings.update_password && !has_password) {
671     update_secure_secret = false;
672   }
673 
674   // setting an empty password
675   if (update_settings.update_password && update_settings.new_password.empty()) {
676     update_secure_secret = false;
677   }
678 
679   tl_object_ptr<telegram_api::secureSecretSettings> new_secure_settings;
680   if (update_secure_secret) {
681     auto secret = have_secret ? private_state->secret.value() : secure_storage::Secret::create_new();
682     auto algorithm =
683         make_tl_object<telegram_api::securePasswordKdfAlgoPBKDF2HMACSHA512iter100000>(create_salt(state.secure_salt));
684     auto encrypted_secret = secret.encrypt(
685         update_settings.update_password ? update_settings.new_password : update_settings.current_password,
686         algorithm->salt_.as_slice(), secure_storage::EnryptionAlgorithm::Pbkdf2);
687 
688     flags |= telegram_api::account_passwordInputSettings::NEW_SECURE_SETTINGS_MASK;
689     new_secure_settings = make_tl_object<telegram_api::secureSecretSettings>(
690         std::move(algorithm), BufferSlice(encrypted_secret.as_slice()), secret.get_hash());
691   }
692   if (update_settings.update_recovery_email_address) {
693     flags |= telegram_api::account_passwordInputSettings::EMAIL_MASK;
694   }
695   return make_tl_object<telegram_api::account_passwordInputSettings>(
696       flags, std::move(new_algo), std::move(new_password_hash), new_hint, update_settings.recovery_email_address,
697       std::move(new_secure_settings));
698 }
699 
do_update_password_settings_impl(UpdateSettings update_settings,PasswordState state,PasswordPrivateState private_state,Promise<bool> promise)700 void PasswordManager::do_update_password_settings_impl(UpdateSettings update_settings, PasswordState state,
701                                                        PasswordPrivateState private_state, Promise<bool> promise) {
702   TRY_RESULT_PROMISE(promise, new_settings,
703                      get_password_input_settings(update_settings, state.has_password, state.new_state, &private_state));
704   auto current_hash = get_input_check_password(state.has_password ? update_settings.current_password : Slice(), state);
705   auto query = G()->net_query_creator().create(
706       telegram_api::account_updatePasswordSettings(std::move(current_hash), std::move(new_settings)));
707 
708   send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this), promise = std::move(promise)](
709                                                                  Result<NetQueryPtr> r_query) mutable {
710                       auto r_result = fetch_result<telegram_api::account_updatePasswordSettings>(std::move(r_query));
711                       if (r_result.is_error()) {
712                         Slice prefix("EMAIL_UNCONFIRMED");
713                         Slice message = r_result.error().message();
714                         if (r_result.error().code() == 400 && begins_with(message, prefix)) {
715                           if (message.size() >= prefix.size() + 2 && message[prefix.size()] == '_') {
716                             send_closure(actor_id, &PasswordManager::on_get_code_length,
717                                          to_integer<int32>(message.substr(prefix.size() + 1)));
718                           }
719                           return promise.set_value(true);
720                         }
721                         return promise.set_error(r_result.move_as_error());
722                       }
723                       return promise.set_value(r_result.move_as_ok());
724                     }));
725 }
726 
on_get_code_length(int32 code_length)727 void PasswordManager::on_get_code_length(int32 code_length) {
728   if (code_length <= 0 || code_length > 100) {
729     LOG(ERROR) << "Receive invalid code length " << code_length;
730     return;
731   }
732 
733   LOG(INFO) << "Set code length to " << code_length;
734   last_code_length_ = code_length;
735 }
736 
get_state(Promise<State> promise)737 void PasswordManager::get_state(Promise<State> promise) {
738   do_get_state(PromiseCreator::lambda([promise = std::move(promise)](Result<PasswordState> r_state) mutable {
739     if (r_state.is_error()) {
740       return promise.set_error(r_state.move_as_error());
741     }
742     promise.set_value(r_state.move_as_ok().get_password_state_object());
743   }));
744 }
745 
do_get_state(Promise<PasswordState> promise)746 void PasswordManager::do_get_state(Promise<PasswordState> promise) {
747   auto query = G()->net_query_creator().create(telegram_api::account_getPassword());
748   send_with_promise(
749       std::move(query), PromiseCreator::lambda([actor_id = actor_id(this), code_length = last_code_length_,
750                                                 promise = std::move(promise)](Result<NetQueryPtr> r_query) mutable {
751         TRY_STATUS_PROMISE(promise, G()->close_status());
752 
753         auto r_result = fetch_result<telegram_api::account_getPassword>(std::move(r_query));
754         if (r_result.is_error()) {
755           return promise.set_error(r_result.move_as_error());
756         }
757         auto password = r_result.move_as_ok();
758         LOG(INFO) << "Receive password info: " << to_string(password);
759         Random::add_seed(password->secure_random_.as_slice());
760 
761         PasswordState state;
762         if (password->current_algo_ != nullptr) {
763           state.has_password = true;
764 
765           switch (password->current_algo_->get_id()) {
766             case telegram_api::passwordKdfAlgoUnknown::ID:
767               return promise.set_error(Status::Error(400, "Please update client to continue"));
768             case telegram_api::passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow::ID: {
769               auto algo =
770                   move_tl_object_as<telegram_api::passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow>(
771                       password->current_algo_);
772               state.current_client_salt = algo->salt1_.as_slice().str();
773               state.current_server_salt = algo->salt2_.as_slice().str();
774               state.current_srp_g = algo->g_;
775               state.current_srp_p = algo->p_.as_slice().str();
776               break;
777             }
778             default:
779               UNREACHABLE();
780           }
781           state.current_srp_B = password->srp_B_.as_slice().str();
782           state.current_srp_id = password->srp_id_;
783           state.password_hint = std::move(password->hint_);
784           state.has_recovery_email_address = password->has_recovery_;
785           state.has_secure_values = password->has_secure_values_;
786 
787           auto days = narrow_cast<int32>(G()->shared_config().get_option_integer("otherwise_relogin_days"));
788           if (days > 0) {
789             dismiss_suggested_action(SuggestedAction{SuggestedAction::Type::SetPassword, DialogId(), days},
790                                      Promise<Unit>());
791           }
792         } else {
793           state.has_password = false;
794           send_closure(actor_id, &PasswordManager::drop_cached_secret);
795         }
796         state.unconfirmed_recovery_email_address_pattern = std::move(password->email_unconfirmed_pattern_);
797         state.code_length = code_length;
798 
799         if (password->flags_ & telegram_api::account_password::PENDING_RESET_DATE_MASK) {
800           state.pending_reset_date = td::max(password->pending_reset_date_, 0);
801         }
802 
803         auto &new_state = state.new_state;
804         TRY_RESULT_PROMISE_ASSIGN(
805             promise, new_state,
806             get_new_password_state(std::move(password->new_algo_), std::move(password->new_secure_algo_)));
807 
808         promise.set_value(std::move(state));
809       }));
810 }
811 
cache_secret(secure_storage::Secret secret)812 void PasswordManager::cache_secret(secure_storage::Secret secret) {
813   LOG(INFO) << "Cache passport secret";
814   secret_ = std::move(secret);
815 
816   const int32 max_cache_time = 3600;
817   secret_expire_time_ = Time::now() + max_cache_time;
818   set_timeout_at(secret_expire_time_);
819 }
820 
drop_cached_secret()821 void PasswordManager::drop_cached_secret() {
822   LOG(INFO) << "Drop passport secret";
823   secret_ = optional<secure_storage::Secret>();
824 }
825 
timeout_expired()826 void PasswordManager::timeout_expired() {
827   if (Time::now() >= secret_expire_time_) {
828     drop_cached_secret();
829   } else {
830     set_timeout_at(secret_expire_time_);
831   }
832 }
833 
on_result(NetQueryPtr query)834 void PasswordManager::on_result(NetQueryPtr query) {
835   auto token = get_link_token();
836   container_.extract(token).set_value(std::move(query));
837 }
838 
send_with_promise(NetQueryPtr query,Promise<NetQueryPtr> promise)839 void PasswordManager::send_with_promise(NetQueryPtr query, Promise<NetQueryPtr> promise) {
840   auto id = container_.create(std::move(promise));
841   G()->net_query_dispatcher().dispatch_with_callback(std::move(query), actor_shared(this, id));
842 }
843 
start_up()844 void PasswordManager::start_up() {
845   temp_password_state_ = get_temp_password_state_sync();
846 }
847 
hangup()848 void PasswordManager::hangup() {
849   container_.for_each(
850       [](auto id, Promise<NetQueryPtr> &promise) { promise.set_error(Global::request_aborted_error()); });
851   stop();
852 }
853 
854 }  // namespace td
855