Improve protocol support.

This commit is contained in:
John Preston 2016-12-31 12:58:56 +04:00
parent 0339b1b54b
commit 299143108b
4 changed files with 169 additions and 52 deletions

View file

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

View file

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

View file

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

View file

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