diff --git a/Telegram/Resources/scheme.tl b/Telegram/Resources/scheme.tl index d71a1c1b0..e20a6ce93 100644 --- a/Telegram/Resources/scheme.tl +++ b/Telegram/Resources/scheme.tl @@ -281,6 +281,8 @@ messageActionPhoneCall#80e11a7f flags:# call_id:long reason:flags.0?PhoneCallDis messageActionScreenshotTaken#4792929b = MessageAction; messageActionCustomAction#fae69f56 message:string = MessageAction; messageActionBotAllowed#abe9affe domain:string = MessageAction; +messageActionSecureValuesSentMe#1b287353 values:Vector credentials:SecureCredentialsEncrypted = MessageAction; +messageActionSecureValuesSent#d95c6154 types:Vector = MessageAction; dialog#e4def5db flags:# pinned:flags.2?true peer:Peer top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int notify_settings:PeerNotifySettings pts:flags.0?int draft:flags.1?DraftMessage = Dialog; @@ -584,12 +586,12 @@ authorization#7bf2e6f6 hash:long flags:int device_model:string platform:string s account.authorizations#1250abde authorizations:Vector = account.Authorizations; -account.noPassword#c86371bd new_salt:bytes secret_random:bytes email_unconfirmed_pattern:string = account.Password; -account.password#49efccf5 current_salt:bytes new_salt:bytes secret_random:bytes hint:string has_recovery:Bool email_unconfirmed_pattern:string = account.Password; +account.noPassword#5ea182f6 new_salt:bytes new_secure_salt:bytes secure_random:bytes email_unconfirmed_pattern:string = account.Password; +account.password#d06c5fc3 current_salt:bytes new_salt:bytes new_secure_salt:bytes secure_random:bytes hint:string has_recovery:Bool email_unconfirmed_pattern:string = account.Password; -account.passwordSettings#f8ebfc3f email:string secure_secret:bytes = account.PasswordSettings; +account.passwordSettings#48ec1750 email:string secure_salt:bytes secure_secret:bytes secure_secret_hash:long = account.PasswordSettings; -account.passwordInputSettings#20ce9e59 flags:# new_salt:flags.0?bytes new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string new_secure_secret:flags.2?bytes = account.PasswordInputSettings; +account.passwordInputSettings#e553a6cf flags:# new_salt:flags.0?bytes new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string new_secure_salt:flags.2?bytes new_secure_secret:flags.2?bytes new_secure_secret_hash:flags.2?long = account.PasswordInputSettings; auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery; @@ -965,36 +967,40 @@ help.proxyDataPromo#2bf7ee23 expires:int peer:Peer chats:Vector users:Vect help.termsOfServiceUpdateEmpty#e3309f7f expires:int = help.TermsOfServiceUpdate; help.termsOfServiceUpdate#28ecf961 expires:int terms_of_service:help.TermsOfService = help.TermsOfServiceUpdate; -inputSecureFileUploaded#c53ed020 id:long parts:int md5_checksum:string file_hash:string = InputSecureFile; +inputSecureFileUploaded#c53ed020 id:long parts:int md5_checksum:string file_hash:bytes = InputSecureFile; inputSecureFile#5367e5be id:long access_hash:long = InputSecureFile; secureFileEmpty#64199744 = SecureFile; -secureFile#40ad69ba id:long access_hash:long size:int dc_id:int file_hash:string = SecureFile; +secureFile#40ad69ba id:long access_hash:long size:int dc_id:int file_hash:bytes = SecureFile; -secureValueEmpty#f0cec6e0 name:string = SecureValue; -secureValueData#6c3bf275 name:string data:bytes hash:string secret:bytes = SecureValue; -secureValueFile#a145a158 name:string file:Vector hash:string secret:bytes = SecureValue; -secureValueText#1bb37ce3 name:string text:string hash:string = SecureValue; +secureData#262b0134 data:bytes data_hash:bytes = SecureData; -inputSecureValueData#627bcf0d name:string data:bytes hash:string secret:bytes = InputSecureValue; -inputSecureValueFile#e24f9466 name:string file:Vector hash:string secret:bytes = InputSecureValue; -inputSecureValueText#955a14bb name:string text:string hash:string = InputSecureValue; +secureValueVerified#612d86df date:int provider:string = SecureValueVerified; -secureValueHash#e4c2677 name:string hash:string = SecureValueHash; +secureValueTypeIdentity#37da58ca = SecureValueType; +secureValueTypeAddress#cbe31e26 = SecureValueType; +secureValueTypePhone#b320aadb = SecureValueType; +secureValueTypeEmail#8e3ca7ee = SecureValueType; -authFieldTypeIdentity#4413b5d = AuthFieldType; -authFieldTypeAddress#63bbccf3 = AuthFieldType; -authFieldTypePhone#b56120ff = AuthFieldType; -authFieldTypeEmail#4cc9fd06 = AuthFieldType; +secureValueIdentity#a6c927ad flags:# data:SecureData files:Vector secret:bytes hash:bytes verified:flags.0?SecureValueVerified = SecureValue; +secureValueAddress#74a0d79c flags:# data:SecureData files:Vector secret:bytes hash:bytes verified:flags.0?SecureValueVerified = SecureValue; +secureValuePhone#a1ca84fe flags:# phone:string hash:bytes verified:flags.0?SecureValueVerified = SecureValue; +secureValueEmail#c4db6579 flags:# email:string hash:bytes verified:flags.0?SecureValueVerified = SecureValue; -authField#ed81c3dc flags:# type:AuthFieldType data:SecureValue document:flags.0?SecureValue = AuthField; +inputSecureValueIdentity#ae27d606 data:SecureData files:Vector secret:bytes hash:bytes = InputSecureValue; +inputSecureValueAddress#c1f733e5 data:SecureData files:Vector secret:bytes hash:bytes = InputSecureValue; +inputSecureValuePhone#141e00b8 phone:string hash:bytes = InputSecureValue; +inputSecureValueEmail#2dc15b9a email:string hash:bytes = InputSecureValue; -account.secureValueResultSaved#f4e0ab11 = account.SecureValueResult; -account.secureValueVerificationNeeded#9192a1db code_hash:string = account.SecureValueResult; +secureValueHash#ed1ecdb0 type:SecureValueType hash:bytes = SecureValueHash; -account.authorizationForm#40997b71 flags:# accepted:flags.0?true request_write:flags.1?true bot_id:int fields:Vector accepted_fields:Vector users:Vector = account.AuthorizationForm; +secureValueSaved#c9147049 files:Vector = SecureValueSaved; -account.authorizationResult#836727ce user_data:DataJSON accepted_fields:Vector = account.AuthorizationResult; +secureCredentialsEncrypted#628fe12a data:bytes secret:bytes hash:bytes = SecureCredentialsEncrypted; + +account.authorizationForm#8d9dddeb flags:# accepted:flags.0?true required_types:Vector values:Vector users:Vector = account.AuthorizationForm; + +account.sentEmailCode#28b1633b email_pattern:string = account.SentEmailCode; ---functions--- @@ -1051,11 +1057,15 @@ account.getTmpPassword#4a82327e password_hash:bytes period:int = account.TmpPass account.getWebAuthorizations#182e6d6f = account.WebAuthorizations; account.resetWebAuthorization#2d01b9ef hash:long = Bool; account.resetWebAuthorizations#682d2594 = Bool; -account.getSecureValue#d86fa683 user_id:InputUser name:Vector = Vector; -account.saveSecureValue#9fc2a624 data:InputSecureValue = account.SecureValueResult; -account.verifySecureValue#68367bed name:string code_hash:string code:string = Bool; -account.getAuthorizationForm#2f003837 flags:# bot_id:int scope:Vector origin:flags.0?string package_name:flags.1?string bundle_id:flags.2?string public_key:flags.3?string = account.AuthorizationForm; -account.acceptAuthorization#df98a3f5 flags:# request_write:flags.4?true bot_id:int scope:Vector origin:flags.0?string package_name:flags.1?string bundle_id:flags.2?string public_key:flags.3?string accepted_fields:Vector value_hashes:Vector = account.AuthorizationResult; +account.getSecureValue#d97e77cb user_id:InputUser types:Vector = Vector; +account.saveSecureValue#842659a5 value:InputSecureValue secure_secret_hash:long = SecureValueSaved; +account.deleteSecureValue#b880bc4b types:Vector = Bool; +account.getAuthorizationForm#b86ba8e1 bot_id:int scope:string public_key:string = account.AuthorizationForm; +account.acceptAuthorization#e7027c94 bot_id:int scope:string public_key:string value_hashes:Vector credentials:SecureCredentialsEncrypted = Bool; +account.sendVerifyPhoneCode#823380b4 flags:# allow_flashcall:flags.0?true phone_number:string current_number:flags.0?Bool = auth.SentCode; +account.verifyPhone#4dd3a7f6 phone_number:string phone_code_hash:string phone_code:string = Bool; +account.sendVerifyEmailCode#7011509f email:string = account.SentEmailCode; +account.verifyEmail#ecba39db email:string code:string = Bool; users.getUsers#d91a548 id:Vector = Vector; users.getFullUser#ca30a5b1 id:InputUser = UserFull; diff --git a/Telegram/SourceFiles/boxes/passcode_box.cpp b/Telegram/SourceFiles/boxes/passcode_box.cpp index bbd8ec3fe..2926864a3 100644 --- a/Telegram/SourceFiles/boxes/passcode_box.cpp +++ b/Telegram/SourceFiles/boxes/passcode_box.cpp @@ -331,6 +331,8 @@ void PasscodeBox::save(bool force) { flags |= MTPDaccount_passwordInputSettings::Flag::f_email; } const auto newSecureSecret = bytes::vector(); + const auto newSecureSalt = bytes::vector(); + const auto newSecureSecretHash = 0ULL; _setRequest = MTP::send( MTPaccount_UpdatePasswordSettings( MTP_bytes(oldPasswordHash), @@ -340,7 +342,9 @@ void PasscodeBox::save(bool force) { MTP_bytes(newPasswordHash), MTP_string(hint), MTP_string(email), - MTP_bytes(newSecureSecret))), + MTP_bytes(newSecureSalt), + MTP_bytes(newSecureSecret), + MTP_long(newSecureSecretHash))), rpcDone(&PasscodeBox::setPasswordDone), rpcFail(&PasscodeBox::setPasswordFail)); } diff --git a/Telegram/SourceFiles/history/history_service.cpp b/Telegram/SourceFiles/history/history_service.cpp index a243d579c..91f5a8470 100644 --- a/Telegram/SourceFiles/history/history_service.cpp +++ b/Telegram/SourceFiles/history/history_service.cpp @@ -166,6 +166,11 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { return result; }; + auto prepareSecureValuesSent = [&](const MTPDmessageActionSecureValuesSent &action) { + // #TODO passport + return PreparedText{ QString("Secure values sent.") }; + }; + auto messageText = PreparedText {}; switch (action.type()) { @@ -187,6 +192,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { case mtpc_messageActionScreenshotTaken: messageText = prepareScreenshotTaken(); break; case mtpc_messageActionCustomAction: messageText = prepareCustomAction(action.c_messageActionCustomAction()); break; case mtpc_messageActionBotAllowed: messageText = prepareBotAllowed(action.c_messageActionBotAllowed()); break; + case mtpc_messageActionSecureValuesSent: messageText = prepareSecureValuesSent(action.c_messageActionSecureValuesSent()); break; default: messageText.text = lang(lng_message_empty); break; } diff --git a/Telegram/SourceFiles/messenger.cpp b/Telegram/SourceFiles/messenger.cpp index b13681e3d..62671afe1 100644 --- a/Telegram/SourceFiles/messenger.cpp +++ b/Telegram/SourceFiles/messenger.cpp @@ -914,7 +914,7 @@ bool Messenger::openLocalUrl(const QString &url) { authMatch->captured(1), UrlParamNameTransform::ToLower); if (const auto botId = params.value("bot_id", QString()).toInt()) { - const auto scope = params.value("scope", QString()).split(','); + const auto scope = params.value("scope", QString()); const auto callback = params.value("callback_url", QString()); const auto publicKey = params.value("public_key", QString()); if (const auto window = App::wnd()) { diff --git a/Telegram/SourceFiles/passport/passport_encryption.cpp b/Telegram/SourceFiles/passport/passport_encryption.cpp index 928fa895e..6ce6c02df 100644 --- a/Telegram/SourceFiles/passport/passport_encryption.cpp +++ b/Telegram/SourceFiles/passport/passport_encryption.cpp @@ -146,13 +146,19 @@ bytes::vector EncryptSecretBytes( return Encrypt(secret, std::move(params)); } -bytes::vector Concatenate( - bytes::const_span a, - bytes::const_span b) { - auto result = bytes::vector(a.size() + b.size()); - bytes::copy(result, a); - bytes::copy(gsl::make_span(result).subspan(a.size()), b); - return result; +bytes::vector DecryptSecureSecret( + bytes::const_span salt, + bytes::const_span encryptedSecret, + bytes::const_span password) { + Expects(!salt.empty()); + Expects(!encryptedSecret.empty()); + Expects(!password.empty()); + + const auto hash = openssl::Sha512(bytes::concatenate( + salt, + password, + salt)); + return DecryptSecretBytes(encryptedSecret, hash); } bytes::vector SerializeData(const std::map &data) { @@ -246,8 +252,9 @@ EncryptedData EncryptData( gsl::make_span(unencrypted).subspan(padding), bytes); const auto dataHash = openssl::Sha256(unencrypted); - const auto dataSecretHash = openssl::Sha512( - Concatenate(dataSecret, dataHash)); + const auto dataSecretHash = openssl::Sha512(bytes::concatenate( + dataSecret, + dataHash)); auto params = PrepareAesParams(dataSecretHash); return { @@ -272,8 +279,9 @@ bytes::vector DecryptData( return {}; } - const auto dataSecretHash = openssl::Sha512( - Concatenate(dataSecret, dataHash)); + const auto dataSecretHash = openssl::Sha512(bytes::concatenate( + dataSecret, + dataHash)); auto params = PrepareAesParams(dataSecretHash); const auto decrypted = Decrypt(encrypted, std::move(params)); if (bytes::compare(openssl::Sha256(decrypted), dataHash) != 0) { @@ -294,22 +302,15 @@ bytes::vector DecryptData( bytes::vector PrepareValueHash( bytes::const_span dataHash, bytes::const_span valueSecret) { - const auto result = openssl::Sha256(Concatenate(dataHash, valueSecret)); - return { result.begin(), result.end() }; + return openssl::Sha256(bytes::concatenate(dataHash, valueSecret)); } bytes::vector PrepareFilesHash( gsl::span fileHashes, bytes::const_span valueSecret) { - auto resultInner = bytes::vector{ - valueSecret.begin(), - valueSecret.end() - }; - for (const auto &hash : base::reversed(fileHashes)) { - resultInner = Concatenate(hash, resultInner); - } - const auto result = openssl::Sha256(resultInner); - return { result.begin(), result.end() }; + return openssl::Sha256(bytes::concatenate( + bytes::concatenate(fileHashes), + valueSecret)); } bytes::vector EncryptValueSecret( @@ -317,7 +318,7 @@ bytes::vector EncryptValueSecret( bytes::const_span secret, bytes::const_span valueHash) { const auto valueSecretHash = openssl::Sha512( - Concatenate(secret, valueHash)); + bytes::concatenate(secret, valueHash)); return EncryptSecretBytes(valueSecret, valueSecretHash); } @@ -325,9 +326,18 @@ bytes::vector DecryptValueSecret( bytes::const_span encrypted, bytes::const_span secret, bytes::const_span valueHash) { - const auto valueSecretHash = openssl::Sha512( - Concatenate(secret, valueHash)); + const auto valueSecretHash = openssl::Sha512(bytes::concatenate( + secret, + valueHash)); return DecryptSecretBytes(encrypted, valueSecretHash); } +uint64 CountSecureSecretHash(bytes::const_span secret) { + const auto full = openssl::Sha256(secret); + const auto part = bytes::make_span(full).subspan( + full.size() - sizeof(uint64), + sizeof(uint64)); + return *reinterpret_cast(part.data()); +} + } // namespace Passport diff --git a/Telegram/SourceFiles/passport/passport_encryption.h b/Telegram/SourceFiles/passport/passport_encryption.h index 02e6e18f3..173049720 100644 --- a/Telegram/SourceFiles/passport/passport_encryption.h +++ b/Telegram/SourceFiles/passport/passport_encryption.h @@ -18,6 +18,11 @@ bytes::vector DecryptSecretBytes( bytes::const_span encryptedSecret, bytes::const_span passwordHashForSecret); +bytes::vector DecryptSecureSecret( + bytes::const_span salt, + bytes::const_span encryptedSecret, + bytes::const_span password); + bytes::vector PasswordHashForSecret(bytes::const_span passwordUtf8); bytes::vector SerializeData(const std::map &data); @@ -58,4 +63,6 @@ bytes::vector PrepareFilesHash( gsl::span fileHashes, bytes::const_span valueSecret); +uint64 CountSecureSecretHash(bytes::const_span secret); + } // namespace Passport diff --git a/Telegram/SourceFiles/passport/passport_form_controller.cpp b/Telegram/SourceFiles/passport/passport_form_controller.cpp index f8823071d..a8c6b2587 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_form_controller.cpp @@ -23,7 +23,7 @@ namespace Passport { FormRequest::FormRequest( UserId botId, - const QStringList &scope, + const QString &scope, const QString &callbackUrl, const QString &publicKey) : botId(botId) @@ -65,8 +65,6 @@ FormController::FormController( const FormRequest &request) : _controller(controller) , _request(request) { - const auto url = QUrl(_request.callbackUrl); - _origin = url.scheme() + "://" + url.host(); } void FormController::show() { @@ -74,8 +72,16 @@ void FormController::show() { requestPassword(); } +bytes::vector FormController::passwordHashForAuth( + bytes::const_span password) const { + return openssl::Sha256(bytes::concatenate( + _password.salt, + password, + _password.salt)); +} + void FormController::submitPassword(const QString &password) { - Expects(!_password.salt.isEmpty()); + Expects(!_password.salt.empty()); if (_passwordCheckRequestId) { return; @@ -83,41 +89,19 @@ void FormController::submitPassword(const QString &password) { _passwordError.fire(QString()); } const auto passwordBytes = password.toUtf8(); - _passwordHashForAuth = openssl::Sha256(bytes::concatenate( - bytes::make_span(_password.salt), - bytes::make_span(passwordBytes), - bytes::make_span(_password.salt))); _passwordCheckRequestId = request(MTPaccount_GetPasswordSettings( - MTP_bytes(_passwordHashForAuth) + MTP_bytes(passwordHashForAuth(bytes::make_span(passwordBytes))) )).handleFloodErrors( ).done([=](const MTPaccount_PasswordSettings &result) { Expects(result.type() == mtpc_account_passwordSettings); _passwordCheckRequestId = 0; const auto &data = result.c_account_passwordSettings(); - _passwordEmail = qs(data.vemail); - _passwordHashForSecret = openssl::Sha512(bytes::make_span(passwordBytes)); - _secret = DecryptSecretBytes( + _password.confirmedEmail = qs(data.vemail); + validateSecureSecret( + bytes::make_span(data.vsecure_salt.v), bytes::make_span(data.vsecure_secret.v), - _passwordHashForSecret); - for (auto &field : _form.fields) { - field.data.values = fillData(field.data); - if (auto &document = field.document) { - const auto filesHash = bytes::make_span(document->filesHash); - document->filesSecret = DecryptValueSecret( - bytes::make_span(document->filesSecretEncrypted), - _secret, - filesHash); - if (document->filesSecret.empty() - && !document->files.empty()) { - LOG(("API Error: Empty decrypted files secret. " - "Forgetting files.")); - document->files.clear(); - document->filesHash.clear(); - } - } - } - _secretReady.fire({}); + bytes::make_span(passwordBytes)); }).fail([=](const RPCError &error) { _passwordCheckRequestId = 0; if (MTP::isFloodError(error)) { @@ -130,6 +114,87 @@ void FormController::submitPassword(const QString &password) { }).send(); } +void FormController::validateSecureSecret( + bytes::const_span salt, + bytes::const_span encryptedSecret, + bytes::const_span password) { + if (!salt.empty() && !encryptedSecret.empty()) { + _secret = DecryptSecureSecret(salt, encryptedSecret, password); + if (_secret.empty()) { + LOG(("API Error: Failed to decrypt secure secret. " + "Forgetting all files and data :(")); + for (auto &field : _form.fields) { + if (!field.encryptedSecret.empty()) { + resetField(field); + } + } + } else { + decryptFields(); + } + } + if (_secret.empty()) { + generateSecret(password); + } + _secretReady.fire({}); +} + +void FormController::decryptFields() { + Expects(!_secret.empty()); + + for (auto &field : _form.fields) { + if (field.encryptedSecret.empty()) { + continue; + } + decryptField(field); + } +} + +void FormController::decryptField(Field &field) { + Expects(!_secret.empty()); + Expects(!field.encryptedSecret.empty()); + + if (!validateFieldSecret(field)) { + resetField(field); + return; + } + const auto dataHash = bytes::make_span(field.dataHash); + field.parsedData = DeserializeData(DecryptData( + bytes::make_span(field.originalData), + field.dataHash, + field.secret)); +} + +bool FormController::validateFieldSecret(Field &field) { + field.secret = DecryptValueSecret( + field.encryptedSecret, + _secret, + field.hash); + if (field.secret.empty()) { + LOG(("API Error: Could not decrypt value secret. " + "Forgetting files and data :(")); + return false; + } + const auto fileHashes = ranges::view::all( + field.files + ) | ranges::view::transform([](File &file) { + return bytes::make_span(file.fileHash); + }); + const auto countedHash = openssl::Sha256(bytes::concatenate( + field.dataHash, + bytes::concatenate(fileHashes), + field.secret)); + if (field.hash != countedHash) { + LOG(("API Error: Wrong hash after decrypting value secret. " + "Forgetting files and data :(")); + return false; + } + return true; +} + +void FormController::resetField(Field &field) { + field = Field(field.type); +} + rpl::producer FormController::passwordError() const { return _passwordError.events(); } @@ -141,22 +206,21 @@ QString FormController::passwordHint() const { void FormController::uploadScan(int index, QByteArray &&content) { Expects(_editBox != nullptr); Expects(index >= 0 && index < _form.fields.size()); - Expects(_form.fields[index].document.has_value()); - auto &document = *_form.fields[index].document; - if (document.filesSecret.empty()) { - document.filesSecret = GenerateSecretBytes(); + auto &field = _form.fields[index]; + if (field.secret.empty()) { + field.secret = GenerateSecretBytes(); } const auto weak = _editBox; crl::async([ =, bytes = std::move(content), - filesSecret = document.filesSecret + valueSecret = field.secret ] { auto data = EncryptData( bytes::make_span(bytes), - filesSecret); + valueSecret); auto result = UploadedScan(); result.fileId = rand_value(); result.hash = std::move(data.hash); @@ -193,14 +257,13 @@ void FormController::subscribeToUploader() { void FormController::uploadEncryptedScan(int index, UploadedScan &&data) { Expects(_editBox != nullptr); Expects(index >= 0 && index < _form.fields.size()); - Expects(_form.fields[index].document.has_value()); subscribeToUploader(); - _form.fields[index].document->filesInEdit.emplace_back( + _form.fields[index].filesInEdit.emplace_back( File(), std::make_unique(std::move(data))); - auto &file = _form.fields[index].document->filesInEdit.back(); + auto &file = _form.fields[index].filesInEdit.back(); auto uploaded = std::make_shared( TaskId(), @@ -238,7 +301,7 @@ rpl::producer<> FormController::secretReadyEvents() const { } QString FormController::defaultEmail() const { - return _passwordEmail; + return _password.confirmedEmail; } QString FormController::defaultPhoneNumber() const { @@ -294,13 +357,9 @@ void FormController::editField(int index) { auto &field = _form.fields[index]; switch (field.type) { case Field::Type::Identity: - if (field.document) { - loadFiles(field.document->files); - } else { - field.document = Value(); - } - field.document->filesInEdit = ranges::view::all( - field.document->files + loadFiles(field.files); + field.filesInEdit = ranges::view::all( + field.files ) | ranges::view::transform([](const File &file) { return EditFile(file, nullptr); }) | ranges::to_vector; @@ -353,7 +412,7 @@ void FormController::fileLoaded(FileKey key, const QByteArray &bytes) { const auto decrypted = DecryptData( bytes::make_span(bytes), file->fileHash, - field->document->filesSecret); + field->secret); auto image = App::readImage(QByteArray::fromRawData( reinterpret_cast(decrypted.data()), decrypted.size())); @@ -368,7 +427,7 @@ void FormController::fileLoaded(FileKey key, const QByteArray &bytes) { } IdentityData FormController::fieldDataIdentity(const Field &field) const { - const auto &map = field.data.values; + const auto &map = field.parsedData; auto result = IdentityData(); if (const auto i = map.find(qsl("first_name")); i != map.cend()) { result.name = i->second; @@ -381,10 +440,8 @@ IdentityData FormController::fieldDataIdentity(const Field &field) const { std::vector FormController::fieldFilesIdentity( const Field &field) const { - Expects(field.document.has_value()); - auto result = std::vector(); - for (const auto &file : field.document->filesInEdit) { + for (const auto &file : field.filesInEdit) { result.push_back({ FileKey{ file.fields.id, file.fields.dcId }, langDateTime(QDateTime::currentDateTime()), @@ -400,178 +457,119 @@ void FormController::saveFieldIdentity( Expects(index >= 0 && index < _form.fields.size()); Expects(_form.fields[index].type == Field::Type::Identity); - _form.fields[index].data.values[qsl("first_name")] = data.name; - _form.fields[index].data.values[qsl("last_name")] = data.surname; + _form.fields[index].parsedData[qsl("first_name")] = data.name; + _form.fields[index].parsedData[qsl("last_name")] = data.surname; - saveData(index); - saveFiles(index); - - _editBox->closeBox(); + saveIdentity(index); } -std::map FormController::fillData( - const Value &from) const { - if (from.dataEncrypted.isEmpty()) { - return {}; - } - const auto valueHash = bytes::make_span(from.dataHash); - const auto valueSecret = DecryptValueSecret( - bytes::make_span(from.dataSecretEncrypted), - _secret, - valueHash); - return DeserializeData(DecryptData( - bytes::make_span(from.dataEncrypted), - valueHash, - valueSecret)); -} - -void FormController::saveData(int index) { +void FormController::saveIdentity(int index) { Expects(index >= 0 && index < _form.fields.size()); + Expects(_form.fields[index].type == Field::Type::Identity); if (_secret.empty()) { - generateSecret([=] { - saveData(index); + _secretCallbacks.push_back([=] { + saveIdentity(index); }); return; } - const auto &data = _form.fields[index].data; - const auto encrypted = EncryptData(SerializeData(data.values)); - // #TODO file_hash + file_hash + ... - // PrepareValueHash(encrypted.hash, valueSecret); - const auto valueHash = encrypted.hash; - auto valueHashString = QString(); - valueHashString.reserve(valueHash.size() * 2); - const auto hex = [](uchar value) -> QChar { - return (value >= 10) ? ('a' + (value - 10)) : ('0' + value); - }; - for (const auto byte : valueHash) { - const auto value = uchar(byte); - const auto high = uchar(value / 16); - const auto low = uchar(value % 16); - valueHashString.append(hex(high)).append(hex(low)); + _editBox->closeBox(); + + auto &field = _form.fields[index]; + + auto inputFiles = QVector(); + inputFiles.reserve(field.filesInEdit.size()); + for (const auto &file : field.filesInEdit) { + if (const auto uploaded = file.uploaded.get()) { + inputFiles.push_back(MTP_inputSecureFileUploaded( + MTP_long(file.fields.id), + MTP_int(uploaded->partsCount), + MTP_bytes(uploaded->md5checksum), + MTP_bytes(file.fields.fileHash))); + } else { + inputFiles.push_back(MTP_inputSecureFile( + MTP_long(file.fields.id), + MTP_long(file.fields.accessHash))); + } } + if (field.secret.empty()) { + field.secret = GenerateSecretBytes(); + } + const auto encryptedData = EncryptData( + SerializeData(field.parsedData), + field.secret); + const auto fileHashes = ranges::view::all( + field.filesInEdit + ) | ranges::view::transform([](const EditFile &file) { + return bytes::make_span(file.fields.fileHash); + }); + const auto valueHash = openssl::Sha256(bytes::concatenate( + encryptedData.hash, + bytes::concatenate(fileHashes), + field.secret)); - const auto encryptedValueSecret = EncryptValueSecret( - encrypted.secret, + field.encryptedSecret = EncryptValueSecret( + field.secret, _secret, valueHash); - request(MTPaccount_SaveSecureValue(MTP_inputSecureValueData( - MTP_string(data.name), - MTP_bytes(encrypted.bytes), - MTP_string(valueHashString), - MTP_bytes(encryptedValueSecret) - ))).done([=](const MTPaccount_SecureValueResult &result) { - if (result.type() == mtpc_account_secureValueResultSaved) { - Ui::show(Box("Saved"), LayerOption::KeepOther); - } else if (result.type() == mtpc_account_secureValueVerificationNeeded) { - Ui::show(Box("Verification needed :("), LayerOption::KeepOther); - } + request(MTPaccount_SaveSecureValue( + MTP_inputSecureValueIdentity( + MTP_secureData( + MTP_bytes(encryptedData.bytes), + MTP_bytes(encryptedData.hash)), + MTP_vector(inputFiles), + MTP_bytes(field.encryptedSecret), + MTP_bytes(valueHash)), + MTP_long(CountSecureSecretHash(_secret)) + )).done([=](const MTPSecureValueSaved &result) { + Ui::show(Box("Saved"), LayerOption::KeepOther); }).fail([=](const RPCError &error) { Ui::show(Box("Error saving value.")); }).send(); } -void FormController::saveFiles(int index) { - Expects(index >= 0 && index < _form.fields.size()); - Expects(_form.fields[index].document.has_value()); - - if (_secret.empty()) { - generateSecret([=] { - saveFiles(index); - }); - return; - } - auto &document = *_form.fields[index].document; - if (document.filesSecret.empty()) { - Assert(document.filesInEdit.empty()); - return; - } - auto filesHash = computeFilesHash( - ranges::view::all( - document.filesInEdit - ) | ranges::view::transform([=](const EditFile &file) { - return bytes::make_span(file.fields.fileHash); - }), - document.filesSecret); - - auto filesHashString = QString(); - filesHashString.reserve(filesHash.size() * 2); - const auto hex = [](uchar value) -> QChar { - return (value >= 10) ? ('a' + (value - 10)) : ('0' + value); - }; - for (const auto byte : filesHash) { - const auto value = uchar(byte); - const auto high = uchar(value / 16); - const auto low = uchar(value % 16); - filesHashString.append(hex(high)).append(hex(low)); - } - - auto files = QVector(); - files.reserve(document.filesInEdit.size()); - for (const auto &file : document.filesInEdit) { - if (const auto uploaded = file.uploaded.get()) { - auto fileHashString = QString(); - for (const auto byte : file.fields.fileHash) { - const auto value = uchar(byte); - const auto high = uchar(value / 16); - const auto low = uchar(value % 16); - fileHashString.append(hex(high)).append(hex(low)); - } - files.push_back(MTP_inputSecureFileUploaded( - MTP_long(file.fields.id), - MTP_int(uploaded->partsCount), - MTP_bytes(uploaded->md5checksum), - MTP_string(fileHashString))); - } else { - files.push_back(MTP_inputSecureFile( - MTP_long(file.fields.id), - MTP_long(file.fields.accessHash))); - } - } - - const auto encryptedFilesSecret = EncryptValueSecret( - document.filesSecret, - _secret, - filesHash); - request(MTPaccount_SaveSecureValue(MTP_inputSecureValueFile( - MTP_string(document.name), - MTP_vector(files), - MTP_string(filesHashString), - MTP_bytes(encryptedFilesSecret) - ))).done([=](const MTPaccount_SecureValueResult &result) { - if (result.type() == mtpc_account_secureValueResultSaved) { - Ui::show(Box("Files Saved"), LayerOption::KeepOther); - } else if (result.type() == mtpc_account_secureValueVerificationNeeded) { - Ui::show(Box("Verification needed :("), LayerOption::KeepOther); - } - }).fail([=](const RPCError &error) { - Ui::show(Box("Error saving files.")); - }).send(); -} - -void FormController::generateSecret(base::lambda callback) { - _secretCallbacks.push_back(callback); +void FormController::generateSecret(bytes::const_span password) { if (_saveSecretRequestId) { return; } auto secret = GenerateSecretBytes(); + + auto randomSaltPart = bytes::vector(8); + bytes::set_random(randomSaltPart); + auto newSecureSaltFull = bytes::concatenate( + _password.newSecureSalt, + randomSaltPart); + + const auto hashForSecret = openssl::Sha512(bytes::concatenate( + newSecureSaltFull, + password, + newSecureSaltFull)); + const auto hashForAuth = openssl::Sha256(bytes::concatenate( + _password.salt, + password, + _password.salt)); + + auto secureSecretHash = CountSecureSecretHash(secret); auto encryptedSecret = EncryptSecretBytes( secret, - _passwordHashForSecret); + hashForSecret); using Flag = MTPDaccount_passwordInputSettings::Flag; _saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings( - MTP_bytes(_passwordHashForAuth), + MTP_bytes(hashForAuth), MTP_account_passwordInputSettings( MTP_flags(Flag::f_new_secure_secret), MTPbytes(), // new_salt MTPbytes(), // new_password_hash MTPstring(), // hint MTPstring(), // email - MTP_bytes(encryptedSecret)) + MTP_bytes(newSecureSaltFull), + MTP_bytes(encryptedSecret), + MTP_long(secureSecretHash)) )).done([=](const MTPBool &result) { _saveSecretRequestId = 0; _secret = secret; + //_password.salt = newPasswordSaltFull; for (const auto &callback : base::take(_secretCallbacks)) { callback(); } @@ -583,23 +581,12 @@ void FormController::generateSecret(base::lambda callback) { } void FormController::requestForm() { - auto scope = QVector(); - scope.reserve(_request.scope.size()); - for (const auto &element : _request.scope) { - scope.push_back(MTP_string(element)); - } auto normalizedKey = _request.publicKey; normalizedKey.replace("\r\n", "\n"); - const auto bytes = normalizedKey.toUtf8(); _formRequestId = request(MTPaccount_GetAuthorizationForm( - MTP_flags(MTPaccount_GetAuthorizationForm::Flag::f_origin - | MTPaccount_GetAuthorizationForm::Flag::f_public_key), MTP_int(_request.botId), - MTP_vector(std::move(scope)), - MTP_string(_origin), - MTPstring(), // package_name - MTPstring(), // bundle_id - MTP_bytes(bytes) + MTP_string(_request.scope), + MTP_bytes(normalizedKey.toUtf8()) )).done([=](const MTPaccount_AuthorizationForm &result) { _formRequestId = 0; formDone(result); @@ -608,130 +595,110 @@ void FormController::requestForm() { }).send(); } -auto FormController::convertValue( - const MTPSecureValue &value) const -> Value { - auto result = Value(); - switch (value.type()) { - case mtpc_secureValueEmpty: { - const auto &data = value.c_secureValueEmpty(); - result.name = qs(data.vname); - } break; - case mtpc_secureValueData: { - const auto &data = value.c_secureValueData(); - result.name = qs(data.vname); - result.dataEncrypted = data.vdata.v; - const auto hashString = qs(data.vhash); - for (auto i = 0, count = hashString.size(); i + 1 < count; i += 2) { - auto digit = [&](QChar ch) -> int { - const auto code = ch.unicode(); - if (code >= 'a' && code <= 'f') { - return (code - 'a') + 10; - } else if (code >= 'A' && code <= 'F') { - return (code - 'A') + 10; - } else if (code >= '0' && code <= '9') { - return (code - '0'); - } - return -1; - }; - const auto ch1 = digit(hashString[i]); - const auto ch2 = digit(hashString[i + 1]); - if (ch1 >= 0 && ch2 >= 0) { - const auto byte = ch1 * 16 + ch2; - result.dataHash.push_back(byte); - } +template +auto FormController::parseEncryptedField( + Field::Type type, + const DataType &data) const -> Field { + Expects(data.vdata.type() == mtpc_secureData); + + auto result = Field(type); + if (data.has_verified()) { + result.verification = parseVerified(data.vverified); + } + result.encryptedSecret = bytes::make_vector(data.vsecret.v); + result.hash = bytes::make_vector(data.vhash.v); + const auto &fields = data.vdata.c_secureData(); + result.originalData = fields.vdata.v; + result.dataHash = bytes::make_vector(fields.vdata_hash.v); + for (const auto &file : data.vfiles.v) { + switch (file.type()) { + case mtpc_secureFileEmpty: { + result.files.push_back(File()); + } break; + case mtpc_secureFile: { + const auto &fields = file.c_secureFile(); + auto normal = File(); + normal.id = fields.vid.v; + normal.accessHash = fields.vaccess_hash.v; + normal.size = fields.vsize.v; + normal.dcId = fields.vdc_id.v; + normal.fileHash = bytes::make_vector(fields.vfile_hash.v); + result.files.push_back(std::move(normal)); + } break; } - if (result.dataHash.size() != 32) { - result.dataHash.clear(); - } -// result.dataHash = data.vhash.v; - result.dataSecretEncrypted = data.vsecret.v; - } break; - case mtpc_secureValueText: { - const auto &data = value.c_secureValueText(); - result.name = qs(data.vname); - result.text = qs(data.vtext); - result.textHash = data.vhash.v; - } break; - case mtpc_secureValueFile: { - const auto &data = value.c_secureValueFile(); - result.name = qs(data.vname); - const auto hashString = qs(data.vhash); - for (auto i = 0, count = hashString.size(); i + 1 < count; i += 2) { - auto digit = [&](QChar ch) -> int { - const auto code = ch.unicode(); - if (code >= 'a' && code <= 'f') { - return (code - 'a') + 10; - } else if (code >= 'A' && code <= 'F') { - return (code - 'A') + 10; - } else if (code >= '0' && code <= '9') { - return (code - '0'); - } - return -1; - }; - const auto ch1 = digit(hashString[i]); - const auto ch2 = digit(hashString[i + 1]); - if (ch1 >= 0 && ch2 >= 0) { - const auto byte = ch1 * 16 + ch2; - result.filesHash.push_back(byte); - } - } - if (result.filesHash.size() != 32) { - result.filesHash.clear(); - } -// result.filesHash = data.vhash.v; - result.filesSecretEncrypted = data.vsecret.v; - for (const auto &file : data.vfile.v) { - switch (file.type()) { - case mtpc_secureFileEmpty: { - result.files.push_back(File()); - } break; - case mtpc_secureFile: { - const auto &fields = file.c_secureFile(); - auto normal = File(); - normal.id = fields.vid.v; - normal.accessHash = fields.vaccess_hash.v; - normal.size = fields.vsize.v; - normal.dcId = fields.vdc_id.v; - const auto fileHashString = qs(fields.vfile_hash); - for (auto i = 0, count = fileHashString.size(); i + 1 < count; i += 2) { - auto digit = [&](QChar ch) -> int { - const auto code = ch.unicode(); - if (code >= 'a' && code <= 'f') { - return (code - 'a') + 10; - } else if (code >= 'A' && code <= 'F') { - return (code - 'A') + 10; - } else if (code >= '0' && code <= '9') { - return (code - '0'); - } - return -1; - }; - const auto ch1 = digit(fileHashString[i]); - const auto ch2 = digit(fileHashString[i + 1]); - if (ch1 >= 0 && ch2 >= 0) { - const auto byte = ch1 * 16 + ch2; - normal.fileHash.push_back(gsl::byte(byte)); - } - } - if (normal.fileHash.size() != 32) { - normal.fileHash.clear(); - } -// normal.fileHash = bytes::make_vector(fields.vfile_hash.v); - result.files.push_back(std::move(normal)); - } break; - } - } - } break; } return result; } +template +auto FormController::parsePlainTextField( + Field::Type type, + const QByteArray &value, + const DataType &data) const -> Field { + auto result = Field(type); + const auto check = bytes::compare( + bytes::make_span(data.vhash.v), + openssl::Sha256(bytes::make_span(value))); + if (check != 0) { + LOG(("API Error: Bad hash for plain text value. " + "Value '%1', hash '%2'" + ).arg(QString::fromUtf8(value) + ).arg(Logs::mb(data.vhash.v.data(), data.vhash.v.size()).str() + )); + return result; + } + result.parsedData[QString("value")] = QString::fromUtf8(value); + if (data.has_verified()) { + result.verification = parseVerified(data.vverified); + } + result.hash = bytes::make_vector(data.vhash.v); + return result; +} + +auto FormController::parseValue( + const MTPSecureValue &value) const -> Field { + switch (value.type()) { + case mtpc_secureValueIdentity: { + return parseEncryptedField( + Field::Type::Identity, + value.c_secureValueIdentity()); + } break; + case mtpc_secureValueAddress: { + return parseEncryptedField( + Field::Type::Address, + value.c_secureValueAddress()); + } break; + case mtpc_secureValuePhone: { + const auto &data = value.c_secureValuePhone(); + return parsePlainTextField( + Field::Type::Phone, + data.vphone.v, + data); + } break; + case mtpc_secureValueEmail: { + const auto &data = value.c_secureValueEmail(); + return parsePlainTextField( + Field::Type::Phone, + data.vemail.v, + data); + } break; + } + Unexpected("secureValue type."); +} + +auto FormController::parseVerified(const MTPSecureValueVerified &data) const +-> Verification { + Expects(data.type() == mtpc_secureValueVerified); + + const auto &fields = data.c_secureValueVerified(); + return Verification{ fields.vdate.v, qs(fields.vprovider) }; +} + auto FormController::findEditFile(const FullMsgId &fullId) -> EditFile* { for (auto &field : _form.fields) { - if (auto &document = field.document) { - for (auto &file : document->filesInEdit) { - if (file.uploaded && file.uploaded->fullId == fullId) { - return &file; - } + for (auto &file : field.filesInEdit) { + if (file.uploaded && file.uploaded->fullId == fullId) { + return &file; } } } @@ -741,11 +708,9 @@ auto FormController::findEditFile(const FullMsgId &fullId) -> EditFile* { auto FormController::findFile(const FileKey &key) -> std::pair { for (auto &field : _form.fields) { - if (auto &document = field.document) { - for (auto &file : document->files) { - if (file.dcId == key.dcId && file.id == key.id) { - return { &field, &file }; - } + for (auto &file : field.files) { + if (file.dcId == key.dcId && file.id == key.id) { + return { &field, &file }; } } } @@ -763,33 +728,40 @@ void FormController::parseForm(const MTPaccount_AuthorizationForm &result) { Expects(result.type() == mtpc_account_authorizationForm); const auto &data = result.c_account_authorizationForm(); - _form.requestWrite = data.is_request_write(); - if (_request.botId != data.vbot_id.v) { - LOG(("API Error: Wrong account.authorizationForm.bot_id.")); - _request.botId = data.vbot_id.v; - } - App::feedUsers(data.vusers); - for (const auto &field : data.vfields.v) { - Assert(field.type() == mtpc_authField); + auto values = std::vector(); + for (const auto &value : data.vvalues.v) { + values.push_back(parseValue(value)); + } + const auto findValue = [&](Field::Type type) -> Field* { + for (auto &value : values) { + if (value.type == type) { + return &value; + } + } + return nullptr; + }; + + _form.requestWrite = false; + App::feedUsers(data.vusers); + for (const auto &required : data.vrequired_types.v) { using Type = Field::Type; - const auto &data = field.c_authField(); const auto type = [&] { - switch (data.vtype.type()) { - case mtpc_authFieldTypeIdentity: return Type::Identity; - case mtpc_authFieldTypeAddress: return Type::Address; - case mtpc_authFieldTypeEmail: return Type::Email; - case mtpc_authFieldTypePhone: return Type::Phone; + switch (required.type()) { + case mtpc_secureValueTypeIdentity: return Type::Identity; + case mtpc_secureValueTypeAddress: return Type::Address; + case mtpc_secureValueTypeEmail: return Type::Email; + case mtpc_secureValueTypePhone: return Type::Phone; } - Unexpected("Type in authField.type."); + Unexpected("Type in secureValueType type."); }(); - auto entry = Field(type); - entry.data = convertValue(data.vdata); - if (data.has_document()) { - entry.document = convertValue(data.vdocument); + + if (auto field = findValue(type)) { + _form.fields.push_back(std::move(*field)); + } else { + _form.fields.push_back(Field(type)); } - _form.fields.push_back(std::move(entry)); } _bot = App::userLoaded(_request.botId); } @@ -837,17 +809,19 @@ void FormController::passwordFail(const RPCError &error) { void FormController::parsePassword(const MTPDaccount_noPassword &result) { _password.unconfirmedPattern = qs(result.vemail_unconfirmed_pattern); - _password.newSalt = result.vnew_salt.v; - openssl::AddRandomSeed(bytes::make_span(result.vsecret_random.v)); + _password.newSalt = bytes::make_vector(result.vnew_salt.v); + _password.newSecureSalt = bytes::make_vector(result.vnew_secure_salt.v); + openssl::AddRandomSeed(bytes::make_span(result.vsecure_random.v)); } void FormController::parsePassword(const MTPDaccount_password &result) { _password.hint = qs(result.vhint); _password.hasRecovery = mtpIsTrue(result.vhas_recovery); - _password.salt = result.vcurrent_salt.v; + _password.salt = bytes::make_vector(result.vcurrent_salt.v); _password.unconfirmedPattern = qs(result.vemail_unconfirmed_pattern); - _password.newSalt = result.vnew_salt.v; - openssl::AddRandomSeed(bytes::make_span(result.vsecret_random.v)); + _password.newSalt = bytes::make_vector(result.vnew_salt.v); + _password.newSecureSalt = bytes::make_vector(result.vnew_secure_salt.v); + openssl::AddRandomSeed(bytes::make_span(result.vsecure_random.v)); } FormController::~FormController() = default; diff --git a/Telegram/SourceFiles/passport/passport_form_controller.h b/Telegram/SourceFiles/passport/passport_form_controller.h index 54ab8146c..cafa35229 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.h +++ b/Telegram/SourceFiles/passport/passport_form_controller.h @@ -25,12 +25,12 @@ namespace Passport { struct FormRequest { FormRequest( UserId botId, - const QStringList &scope, + const QString &scope, const QString &callbackUrl, const QString &publicKey); UserId botId; - QStringList scope; + QString scope; QString callbackUrl; QString publicKey; @@ -128,23 +128,9 @@ private: File fields; std::unique_ptr uploaded; }; - struct Value { - QString name; - - QByteArray dataEncrypted; - QByteArray dataHash; - QByteArray dataSecretEncrypted; - std::map values; - - QString text; - QByteArray textHash; - - std::vector files; - QByteArray filesHash; - QByteArray filesSecretEncrypted; - bytes::vector filesSecret; - - std::vector filesInEdit; + struct Verification { + TimeId date; + QString provider; }; struct Field { enum class Type { @@ -153,25 +139,36 @@ private: Phone, Email, }; + explicit Field(Type type); Field(Field &&other) = default; + Field &operator=(Field &&other) = default; Type type; - Value data; - base::optional document; + QByteArray originalData; + std::map parsedData; + bytes::vector dataHash; + std::vector files; + std::vector filesInEdit; + bytes::vector secret; + bytes::vector encryptedSecret; + bytes::vector hash; + base::optional verification; }; struct Form { bool requestWrite = false; std::vector fields; }; struct PasswordSettings { - QByteArray salt; - QByteArray newSalt; + bytes::vector salt; + bytes::vector newSalt; + bytes::vector newSecureSalt; QString hint; QString unconfirmedPattern; + QString confirmedEmail; bool hasRecovery = false; }; - Value convertValue(const MTPSecureValue &value) const; + EditFile *findEditFile(const FullMsgId &fullId); std::pair findFile(const FileKey &key); @@ -182,21 +179,39 @@ private: void formFail(const RPCError &error); void parseForm(const MTPaccount_AuthorizationForm &result); void showForm(); + Field parseValue(const MTPSecureValue &value) const; + template + Field parseEncryptedField( + Field::Type type, + const DataType &data) const; + template + Field parsePlainTextField( + Field::Type type, + const QByteArray &value, + const DataType &data) const; + Verification parseVerified(const MTPSecureValueVerified &data) const; void passwordDone(const MTPaccount_Password &result); void passwordFail(const RPCError &error); void parsePassword(const MTPDaccount_noPassword &settings); void parsePassword(const MTPDaccount_password &settings); + bytes::vector passwordHashForAuth(bytes::const_span password) const; + void validateSecureSecret( + bytes::const_span salt, + bytes::const_span encryptedSecret, + bytes::const_span password); + void decryptFields(); + void decryptField(Field &field); + bool validateFieldSecret(Field &field); + void resetField(Field &field); IdentityData fieldDataIdentity(const Field &field) const; std::vector fieldFilesIdentity(const Field &field) const; + void saveIdentity(int index); void loadFiles(const std::vector &files); void fileLoaded(FileKey key, const QByteArray &bytes); - std::map fillData(const Value &from) const; - void saveData(int index); - void saveFiles(int index); - void generateSecret(base::lambda callback); + void generateSecret(bytes::const_span password); template bytes::vector computeFilesHash( @@ -210,7 +225,6 @@ private: not_null _controller; FormRequest _request; UserData *_bot = nullptr; - QString _origin; mtpRequestId _formRequestId = 0; mtpRequestId _passwordRequestId = 0; @@ -221,12 +235,9 @@ private: std::map> _fileLoaders; rpl::event_stream _scanUpdated; - bytes::vector _passwordHashForSecret; - bytes::vector _passwordHashForAuth; bytes::vector _secret; std::vector> _secretCallbacks; mtpRequestId _saveSecretRequestId = 0; - QString _passwordEmail; rpl::event_stream<> _secretReady; rpl::event_stream _passwordError; diff --git a/Telegram/SourceFiles/settings/settings_privacy_widget.cpp b/Telegram/SourceFiles/settings/settings_privacy_widget.cpp index e5c052758..224fc5cf2 100644 --- a/Telegram/SourceFiles/settings/settings_privacy_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_privacy_widget.cpp @@ -85,12 +85,14 @@ void CloudPasswordState::onTurnOff() { MTPaccount_UpdatePasswordSettings( MTP_bytes(QByteArray()), MTP_account_passwordInputSettings( - MTP_flags(MTPDaccount_passwordInputSettings::Flag::f_email), - MTP_bytes(QByteArray()), - MTP_bytes(QByteArray()), - MTP_string(QString()), - MTP_string(QString()), - MTP_bytes(QByteArray()))), + MTP_flags(MTPDaccount_passwordInputSettings::Flag::f_email), + MTP_bytes(QByteArray()), // new_salt + MTP_bytes(QByteArray()), // new_password_hash + MTP_string(QString()), // hint + MTP_string(QString()), // email + MTP_bytes(QByteArray()), // new_secure_salt + MTP_bytes(QByteArray()), // new_secure_secret + MTP_long(0))), // new_secure_secret_hash rpcDone(&CloudPasswordState::offPasswordDone), rpcFail(&CloudPasswordState::offPasswordFail)); } else {