From 90b3b5c3e52beabab51d3283ea78ca7fe24f737a Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 22 Feb 2017 18:18:26 +0300 Subject: [PATCH] MTP::AuthKey uses std::array for data. Also using "volatile char*" iteration for secure memset on Mac/Linux. --- Telegram/SourceFiles/config.h | 1 - Telegram/SourceFiles/core/utils.h | 25 ++++++++++ Telegram/SourceFiles/localstorage.cpp | 40 +++++++--------- Telegram/SourceFiles/mtproto/auth_key.h | 52 ++++++++++---------- Telegram/SourceFiles/mtproto/connection.cpp | 53 +++++++++++++-------- Telegram/SourceFiles/mtproto/connection.h | 4 +- Telegram/SourceFiles/mtproto/facade.cpp | 8 +++- Telegram/SourceFiles/mtproto/facade.h | 2 +- 8 files changed, 110 insertions(+), 75 deletions(-) diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index 235690160..8a4ad1e43 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -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, diff --git a/Telegram/SourceFiles/core/utils.h b/Telegram/SourceFiles/core/utils.h index ce97b7c3a..3e63aba87 100644 --- a/Telegram/SourceFiles/core/utils.h +++ b/Telegram/SourceFiles/core/utils.h @@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #pragma once #include "core/basic_types.h" +#include 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 hashSha1(const void *data, int len) { + auto result = std::array(); + 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 hashSha256(const void *data, int size) { + auto result = std::array(); + 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 hashMd5(const void *data, int size) { + auto result = std::array(); + 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 hashMd5Hex(const void *data, int size) { + auto result = std::array(); + hashMd5Hex(data, size, result.data()); + return result; +} // good random (using openssl implementation) void memset_rand(void *data, uint32 len); diff --git a/Telegram/SourceFiles/localstorage.cpp b/Telegram/SourceFiles/localstorage.cpp index 5b21808a0..cbcf62a74 100644 --- a/Telegram/SourceFiles/localstorage.cpp +++ b/Telegram/SourceFiles/localstorage.cpp @@ -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); diff --git a/Telegram/SourceFiles/mtproto/auth_key.h b/Telegram/SourceFiles/mtproto/auth_key.h index 963940d1a..22860c14d 100644 --- a/Telegram/SourceFiles/mtproto/auth_key.h +++ b/Telegram/SourceFiles/mtproto/auth_key.h @@ -20,30 +20,34 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #pragma once +#include +#include + namespace MTP { class AuthKey { public: - - AuthKey() : _isset(false), _dc(0) { - } + static constexpr auto kSize = 256; // 2048 bits. + using Data = std::array; 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(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(&aesKey); + auto iv = reinterpret_cast(&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 AuthKeyPtr; -typedef QVector AuthKeysMap; +using AuthKeyPtr = std::shared_ptr; +using AuthKeysMap = QVector; 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); diff --git a/Telegram/SourceFiles/mtproto/connection.cpp b/Telegram/SourceFiles/mtproto/connection.cpp index 5d91fc6f5..cf67febb1 100644 --- a/Telegram/SourceFiles/mtproto/connection.cpp +++ b/Telegram/SourceFiles/mtproto/connection.cpp @@ -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->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(data) + size; + for (volatile auto p = static_cast(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); } diff --git a/Telegram/SourceFiles/mtproto/connection.h b/Telegram/SourceFiles/mtproto/connection.h index 54e3940d8..59704ea9e 100644 --- a/Telegram/SourceFiles/mtproto/connection.h +++ b/Telegram/SourceFiles/mtproto/connection.h @@ -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 _authKeyData; std::unique_ptr _authKeyStrings; diff --git a/Telegram/SourceFiles/mtproto/facade.cpp b/Telegram/SourceFiles/mtproto/facade.cpp index 561b641c8..7d846bea6 100644 --- a/Telegram/SourceFiles/mtproto/facade.cpp +++ b/Telegram/SourceFiles/mtproto/facade.cpp @@ -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(); + keyPtr->setDC(dcId); + keyPtr->setKey(key); + return internal::setAuthKey(dc, std::move(keyPtr)); } QReadWriteLock *dcOptionsMutex() { diff --git a/Telegram/SourceFiles/mtproto/facade.h b/Telegram/SourceFiles/mtproto/facade.h index 4f2086485..94f49743e 100644 --- a/Telegram/SourceFiles/mtproto/facade.h +++ b/Telegram/SourceFiles/mtproto/facade.h @@ -220,7 +220,7 @@ void clearGlobalHandlers(); void updateDcOptions(const QVector &options); AuthKeysMap getKeys(); -void setKey(int32 dc, AuthKeyPtr key); +void setKey(int dc, const AuthKey::Data &key); QReadWriteLock *dcOptionsMutex();