mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-03-25 04:38:23 +00:00
MTP::AuthKey uses std::array for data.
Also using "volatile char*" iteration for secure memset on Mac/Linux.
This commit is contained in:
parent
21efb44e81
commit
90b3b5c3e5
@ -76,7 +76,6 @@ enum {
|
||||
LocalEncryptIterCount = 4000, // key derivation iteration count
|
||||
LocalEncryptNoPwdIterCount = 4, // key derivation iteration count without pwd (not secure anyway)
|
||||
LocalEncryptSaltSize = 32, // 256 bit
|
||||
LocalEncryptKeySize = 256, // 2048 bit
|
||||
|
||||
AnimationTimerDelta = 7,
|
||||
ClipThreadsCount = 8,
|
||||
|
@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
#pragma once
|
||||
|
||||
#include "core/basic_types.h"
|
||||
#include <array>
|
||||
|
||||
namespace base {
|
||||
|
||||
@ -242,13 +243,37 @@ private:
|
||||
};
|
||||
|
||||
int32 hashCrc32(const void *data, uint32 len);
|
||||
|
||||
int32 *hashSha1(const void *data, uint32 len, void *dest); // dest - ptr to 20 bytes, returns (int32*)dest
|
||||
inline std::array<char, 20> hashSha1(const void *data, int len) {
|
||||
auto result = std::array<char, 20>();
|
||||
hashSha1(data, len, result.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
int32 *hashSha256(const void *data, uint32 len, void *dest); // dest - ptr to 32 bytes, returns (int32*)dest
|
||||
inline std::array<char, 32> hashSha256(const void *data, int size) {
|
||||
auto result = std::array<char, 32>();
|
||||
hashSha1(data, size, result.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
int32 *hashMd5(const void *data, uint32 len, void *dest); // dest = ptr to 16 bytes, returns (int32*)dest
|
||||
inline std::array<char, 16> hashMd5(const void *data, int size) {
|
||||
auto result = std::array<char, 16>();
|
||||
hashMd5(data, size, result.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
char *hashMd5Hex(const int32 *hashmd5, void *dest); // dest = ptr to 32 bytes, returns (char*)dest
|
||||
inline char *hashMd5Hex(const void *data, uint32 len, void *dest) { // dest = ptr to 32 bytes, returns (char*)dest
|
||||
return hashMd5Hex(HashMd5(data, len).result(), dest);
|
||||
}
|
||||
inline std::array<char, 32> hashMd5Hex(const void *data, int size) {
|
||||
auto result = std::array<char, 32>();
|
||||
hashMd5Hex(data, size, result.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
// good random (using openssl implementation)
|
||||
void memset_rand(void *data, uint32 len);
|
||||
|
@ -135,9 +135,10 @@ bool _checkStreamStatus(QDataStream &stream) {
|
||||
|
||||
QByteArray _settingsSalt, _passKeySalt, _passKeyEncrypted;
|
||||
|
||||
constexpr auto kLocalKeySize = MTP::AuthKey::kSize;
|
||||
MTP::AuthKey _oldKey, _settingsKey, _passKey, _localKey;
|
||||
void createLocalKey(const QByteArray &pass, QByteArray *salt, MTP::AuthKey *result) {
|
||||
uchar key[LocalEncryptKeySize] = { 0 };
|
||||
MTP::AuthKey::Data key = { 0 };
|
||||
int32 iterCount = pass.size() ? LocalEncryptIterCount : LocalEncryptNoPwdIterCount; // dont slow down for no password
|
||||
QByteArray newSalt;
|
||||
if (!salt) {
|
||||
@ -148,7 +149,7 @@ void createLocalKey(const QByteArray &pass, QByteArray *salt, MTP::AuthKey *resu
|
||||
cSetLocalSalt(newSalt);
|
||||
}
|
||||
|
||||
PKCS5_PBKDF2_HMAC_SHA1(pass.constData(), pass.size(), (uchar*)salt->data(), salt->size(), iterCount, LocalEncryptKeySize, key);
|
||||
PKCS5_PBKDF2_HMAC_SHA1(pass.constData(), pass.size(), (uchar*)salt->data(), salt->size(), iterCount, key.size(), (uchar*)key.data());
|
||||
|
||||
result->setKey(key);
|
||||
}
|
||||
@ -894,18 +895,12 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version) {
|
||||
|
||||
case dbiKey: {
|
||||
qint32 dcId;
|
||||
quint32 key[64];
|
||||
MTP::AuthKey::Data key = { 0 };
|
||||
stream >> dcId;
|
||||
stream.readRawData((char*)key, 256);
|
||||
stream.readRawData(key.data(), key.size());
|
||||
if (!_checkStreamStatus(stream)) return false;
|
||||
|
||||
DEBUG_LOG(("MTP Info: key found, dc %1, key: %2").arg(dcId).arg(Logs::mb(key, 256).str()));
|
||||
dcId = MTP::bareDcId(dcId);
|
||||
MTP::AuthKeyPtr keyPtr(new MTP::AuthKey());
|
||||
keyPtr->setKey(key);
|
||||
keyPtr->setDC(dcId);
|
||||
|
||||
MTP::setKey(dcId, keyPtr);
|
||||
MTP::setKey(dcId, key);
|
||||
} break;
|
||||
|
||||
case dbiAutoStart: {
|
||||
@ -1777,14 +1772,14 @@ void _writeMtpData() {
|
||||
return;
|
||||
}
|
||||
|
||||
MTP::AuthKeysMap keys = MTP::getKeys();
|
||||
auto keys = MTP::getKeys();
|
||||
|
||||
quint32 size = sizeof(quint32) + sizeof(qint32) + sizeof(quint32);
|
||||
size += keys.size() * (sizeof(quint32) + sizeof(quint32) + 256);
|
||||
size += keys.size() * (sizeof(quint32) + sizeof(quint32) + MTP::AuthKey::kSize);
|
||||
|
||||
EncryptedDescriptor data(size);
|
||||
data.stream << quint32(dbiUser) << qint32(MTP::authedId()) << quint32(MTP::maindc());
|
||||
for_const (const MTP::AuthKeyPtr &key, keys) {
|
||||
for_const (auto &key, keys) {
|
||||
data.stream << quint32(dbiKey) << quint32(key->getDC());
|
||||
key->write(data.stream);
|
||||
}
|
||||
@ -1847,8 +1842,8 @@ ReadMapState _readMap(const QByteArray &pass) {
|
||||
LOG(("App Info: could not decrypt pass-protected key from map file, maybe bad password..."));
|
||||
return ReadMapPassNeeded;
|
||||
}
|
||||
uchar key[LocalEncryptKeySize] = { 0 };
|
||||
if (keyData.stream.readRawData((char*)key, LocalEncryptKeySize) != LocalEncryptKeySize || !keyData.stream.atEnd()) {
|
||||
auto key = MTP::AuthKey::Data { 0 };
|
||||
if (keyData.stream.readRawData(key.data(), key.size()) != key.size() || !keyData.stream.atEnd()) {
|
||||
LOG(("App Error: could not read pass-protected key from map file"));
|
||||
return ReadMapFailed;
|
||||
}
|
||||
@ -2044,8 +2039,7 @@ void _writeMap(WriteMapWhen when) {
|
||||
|
||||
FileWriteDescriptor map(qsl("map"));
|
||||
if (_passKeySalt.isEmpty() || _passKeyEncrypted.isEmpty()) {
|
||||
uchar local5Key[LocalEncryptKeySize] = { 0 };
|
||||
QByteArray pass(LocalEncryptKeySize, Qt::Uninitialized), salt(LocalEncryptSaltSize, Qt::Uninitialized);
|
||||
QByteArray pass(kLocalKeySize, Qt::Uninitialized), salt(LocalEncryptSaltSize, Qt::Uninitialized);
|
||||
memset_rand(pass.data(), pass.size());
|
||||
memset_rand(salt.data(), salt.size());
|
||||
createLocalKey(pass, &salt, &_localKey);
|
||||
@ -2054,7 +2048,7 @@ void _writeMap(WriteMapWhen when) {
|
||||
memset_rand(_passKeySalt.data(), _passKeySalt.size());
|
||||
createLocalKey(QByteArray(), &_passKeySalt, &_passKey);
|
||||
|
||||
EncryptedDescriptor passKeyData(LocalEncryptKeySize);
|
||||
EncryptedDescriptor passKeyData(kLocalKeySize);
|
||||
_localKey.write(passKeyData.stream);
|
||||
_passKeyEncrypted = FileWriteDescriptor::prepareEncrypted(passKeyData, _passKey);
|
||||
}
|
||||
@ -2376,15 +2370,15 @@ void reset() {
|
||||
}
|
||||
|
||||
bool checkPasscode(const QByteArray &passcode) {
|
||||
MTP::AuthKey tmp;
|
||||
createLocalKey(passcode, &_passKeySalt, &tmp);
|
||||
return (tmp == _passKey);
|
||||
auto checkKey = MTP::AuthKey();
|
||||
createLocalKey(passcode, &_passKeySalt, &checkKey);
|
||||
return (checkKey == _passKey);
|
||||
}
|
||||
|
||||
void setPasscode(const QByteArray &passcode) {
|
||||
createLocalKey(passcode, &_passKeySalt, &_passKey);
|
||||
|
||||
EncryptedDescriptor passKeyData(LocalEncryptKeySize);
|
||||
EncryptedDescriptor passKeyData(kLocalKeySize);
|
||||
_localKey.write(passKeyData.stream);
|
||||
_passKeyEncrypted = FileWriteDescriptor::prepareEncrypted(passKeyData, _passKey);
|
||||
|
||||
|
@ -20,30 +20,34 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
namespace MTP {
|
||||
|
||||
class AuthKey {
|
||||
public:
|
||||
|
||||
AuthKey() : _isset(false), _dc(0) {
|
||||
}
|
||||
static constexpr auto kSize = 256; // 2048 bits.
|
||||
using Data = std::array<char, kSize>;
|
||||
|
||||
bool created() const {
|
||||
return _isset;
|
||||
}
|
||||
|
||||
void setKey(const void *from) {
|
||||
memcpy(_key, from, 256);
|
||||
uchar sha1Buffer[20];
|
||||
_keyId = *(uint64*)(hashSha1(_key, 256, sha1Buffer) + 3);
|
||||
void setKey(const Data &from) {
|
||||
_key = from;
|
||||
auto sha1 = hashSha1(_key.data(), _key.size());
|
||||
|
||||
// Lower 64 bits = 8 bytes of 20 byte SHA1 hash.
|
||||
_keyId = *reinterpret_cast<uint64*>(sha1.data() + 12);
|
||||
_isset = true;
|
||||
}
|
||||
|
||||
void setDC(uint32 dc) {
|
||||
void setDC(int dc) {
|
||||
_dc = dc;
|
||||
}
|
||||
|
||||
uint32 getDC() const {
|
||||
int getDC() const {
|
||||
t_assert(_isset);
|
||||
return _dc;
|
||||
}
|
||||
@ -60,26 +64,27 @@ public:
|
||||
|
||||
uchar data_a[16 + 32], sha1_a[20];
|
||||
memcpy(data_a, &msgKey, 16);
|
||||
memcpy(data_a + 16, _key + x, 32);
|
||||
memcpy(data_a + 16, _key.data() + x, 32);
|
||||
hashSha1(data_a, 16 + 32, sha1_a);
|
||||
|
||||
uchar data_b[16 + 16 + 16], sha1_b[20];
|
||||
memcpy(data_b, _key + 32 + x, 16);
|
||||
memcpy(data_b, _key.data() + 32 + x, 16);
|
||||
memcpy(data_b + 16, &msgKey, 16);
|
||||
memcpy(data_b + 32, _key + 48 + x, 16);
|
||||
memcpy(data_b + 32, _key.data() + 48 + x, 16);
|
||||
hashSha1(data_b, 16 + 16 + 16, sha1_b);
|
||||
|
||||
uchar data_c[32 + 16], sha1_c[20];
|
||||
memcpy(data_c, _key + 64 + x, 32);
|
||||
memcpy(data_c, _key.data() + 64 + x, 32);
|
||||
memcpy(data_c + 32, &msgKey, 16);
|
||||
hashSha1(data_c, 32 + 16, sha1_c);
|
||||
|
||||
uchar data_d[16 + 32], sha1_d[20];
|
||||
memcpy(data_d, &msgKey, 16);
|
||||
memcpy(data_d + 16, _key + 96 + x, 32);
|
||||
memcpy(data_d + 16, _key.data() + 96 + x, 32);
|
||||
hashSha1(data_d, 16 + 32, sha1_d);
|
||||
|
||||
uchar *key((uchar*)&aesKey), *iv((uchar*)&aesIV);
|
||||
auto key = reinterpret_cast<uchar*>(&aesKey);
|
||||
auto iv = reinterpret_cast<uchar*>(&aesIV);
|
||||
memcpy(key, sha1_a, 8);
|
||||
memcpy(key + 8, sha1_b + 8, 12);
|
||||
memcpy(key + 8 + 12, sha1_c + 4, 12);
|
||||
@ -91,7 +96,7 @@ public:
|
||||
|
||||
void write(QDataStream &to) const {
|
||||
t_assert(_isset);
|
||||
to.writeRawData(_key, 256);
|
||||
to.writeRawData(_key.data(), _key.size());
|
||||
}
|
||||
|
||||
static const uint64 RecreateKeyId = 0xFFFFFFFFFFFFFFFFL;
|
||||
@ -99,20 +104,19 @@ public:
|
||||
friend bool operator==(const AuthKey &a, const AuthKey &b);
|
||||
|
||||
private:
|
||||
|
||||
char _key[256];
|
||||
uint64 _keyId;
|
||||
bool _isset;
|
||||
uint32 _dc;
|
||||
Data _key = { 0 };
|
||||
uint64 _keyId = 0;
|
||||
bool _isset = false;
|
||||
int _dc = 0;
|
||||
|
||||
};
|
||||
|
||||
inline bool operator==(const AuthKey &a, const AuthKey &b) {
|
||||
return !memcmp(a._key, b._key, 256);
|
||||
return (a._key == b._key);
|
||||
}
|
||||
|
||||
typedef QSharedPointer<AuthKey> AuthKeyPtr;
|
||||
typedef QVector<AuthKeyPtr> AuthKeysMap;
|
||||
using AuthKeyPtr = std::shared_ptr<AuthKey>;
|
||||
using AuthKeysMap = QVector<AuthKeyPtr>;
|
||||
|
||||
void aesIgeEncrypt(const void *src, void *dst, uint32 len, const void *key, const void *iv);
|
||||
void aesIgeDecrypt(const void *src, void *dst, uint32 len, const void *key, const void *iv);
|
||||
|
@ -443,7 +443,7 @@ ConnectionPrivate::ConnectionPrivate(QThread *thread, Connection *owner, Session
|
||||
, _owner(owner)
|
||||
, _waitForReceived(MTPMinReceiveDelay)
|
||||
, _waitForConnected(MTPMinConnectDelay)
|
||||
// , sessionDataMutex(QReadWriteLock::Recursive)
|
||||
//, sessionDataMutex(QReadWriteLock::Recursive)
|
||||
, sessionData(data) {
|
||||
oldConnectionTimer.moveToThread(thread);
|
||||
_waitForConnectedTimer.moveToThread(thread);
|
||||
@ -1197,7 +1197,7 @@ void ConnectionPrivate::restart(bool mayBeBadKey) {
|
||||
_waitForReceivedTimer.stop();
|
||||
_waitForConnectedTimer.stop();
|
||||
|
||||
AuthKeyPtr key(sessionData->getKey());
|
||||
auto key = sessionData->getKey();
|
||||
if (key) {
|
||||
if (!sessionData->isCheckedKey()) {
|
||||
if (mayBeBadKey) {
|
||||
@ -1368,7 +1368,7 @@ void ConnectionPrivate::handleReceived() {
|
||||
return restart();
|
||||
}
|
||||
|
||||
AuthKeyPtr key(sessionData->getKey());
|
||||
auto key = sessionData->getKey();
|
||||
if (!key || key->keyId() != keyId) {
|
||||
DEBUG_LOG(("MTP Error: auth_key id for dc %1 changed").arg(dc));
|
||||
|
||||
@ -2360,7 +2360,7 @@ void ConnectionPrivate::updateAuthKey() {
|
||||
keyId = newKeyId;
|
||||
return; // some other connection is getting key
|
||||
}
|
||||
const AuthKeyPtr &key(sessionData->getKey());
|
||||
auto key = sessionData->getKey();
|
||||
newKeyId = key ? key->keyId() : 0;
|
||||
}
|
||||
if (keyId != newKeyId) {
|
||||
@ -2642,15 +2642,14 @@ void ConnectionPrivate::dhClientParamsSend() {
|
||||
|
||||
// count g_b and auth_key using openssl BIGNUM methods
|
||||
MTP::internal::BigNumCounter bnCounter;
|
||||
if (!bnCounter.count(b, _authKeyStrings->dh_prime.constData(), _authKeyData->g, g_b, _authKeyStrings->g_a.constData(), _authKeyData->auth_key)) {
|
||||
if (!bnCounter.count(b, _authKeyStrings->dh_prime.constData(), _authKeyData->g, g_b, _authKeyStrings->g_a.constData(), _authKeyStrings->auth_key.data())) {
|
||||
return dhClientParamsSend();
|
||||
}
|
||||
|
||||
// count auth_key hashes - parts of sha1(auth_key)
|
||||
uchar sha1Buffer[20];
|
||||
int32 *auth_key_sha = hashSha1(_authKeyData->auth_key, 256, sha1Buffer);
|
||||
memcpy(&_authKeyData->auth_key_aux_hash, auth_key_sha, 8);
|
||||
memcpy(&_authKeyData->auth_key_hash, auth_key_sha + 3, 8);
|
||||
auto auth_key_sha = hashSha1(_authKeyStrings->auth_key.data(), _authKeyStrings->auth_key.size());
|
||||
memcpy(&_authKeyData->auth_key_aux_hash, auth_key_sha.data(), 8);
|
||||
memcpy(&_authKeyData->auth_key_hash, auth_key_sha.data() + 12, 8);
|
||||
|
||||
MTPSet_client_DH_params req_client_DH_params;
|
||||
req_client_DH_params.vnonce = _authKeyData->nonce;
|
||||
@ -2727,11 +2726,11 @@ void ConnectionPrivate::dhClientParamsAnswered() {
|
||||
uint64 salt1 = _authKeyData->new_nonce.l.l, salt2 = _authKeyData->server_nonce.l, serverSalt = salt1 ^ salt2;
|
||||
sessionData->setSalt(serverSalt);
|
||||
|
||||
AuthKeyPtr authKey(new AuthKey());
|
||||
authKey->setKey(_authKeyData->auth_key);
|
||||
auto authKey = std::make_shared<AuthKey>();
|
||||
authKey->setKey(_authKeyStrings->auth_key);
|
||||
authKey->setDC(bareDcId(dc));
|
||||
|
||||
DEBUG_LOG(("AuthKey Info: auth key gen succeed, id: %1, server salt: %2, auth key: %3").arg(authKey->keyId()).arg(serverSalt).arg(Logs::mb(_authKeyData->auth_key, 256).str()));
|
||||
DEBUG_LOG(("AuthKey Info: auth key gen succeed, id: %1, server salt: %2").arg(authKey->keyId()).arg(serverSalt));
|
||||
|
||||
sessionData->owner()->notifyKeyCreated(authKey); // slot will call authKeyCreated()
|
||||
sessionData->clear();
|
||||
@ -2823,17 +2822,28 @@ void ConnectionPrivate::authKeyCreated() {
|
||||
}
|
||||
|
||||
void ConnectionPrivate::clearAuthKeyData() {
|
||||
if (_authKeyData) {
|
||||
auto zeroMemory = [](void *data, int size) {
|
||||
#ifdef Q_OS_WIN
|
||||
SecureZeroMemory(_authKeyData.get(), sizeof(AuthKeyCreateData));
|
||||
if (!_authKeyStrings->dh_prime.isEmpty()) SecureZeroMemory(_authKeyStrings->dh_prime.data(), _authKeyStrings->dh_prime.size());
|
||||
if (!_authKeyStrings->g_a.isEmpty()) SecureZeroMemory(_authKeyStrings->g_a.data(), _authKeyStrings->g_a.size());
|
||||
#else
|
||||
memset(_authKeyData.get(), 0, sizeof(AuthKeyCreateData));
|
||||
if (!_authKeyStrings->dh_prime.isEmpty()) memset(_authKeyStrings->dh_prime.data(), 0, _authKeyStrings->dh_prime.size());
|
||||
if (!_authKeyStrings->g_a.isEmpty()) memset(_authKeyStrings->g_a.data(), 0, _authKeyStrings->g_a.size());
|
||||
#endif
|
||||
SecureZeroMemory(data, size);
|
||||
#else // Q_OS_WIN
|
||||
auto end = static_cast<char*>(data) + size;
|
||||
for (volatile auto p = static_cast<volatile char*>(data); p != end; ++p) {
|
||||
*p = 0;
|
||||
}
|
||||
#endif // Q_OS_WIN
|
||||
};
|
||||
if (_authKeyData) {
|
||||
zeroMemory(_authKeyData.get(), sizeof(AuthKeyCreateData));
|
||||
_authKeyData.reset();
|
||||
}
|
||||
if (_authKeyStrings) {
|
||||
if (!_authKeyStrings->dh_prime.isEmpty()) {
|
||||
zeroMemory(_authKeyStrings->dh_prime.data(), _authKeyStrings->dh_prime.size());
|
||||
}
|
||||
if (!_authKeyStrings->g_a.isEmpty()) {
|
||||
zeroMemory(_authKeyStrings->g_a.data(), _authKeyStrings->g_a.size());
|
||||
}
|
||||
zeroMemory(_authKeyStrings->auth_key.data(), _authKeyStrings->auth_key.size());
|
||||
_authKeyStrings.reset();
|
||||
}
|
||||
}
|
||||
@ -3031,6 +3041,7 @@ void ConnectionPrivate::unlockKey() {
|
||||
}
|
||||
|
||||
ConnectionPrivate::~ConnectionPrivate() {
|
||||
clearAuthKeyData();
|
||||
t_assert(_finished && _conn == nullptr && _conn4 == nullptr && _conn6 == nullptr);
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,6 @@ class ConnectionPrivate : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
ConnectionPrivate(QThread *thread, Connection *owner, SessionData *data, uint32 dc);
|
||||
~ConnectionPrivate();
|
||||
|
||||
@ -89,7 +88,6 @@ public:
|
||||
QString transport() const;
|
||||
|
||||
signals:
|
||||
|
||||
void needToReceive();
|
||||
void needToRestart();
|
||||
void stateChanged(qint32 newState);
|
||||
@ -252,7 +250,6 @@ private:
|
||||
|
||||
uchar aesKey[32] = { 0 };
|
||||
uchar aesIV[32] = { 0 };
|
||||
uint32 auth_key[64] = { 0 };
|
||||
MTPlong auth_key_hash;
|
||||
|
||||
uint32 req_num = 0; // sent not encrypted request number
|
||||
@ -261,6 +258,7 @@ private:
|
||||
struct AuthKeyCreateStrings {
|
||||
QByteArray dh_prime;
|
||||
QByteArray g_a;
|
||||
AuthKey::Data auth_key = { 0 };
|
||||
};
|
||||
std::unique_ptr<AuthKeyCreateData> _authKeyData;
|
||||
std::unique_ptr<AuthKeyCreateStrings> _authKeyStrings;
|
||||
|
@ -895,8 +895,12 @@ AuthKeysMap getKeys() {
|
||||
return internal::getAuthKeys();
|
||||
}
|
||||
|
||||
void setKey(int32 dc, AuthKeyPtr key) {
|
||||
return internal::setAuthKey(dc, key);
|
||||
void setKey(int dc, const AuthKey::Data &key) {
|
||||
auto dcId = MTP::bareDcId(dc);
|
||||
auto keyPtr = std::make_shared<MTP::AuthKey>();
|
||||
keyPtr->setDC(dcId);
|
||||
keyPtr->setKey(key);
|
||||
return internal::setAuthKey(dc, std::move(keyPtr));
|
||||
}
|
||||
|
||||
QReadWriteLock *dcOptionsMutex() {
|
||||
|
@ -220,7 +220,7 @@ void clearGlobalHandlers();
|
||||
void updateDcOptions(const QVector<MTPDcOption> &options);
|
||||
|
||||
AuthKeysMap getKeys();
|
||||
void setKey(int32 dc, AuthKeyPtr key);
|
||||
void setKey(int dc, const AuthKey::Data &key);
|
||||
|
||||
QReadWriteLock *dcOptionsMutex();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user