"Fossies" - the Fresh Open Source Software Archive

Member "tdesktop-2.6.1/Telegram/SourceFiles/storage/storage_domain.cpp" (24 Feb 2021, 7839 Bytes) of package /linux/misc/tdesktop-2.6.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "storage_domain.cpp" see the Fossies "Dox" file reference documentation.

    1 /*
    2 This file is part of Telegram Desktop,
    3 the official desktop application for the Telegram messaging service.
    4 
    5 For license and copyright information please follow this link:
    6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
    7 */
    8 #include "storage/storage_domain.h"
    9 
   10 #include "storage/details/storage_file_utilities.h"
   11 #include "storage/serialize_common.h"
   12 #include "mtproto/mtproto_config.h"
   13 #include "main/main_domain.h"
   14 #include "main/main_account.h"
   15 #include "facades.h"
   16 
   17 namespace Storage {
   18 namespace {
   19 
   20 using namespace details;
   21 
   22 [[nodiscard]] QString BaseGlobalPath() {
   23     return cWorkingDir() + qsl("tdata/");
   24 }
   25 
   26 [[nodiscard]] QString ComputeKeyName(const QString &dataName) {
   27     // We dropped old test authorizations when migrated to multi auth.
   28     //return "key_" + dataName + (cTestMode() ? "[test]" : "");
   29     return "key_" + dataName;
   30 }
   31 
   32 [[nodiscard]] QString ComputeInfoName(const QString &dataName) {
   33     // We dropped old test authorizations when migrated to multi auth.
   34     //return "info_" + dataName + (cTestMode() ? "[test]" : "");
   35     return "info_" + dataName;
   36 }
   37 
   38 } // namespace
   39 
   40 Domain::Domain(not_null<Main::Domain*> owner, const QString &dataName)
   41 : _owner(owner)
   42 , _dataName(dataName) {
   43 }
   44 
   45 Domain::~Domain() = default;
   46 
   47 StartResult Domain::start(const QByteArray &passcode) {
   48     const auto modern = startModern(passcode);
   49     if (modern == StartModernResult::Success) {
   50         if (_oldVersion < AppVersion) {
   51             writeAccounts();
   52         }
   53         return StartResult::Success;
   54     } else if (modern == StartModernResult::IncorrectPasscode) {
   55         return StartResult::IncorrectPasscode;
   56     } else if (modern == StartModernResult::Failed) {
   57         startFromScratch();
   58         return StartResult::Success;
   59     }
   60     auto legacy = std::make_unique<Main::Account>(_owner, _dataName, 0);
   61     const auto result = legacy->legacyStart(passcode);
   62     if (result == StartResult::Success) {
   63         _oldVersion = legacy->local().oldMapVersion();
   64         startWithSingleAccount(passcode, std::move(legacy));
   65     }
   66     return result;
   67 }
   68 
   69 void Domain::startAdded(
   70         not_null<Main::Account*> account,
   71         std::unique_ptr<MTP::Config> config) {
   72     Expects(_localKey != nullptr);
   73 
   74     account->prepareToStartAdded(_localKey);
   75     account->start(std::move(config));
   76 }
   77 
   78 void Domain::startWithSingleAccount(
   79         const QByteArray &passcode,
   80         std::unique_ptr<Main::Account> account) {
   81     Expects(account != nullptr);
   82 
   83     if (auto localKey = account->local().peekLegacyLocalKey()) {
   84         _localKey = std::move(localKey);
   85         encryptLocalKey(passcode);
   86         account->start(nullptr);
   87     } else {
   88         generateLocalKey();
   89         account->start(account->prepareToStart(_localKey));
   90     }
   91     _owner->accountAddedInStorage(Main::Domain::AccountWithIndex{
   92         .account = std::move(account)
   93     });
   94     writeAccounts();
   95 }
   96 
   97 void Domain::generateLocalKey() {
   98     Expects(_localKey == nullptr);
   99     Expects(_passcodeKeySalt.isEmpty());
  100     Expects(_passcodeKeyEncrypted.isEmpty());
  101 
  102     auto pass = QByteArray(MTP::AuthKey::kSize, Qt::Uninitialized);
  103     auto salt = QByteArray(LocalEncryptSaltSize, Qt::Uninitialized);
  104     memset_rand(pass.data(), pass.size());
  105     memset_rand(salt.data(), salt.size());
  106     _localKey = CreateLocalKey(pass, salt);
  107 
  108     encryptLocalKey(QByteArray());
  109 }
  110 
  111 void Domain::encryptLocalKey(const QByteArray &passcode) {
  112     _passcodeKeySalt.resize(LocalEncryptSaltSize);
  113     memset_rand(_passcodeKeySalt.data(), _passcodeKeySalt.size());
  114     _passcodeKey = CreateLocalKey(passcode, _passcodeKeySalt);
  115 
  116     EncryptedDescriptor passKeyData(MTP::AuthKey::kSize);
  117     _localKey->write(passKeyData.stream);
  118     _passcodeKeyEncrypted = PrepareEncrypted(passKeyData, _passcodeKey);
  119 }
  120 
  121 Domain::StartModernResult Domain::startModern(
  122         const QByteArray &passcode) {
  123     const auto name = ComputeKeyName(_dataName);
  124 
  125     FileReadDescriptor keyData;
  126     if (!ReadFile(keyData, name, BaseGlobalPath())) {
  127         return StartModernResult::Empty;
  128     }
  129     LOG(("App Info: reading accounts info..."));
  130 
  131     QByteArray salt, keyEncrypted, infoEncrypted;
  132     keyData.stream >> salt >> keyEncrypted >> infoEncrypted;
  133     if (!CheckStreamStatus(keyData.stream)) {
  134         return StartModernResult::Failed;
  135     }
  136 
  137     if (salt.size() != LocalEncryptSaltSize) {
  138         LOG(("App Error: bad salt in info file, size: %1").arg(salt.size()));
  139         return StartModernResult::Failed;
  140     }
  141     _passcodeKey = CreateLocalKey(passcode, salt);
  142 
  143     EncryptedDescriptor keyInnerData, info;
  144     if (!DecryptLocal(keyInnerData, keyEncrypted, _passcodeKey)) {
  145         LOG(("App Info: could not decrypt pass-protected key from info file, "
  146             "maybe bad password..."));
  147         return StartModernResult::IncorrectPasscode;
  148     }
  149     auto key = Serialize::read<MTP::AuthKey::Data>(keyInnerData.stream);
  150     if (keyInnerData.stream.status() != QDataStream::Ok
  151         || !keyInnerData.stream.atEnd()) {
  152         LOG(("App Error: could not read pass-protected key from info file"));
  153         return StartModernResult::Failed;
  154     }
  155     _localKey = std::make_shared<MTP::AuthKey>(key);
  156 
  157     _passcodeKeyEncrypted = keyEncrypted;
  158     _passcodeKeySalt = salt;
  159 
  160     if (!DecryptLocal(info, infoEncrypted, _localKey)) {
  161         LOG(("App Error: could not decrypt info."));
  162         return StartModernResult::Failed;
  163     }
  164     LOG(("App Info: reading encrypted info..."));
  165     auto count = qint32();
  166     info.stream >> count;
  167     if (count <= 0 || count > Main::Domain::kMaxAccounts) {
  168         LOG(("App Error: bad accounts count: %1").arg(count));
  169         return StartModernResult::Failed;
  170     }
  171 
  172     _oldVersion = keyData.version;
  173 
  174     auto tried = base::flat_set<int>();
  175     auto sessions = base::flat_set<uint64>();
  176     auto active = 0;
  177     for (auto i = 0; i != count; ++i) {
  178         auto index = qint32();
  179         info.stream >> index;
  180         if (index >= 0
  181             && index < Main::Domain::kMaxAccounts
  182             && tried.emplace(index).second) {
  183             auto account = std::make_unique<Main::Account>(
  184                 _owner,
  185                 _dataName,
  186                 index);
  187             auto config = account->prepareToStart(_localKey);
  188             const auto sessionId = account->willHaveSessionUniqueId(
  189                 config.get());
  190             if (!sessions.contains(sessionId)
  191                 && (sessionId != 0 || (sessions.empty() && i + 1 == count))) {
  192                 if (sessions.empty()) {
  193                     active = index;
  194                 }
  195                 account->start(std::move(config));
  196                 _owner->accountAddedInStorage({
  197                     .index = index,
  198                     .account = std::move(account)
  199                 });
  200                 sessions.emplace(sessionId);
  201             }
  202         }
  203     }
  204     if (sessions.empty()) {
  205         LOG(("App Error: no accounts read."));
  206         return StartModernResult::Failed;
  207     }
  208 
  209     if (!info.stream.atEnd()) {
  210         info.stream >> active;
  211     }
  212     _owner->activateFromStorage(active);
  213 
  214     Ensures(!sessions.empty());
  215     return StartModernResult::Success;
  216 }
  217 
  218 void Domain::writeAccounts() {
  219     Expects(!_owner->accounts().empty());
  220 
  221     const auto path = BaseGlobalPath();
  222     if (!QDir().exists(path)) {
  223         QDir().mkpath(path);
  224     }
  225 
  226     FileWriteDescriptor key(ComputeKeyName(_dataName), path);
  227     key.writeData(_passcodeKeySalt);
  228     key.writeData(_passcodeKeyEncrypted);
  229 
  230     const auto &list = _owner->accounts();
  231 
  232     auto keySize = sizeof(qint32) + sizeof(qint32) * list.size();
  233 
  234     EncryptedDescriptor keyData(keySize);
  235     keyData.stream << qint32(list.size());
  236     for (const auto &[index, account] : list) {
  237         keyData.stream << qint32(index);
  238     }
  239     keyData.stream << qint32(_owner->activeForStorage());
  240     key.writeEncrypted(keyData, _localKey);
  241 }
  242 
  243 void Domain::startFromScratch() {
  244     startWithSingleAccount(
  245         QByteArray(),
  246         std::make_unique<Main::Account>(_owner, _dataName, 0));
  247 }
  248 
  249 bool Domain::checkPasscode(const QByteArray &passcode) const {
  250     Expects(!_passcodeKeySalt.isEmpty());
  251     Expects(_passcodeKey != nullptr);
  252 
  253     const auto checkKey = CreateLocalKey(passcode, _passcodeKeySalt);
  254     return checkKey->equals(_passcodeKey);
  255 }
  256 
  257 void Domain::setPasscode(const QByteArray &passcode) {
  258     Expects(!_passcodeKeySalt.isEmpty());
  259     Expects(_localKey != nullptr);
  260 
  261     encryptLocalKey(passcode);
  262     writeAccounts();
  263 
  264     Global::SetLocalPasscode(!passcode.isEmpty());
  265     Global::RefLocalPasscodeChanged().notify();
  266 }
  267 
  268 int Domain::oldVersion() const {
  269     return _oldVersion;
  270 }
  271 
  272 void Domain::clearOldVersion() {
  273     _oldVersion = 0;
  274 }
  275 
  276 } // namespace Storage