Update layer to 84.

GitOrigin-RevId: a2057596f00201ee967e7d038e048b618f6e629d
This commit is contained in:
levlam 2018-08-10 16:22:48 +03:00
parent 6c1408565e
commit 00fabb8f97
9 changed files with 179 additions and 38 deletions

View File

@ -468,7 +468,7 @@ authorization#7bf2e6f6 hash:long flags:int device_model:string platform:string s
account.authorizations#1250abde authorizations:Vector<Authorization> = 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;

Binary file not shown.

View File

@ -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<telegram_api::passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000>(
case telegram_api::passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow::ID: {
auto algo = move_tl_object_as<telegram_api::passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow>(
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;

View File

@ -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_;

View File

@ -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<int32>(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<int32>(state_)));

View File

@ -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<BufferSlice> 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<telegram_api::InputCheckPasswordSRP> 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<telegram_api::inputCheckPasswordEmpty>();
}
if (DhHandshake::check_config(g, p, DhCache::instance()).is_error()) {
LOG(ERROR) << "Receive invalid config " << g << " " << format::escaped(p);
return make_tl_object<telegram_api::inputCheckPasswordEmpty>();
}
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<telegram_api::inputCheckPasswordEmpty>();
}
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<char>(static_cast<unsigned char>(h1[i]) ^ static_cast<unsigned char>(h2[i]));
}
auto M = sha256(PSLICE() << h1 << sha256(client_salt) << sha256(server_salt) << A << B << K);
return make_tl_object<telegram_api::inputCheckPasswordSRP>(id, BufferSlice(A), BufferSlice(M));
}
tl_object_ptr<telegram_api::InputCheckPasswordSRP> 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<TempPasswordState> 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<NetQueryPtr> r_query) mutable {
@ -235,7 +323,7 @@ Result<secure_storage::Secret> PasswordManager::decrypt_secure_secret(
}
void PasswordManager::do_get_full_state(string password, PasswordState state, Promise<PasswordFullState> 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<telegram_api::passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000>(
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<telegram_api::passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow>(
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<PasswordState> 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<telegram_api::passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000>(
password->current_algo_);
case telegram_api::passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow::ID: {
auto algo =
move_tl_object_as<telegram_api::passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow>(
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<PasswordState> 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<telegram_api::passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000>(
password->new_algo_);
case telegram_api::passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow::ID: {
auto algo =
move_tl_object_as<telegram_api::passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow>(
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:

View File

@ -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<telegram_api::InputCheckPasswordSRP> get_input_check_password(Slice password, Slice client_salt,
Slice server_salt, int32 g,
Slice p, Slice B, int64 id);
void get_state(Promise<State> 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<secure_storage::Secret> decrypt_secure_secret(
Slice password, tl_object_ptr<telegram_api::SecurePasswordKdfAlgo> 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<BufferSlice> calc_password_srp_hash(Slice password, Slice client_salt, Slice server_salt, int32 g,
Slice p);
tl_object_ptr<telegram_api::InputCheckPasswordSRP> get_input_check_password(Slice password,
const PasswordState &state) const;
void update_password_settings(UpdateSettings update_settings, Promise<State> promise);
void do_update_password_settings(UpdateSettings update_settings, PasswordFullState full_state, Promise<bool> promise);

View File

@ -1766,8 +1766,8 @@ class CliClient final : public Actor {
vector<tl_object_ptr<td_api::LanguagePackString>> strings;
strings.push_back(make_tl_object<td_api::languagePackStringValue>(key, "Ordinary value"));
strings.push_back(make_tl_object<td_api::languagePackStringPluralized>("Plu", "Zero", string("One\0One", 7), "Two", "Few",
"Many", "Other"));
strings.push_back(make_tl_object<td_api::languagePackStringPluralized>("Plu", "Zero", string("One\0One", 7),
"Two", "Few", "Many", "Other"));
strings.push_back(make_tl_object<td_api::languagePackStringDeleted>("DELETED"));
send_request(make_tl_object<td_api::setCustomLanguage>(

View File

@ -21,7 +21,7 @@ class HeaderStorer {
}
template <class StorerT>
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;