| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 |
- /*
- 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_domain.h"
- #include "storage/details/storage_file_utilities.h"
- #include "storage/serialize_common.h"
- #include "mtproto/mtproto_config.h"
- #include "main/main_domain.h"
- #include "main/main_account.h"
- #include "base/random.h"
- namespace Storage {
- namespace {
- using namespace details;
- [[nodiscard]] QString BaseGlobalPath() {
- return cWorkingDir() + u"tdata/"_q;
- }
- [[nodiscard]] QString ComputeKeyName(const QString &dataName) {
- // We dropped old test authorizations when migrated to multi auth.
- //return "key_" + dataName + (cTestMode() ? "[test]" : "");
- return "key_" + dataName;
- }
- } // namespace
- Domain::Domain(not_null<Main::Domain*> owner, const QString &dataName)
- : _owner(owner)
- , _dataName(dataName) {
- }
- Domain::~Domain() = default;
- StartResult Domain::start(const QByteArray &passcode) {
- const auto modern = startModern(passcode);
- if (modern == StartModernResult::Success) {
- if (_oldVersion < AppVersion) {
- writeAccounts();
- }
- return StartResult::Success;
- } else if (modern == StartModernResult::IncorrectPasscode) {
- return StartResult::IncorrectPasscode;
- } else if (modern == StartModernResult::Failed) {
- startFromScratch();
- return StartResult::Success;
- }
- auto legacy = std::make_unique<Main::Account>(_owner, _dataName, 0);
- const auto result = legacy->legacyStart(passcode);
- if (result == StartResult::Success) {
- _oldVersion = legacy->local().oldMapVersion();
- startWithSingleAccount(passcode, std::move(legacy));
- }
- return result;
- }
- void Domain::startAdded(
- not_null<Main::Account*> account,
- std::unique_ptr<MTP::Config> config) {
- Expects(_localKey != nullptr);
- account->prepareToStartAdded(_localKey);
- account->start(std::move(config));
- }
- void Domain::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);
- account->start(nullptr);
- } else {
- generateLocalKey();
- account->start(account->prepareToStart(_localKey));
- }
- _owner->accountAddedInStorage(Main::Domain::AccountWithIndex{
- .account = std::move(account)
- });
- writeAccounts();
- }
- void Domain::generateLocalKey() {
- Expects(_localKey == nullptr);
- Expects(_passcodeKeySalt.isEmpty());
- Expects(_passcodeKeyEncrypted.isEmpty());
- auto pass = QByteArray(MTP::AuthKey::kSize, Qt::Uninitialized);
- auto salt = QByteArray(LocalEncryptSaltSize, Qt::Uninitialized);
- base::RandomFill(pass.data(), pass.size());
- base::RandomFill(salt.data(), salt.size());
- _localKey = CreateLocalKey(pass, salt);
- encryptLocalKey(QByteArray());
- }
- void Domain::encryptLocalKey(const QByteArray &passcode) {
- _passcodeKeySalt.resize(LocalEncryptSaltSize);
- base::RandomFill(_passcodeKeySalt.data(), _passcodeKeySalt.size());
- _passcodeKey = CreateLocalKey(passcode, _passcodeKeySalt);
- EncryptedDescriptor passKeyData(MTP::AuthKey::kSize);
- _localKey->write(passKeyData.stream);
- _passcodeKeyEncrypted = PrepareEncrypted(passKeyData, _passcodeKey);
- _hasLocalPasscode = !passcode.isEmpty();
- }
- Domain::StartModernResult Domain::startModern(
- const QByteArray &passcode) {
- 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;
- _hasLocalPasscode = !passcode.isEmpty();
- 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 > Main::Domain::kPremiumMaxAccounts) {
- LOG(("App Error: bad accounts count: %1").arg(count));
- return StartModernResult::Failed;
- }
- _oldVersion = keyData.version;
- auto tried = base::flat_set<int>();
- auto sessions = base::flat_set<uint64>();
- auto active = 0;
- for (auto i = 0; i != count; ++i) {
- auto index = qint32();
- info.stream >> index;
- if (index >= 0
- && index < Main::Domain::kPremiumMaxAccounts
- && tried.emplace(index).second) {
- auto account = std::make_unique<Main::Account>(
- _owner,
- _dataName,
- index);
- auto config = account->prepareToStart(_localKey);
- const auto sessionId = account->willHaveSessionUniqueId(
- config.get());
- if (!sessions.contains(sessionId)
- && (sessionId != 0 || (sessions.empty() && i + 1 == count))) {
- if (sessions.empty()) {
- active = index;
- }
- account->start(std::move(config));
- _owner->accountAddedInStorage({
- .index = index,
- .account = std::move(account)
- });
- sessions.emplace(sessionId);
- }
- }
- }
- if (sessions.empty()) {
- LOG(("App Error: no accounts read."));
- return StartModernResult::Failed;
- }
- if (!info.stream.atEnd()) {
- info.stream >> active;
- }
- _owner->activateFromStorage(active);
- Ensures(!sessions.empty());
- return StartModernResult::Success;
- }
- void Domain::writeAccounts() {
- Expects(!_owner->accounts().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->accounts();
- auto keySize = sizeof(qint32) + sizeof(qint32) * list.size();
- EncryptedDescriptor keyData(keySize);
- keyData.stream << qint32(list.size());
- for (const auto &[index, account] : list) {
- keyData.stream << qint32(index);
- }
- keyData.stream << qint32(_owner->activeForStorage());
- key.writeEncrypted(keyData, _localKey);
- }
- void Domain::startFromScratch() {
- startWithSingleAccount(
- QByteArray(),
- std::make_unique<Main::Account>(_owner, _dataName, 0));
- }
- bool Domain::checkPasscode(const QByteArray &passcode) const {
- Expects(!_passcodeKeySalt.isEmpty());
- Expects(_passcodeKey != nullptr);
- const auto checkKey = CreateLocalKey(passcode, _passcodeKeySalt);
- return checkKey->equals(_passcodeKey);
- }
- void Domain::setPasscode(const QByteArray &passcode) {
- Expects(!_passcodeKeySalt.isEmpty());
- Expects(_localKey != nullptr);
- encryptLocalKey(passcode);
- writeAccounts();
- _passcodeKeyChanged.fire({});
- }
- int Domain::oldVersion() const {
- return _oldVersion;
- }
- void Domain::clearOldVersion() {
- _oldVersion = 0;
- }
- rpl::producer<> Domain::localPasscodeChanged() const {
- return _passcodeKeyChanged.events();
- }
- bool Domain::hasLocalPasscode() const {
- return _hasLocalPasscode;
- }
- } // namespace Storage
|