tdesktop/Telegram/SourceFiles/mtproto/mtproto_dh_utils.cpp

176 lines
5.4 KiB
C++

/*
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"
#include "base/openssl_help.h"
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