2019-11-13 14:12:04 +00:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
|
|
|
the official desktop application for the Telegram messaging service.
|
|
|
|
|
|
|
|
For license and copyright information please follow this link:
|
|
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
|
|
*/
|
|
|
|
#include "mtproto/mtproto_dh_utils.h"
|
|
|
|
|
2021-09-15 10:21:45 +00:00
|
|
|
#include "base/openssl_help.h"
|
|
|
|
|
2019-11-13 14:12:04 +00:00
|
|
|
namespace MTP {
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
constexpr auto kMaxModExpSize = 256;
|
|
|
|
|
|
|
|
bool IsPrimeAndGoodCheck(const openssl::BigNum &prime, int g) {
|
|
|
|
constexpr auto kGoodPrimeBitsCount = 2048;
|
|
|
|
|
|
|
|
if (prime.failed()
|
|
|
|
|| prime.isNegative()
|
|
|
|
|| prime.bitsSize() != kGoodPrimeBitsCount) {
|
|
|
|
LOG(("MTP Error: Bad prime bits count %1, expected %2."
|
|
|
|
).arg(prime.bitsSize()
|
|
|
|
).arg(kGoodPrimeBitsCount));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto context = openssl::Context();
|
|
|
|
if (!prime.isPrime(context)) {
|
|
|
|
LOG(("MTP Error: Bad prime."));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (g) {
|
|
|
|
case 2: {
|
|
|
|
const auto mod8 = prime.countModWord(8);
|
|
|
|
if (mod8 != 7) {
|
|
|
|
LOG(("BigNum PT Error: bad g value: %1, mod8: %2").arg(g).arg(mod8));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case 3: {
|
|
|
|
const auto mod3 = prime.countModWord(3);
|
|
|
|
if (mod3 != 2) {
|
|
|
|
LOG(("BigNum PT Error: bad g value: %1, mod3: %2").arg(g).arg(mod3));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case 4: break;
|
|
|
|
case 5: {
|
|
|
|
const auto mod5 = prime.countModWord(5);
|
|
|
|
if (mod5 != 1 && mod5 != 4) {
|
|
|
|
LOG(("BigNum PT Error: bad g value: %1, mod5: %2").arg(g).arg(mod5));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case 6: {
|
|
|
|
const auto mod24 = prime.countModWord(24);
|
|
|
|
if (mod24 != 19 && mod24 != 23) {
|
|
|
|
LOG(("BigNum PT Error: bad g value: %1, mod24: %2").arg(g).arg(mod24));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case 7: {
|
|
|
|
const auto mod7 = prime.countModWord(7);
|
|
|
|
if (mod7 != 3 && mod7 != 5 && mod7 != 6) {
|
|
|
|
LOG(("BigNum PT Error: bad g value: %1, mod7: %2").arg(g).arg(mod7));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
default: {
|
|
|
|
LOG(("BigNum PT Error: bad g value: %1").arg(g));
|
|
|
|
return false;
|
|
|
|
} break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!openssl::BigNum(prime).subWord(1).divWord(2).isPrime(context)) {
|
|
|
|
LOG(("MTP Error: Bad (prime - 1) / 2."));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
bool IsGoodModExpFirst(
|
|
|
|
const openssl::BigNum &modexp,
|
|
|
|
const openssl::BigNum &prime) {
|
|
|
|
const auto diff = openssl::BigNum::Sub(prime, modexp);
|
|
|
|
if (modexp.failed() || prime.failed() || diff.failed()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
constexpr auto kMinDiffBitsCount = 2048 - 64;
|
|
|
|
if (diff.isNegative()
|
|
|
|
|| diff.bitsSize() < kMinDiffBitsCount
|
|
|
|
|| modexp.bitsSize() < kMinDiffBitsCount
|
|
|
|
|| modexp.bytesSize() > kMaxModExpSize) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsPrimeAndGood(bytes::const_span primeBytes, int g) {
|
|
|
|
static constexpr unsigned char GoodPrime[] = {
|
|
|
|
0xC7, 0x1C, 0xAE, 0xB9, 0xC6, 0xB1, 0xC9, 0x04, 0x8E, 0x6C, 0x52, 0x2F, 0x70, 0xF1, 0x3F, 0x73,
|
|
|
|
0x98, 0x0D, 0x40, 0x23, 0x8E, 0x3E, 0x21, 0xC1, 0x49, 0x34, 0xD0, 0x37, 0x56, 0x3D, 0x93, 0x0F,
|
|
|
|
0x48, 0x19, 0x8A, 0x0A, 0xA7, 0xC1, 0x40, 0x58, 0x22, 0x94, 0x93, 0xD2, 0x25, 0x30, 0xF4, 0xDB,
|
|
|
|
0xFA, 0x33, 0x6F, 0x6E, 0x0A, 0xC9, 0x25, 0x13, 0x95, 0x43, 0xAE, 0xD4, 0x4C, 0xCE, 0x7C, 0x37,
|
|
|
|
0x20, 0xFD, 0x51, 0xF6, 0x94, 0x58, 0x70, 0x5A, 0xC6, 0x8C, 0xD4, 0xFE, 0x6B, 0x6B, 0x13, 0xAB,
|
|
|
|
0xDC, 0x97, 0x46, 0x51, 0x29, 0x69, 0x32, 0x84, 0x54, 0xF1, 0x8F, 0xAF, 0x8C, 0x59, 0x5F, 0x64,
|
|
|
|
0x24, 0x77, 0xFE, 0x96, 0xBB, 0x2A, 0x94, 0x1D, 0x5B, 0xCD, 0x1D, 0x4A, 0xC8, 0xCC, 0x49, 0x88,
|
|
|
|
0x07, 0x08, 0xFA, 0x9B, 0x37, 0x8E, 0x3C, 0x4F, 0x3A, 0x90, 0x60, 0xBE, 0xE6, 0x7C, 0xF9, 0xA4,
|
|
|
|
0xA4, 0xA6, 0x95, 0x81, 0x10, 0x51, 0x90, 0x7E, 0x16, 0x27, 0x53, 0xB5, 0x6B, 0x0F, 0x6B, 0x41,
|
|
|
|
0x0D, 0xBA, 0x74, 0xD8, 0xA8, 0x4B, 0x2A, 0x14, 0xB3, 0x14, 0x4E, 0x0E, 0xF1, 0x28, 0x47, 0x54,
|
|
|
|
0xFD, 0x17, 0xED, 0x95, 0x0D, 0x59, 0x65, 0xB4, 0xB9, 0xDD, 0x46, 0x58, 0x2D, 0xB1, 0x17, 0x8D,
|
|
|
|
0x16, 0x9C, 0x6B, 0xC4, 0x65, 0xB0, 0xD6, 0xFF, 0x9C, 0xA3, 0x92, 0x8F, 0xEF, 0x5B, 0x9A, 0xE4,
|
|
|
|
0xE4, 0x18, 0xFC, 0x15, 0xE8, 0x3E, 0xBE, 0xA0, 0xF8, 0x7F, 0xA9, 0xFF, 0x5E, 0xED, 0x70, 0x05,
|
|
|
|
0x0D, 0xED, 0x28, 0x49, 0xF4, 0x7B, 0xF9, 0x59, 0xD9, 0x56, 0x85, 0x0C, 0xE9, 0x29, 0x85, 0x1F,
|
|
|
|
0x0D, 0x81, 0x15, 0xF6, 0x35, 0xB1, 0x05, 0xEE, 0x2E, 0x4E, 0x15, 0xD0, 0x4B, 0x24, 0x54, 0xBF,
|
|
|
|
0x6F, 0x4F, 0xAD, 0xF0, 0x34, 0xB1, 0x04, 0x03, 0x11, 0x9C, 0xD8, 0xE3, 0xB9, 0x2F, 0xCC, 0x5B };
|
|
|
|
|
|
|
|
if (!bytes::compare(bytes::make_span(GoodPrime), primeBytes)) {
|
|
|
|
if (g == 3 || g == 4 || g == 5 || g == 7) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return IsPrimeAndGoodCheck(openssl::BigNum(primeBytes), g);
|
|
|
|
}
|
|
|
|
|
|
|
|
ModExpFirst CreateModExp(
|
|
|
|
int g,
|
|
|
|
bytes::const_span primeBytes,
|
|
|
|
bytes::const_span randomSeed) {
|
|
|
|
Expects(randomSeed.size() == ModExpFirst::kRandomPowerSize);
|
|
|
|
|
|
|
|
using namespace openssl;
|
|
|
|
|
|
|
|
BigNum prime(primeBytes);
|
|
|
|
auto result = ModExpFirst();
|
|
|
|
result.randomPower.resize(ModExpFirst::kRandomPowerSize);
|
|
|
|
while (true) {
|
|
|
|
bytes::set_random(result.randomPower);
|
|
|
|
for (auto i = 0; i != ModExpFirst::kRandomPowerSize; ++i) {
|
|
|
|
result.randomPower[i] ^= randomSeed[i];
|
|
|
|
}
|
|
|
|
const auto modexp = BigNum::ModExp(
|
|
|
|
BigNum(g),
|
|
|
|
BigNum(result.randomPower),
|
|
|
|
prime);
|
|
|
|
if (IsGoodModExpFirst(modexp, prime)) {
|
|
|
|
result.modexp = modexp.getBytes();
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bytes::vector CreateAuthKey(
|
|
|
|
bytes::const_span firstBytes,
|
|
|
|
bytes::const_span randomBytes,
|
|
|
|
bytes::const_span primeBytes) {
|
|
|
|
using openssl::BigNum;
|
|
|
|
|
|
|
|
const auto first = BigNum(firstBytes);
|
|
|
|
const auto prime = BigNum(primeBytes);
|
|
|
|
if (!IsGoodModExpFirst(first, prime)) {
|
|
|
|
LOG(("AuthKey Error: Bad first prime in CreateAuthKey()."));
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
return BigNum::ModExp(first, BigNum(randomBytes), prime).getBytes();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace MTP
|