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