tdesktop/Telegram/SourceFiles/storage/storage_accounts.cpp

257 lines
7.2 KiB
C++
Raw Normal View History

2020-06-15 16:25:02 +00:00
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "storage/storage_accounts.h"
#include "storage/details/storage_file_utilities.h"
#include "storage/serialize_common.h"
#include "mtproto/mtproto_config.h"
2020-06-15 16:25:02 +00:00
#include "main/main_accounts.h"
#include "main/main_account.h"
#include "facades.h"
namespace Storage {
namespace {
using namespace details;
constexpr auto kMaxAccounts = 3;
[[nodiscard]] QString BaseGlobalPath() {
return cWorkingDir() + qsl("tdata/");
}
[[nodiscard]] QString ComputeKeyName(const QString &dataName) {
// We dropped old test authorizations when migrated to multi auth.
//return "key_" + dataName + (cTestMode() ? "[test]" : "");
return "key_" + dataName;
2020-06-15 16:25:02 +00:00
}
[[nodiscard]] QString ComputeInfoName(const QString &dataName) {
// We dropped old test authorizations when migrated to multi auth.
//return "info_" + dataName + (cTestMode() ? "[test]" : "");
return "info_" + dataName;
2020-06-15 16:25:02 +00:00
}
} // namespace
Accounts::Accounts(not_null<Main::Accounts*> owner, const QString &dataName)
: _owner(owner)
, _dataName(dataName) {
}
Accounts::~Accounts() = default;
2020-06-16 09:40:43 +00:00
StartResult Accounts::start(const QByteArray &passcode) {
const auto modern = startModern(passcode);
2020-06-15 16:25:02 +00:00
if (modern == StartModernResult::Success) {
if (_oldVersion < AppVersion) {
writeAccounts();
}
return StartResult::Success;
} else if (modern == StartModernResult::IncorrectPasscode) {
return StartResult::IncorrectPasscode;
} else if (modern == StartModernResult::Failed) {
2020-06-16 09:40:43 +00:00
startFromScratch();
2020-06-15 16:25:02 +00:00
return StartResult::Success;
}
auto legacy = std::make_unique<Main::Account>(_dataName, 0);
const auto result = legacy->legacyStart(passcode);
if (result == StartResult::Success) {
_oldVersion = legacy->local().oldMapVersion();
2020-06-16 09:40:43 +00:00
startWithSingleAccount(passcode, std::move(legacy));
2020-06-15 16:25:02 +00:00
}
return result;
}
void Accounts::startAdded(
not_null<Main::Account*> account,
std::unique_ptr<MTP::Config> config) {
2020-06-16 06:42:47 +00:00
Expects(_localKey != nullptr);
account->startAdded(_localKey, std::move(config));
2020-06-16 06:42:47 +00:00
}
2020-06-15 16:25:02 +00:00
void Accounts::startWithSingleAccount(
const QByteArray &passcode,
std::unique_ptr<Main::Account> account) {
Expects(account != nullptr);
if (auto localKey = account->local().peekLegacyLocalKey()) {
_localKey = std::move(localKey);
encryptLocalKey(passcode);
} else {
generateLocalKey();
account->start(_localKey);
}
2020-06-16 09:40:43 +00:00
_owner->accountAddedInStorage(0, std::move(account));
2020-06-15 16:25:02 +00:00
writeAccounts();
}
void Accounts::generateLocalKey() {
Expects(_localKey == nullptr);
Expects(_passcodeKeySalt.isEmpty());
Expects(_passcodeKeyEncrypted.isEmpty());
auto pass = QByteArray(MTP::AuthKey::kSize, Qt::Uninitialized);
auto salt = QByteArray(LocalEncryptSaltSize, Qt::Uninitialized);
memset_rand(pass.data(), pass.size());
memset_rand(salt.data(), salt.size());
_localKey = CreateLocalKey(pass, salt);
encryptLocalKey(QByteArray());
}
void Accounts::encryptLocalKey(const QByteArray &passcode) {
_passcodeKeySalt.resize(LocalEncryptSaltSize);
memset_rand(_passcodeKeySalt.data(), _passcodeKeySalt.size());
_passcodeKey = CreateLocalKey(passcode, _passcodeKeySalt);
EncryptedDescriptor passKeyData(MTP::AuthKey::kSize);
_localKey->write(passKeyData.stream);
_passcodeKeyEncrypted = PrepareEncrypted(passKeyData, _passcodeKey);
}
Accounts::StartModernResult Accounts::startModern(
2020-06-16 09:40:43 +00:00
const QByteArray &passcode) {
2020-06-15 16:25:02 +00:00
const auto name = ComputeKeyName(_dataName);
FileReadDescriptor keyData;
if (!ReadFile(keyData, name, BaseGlobalPath())) {
return StartModernResult::Empty;
}
LOG(("App Info: reading accounts info..."));
QByteArray salt, keyEncrypted, infoEncrypted;
keyData.stream >> salt >> keyEncrypted >> infoEncrypted;
if (!CheckStreamStatus(keyData.stream)) {
return StartModernResult::Failed;
}
if (salt.size() != LocalEncryptSaltSize) {
LOG(("App Error: bad salt in info file, size: %1").arg(salt.size()));
return StartModernResult::Failed;
}
_passcodeKey = CreateLocalKey(passcode, salt);
EncryptedDescriptor keyInnerData, info;
if (!DecryptLocal(keyInnerData, keyEncrypted, _passcodeKey)) {
LOG(("App Info: could not decrypt pass-protected key from info file, "
"maybe bad password..."));
return StartModernResult::IncorrectPasscode;
}
auto key = Serialize::read<MTP::AuthKey::Data>(keyInnerData.stream);
if (keyInnerData.stream.status() != QDataStream::Ok
|| !keyInnerData.stream.atEnd()) {
LOG(("App Error: could not read pass-protected key from info file"));
return StartModernResult::Failed;
}
_localKey = std::make_shared<MTP::AuthKey>(key);
_passcodeKeyEncrypted = keyEncrypted;
_passcodeKeySalt = salt;
if (!DecryptLocal(info, infoEncrypted, _localKey)) {
LOG(("App Error: could not decrypt info."));
return StartModernResult::Failed;
}
LOG(("App Info: reading encrypted info..."));
auto count = qint32();
info.stream >> count;
if (count <= 0 || count > kMaxAccounts) {
LOG(("App Error: bad accounts count: %1").arg(count));
return StartModernResult::Failed;
}
_oldVersion = keyData.version;
auto tried = base::flat_set<int>();
auto users = base::flat_set<UserId>();
for (auto i = 0; i != count; ++i) {
auto index = qint32();
info.stream >> index;
if (index >= 0
&& index < kMaxAccounts
&& tried.emplace(index).second) {
auto account = std::make_unique<Main::Account>(_dataName, index);
account->start(_localKey);
const auto userId = account->willHaveUserId();
if (!users.contains(userId)
&& (userId != 0 || (users.empty() && i + 1 == count))) {
2020-06-16 09:40:43 +00:00
_owner->accountAddedInStorage(index, std::move(account));
2020-06-15 16:25:02 +00:00
users.emplace(userId);
}
}
}
Ensures(!users.empty());
return StartModernResult::Success;
}
void Accounts::writeAccounts() {
Expects(!_owner->list().empty());
const auto path = BaseGlobalPath();
if (!QDir().exists(path)) {
QDir().mkpath(path);
}
FileWriteDescriptor key(ComputeKeyName(_dataName), path);
key.writeData(_passcodeKeySalt);
key.writeData(_passcodeKeyEncrypted);
const auto &list = _owner->list();
2020-06-16 06:42:47 +00:00
const auto active = _owner->activeIndex();
2020-06-15 16:25:02 +00:00
auto keySize = sizeof(qint32) + sizeof(qint32) * list.size();
EncryptedDescriptor keyData(keySize);
keyData.stream << qint32(list.size());
2020-06-16 06:42:47 +00:00
keyData.stream << qint32(active);
2020-06-15 16:25:02 +00:00
for (const auto &[index, account] : list) {
2020-06-16 06:42:47 +00:00
if (index != active) {
keyData.stream << qint32(index);
}
2020-06-15 16:25:02 +00:00
}
key.writeEncrypted(keyData, _localKey);
}
2020-06-16 09:40:43 +00:00
void Accounts::startFromScratch() {
startWithSingleAccount(
QByteArray(),
std::make_unique<Main::Account>(_dataName, 0));
}
2020-06-15 16:25:02 +00:00
bool Accounts::checkPasscode(const QByteArray &passcode) const {
Expects(!_passcodeKeySalt.isEmpty());
Expects(_passcodeKey != nullptr);
const auto checkKey = CreateLocalKey(passcode, _passcodeKeySalt);
return checkKey->equals(_passcodeKey);
}
void Accounts::setPasscode(const QByteArray &passcode) {
Expects(!_passcodeKeySalt.isEmpty());
Expects(_localKey != nullptr);
encryptLocalKey(passcode);
writeAccounts();
Global::SetLocalPasscode(!passcode.isEmpty());
Global::RefLocalPasscodeChanged().notify();
}
int Accounts::oldVersion() const {
return _oldVersion;
}
void Accounts::clearOldVersion() {
_oldVersion = 0;
}
} // namespace Storage