MTP::AuthKey uses std::array for data.

Also using "volatile char*" iteration for secure memset on Mac/Linux.
This commit is contained in:
John Preston 2017-02-22 18:18:26 +03:00
parent 21efb44e81
commit 90b3b5c3e5
8 changed files with 110 additions and 75 deletions

View File

@ -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,

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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;

View File

@ -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() {

View File

@ -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();