From 3d05d44be891c1cfbbcec84df961ea7b16c5ce3d Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 10 Aug 2018 19:39:48 +0300 Subject: [PATCH] Update layer to 85. GitOrigin-RevId: 0a32bd0287e3d09dd484c8dbe230f29b92d19771 --- td/generate/scheme/td_api.tl | 43 +++++++--- td/generate/scheme/td_api.tlo | Bin 131180 -> 132100 bytes td/generate/scheme/telegram_api.tl | 12 ++- td/generate/scheme/telegram_api.tlo | Bin 163228 -> 164192 bytes td/telegram/SecureManager.cpp | 123 +++++++++++++++++++++++++--- td/telegram/SecureManager.h | 1 + td/telegram/SecureValue.cpp | 88 ++++++++++++++------ td/telegram/SecureValue.h | 13 ++- td/telegram/SecureValue.hpp | 20 +++++ td/telegram/cli.cpp | 29 +++++-- td/telegram/net/MtprotoHeader.cpp | 2 +- 11 files changed, 267 insertions(+), 64 deletions(-) diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index f36acbcef..21398acb3 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -873,17 +873,17 @@ passportElementTypeEmailAddress = PassportElementType; //@description Represents a date according to the Gregorian calendar @day Day of the month, 1-31 @month Month, 1-12 @year Year, 1-9999 date day:int32 month:int32 year:int32 = Date; -//@description Contains the user's personal details @first_name First name of the user; 1-255 characters @last_name Last name of the user; 1-255 characters @birthdate Birthdate of the user +//@description Contains the user's personal details @first_name First name of the user; 1-255 characters @middle_name Middle name of the user; 0-255 characters @last_name Last name of the user; 1-255 characters @birthdate Birthdate of the user //@gender Gender of the user, "male" or "female" @country_code A two-letter ISO 3166-1 alpha-2 country code for the user's country @residence_country_code A two-letter ISO 3166-1 alpha-2 country code for the user's residence country -personalDetails first_name:string last_name:string birthdate:date gender:string country_code:string residence_country_code:string = PersonalDetails; +personalDetails first_name:string middle_name:string last_name:string birthdate:date gender:string country_code:string residence_country_code:string = PersonalDetails; //@description An identity document @number Document number; 1-24 characters @expiry_date Document expiry date; may be null @front_side Front side of the document -//@reverse_side Reverse side of the document; only for driver license and identity card @selfie Selfie with the document; may be null -identityDocument number:string expiry_date:date front_side:datedFile reverse_side:datedFile selfie:datedFile = IdentityDocument; +//@reverse_side Reverse side of the document; only for driver license and identity card @selfie Selfie with the document; may be null @translations List of files with the document translation +identityDocument number:string expiry_date:date front_side:datedFile reverse_side:datedFile selfie:datedFile translations:vector = IdentityDocument; //@description An identity document to be saved @number Document number; 1-24 characters @expiry_date Document expiry date, if available @front_side Front side of the document -//@reverse_side Reverse side of the document; only for driver license and identity card @selfie Selfie with the document, if available -inputIdentityDocument number:string expiry_date:date front_side:InputFile reverse_side:InputFile selfie:InputFile = InputIdentityDocument; +//@reverse_side Reverse side of the document; only for driver license and identity card @selfie Selfie with the document, if available @translations List of files with the document translation +inputIdentityDocument number:string expiry_date:date front_side:InputFile reverse_side:InputFile selfie:InputFile translations:vector = InputIdentityDocument; //@class PassportElement @description Contains information about a Telegram Passport element @@ -976,6 +976,9 @@ passportElements elements:vector = PassportElements; //@class PassportElementErrorSource @description Contains the description of an error in a Telegram Passport element +//@description The element contains an error in an unspecified place. The error will be considered resolved when anything changes +passportElementErrorSourceUnspecified = PassportElementErrorSource; + //@description One of the data fields contains an error. The error will be considered resolved when the value of the field changes @field_name Field name passportElementErrorSourceDataField field_name:string = PassportElementErrorSource; @@ -988,6 +991,12 @@ passportElementErrorSourceReverseSide = PassportElementErrorSource; //@description The selfie with the document contains an error. The error will be considered resolved when the file with the selfie changes passportElementErrorSourceSelfie = PassportElementErrorSource; +//@description The translation of the document contains an error. The error will be considered resolved when the file with the translation changes +passportElementErrorSourceTranslation = PassportElementErrorSource; + +//@description The list of translations of the document contains an error. The error will be considered resolved when the list of translations changes +passportElementErrorSourceTranslations = PassportElementErrorSource; + //@description The file contains an error. The error will be considered resolved when the file changes passportElementErrorSourceFile = PassportElementErrorSource; @@ -1001,20 +1010,24 @@ passportElementError type:PassportElementType message:string source:PassportElem //@description Contains information about a Telegram Passport authorization form that was requested @id Unique identifier of the authorization form //@required_types Telegram Passport element types that need to be provided to complete the form. If the user has more than one identity document or proof of address document, any one of each can be chosen -//@elements Already available Telegram Passport elements @errors Errors in the elements that is already available @is_selfie_required True, if a selfie is required with the identity document @privacy_policy_url URL for the privacy policy of the service; can be empty -passportAuthorizationForm id:int32 required_types:vector elements:vector errors:vector is_selfie_required:Bool privacy_policy_url:string = PassportAuthorizationForm; +//@elements Already available Telegram Passport elements @errors Errors in the elements that is already available @is_selfie_required True, if a selfie is required with the identity document +//@is_translation_required True, if a translation is required with the identity document @privacy_policy_url URL for the privacy policy of the service; can be empty +passportAuthorizationForm id:int32 required_types:vector elements:vector errors:vector is_selfie_required:Bool is_translation_required:Bool privacy_policy_url:string = PassportAuthorizationForm; //@description Contains encrypted Telegram Passport data credentials @data The encrypted credentials @hash The decrypted data hash @secret Secret for data decryption, encrypted with service's public key encryptedCredentials data:bytes hash:bytes secret:bytes = EncryptedCredentials; -//@description Contains information about encrypted Telegram Passport element; for bots only @type Type of Telegram Passport element @data Encrypted JSON-encoded data about the user @front_side The front side of an identity document @reverse_side The reverse side of an identity document; may be null @selfie Selfie with the document; may be null @files List of attached files @value Unencrypted data, phone number or email address -encryptedPassportElement type:PassportElementType data:bytes front_side:datedFile reverse_side:datedFile selfie:datedFile files:vector value:string = EncryptedPassportElement; +//@description Contains information about encrypted Telegram Passport element; for bots only @type Type of Telegram Passport element @data Encrypted JSON-encoded data about the user @front_side The front side of an identity document @reverse_side The reverse side of an identity document; may be null @selfie Selfie with the document; may be null @translations List of files with the document translation @files List of attached files @value Unencrypted data, phone number or email address @hash Hash of the whole element +encryptedPassportElement type:PassportElementType data:bytes front_side:datedFile reverse_side:datedFile selfie:datedFile translations:vector files:vector value:string hash:string = EncryptedPassportElement; //@class InputPassportElementErrorSource @description Contains the description of an error in Telegram Passport element; for bots only +//@description The element contains an error in an unspecified place. The error will be considered resolved when anything changes @element_hash Current hash of the whole element +inputPassportElementErrorSourceUnspecified element_hash:bytes = InputPassportElementErrorSource; + //@description A data field contains an error. The error is considered resolved when the field's value changes @field_name Field name @data_hash Current data hash inputPassportElementErrorSourceDataField field_name:string data_hash:bytes = InputPassportElementErrorSource; @@ -1027,10 +1040,16 @@ inputPassportElementErrorSourceReverseSide file_hash:bytes = InputPassportElemen //@description The selfie contains an error. The error is considered resolved when the file with the selfie changes @file_hash Current hash of the file containing the selfie inputPassportElementErrorSourceSelfie file_hash:bytes = InputPassportElementErrorSource; -//@description The file contains an error. The error is considered resolved when the file changes @file_hash Hash of the file with an error +//@description The translation contains an error. The error is considered resolved when the file with the translation changes @file_hash Current hash of the file containing the translation +inputPassportElementErrorSourceTranslation file_hash:bytes = InputPassportElementErrorSource; + +//@description The list of translation files contains an error. The error is considered resolved when the file list changes @file_hashes Current hashes of all files with the translation +inputPassportElementErrorSourceTranslations file_hashes:vector = InputPassportElementErrorSource; + +//@description The file contains an error. The error is considered resolved when the file changes @file_hash Current hash of the file with an error inputPassportElementErrorSourceFile file_hash:bytes = InputPassportElementErrorSource; -//@description The list of attached files contains an error. The error is considered resolved when the file list changes @file_hashes Hashes of all files +//@description The list of attached files contains an error. The error is considered resolved when the file list changes @file_hashes Current hashes of all attached files inputPassportElementErrorSourceFiles file_hashes:vector = InputPassportElementErrorSource; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index 14a98f68318079f3deafad3a855cea32532a15eb..6d2b20796799b30ade4de607aa97c971bc3e8d12 100644 GIT binary patch delta 1297 zcmaJ>Uq};i9KT=ObeBt)ZaOFCjZ`!R4My!jX`09$1XnCDkmhnHJJ>((Hn2Yr10{Sg zkiOQvSShJ2|0Q0~gF*yR1`$OI>P6qgKNGjNN*~(qPPdt@d%5rb@8|RVe($0o<-nkF zAxx2WwAe1rQe%kJ2Wy%|YfcD}Ux%+gdKOV3#iid>Y#EH7DhBUN7NQ`IibQp8t%4kl zFRg>QU_`N07hKW{p(xVekQ0O)qtciW*~5zuMXRHx+4DVaBa4Q`H^OT;#;*WRv zZ4@+B7QtuNs7e*!9A4h-2(Ugc&~-Env!}tswhJLGQb40C4~Co?8SM(Ypfq5#YSzhk z18m?{i?2KANr(hNU^}jy1x@{S_{rITW)(Tdi>tuAqm(&!@Ne@Z&OrC;C`|safCybr z$iJZ(;MY`v6u&rNp(UCyHc5kf!Xj6}%J&ItN@<%+Dt$?-*f3=yNjzZ~nWh!K(Ve_= zF#cRC3`Y@(hgp5i%lnybwufb$1dhX~P=aE~`dYP$aWj~)-R0n2;J8>OR|-!z$il{S zhb*+r99aCbM%l7_%wn81$*x3<7I#WCk)Cg7PIj(-ycs(ROl?c8N&*J|%9|`YyI*sJ zP8auC ztB)%sc4?`6##Er=^_lUy{^JxE;x?)j|5Nh(49EMt4tEO^aIkJ3+wdf_!LV18o#SDh qPB#Ou@0SX(yNCw5LIJ&dPvzf{X1{Au+Op6$kCr9rmFWdCUikxma`-g> delta 819 zcmZqa;CR!(v0()#OSSxjo152gE?{JdxfXF|a{zY(Bg^;Ba<#=*co3|^JR%N^EFw&ou5Atw zt>9sd*nCp)xEmYD7Kzl!>o}!2LCl-SgnZa1JF1CKzK}6z@`}uW$se>hAWl)q+Q6X* za*9A^N@`w7W=W+>esXCpkjcQn01^l3Wu1JpIexN8X2|3-?>Hb1|4@8{UlrtV(agMp z(h^S$%^*3L-p!K}R&s#7x%olhnEr7iqZA}gbaXT(FL-rj@{-pE5P=)7V<6shc&{)y z;*A41v?hZ%d*1jUI67}V5S$rr1K^y^JnuAESU}M{+2WJJ plain_data:flags.5?SecurePlainData hash:bytes = SecureValue; +secureValue flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?SecureFile reverse_side:flags.2?SecureFile selfie:flags.3?SecureFile translations:flags.6?Vector files:flags.4?Vector plain_data:flags.5?SecurePlainData hash:bytes = SecureValue; -inputSecureValue#67872e8 flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?InputSecureFile reverse_side:flags.2?InputSecureFile selfie:flags.3?InputSecureFile files:flags.4?Vector plain_data:flags.5?SecurePlainData = InputSecureValue; +inputSecureValue#8ec23218 flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?InputSecureFile reverse_side:flags.2?InputSecureFile selfie:flags.3?InputSecureFile translations:flags.6?Vector files:flags.4?Vector plain_data:flags.5?SecurePlainData = InputSecureValue; secureValueHash#ed1ecdb0 type:SecureValueType hash:bytes = SecureValueHash; @@ -885,10 +885,13 @@ secureValueErrorReverseSide#868a2aa5 type:SecureValueType file_hash:bytes text:s secureValueErrorSelfie#e537ced6 type:SecureValueType file_hash:bytes text:string = SecureValueError; secureValueErrorFile#7a700873 type:SecureValueType file_hash:bytes text:string = SecureValueError; secureValueErrorFiles#666220e9 type:SecureValueType file_hash:Vector text:string = SecureValueError; +secureValueError#869d758f type:SecureValueType hash:bytes text:string = SecureValueError; +secureValueErrorTranslation#5fbc1134 type:SecureValueType file_hash:bytes text:string = SecureValueError; +secureValueErrorTranslations type:SecureValueType file_hash:Vector text:string = SecureValueError; secureCredentialsEncrypted#33f0ea47 data:bytes hash:bytes secret:bytes = SecureCredentialsEncrypted; -account.authorizationForm#cb976d53 flags:# selfie_required:flags.1?true required_types:Vector values:Vector errors:Vector users:Vector privacy_policy_url:flags.0?string = account.AuthorizationForm; +account.authorizationForm#ad2e1cd8 flags:# selfie_required:flags.1?true translation_required:flags.2?true required_types:Vector values:Vector errors:Vector users:Vector privacy_policy_url:flags.0?string = account.AuthorizationForm; account.sentEmailCode#811f854f email_pattern:string length:int = account.SentEmailCode; @@ -911,6 +914,9 @@ secureSecretSettings#1527bcac secure_algo:SecurePasswordKdfAlgo secure_secret:by inputCheckPasswordEmpty#9880f658 = InputCheckPasswordSRP; inputCheckPasswordSRP#d27ff082 srp_id:long A:bytes M1:bytes = InputCheckPasswordSRP; +secureRequiredType#c84462de type:SecureValueType = SecureRequiredType; +secureRequiredTypeOneOf#193c4874 types:Vector = SecureRequiredType; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; diff --git a/td/generate/scheme/telegram_api.tlo b/td/generate/scheme/telegram_api.tlo index b6705d6eaff4454e45855b231aefe7d861dc2271..859a016be3326319f770253c8c565c3cb7709425 100644 GIT binary patch delta 929 zcmbR9oAW^v7w@Cl`c@23@M$COYhD%!qeFe0|M1@7lvt%zaZxBZHMz7XH7K>PG_xo* zC8V+-m0|J$aUT|0cEzic1te4?U;XJa6M(1+OUx-vbuB8&F9Mp#IoXj>d~=G#4Q7@b zGJ0z_JIHL{V_drVqn3QX49E_F%)Elq5+sd4Js|PP3X=>b$Lu=8%TrR6m{**WSdy8a zS3Ef|Qg|{)s>tM!-2#&}cIQBxIb*kuGT0^452Q0mY<_TWhL7UCB$pFH#h9)J`R3*^ zAs@!+jCzd9lMC*17=VOep1&%`6;tA2BPoul)ju!QKMkaIvdMiH77$~4jyB^F5l|3t zLzIJk4i1tJE+E&ZOuwPSn1Shq=@Gh&D$^Zw8G9hkzM#vf!2$}w>3?(?r$AU!^cZy@ zECBAR*y3W~2auqUobQS~M3DXU6Jvd0V zKQUzF2~hzhEy={>7; zj(dy-V8Q8o?g1kWWbJm7`-~b)VCn4%4;e48a)J^FCCIClZZNY1=T1Mpxj<$EALD|}irVu1G9Y6FGV=;b zOOP}HEdq&8U(m^DFu7;fnaOi@=Rhpv*`uQjwrqOAJw}tw1@~)wrti^V+#v#T0CzFM zUa*4#CV{l4OqbDP%mA99Gku31qrl`FyEUfk=rd|S%!vT9I6Zxy+#|RoatwjSgY}~65HP|au?1P81FUy@ zfhi+T2sp^5H*_+}fLW6_Ofi^#0T?dRExH(WCf~TvGyQ-eBgv)!kg@lO~;+UF6Y0o2Gxj3p51ACDL{n84E8Rh}|lV1=ms@rqG~6%-8&(=~M& Wts(LeuNhxVWd-?+Vfw;{jM4!54(P-H diff --git a/td/telegram/SecureManager.cpp b/td/telegram/SecureManager.cpp index 446b9cdae..36a27d870 100644 --- a/td/telegram/SecureManager.cpp +++ b/td/telegram/SecureManager.cpp @@ -79,7 +79,8 @@ class SetSecureValue : public NetQueryCallback { optional secret_; size_t files_left_to_upload_ = 0; - vector to_upload_; + vector files_to_upload_; + vector translations_to_upload_; optional front_side_; optional reverse_side_; optional selfie_; @@ -318,7 +319,13 @@ void SetSecureValue::UploadCallback::on_upload_error(FileId file_id, Status erro void SetSecureValue::on_upload_ok(FileId file_id, tl_object_ptr input_file) { SecureInputFile *info_ptr = nullptr; - for (auto &info : to_upload_) { + for (auto &info : files_to_upload_) { + if (info.file_id == file_id) { + info_ptr = &info; + break; + } + } + for (auto &info : translations_to_upload_) { if (info.file_id == file_id) { info_ptr = &info; break; @@ -398,6 +405,8 @@ void SetSecureValue::start_up() { return on_error(Status::Error(400, "Reverse side and selfie must be different")); } } + + CHECK(secure_value_.files.empty() || secure_value_.translations.empty()); if (!secure_value_.files.empty()) { CHECK(!front_side_file_id.is_valid()); CHECK(!reverse_side_file_id.is_valid()); @@ -418,11 +427,33 @@ void SetSecureValue::start_up() { } } } + if (!secure_value_.translations.empty()) { + for (auto it = secure_value_.translations.begin(); it != secure_value_.translations.end();) { + auto file_id = file_manager->get_file_view(it->file_id).file_id(); + bool is_duplicate = file_id == front_side_file_id || file_id == reverse_side_file_id || file_id == selfie_file_id; + for (auto pit = secure_value_.translations.begin(); pit != it; pit++) { + if (file_id == file_manager->get_file_view(pit->file_id).file_id()) { + is_duplicate = true; + break; + } + } + if (is_duplicate) { + it = secure_value_.translations.erase(it); + } else { + ++it; + } + } + } - to_upload_.resize(secure_value_.files.size()); upload_callback_ = std::make_shared(actor_id(this)); - for (size_t i = 0; i < to_upload_.size(); i++) { - start_upload(file_manager, secure_value_.files[i].file_id, to_upload_[i]); + + files_to_upload_.resize(secure_value_.files.size()); + for (size_t i = 0; i < files_to_upload_.size(); i++) { + start_upload(file_manager, secure_value_.files[i].file_id, files_to_upload_[i]); + } + translations_to_upload_.resize(secure_value_.translations.size()); + for (size_t i = 0; i < translations_to_upload_.size(); i++) { + start_upload(file_manager, secure_value_.translations[i].file_id, translations_to_upload_[i]); } if (front_side_) { start_upload(file_manager, secure_value_.front_side.file_id, front_side_.value()); @@ -461,7 +492,7 @@ void SetSecureValue::loop() { auto *file_manager = G()->td().get_actor_unsafe()->file_manager_.get(); auto input_secure_value = get_input_secure_value_object(file_manager, encrypt_secure_value(file_manager, *secret_, secure_value_), - to_upload_, front_side_, reverse_side_, selfie_); + files_to_upload_, front_side_, reverse_side_, selfie_, translations_to_upload_); auto save_secure_value = telegram_api::account_saveSecureValue(std::move(input_secure_value), secret_.value().get_hash()); auto query = G()->net_query_creator().create(create_storer(save_secure_value)); @@ -480,7 +511,10 @@ void SetSecureValue::tear_down() { if (file_manager == nullptr) { return; } - for (auto &file_info : to_upload_) { + for (auto &file_info : files_to_upload_) { + file_manager->upload(file_info.file_id, nullptr, 0, 0); + } + for (auto &file_info : translations_to_upload_) { file_manager->upload(file_info.file_id, nullptr, 0, 0); } if (front_side_) { @@ -520,6 +554,9 @@ void SetSecureValue::on_result(NetQueryPtr query) { if (secure_value_.selfie.file_id.is_valid() && encrypted_secure_value.selfie.file.file_id.is_valid()) { merge(file_manager, secure_value_.selfie.file_id, encrypted_secure_value.selfie); } + for (size_t i = 0; i < secure_value_.translations.size(); i++) { + merge(file_manager, secure_value_.translations[i].file_id, encrypted_secure_value.translations[i]); + } auto r_secure_value = decrypt_secure_value(file_manager, *secret_, encrypted_secure_value); if (r_secure_value.is_error()) { return on_error(r_secure_value.move_as_error()); @@ -647,7 +684,26 @@ class GetPassportAuthorizationForm : public NetQueryCallback { std::vector values; bool is_selfie_required = (authorization_form_->flags_ & telegram_api::account_authorizationForm::SELFIE_REQUIRED_MASK) != 0; - auto types = get_secure_value_types(authorization_form_->required_types_); + bool is_translation_required = + (authorization_form_->flags_ & telegram_api::account_authorizationForm::TRANSLATION_REQUIRED_MASK) != 0; + vector types; + for (auto &type_ptr : authorization_form_->required_types_) { + CHECK(type_ptr != nullptr); + switch (type_ptr->get_id()) { + case telegram_api::secureRequiredType::ID: { + auto type = move_tl_object_as(type_ptr); + types.push_back(get_secure_value_type(type->type_)); + break; + } + case telegram_api::secureRequiredTypeOneOf::ID: { + auto type = move_tl_object_as(type_ptr); + append(types, get_secure_value_types(type->types_)); + break; + } + default: + UNREACHABLE(); + } + } for (auto type : types) { for (auto &value : authorization_form_->values_) { if (value == nullptr) { @@ -685,6 +741,13 @@ class GetPassportAuthorizationForm : public NetQueryCallback { td_api::object_ptr source; string message; switch (error_ptr->get_id()) { + case telegram_api::secureValueError::ID: { + auto error = move_tl_object_as(error_ptr); + type = get_secure_value_type(error->type_); + message = std::move(error->text_); + source = td_api::make_object(); + break; + } case telegram_api::secureValueErrorData::ID: { auto error = move_tl_object_as(error_ptr); type = get_secure_value_type(error->type_); @@ -731,6 +794,20 @@ class GetPassportAuthorizationForm : public NetQueryCallback { source = td_api::make_object(); break; } + case telegram_api::secureValueErrorTranslation::ID: { + auto error = move_tl_object_as(error_ptr); + type = get_secure_value_type(error->type_); + message = std::move(error->text_); + source = td_api::make_object(); + break; + } + case telegram_api::secureValueErrorTranslations::ID: { + auto error = move_tl_object_as(error_ptr); + type = get_secure_value_type(error->type_); + message = std::move(error->text_); + source = td_api::make_object(); + break; + } default: UNREACHABLE(); } @@ -744,7 +821,7 @@ class GetPassportAuthorizationForm : public NetQueryCallback { promise_.set_value(make_tl_object( authorization_form_id_, get_passport_element_types_object(types), std::move(values), std::move(errors), - is_selfie_required, authorization_form_->privacy_policy_url_)); + is_selfie_required, is_translation_required, authorization_form_->privacy_policy_url_)); stop(); } }; @@ -841,6 +918,12 @@ void SecureManager::set_secure_value_errors(Td *td, tl_object_ptrtype_)); switch (error->source_->get_id()) { + case td_api::inputPassportElementErrorSourceUnspecified::ID: { + auto source = td_api::move_object_as(error->source_); + input_errors.push_back(make_tl_object( + std::move(type), BufferSlice(source->element_hash_), error->message_)); + break; + } case td_api::inputPassportElementErrorSourceDataField::ID: { auto source = td_api::move_object_as(error->source_); if (!clean_input_string(source->field_name_)) { @@ -869,6 +952,22 @@ void SecureManager::set_secure_value_errors(Td *td, tl_object_ptrfile_hash_), error->message_)); break; } + case td_api::inputPassportElementErrorSourceTranslation::ID: { + auto source = td_api::move_object_as(error->source_); + input_errors.push_back(make_tl_object( + std::move(type), BufferSlice(source->file_hash_), error->message_)); + break; + } + case td_api::inputPassportElementErrorSourceTranslations::ID: { + auto source = td_api::move_object_as(error->source_); + if (source->file_hashes_.empty()) { + return promise.set_error(Status::Error(400, "File hashes must be non-empty")); + } + auto file_hashes = transform(source->file_hashes_, [](Slice hash) { return BufferSlice(hash); }); + input_errors.push_back(make_tl_object( + std::move(type), std::move(file_hashes), error->message_)); + break; + } case td_api::inputPassportElementErrorSourceFile::ID: { auto source = td_api::move_object_as(error->source_); input_errors.push_back(make_tl_object( @@ -878,7 +977,7 @@ void SecureManager::set_secure_value_errors(Td *td, tl_object_ptr(error->source_); if (source->file_hashes_.empty()) { - return promise.set_error(Status::Error(400, "Error hashes must be non-empty")); + return promise.set_error(Status::Error(400, "File hashes must be non-empty")); } auto file_hashes = transform(source->file_hashes_, [](Slice hash) { return BufferSlice(hash); }); input_errors.push_back(make_tl_object( @@ -927,6 +1026,7 @@ void SecureManager::on_get_passport_authorization_form(int32 authorization_form_ auto authorization_form = r_authorization_form.move_as_ok(); CHECK(authorization_form != nullptr); it->second.is_selfie_required = authorization_form->is_selfie_required_; + it->second.is_translation_required = authorization_form->is_translation_required_; promise.set_value(std::move(authorization_form)); } @@ -997,7 +1097,8 @@ void SecureManager::do_send_passport_authorization_form(int32 authorization_form } auto r_encrypted_credentials = - get_encrypted_credentials(credentials, it->second.payload, it->second.is_selfie_required, it->second.public_key); + get_encrypted_credentials(credentials, it->second.payload, it->second.is_selfie_required, + it->second.is_translation_required, it->second.public_key); if (r_encrypted_credentials.is_error()) { return promise.set_error(r_encrypted_credentials.move_as_error()); } diff --git a/td/telegram/SecureManager.h b/td/telegram/SecureManager.h index 19574e992..a92ff4b85 100644 --- a/td/telegram/SecureManager.h +++ b/td/telegram/SecureManager.h @@ -57,6 +57,7 @@ class SecureManager : public NetQueryCallback { string public_key; string payload; bool is_selfie_required; + bool is_translation_required; bool is_received; }; diff --git a/td/telegram/SecureValue.cpp b/td/telegram/SecureValue.cpp index 3fff63f8b..6896368cf 100644 --- a/td/telegram/SecureValue.cpp +++ b/td/telegram/SecureValue.cpp @@ -239,8 +239,8 @@ vector> get_passport_element_typ string get_secure_value_data_field_name(SecureValueType type, string field_name) { switch (type) { case SecureValueType::PersonalDetails: - if (field_name == "first_name" || field_name == "last_name" || field_name == "gender" || - field_name == "country_code" || field_name == "residence_country_code") { + if (field_name == "first_name" || field_name == "middle_name" || field_name == "last_name" || + field_name == "gender" || field_name == "country_code" || field_name == "residence_country_code") { return field_name; } if (field_name == "birth_date") { @@ -433,7 +433,7 @@ telegram_api::object_ptr get_secure_data_object(const bool operator==(const EncryptedSecureValue &lhs, const EncryptedSecureValue &rhs) { return lhs.type == rhs.type && lhs.data == rhs.data && lhs.files == rhs.files && lhs.front_side == rhs.front_side && - lhs.reverse_side == rhs.reverse_side && lhs.selfie == rhs.selfie; + lhs.reverse_side == rhs.reverse_side && lhs.selfie == rhs.selfie && lhs.translations == rhs.translations; } bool operator!=(const EncryptedSecureValue &lhs, const EncryptedSecureValue &rhs) { @@ -447,10 +447,12 @@ static bool check_encrypted_secure_value(const EncryptedSecureValue &value) { bool has_front_side = value.front_side.file.file_id.is_valid(); bool has_reverse_side = value.reverse_side.file.file_id.is_valid(); bool has_selfie = value.selfie.file.file_id.is_valid(); + bool has_translations = !value.translations.empty(); switch (value.type) { case SecureValueType::PersonalDetails: case SecureValueType::Address: - return has_encrypted_data && !has_files && !has_front_side && !has_reverse_side && !has_selfie; + return has_encrypted_data && !has_files && !has_front_side && !has_reverse_side && !has_selfie && + !has_translations; case SecureValueType::Passport: case SecureValueType::InternalPassport: return has_encrypted_data && !has_files && has_front_side && !has_reverse_side; @@ -462,11 +464,12 @@ static bool check_encrypted_secure_value(const EncryptedSecureValue &value) { case SecureValueType::RentalAgreement: case SecureValueType::PassportRegistration: case SecureValueType::TemporaryRegistration: - return !has_encrypted_data && !has_plain_data && has_files && !has_front_side && !has_reverse_side && !has_selfie; + return !has_encrypted_data && !has_plain_data && has_files && !has_front_side && !has_reverse_side && + !has_selfie && !has_translations; case SecureValueType::PhoneNumber: - return has_plain_data && !has_files && !has_front_side && !has_reverse_side && !has_selfie; + return has_plain_data && !has_files && !has_front_side && !has_reverse_side && !has_selfie && !has_translations; case SecureValueType::EmailAddress: - return has_plain_data && !has_files && !has_front_side && !has_reverse_side && !has_selfie; + return has_plain_data && !has_files && !has_front_side && !has_reverse_side && !has_selfie && !has_translations; case SecureValueType::None: return false; default: @@ -507,6 +510,7 @@ EncryptedSecureValue get_encrypted_secure_value(FileManager *file_manager, if (secure_value->selfie_ != nullptr) { result.selfie = get_encrypted_secure_file(file_manager, std::move(secure_value->selfie_)); } + result.translations = get_encrypted_secure_files(file_manager, std::move(secure_value->translations_)); result.hash = secure_value->hash_.as_slice().str(); if (!check_encrypted_secure_value(result)) { LOG(ERROR) << "Receive invalid encrypted secure value of type " << result.type; @@ -536,12 +540,14 @@ td_api::object_ptr get_encrypted_passport_elem value.front_side.file.file_id.is_valid() ? get_dated_file_object(file_manager, value.front_side) : nullptr, value.reverse_side.file.file_id.is_valid() ? get_dated_file_object(file_manager, value.reverse_side) : nullptr, value.selfie.file.file_id.is_valid() ? get_dated_file_object(file_manager, value.selfie) : nullptr, - get_dated_files_object(file_manager, value.files), is_plain ? value.data.data : string()); + get_dated_files_object(file_manager, value.translations), get_dated_files_object(file_manager, value.files), + is_plain ? value.data.data : string(), value.hash); } telegram_api::object_ptr get_input_secure_value_object( - FileManager *file_manager, const EncryptedSecureValue &value, std::vector &input_files, - optional &front_side, optional &reverse_side, optional &selfie) { + FileManager *file_manager, const EncryptedSecureValue &value, std::vector &files, + optional &front_side, optional &reverse_side, optional &selfie, + std::vector &translations) { bool is_plain = value.type == SecureValueType::PhoneNumber || value.type == SecureValueType::EmailAddress; bool has_front_side = value.front_side.file.file_id.is_valid(); bool has_reverse_side = value.reverse_side.file.file_id.is_valid(); @@ -573,12 +579,16 @@ telegram_api::object_ptr get_input_secure_value_ flags |= telegram_api::inputSecureValue::SELFIE_MASK; CHECK(selfie); } + if (!value.translations.empty()) { + flags |= telegram_api::inputSecureValue::TRANSLATIONS_MASK; + } return telegram_api::make_object( flags, get_input_secure_value_type(value.type), is_plain ? nullptr : get_secure_data_object(value.data), has_front_side ? get_input_secure_file_object(file_manager, value.front_side, *front_side) : nullptr, has_reverse_side ? get_input_secure_file_object(file_manager, value.reverse_side, *reverse_side) : nullptr, has_selfie ? get_input_secure_file_object(file_manager, value.selfie, *selfie) : nullptr, - get_input_secure_files_object(file_manager, value.files, input_files), std::move(plain_data)); + get_input_secure_files_object(file_manager, value.translations, translations), + get_input_secure_files_object(file_manager, value.files, files), std::move(plain_data)); } vector> get_encrypted_passport_element_object( @@ -700,6 +710,16 @@ static Status check_first_name(string &first_name) { return Status::OK(); } +static Status check_middle_name(string &middle_name) { + if (!clean_input_string(middle_name)) { + return Status::Error(400, "Middle name must be encoded in UTF-8"); + } + if (utf8_length(middle_name) > 255) { + return Status::Error(400, "Middle name is too long"); + } + return Status::OK(); +} + static Status check_last_name(string &last_name) { if (!clean_input_string(last_name)) { return Status::Error(400, "Last name must be encoded in UTF-8"); @@ -725,6 +745,7 @@ static Result get_personal_details(td_api::object_ptrfirst_name_)); + TRY_STATUS(check_middle_name(personal_details->middle_name_)); TRY_STATUS(check_last_name(personal_details->last_name_)); TRY_RESULT(birthdate, get_date(std::move(personal_details->birthdate_))); if (birthdate.empty()) { @@ -736,6 +757,7 @@ static Result get_personal_details(td_api::object_ptr(json_object([&](auto &o) { o("first_name", personal_details->first_name_); + o("middle_name", personal_details->middle_name_); o("last_name", personal_details->last_name_); o("birth_date", birthdate); o("gender", personal_details->gender_); @@ -758,6 +780,7 @@ static Result> get_personal_details_ auto &object = value.get_object(); TRY_RESULT(first_name, get_json_object_string_field(object, "first_name", true)); + TRY_RESULT(middle_name, get_json_object_string_field(object, "middle_name", true)); TRY_RESULT(last_name, get_json_object_string_field(object, "last_name", true)); TRY_RESULT(birthdate, get_json_object_string_field(object, "birth_date", true)); if (birthdate.empty()) { @@ -768,15 +791,16 @@ static Result> get_personal_details_ TRY_RESULT(residence_country_code, get_json_object_string_field(object, "residence_country_code", true)); TRY_STATUS(check_first_name(first_name)); + TRY_STATUS(check_middle_name(middle_name)); TRY_STATUS(check_last_name(last_name)); TRY_RESULT(date, get_date_object(birthdate)); TRY_STATUS(check_gender(gender)); TRY_STATUS(check_country_code(country_code)); TRY_STATUS(check_country_code(residence_country_code)); - return td_api::make_object(std::move(first_name), std::move(last_name), std::move(date), - std::move(gender), std::move(country_code), - std::move(residence_country_code)); + return td_api::make_object(std::move(first_name), std::move(middle_name), + std::move(last_name), std::move(date), std::move(gender), + std::move(country_code), std::move(residence_country_code)); } static Status check_document_number(string &number) { @@ -841,14 +865,18 @@ static Result get_identity_document(SecureValueType type, FileManag } TRY_RESULT(front_side, get_secure_file(file_manager, std::move(identity_document->front_side_))); - res.front_side = front_side; + res.front_side = std::move(front_side); if (identity_document->reverse_side_ != nullptr) { TRY_RESULT(reverse_side, get_secure_file(file_manager, std::move(identity_document->reverse_side_))); - res.reverse_side = reverse_side; + res.reverse_side = std::move(reverse_side); } if (identity_document->selfie_ != nullptr) { TRY_RESULT(selfie, get_secure_file(file_manager, std::move(identity_document->selfie_))); - res.selfie = selfie; + res.selfie = std::move(selfie); + } + if (!identity_document->translations_.empty()) { + TRY_RESULT(translations, get_secure_files(file_manager, std::move(identity_document->translations_))); + res.translations = std::move(translations); } return res; } @@ -888,8 +916,11 @@ static Result> get_identity_documen TRY_STATUS(check_document_number(number)); TRY_RESULT(date, get_date_object(expiry_date)); + auto translations = transform( + value.translations, [file_manager](const DatedFile &file) { return get_dated_file_object(file_manager, file); }); return td_api::make_object(std::move(number), std::move(date), std::move(front_side), - std::move(reverse_side), std::move(selfie)); + std::move(reverse_side), std::move(selfie), + std::move(translations)); } static Status check_phone_number(string &phone_number) { @@ -1167,6 +1198,9 @@ Result decrypt_secure_value(FileManager *file_manage if (res.selfie.file_id.is_valid()) { res_credentials.selfie = std::move(selfie.second); } + TRY_RESULT(translations, decrypt_secure_files(file_manager, secret, encrypted_secure_value.translations)); + res.translations = std::move(translations.first); + res_credentials.translations = std::move(translations.second); break; } } @@ -1281,6 +1315,7 @@ EncryptedSecureValue encrypt_secure_value(FileManager *file_manager, const secur res.front_side = encrypt_secure_file(file_manager, master_secret, secure_value.front_side, to_hash); res.reverse_side = encrypt_secure_file(file_manager, master_secret, secure_value.reverse_side, to_hash); res.selfie = encrypt_secure_file(file_manager, master_secret, secure_value.selfie, to_hash); + res.translations = encrypt_secure_files(file_manager, master_secret, secure_value.translations, to_hash); res.hash = secure_storage::calc_value_hash(to_hash).as_slice().str(); break; } @@ -1346,15 +1381,15 @@ static Slice secure_value_type_as_slice(SecureValueType type) { } static auto credentials_as_jsonable(const std::vector &credentials, Slice payload, - bool with_selfie) { - return json_object([&credentials, payload, with_selfie](auto &o) { - o("secure_data", json_object([&credentials, with_selfie](auto &o) { + bool with_selfie, bool with_translations) { + return json_object([&credentials, payload, with_selfie, with_translations](auto &o) { + o("secure_data", json_object([&credentials, with_selfie, with_translations](auto &o) { for (auto &cred : credentials) { if (cred.type == SecureValueType::PhoneNumber || cred.type == SecureValueType::EmailAddress) { continue; } - o(secure_value_type_as_slice(cred.type), json_object([&cred, with_selfie](auto &o) { + o(secure_value_type_as_slice(cred.type), json_object([&cred, with_selfie, with_translations](auto &o) { if (cred.data) { o("data", as_jsonable(cred.data.value())); } @@ -1370,6 +1405,9 @@ static auto credentials_as_jsonable(const std::vector &c if (cred.selfie && with_selfie) { o("selfie", as_jsonable(cred.selfie.value())); } + if (!cred.translations.empty() && with_translations) { + o("translations", as_jsonable(cred.translations)); + } })); } })); @@ -1378,8 +1416,10 @@ static auto credentials_as_jsonable(const std::vector &c } Result get_encrypted_credentials(const std::vector &credentials, - Slice payload, bool with_selfie, Slice public_key) { - auto encoded_credentials = json_encode(credentials_as_jsonable(credentials, payload, with_selfie)); + Slice payload, bool with_selfie, bool with_translations, + Slice public_key) { + auto encoded_credentials = + json_encode(credentials_as_jsonable(credentials, payload, with_selfie, with_translations)); LOG(INFO) << "Created credentials " << encoded_credentials; auto secret = secure_storage::Secret::create_new(); diff --git a/td/telegram/SecureValue.h b/td/telegram/SecureValue.h index 09495a224..de7dc27db 100644 --- a/td/telegram/SecureValue.h +++ b/td/telegram/SecureValue.h @@ -113,7 +113,8 @@ struct EncryptedSecureValue { EncryptedSecureFile front_side; EncryptedSecureFile reverse_side; EncryptedSecureFile selfie; - string hash; // memory only + vector translations; + string hash; }; bool operator==(const EncryptedSecureValue &lhs, const EncryptedSecureValue &rhs); @@ -128,8 +129,9 @@ vector get_encrypted_secure_values( td_api::object_ptr get_encrypted_passport_element_object( FileManager *file_manager, const EncryptedSecureValue &value); telegram_api::object_ptr get_input_secure_value_object( - FileManager *file_manager, const EncryptedSecureValue &value, vector &input_files, - optional &front_side, optional &reverse_side, optional &selfie); + FileManager *file_manager, const EncryptedSecureValue &value, vector &files, + optional &front_side, optional &reverse_side, optional &selfie, + vector &translations); vector> get_encrypted_passport_element_object( FileManager *file_manager, const vector &values); @@ -168,10 +170,12 @@ struct SecureValueCredentials { optional front_side; optional reverse_side; optional selfie; + std::vector translations; }; Result get_encrypted_credentials(const std::vector &credentials, - Slice payload, bool with_selfie, Slice public_key); + Slice payload, bool with_selfie, bool with_translations, + Slice public_key); class SecureValue { public: @@ -181,6 +185,7 @@ class SecureValue { DatedFile front_side; DatedFile reverse_side; DatedFile selfie; + vector translations; }; struct SecureValueWithCredentials { diff --git a/td/telegram/SecureValue.hpp b/td/telegram/SecureValue.hpp index dcba15ce5..386025cc2 100644 --- a/td/telegram/SecureValue.hpp +++ b/td/telegram/SecureValue.hpp @@ -75,12 +75,16 @@ void store(const EncryptedSecureValue &value, StorerT &storer) { bool has_front_side = value.front_side.file.file_id.is_valid(); bool has_reverse_side = value.reverse_side.file.file_id.is_valid(); bool has_selfie = value.selfie.file.file_id.is_valid(); + bool has_hash = !value.hash.empty(); + bool has_translations = !value.translations.empty(); BEGIN_STORE_FLAGS(); STORE_FLAG(has_data_hash); STORE_FLAG(has_files); STORE_FLAG(has_front_side); STORE_FLAG(has_reverse_side); STORE_FLAG(has_selfie); + STORE_FLAG(has_hash); + STORE_FLAG(has_translations); END_STORE_FLAGS(); store(value.type, storer); if (has_data_hash) { @@ -100,6 +104,12 @@ void store(const EncryptedSecureValue &value, StorerT &storer) { if (has_selfie) { store(value.selfie, storer); } + if (has_hash) { + store(value.hash, storer); + } + if (has_translations) { + store(value.translations, storer); + } } template @@ -109,12 +119,16 @@ void parse(EncryptedSecureValue &value, ParserT &parser) { bool has_front_side; bool has_reverse_side; bool has_selfie; + bool has_hash; + bool has_translations; BEGIN_PARSE_FLAGS(); PARSE_FLAG(has_data_hash); PARSE_FLAG(has_files); PARSE_FLAG(has_front_side); PARSE_FLAG(has_reverse_side); PARSE_FLAG(has_selfie); + PARSE_FLAG(has_hash); + PARSE_FLAG(has_translations); END_PARSE_FLAGS(); parse(value.type, parser); if (has_data_hash) { @@ -134,6 +148,12 @@ void parse(EncryptedSecureValue &value, ParserT &parser) { if (has_selfie) { parse(value.selfie, parser); } + if (has_hash) { + parse(value.hash, parser); + } + if (has_translations) { + parse(value.translations, parser); + } } } // namespace td diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index b47c4a9b8..9064f3074 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -1093,23 +1093,34 @@ class CliClient final : public Actor { return make_tl_object(arg); } else if (passport_element_type == "pd") { return make_tl_object(make_tl_object( - "Mike", "Towers", make_tl_object(29, 2, 2000), "male", "US", "GB")); + "Mike", "Jr", "Towers", make_tl_object(29, 2, 2000), "male", "US", "GB")); } else if (passport_element_type == "driver_license" || passport_element_type == "dl") { - if (input_files.size() == 2) { + if (input_files.size() >= 2) { + auto front_side = std::move(input_files[0]); + input_files.erase(input_files.begin()); + auto reverse_side = std::move(input_files[0]); + input_files.erase(input_files.begin()); return make_tl_object(make_tl_object( - "1234567890", make_tl_object(1, 3, 2029), std::move(input_files[0]), - std::move(input_files[1]), std::move(selfie))); + "1234567890", make_tl_object(1, 3, 2029), std::move(front_side), std::move(reverse_side), + std::move(selfie), std::move(input_files))); } } else if (passport_element_type == "identity_card" || passport_element_type == "ic") { - if (input_files.size() == 2) { + if (input_files.size() >= 2) { + auto front_side = std::move(input_files[0]); + input_files.erase(input_files.begin()); + auto reverse_side = std::move(input_files[0]); + input_files.erase(input_files.begin()); return make_tl_object(make_tl_object( - "1234567890", nullptr, std::move(input_files[0]), std::move(input_files[1]), std::move(selfie))); + "1234567890", nullptr, std::move(front_side), std::move(reverse_side), std::move(selfie), + std::move(input_files))); } } else if (passport_element_type == "internal_passport" || passport_element_type == "ip") { - if (input_files.size() == 1) { + if (input_files.size() >= 1) { + auto front_side = std::move(input_files[0]); + input_files.erase(input_files.begin()); return make_tl_object( - make_tl_object("1234567890", nullptr, std::move(input_files[0]), nullptr, - std::move(selfie))); + make_tl_object("1234567890", nullptr, std::move(front_side), nullptr, + std::move(selfie), std::move(input_files))); } } else if (passport_element_type == "rental_aggrement" || passport_element_type == "ra") { return make_tl_object(std::move(input_files)); diff --git a/td/telegram/net/MtprotoHeader.cpp b/td/telegram/net/MtprotoHeader.cpp index 31ed973b3..b06f0b66c 100644 --- a/td/telegram/net/MtprotoHeader.cpp +++ b/td/telegram/net/MtprotoHeader.cpp @@ -21,7 +21,7 @@ class HeaderStorer { } template void store(StorerT &storer) const { - constexpr int32 LAYER = 84; + constexpr int32 LAYER = 85; using td::store; // invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;