From 00fabb8f9718c13b5c9e28fab005b75d51cbb6e2 Mon Sep 17 00:00:00 2001 From: levlam Date: Fri, 10 Aug 2018 16:22:48 +0300 Subject: [PATCH] Update layer to 84. GitOrigin-RevId: a2057596f00201ee967e7d038e048b618f6e629d --- td/generate/scheme/telegram_api.tl | 15 +-- td/generate/scheme/telegram_api.tlo | Bin 162844 -> 163228 bytes td/telegram/AuthManager.cpp | 14 ++- td/telegram/AuthManager.h | 4 + td/telegram/AuthManager.hpp | 15 +++ td/telegram/PasswordManager.cpp | 145 +++++++++++++++++++++++----- td/telegram/PasswordManager.h | 18 +++- td/telegram/cli.cpp | 4 +- td/telegram/net/MtprotoHeader.cpp | 2 +- 9 files changed, 179 insertions(+), 38 deletions(-) diff --git a/td/generate/scheme/telegram_api.tl b/td/generate/scheme/telegram_api.tl index 48baa7c2..e8b393e5 100644 --- a/td/generate/scheme/telegram_api.tl +++ b/td/generate/scheme/telegram_api.tl @@ -468,7 +468,7 @@ authorization#7bf2e6f6 hash:long flags:int device_model:string platform:string s account.authorizations#1250abde authorizations:Vector = account.Authorizations; -account.password#68873ba5 flags:# has_recovery:flags.0?true has_secure_values:flags.1?true has_password:flags.2?true current_algo:flags.2?PasswordKdfAlgo hint:flags.3?string email_unconfirmed_pattern:flags.4?string new_algo:PasswordKdfAlgo new_secure_algo:SecurePasswordKdfAlgo secure_random:bytes = account.Password; +account.password#ad2641f8 flags:# has_recovery:flags.0?true has_secure_values:flags.1?true has_password:flags.2?true current_algo:flags.2?PasswordKdfAlgo srp_B:flags.2?bytes srp_id:flags.2?long hint:flags.3?string email_unconfirmed_pattern:flags.4?string new_algo:PasswordKdfAlgo new_secure_algo:SecurePasswordKdfAlgo secure_random:bytes = account.Password; account.passwordSettings#9a5c33e5 flags:# email:flags.0?string secure_settings:flags.1?SecureSecretSettings = account.PasswordSettings; @@ -900,7 +900,7 @@ savedPhoneContact#1142bd56 phone:string first_name:string last_name:string date: account.takeout#4dba4501 id:long = account.Takeout; passwordKdfAlgoUnknown#d45ab096 = PasswordKdfAlgo; -passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000#b6425eaa salt1:bytes salt2:bytes = PasswordKdfAlgo; +passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow#3a912d4a salt1:bytes salt2:bytes g:int p:bytes = PasswordKdfAlgo; securePasswordKdfAlgoUnknown#4a8537 = SecurePasswordKdfAlgo; securePasswordKdfAlgoPBKDF2HMACSHA512iter100000#bbf2dda0 salt:bytes = SecurePasswordKdfAlgo; @@ -908,6 +908,9 @@ securePasswordKdfAlgoSHA512#86471d92 salt:bytes = SecurePasswordKdfAlgo; secureSecretSettings#1527bcac secure_algo:SecurePasswordKdfAlgo secure_secret:bytes secure_secret_id:long = SecureSecretSettings; +inputCheckPasswordEmpty#9880f658 = InputCheckPasswordSRP; +inputCheckPasswordSRP#d27ff082 srp_id:long A:bytes M1:bytes = InputCheckPasswordSRP; + ---functions--- invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X; @@ -927,7 +930,7 @@ auth.exportAuthorization#e5bfffcd dc_id:int = auth.ExportedAuthorization; auth.importAuthorization#e3ef9613 id:int bytes:bytes = auth.Authorization; auth.bindTempAuthKey#cdd42a05 perm_auth_key_id:long nonce:long expires_at:int encrypted_message:bytes = Bool; auth.importBotAuthorization#67a3ff2c flags:int api_id:int api_hash:string bot_auth_token:string = auth.Authorization; -auth.checkPassword#a63011e password_hash:bytes = auth.Authorization; +auth.checkPassword#d18b4d16 password:InputCheckPasswordSRP = auth.Authorization; auth.requestPasswordRecovery#d897bc66 = auth.PasswordRecovery; auth.recoverPassword#4ea56e92 code:string = auth.Authorization; auth.resendCode#3ef1a9bf phone_number:string phone_code_hash:string = auth.SentCode; @@ -956,11 +959,11 @@ account.updateDeviceLocked#38df3532 period:int = Bool; account.getAuthorizations#e320c158 = account.Authorizations; account.resetAuthorization#df77f3bc hash:long = Bool; account.getPassword#548a30f5 = account.Password; -account.getPasswordSettings#bc8d11bb current_password_hash:bytes = account.PasswordSettings; -account.updatePasswordSettings#fa7c4b86 current_password_hash:bytes new_settings:account.PasswordInputSettings = Bool; +account.getPasswordSettings#9cd4eaf9 password:InputCheckPasswordSRP = account.PasswordSettings; +account.updatePasswordSettings#a59b102f password:InputCheckPasswordSRP new_settings:account.PasswordInputSettings = Bool; account.sendConfirmPhoneCode#1516d7bd flags:# allow_flashcall:flags.0?true hash:string current_number:flags.0?Bool = auth.SentCode; account.confirmPhone#5f2178c3 phone_code_hash:string phone_code:string = Bool; -account.getTmpPassword#4a82327e password_hash:bytes period:int = account.TmpPassword; +account.getTmpPassword#449e0b51 password:InputCheckPasswordSRP period:int = account.TmpPassword; account.getWebAuthorizations#182e6d6f = account.WebAuthorizations; account.resetWebAuthorization#2d01b9ef hash:long = Bool; account.resetWebAuthorizations#682d2594 = Bool; diff --git a/td/generate/scheme/telegram_api.tlo b/td/generate/scheme/telegram_api.tlo index d55feae3c5883ce42fe9033d06c22afa527a460f..b6705d6eaff4454e45855b231aefe7d861dc2271 100644 GIT binary patch delta 681 zcmbR9gLBSrPTohe^{p77;NwPKZZ6qdZ2!GPJ@X1mOPn)Old}U7i;K(ii&BDv0vH&W zHZyXa=U}-r_w>8XSA-UDvixvVTf4bJ#)Xe@!REbM?78(3-x_9!XJY7f%`GUY1etvE zn2-<1x*}&b9S+;JgCH@O{a59P!4kQlN9ymTj8IZkh6W;7KK z_HZ;ZHS^6+3CJ&JV1Q|6nSPL&(Rum=9!8ewJJvFqOcp4BgvfRQ7RC)tGN2$4NK8)7 zFU>2_D}aU`&<2qBbb|wo2Ge=k8MlZb+Y9mxE66iWleH@3z(%uxJU3b3oycSfOTp=- zos9g`AG9-OfP;RzPX}WHIOwNu=wNIBi)}aQWYh`O09k_WLy#D<4}ZS8GG}_nJw_b~ z4kYJ+RKOx+`kF3Ah3P-;F+N}kGV6V84d8L iGED1yU0gIj&1-7ukcm? delta 409 zcmbR9n{&<&PTohe^{p77;KN26X-WXu3Na{8N2#sshFEzXGfHfq@`;h78vyzdj&1+| diff --git a/td/telegram/AuthManager.cpp b/td/telegram/AuthManager.cpp index b9ce28e4..147b21dd 100644 --- a/td/telegram/AuthManager.cpp +++ b/td/telegram/AuthManager.cpp @@ -614,8 +614,10 @@ void AuthManager::check_password(uint64 query_id, string password) { return on_query_error(query_id, Status::Error(8, "checkAuthenticationPassword unexpected")); } - auto hash = PasswordManager::calc_password_hash(password, wait_password_state_.current_client_salt_, - wait_password_state_.current_server_salt_); + auto hash = PasswordManager::get_input_check_password(password, wait_password_state_.current_client_salt_, + wait_password_state_.current_server_salt_, + wait_password_state_.srp_g_, wait_password_state_.srp_p_, + wait_password_state_.srp_B_, wait_password_state_.srp_id_); on_new_query(query_id); start_net_query(NetQueryType::CheckPassword, @@ -755,11 +757,15 @@ void AuthManager::on_get_password_result(NetQueryPtr &result) { case telegram_api::passwordKdfAlgoUnknown::ID: // TODO we need to abort authorization somehow break; - case telegram_api::passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000::ID: { - auto algo = move_tl_object_as( + case telegram_api::passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow::ID: { + auto algo = move_tl_object_as( password->current_algo_); wait_password_state_.current_client_salt_ = algo->salt1_.as_slice().str(); wait_password_state_.current_server_salt_ = algo->salt2_.as_slice().str(); + wait_password_state_.srp_g_ = algo->g_; + wait_password_state_.srp_p_ = algo->p_.as_slice().str(); + wait_password_state_.srp_B_ = password->srp_B_.as_slice().str(); + wait_password_state_.srp_id_ = password->srp_id_; wait_password_state_.hint_ = std::move(password->hint_); wait_password_state_.has_recovery_ = (password->flags_ & telegram_api::account_password::HAS_RECOVERY_MASK) != 0; diff --git a/td/telegram/AuthManager.h b/td/telegram/AuthManager.h index b8ccc584..da1be320 100644 --- a/td/telegram/AuthManager.h +++ b/td/telegram/AuthManager.h @@ -198,6 +198,10 @@ class AuthManager : public NetActor { struct WaitPasswordState { string current_client_salt_; string current_server_salt_; + int32 srp_g_ = 0; + string srp_p_; + string srp_B_; + int64 srp_id_ = 0; string hint_; bool has_recovery_ = false; string email_address_pattern_; diff --git a/td/telegram/AuthManager.hpp b/td/telegram/AuthManager.hpp index d6d9e724..41b64ca2 100644 --- a/td/telegram/AuthManager.hpp +++ b/td/telegram/AuthManager.hpp @@ -59,6 +59,10 @@ void AuthManager::WaitPasswordState::store(T &storer) const { using td::store; store(current_client_salt_, storer); store(current_server_salt_, storer); + store(srp_g_, storer); + store(srp_p_, storer); + store(srp_B_, storer); + store(srp_id_, storer); store(hint_, storer); store(has_recovery_, storer); store(email_address_pattern_, storer); @@ -69,6 +73,10 @@ void AuthManager::WaitPasswordState::parse(T &parser) { using td::parse; parse(current_client_salt_, parser); parse(current_server_salt_, parser); + parse(srp_g_, parser); + parse(srp_p_, parser); + parse(srp_B_, parser); + parse(srp_id_, parser); parse(hint_, parser); parse(has_recovery_, parser); parse(email_address_pattern_, parser); @@ -79,9 +87,11 @@ void AuthManager::DbState::store(T &storer) const { using td::store; bool has_terms_of_service = !terms_of_service_.get_id().empty(); bool is_pbkdf2_supported = true; + bool is_srp_supported = true; BEGIN_STORE_FLAGS(); STORE_FLAG(has_terms_of_service); STORE_FLAG(is_pbkdf2_supported); + STORE_FLAG(is_srp_supported); END_STORE_FLAGS(); store(state_, storer); store(api_id_, storer); @@ -106,10 +116,12 @@ void AuthManager::DbState::parse(T &parser) { using td::parse; bool has_terms_of_service = false; bool is_pbkdf2_supported = false; + bool is_srp_supported = false; if (parser.version() >= static_cast(Version::AddTermsOfService)) { BEGIN_PARSE_FLAGS(); PARSE_FLAG(has_terms_of_service); PARSE_FLAG(is_pbkdf2_supported); + PARSE_FLAG(is_srp_supported); END_PARSE_FLAGS(); } parse(state_, parser); @@ -130,6 +142,9 @@ void AuthManager::DbState::parse(T &parser) { if (!is_pbkdf2_supported) { return parser.set_error("Need PBKDF2 support"); } + if (!is_srp_supported) { + return parser.set_error("Need SRP support"); + } parse(wait_password_state_, parser); } else { parser.set_error(PSTRING() << "Unexpected " << tag("state", static_cast(state_))); diff --git a/td/telegram/PasswordManager.cpp b/td/telegram/PasswordManager.cpp index b4574e51..b1741288 100644 --- a/td/telegram/PasswordManager.cpp +++ b/td/telegram/PasswordManager.cpp @@ -6,13 +6,17 @@ // #include "td/telegram/PasswordManager.h" +#include "td/telegram/DhCache.h" #include "td/telegram/Global.h" #include "td/telegram/logevent/LogEvent.h" #include "td/telegram/net/NetQueryDispatcher.h" #include "td/telegram/SecureStorage.h" +#include "td/mtproto/crypto.h" + #include "td/utils/buffer.h" #include "td/utils/crypto.h" +#include "td/utils/format.h" #include "td/utils/logging.h" #include "td/utils/Random.h" #include "td/utils/Slice.h" @@ -34,20 +38,104 @@ static void hash_sha256(Slice data, Slice salt, MutableSlice dest) { } BufferSlice PasswordManager::calc_password_hash(Slice password, Slice client_salt, Slice server_salt) { - if (password.empty()) { - return BufferSlice(); - } - BufferSlice buf(32); hash_sha256(password, client_salt, buf.as_slice()); hash_sha256(buf.as_slice(), server_salt, buf.as_slice()); BufferSlice hash(64); pbkdf2_sha512(buf.as_slice(), client_salt, 100000, hash.as_slice()); - return hash; + hash_sha256(hash.as_slice(), server_salt, buf.as_slice()); + return buf; } -BufferSlice PasswordManager::calc_password_hash(Slice password, const PasswordState &state) const { - return calc_password_hash(password, state.current_client_salt, state.current_server_salt); +Result PasswordManager::calc_password_srp_hash(Slice password, Slice client_salt, Slice server_salt, + int32 g, Slice p) { + TRY_STATUS(DhHandshake::check_config(g, p, DhCache::instance())); + + auto hash = calc_password_hash(password, client_salt, server_salt); + auto p_bn = BigNum::from_binary(p); + BigNum g_bn; + g_bn.set_value(g); + auto x_bn = BigNum::from_binary(hash.as_slice()); + + BigNumContext ctx; + BigNum v_bn; + BigNum::mod_exp(v_bn, g_bn, x_bn, p_bn, ctx); + + return BufferSlice(v_bn.to_binary(256)); +} + +tl_object_ptr PasswordManager::get_input_check_password( + Slice password, Slice client_salt, Slice server_salt, int32 g, Slice p, Slice B, int64 id) { + if (password.empty()) { + return make_tl_object(); + } + + if (DhHandshake::check_config(g, p, DhCache::instance()).is_error()) { + LOG(ERROR) << "Receive invalid config " << g << " " << format::escaped(p); + return make_tl_object(); + } + + auto p_bn = BigNum::from_binary(p); + auto B_bn = BigNum::from_binary(B); + auto zero = BigNum::from_decimal("0").move_as_ok(); + if (BigNum::compare(zero, B_bn) != -1 || BigNum::compare(B_bn, p_bn) != -1 || B.size() != 256) { + LOG(ERROR) << "Receive invalid value of B(" << B.size() << "): " << B_bn << " " << p_bn; + return make_tl_object(); + } + + BigNum g_bn; + g_bn.set_value(g); + auto g_padded = g_bn.to_binary(256); + + auto x = calc_password_hash(password, client_salt, server_salt); + auto x_bn = BigNum::from_binary(x.as_slice()); + + BufferSlice a(2048 / 8); + Random::secure_bytes(a.as_slice()); + auto a_bn = BigNum::from_binary(a.as_slice()); + + BigNumContext ctx; + BigNum A_bn; + BigNum::mod_exp(A_bn, g_bn, a_bn, p_bn, ctx); + string A = A_bn.to_binary(256); + + string u = sha256(PSLICE() << A << B); + auto u_bn = BigNum::from_binary(u); + string k = sha256(PSLICE() << p << g_padded); + auto k_bn = BigNum::from_binary(k); + + BigNum v_bn; + BigNum::mod_exp(v_bn, g_bn, x_bn, p_bn, ctx); + BigNum kv_bn; + BigNum::mod_mul(kv_bn, k_bn, v_bn, p_bn, ctx); + BigNum t_bn; + BigNum::sub(t_bn, B_bn, kv_bn); + if (BigNum::compare(t_bn, zero) == -1) { + BigNum::add(t_bn, t_bn, p_bn); + } + BigNum exp_bn; + BigNum::mul(exp_bn, u_bn, x_bn, ctx); + BigNum::add(exp_bn, exp_bn, a_bn); + + BigNum S_bn; + BigNum::mod_exp(S_bn, t_bn, exp_bn, p_bn, ctx); + string S = S_bn.to_binary(256); + auto K = sha256(S); + + auto h1 = sha256(p); + auto h2 = sha256(g_padded); + for (size_t i = 0; i < h1.size(); i++) { + h1[i] = static_cast(static_cast(h1[i]) ^ static_cast(h2[i])); + } + auto M = sha256(PSLICE() << h1 << sha256(client_salt) << sha256(server_salt) << A << B << K); + + return make_tl_object(id, BufferSlice(A), BufferSlice(M)); +} + +tl_object_ptr PasswordManager::get_input_check_password( + Slice password, const PasswordState &state) const { + return get_input_check_password(password, state.current_client_salt, state.current_server_salt, state.current_srp_g, + state.current_srp_p, state.current_srp_B, state.current_srp_id); } void PasswordManager::set_password(string current_password, string new_password, string new_hint, @@ -164,7 +252,7 @@ void PasswordManager::drop_temp_password() { void PasswordManager::do_create_temp_password(string password, int32 timeout, PasswordState &&password_state, Promise promise) { - auto hash = calc_password_hash(password, password_state); + auto hash = get_input_check_password(password, password_state); send_with_promise( G()->net_query_creator().create(create_storer(telegram_api::account_getTmpPassword(std::move(hash), timeout))), PromiseCreator::lambda([promise = std::move(promise)](Result r_query) mutable { @@ -235,7 +323,7 @@ Result PasswordManager::decrypt_secure_secret( } void PasswordManager::do_get_full_state(string password, PasswordState state, Promise promise) { - auto hash = calc_password_hash(password, state); + auto hash = get_input_check_password(password, state); send_with_promise( G()->net_query_creator().create(create_storer(telegram_api::account_getPasswordSettings(std::move(hash)))), PromiseCreator::lambda( @@ -390,10 +478,16 @@ void PasswordManager::do_update_password_settings(UpdateSettings update_settings if (!update_settings.new_password.empty()) { auto new_client_salt = create_salt(state.new_client_salt); - new_settings->new_password_hash_ = - calc_password_hash(update_settings.new_password, new_client_salt.as_slice(), state.new_server_salt); - new_settings->new_algo_ = make_tl_object( - std::move(new_client_salt), BufferSlice(state.new_server_salt)); + auto new_hash = calc_password_srp_hash(update_settings.new_password, new_client_salt.as_slice(), + state.new_server_salt, state.new_srp_g, state.new_srp_p); + if (new_hash.is_error()) { + return promise.set_error(Status::Error(400, "Unable to change password, because it may be unsafe")); + } + new_settings->new_password_hash_ = new_hash.move_as_ok(); + new_settings->new_algo_ = + make_tl_object( + std::move(new_client_salt), BufferSlice(state.new_server_salt), state.new_srp_g, + BufferSlice(state.new_srp_p)); new_settings->hint_ = std::move(update_settings.new_hint); if (private_state.secret) { update_settings.update_secure_secret = true; @@ -429,10 +523,7 @@ void PasswordManager::do_update_password_settings(UpdateSettings update_settings new_settings->flags_ |= telegram_api::account_passwordInputSettings::EMAIL_MASK; new_settings->email_ = std::move(update_settings.recovery_email_address); } - BufferSlice current_hash; - if (state.has_password) { - current_hash = calc_password_hash(update_settings.current_password, state); - } + auto current_hash = get_input_check_password(state.has_password ? update_settings.current_password : Slice(), state); auto query = G()->net_query_creator().create( create_storer(telegram_api::account_updatePasswordSettings(std::move(current_hash), std::move(new_settings)))); @@ -477,16 +568,21 @@ void PasswordManager::do_get_state(Promise promise) { switch (password->current_algo_->get_id()) { case telegram_api::passwordKdfAlgoUnknown::ID: return promise.set_error(Status::Error(400, "Please update client to continue")); - case telegram_api::passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000::ID: { - auto algo = move_tl_object_as( - password->current_algo_); + case telegram_api::passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow::ID: { + auto algo = + move_tl_object_as( + password->current_algo_); state.current_client_salt = algo->salt1_.as_slice().str(); state.current_server_salt = algo->salt2_.as_slice().str(); + state.current_srp_g = algo->g_; + state.current_srp_p = algo->p_.as_slice().str(); break; } default: UNREACHABLE(); } + state.current_srp_B = password->srp_B_.as_slice().str(); + state.current_srp_id = password->srp_id_; state.password_hint = std::move(password->hint_); state.has_recovery_email_address = (password->flags_ & telegram_api::account_password::HAS_RECOVERY_MASK) != 0; @@ -500,11 +596,14 @@ void PasswordManager::do_get_state(Promise promise) { switch (password->new_algo_->get_id()) { case telegram_api::passwordKdfAlgoUnknown::ID: return promise.set_error(Status::Error(400, "Please update client to continue")); - case telegram_api::passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000::ID: { - auto algo = move_tl_object_as( - password->new_algo_); + case telegram_api::passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow::ID: { + auto algo = + move_tl_object_as( + password->new_algo_); state.new_client_salt = algo->salt1_.as_slice().str(); state.new_server_salt = algo->salt2_.as_slice().str(); + state.new_srp_g = algo->g_; + state.new_srp_p = algo->p_.as_slice().str(); break; } default: diff --git a/td/telegram/PasswordManager.h b/td/telegram/PasswordManager.h index a9899779..292b8dee 100644 --- a/td/telegram/PasswordManager.h +++ b/td/telegram/PasswordManager.h @@ -56,7 +56,9 @@ class PasswordManager : public NetQueryCallback { explicit PasswordManager(ActorShared<> parent) : parent_(std::move(parent)) { } - static BufferSlice calc_password_hash(Slice password, Slice client_salt, Slice server_salt); + static tl_object_ptr get_input_check_password(Slice password, Slice client_salt, + Slice server_salt, int32 g, + Slice p, Slice B, int64 id); void get_state(Promise promise); void set_password(string current_password, string new_password, string new_hint, bool set_recovery_email_address, @@ -96,8 +98,14 @@ class PasswordManager : public NetQueryCallback { string current_client_salt; string current_server_salt; + int32 current_srp_g; + string current_srp_p; + string current_srp_B; + int64 current_srp_id; string new_client_salt; string new_server_salt; + int32 new_srp_g; + string new_srp_p; string new_secure_salt; @@ -139,7 +147,13 @@ class PasswordManager : public NetQueryCallback { static Result decrypt_secure_secret( Slice password, tl_object_ptr algo_ptr, Slice secret, int64 secret_id); - BufferSlice calc_password_hash(Slice password, const PasswordState &state) const; + static BufferSlice calc_password_hash(Slice password, Slice client_salt, Slice server_salt); + + static Result calc_password_srp_hash(Slice password, Slice client_salt, Slice server_salt, int32 g, + Slice p); + + tl_object_ptr get_input_check_password(Slice password, + const PasswordState &state) const; void update_password_settings(UpdateSettings update_settings, Promise promise); void do_update_password_settings(UpdateSettings update_settings, PasswordFullState full_state, Promise promise); diff --git a/td/telegram/cli.cpp b/td/telegram/cli.cpp index 48ab556b..b47c4a9b 100644 --- a/td/telegram/cli.cpp +++ b/td/telegram/cli.cpp @@ -1766,8 +1766,8 @@ class CliClient final : public Actor { vector> strings; strings.push_back(make_tl_object(key, "Ordinary value")); - strings.push_back(make_tl_object("Plu", "Zero", string("One\0One", 7), "Two", "Few", - "Many", "Other")); + strings.push_back(make_tl_object("Plu", "Zero", string("One\0One", 7), + "Two", "Few", "Many", "Other")); strings.push_back(make_tl_object("DELETED")); send_request(make_tl_object( diff --git a/td/telegram/net/MtprotoHeader.cpp b/td/telegram/net/MtprotoHeader.cpp index 5eb78c0e..31ed973b 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 = 83; + constexpr int32 LAYER = 84; using td::store; // invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;