mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-03-25 04:38:23 +00:00
Improve protocol support.
This commit is contained in:
parent
0339b1b54b
commit
299143108b
@ -24,7 +24,65 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||
|
||||
namespace MTP {
|
||||
|
||||
void aesIgeEncrypt(const void *src, void *dst, uint32 len, const void *key, const void *iv) {
|
||||
void AuthKey::prepareAES_oldmtp(const MTPint128 &msgKey, MTPint256 &aesKey, MTPint256 &aesIV, bool send) const {
|
||||
uint32 x = send ? 0 : 8;
|
||||
|
||||
uchar data_a[16 + 32], sha1_a[20];
|
||||
memcpy(data_a, &msgKey, 16);
|
||||
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.data() + 32 + x, 16);
|
||||
memcpy(data_b + 16, &msgKey, 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.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.data() + 96 + x, 32);
|
||||
hashSha1(data_d, 16 + 32, sha1_d);
|
||||
|
||||
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);
|
||||
memcpy(iv, sha1_a + 8, 12);
|
||||
memcpy(iv + 12, sha1_b, 8);
|
||||
memcpy(iv + 12 + 8, sha1_c + 16, 4);
|
||||
memcpy(iv + 12 + 8 + 4, sha1_d, 8);
|
||||
}
|
||||
|
||||
void AuthKey::prepareAES(const MTPint128 &msgKey, MTPint256 &aesKey, MTPint256 &aesIV, bool send) const {
|
||||
uint32 x = send ? 0 : 8;
|
||||
|
||||
uchar data_a[16 + 36], sha256_a[32];
|
||||
memcpy(data_a, &msgKey, 16);
|
||||
memcpy(data_a + 16, _key.data() + x, 36);
|
||||
hashSha256(data_a, 16 + 36, sha256_a);
|
||||
|
||||
uchar data_b[36 + 16], sha256_b[32];
|
||||
memcpy(data_b, _key.data() + 40 + x, 36);
|
||||
memcpy(data_b + 36, &msgKey, 16);
|
||||
hashSha256(data_b, 36 + 16, sha256_b);
|
||||
|
||||
auto key = reinterpret_cast<uchar*>(&aesKey);
|
||||
auto iv = reinterpret_cast<uchar*>(&aesIV);
|
||||
memcpy(key, sha256_a, 8);
|
||||
memcpy(key + 8, sha256_b + 8, 16);
|
||||
memcpy(key + 8 + 16, sha256_a + 24, 8);
|
||||
memcpy(iv, sha256_b, 8);
|
||||
memcpy(iv + 8, sha256_a + 8, 16);
|
||||
memcpy(iv + 8 + 16, sha256_b + 24, 8);
|
||||
}
|
||||
|
||||
void aesIgeEncryptRaw(const void *src, void *dst, uint32 len, const void *key, const void *iv) {
|
||||
uchar aes_key[32], aes_iv[32];
|
||||
memcpy(aes_key, key, 32);
|
||||
memcpy(aes_iv, iv, 32);
|
||||
@ -34,7 +92,7 @@ void aesIgeEncrypt(const void *src, void *dst, uint32 len, const void *key, cons
|
||||
AES_ige_encrypt(static_cast<const uchar*>(src), static_cast<uchar*>(dst), len, &aes, aes_iv, AES_ENCRYPT);
|
||||
}
|
||||
|
||||
void aesIgeDecrypt(const void *src, void *dst, uint32 len, const void *key, const void *iv) {
|
||||
void aesIgeDecryptRaw(const void *src, void *dst, uint32 len, const void *key, const void *iv) {
|
||||
uchar aes_key[32], aes_iv[32];
|
||||
memcpy(aes_key, key, 32);
|
||||
memcpy(aes_iv, iv, 32);
|
||||
|
@ -58,39 +58,11 @@ public:
|
||||
return _keyId;
|
||||
}
|
||||
|
||||
void prepareAES(const MTPint128 &msgKey, MTPint256 &aesKey, MTPint256 &aesIV, bool send = true) const {
|
||||
uint32 x = send ? 0 : 8;
|
||||
void prepareAES_oldmtp(const MTPint128 &msgKey, MTPint256 &aesKey, MTPint256 &aesIV, bool send) const;
|
||||
void prepareAES(const MTPint128 &msgKey, MTPint256 &aesKey, MTPint256 &aesIV, bool send) const;
|
||||
|
||||
uchar data_a[16 + 32], sha1_a[20];
|
||||
memcpy(data_a, &msgKey, 16);
|
||||
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.data() + 32 + x, 16);
|
||||
memcpy(data_b + 16, &msgKey, 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.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.data() + 96 + x, 32);
|
||||
hashSha1(data_d, 16 + 32, sha1_d);
|
||||
|
||||
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);
|
||||
memcpy(iv, sha1_a + 8, 12);
|
||||
memcpy(iv + 12, sha1_b, 8);
|
||||
memcpy(iv + 12 + 8, sha1_c + 16, 4);
|
||||
memcpy(iv + 12 + 8 + 4, sha1_d, 8);
|
||||
const void *partForMsgKey(bool send) const {
|
||||
return _key.data() + 88 + (send ? 0 : 8);
|
||||
}
|
||||
|
||||
void write(QDataStream &to) const {
|
||||
@ -131,35 +103,49 @@ private:
|
||||
using AuthKeyPtr = std::shared_ptr<AuthKey>;
|
||||
using AuthKeysList = std::vector<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);
|
||||
void aesIgeEncryptRaw(const void *src, void *dst, uint32 len, const void *key, const void *iv);
|
||||
void aesIgeDecryptRaw(const void *src, void *dst, uint32 len, const void *key, const void *iv);
|
||||
|
||||
inline void aesIgeEncrypt_oldmtp(const void *src, void *dst, uint32 len, const AuthKeyPtr &authKey, const MTPint128 &msgKey) {
|
||||
MTPint256 aesKey, aesIV;
|
||||
authKey->prepareAES_oldmtp(msgKey, aesKey, aesIV, true);
|
||||
|
||||
return aesIgeEncryptRaw(src, dst, len, static_cast<const void*>(&aesKey), static_cast<const void*>(&aesIV));
|
||||
}
|
||||
|
||||
inline void aesIgeEncrypt(const void *src, void *dst, uint32 len, const AuthKeyPtr &authKey, const MTPint128 &msgKey) {
|
||||
MTPint256 aesKey, aesIV;
|
||||
authKey->prepareAES(msgKey, aesKey, aesIV);
|
||||
authKey->prepareAES(msgKey, aesKey, aesIV, true);
|
||||
|
||||
return aesIgeEncrypt(src, dst, len, static_cast<const void*>(&aesKey), static_cast<const void*>(&aesIV));
|
||||
return aesIgeEncryptRaw(src, dst, len, static_cast<const void*>(&aesKey), static_cast<const void*>(&aesIV));
|
||||
}
|
||||
|
||||
inline void aesEncryptLocal(const void *src, void *dst, uint32 len, const AuthKeyPtr &authKey, const void *key128) {
|
||||
MTPint256 aesKey, aesIV;
|
||||
authKey->prepareAES(*(const MTPint128*)key128, aesKey, aesIV, false);
|
||||
authKey->prepareAES_oldmtp(*(const MTPint128*)key128, aesKey, aesIV, false);
|
||||
|
||||
return aesIgeEncrypt(src, dst, len, static_cast<const void*>(&aesKey), static_cast<const void*>(&aesIV));
|
||||
return aesIgeEncryptRaw(src, dst, len, static_cast<const void*>(&aesKey), static_cast<const void*>(&aesIV));
|
||||
}
|
||||
|
||||
inline void aesIgeDecrypt_oldmtp(const void *src, void *dst, uint32 len, const AuthKeyPtr &authKey, const MTPint128 &msgKey) {
|
||||
MTPint256 aesKey, aesIV;
|
||||
authKey->prepareAES_oldmtp(msgKey, aesKey, aesIV, false);
|
||||
|
||||
return aesIgeDecryptRaw(src, dst, len, static_cast<const void*>(&aesKey), static_cast<const void*>(&aesIV));
|
||||
}
|
||||
|
||||
inline void aesIgeDecrypt(const void *src, void *dst, uint32 len, const AuthKeyPtr &authKey, const MTPint128 &msgKey) {
|
||||
MTPint256 aesKey, aesIV;
|
||||
authKey->prepareAES(msgKey, aesKey, aesIV, false);
|
||||
|
||||
return aesIgeDecrypt(src, dst, len, static_cast<const void*>(&aesKey), static_cast<const void*>(&aesIV));
|
||||
return aesIgeDecryptRaw(src, dst, len, static_cast<const void*>(&aesKey), static_cast<const void*>(&aesIV));
|
||||
}
|
||||
|
||||
inline void aesDecryptLocal(const void *src, void *dst, uint32 len, const AuthKeyPtr &authKey, const void *key128) {
|
||||
MTPint256 aesKey, aesIV;
|
||||
authKey->prepareAES(*(const MTPint128*)key128, aesKey, aesIV, false);
|
||||
authKey->prepareAES_oldmtp(*(const MTPint128*)key128, aesKey, aesIV, false);
|
||||
|
||||
return aesIgeDecrypt(src, dst, len, static_cast<const void*>(&aesKey), static_cast<const void*>(&aesIV));
|
||||
return aesIgeDecryptRaw(src, dst, len, static_cast<const void*>(&aesKey), static_cast<const void*>(&aesIV));
|
||||
}
|
||||
|
||||
// ctr used inplace, encrypt the data and leave it at the same place
|
||||
|
@ -43,6 +43,9 @@ constexpr auto kRecreateKeyId = AuthKey::KeyId(0xFFFFFFFFFFFFFFFFULL);
|
||||
constexpr auto kIntSize = static_cast<int>(sizeof(mtpPrime));
|
||||
constexpr auto kMaxModExpSize = 256;
|
||||
|
||||
// Don't try to handle messages larger than this size.
|
||||
constexpr auto kMaxMessageLength = 16 * 1024 * 1024;
|
||||
|
||||
bool IsGoodModExpFirst(const openssl::BigNum &modexp, const openssl::BigNum &prime) {
|
||||
auto diff = prime - modexp;
|
||||
if (modexp.failed() || prime.failed() || diff.failed()) {
|
||||
@ -1266,7 +1269,7 @@ void ConnectionPrivate::handleReceived() {
|
||||
constexpr auto kMinimalIntsCount = kExternalHeaderIntsCount + kMinimalEncryptedIntsCount;
|
||||
auto intsCount = uint32(intsBuffer.size());
|
||||
auto ints = intsBuffer.constData();
|
||||
if (intsCount < kMinimalIntsCount) {
|
||||
if ((intsCount < kMinimalIntsCount) || (intsCount > kMaxMessageLength / kIntSize)) {
|
||||
LOG(("TCP Error: bad message received, len %1").arg(intsCount * kIntSize));
|
||||
TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(ints, intsCount * kIntSize).str()));
|
||||
|
||||
@ -1285,7 +1288,11 @@ void ConnectionPrivate::handleReceived() {
|
||||
auto decryptedBuffer = QByteArray(encryptedBytesCount, Qt::Uninitialized);
|
||||
auto msgKey = *(MTPint128*)(ints + 2);
|
||||
|
||||
#ifdef TDESKTOP_MTPROTO_OLD
|
||||
aesIgeDecrypt_oldmtp(encryptedInts, decryptedBuffer.data(), encryptedBytesCount, key, msgKey);
|
||||
#else // TDESKTOP_MTPROTO_OLD
|
||||
aesIgeDecrypt(encryptedInts, decryptedBuffer.data(), encryptedBytesCount, key, msgKey);
|
||||
#endif // TDESKTOP_MTPROTO_OLD
|
||||
|
||||
auto decryptedInts = reinterpret_cast<const mtpPrime*>(decryptedBuffer.constData());
|
||||
auto serverSalt = *(uint64*)&decryptedInts[0];
|
||||
@ -1295,19 +1302,55 @@ void ConnectionPrivate::handleReceived() {
|
||||
auto needAck = ((seqNo & 0x01) != 0);
|
||||
|
||||
auto messageLength = *(uint32*)&decryptedInts[7];
|
||||
if (messageLength > kMaxMessageLength) {
|
||||
LOG(("TCP Error: bad messageLength %1").arg(messageLength));
|
||||
TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(ints, intsCount * kIntSize).str()));
|
||||
|
||||
return restartOnError();
|
||||
|
||||
}
|
||||
auto fullDataLength = kEncryptedHeaderIntsCount * kIntSize + messageLength; // Without padding.
|
||||
|
||||
constexpr auto kMaxPaddingSize = 15U;
|
||||
auto paddingSize = encryptedBytesCount - fullDataLength; // Can underflow.
|
||||
auto badMessageLength = (/*paddingSize < 0 || */paddingSize > kMaxPaddingSize);
|
||||
// Can underflow, but it is an unsigned type, so we just check the range later.
|
||||
auto paddingSize = static_cast<uint32>(encryptedBytesCount) - static_cast<uint32>(fullDataLength);
|
||||
|
||||
#ifdef TDESKTOP_MTPROTO_OLD
|
||||
constexpr auto kMinPaddingSize_oldmtp = 0U;
|
||||
constexpr auto kMaxPaddingSize_oldmtp = 15U;
|
||||
auto badMessageLength = (/*paddingSize < kMinPaddingSize_oldmtp || */paddingSize > kMaxPaddingSize_oldmtp);
|
||||
|
||||
auto hashedDataLength = badMessageLength ? encryptedBytesCount : fullDataLength;
|
||||
auto sha1ForMsgKeyCheck = hashSha1(decryptedInts, hashedDataLength);
|
||||
if (memcmp(&msgKey, sha1ForMsgKeyCheck.data() + sha1ForMsgKeyCheck.size() - sizeof(msgKey), sizeof(msgKey)) != 0) {
|
||||
|
||||
constexpr auto kMsgKeyShift_oldmtp = 4U;
|
||||
if (memcmp(&msgKey, sha1ForMsgKeyCheck.data() + kMsgKeyShift_oldmtp, sizeof(msgKey)) != 0) {
|
||||
LOG(("TCP Error: bad SHA1 hash after aesDecrypt in message."));
|
||||
TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(encryptedInts, encryptedBytesCount).str()));
|
||||
|
||||
return restartOnError();
|
||||
}
|
||||
#else // TDESKTOP_MTPROTO_OLD
|
||||
constexpr auto kMinPaddingSize = 12U;
|
||||
constexpr auto kMaxPaddingSize = 1024U;
|
||||
auto badMessageLength = (paddingSize < kMinPaddingSize || paddingSize > kMaxPaddingSize);
|
||||
|
||||
std::array<uchar, 32> sha256Buffer = { { 0 } };
|
||||
|
||||
SHA256_CTX msgKeyLargeContext;
|
||||
SHA256_Init(&msgKeyLargeContext);
|
||||
SHA256_Update(&msgKeyLargeContext, key->partForMsgKey(false), 32);
|
||||
SHA256_Update(&msgKeyLargeContext, decryptedInts, encryptedBytesCount);
|
||||
SHA256_Final(sha256Buffer.data(), &msgKeyLargeContext);
|
||||
|
||||
constexpr auto kMsgKeyShift = 8U;
|
||||
if (memcmp(&msgKey, sha256Buffer.data() + kMsgKeyShift, sizeof(msgKey)) != 0) {
|
||||
LOG(("TCP Error: bad SHA256 hash after aesDecrypt in message"));
|
||||
TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(encryptedInts, encryptedBytesCount).str()));
|
||||
|
||||
return restartOnError();
|
||||
}
|
||||
#endif // TDESKTOP_MTPROTO_OLD
|
||||
|
||||
if (badMessageLength || (messageLength & 0x03)) {
|
||||
LOG(("TCP Error: bad msg_len received %1, data size: %2").arg(messageLength).arg(encryptedBytesCount));
|
||||
TCP_LOG(("TCP Error: bad message %1").arg(Logs::mb(encryptedInts, encryptedBytesCount).str()));
|
||||
@ -2465,7 +2508,7 @@ void ConnectionPrivate::dhParamsAnswered() {
|
||||
memcpy(_authKeyData->aesIV + 8, sha1nn, 20);
|
||||
memcpy(_authKeyData->aesIV + 28, &_authKeyData->new_nonce, 4);
|
||||
|
||||
aesIgeDecrypt(encDHStr.constData(), &decBuffer[0], encDHLen, _authKeyData->aesKey, _authKeyData->aesIV);
|
||||
aesIgeDecryptRaw(encDHStr.constData(), &decBuffer[0], encDHLen, _authKeyData->aesKey, _authKeyData->aesIV);
|
||||
|
||||
const mtpPrime *from(&decBuffer[5]), *to(from), *end(from + (encDHBufLen - 5));
|
||||
MTPServer_DH_inner_data dh_inner;
|
||||
@ -2591,7 +2634,7 @@ std::string ConnectionPrivate::encryptClientDHInner(const MTPClient_DH_Inner_Dat
|
||||
|
||||
auto sdhEncString = std::string(encFullSize * 4, ' ');
|
||||
|
||||
aesIgeEncrypt(&encBuffer[0], &sdhEncString[0], encFullSize * sizeof(mtpPrime), _authKeyData->aesKey, _authKeyData->aesIV);
|
||||
aesIgeEncryptRaw(&encBuffer[0], &sdhEncString[0], encFullSize * sizeof(mtpPrime), _authKeyData->aesKey, _authKeyData->aesIV);
|
||||
|
||||
return sdhEncString;
|
||||
}
|
||||
@ -2893,7 +2936,7 @@ bool ConnectionPrivate::sendRequest(mtpRequest &request, bool needAnyResponse, Q
|
||||
return false;
|
||||
}
|
||||
|
||||
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(_shiftedDcId));
|
||||
|
||||
@ -2902,7 +2945,6 @@ bool ConnectionPrivate::sendRequest(mtpRequest &request, bool needAnyResponse, Q
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 padding = fullSize - 4 - messageSize;
|
||||
uint64 session(sessionData->getSession()), salt(sessionData->getSalt());
|
||||
|
||||
memcpy(request->data() + 0, &salt, 2 * sizeof(mtpPrime));
|
||||
@ -2911,6 +2953,9 @@ bool ConnectionPrivate::sendRequest(mtpRequest &request, bool needAnyResponse, Q
|
||||
const mtpPrime *from = request->constData() + 4;
|
||||
MTP_LOG(_shiftedDcId, ("Send: ") + mtpTextSerialize(from, from + messageSize));
|
||||
|
||||
#ifdef TDESKTOP_MTPROTO_OLD
|
||||
uint32 padding = fullSize - 4 - messageSize;
|
||||
|
||||
uchar encryptedSHA[20];
|
||||
MTPint128 &msgKey(*(MTPint128*)(encryptedSHA + 4));
|
||||
hashSha1(request->constData(), (fullSize - padding) * sizeof(mtpPrime), encryptedSHA);
|
||||
@ -2920,7 +2965,24 @@ bool ConnectionPrivate::sendRequest(mtpRequest &request, bool needAnyResponse, Q
|
||||
*((uint64*)&result[2]) = keyId;
|
||||
*((MTPint128*)&result[4]) = msgKey;
|
||||
|
||||
aesIgeEncrypt_oldmtp(request->constData(), &result[8], fullSize * sizeof(mtpPrime), key, msgKey);
|
||||
#else // TDESKTOP_MTPROTO_OLD
|
||||
uchar encryptedSHA256[32];
|
||||
MTPint128 &msgKey(*(MTPint128*)(encryptedSHA256 + 8));
|
||||
|
||||
SHA256_CTX msgKeyLargeContext;
|
||||
SHA256_Init(&msgKeyLargeContext);
|
||||
SHA256_Update(&msgKeyLargeContext, key->partForMsgKey(true), 32);
|
||||
SHA256_Update(&msgKeyLargeContext, request->constData(), fullSize * sizeof(mtpPrime));
|
||||
SHA256_Final(encryptedSHA256, &msgKeyLargeContext);
|
||||
|
||||
mtpBuffer result;
|
||||
result.resize(9 + fullSize);
|
||||
*((uint64*)&result[2]) = keyId;
|
||||
*((MTPint128*)&result[4]) = msgKey;
|
||||
|
||||
aesIgeEncrypt(request->constData(), &result[8], fullSize * sizeof(mtpPrime), key, msgKey);
|
||||
#endif // TDESKTOP_MTPROTO_OLD
|
||||
|
||||
DEBUG_LOG(("MTP Info: sending request, size: %1, num: %2, time: %3").arg(fullSize + 6).arg((*request)[4]).arg((*request)[5]));
|
||||
|
||||
|
@ -100,7 +100,18 @@ public:
|
||||
|
||||
private:
|
||||
static uint32 _padding(uint32 requestSize) {
|
||||
#ifdef TDESKTOP_MTPROTO_OLD
|
||||
return ((8 + requestSize) & 0x03) ? (4 - ((8 + requestSize) & 0x03)) : 0;
|
||||
#else // TDESKTOP_MTPROTO_OLD
|
||||
auto result = ((8 + requestSize) & 0x03) ? (4 - ((8 + requestSize) & 0x03)) : 0;
|
||||
|
||||
// At least 12 bytes of random padding.
|
||||
if (result < 3) {
|
||||
result += 4;
|
||||
}
|
||||
|
||||
return result;
|
||||
#endif // TDESKTOP_MTPROTO_OLD
|
||||
}
|
||||
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user