Update layer to 83.

GitOrigin-RevId: 2220a0399ed081441fefc06069b06efa11b8447b
This commit is contained in:
levlam 2018-08-04 09:55:49 +03:00
parent 7f62960ad3
commit c9c2760173
14 changed files with 332 additions and 184 deletions

View File

@ -468,12 +468,11 @@ authorization#7bf2e6f6 hash:long flags:int device_model:string platform:string s
account.authorizations#1250abde authorizations:Vector<Authorization> = account.Authorizations;
account.noPassword#5ea182f6 new_salt:bytes new_secure_salt:bytes secure_random:bytes email_unconfirmed_pattern:string = account.Password;
account.password#ca39b447 flags:# has_recovery:flags.0?true has_secure_values:flags.1?true current_salt:bytes new_salt:bytes new_secure_salt:bytes secure_random:bytes hint:string email_unconfirmed_pattern:string = account.Password;
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.passwordSettings#7bd9c3f1 email:string secure_salt:bytes secure_secret:bytes secure_secret_id:long = account.PasswordSettings;
account.passwordSettings#9a5c33e5 flags:# email:flags.0?string secure_settings:flags.1?SecureSecretSettings = account.PasswordSettings;
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;
account.passwordInputSettings#c23727c9 flags:# new_algo:flags.0?PasswordKdfAlgo new_password_hash:flags.0?bytes hint:flags.0?string email:flags.1?string new_secure_settings:flags.2?SecureSecretSettings = account.PasswordInputSettings;
auth.passwordRecovery#137948a5 email_pattern:string = auth.PasswordRecovery;
@ -900,6 +899,15 @@ 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;
securePasswordKdfAlgoUnknown#4a8537 = SecurePasswordKdfAlgo;
securePasswordKdfAlgoPBKDF2HMACSHA512iter100000#bbf2dda0 salt:bytes = SecurePasswordKdfAlgo;
securePasswordKdfAlgoSHA512#86471d92 salt:bytes = SecurePasswordKdfAlgo;
secureSecretSettings#1527bcac secure_algo:SecurePasswordKdfAlgo secure_secret:bytes secure_secret_id:long = SecureSecretSettings;
---functions---
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;

Binary file not shown.

View File

@ -14,14 +14,14 @@
#include "td/telegram/ConfigShared.h"
#include "td/telegram/ContactsManager.h"
#include "td/telegram/Global.h"
#include "td/telegram/logevent/LogEvent.h"
#include "td/telegram/misc.h"
#include "td/telegram/net/DcId.h"
#include "td/telegram/net/NetQueryDispatcher.h"
#include "td/telegram/PasswordManager.h"
#include "td/telegram/Td.h"
#include "td/telegram/UpdatesManager.h"
#include "td/telegram/logevent/LogEvent.h"
#include "td/actor/PromiseFuture.h"
#include "td/utils/buffer.h"
@ -613,13 +613,13 @@ void AuthManager::check_password(uint64 query_id, string password) {
if (state_ != State::WaitPassword) {
return on_query_error(query_id, Status::Error(8, "checkAuthenticationPassword unexpected"));
}
BufferSlice buf(32);
password = wait_password_state_.current_salt_ + password + wait_password_state_.current_salt_;
sha256(password, buf.as_slice());
auto hash = PasswordManager::calc_password_hash(password, wait_password_state_.current_client_salt_,
wait_password_state_.current_server_salt_);
on_new_query(query_id);
start_net_query(NetQueryType::CheckPassword,
G()->net_query_creator().create(create_storer(telegram_api::auth_checkPassword(std::move(buf))),
G()->net_query_creator().create(create_storer(telegram_api::auth_checkPassword(std::move(hash))),
DcId::main(), NetQuery::Type::Common, NetQuery::AuthFlag::Off));
}
@ -748,18 +748,30 @@ void AuthManager::on_get_password_result(NetQueryPtr &result) {
return on_query_error(r_password.move_as_error());
}
auto password = r_password.move_as_ok();
wait_password_state_ = WaitPasswordState();
if (password->get_id() == telegram_api::account_noPassword::ID) {
auto no_password = move_tl_object_as<telegram_api::account_noPassword>(password);
wait_password_state_.new_salt_ = no_password->new_salt_.as_slice().str();
if (password->current_algo_ != nullptr) {
switch (password->current_algo_->get_id()) {
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>(
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_.hint_ = std::move(password->hint_);
wait_password_state_.has_recovery_ =
(password->flags_ & telegram_api::account_password::HAS_RECOVERY_MASK) != 0;
break;
}
default:
UNREACHABLE();
}
} else {
CHECK(password->get_id() == telegram_api::account_password::ID);
auto password_info = move_tl_object_as<telegram_api::account_password>(password);
wait_password_state_.current_salt_ = password_info->current_salt_.as_slice().str();
wait_password_state_.new_salt_ = password_info->new_salt_.as_slice().str();
wait_password_state_.hint_ = password_info->hint_;
wait_password_state_.has_recovery_ = password_info->has_recovery_;
// TODO we need to resend auth_signIn instead of going to WaitPassword state
}
update_state(State::WaitPassword);
on_query_ok();
}
@ -771,7 +783,7 @@ void AuthManager::on_request_password_recovery_result(NetQueryPtr &result) {
}
auto email_address_pattern = r_email_address_pattern.move_as_ok();
CHECK(email_address_pattern->get_id() == telegram_api::auth_passwordRecovery::ID);
wait_password_state_.email_address_pattern_ = email_address_pattern->email_pattern_;
wait_password_state_.email_address_pattern_ = std::move(email_address_pattern->email_pattern_);
update_state(State::WaitPassword, true);
on_query_ok();
}

View File

@ -196,10 +196,10 @@ class AuthManager : public NetActor {
};
struct WaitPasswordState {
string current_salt_;
string new_salt_;
string current_client_salt_;
string current_server_salt_;
string hint_;
bool has_recovery_;
bool has_recovery_ = false;
string email_address_pattern_;
template <class T>

View File

@ -57,8 +57,8 @@ void SendCodeHelper::parse(T &parser) {
template <class T>
void AuthManager::WaitPasswordState::store(T &storer) const {
using td::store;
store(current_salt_, storer);
store(new_salt_, storer);
store(current_client_salt_, storer);
store(current_server_salt_, storer);
store(hint_, storer);
store(has_recovery_, storer);
store(email_address_pattern_, storer);
@ -67,8 +67,8 @@ void AuthManager::WaitPasswordState::store(T &storer) const {
template <class T>
void AuthManager::WaitPasswordState::parse(T &parser) {
using td::parse;
parse(current_salt_, parser);
parse(new_salt_, parser);
parse(current_client_salt_, parser);
parse(current_server_salt_, parser);
parse(hint_, parser);
parse(has_recovery_, parser);
parse(email_address_pattern_, parser);
@ -78,8 +78,10 @@ template <class T>
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;
BEGIN_STORE_FLAGS();
STORE_FLAG(has_terms_of_service);
STORE_FLAG(is_pbkdf2_supported);
END_STORE_FLAGS();
store(state_, storer);
store(api_id_, storer);
@ -103,9 +105,11 @@ template <class T>
void AuthManager::DbState::parse(T &parser) {
using td::parse;
bool has_terms_of_service = false;
bool is_pbkdf2_supported = false;
if (parser.version() >= static_cast<int32>(Version::AddTermsOfService)) {
BEGIN_PARSE_FLAGS();
PARSE_FLAG(has_terms_of_service);
PARSE_FLAG(is_pbkdf2_supported);
END_PARSE_FLAGS();
}
parse(state_, parser);
@ -120,9 +124,12 @@ void AuthManager::DbState::parse(T &parser) {
if (state_ == State::WaitCode) {
parse(send_code_helper_, parser);
if (parser.version() < static_cast<int32>(Version::AddTermsOfService)) {
parser.set_error("Have no terms of service");
return parser.set_error("Have no terms of service");
}
} else if (state_ == State::WaitPassword) {
if (!is_pbkdf2_supported) {
return parser.set_error("Need PBKDF2 support");
}
parse(wait_password_state_, parser);
} else {
parser.set_error(PSTRING() << "Unexpected " << tag("state", static_cast<int32>(state_)));

View File

@ -9,6 +9,7 @@
#include "td/telegram/Global.h"
#include "td/telegram/logevent/LogEvent.h"
#include "td/telegram/net/NetQueryDispatcher.h"
#include "td/telegram/SecureStorage.h"
#include "td/utils/buffer.h"
#include "td/utils/crypto.h"
@ -28,14 +29,25 @@ tl_object_ptr<td_api::temporaryPasswordState> TempPasswordState::as_td_api() con
return make_tl_object<td_api::temporaryPasswordState>(true, valid_until - G()->unix_time_cached());
}
static BufferSlice calc_password_hash(const string &password, const string &salt) {
static void hash_sha256(Slice data, Slice salt, MutableSlice dest) {
sha256(PSLICE() << salt << data << salt, dest);
}
BufferSlice PasswordManager::calc_password_hash(Slice password, Slice client_salt, Slice server_salt) {
if (password.empty()) {
return BufferSlice();
}
BufferSlice buf(32);
string salted_password = salt + password + salt;
sha256(salted_password, buf.as_slice());
return buf;
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;
}
BufferSlice PasswordManager::calc_password_hash(Slice password, const PasswordState &state) const {
return calc_password_hash(password, state.current_client_salt, state.current_server_salt);
}
void PasswordManager::set_password(string current_password, string new_password, string new_hint,
@ -152,20 +164,21 @@ void PasswordManager::drop_temp_password() {
void PasswordManager::do_create_temp_password(string password, int32 timeout, PasswordState &&password_state,
Promise<TempPasswordState> promise) {
send_with_promise(G()->net_query_creator().create(create_storer(telegram_api::account_getTmpPassword(
calc_password_hash(password, password_state.current_salt), timeout))),
PromiseCreator::lambda([promise = std::move(promise)](Result<NetQueryPtr> r_query) mutable {
auto r_result = fetch_result<telegram_api::account_getTmpPassword>(std::move(r_query));
if (r_result.is_error()) {
return promise.set_error(r_result.move_as_error());
}
auto result = r_result.move_as_ok();
TempPasswordState res;
res.has_temp_password = true;
res.temp_password = result->tmp_password_.as_slice().str();
res.valid_until = result->valid_until_;
promise.set_value(std::move(res));
}));
auto hash = calc_password_hash(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 {
auto r_result = fetch_result<telegram_api::account_getTmpPassword>(std::move(r_query));
if (r_result.is_error()) {
return promise.set_error(r_result.move_as_error());
}
auto result = r_result.move_as_ok();
TempPasswordState res;
res.has_temp_password = true;
res.temp_password = result->tmp_password_.as_slice().str();
res.valid_until = result->valid_until_;
promise.set_value(std::move(res));
}));
}
void PasswordManager::on_finish_create_temp_password(Result<TempPasswordState> result, bool /*dummy*/) {
@ -190,32 +203,60 @@ void PasswordManager::get_full_state(string password, Promise<PasswordFullState>
}));
}
Result<secure_storage::Secret> PasswordManager::decrypt_secure_secret(
Slice password, tl_object_ptr<telegram_api::SecurePasswordKdfAlgo> algo_ptr, Slice secret, int64 secret_id) {
TRY_RESULT(encrypted_secret, secure_storage::EncryptedSecret::create(secret));
CHECK(algo_ptr != nullptr);
BufferSlice salt;
secure_storage::EnryptionAlgorithm algorithm = secure_storage::EnryptionAlgorithm::Pbkdf2;
switch (algo_ptr->get_id()) {
case telegram_api::securePasswordKdfAlgoUnknown::ID:
return Status::Error(400, "Unsupported algorithm");
case telegram_api::securePasswordKdfAlgoSHA512::ID: {
auto algo = move_tl_object_as<telegram_api::securePasswordKdfAlgoSHA512>(algo_ptr);
salt = std::move(algo->salt_);
algorithm = secure_storage::EnryptionAlgorithm::Sha512;
break;
}
case telegram_api::securePasswordKdfAlgoPBKDF2HMACSHA512iter100000::ID: {
auto algo = move_tl_object_as<telegram_api::securePasswordKdfAlgoPBKDF2HMACSHA512iter100000>(algo_ptr);
salt = std::move(algo->salt_);
break;
}
default:
UNREACHABLE();
}
TRY_RESULT(result, encrypted_secret.decrypt(password, salt.as_slice(), algorithm));
if (secret_id != result.get_hash()) {
return Status::Error("Secret hash mismatch");
}
return result;
}
void PasswordManager::do_get_full_state(string password, PasswordState state, Promise<PasswordFullState> promise) {
auto current_salt = state.current_salt;
auto hash = calc_password_hash(password, state);
send_with_promise(
G()->net_query_creator().create(
create_storer(telegram_api::account_getPasswordSettings(calc_password_hash(password, current_salt)))),
PromiseCreator::lambda([promise = std::move(promise), state = std::move(state),
password](Result<NetQueryPtr> r_query) mutable {
promise.set_result([&]() -> Result<PasswordFullState> {
TRY_RESULT(result, fetch_result<telegram_api::account_getPasswordSettings>(std::move(r_query)));
PasswordPrivateState private_state;
private_state.email = result->email_;
G()->net_query_creator().create(create_storer(telegram_api::account_getPasswordSettings(std::move(hash)))),
PromiseCreator::lambda(
[promise = std::move(promise), state = std::move(state), password](Result<NetQueryPtr> r_query) mutable {
promise.set_result([&]() -> Result<PasswordFullState> {
TRY_RESULT(result, fetch_result<telegram_api::account_getPasswordSettings>(std::move(r_query)));
PasswordPrivateState private_state;
private_state.email = std::move(result->email_);
auto r_secret = [&]() -> Result<secure_storage::Secret> {
TRY_RESULT(encrypted_secret, secure_storage::EncryptedSecret::create(result->secure_secret_.as_slice()));
auto r_secret = encrypted_secret.decrypt(PSLICE() << result->secure_salt_.as_slice() << password
<< result->secure_salt_.as_slice());
if (r_secret.is_ok() && result->secure_secret_id_ != r_secret.ok().get_hash()) {
return Status::Error("Secret hash mismatch");
}
return r_secret;
}();
if (result->secure_settings_ != nullptr) {
auto r_secret = decrypt_secure_secret(password, std::move(result->secure_settings_->secure_algo_),
result->secure_settings_->secure_secret_.as_slice(),
result->secure_settings_->secure_secret_id_);
if (r_secret.is_ok()) {
private_state.secret = r_secret.move_as_ok();
}
}
private_state.secret = std::move(r_secret);
return PasswordFullState{std::move(state), std::move(private_state)};
}());
}));
return PasswordFullState{std::move(state), std::move(private_state)};
}());
}));
}
void PasswordManager::get_recovery_email_address(string password,
@ -329,15 +370,13 @@ void PasswordManager::update_password_settings(UpdateSettings update_settings, P
}));
}
namespace {
BufferSlice create_salt(Slice server_salt) {
static BufferSlice create_salt(Slice salt_prefix) {
static constexpr size_t ADDED_SALT_SIZE = 32;
BufferSlice new_salt(server_salt.size() + ADDED_SALT_SIZE);
new_salt.as_slice().copy_from(server_salt);
Random::secure_bytes(new_salt.as_slice().substr(server_salt.size()));
BufferSlice new_salt(salt_prefix.size() + ADDED_SALT_SIZE);
new_salt.as_slice().copy_from(salt_prefix);
Random::secure_bytes(new_salt.as_slice().substr(salt_prefix.size()));
return new_salt;
}
} // namespace
void PasswordManager::do_update_password_settings(UpdateSettings update_settings, PasswordFullState full_state,
Promise<bool> promise) {
@ -346,18 +385,21 @@ void PasswordManager::do_update_password_settings(UpdateSettings update_settings
auto new_settings = make_tl_object<telegram_api::account_passwordInputSettings>();
if (update_settings.update_password) {
new_settings->flags_ |= telegram_api::account_passwordInputSettings::NEW_PASSWORD_HASH_MASK;
new_settings->flags_ |= telegram_api::account_passwordInputSettings::NEW_SALT_MASK;
new_settings->flags_ |= telegram_api::account_passwordInputSettings::NEW_ALGO_MASK;
new_settings->flags_ |= telegram_api::account_passwordInputSettings::HINT_MASK;
if (!update_settings.new_password.empty()) {
auto new_salt = create_salt(state.new_salt);
auto new_client_salt = create_salt(state.new_client_salt);
new_settings->new_salt_ = std::move(new_salt);
new_settings->new_password_hash_ =
calc_password_hash(update_settings.new_password, new_settings->new_salt_.as_slice().str());
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));
new_settings->hint_ = std::move(update_settings.new_hint);
if (private_state.secret) {
update_settings.update_secure_secret = true;
}
} else {
new_settings->new_algo_ = make_tl_object<telegram_api::passwordKdfAlgoUnknown>();
}
}
@ -372,24 +414,16 @@ void PasswordManager::do_update_password_settings(UpdateSettings update_settings
}
if (update_settings.update_secure_secret) {
new_settings->flags_ |= telegram_api::account_passwordInputSettings::NEW_SECURE_SECRET_ID_MASK;
new_settings->flags_ |= telegram_api::account_passwordInputSettings::NEW_SECURE_SALT_MASK;
new_settings->flags_ |= telegram_api::account_passwordInputSettings::NEW_SECURE_SECRET_MASK;
auto secret = [&]() {
if (private_state.secret) {
return std::move(private_state.secret.value());
}
return secure_storage::Secret::create_new();
}();
auto new_secure_salt = create_salt(state.new_secure_salt);
auto secret = private_state.secret ? std::move(private_state.secret.value()) : secure_storage::Secret::create_new();
auto algorithm = make_tl_object<telegram_api::securePasswordKdfAlgoPBKDF2HMACSHA512iter100000>(
create_salt(state.new_secure_salt));
auto encrypted_secret = secret.encrypt(
PSLICE() << new_secure_salt.as_slice()
<< (update_settings.update_password ? update_settings.new_password : update_settings.current_password)
<< new_secure_salt.as_slice());
update_settings.update_password ? update_settings.new_password : update_settings.current_password,
algorithm->salt_.as_slice(), secure_storage::EnryptionAlgorithm::Pbkdf2);
new_settings->new_secure_salt_ = std::move(new_secure_salt);
new_settings->new_secure_secret_ = BufferSlice(encrypted_secret.as_slice());
new_settings->new_secure_secret_id_ = secret.get_hash();
new_settings->flags_ |= telegram_api::account_passwordInputSettings::NEW_SECURE_SETTINGS_MASK;
new_settings->new_secure_settings_ = make_tl_object<telegram_api::secureSecretSettings>(
std::move(algorithm), BufferSlice(encrypted_secret.as_slice()), secret.get_hash());
}
if (update_settings.update_recovery_email_address) {
new_settings->flags_ |= telegram_api::account_passwordInputSettings::EMAIL_MASK;
@ -397,7 +431,7 @@ void PasswordManager::do_update_password_settings(UpdateSettings update_settings
}
BufferSlice current_hash;
if (state.has_password) {
current_hash = calc_password_hash(update_settings.current_password, state.current_salt);
current_hash = calc_password_hash(update_settings.current_password, state);
}
auto query = G()->net_query_creator().create(
create_storer(telegram_api::account_updatePasswordSettings(std::move(current_hash), std::move(new_settings))));
@ -426,52 +460,81 @@ void PasswordManager::get_state(Promise<State> promise) {
void PasswordManager::do_get_state(Promise<PasswordState> promise) {
auto query = G()->net_query_creator().create(create_storer(telegram_api::account_getPassword()));
send_with_promise(std::move(query), PromiseCreator::lambda([actor_id = actor_id(this), promise = std::move(promise)](
Result<NetQueryPtr> r_query) mutable {
auto r_result = fetch_result<telegram_api::account_getPassword>(std::move(r_query));
if (r_result.is_error()) {
return promise.set_error(r_result.move_as_error());
}
auto result = r_result.move_as_ok();
send_with_promise(
std::move(query), PromiseCreator::lambda([actor_id = actor_id(this),
promise = std::move(promise)](Result<NetQueryPtr> r_query) mutable {
auto r_result = fetch_result<telegram_api::account_getPassword>(std::move(r_query));
if (r_result.is_error()) {
return promise.set_error(r_result.move_as_error());
}
auto password = r_result.move_as_ok();
Random::add_seed(password->secure_random_.as_slice());
PasswordState state;
string secure_random;
if (result->get_id() == telegram_api::account_noPassword::ID) {
auto no_password = move_tl_object_as<telegram_api::account_noPassword>(result);
state.has_password = false;
state.password_hint = "";
state.current_salt = "";
state.new_salt = no_password->new_salt_.as_slice().str();
state.new_secure_salt = no_password->new_secure_salt_.as_slice().str();
secure_random = no_password->secure_random_.as_slice().str();
state.has_recovery_email_address = false;
state.has_secure_values = false;
state.unconfirmed_recovery_email_address_pattern = no_password->email_unconfirmed_pattern_;
} else if (result->get_id() == telegram_api::account_password::ID) {
auto password = move_tl_object_as<telegram_api::account_password>(result);
state.has_password = true;
state.password_hint = password->hint_;
state.current_salt = password->current_salt_.as_slice().str();
state.new_salt = password->new_salt_.as_slice().str();
state.new_secure_salt = password->new_secure_salt_.as_slice().str();
secure_random = password->secure_random_.as_slice().str();
state.has_recovery_email_address =
(password->flags_ & telegram_api::account_password::HAS_RECOVERY_MASK) != 0;
state.has_secure_values =
(password->flags_ & telegram_api::account_password::HAS_SECURE_VALUES_MASK) != 0;
state.unconfirmed_recovery_email_address_pattern = password->email_unconfirmed_pattern_;
} else {
UNREACHABLE();
}
Random::add_seed(secure_random);
if (state.new_secure_salt.size() < MIN_NEW_SECURE_SALT_SIZE) {
return promise.set_error(Status::Error(500, "New secure salt length too small"));
}
if (state.new_salt.size() < MIN_NEW_SALT_SIZE) {
return promise.set_error(Status::Error(500, "New salt length too small"));
}
promise.set_value(std::move(state));
}));
PasswordState state;
if (password->current_algo_ != nullptr) {
state.has_password = true;
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_);
state.current_client_salt = algo->salt1_.as_slice().str();
state.current_server_salt = algo->salt2_.as_slice().str();
break;
}
default:
UNREACHABLE();
}
state.password_hint = std::move(password->hint_);
state.has_recovery_email_address =
(password->flags_ & telegram_api::account_password::HAS_RECOVERY_MASK) != 0;
state.has_secure_values = (password->flags_ & telegram_api::account_password::HAS_SECURE_VALUES_MASK) != 0;
} else {
state.has_password = false;
}
state.unconfirmed_recovery_email_address_pattern = std::move(password->email_unconfirmed_pattern_);
CHECK(password->new_algo_ != nullptr);
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_);
state.new_client_salt = algo->salt1_.as_slice().str();
state.new_server_salt = algo->salt2_.as_slice().str();
break;
}
default:
UNREACHABLE();
}
CHECK(password->new_secure_algo_ != nullptr);
switch (password->new_secure_algo_->get_id()) {
case telegram_api::securePasswordKdfAlgoUnknown::ID:
return promise.set_error(Status::Error(400, "Please update client to continue"));
case telegram_api::securePasswordKdfAlgoSHA512::ID:
return promise.set_error(Status::Error(500, "Server has sent outdated secret encryption mode"));
case telegram_api::securePasswordKdfAlgoPBKDF2HMACSHA512iter100000::ID: {
auto algo = move_tl_object_as<telegram_api::securePasswordKdfAlgoPBKDF2HMACSHA512iter100000>(
password->new_secure_algo_);
state.new_secure_salt = algo->salt_.as_slice().str();
break;
}
default:
UNREACHABLE();
}
if (state.new_secure_salt.size() < MIN_NEW_SECURE_SALT_SIZE) {
return promise.set_error(Status::Error(500, "New secure salt length too small"));
}
if (state.new_client_salt.size() < MIN_NEW_SALT_SIZE) {
return promise.set_error(Status::Error(500, "New salt length too small"));
}
promise.set_value(std::move(state));
}));
}
void PasswordManager::cache_secret(secure_storage::Secret secret) {

View File

@ -9,17 +9,19 @@
#include "td/telegram/net/NetQuery.h"
#include "td/telegram/SecureStorage.h"
#include "td/telegram/td_api.h"
#include "td/telegram/telegram_api.h"
#include "td/actor/actor.h"
#include "td/actor/PromiseFuture.h"
#include "td/utils/Container.h"
#include "td/utils/logging.h"
#include "td/utils/optional.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include "td/utils/tl_helpers.h"
#include "td/telegram/td_api.h"
namespace td {
struct TempPasswordState {
@ -54,6 +56,8 @@ 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);
void get_state(Promise<State> promise);
void set_password(string current_password, string new_password, string new_hint, bool set_recovery_email_address,
string recovery_email_address, Promise<State> promise);
@ -88,10 +92,12 @@ class PasswordManager : public NetQueryCallback {
string password_hint;
bool has_recovery_email_address = false;
bool has_secure_values = false;
string unconfirmed_recovery_email_address_pattern = "";
string unconfirmed_recovery_email_address_pattern;
string current_salt;
string new_salt;
string current_client_salt;
string current_server_salt;
string new_client_salt;
string new_server_salt;
string new_secure_salt;
@ -130,6 +136,11 @@ class PasswordManager : public NetQueryCallback {
string last_verified_email_address_;
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;
void update_password_settings(UpdateSettings update_settings, Promise<State> promise);
void do_update_password_settings(UpdateSettings update_settings, PasswordFullState full_state, Promise<bool> promise);
void do_get_state(Promise<PasswordState> promise);

View File

@ -859,7 +859,6 @@ Result<std::tuple<uint64, BufferSlice, int32>> SecretChatActor::decrypt(BufferSl
}
case mtproto::Transport::ReadResult::Nop: {
return Status::Error("Got nop instead of a message");
break;
}
case mtproto::Transport::ReadResult::Packet: {
data = read_result.packet();

View File

@ -24,15 +24,27 @@ Result<ValueHash> ValueHash::create(Slice data) {
return ValueHash{hash};
}
AesCbcState calc_aes_cbc_state(Slice seed) {
static AesCbcState calc_aes_cbc_state_hash(Slice hash) {
CHECK(hash.size() == 64);
UInt256 key;
as_slice(key).copy_from(hash.substr(0, 32));
UInt128 iv;
as_slice(iv).copy_from(hash.substr(32, 16));
return AesCbcState{key, iv};
}
AesCbcState calc_aes_cbc_state_pbkdf2(Slice secret, Slice salt) {
UInt<512> hash;
auto hash_slice = as_slice(hash);
pbkdf2_sha512(secret, salt, 100000, hash_slice);
return calc_aes_cbc_state_hash(hash_slice);
}
AesCbcState calc_aes_cbc_state_sha512(Slice seed) {
UInt<512> hash;
auto hash_slice = as_slice(hash);
sha512(seed, hash_slice);
UInt256 key;
as_slice(key).copy_from(hash_slice.substr(0, 32));
UInt128 iv;
as_slice(iv).copy_from(hash_slice.substr(32, 16));
return AesCbcState{key, iv};
return calc_aes_cbc_state_hash(hash_slice);
}
template <class F>
@ -89,9 +101,11 @@ Result<BufferSlice> FileDataView::pread(int64 offset, int64 size) {
BufferSliceDataView::BufferSliceDataView(BufferSlice buffer_slice) : buffer_slice_(std::move(buffer_slice)) {
}
int64 BufferSliceDataView::size() const {
return narrow_cast<int64>(buffer_slice_.size());
}
Result<BufferSlice> BufferSliceDataView::pread(int64 offset, int64 size) {
auto end_offset = size + offset;
if (this->size() < end_offset) {
@ -102,9 +116,11 @@ Result<BufferSlice> BufferSliceDataView::pread(int64 offset, int64 size) {
ConcatDataView::ConcatDataView(DataView &left, DataView &right) : left_(left), right_(right) {
}
int64 ConcatDataView::size() const {
return left_.size() + right_.size();
}
Result<BufferSlice> ConcatDataView::pread(int64 offset, int64 size) {
auto end_offset = size + offset;
if (this->size() < end_offset) {
@ -136,23 +152,20 @@ Result<BufferSlice> ConcatDataView::pread(int64 offset, int64 size) {
return std::move(res);
}
// Password
Password::Password(std::string password) : password_(std::move(password)) {
}
Slice Password::as_slice() const {
return password_;
}
// Secret
namespace {
uint8 secret_checksum(Slice secret) {
static uint8 secret_checksum(Slice secret) {
uint32 sum = 0;
for (uint8 c : secret) {
sum += c;
}
return static_cast<uint8>((255 + 239 - sum % 255) % 255);
}
} // namespace
Result<Secret> Secret::create(Slice secret) {
if (secret.size() != 32) {
@ -194,8 +207,19 @@ Secret Secret::clone() const {
return {secret_, hash_};
}
EncryptedSecret Secret::encrypt(Slice key) {
auto aes_cbc_state = calc_aes_cbc_state(key);
EncryptedSecret Secret::encrypt(Slice key, Slice salt, EnryptionAlgorithm algorithm) {
auto aes_cbc_state = [&]() {
switch (algorithm) {
case EnryptionAlgorithm::Sha512:
return calc_aes_cbc_state_sha512(PSLICE() << salt << key << salt);
case EnryptionAlgorithm::Pbkdf2:
return calc_aes_cbc_state_pbkdf2(key, salt);
default:
UNREACHABLE();
return AesCbcState(UInt256(), UInt128());
}
}();
UInt256 res;
aes_cbc_state.encrypt(as_slice(), td::as_slice(res));
return EncryptedSecret::create(td::as_slice(res)).move_as_ok();
@ -204,7 +228,6 @@ EncryptedSecret Secret::encrypt(Slice key) {
Secret::Secret(UInt256 secret, int64 hash) : secret_(secret), hash_(hash) {
}
//EncryptedSecret
Result<EncryptedSecret> EncryptedSecret::create(Slice encrypted_secret) {
if (encrypted_secret.size() != 32) {
return Status::Error("Wrong encrypted secret size");
@ -213,8 +236,20 @@ Result<EncryptedSecret> EncryptedSecret::create(Slice encrypted_secret) {
td::as_slice(res).copy_from(encrypted_secret);
return EncryptedSecret{res};
}
Result<Secret> EncryptedSecret::decrypt(Slice key) {
auto aes_cbc_state = calc_aes_cbc_state(key);
Result<Secret> EncryptedSecret::decrypt(Slice key, Slice salt, EnryptionAlgorithm algorithm) {
auto aes_cbc_state = [&]() {
switch (algorithm) {
case EnryptionAlgorithm::Sha512:
return calc_aes_cbc_state_sha512(PSLICE() << salt << key << salt);
case EnryptionAlgorithm::Pbkdf2:
return calc_aes_cbc_state_pbkdf2(key, salt);
default:
UNREACHABLE();
return AesCbcState(UInt256(), UInt128());
}
}();
UInt256 res;
aes_cbc_state.decrypt(td::as_slice(encrypted_secret_), td::as_slice(res));
return Secret::create(td::as_slice(res));
@ -227,10 +262,10 @@ Slice EncryptedSecret::as_slice() const {
EncryptedSecret::EncryptedSecret(UInt256 encrypted_secret) : encrypted_secret_(encrypted_secret) {
}
// Decryption
Decryptor::Decryptor(AesCbcState aes_cbc_state) : aes_cbc_state_(std::move(aes_cbc_state)) {
sha256_init(&sha256_state_);
}
Result<BufferSlice> Decryptor::append(BufferSlice data) {
if (data.empty()) {
return BufferSlice();
@ -251,6 +286,7 @@ Result<BufferSlice> Decryptor::append(BufferSlice data) {
}
return std::move(data);
}
Result<ValueHash> Decryptor::finish() {
if (!skipped_prefix_) {
return Status::Error("No data was given");
@ -263,7 +299,6 @@ Result<ValueHash> Decryptor::finish() {
return ValueHash{res};
}
// Encryptor
Encryptor::Encryptor(AesCbcState aes_cbc_state, DataView &data_view)
: aes_cbc_state_(std::move(aes_cbc_state)), data_view_(data_view) {
}
@ -292,14 +327,14 @@ Result<EncryptedValue> encrypt_value(const Secret &secret, Slice data) {
TRY_RESULT(hash, calc_value_hash(full_view));
auto aes_cbc_state = calc_aes_cbc_state(PSLICE() << secret.as_slice() << hash.as_slice());
auto aes_cbc_state = calc_aes_cbc_state_sha512(PSLICE() << secret.as_slice() << hash.as_slice());
Encryptor encryptor(aes_cbc_state, full_view);
TRY_RESULT(encrypted_data, encryptor.pread(0, encryptor.size()));
return EncryptedValue{std::move(encrypted_data), std::move(hash)};
}
Result<BufferSlice> decrypt_value(const Secret &secret, const ValueHash &hash, Slice data) {
auto aes_cbc_state = calc_aes_cbc_state(PSLICE() << secret.as_slice() << hash.as_slice());
auto aes_cbc_state = calc_aes_cbc_state_sha512(PSLICE() << secret.as_slice() << hash.as_slice());
Decryptor decryptor(aes_cbc_state);
TRY_RESULT(decrypted_value, decryptor.append(BufferSlice(data)));
TRY_RESULT(got_hash, decryptor.finish());
@ -321,7 +356,7 @@ Result<ValueHash> encrypt_file(const Secret &secret, std::string src, std::strin
TRY_RESULT(hash, calc_value_hash(full_view));
auto aes_cbc_state = calc_aes_cbc_state(PSLICE() << secret.as_slice() << hash.as_slice());
auto aes_cbc_state = calc_aes_cbc_state_sha512(PSLICE() << secret.as_slice() << hash.as_slice());
Encryptor encryptor(aes_cbc_state, full_view);
TRY_STATUS(
data_view_for_each(encryptor, [&dest_file](BufferSlice bytes) { return dest_file.write(bytes.as_slice()); }));
@ -335,7 +370,7 @@ Status decrypt_file(const Secret &secret, const ValueHash &hash, std::string src
auto src_file_view = FileDataView(src_file, src_file_size);
auto aes_cbc_state = calc_aes_cbc_state(PSLICE() << secret.as_slice() << hash.as_slice());
auto aes_cbc_state = calc_aes_cbc_state_sha512(PSLICE() << secret.as_slice() << hash.as_slice());
Decryptor decryptor(aes_cbc_state);
TRY_STATUS(data_view_for_each(src_file_view, [&decryptor, &dest_file](BufferSlice bytes) {
TRY_RESULT(decrypted_bytes, decryptor.append(std::move(bytes)));
@ -351,5 +386,6 @@ Status decrypt_file(const Secret &secret, const ValueHash &hash, std::string src
return Status::OK();
}
} // namespace secure_storage
} // namespace td

View File

@ -35,7 +35,6 @@ namespace td {
// do_encrypt :: RandomPrefix -> Value -> AesCbcState -> EncryptedValue // async
// encrypt :: (ValueSecret, RandomPrefix, Value) -> (EncryptedValue, ValueHash)
//
//
// To decrypt data:
// ValueSecret, ValueHash, EncryptedValue
// do_decrypt :: EncryptedValue -> AesCbcState -> (RandomPrefix, Value, ValueHash) // async
@ -102,7 +101,8 @@ class ConcatDataView : public DataView {
DataView &right_;
};
AesCbcState calc_aes_cbc_state(Slice seed);
AesCbcState calc_aes_cbc_state_pbkdf2(Slice secret, Slice salt);
AesCbcState calc_aes_cbc_state_sha512(Slice seed);
Result<ValueHash> calc_value_hash(DataView &data_view);
ValueHash calc_value_hash(Slice data);
BufferSlice gen_random_prefix(int64 data_size);
@ -118,13 +118,15 @@ class Password {
class EncryptedSecret;
enum class EnryptionAlgorithm : int32 { Sha512, Pbkdf2 };
class Secret {
public:
static Result<Secret> create(Slice secret);
static Secret create_new();
Slice as_slice() const;
EncryptedSecret encrypt(Slice key);
EncryptedSecret encrypt(Slice key, Slice salt, EnryptionAlgorithm algorithm);
int64 get_hash() const;
Secret clone() const;
@ -142,7 +144,7 @@ class Secret {
class EncryptedSecret {
public:
static Result<EncryptedSecret> create(Slice encrypted_secret);
Result<Secret> decrypt(Slice key);
Result<Secret> decrypt(Slice key, Slice salt, EnryptionAlgorithm algorithm);
Slice as_slice() const;
private:

View File

@ -1088,7 +1088,8 @@ static Result<std::pair<DatedFile, SecureFileCredentials>> decrypt_secure_file(
}
TRY_RESULT(hash, secure_storage::ValueHash::create(secure_file.file_hash));
TRY_RESULT(encrypted_secret, secure_storage::EncryptedSecret::create(secure_file.encrypted_secret));
TRY_RESULT(secret, encrypted_secret.decrypt(PSLICE() << master_secret.as_slice() << hash.as_slice()));
TRY_RESULT(secret, encrypted_secret.decrypt(PSLICE() << master_secret.as_slice() << hash.as_slice(), "",
secure_storage::EnryptionAlgorithm::Sha512));
FileEncryptionKey key{secret};
key.set_value_hash(hash);
file_manager->set_encryption_key(secure_file.file.file_id, std::move(key));
@ -1114,7 +1115,8 @@ static Result<std::pair<string, SecureDataCredentials>> decrypt_secure_data(cons
const EncryptedSecureData &secure_data) {
TRY_RESULT(hash, secure_storage::ValueHash::create(secure_data.hash));
TRY_RESULT(encrypted_secret, secure_storage::EncryptedSecret::create(secure_data.encrypted_secret));
TRY_RESULT(secret, encrypted_secret.decrypt(PSLICE() << master_secret.as_slice() << hash.as_slice()));
TRY_RESULT(secret, encrypted_secret.decrypt(PSLICE() << master_secret.as_slice() << hash.as_slice(), "",
secure_storage::EnryptionAlgorithm::Sha512));
TRY_RESULT(value, secure_storage::decrypt_value(secret, hash, secure_data.data));
return std::make_pair(value.as_slice().str(), SecureDataCredentials{secret.as_slice().str(), hash.as_slice().str()});
}
@ -1206,7 +1208,11 @@ static EncryptedSecureFile encrypt_secure_file(FileManager *file_manager, const
EncryptedSecureFile res;
res.file = file;
res.file_hash = value_hash.as_slice().str();
res.encrypted_secret = secret.encrypt(PSLICE() << master_secret.as_slice() << value_hash.as_slice()).as_slice().str();
res.encrypted_secret = secret
.encrypt(PSLICE() << master_secret.as_slice() << value_hash.as_slice(), "",
secure_storage::EnryptionAlgorithm::Sha512)
.as_slice()
.str();
to_hash.append(res.file_hash);
to_hash.append(secret.as_slice().str());
@ -1236,8 +1242,11 @@ static EncryptedSecureData encrypt_secure_data(const secure_storage::Secret &mas
auto secret = secure_storage::Secret::create_new();
auto encrypted = encrypt_value(secret, data).move_as_ok();
EncryptedSecureData res;
res.encrypted_secret =
secret.encrypt(PSLICE() << master_secret.as_slice() << encrypted.hash.as_slice()).as_slice().str();
res.encrypted_secret = secret
.encrypt(PSLICE() << master_secret.as_slice() << encrypted.hash.as_slice(), "",
secure_storage::EnryptionAlgorithm::Sha512)
.as_slice()
.str();
res.data = encrypted.data.as_slice().str();
res.hash = encrypted.hash.as_slice().str();
to_hash.append(res.hash);

View File

@ -1323,7 +1323,7 @@ class CliClient final : public Actor {
std::tie(password, args) = split(args);
std::tie(passport_element_type, arg) = split(args);
send_request(make_tl_object<td_api::setPassportElement>(
as_input_passport_element(passport_element_type, arg, op == "spds"), password));
as_input_passport_element(passport_element_type, arg, op == "spes"), password));
} else if (op == "dpe") {
string passport_element_type = args;
send_request(make_tl_object<td_api::deletePassportElement>(as_passport_element_type(passport_element_type)));

View File

@ -19,7 +19,7 @@ class HeaderStorer {
}
template <class StorerT>
void store(StorerT &storer) const {
constexpr int32 LAYER = 82;
constexpr int32 LAYER = 83;
using td::store;
// invokeWithLayer#da9b0d0d {X:Type} layer:int query:!X = X;

View File

@ -18,11 +18,11 @@ TEST(SecureStorage, secret) {
using namespace td::secure_storage;
auto secret = Secret::create_new();
std::string key = "cucumber";
auto encrypted_secret = secret.encrypt(key);
auto encrypted_secret = secret.encrypt(key, "", secure_storage::EnryptionAlgorithm::Sha512);
ASSERT_TRUE(encrypted_secret.as_slice() != secret.as_slice());
auto decrypted_secret = encrypted_secret.decrypt(key).ok();
auto decrypted_secret = encrypted_secret.decrypt(key, "", secure_storage::EnryptionAlgorithm::Sha512).ok();
ASSERT_TRUE(secret.as_slice() == decrypted_secret.as_slice());
ASSERT_TRUE(encrypted_secret.decrypt("notcucumber").is_error());
ASSERT_TRUE(encrypted_secret.decrypt("notcucumber", "", secure_storage::EnryptionAlgorithm::Sha512).is_error());
}
TEST(SecureStorage, simple) {
@ -38,10 +38,11 @@ TEST(SecureStorage, simple) {
auto full_value_view = ConcatDataView(prefix_view, value_view);
auto hash = calc_value_hash(full_value_view).move_as_ok();
Encryptor encryptor(calc_aes_cbc_state(PSLICE() << value_secret.as_slice() << hash.as_slice()), full_value_view);
Encryptor encryptor(calc_aes_cbc_state_sha512(PSLICE() << value_secret.as_slice() << hash.as_slice()),
full_value_view);
auto encrypted_value = encryptor.pread(0, encryptor.size()).move_as_ok();
Decryptor decryptor(calc_aes_cbc_state(PSLICE() << value_secret.as_slice() << hash.as_slice()));
Decryptor decryptor(calc_aes_cbc_state_sha512(PSLICE() << value_secret.as_slice() << hash.as_slice()));
auto res = decryptor.append(encrypted_value.copy()).move_as_ok();
auto decrypted_hash = decryptor.finish().ok();
ASSERT_TRUE(decrypted_hash.as_slice() == hash.as_slice());