From 299143108b66de9e1fbb1cf4938e7682d86db855 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 31 Dec 2016 12:58:56 +0400 Subject: [PATCH] Improve protocol support. --- Telegram/SourceFiles/mtproto/auth_key.cpp | 62 +++++++++++++++- Telegram/SourceFiles/mtproto/auth_key.h | 68 +++++++----------- Telegram/SourceFiles/mtproto/connection.cpp | 80 ++++++++++++++++++--- Telegram/SourceFiles/mtproto/core_types.h | 11 +++ 4 files changed, 169 insertions(+), 52 deletions(-) diff --git a/Telegram/SourceFiles/mtproto/auth_key.cpp b/Telegram/SourceFiles/mtproto/auth_key.cpp index c859564451..24d7f13a2d 100644 --- a/Telegram/SourceFiles/mtproto/auth_key.cpp +++ b/Telegram/SourceFiles/mtproto/auth_key.cpp @@ -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(&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); + 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(&aesKey); + auto iv = reinterpret_cast(&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(src), static_cast(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); diff --git a/Telegram/SourceFiles/mtproto/auth_key.h b/Telegram/SourceFiles/mtproto/auth_key.h index d11ae7cc03..823eefde35 100644 --- a/Telegram/SourceFiles/mtproto/auth_key.h +++ b/Telegram/SourceFiles/mtproto/auth_key.h @@ -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(&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); - 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; using AuthKeysList = std::vector; -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(&aesKey), static_cast(&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(&aesKey), static_cast(&aesIV)); + return aesIgeEncryptRaw(src, dst, len, static_cast(&aesKey), static_cast(&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(&aesKey), static_cast(&aesIV)); + return aesIgeEncryptRaw(src, dst, len, static_cast(&aesKey), static_cast(&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(&aesKey), static_cast(&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(&aesKey), static_cast(&aesIV)); + return aesIgeDecryptRaw(src, dst, len, static_cast(&aesKey), static_cast(&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(&aesKey), static_cast(&aesIV)); + return aesIgeDecryptRaw(src, dst, len, static_cast(&aesKey), static_cast(&aesIV)); } // ctr used inplace, encrypt the data and leave it at the same place diff --git a/Telegram/SourceFiles/mtproto/connection.cpp b/Telegram/SourceFiles/mtproto/connection.cpp index 4aec606773..21f4849f14 100644 --- a/Telegram/SourceFiles/mtproto/connection.cpp +++ b/Telegram/SourceFiles/mtproto/connection.cpp @@ -43,6 +43,9 @@ constexpr auto kRecreateKeyId = AuthKey::KeyId(0xFFFFFFFFFFFFFFFFULL); constexpr auto kIntSize = static_cast(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(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(encryptedBytesCount) - static_cast(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 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])); diff --git a/Telegram/SourceFiles/mtproto/core_types.h b/Telegram/SourceFiles/mtproto/core_types.h index 00b3d59b3a..4eaeb45e11 100644 --- a/Telegram/SourceFiles/mtproto/core_types.h +++ b/Telegram/SourceFiles/mtproto/core_types.h @@ -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 } };