diff --git a/Telegram/Resources/scheme.tl b/Telegram/Resources/scheme.tl index e20a6ce93..43276bddc 100644 --- a/Telegram/Resources/scheme.tl +++ b/Telegram/Resources/scheme.tl @@ -281,7 +281,7 @@ 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; +messageActionSecureValuesSentMe#9bc8ec4 values:Vector credentials:SecureCredentialsEncrypted payload:bytes = 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; @@ -589,9 +589,9 @@ account.authorizations#1250abde authorizations:Vector = account.A 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#48ec1750 email:string secure_salt:bytes secure_secret:bytes secure_secret_hash:long = account.PasswordSettings; +account.passwordSettings#7bd9c3f1 email:string secure_salt:bytes secure_secret:bytes secure_secret_id:long = account.PasswordSettings; -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; +account.passwordInputSettings#21ffa60d 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_id:flags.2?long = account.PasswordInputSettings; auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery; @@ -967,28 +967,28 @@ 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:bytes = InputSecureFile; +inputSecureFileUploaded#3334b0f0 id:long parts:int md5_checksum:string file_hash:bytes secret: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:bytes = SecureFile; +secureFile#e0277a62 id:long access_hash:long size:int dc_id:int date:int file_hash:bytes secret:bytes = SecureFile; -secureData#262b0134 data:bytes data_hash:bytes = SecureData; +secureData#8aeabec3 data:bytes data_hash:bytes secret:bytes = SecureData; -secureValueVerified#612d86df date:int provider:string = SecureValueVerified; +secureValueVerified#2c8e61e2 date:int = SecureValueVerified; secureValueTypeIdentity#37da58ca = SecureValueType; secureValueTypeAddress#cbe31e26 = SecureValueType; secureValueTypePhone#b320aadb = SecureValueType; secureValueTypeEmail#8e3ca7ee = SecureValueType; -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; +secureValueIdentity#4838ff84 flags:# data:SecureData files:Vector hash:bytes verified:flags.0?SecureValueVerified = SecureValue; +secureValueAddress#2b9f6bef flags:# data:SecureData files:Vector 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; -inputSecureValueIdentity#ae27d606 data:SecureData files:Vector secret:bytes hash:bytes = InputSecureValue; -inputSecureValueAddress#c1f733e5 data:SecureData files:Vector secret:bytes hash:bytes = InputSecureValue; +inputSecureValueIdentity#df93404 data:SecureData files:Vector hash:bytes = InputSecureValue; +inputSecureValueAddress#5589502 data:SecureData files:Vector hash:bytes = InputSecureValue; inputSecureValuePhone#141e00b8 phone:string hash:bytes = InputSecureValue; inputSecureValueEmail#2dc15b9a email:string hash:bytes = InputSecureValue; @@ -996,9 +996,9 @@ secureValueHash#ed1ecdb0 type:SecureValueType hash:bytes = SecureValueHash; secureValueSaved#c9147049 files:Vector = SecureValueSaved; -secureCredentialsEncrypted#628fe12a data:bytes secret:bytes hash:bytes = SecureCredentialsEncrypted; +secureCredentialsEncrypted#33f0ea47 data:bytes hash:bytes secret:bytes = SecureCredentialsEncrypted; -account.authorizationForm#8d9dddeb flags:# accepted:flags.0?true required_types:Vector values:Vector users:Vector = account.AuthorizationForm; +account.authorizationForm#4cace8c4 required_types:Vector values:Vector users:Vector = account.AuthorizationForm; account.sentEmailCode#28b1633b email_pattern:string = account.SentEmailCode; @@ -1058,10 +1058,10 @@ account.getWebAuthorizations#182e6d6f = account.WebAuthorizations; account.resetWebAuthorization#2d01b9ef hash:long = Bool; account.resetWebAuthorizations#682d2594 = Bool; account.getSecureValue#d97e77cb user_id:InputUser types:Vector = Vector; -account.saveSecureValue#842659a5 value:InputSecureValue secure_secret_hash:long = SecureValueSaved; +account.saveSecureValue#78969d0b value:InputSecureValue secure_secret_id: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.acceptAuthorization#8d5e02e6 bot_id:int scope:string public_key:string value_hashes:Vector credentials:SecureCredentialsEncrypted payload:bytes = 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; diff --git a/Telegram/SourceFiles/core/utils.cpp b/Telegram/SourceFiles/core/utils.cpp index c99620497..5bf0270c9 100644 --- a/Telegram/SourceFiles/core/utils.cpp +++ b/Telegram/SourceFiles/core/utils.cpp @@ -494,10 +494,6 @@ int32 *hashSha256(const void *data, uint32 len, void *dest) { return (int32*)SHA256((const uchar*)data, (size_t)len, (uchar*)dest); } -int32 *hashSha512(const void *data, uint32 len, void *dest) { - return (int32*)SHA512((const uchar*)data, (size_t)len, (uchar*)dest); -} - // md5 hash, taken somewhere from the internet namespace { diff --git a/Telegram/SourceFiles/messenger.cpp b/Telegram/SourceFiles/messenger.cpp index 62671afe1..4c2e7edcf 100644 --- a/Telegram/SourceFiles/messenger.cpp +++ b/Telegram/SourceFiles/messenger.cpp @@ -909,7 +909,7 @@ bool Messenger::openLocalUrl(const QString &url) { auto params = url_parse_params(proxyMatch->captured(1), UrlParamNameTransform::ToLower); ProxiesBoxController::ShowApplyConfirmation(ProxyData::Type::Mtproto, params); return true; - } else if (auto authMatch = regex_match(qsl("^auth/?\\?(.+)(#|$)"), command, matchOptions)) { + } else if (auto authMatch = regex_match(qsl("^secureid/?\\?(.+)(#|$)"), command, matchOptions)) { const auto params = url_parse_params( authMatch->captured(1), UrlParamNameTransform::ToLower); diff --git a/Telegram/SourceFiles/passport/passport_edit_identity_box.cpp b/Telegram/SourceFiles/passport/passport_edit_identity_box.cpp index 32af47833..9ae9df40d 100644 --- a/Telegram/SourceFiles/passport/passport_edit_identity_box.cpp +++ b/Telegram/SourceFiles/passport/passport_edit_identity_box.cpp @@ -143,11 +143,11 @@ void ScanButton::paintEvent(QPaintEvent *e) { IdentityBox::IdentityBox( QWidget*, not_null controller, - int fieldIndex, + int valueIndex, const IdentityData &data, std::vector &&files) : _controller(controller) -, _fieldIndex(fieldIndex) +, _valueIndex(valueIndex) , _files(std::move(files)) , _uploadScan(this, "Upload scans") // #TODO langs , _name( @@ -175,7 +175,7 @@ void IdentityBox::prepare() { _scans.back()->resizeToWidth(st::boxWideWidth); _scans.back()->deleteClicks( ) | rpl::start_with_next([=] { - _controller->deleteScan(_fieldIndex, index - 1); + _controller->deleteScan(_valueIndex, index - 1); }, lifetime()); } @@ -294,14 +294,14 @@ void IdentityBox::encryptScan(const QString &path) { } void IdentityBox::encryptScanContent(QByteArray &&content) { - _controller->uploadScan(_fieldIndex, std::move(content)); + _controller->uploadScan(_valueIndex, std::move(content)); } void IdentityBox::save() { auto data = IdentityData(); data.name = _name->getLastText(); data.surname = _surname->getLastText(); - _controller->saveFieldIdentity(_fieldIndex, data); + _controller->saveValueIdentity(_valueIndex, data); } } // namespace Passport diff --git a/Telegram/SourceFiles/passport/passport_edit_identity_box.h b/Telegram/SourceFiles/passport/passport_edit_identity_box.h index b5277e345..74a215883 100644 --- a/Telegram/SourceFiles/passport/passport_edit_identity_box.h +++ b/Telegram/SourceFiles/passport/passport_edit_identity_box.h @@ -30,7 +30,7 @@ public: IdentityBox( QWidget*, not_null controller, - int fieldIndex, + int valueIndex, const IdentityData &data, std::vector &&files); @@ -50,7 +50,7 @@ private: void save(); not_null _controller; - int _fieldIndex = -1; + int _valueIndex = -1; std::vector _files; diff --git a/Telegram/SourceFiles/passport/passport_encryption.cpp b/Telegram/SourceFiles/passport/passport_encryption.cpp index 6ce6c02df..dc5b9ebbd 100644 --- a/Telegram/SourceFiles/passport/passport_encryption.cpp +++ b/Telegram/SourceFiles/passport/passport_encryption.cpp @@ -26,8 +26,8 @@ struct AesParams { bytes::vector iv; }; -AesParams PrepareAesParams(bytes::const_span secretHash) { - const auto hash = openssl::Sha512(secretHash); +AesParams PrepareAesParams(bytes::const_span bytesForEncryptionKey) { + const auto hash = openssl::Sha512(bytesForEncryptionKey); const auto view = gsl::make_span(hash); auto result = AesParams(); @@ -82,14 +82,6 @@ bytes::vector Decrypt( return EncryptOrDecrypt(encrypted, std::move(params), AES_DECRYPT); } -bytes::vector PasswordHashForSecret( - bytes::const_span passwordUtf8) { - //new_secure_salt = new_salt + random_bytes(8) // #TODO - //password_hash = SHA512(new_secure_salt + password + new_secure_salt) - const auto result = openssl::Sha512(passwordUtf8); - return { result.begin(), result.end() }; -} - bool CheckBytesMod255(bytes::const_span bytes) { const auto full = ranges::accumulate( bytes, @@ -119,7 +111,7 @@ bytes::vector GenerateSecretBytes() { bytes::vector DecryptSecretBytes( bytes::const_span encryptedSecret, - bytes::const_span passwordHashForSecret) { + bytes::const_span bytesForEncryptionKey) { if (encryptedSecret.empty()) { return {}; } else if (encryptedSecret.size() != kSecretSize) { @@ -127,7 +119,7 @@ bytes::vector DecryptSecretBytes( ).arg(encryptedSecret.size())); return {}; } - auto params = PrepareAesParams(passwordHashForSecret); + auto params = PrepareAesParams(bytesForEncryptionKey); auto result = Decrypt(encryptedSecret, std::move(params)); if (!CheckSecretBytes(result)) { LOG(("API Error: Bad secret bytes.")); @@ -138,11 +130,11 @@ bytes::vector DecryptSecretBytes( bytes::vector EncryptSecretBytes( bytes::const_span secret, - bytes::const_span passwordHashForSecret) { + bytes::const_span bytesForEncryptionKey) { Expects(secret.size() == kSecretSize); Expects(CheckSecretBytes(secret) == true); - auto params = PrepareAesParams(passwordHashForSecret); + auto params = PrepareAesParams(bytesForEncryptionKey); return Encrypt(secret, std::move(params)); } @@ -154,11 +146,26 @@ bytes::vector DecryptSecureSecret( Expects(!encryptedSecret.empty()); Expects(!password.empty()); - const auto hash = openssl::Sha512(bytes::concatenate( + const auto bytesForEncryptionKey = bytes::concatenate( salt, password, - salt)); - return DecryptSecretBytes(encryptedSecret, hash); + salt); + return DecryptSecretBytes(encryptedSecret, bytesForEncryptionKey); +} + +bytes::vector EncryptSecureSecret( + bytes::const_span salt, + bytes::const_span secret, + bytes::const_span password) { + Expects(!salt.empty()); + Expects(secret.size() == kSecretSize); + Expects(!password.empty()); + + const auto bytesForEncryptionKey = bytes::concatenate( + salt, + password, + salt); + return EncryptSecretBytes(secret, bytesForEncryptionKey); } bytes::vector SerializeData(const std::map &data) { @@ -252,11 +259,11 @@ EncryptedData EncryptData( gsl::make_span(unencrypted).subspan(padding), bytes); const auto dataHash = openssl::Sha256(unencrypted); - const auto dataSecretHash = openssl::Sha512(bytes::concatenate( + const auto bytesForEncryptionKey = bytes::concatenate( dataSecret, - dataHash)); + dataHash); - auto params = PrepareAesParams(dataSecretHash); + auto params = PrepareAesParams(bytesForEncryptionKey); return { { dataSecret.begin(), dataSecret.end() }, { dataHash.begin(), dataHash.end() }, @@ -279,10 +286,10 @@ bytes::vector DecryptData( return {}; } - const auto dataSecretHash = openssl::Sha512(bytes::concatenate( + const auto bytesForEncryptionKey = bytes::concatenate( dataSecret, - dataHash)); - auto params = PrepareAesParams(dataSecretHash); + dataHash); + auto params = PrepareAesParams(bytesForEncryptionKey); const auto decrypted = Decrypt(encrypted, std::move(params)); if (bytes::compare(openssl::Sha256(decrypted), dataHash) != 0) { LOG(("API Error: Bad data hash.")); @@ -317,19 +324,20 @@ bytes::vector EncryptValueSecret( bytes::const_span valueSecret, bytes::const_span secret, bytes::const_span valueHash) { - const auto valueSecretHash = openssl::Sha512( - bytes::concatenate(secret, valueHash)); - return EncryptSecretBytes(valueSecret, valueSecretHash); + const auto bytesForEncryptionKey = bytes::concatenate( + secret, + valueHash); + return EncryptSecretBytes(valueSecret, bytesForEncryptionKey); } bytes::vector DecryptValueSecret( bytes::const_span encrypted, bytes::const_span secret, bytes::const_span valueHash) { - const auto valueSecretHash = openssl::Sha512(bytes::concatenate( + const auto bytesForEncryptionKey = bytes::concatenate( secret, - valueHash)); - return DecryptSecretBytes(encrypted, valueSecretHash); + valueHash); + return DecryptSecretBytes(encrypted, bytesForEncryptionKey); } uint64 CountSecureSecretHash(bytes::const_span secret) { diff --git a/Telegram/SourceFiles/passport/passport_encryption.h b/Telegram/SourceFiles/passport/passport_encryption.h index 173049720..6f2ff2e0c 100644 --- a/Telegram/SourceFiles/passport/passport_encryption.h +++ b/Telegram/SourceFiles/passport/passport_encryption.h @@ -11,20 +11,15 @@ namespace Passport { bytes::vector GenerateSecretBytes(); -bytes::vector EncryptSecretBytes( +bytes::vector EncryptSecureSecret( + bytes::const_span salt, bytes::const_span secret, - bytes::const_span passwordHashForSecret); -bytes::vector DecryptSecretBytes( - bytes::const_span encryptedSecret, - bytes::const_span passwordHashForSecret); - + bytes::const_span password); 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); std::map DeserializeData(bytes::const_span bytes); diff --git a/Telegram/SourceFiles/passport/passport_form_box.cpp b/Telegram/SourceFiles/passport/passport_form_box.cpp index d3a1f091e..77226bc75 100644 --- a/Telegram/SourceFiles/passport/passport_form_box.cpp +++ b/Telegram/SourceFiles/passport/passport_form_box.cpp @@ -172,7 +172,7 @@ void FormBox::Inner::refresh() { if (_rows.size() <= index) { _rows.push_back(object_ptr(this, title, description)); _rows[index]->addClickHandler([=] { - _controller->editField(index); + _controller->editValue(index); }); } _rows[index++]->setReady(ready); diff --git a/Telegram/SourceFiles/passport/passport_form_controller.cpp b/Telegram/SourceFiles/passport/passport_form_controller.cpp index 9df2df9f8..2f7b96525 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_form_controller.cpp @@ -42,7 +42,7 @@ FormRequest::FormRequest( , publicKey(publicKey) { } -FormController::UploadedScan::~UploadedScan() { +FormController::UploadScanData::~UploadScanData() { if (fullId) { Auth().uploader().cancel(fullId); } @@ -50,24 +50,12 @@ FormController::UploadedScan::~UploadedScan() { FormController::EditFile::EditFile( const File &fields, - std::unique_ptr &&uploaded) + std::unique_ptr &&uploadData) : fields(std::move(fields)) -, uploaded(std::move(uploaded)) { +, uploadData(std::move(uploadData)) { } -FormController::Field::Field(Type type) : type(type) { -} - -template -bytes::vector FormController::computeFilesHash( - FileHashes fileHashes, - bytes::const_span valueSecret) { - auto vec = bytes::concatenate(fileHashes); - auto hashesVector = std::vector(); - for (const auto &hash : fileHashes) { - hashesVector.push_back(bytes::make_span(hash)); - } - return PrepareFilesHash(hashesVector, valueSecret); +FormController::Value::Value(Type type) : type(type) { } FormController::FormController( @@ -131,15 +119,17 @@ void FormController::validateSecureSecret( if (!salt.empty() && !encryptedSecret.empty()) { _secret = DecryptSecureSecret(salt, encryptedSecret, password); if (_secret.empty()) { + _secretId = 0; LOG(("API Error: Failed to decrypt secure secret. " "Forgetting all files and data :(")); - for (auto &field : _form.fields) { - if (!field.encryptedSecret.empty()) { - resetField(field); + for (auto &value : _form.rows) { + if (!value.data.original.isEmpty()) { + resetValue(value); } } } else { - decryptFields(); + _secretId = CountSecureSecretHash(_secret); + decryptValues(); } } if (_secret.empty()) { @@ -148,61 +138,71 @@ void FormController::validateSecureSecret( _secretReady.fire({}); } -void FormController::decryptFields() { +void FormController::decryptValues() { Expects(!_secret.empty()); - for (auto &field : _form.fields) { - if (field.encryptedSecret.empty()) { + for (auto &value : _form.rows) { + if (value.data.original.isEmpty()) { continue; } - decryptField(field); + decryptValue(value); } } -void FormController::decryptField(Field &field) { +void FormController::decryptValue(Value &value) { Expects(!_secret.empty()); - Expects(!field.encryptedSecret.empty()); + Expects(!value.data.original.isEmpty()); - if (!validateFieldSecret(field)) { - resetField(field); + if (!validateValueSecrets(value)) { + resetValue(value); return; } - const auto dataHash = bytes::make_span(field.dataHash); - field.parsedData = DeserializeData(DecryptData( - bytes::make_span(field.originalData), - field.dataHash, - field.secret)); + value.data.parsed = DeserializeData(DecryptData( + bytes::make_span(value.data.original), + value.data.hash, + value.data.secret)); } -bool FormController::validateFieldSecret(Field &field) { - field.secret = DecryptValueSecret( - field.encryptedSecret, +bool FormController::validateValueSecrets(Value &value) { + value.data.secret = DecryptValueSecret( + value.data.encryptedSecret, _secret, - field.hash); - if (field.secret.empty()) { - LOG(("API Error: Could not decrypt value secret. " + value.data.hash); + if (value.data.secret.empty()) { + LOG(("API Error: Could not decrypt data secret. " "Forgetting files and data :(")); return false; } - const auto fileHashes = ranges::view::all( - field.files + for (auto &file : value.files) { + file.secret = DecryptValueSecret( + file.encryptedSecret, + _secret, + file.hash); + if (file.secret.empty()) { + LOG(("API Error: Could not decrypt file secret. " + "Forgetting files and data :(")); + return false; + } + } + const auto fileHashesSecrets = ranges::view::all( + value.files ) | ranges::view::transform([](File &file) { - return bytes::make_span(file.fileHash); + return bytes::concatenate(file.hash, file.encryptedSecret); }); 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. " + value.data.hash, + value.data.encryptedSecret, + bytes::concatenate(fileHashesSecrets))); + if (value.consistencyHash != countedHash) { + LOG(("API Error: Wrong hash after decrypting value secrets. " "Forgetting files and data :(")); return false; } return true; } -void FormController::resetField(Field &field) { - field = Field(field.type); +void FormController::resetValue(Value &value) { + value = Value(value.type); } rpl::producer FormController::passwordError() const { @@ -213,52 +213,52 @@ QString FormController::passwordHint() const { return _password.hint; } -void FormController::uploadScan(int fieldIndex, QByteArray &&content) { +void FormController::uploadScan(int valueIndex, QByteArray &&content) { Expects(_editBox != nullptr); - Expects(fieldIndex >= 0 && fieldIndex < _form.fields.size()); + Expects(valueIndex >= 0 && valueIndex < _form.rows.size()); - auto &field = _form.fields[fieldIndex]; - if (field.secret.empty()) { - field.secret = GenerateSecretBytes(); - } - auto fileIndex = int(field.filesInEdit.size()); - field.filesInEdit.emplace_back( + auto &value = _form.rows[valueIndex]; + auto fileIndex = int(value.filesInEdit.size()); + value.filesInEdit.emplace_back( File(), nullptr); const auto fileId = rand_value(); - auto &file = field.filesInEdit.back(); + auto &file = value.filesInEdit.back(); file.fields.size = content.size(); file.fields.id = fileId; file.fields.dcId = MTP::maindc(); + file.fields.secret = GenerateSecretBytes(); + file.fields.date = unixtime(); file.fields.image = ReadImage(bytes::make_span(content)); file.fields.downloadOffset = file.fields.size; _scanUpdated.fire(collectScanInfo(file)); - encryptScan(fieldIndex, fileIndex, std::move(content)); + encryptScan(valueIndex, fileIndex, std::move(content)); } void FormController::encryptScan( - int fieldIndex, + int valueIndex, int fileIndex, QByteArray &&content) { Expects(_editBox != nullptr); - Expects(fieldIndex >= 0 && fieldIndex < _form.fields.size()); + Expects(valueIndex >= 0 && valueIndex < _form.rows.size()); Expects(fileIndex >= 0 - && fileIndex < _form.fields[fieldIndex].filesInEdit.size()); + && fileIndex < _form.rows[valueIndex].filesInEdit.size()); - auto &field = _form.fields[fieldIndex]; + const auto &value = _form.rows[valueIndex]; + const auto &file = value.filesInEdit[fileIndex].fields; const auto weak = _editBox; crl::async([ =, - fileId = field.filesInEdit[fileIndex].fields.id, + fileId = file.id, bytes = std::move(content), - valueSecret = field.secret + fileSecret = file.secret ] { auto data = EncryptData( bytes::make_span(bytes), - valueSecret); - auto result = UploadedScan(); + fileSecret); + auto result = UploadScanData(); result.fileId = fileId; result.hash = std::move(data.hash); result.bytes = std::move(data.bytes); @@ -270,7 +270,7 @@ void FormController::encryptScan( crl::on_main([=, encrypted = std::move(result)]() mutable { if (weak) { uploadEncryptedScan( - fieldIndex, + valueIndex, fileIndex, std::move(encrypted)); } @@ -279,13 +279,13 @@ void FormController::encryptScan( } void FormController::deleteScan( - int fieldIndex, + int valueIndex, int fileIndex) { - Expects(fieldIndex >= 0 && fieldIndex < _form.fields.size()); + Expects(valueIndex >= 0 && valueIndex < _form.rows.size()); Expects(fileIndex >= 0 - && fileIndex < _form.fields[fieldIndex].filesInEdit.size()); + && fileIndex < _form.rows[valueIndex].filesInEdit.size()); - auto &file = _form.fields[fieldIndex].filesInEdit[fileIndex]; + auto &file = _form.rows[valueIndex].filesInEdit[fileIndex]; file.deleted = !file.deleted; _scanUpdated.fire(collectScanInfo(file)); } @@ -296,14 +296,17 @@ void FormController::subscribeToUploader() { } using namespace Storage; + Auth().uploader().secureReady( ) | rpl::start_with_next([=](const UploadSecureDone &data) { scanUploadDone(data); }, _uploaderSubscriptions); + Auth().uploader().secureProgress( ) | rpl::start_with_next([=](const UploadSecureProgress &data) { scanUploadProgress(data); }, _uploaderSubscriptions); + Auth().uploader().secureFailed( ) | rpl::start_with_next([=](const FullMsgId &fullId) { scanUploadFail(fullId); @@ -311,44 +314,48 @@ void FormController::subscribeToUploader() { } void FormController::uploadEncryptedScan( - int fieldIndex, + int valueIndex, int fileIndex, - UploadedScan &&data) { + UploadScanData &&data) { Expects(_editBox != nullptr); - Expects(fieldIndex >= 0 && fieldIndex < _form.fields.size()); + Expects(valueIndex >= 0 && valueIndex < _form.rows.size()); Expects(fileIndex >= 0 - && fileIndex < _form.fields[fieldIndex].filesInEdit.size()); + && fileIndex < _form.rows[valueIndex].filesInEdit.size()); subscribeToUploader(); - auto &file = _form.fields[fieldIndex].filesInEdit[fileIndex]; - file.uploaded = std::make_unique(std::move(data)); + auto &file = _form.rows[valueIndex].filesInEdit[fileIndex]; + file.uploadData = std::make_unique(std::move(data)); auto prepared = std::make_shared( TaskId(), - file.uploaded->fileId, + file.uploadData->fileId, FileLoadTo(PeerId(0), false, MsgId(0)), TextWithTags(), std::shared_ptr(nullptr)); prepared->type = SendMediaType::Secure; prepared->content = QByteArray::fromRawData( - reinterpret_cast(file.uploaded->bytes.data()), - file.uploaded->bytes.size()); + reinterpret_cast(file.uploadData->bytes.data()), + file.uploadData->bytes.size()); prepared->setFileData(prepared->content); - prepared->filemd5 = file.uploaded->md5checksum; + prepared->filemd5 = file.uploadData->md5checksum; - file.uploaded->fullId = FullMsgId(0, clientMsgId()); - Auth().uploader().upload(file.uploaded->fullId, std::move(prepared)); + file.uploadData->fullId = FullMsgId(0, clientMsgId()); + Auth().uploader().upload(file.uploadData->fullId, std::move(prepared)); } void FormController::scanUploadDone(const Storage::UploadSecureDone &data) { if (const auto file = findEditFile(data.fullId)) { - Assert(file->uploaded != nullptr); - Assert(file->uploaded->fileId == data.fileId); + Assert(file->uploadData != nullptr); + Assert(file->uploadData->fileId == data.fileId); - file->uploaded->partsCount = data.partsCount; - file->fields.fileHash = std::move(file->uploaded->hash); - file->uploaded->fullId = FullMsgId(); + file->uploadData->partsCount = data.partsCount; + file->fields.hash = std::move(file->uploadData->hash); + file->fields.encryptedSecret = EncryptValueSecret( + file->fields.secret, + _secret, + file->fields.hash); + file->uploadData->fullId = FullMsgId(); _scanUpdated.fire(collectScanInfo(*file)); } @@ -357,9 +364,9 @@ void FormController::scanUploadDone(const Storage::UploadSecureDone &data) { void FormController::scanUploadProgress( const Storage::UploadSecureProgress &data) { if (const auto file = findEditFile(data.fullId)) { - Assert(file->uploaded != nullptr); + Assert(file->uploadData != nullptr); - file->uploaded->offset = data.offset; + file->uploadData->offset = data.offset; _scanUpdated.fire(collectScanInfo(*file)); } @@ -367,9 +374,9 @@ void FormController::scanUploadProgress( void FormController::scanUploadFail(const FullMsgId &fullId) { if (const auto file = findEditFile(fullId)) { - Assert(file->uploaded != nullptr); + Assert(file->uploadData != nullptr); - file->uploaded->offset = -1; + file->uploadData->offset = -1; _scanUpdated.fire(collectScanInfo(*file)); } @@ -399,27 +406,27 @@ void FormController::fillRows( QString title, QString description, bool ready)> callback) { - for (const auto &field : _form.fields) { - switch (field.type) { - case Field::Type::Identity: + for (const auto &value : _form.rows) { + switch (value.type) { + case Value::Type::Identity: callback( lang(lng_passport_identity_title), lang(lng_passport_identity_description), false); break; - case Field::Type::Address: + case Value::Type::Address: callback( lang(lng_passport_address_title), lang(lng_passport_address_description), false); break; - case Field::Type::Phone: + case Value::Type::Phone: callback( lang(lng_passport_phone_title), App::self()->phone(), true); break; - case Field::Type::Email: + case Value::Type::Email: callback( lang(lng_passport_email_title), lang(lng_passport_email_description), @@ -429,24 +436,25 @@ void FormController::fillRows( } } -void FormController::editField(int index) { - Expects(index >= 0 && index < _form.fields.size()); +void FormController::editValue(int index) { + Expects(index >= 0 && index < _form.rows.size()); + + auto &value = _form.rows[index]; + loadFiles(value.files); + value.filesInEdit = ranges::view::all( + value.files + ) | ranges::view::transform([](const File &file) { + return EditFile(file, nullptr); + }) | ranges::to_vector; auto box = [&]() -> object_ptr { - auto &field = _form.fields[index]; - switch (field.type) { - case Field::Type::Identity: - loadFiles(field.files); - field.filesInEdit = ranges::view::all( - field.files - ) | ranges::view::transform([](const File &file) { - return EditFile(file, nullptr); - }) | ranges::to_vector; + switch (value.type) { + case Value::Type::Identity: return Box( this, index, - fieldDataIdentity(field), - fieldFilesIdentity(field)); + valueDataIdentity(value), + valueFilesIdentity(value)); } return { nullptr }; }(); @@ -496,11 +504,15 @@ void FormController::loadFiles(std::vector &files) { } void FormController::fileLoadDone(FileKey key, const QByteArray &bytes) { - if (const auto [field, file] = findFile(key); file != nullptr) { + if (const auto [value, file] = findFile(key); file != nullptr) { const auto decrypted = DecryptData( bytes::make_span(bytes), - file->fileHash, - field->secret); + file->hash, + file->secret); + if (decrypted.empty()) { + fileLoadFail(key); + return; + } file->downloadOffset = file->size; file->image = App::readImage(QByteArray::fromRawData( reinterpret_cast(decrypted.data()), @@ -514,7 +526,7 @@ void FormController::fileLoadDone(FileKey key, const QByteArray &bytes) { } void FormController::fileLoadProgress(FileKey key, int offset) { - if (const auto [field, file] = findFile(key); file != nullptr) { + if (const auto [value, file] = findFile(key); file != nullptr) { file->downloadOffset = offset; if (const auto fileInEdit = findEditFile(key)) { fileInEdit->fields.downloadOffset = file->downloadOffset; @@ -524,7 +536,7 @@ void FormController::fileLoadProgress(FileKey key, int offset) { } void FormController::fileLoadFail(FileKey key) { - if (const auto [field, file] = findFile(key); file != nullptr) { + if (const auto [value, file] = findFile(key); file != nullptr) { file->downloadOffset = -1; if (const auto fileInEdit = findEditFile(key)) { fileInEdit->fields.downloadOffset = file->downloadOffset; @@ -545,15 +557,16 @@ ScanInfo FormController::collectScanInfo(const EditFile &file) const { ).arg(file.fields.downloadOffset ).arg(file.fields.size); } else { - return QString("download ready"); + return QString("uploaded ") + + langDateTimeFull(ParseDateTime(file.fields.date)); } - } else if (file.uploaded) { - if (file.uploaded->offset < 0) { + } else if (file.uploadData) { + if (file.uploadData->offset < 0) { return QString("upload failed"); - } else if (file.uploaded->fullId) { + } else if (file.uploadData->fullId) { return QString("uploading %1 / %2" - ).arg(file.uploaded->offset - ).arg(file.uploaded->bytes.size()); + ).arg(file.uploadData->offset + ).arg(file.uploadData->bytes.size()); } else { return QString("upload ready"); } @@ -567,8 +580,8 @@ ScanInfo FormController::collectScanInfo(const EditFile &file) const { file.fields.image }; } -IdentityData FormController::fieldDataIdentity(const Field &field) const { - const auto &map = field.parsedData; +IdentityData FormController::valueDataIdentity(const Value &value) const { + const auto &map = value.data.parsed; auto result = IdentityData(); if (const auto i = map.find(qsl("first_name")); i != map.cend()) { result.name = i->second; @@ -579,31 +592,31 @@ IdentityData FormController::fieldDataIdentity(const Field &field) const { return result; } -std::vector FormController::fieldFilesIdentity( - const Field &field) const { +std::vector FormController::valueFilesIdentity( + const Value &value) const { auto result = std::vector(); - for (const auto &file : field.filesInEdit) { + for (const auto &file : value.filesInEdit) { result.push_back(collectScanInfo(file)); } return result; } -void FormController::saveFieldIdentity( +void FormController::saveValueIdentity( int index, const IdentityData &data) { Expects(_editBox != nullptr); - Expects(index >= 0 && index < _form.fields.size()); - Expects(_form.fields[index].type == Field::Type::Identity); + Expects(index >= 0 && index < _form.rows.size()); + Expects(_form.rows[index].type == Value::Type::Identity); - _form.fields[index].parsedData[qsl("first_name")] = data.name; - _form.fields[index].parsedData[qsl("last_name")] = data.surname; + _form.rows[index].data.parsed[qsl("first_name")] = data.name; + _form.rows[index].data.parsed[qsl("last_name")] = data.surname; saveIdentity(index); } void FormController::saveIdentity(int index) { - Expects(index >= 0 && index < _form.fields.size()); - Expects(_form.fields[index].type == Field::Type::Identity); + Expects(index >= 0 && index < _form.rows.size()); + Expects(_form.rows[index].type == Value::Type::Identity); if (_secret.empty()) { _secretCallbacks.push_back([=] { @@ -614,63 +627,69 @@ void FormController::saveIdentity(int index) { _editBox->closeBox(); - auto &field = _form.fields[index]; + auto &value = _form.rows[index]; auto inputFiles = QVector(); - inputFiles.reserve(field.filesInEdit.size()); - for (const auto &file : field.filesInEdit) { + inputFiles.reserve(value.filesInEdit.size()); + for (const auto &file : value.filesInEdit) { if (file.deleted) { continue; - } else if (const auto uploaded = file.uploaded.get()) { + } else if (const auto uploadData = file.uploadData.get()) { inputFiles.push_back(MTP_inputSecureFileUploaded( MTP_long(file.fields.id), - MTP_int(uploaded->partsCount), - MTP_bytes(uploaded->md5checksum), - MTP_bytes(file.fields.fileHash))); + MTP_int(uploadData->partsCount), + MTP_bytes(uploadData->md5checksum), + MTP_bytes(file.fields.hash), + MTP_bytes(file.fields.encryptedSecret))); } else { inputFiles.push_back(MTP_inputSecureFile( MTP_long(file.fields.id), MTP_long(file.fields.accessHash))); } } - if (field.secret.empty()) { - field.secret = GenerateSecretBytes(); + + if (value.data.secret.empty()) { + value.data.secret = GenerateSecretBytes(); } const auto encryptedData = EncryptData( - SerializeData(field.parsedData), - field.secret); - const auto fileHashes = ranges::view::all( - field.filesInEdit + SerializeData(value.data.parsed), + value.data.secret); + value.data.hash = encryptedData.hash; + value.data.encryptedSecret = EncryptValueSecret( + value.data.secret, + _secret, + value.data.hash); + + const auto fileHashesSecrets = ranges::view::all( + value.filesInEdit ) | ranges::view::filter([](const EditFile &file) { return !file.deleted; }) | ranges::view::transform([](const EditFile &file) { - return bytes::make_span(file.fields.fileHash); + return bytes::concatenate( + file.fields.hash, + file.fields.encryptedSecret); }); - const auto valueHash = openssl::Sha256(bytes::concatenate( - encryptedData.hash, - bytes::concatenate(fileHashes), - field.secret)); + value.consistencyHash = openssl::Sha256(bytes::concatenate( + value.data.hash, + value.data.encryptedSecret, + bytes::concatenate(fileHashesSecrets))); - field.encryptedSecret = EncryptValueSecret( - field.secret, - _secret, - valueHash); request(MTPaccount_SaveSecureValue( MTP_inputSecureValueIdentity( MTP_secureData( MTP_bytes(encryptedData.bytes), - MTP_bytes(encryptedData.hash)), + MTP_bytes(value.data.hash), + MTP_bytes(value.data.encryptedSecret)), MTP_vector(inputFiles), - MTP_bytes(field.encryptedSecret), - MTP_bytes(valueHash)), + MTP_bytes(value.consistencyHash)), MTP_long(CountSecureSecretHash(_secret)) )).done([=](const MTPSecureValueSaved &result) { Expects(result.type() == mtpc_secureValueSaved); const auto &data = result.c_secureValueSaved(); - _form.fields[index].files = parseFiles( + _form.rows[index].files = parseFiles( data.vfiles.v, - base::take(_form.fields[index].filesInEdit)); + base::take(_form.rows[index].filesInEdit)); Ui::show(Box("Saved"), LayerOption::KeepOther); }).fail([=](const RPCError &error) { @@ -690,19 +709,17 @@ void FormController::generateSecret(bytes::const_span password) { _password.newSecureSalt, randomSaltPart); - const auto hashForSecret = openssl::Sha512(bytes::concatenate( + auto secureSecretId = CountSecureSecretHash(secret); + auto encryptedSecret = EncryptSecureSecret( newSecureSaltFull, - password, - newSecureSaltFull)); + secret, + password); + const auto hashForAuth = openssl::Sha256(bytes::concatenate( _password.salt, password, _password.salt)); - auto secureSecretHash = CountSecureSecretHash(secret); - auto encryptedSecret = EncryptSecretBytes( - secret, - hashForSecret); using Flag = MTPDaccount_passwordInputSettings::Flag; _saveSecretRequestId = request(MTPaccount_UpdatePasswordSettings( MTP_bytes(hashForAuth), @@ -714,10 +731,11 @@ void FormController::generateSecret(bytes::const_span password) { MTPstring(), // email MTP_bytes(newSecureSaltFull), MTP_bytes(encryptedSecret), - MTP_long(secureSecretHash)) + MTP_long(secureSecretId)) )).done([=](const MTPBool &result) { _saveSecretRequestId = 0; _secret = secret; + _secretId = secureSecretId; //_password.salt = newPasswordSaltFull; for (const auto &callback : base::take(_secretCallbacks)) { callback(); @@ -745,20 +763,20 @@ void FormController::requestForm() { } template -auto FormController::parseEncryptedField( - Field::Type type, - const DataType &data) const -> Field { +auto FormController::parseEncryptedValue( + Value::Type type, + const DataType &data) const -> Value { Expects(data.vdata.type() == mtpc_secureData); - auto result = Field(type); + auto result = Value(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); + result.consistencyHash = 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); + result.data.original = fields.vdata.v; + result.data.hash = bytes::make_vector(fields.vdata_hash.v); + result.data.encryptedSecret = bytes::make_vector(fields.vsecret.v); result.files = parseFiles(data.vfiles.v); return result; } @@ -782,28 +800,11 @@ auto FormController::parseFiles( normal.id = fields.vid.v; normal.accessHash = fields.vaccess_hash.v; normal.size = fields.vsize.v; + normal.date = fields.vdate.v; normal.dcId = fields.vdc_id.v; - normal.fileHash = bytes::make_vector(fields.vfile_hash.v); - const auto i = ranges::find( - editData, - normal.fileHash, - [](const EditFile &file) { return file.fields.fileHash; }); - if (i != editData.end()) { - normal.image = i->fields.image; - normal.downloadOffset = i->fields.downloadOffset; - if (i->uploaded) { - Local::writeImage( - StorageKey( - storageMix32To64( - SecureFileLocation, - normal.dcId), - normal.id), - StorageImageSaved(QByteArray::fromRawData( - reinterpret_cast( - i->uploaded->bytes.data()), - i->uploaded->bytes.size()))); - } - } + normal.hash = bytes::make_vector(fields.vfile_hash.v); + normal.encryptedSecret = bytes::make_vector(fields.vsecret.v); + fillDownloadedFile(normal, editData); result.push_back(std::move(normal)); } break; } @@ -813,12 +814,39 @@ auto FormController::parseFiles( return result; } +void FormController::fillDownloadedFile( + File &destination, + const std::vector &source) const { + const auto i = ranges::find( + source, + destination.hash, + [](const EditFile &file) { return file.fields.hash; }); + if (i == source.end()) { + return; + } + destination.image = i->fields.image; + destination.downloadOffset = i->fields.downloadOffset; + if (!i->uploadData) { + return; + } + Local::writeImage( + StorageKey( + storageMix32To64( + SecureFileLocation, + destination.dcId), + destination.id), + StorageImageSaved(QByteArray::fromRawData( + reinterpret_cast( + i->uploadData->bytes.data()), + i->uploadData->bytes.size()))); +} + template -auto FormController::parsePlainTextField( - Field::Type type, +auto FormController::parsePlainTextValue( + Value::Type type, const QByteArray &value, - const DataType &data) const -> Field { - auto result = Field(type); + const DataType &data) const -> Value { + auto result = Value(type); const auto check = bytes::compare( bytes::make_span(data.vhash.v), openssl::Sha256(bytes::make_span(value))); @@ -830,38 +858,38 @@ auto FormController::parsePlainTextField( )); return result; } - result.parsedData[QString("value")] = QString::fromUtf8(value); + result.data.parsed[QString("value")] = QString::fromUtf8(value); if (data.has_verified()) { result.verification = parseVerified(data.vverified); } - result.hash = bytes::make_vector(data.vhash.v); + result.consistencyHash = bytes::make_vector(data.vhash.v); return result; } auto FormController::parseValue( - const MTPSecureValue &value) const -> Field { + const MTPSecureValue &value) const -> Value { switch (value.type()) { case mtpc_secureValueIdentity: { - return parseEncryptedField( - Field::Type::Identity, + return parseEncryptedValue( + Value::Type::Identity, value.c_secureValueIdentity()); } break; case mtpc_secureValueAddress: { - return parseEncryptedField( - Field::Type::Address, + return parseEncryptedValue( + Value::Type::Address, value.c_secureValueAddress()); } break; case mtpc_secureValuePhone: { const auto &data = value.c_secureValuePhone(); - return parsePlainTextField( - Field::Type::Phone, + return parsePlainTextValue( + Value::Type::Phone, data.vphone.v, data); } break; case mtpc_secureValueEmail: { const auto &data = value.c_secureValueEmail(); - return parsePlainTextField( - Field::Type::Phone, + return parsePlainTextValue( + Value::Type::Phone, data.vemail.v, data); } break; @@ -874,13 +902,13 @@ auto FormController::parseVerified(const MTPSecureValueVerified &data) const Expects(data.type() == mtpc_secureValueVerified); const auto &fields = data.c_secureValueVerified(); - return Verification{ fields.vdate.v, qs(fields.vprovider) }; + return Verification{ fields.vdate.v }; } auto FormController::findEditFile(const FullMsgId &fullId) -> EditFile* { - for (auto &field : _form.fields) { - for (auto &file : field.filesInEdit) { - if (file.uploaded && file.uploaded->fullId == fullId) { + for (auto &value : _form.rows) { + for (auto &file : value.filesInEdit) { + if (file.uploadData && file.uploadData->fullId == fullId) { return &file; } } @@ -889,8 +917,8 @@ auto FormController::findEditFile(const FullMsgId &fullId) -> EditFile* { } auto FormController::findEditFile(const FileKey &key) -> EditFile* { - for (auto &field : _form.fields) { - for (auto &file : field.filesInEdit) { + for (auto &value : _form.rows) { + for (auto &file : value.filesInEdit) { if (file.fields.dcId == key.dcId && file.fields.id == key.id) { return &file; } @@ -900,11 +928,11 @@ auto FormController::findEditFile(const FileKey &key) -> EditFile* { } auto FormController::findFile(const FileKey &key) --> std::pair { - for (auto &field : _form.fields) { - for (auto &file : field.files) { +-> std::pair { + for (auto &value : _form.rows) { + for (auto &file : value.files) { if (file.dcId == key.dcId && file.id == key.id) { - return { &field, &file }; + return { &value, &file }; } } } @@ -923,11 +951,11 @@ void FormController::parseForm(const MTPaccount_AuthorizationForm &result) { const auto &data = result.c_account_authorizationForm(); - auto values = std::vector(); + auto values = std::vector(); for (const auto &value : data.vvalues.v) { values.push_back(parseValue(value)); } - const auto findValue = [&](Field::Type type) -> Field* { + const auto findValue = [&](Value::Type type) -> Value* { for (auto &value : values) { if (value.type == type) { return &value; @@ -936,10 +964,9 @@ void FormController::parseForm(const MTPaccount_AuthorizationForm &result) { return nullptr; }; - _form.requestWrite = false; App::feedUsers(data.vusers); for (const auto &required : data.vrequired_types.v) { - using Type = Field::Type; + using Type = Value::Type; const auto type = [&] { switch (required.type()) { @@ -951,10 +978,10 @@ void FormController::parseForm(const MTPaccount_AuthorizationForm &result) { Unexpected("Type in secureValueType type."); }(); - if (auto field = findValue(type)) { - _form.fields.push_back(std::move(*field)); + if (auto value = findValue(type)) { + _form.rows.push_back(std::move(*value)); } else { - _form.fields.push_back(Field(type)); + _form.rows.push_back(Value(type)); } } _bot = App::userLoaded(_request.botId); diff --git a/Telegram/SourceFiles/passport/passport_form_controller.h b/Telegram/SourceFiles/passport/passport_form_controller.h index 8f234bb43..c1db98a40 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.h +++ b/Telegram/SourceFiles/passport/passport_form_controller.h @@ -83,8 +83,8 @@ public: rpl::producer passwordError() const; QString passwordHint() const; - void uploadScan(int fieldIndex, QByteArray &&content); - void deleteScan(int fieldIndex, int fileIndex); + void uploadScan(int valueIndex, QByteArray &&content); + void deleteScan(int valueIndex, int fileIndex); rpl::producer<> secretReadyEvents() const; @@ -97,15 +97,15 @@ public: QString title, QString description, bool ready)> callback); - void editField(int index); + void editValue(int index); - void saveFieldIdentity(int index, const IdentityData &data); + void saveValueIdentity(int index, const IdentityData &data); ~FormController(); private: - struct UploadedScan { - ~UploadedScan(); + struct UploadScanData { + ~UploadScanData(); FullMsgId fullId; uint64 fileId = 0; @@ -121,7 +121,10 @@ private: uint64 accessHash = 0; int32 size = 0; int32 dcId = 0; - bytes::vector fileHash; + TimeId date = 0; + bytes::vector hash; + bytes::vector secret; + bytes::vector encryptedSecret; int downloadOffset = 0; QImage image; @@ -129,17 +132,23 @@ private: struct EditFile { EditFile( const File &fields, - std::unique_ptr &&uploaded); + std::unique_ptr &&uploadData); File fields; - std::unique_ptr uploaded; + std::unique_ptr uploadData; bool deleted = false; }; struct Verification { TimeId date; - QString provider; }; - struct Field { + struct ValueData { + QByteArray original; + std::map parsed; + bytes::vector hash; + bytes::vector secret; + bytes::vector encryptedSecret; + }; + struct Value { enum class Type { Identity, Address, @@ -147,24 +156,19 @@ private: Email, }; - explicit Field(Type type); - Field(Field &&other) = default; - Field &operator=(Field &&other) = default; + explicit Value(Type type); + Value(Value &&other) = default; + Value &operator=(Value &&other) = default; Type type; - QByteArray originalData; - std::map parsedData; - bytes::vector dataHash; + ValueData data; std::vector files; std::vector filesInEdit; - bytes::vector secret; - bytes::vector encryptedSecret; - bytes::vector hash; + bytes::vector consistencyHash; base::optional verification; }; struct Form { - bool requestWrite = false; - std::vector fields; + std::vector rows; }; struct PasswordSettings { bytes::vector salt; @@ -178,7 +182,7 @@ private: EditFile *findEditFile(const FullMsgId &fullId); EditFile *findEditFile(const FileKey &key); - std::pair findFile(const FileKey &key); + std::pair findFile(const FileKey &key); void requestForm(); void requestPassword(); @@ -187,20 +191,23 @@ private: void formFail(const RPCError &error); void parseForm(const MTPaccount_AuthorizationForm &result); void showForm(); - Field parseValue(const MTPSecureValue &value) const; + Value parseValue(const MTPSecureValue &value) const; template - Field parseEncryptedField( - Field::Type type, + Value parseEncryptedValue( + Value::Type type, const DataType &data) const; template - Field parsePlainTextField( - Field::Type type, - const QByteArray &value, + Value parsePlainTextValue( + Value::Type type, + const QByteArray &text, const DataType &data) const; Verification parseVerified(const MTPSecureValueVerified &data) const; std::vector parseFiles( const QVector &data, const std::vector &editData = {}) const; + void fillDownloadedFile( + File &destination, + const std::vector &source) const; void passwordDone(const MTPaccount_Password &result); void passwordFail(const RPCError &error); @@ -211,13 +218,13 @@ private: 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); + void decryptValues(); + void decryptValue(Value &value); + bool validateValueSecrets(Value &value); + void resetValue(Value &value); - IdentityData fieldDataIdentity(const Field &field) const; - std::vector fieldFilesIdentity(const Field &field) const; + IdentityData valueDataIdentity(const Value &value) const; + std::vector valueFilesIdentity(const Value &value) const; void saveIdentity(int index); void loadFiles(std::vector &files); @@ -226,17 +233,15 @@ private: void fileLoadFail(FileKey key); void generateSecret(bytes::const_span password); - template - bytes::vector computeFilesHash( - FileHashes fileHashes, - bytes::const_span valueHash); - void subscribeToUploader(); void encryptScan( - int fieldIndex, + int valueIndex, int fileIndex, QByteArray &&content); - void uploadEncryptedScan(int fieldIndex, int fileIndex, UploadedScan &&data); + void uploadEncryptedScan( + int valueIndex, + int fileIndex, + UploadScanData &&data); void scanUploadDone(const Storage::UploadSecureDone &data); void scanUploadProgress(const Storage::UploadSecureProgress &data); void scanUploadFail(const FullMsgId &fullId); @@ -256,6 +261,7 @@ private: rpl::event_stream _scanUpdated; bytes::vector _secret; + uint64 _secretId = 0; std::vector> _secretCallbacks; mtpRequestId _saveSecretRequestId = 0; rpl::event_stream<> _secretReady;