api_cloud_password.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #include "api/api_cloud_password.h"
  8. #include "apiwrap.h"
  9. #include "base/random.h"
  10. #include "core/core_cloud_password.h"
  11. #include "passport/passport_encryption.h"
  12. #include "base/unixtime.h"
  13. #include "base/call_delayed.h"
  14. namespace Api {
  15. namespace {
  16. [[nodiscard]] Core::CloudPasswordState ProcessMtpState(
  17. const MTPaccount_password &state) {
  18. return state.match([&](const MTPDaccount_password &data) {
  19. base::RandomAddSeed(bytes::make_span(data.vsecure_random().v));
  20. return Core::ParseCloudPasswordState(data);
  21. });
  22. }
  23. } // namespace
  24. CloudPassword::CloudPassword(not_null<ApiWrap*> api)
  25. : _api(&api->instance()) {
  26. }
  27. void CloudPassword::apply(Core::CloudPasswordState state) {
  28. if (_state) {
  29. *_state = std::move(state);
  30. } else {
  31. _state = std::make_unique<Core::CloudPasswordState>(std::move(state));
  32. }
  33. _stateChanges.fire_copy(*_state);
  34. }
  35. void CloudPassword::reload() {
  36. if (_requestId) {
  37. return;
  38. }
  39. _requestId = _api.request(MTPaccount_GetPassword(
  40. )).done([=](const MTPaccount_Password &result) {
  41. _requestId = 0;
  42. apply(ProcessMtpState(result));
  43. }).fail([=] {
  44. _requestId = 0;
  45. }).send();
  46. }
  47. void CloudPassword::clearUnconfirmedPassword() {
  48. _requestId = _api.request(MTPaccount_CancelPasswordEmail(
  49. )).done([=] {
  50. _requestId = 0;
  51. reload();
  52. }).fail([=] {
  53. _requestId = 0;
  54. reload();
  55. }).send();
  56. }
  57. rpl::producer<Core::CloudPasswordState> CloudPassword::state() const {
  58. return _state
  59. ? _stateChanges.events_starting_with_copy(*_state)
  60. : (_stateChanges.events() | rpl::type_erased());
  61. }
  62. auto CloudPassword::stateCurrent() const
  63. -> std::optional<Core::CloudPasswordState> {
  64. return _state
  65. ? base::make_optional(*_state)
  66. : std::nullopt;
  67. }
  68. auto CloudPassword::resetPassword()
  69. -> rpl::producer<CloudPassword::ResetRetryDate, QString> {
  70. return [=](auto consumer) {
  71. _api.request(MTPaccount_ResetPassword(
  72. )).done([=](const MTPaccount_ResetPasswordResult &result) {
  73. result.match([&](const MTPDaccount_resetPasswordOk &data) {
  74. reload();
  75. }, [&](const MTPDaccount_resetPasswordRequestedWait &data) {
  76. if (!_state) {
  77. reload();
  78. return;
  79. }
  80. const auto until = data.vuntil_date().v;
  81. if (_state->pendingResetDate != until) {
  82. _state->pendingResetDate = until;
  83. _stateChanges.fire_copy(*_state);
  84. }
  85. }, [&](const MTPDaccount_resetPasswordFailedWait &data) {
  86. consumer.put_next_copy(data.vretry_date().v);
  87. });
  88. consumer.put_done();
  89. }).fail([=](const MTP::Error &error) {
  90. consumer.put_error_copy(error.type());
  91. }).send();
  92. return rpl::lifetime();
  93. };
  94. }
  95. auto CloudPassword::cancelResetPassword()
  96. -> rpl::producer<rpl::no_value, QString> {
  97. return [=](auto consumer) {
  98. _api.request(MTPaccount_DeclinePasswordReset(
  99. )).done([=] {
  100. reload();
  101. consumer.put_done();
  102. }).fail([=](const MTP::Error &error) {
  103. consumer.put_error_copy(error.type());
  104. }).send();
  105. return rpl::lifetime();
  106. };
  107. }
  108. rpl::producer<CloudPassword::SetOk, QString> CloudPassword::set(
  109. const QString &oldPassword,
  110. const QString &newPassword,
  111. const QString &hint,
  112. bool hasRecoveryEmail,
  113. const QString &recoveryEmail) {
  114. const auto generatePasswordCheck = [=](
  115. const Core::CloudPasswordState &latestState) {
  116. if (oldPassword.isEmpty() || !latestState.hasPassword) {
  117. return Core::CloudPasswordResult{
  118. MTP_inputCheckPasswordEmpty()
  119. };
  120. }
  121. const auto hash = Core::ComputeCloudPasswordHash(
  122. latestState.mtp.request.algo,
  123. bytes::make_span(oldPassword.toUtf8()));
  124. return Core::ComputeCloudPasswordCheck(
  125. latestState.mtp.request,
  126. hash);
  127. };
  128. const auto finish = [=](auto consumer, int unconfirmedEmailLengthCode) {
  129. _api.request(MTPaccount_GetPassword(
  130. )).done([=](const MTPaccount_Password &result) {
  131. apply(ProcessMtpState(result));
  132. if (unconfirmedEmailLengthCode) {
  133. consumer.put_next(SetOk{ unconfirmedEmailLengthCode });
  134. } else {
  135. consumer.put_done();
  136. }
  137. }).fail([=](const MTP::Error &error) {
  138. consumer.put_error_copy(error.type());
  139. }).handleFloodErrors().send();
  140. };
  141. const auto sendMTPaccountUpdatePasswordSettings = [=](
  142. const Core::CloudPasswordState &latestState,
  143. const QByteArray &secureSecret,
  144. auto consumer) {
  145. const auto newPasswordBytes = newPassword.toUtf8();
  146. const auto newPasswordHash = Core::ComputeCloudPasswordDigest(
  147. latestState.mtp.newPassword,
  148. bytes::make_span(newPasswordBytes));
  149. if (!newPassword.isEmpty() && newPasswordHash.modpow.empty()) {
  150. consumer.put_error("INTERNAL_SERVER_ERROR");
  151. return;
  152. }
  153. using Flag = MTPDaccount_passwordInputSettings::Flag;
  154. const auto flags = Flag::f_new_algo
  155. | Flag::f_new_password_hash
  156. | Flag::f_hint
  157. | (secureSecret.isEmpty() ? Flag(0) : Flag::f_new_secure_settings)
  158. | ((!hasRecoveryEmail) ? Flag(0) : Flag::f_email);
  159. auto newSecureSecret = bytes::vector();
  160. auto newSecureSecretId = 0ULL;
  161. if (!secureSecret.isEmpty()) {
  162. newSecureSecretId = Passport::CountSecureSecretId(
  163. bytes::make_span(secureSecret));
  164. newSecureSecret = Passport::EncryptSecureSecret(
  165. bytes::make_span(secureSecret),
  166. Core::ComputeSecureSecretHash(
  167. latestState.mtp.newSecureSecret,
  168. bytes::make_span(newPasswordBytes)));
  169. }
  170. const auto settings = MTP_account_passwordInputSettings(
  171. MTP_flags(flags),
  172. Core::PrepareCloudPasswordAlgo(newPassword.isEmpty()
  173. ? v::null
  174. : latestState.mtp.newPassword),
  175. newPassword.isEmpty()
  176. ? MTP_bytes()
  177. : MTP_bytes(newPasswordHash.modpow),
  178. MTP_string(hint),
  179. MTP_string(recoveryEmail),
  180. MTP_secureSecretSettings(
  181. Core::PrepareSecureSecretAlgo(
  182. latestState.mtp.newSecureSecret),
  183. MTP_bytes(newSecureSecret),
  184. MTP_long(newSecureSecretId)));
  185. _api.request(MTPaccount_UpdatePasswordSettings(
  186. generatePasswordCheck(latestState).result,
  187. settings
  188. )).done([=] {
  189. finish(consumer, 0);
  190. }).fail([=](const MTP::Error &error) {
  191. const auto &type = error.type();
  192. const auto prefix = u"EMAIL_UNCONFIRMED_"_q;
  193. if (type.startsWith(prefix)) {
  194. const auto codeLength = base::StringViewMid(
  195. type,
  196. prefix.size()).toInt();
  197. finish(consumer, codeLength);
  198. } else {
  199. consumer.put_error_copy(type);
  200. }
  201. }).handleFloodErrors().send();
  202. };
  203. return [=](auto consumer) {
  204. _api.request(MTPaccount_GetPassword(
  205. )).done([=](const MTPaccount_Password &result) {
  206. const auto latestState = ProcessMtpState(result);
  207. if (latestState.hasPassword
  208. && !oldPassword.isEmpty()
  209. && !newPassword.isEmpty()) {
  210. _api.request(MTPaccount_GetPasswordSettings(
  211. generatePasswordCheck(latestState).result
  212. )).done([=](const MTPaccount_PasswordSettings &result) {
  213. using Settings = MTPDaccount_passwordSettings;
  214. const auto &data = result.match([&](
  215. const Settings &data) -> const Settings & {
  216. return data;
  217. });
  218. auto secureSecret = QByteArray();
  219. if (const auto wrapped = data.vsecure_settings()) {
  220. using Secure = MTPDsecureSecretSettings;
  221. const auto &settings = wrapped->match([](
  222. const Secure &data) -> const Secure & {
  223. return data;
  224. });
  225. const auto passwordUtf = oldPassword.toUtf8();
  226. const auto secret = Passport::DecryptSecureSecret(
  227. bytes::make_span(settings.vsecure_secret().v),
  228. Core::ComputeSecureSecretHash(
  229. Core::ParseSecureSecretAlgo(
  230. settings.vsecure_algo()),
  231. bytes::make_span(passwordUtf)));
  232. if (secret.empty()) {
  233. LOG(("API Error: "
  234. "Failed to decrypt secure secret."));
  235. consumer.put_error("SUGGEST_SECRET_RESET");
  236. return;
  237. } else if (Passport::CountSecureSecretId(secret)
  238. != settings.vsecure_secret_id().v) {
  239. LOG(("API Error: Wrong secure secret id."));
  240. consumer.put_error("SUGGEST_SECRET_RESET");
  241. return;
  242. } else {
  243. secureSecret = QByteArray(
  244. reinterpret_cast<const char*>(secret.data()),
  245. secret.size());
  246. }
  247. }
  248. _api.request(MTPaccount_GetPassword(
  249. )).done([=](const MTPaccount_Password &result) {
  250. const auto latestState = ProcessMtpState(result);
  251. sendMTPaccountUpdatePasswordSettings(
  252. latestState,
  253. secureSecret,
  254. consumer);
  255. }).fail([=](const MTP::Error &error) {
  256. consumer.put_error_copy(error.type());
  257. }).send();
  258. }).fail([=](const MTP::Error &error) {
  259. consumer.put_error_copy(error.type());
  260. }).send();
  261. } else {
  262. sendMTPaccountUpdatePasswordSettings(
  263. latestState,
  264. QByteArray(),
  265. consumer);
  266. }
  267. }).fail([=](const MTP::Error &error) {
  268. consumer.put_error_copy(error.type());
  269. }).send();
  270. return rpl::lifetime();
  271. };
  272. }
  273. rpl::producer<rpl::no_value, QString> CloudPassword::check(
  274. const QString &password) {
  275. return [=](auto consumer) {
  276. _api.request(MTPaccount_GetPassword(
  277. )).done([=](const MTPaccount_Password &result) {
  278. const auto latestState = ProcessMtpState(result);
  279. const auto input = [&] {
  280. if (password.isEmpty()) {
  281. return Core::CloudPasswordResult{
  282. MTP_inputCheckPasswordEmpty()
  283. };
  284. }
  285. const auto hash = Core::ComputeCloudPasswordHash(
  286. latestState.mtp.request.algo,
  287. bytes::make_span(password.toUtf8()));
  288. return Core::ComputeCloudPasswordCheck(
  289. latestState.mtp.request,
  290. hash);
  291. }();
  292. _api.request(MTPaccount_GetPasswordSettings(
  293. input.result
  294. )).done([=](const MTPaccount_PasswordSettings &result) {
  295. consumer.put_done();
  296. }).fail([=](const MTP::Error &error) {
  297. consumer.put_error_copy(error.type());
  298. }).send();
  299. }).fail([=](const MTP::Error &error) {
  300. consumer.put_error_copy(error.type());
  301. }).send();
  302. return rpl::lifetime();
  303. };
  304. }
  305. rpl::producer<rpl::no_value, QString> CloudPassword::confirmEmail(
  306. const QString &code) {
  307. return [=](auto consumer) {
  308. _api.request(MTPaccount_ConfirmPasswordEmail(
  309. MTP_string(code)
  310. )).done([=] {
  311. _api.request(MTPaccount_GetPassword(
  312. )).done([=](const MTPaccount_Password &result) {
  313. apply(ProcessMtpState(result));
  314. consumer.put_done();
  315. }).fail([=](const MTP::Error &error) {
  316. consumer.put_error_copy(error.type());
  317. }).send();
  318. }).fail([=](const MTP::Error &error) {
  319. consumer.put_error_copy(error.type());
  320. }).handleFloodErrors().send();
  321. return rpl::lifetime();
  322. };
  323. }
  324. rpl::producer<rpl::no_value, QString> CloudPassword::resendEmailCode() {
  325. return [=](auto consumer) {
  326. _api.request(MTPaccount_ResendPasswordEmail(
  327. )).done([=] {
  328. _api.request(MTPaccount_GetPassword(
  329. )).done([=](const MTPaccount_Password &result) {
  330. apply(ProcessMtpState(result));
  331. consumer.put_done();
  332. }).fail([=](const MTP::Error &error) {
  333. consumer.put_error_copy(error.type());
  334. }).send();
  335. }).fail([=](const MTP::Error &error) {
  336. consumer.put_error_copy(error.type());
  337. }).handleFloodErrors().send();
  338. return rpl::lifetime();
  339. };
  340. }
  341. rpl::producer<CloudPassword::SetOk, QString> CloudPassword::setEmail(
  342. const QString &oldPassword,
  343. const QString &recoveryEmail) {
  344. const auto generatePasswordCheck = [=](
  345. const Core::CloudPasswordState &latestState) {
  346. if (oldPassword.isEmpty() || !latestState.hasPassword) {
  347. return Core::CloudPasswordResult{
  348. MTP_inputCheckPasswordEmpty()
  349. };
  350. }
  351. const auto hash = Core::ComputeCloudPasswordHash(
  352. latestState.mtp.request.algo,
  353. bytes::make_span(oldPassword.toUtf8()));
  354. return Core::ComputeCloudPasswordCheck(
  355. latestState.mtp.request,
  356. hash);
  357. };
  358. const auto finish = [=](auto consumer, int unconfirmedEmailLengthCode) {
  359. _api.request(MTPaccount_GetPassword(
  360. )).done([=](const MTPaccount_Password &result) {
  361. apply(ProcessMtpState(result));
  362. if (unconfirmedEmailLengthCode) {
  363. consumer.put_next(SetOk{ unconfirmedEmailLengthCode });
  364. } else {
  365. consumer.put_done();
  366. }
  367. }).fail([=](const MTP::Error &error) {
  368. consumer.put_error_copy(error.type());
  369. }).handleFloodErrors().send();
  370. };
  371. const auto sendMTPaccountUpdatePasswordSettings = [=](
  372. const Core::CloudPasswordState &latestState,
  373. auto consumer) {
  374. const auto settings = MTP_account_passwordInputSettings(
  375. MTP_flags(MTPDaccount_passwordInputSettings::Flag::f_email),
  376. MTP_passwordKdfAlgoUnknown(),
  377. MTP_bytes(),
  378. MTP_string(),
  379. MTP_string(recoveryEmail),
  380. MTPSecureSecretSettings());
  381. _api.request(MTPaccount_UpdatePasswordSettings(
  382. generatePasswordCheck(latestState).result,
  383. settings
  384. )).done([=] {
  385. finish(consumer, 0);
  386. }).fail([=](const MTP::Error &error) {
  387. const auto &type = error.type();
  388. const auto prefix = u"EMAIL_UNCONFIRMED_"_q;
  389. if (type.startsWith(prefix)) {
  390. const auto codeLength = base::StringViewMid(
  391. type,
  392. prefix.size()).toInt();
  393. finish(consumer, codeLength);
  394. } else {
  395. consumer.put_error_copy(type);
  396. }
  397. }).handleFloodErrors().send();
  398. };
  399. return [=](auto consumer) {
  400. _api.request(MTPaccount_GetPassword(
  401. )).done([=](const MTPaccount_Password &result) {
  402. const auto latestState = ProcessMtpState(result);
  403. sendMTPaccountUpdatePasswordSettings(latestState, consumer);
  404. }).fail([=](const MTP::Error &error) {
  405. consumer.put_error_copy(error.type());
  406. }).send();
  407. return rpl::lifetime();
  408. };
  409. }
  410. rpl::producer<rpl::no_value, QString> CloudPassword::recoverPassword(
  411. const QString &code,
  412. const QString &newPassword,
  413. const QString &newHint) {
  414. const auto finish = [=](auto consumer) {
  415. _api.request(MTPaccount_GetPassword(
  416. )).done([=](const MTPaccount_Password &result) {
  417. apply(ProcessMtpState(result));
  418. consumer.put_done();
  419. }).fail([=](const MTP::Error &error) {
  420. consumer.put_error_copy(error.type());
  421. }).handleFloodErrors().send();
  422. };
  423. const auto sendMTPaccountUpdatePasswordSettings = [=](
  424. const Core::CloudPasswordState &latestState,
  425. auto consumer) {
  426. const auto newPasswordBytes = newPassword.toUtf8();
  427. const auto newPasswordHash = Core::ComputeCloudPasswordDigest(
  428. latestState.mtp.newPassword,
  429. bytes::make_span(newPasswordBytes));
  430. if (!newPassword.isEmpty() && newPasswordHash.modpow.empty()) {
  431. consumer.put_error("INTERNAL_SERVER_ERROR");
  432. return;
  433. }
  434. using Flag = MTPDaccount_passwordInputSettings::Flag;
  435. const auto flags = Flag::f_new_algo
  436. | Flag::f_new_password_hash
  437. | Flag::f_hint;
  438. const auto settings = MTP_account_passwordInputSettings(
  439. MTP_flags(flags),
  440. Core::PrepareCloudPasswordAlgo(newPassword.isEmpty()
  441. ? v::null
  442. : latestState.mtp.newPassword),
  443. newPassword.isEmpty()
  444. ? MTP_bytes()
  445. : MTP_bytes(newPasswordHash.modpow),
  446. MTP_string(newHint),
  447. MTP_string(),
  448. MTPSecureSecretSettings());
  449. _api.request(MTPauth_RecoverPassword(
  450. MTP_flags(newPassword.isEmpty()
  451. ? MTPauth_RecoverPassword::Flags(0)
  452. : MTPauth_RecoverPassword::Flag::f_new_settings),
  453. MTP_string(code),
  454. settings
  455. )).done([=](const MTPauth_Authorization &result) {
  456. finish(consumer);
  457. }).fail([=](const MTP::Error &error) {
  458. const auto &type = error.type();
  459. consumer.put_error_copy(type);
  460. }).handleFloodErrors().send();
  461. };
  462. return [=](auto consumer) {
  463. _api.request(MTPaccount_GetPassword(
  464. )).done([=](const MTPaccount_Password &result) {
  465. const auto latestState = ProcessMtpState(result);
  466. sendMTPaccountUpdatePasswordSettings(latestState, consumer);
  467. }).fail([=](const MTP::Error &error) {
  468. consumer.put_error_copy(error.type());
  469. }).send();
  470. return rpl::lifetime();
  471. };
  472. }
  473. rpl::producer<QString, QString> CloudPassword::requestPasswordRecovery() {
  474. return [=](auto consumer) {
  475. _api.request(MTPauth_RequestPasswordRecovery(
  476. )).done([=](const MTPauth_PasswordRecovery &result) {
  477. result.match([&](const MTPDauth_passwordRecovery &data) {
  478. consumer.put_next(qs(data.vemail_pattern().v));
  479. });
  480. consumer.put_done();
  481. }).fail([=](const MTP::Error &error) {
  482. consumer.put_error_copy(error.type());
  483. }).send();
  484. return rpl::lifetime();
  485. };
  486. }
  487. auto CloudPassword::checkRecoveryEmailAddressCode(const QString &code)
  488. -> rpl::producer<rpl::no_value, QString> {
  489. return [=](auto consumer) {
  490. _api.request(MTPauth_CheckRecoveryPassword(
  491. MTP_string(code)
  492. )).done([=] {
  493. consumer.put_done();
  494. }).fail([=](const MTP::Error &error) {
  495. consumer.put_error_copy(error.type());
  496. }).handleFloodErrors().send();
  497. return rpl::lifetime();
  498. };
  499. }
  500. } // namespace Api