From b267957abeb7ecd306a159c43ce6e57ecf31a4e2 Mon Sep 17 00:00:00 2001 From: John Preston <johnprestonmail@gmail.com> Date: Mon, 24 Apr 2017 15:16:38 +0300 Subject: [PATCH] Apply all MTP Key generation checks to calls. Also move all OpenSSL BN_* calls to base/openssl_help header. --- Telegram/SourceFiles/base/openssl_help.h | 81 ++- Telegram/SourceFiles/calls/calls_call.cpp | 107 ++-- Telegram/SourceFiles/calls/calls_call.h | 8 +- Telegram/SourceFiles/calls/calls_instance.cpp | 2 +- Telegram/SourceFiles/core/utils.h | 9 + Telegram/SourceFiles/mtproto/auth_key.h | 18 +- Telegram/SourceFiles/mtproto/connection.cpp | 470 +++++++----------- Telegram/SourceFiles/mtproto/connection.h | 18 +- Telegram/SourceFiles/mtproto/dc_options.cpp | 12 +- .../SourceFiles/mtproto/rsa_public_key.cpp | 42 +- Telegram/SourceFiles/mtproto/rsa_public_key.h | 10 +- Telegram/SourceFiles/storage/localstorage.cpp | 9 +- .../SourceFiles/storage/serialize_common.h | 63 ++- 13 files changed, 446 insertions(+), 403 deletions(-) diff --git a/Telegram/SourceFiles/base/openssl_help.h b/Telegram/SourceFiles/base/openssl_help.h index 90639e241..b8f260e06 100644 --- a/Telegram/SourceFiles/base/openssl_help.h +++ b/Telegram/SourceFiles/base/openssl_help.h @@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include <openssl/bn.h> #include <openssl/sha.h> +#include <openssl/rand.h> namespace openssl { @@ -98,9 +99,69 @@ public: _failed = true; } } + void setSub(const BigNum &a, const BigNum &b) { + if (a.failed() || b.failed()) { + _failed = true; + } else if (!BN_sub(raw(), a.raw(), b.raw())) { + _failed = true; + } + } + void setSubWord(unsigned int word) { + if (failed()) { + return; + } else if (!BN_sub_word(raw(), word)) { + _failed = true; + } + } + unsigned int setDivWord(unsigned int word) { + Expects(word != 0); + if (failed()) { + return (BN_ULONG)-1; + } + + auto result = BN_div_word(raw(), word); + if (result == (BN_ULONG)-1) { + _failed = true; + } + return result; + } bool isNegative() const { - return BN_is_negative(raw()); + return failed() ? false : BN_is_negative(raw()); + } + + bool isPrime(const Context &context = Context()) const { + if (failed()) { + return false; + } + constexpr auto kMillerRabinIterationCount = 30; + auto result = BN_is_prime_ex(raw(), kMillerRabinIterationCount, context.raw(), NULL); + if (result == 1) { + return true; + } else if (result != 0) { + _failed = true; + } + return false; + } + + unsigned int modWord(unsigned int word) const { + Expects(word != 0); + if (failed()) { + return (BN_ULONG)-1; + } + + auto result = BN_mod_word(raw(), word); + if (result == (BN_ULONG)-1) { + _failed = true; + } + return result; + } + + int bitsSize() const { + return failed() ? 0 : BN_num_bits(raw()); + } + int bytesSize() const { + return failed() ? 0 : BN_num_bytes(raw()); } std::vector<gsl::byte> getBytes() const { @@ -125,12 +186,24 @@ public: return _failed; } + static BigNum ModExp(const BigNum &base, const BigNum &power, const openssl::BigNum &mod) { + BigNum result; + result.setModExp(base, power, mod); + return result; + } + private: BIGNUM _data; - bool _failed = false; + mutable bool _failed = false; }; +inline BigNum operator-(const BigNum &a, const BigNum &b) { + BigNum result; + result.setSub(a, b); + return result; +} + inline std::array<gsl::byte, SHA256_DIGEST_LENGTH> Sha256(base::const_byte_span bytes) { auto result = std::array<gsl::byte, SHA256_DIGEST_LENGTH>(); SHA256(reinterpret_cast<const unsigned char*>(bytes.data()), bytes.size(), reinterpret_cast<unsigned char*>(result.data())); @@ -143,4 +216,8 @@ inline std::array<gsl::byte, SHA_DIGEST_LENGTH> Sha1(base::const_byte_span bytes return result; } +inline int FillRandom(base::byte_span bytes) { + return RAND_bytes(reinterpret_cast<unsigned char*>(bytes.data()), bytes.size()); +} + } // namespace openssl diff --git a/Telegram/SourceFiles/calls/calls_call.cpp b/Telegram/SourceFiles/calls/calls_call.cpp index 42a5e719f..560397cc6 100644 --- a/Telegram/SourceFiles/calls/calls_call.cpp +++ b/Telegram/SourceFiles/calls/calls_call.cpp @@ -24,9 +24,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "calls/calls_instance.h" #include "base/openssl_help.h" - -#include <openssl/rand.h> -#include <openssl/sha.h> +#include "mtproto/connection.h" #ifdef slots #undef slots @@ -59,25 +57,6 @@ void ConvertEndpoint(std::vector<tgvoip::Endpoint> &ep, const MTPDphoneConnectio ep.push_back(Endpoint((int64_t)mtc.vid.v, (uint16_t)mtc.vport.v, ipv4, ipv6, EP_TYPE_UDP_RELAY, (unsigned char*)mtc.vpeer_tag.v.data())); } -std::vector<gsl::byte> ComputeModExp(const DhConfig &config, const openssl::BigNum &base, const std::array<gsl::byte, Call::kRandomPowerSize> &randomPower) { - using namespace openssl; - - BigNum resultBN; - resultBN.setModExp(base, BigNum(randomPower), BigNum(config.p)); - auto result = resultBN.getBytes(); - constexpr auto kMaxModExpSize = 256; - t_assert(result.size() <= kMaxModExpSize); - return result; -} - -std::vector<gsl::byte> ComputeModExpFirst(const DhConfig &config, const std::array<gsl::byte, Call::kRandomPowerSize> &randomPower) { - return ComputeModExp(config, openssl::BigNum(config.g), randomPower); -} - -std::vector<gsl::byte> ComputeModExpFinal(const DhConfig &config, base::const_byte_span first, const std::array<gsl::byte, Call::kRandomPowerSize> &randomPower) { - return ComputeModExp(config, openssl::BigNum(first), randomPower); -} - constexpr auto kFingerprintDataSize = 256; uint64 ComputeFingerprint(const std::array<gsl::byte, kFingerprintDataSize> &authKey) { auto hash = openssl::Sha1(authKey); @@ -102,11 +81,20 @@ Call::Call(gsl::not_null<Delegate*> delegate, gsl::not_null<UserData*> user, Typ } } -void Call::generateRandomPower(base::const_byte_span random) { - Expects(random.size() == _randomPower.size()); - memset_rand(_randomPower.data(), _randomPower.size()); - for (auto i = 0, count = int(_randomPower.size()); i != count; i++) { - _randomPower[i] ^= random[i]; +void Call::generateModExpFirst(base::const_byte_span randomSeed) { + auto first = MTP::CreateModExp(_dhConfig.g, _dhConfig.p, randomSeed); + if (first.modexp.empty()) { + LOG(("Call Error: Could not compute mod-exp first.")); + setState(State::Failed); + return; + } + + _randomPower = first.randomPower; + if (_type == Type::Incoming) { + _gb = std::move(first.modexp); + } else { + _ga = std::move(first.modexp); + _gaHash = openssl::Sha256(_ga); } } @@ -117,27 +105,21 @@ void Call::start(base::const_byte_span random) { t_assert(_dhConfig.g != 0); t_assert(!_dhConfig.p.empty()); - generateRandomPower(random); - - if (_type == Type::Outgoing) { - startOutgoing(); - } else { - startIncoming(); + generateModExpFirst(random); + if (_state != State::Failed) { + if (_type == Type::Outgoing) { + startOutgoing(); + } else { + startIncoming(); + } } } void Call::startOutgoing() { - _ga = ComputeModExpFirst(_dhConfig, _randomPower); - if (_ga.empty()) { - LOG(("Call Error: Could not compute mod-exp first.")); - setState(State::Failed); - return; - } - _gaHash = openssl::Sha256(_ga); - auto randomID = rand_value<int32>(); + Expects(_type == Type::Outgoing); setState(State::Requesting); - request(MTPphone_RequestCall(_user->inputUser, MTP_int(randomID), MTP_bytes(_gaHash), MTP_phoneCallProtocol(MTP_flags(MTPDphoneCallProtocol::Flag::f_udp_p2p | MTPDphoneCallProtocol::Flag::f_udp_reflector), MTP_int(kMinLayer), MTP_int(kMaxLayer)))).done([this](const MTPphone_PhoneCall &result) { + request(MTPphone_RequestCall(_user->inputUser, MTP_int(rand_value<int32>()), MTP_bytes(_gaHash), MTP_phoneCallProtocol(MTP_flags(MTPDphoneCallProtocol::Flag::f_udp_p2p | MTPDphoneCallProtocol::Flag::f_udp_reflector), MTP_int(kMinLayer), MTP_int(kMaxLayer)))).done([this](const MTPphone_PhoneCall &result) { Expects(result.type() == mtpc_phone_phoneCall); auto &call = result.c_phone_phoneCall(); App::feedUsers(call.vusers); @@ -163,17 +145,17 @@ void Call::startOutgoing() { } void Call::startIncoming() { - setState(State::Ringing); + Expects(_type == Type::Incoming); + + request(MTPphone_ReceivedCall(MTP_inputPhoneCall(MTP_long(_id), MTP_long(_accessHash)))).done([this](const MTPBool &result) { + setState(State::Ringing); + }).fail([this](const RPCError &error) { + setState(State::Failed); + }).send(); } void Call::answer() { Expects(_type == Type::Incoming); - _gb = ComputeModExpFirst(_dhConfig, _randomPower); - if (_gb.empty()) { - LOG(("Call Error: Could not compute mod-exp first.")); - setState(State::Failed); - return; - } setState(State::ExchangingKeys); request(MTPphone_AcceptCall(MTP_inputPhoneCall(MTP_long(_id), MTP_long(_accessHash)), MTP_bytes(_gb), _protocol)).done([this](const MTPphone_PhoneCall &result) { @@ -300,23 +282,15 @@ bool Call::handleUpdate(const MTPPhoneCall &call) { void Call::confirmAcceptedCall(const MTPDphoneCallAccepted &call) { Expects(_type == Type::Outgoing); - // TODO check isGoodGaAndGb - auto computedAuthKey = ComputeModExpFinal(_dhConfig, byteVectorFromMTP(call.vg_b), _randomPower); + auto firstBytes = bytesFromMTP(call.vg_b); + auto computedAuthKey = MTP::CreateAuthKey(firstBytes, _randomPower, _dhConfig.p); if (computedAuthKey.empty()) { LOG(("Call Error: Could not compute mod-exp final.")); setState(State::Failed); return; } - auto computedAuthKeySize = computedAuthKey.size(); - t_assert(computedAuthKeySize <= kAuthKeySize); - auto authKeyBytes = gsl::make_span(_authKey); - if (computedAuthKeySize < kAuthKeySize) { - base::set_bytes(authKeyBytes.subspan(0, kAuthKeySize - computedAuthKeySize), gsl::byte()); - base::copy_bytes(authKeyBytes.subspan(kAuthKeySize - computedAuthKeySize), computedAuthKey); - } else { - base::copy_bytes(authKeyBytes, computedAuthKey); - } + MTP::AuthKey::FillData(_authKey, computedAuthKey); _keyFingerprint = ComputeFingerprint(_authKey); setState(State::ExchangingKeys); @@ -346,23 +320,14 @@ void Call::startConfirmedCall(const MTPDphoneCall &call) { return; } - // TODO check isGoodGaAndGb - auto computedAuthKey = ComputeModExpFinal(_dhConfig, firstBytes, _randomPower); + auto computedAuthKey = MTP::CreateAuthKey(firstBytes, _randomPower, _dhConfig.p); if (computedAuthKey.empty()) { LOG(("Call Error: Could not compute mod-exp final.")); setState(State::Failed); return; } - auto computedAuthKeySize = computedAuthKey.size(); - t_assert(computedAuthKeySize <= kAuthKeySize); - auto authKeyBytes = gsl::make_span(_authKey); - if (computedAuthKeySize < kAuthKeySize) { - base::set_bytes(authKeyBytes.subspan(0, kAuthKeySize - computedAuthKeySize), gsl::byte()); - base::copy_bytes(authKeyBytes.subspan(kAuthKeySize - computedAuthKeySize), computedAuthKey); - } else { - base::copy_bytes(authKeyBytes, computedAuthKey); - } + MTP::AuthKey::FillData(_authKey, computedAuthKey); _keyFingerprint = ComputeFingerprint(_authKey); createAndStartController(call); diff --git a/Telegram/SourceFiles/calls/calls_call.h b/Telegram/SourceFiles/calls/calls_call.h index bb6e274b7..040bf541d 100644 --- a/Telegram/SourceFiles/calls/calls_call.h +++ b/Telegram/SourceFiles/calls/calls_call.h @@ -21,8 +21,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #pragma once #include "base/weak_unique_ptr.h" -#include "mtproto/sender.h" #include "base/timer.h" +#include "mtproto/sender.h" +#include "mtproto/auth_key.h" namespace tgvoip { class VoIPController; @@ -93,14 +94,13 @@ public: ~Call(); private: - static constexpr auto kAuthKeySize = 256; static constexpr auto kSha256Size = 32; void finish(const MTPPhoneCallDiscardReason &reason); void startOutgoing(); void startIncoming(); - void generateRandomPower(base::const_byte_span random); + void generateModExpFirst(base::const_byte_span randomSeed); void handleControllerStateChange(tgvoip::VoIPController *controller, int state); void createAndStartController(const MTPDphoneCall &call); @@ -129,7 +129,7 @@ private: std::vector<gsl::byte> _gb; std::array<gsl::byte, kSha256Size> _gaHash; std::array<gsl::byte, kRandomPowerSize> _randomPower; - std::array<gsl::byte, kAuthKeySize> _authKey; + MTP::AuthKey::Data _authKey; MTPPhoneCallProtocol _protocol; uint64 _id = 0; diff --git a/Telegram/SourceFiles/calls/calls_instance.cpp b/Telegram/SourceFiles/calls/calls_instance.cpp index ce222b154..9e0a67b34 100644 --- a/Telegram/SourceFiles/calls/calls_instance.cpp +++ b/Telegram/SourceFiles/calls/calls_instance.cpp @@ -63,7 +63,7 @@ void Instance::refreshDhConfig() { switch (result.type()) { case mtpc_messages_dhConfig: { auto &config = result.c_messages_dhConfig(); - if (!MTP::IsPrimeAndGood(config.vp.v, config.vg.v)) { + if (!MTP::IsPrimeAndGood(bytesFromMTP(config.vp), config.vg.v)) { LOG(("API Error: bad p/g received in dhConfig.")); callFailed(call.get()); return; diff --git a/Telegram/SourceFiles/core/utils.h b/Telegram/SourceFiles/core/utils.h index c320b8815..ab1e3099b 100644 --- a/Telegram/SourceFiles/core/utils.h +++ b/Telegram/SourceFiles/core/utils.h @@ -22,6 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "core/basic_types.h" #include <array> +#include <vector> #include <algorithm> #include <set> #include <gsl/gsl> @@ -204,6 +205,9 @@ using set_of_shared_ptr = std::set<std::shared_ptr<T>, base::pointer_comparator< using byte_span = gsl::span<gsl::byte>; using const_byte_span = gsl::span<const gsl::byte>; +using byte_vector = std::vector<gsl::byte>; +template <size_t N> +using byte_array = std::array<gsl::byte, N>; inline void copy_bytes(byte_span destination, const_byte_span source) { Expects(destination.size() >= source.size()); @@ -214,6 +218,11 @@ inline void set_bytes(byte_span destination, gsl::byte value) { memset(destination.data(), gsl::to_integer<unsigned char>(value), destination.size()); } +inline int compare_bytes(const_byte_span a, const_byte_span b) { + auto aSize = a.size(), bSize = b.size(); + return (aSize > bSize) ? 1 : (aSize < bSize) ? -1 : memcmp(a.data(), b.data(), aSize); +} + } // namespace base // using for_const instead of plain range-based for loop to ensure usage of const_iterator diff --git a/Telegram/SourceFiles/mtproto/auth_key.h b/Telegram/SourceFiles/mtproto/auth_key.h index 64b38f588..d11ae7cc0 100644 --- a/Telegram/SourceFiles/mtproto/auth_key.h +++ b/Telegram/SourceFiles/mtproto/auth_key.h @@ -28,7 +28,7 @@ namespace MTP { class AuthKey { public: static constexpr auto kSize = 256; // 2048 bits. - using Data = std::array<char, kSize>; + using Data = std::array<gsl::byte, kSize>; using KeyId = uint64; enum class Type { @@ -94,13 +94,25 @@ public: } void write(QDataStream &to) const { - to.writeRawData(_key.data(), _key.size()); + to.writeRawData(reinterpret_cast<const char*>(_key.data()), _key.size()); } bool equals(const std::shared_ptr<AuthKey> &other) const { return other ? (_key == other->_key) : false; } + static void FillData(Data &authKey, base::const_byte_span computedAuthKey) { + auto computedAuthKeySize = computedAuthKey.size(); + t_assert(computedAuthKeySize <= kSize); + auto authKeyBytes = gsl::make_span(authKey); + if (computedAuthKeySize < kSize) { + base::set_bytes(authKeyBytes.subspan(0, kSize - computedAuthKeySize), gsl::byte()); + base::copy_bytes(authKeyBytes.subspan(kSize - computedAuthKeySize), computedAuthKey); + } else { + base::copy_bytes(authKeyBytes, computedAuthKey); + } + } + private: void countKeyId() { auto sha1 = hashSha1(_key.data(), _key.size()); @@ -111,7 +123,7 @@ private: Type _type = Type::Generated; DcId _dcId = 0; - Data _key = { { 0 } }; + Data _key = { { gsl::byte{} } }; KeyId _keyId = 0; }; diff --git a/Telegram/SourceFiles/mtproto/connection.cpp b/Telegram/SourceFiles/mtproto/connection.cpp index 711b46d94..3c04d7c26 100644 --- a/Telegram/SourceFiles/mtproto/connection.cpp +++ b/Telegram/SourceFiles/mtproto/connection.cpp @@ -28,6 +28,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include <openssl/rand.h> #include "zlib.h" #include "lang.h" +#include "base/openssl_help.h" #include "mtproto/rsa_public_key.h" #include "messenger.h" @@ -40,6 +41,149 @@ namespace { constexpr auto kRecreateKeyId = AuthKey::KeyId(0xFFFFFFFFFFFFFFFFULL); constexpr auto kIntSize = static_cast<int>(sizeof(mtpPrime)); +constexpr auto kMaxModExpSize = 256; + +bool IsGoodModExpFirst(const openssl::BigNum &modexp, const openssl::BigNum &prime) { + auto diff = 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) { + return false; + } + t_assert(modexp.bytesSize() <= kMaxModExpSize); + return true; +} + +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; + } + + openssl::Context context; + if (!prime.isPrime(context)) { + LOG(("MTP Error: Bad prime.")); + return false; + } + + switch (g) { + case 2: { + auto mod8 = prime.modWord(8); + if (mod8 != 7) { + LOG(("BigNum PT Error: bad g value: %1, mod8: %2").arg(g).arg(mod8)); + return false; + } + } break; + case 3: { + auto mod3 = prime.modWord(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: { + auto mod5 = prime.modWord(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: { + auto mod24 = prime.modWord(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: { + auto mod7 = prime.modWord(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; + } + + auto primeSubOneDivTwo = prime; + primeSubOneDivTwo.setSubWord(1); + primeSubOneDivTwo.setDivWord(2); + if (!primeSubOneDivTwo.isPrime(context)) { + LOG(("MTP Error: Bad (prime - 1) / 2.")); + return false; + } + + return true; +} + +bool IsPrimeAndGood(base::const_byte_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 (!base::compare_bytes(gsl::as_bytes(gsl::make_span(GoodPrime)), primeBytes)) { + if (g == 3 || g == 4 || g == 5 || g == 7) { + return true; + } + } + + return IsPrimeAndGoodCheck(openssl::BigNum(primeBytes), g); +} + +std::vector<gsl::byte> CreateAuthKey(base::const_byte_span firstBytes, base::const_byte_span randomBytes, base::const_byte_span primeBytes) { + using openssl::BigNum; + BigNum first(firstBytes); + BigNum prime(primeBytes); + if (!IsGoodModExpFirst(first, prime)) { + LOG(("AuthKey Error: Bad first prime in CreateAuthKey().")); + return std::vector<gsl::byte>(); + } + return BigNum::ModExp(first, BigNum(randomBytes), prime).getBytes(); +} + +ModExpFirst CreateModExp(int g, base::const_byte_span primeBytes, base::const_byte_span randomSeed) { + Expects(randomSeed.size() == ModExpFirst::kRandomPowerSize); + + using namespace openssl; + + BigNum prime(primeBytes); + ModExpFirst result; + constexpr auto kMaxModExpFirstTries = 5; + for (auto tries = 0; tries != kMaxModExpFirstTries; ++tries) { + FillRandom(result.randomPower); + for (auto i = 0; i != ModExpFirst::kRandomPowerSize; ++i) { + result.randomPower[i] ^= randomSeed[i]; + } + auto modexp = BigNum::ModExp(BigNum(g), BigNum(result.randomPower), prime); + if (IsGoodModExpFirst(modexp, prime)) { + result.modexp = modexp.getBytes(); + break; + } + } + return result; +} void wrapInvokeAfter(mtpRequest &to, const mtpRequest &from, const mtpRequestMap &haveSent, int32 skipBeforeRequest = 0) { mtpMsgId afterId(*(mtpMsgId*)(from->after->data() + 4)); @@ -106,240 +250,6 @@ bool parsePQ(const QByteArray &pqStr, QByteArray &pStr, QByteArray &qStr) { return true; } -class BigNumCounter { -public: - BigNumCounter() : ctx(BN_CTX_new()) { - BN_init(&bnPower); - BN_init(&bnModul); - BN_init(&bn_g); - BN_init(&bn_g_a); - BN_init(&bnResult); - BN_init(&bnTemp); - } - ~BigNumCounter() { - BN_CTX_free(ctx); - BN_clear_free(&bnPower); - BN_clear_free(&bnModul); - BN_clear_free(&bn_g); - BN_clear_free(&bn_g_a); - BN_clear_free(&bnResult); - BN_clear_free(&bnTemp); - } - - bool count(const void *power, const void *modul, uint32 g, void *gResult, const void *g_a, void *g_aResult) { - DEBUG_LOG(("BigNum Info: counting g_b = g ^ b % dh_prime and auth_key = g_a ^ b % dh_prime")); - uint32 g_be = qToBigEndian(g); - if ( - !BN_bin2bn((const uchar*)power, 64 * sizeof(uint32), &bnPower) || - !BN_bin2bn((const uchar*)modul, 64 * sizeof(uint32), &bnModul) || - !BN_bin2bn((const uchar*)&g_be, sizeof(uint32), &bn_g) || - !BN_bin2bn((const uchar*)g_a, 64 * sizeof(uint32), &bn_g_a) - ) { - ERR_load_crypto_strings(); - LOG(("BigNum Error: BN_bin2bn failed, error: %1").arg(ERR_error_string(ERR_get_error(), 0))); - DEBUG_LOG(("BigNum Error: base %1, power %2, modul %3").arg(Logs::mb(&g_be, sizeof(uint32)).str()).arg(Logs::mb(power, 64 * sizeof(uint32)).str()).arg(Logs::mb(modul, 64 * sizeof(uint32)).str())); - return false; - } - - if (!BN_mod_exp(&bnResult, &bn_g, &bnPower, &bnModul, ctx)) { - ERR_load_crypto_strings(); - LOG(("BigNum Error: BN_mod_exp failed, error: %1").arg(ERR_error_string(ERR_get_error(), 0))); - DEBUG_LOG(("BigNum Error: base %1, power %2, modul %3").arg(Logs::mb(&g_be, sizeof(uint32)).str()).arg(Logs::mb(power, 64 * sizeof(uint32)).str()).arg(Logs::mb(modul, 64 * sizeof(uint32)).str())); - return false; - } - - // check g_b > 2^{2048 - 8} and get the value of g_b - if (BN_is_negative(&bnResult)) { - LOG(("BigNum Error: bad g_b - negative")); - return false; - } - uint32 resultLen = BN_num_bytes(&bnResult); - if (resultLen != 64 * sizeof(uint32)) { - LOG(("BigNum Error: bad g_b len (%1)").arg(resultLen)); - return false; - } - resultLen = BN_bn2bin(&bnResult, (uchar*)gResult); - if (resultLen != 64 * sizeof(uint32)) { - LOG(("BigNum Error: bad g_b export len (%1)").arg(resultLen)); - return false; - } - - // check g_b < dh_prime - 2^{2048 - 8} - BN_sub(&bnTemp, &bnModul, &bnResult); - if (BN_is_negative(&bnTemp)) { - DEBUG_LOG(("BigNum Error: bad g_b > dh_prime")); - return false; - } - if (BN_num_bytes(&bnTemp) != 64 * sizeof(uint32)) { - DEBUG_LOG(("BigNum Error: bad g_b > dh_prime - 2^{2048 - 8}")); - return false; - } - - if (!BN_mod_exp(&bnResult, &bn_g_a, &bnPower, &bnModul, ctx)) { - ERR_load_crypto_strings(); - LOG(("BigNum Error: BN_mod_exp failed, error: %1").arg(ERR_error_string(ERR_get_error(), 0))); - DEBUG_LOG(("BigNum Error: base %1, power %2, modul %3").arg(Logs::mb(&g_be, sizeof(uint32)).str()).arg(Logs::mb(power, 64 * sizeof(uint32)).str()).arg(Logs::mb(modul, 64 * sizeof(uint32)).str())); - return false; - } - - resultLen = BN_num_bytes(&bnResult); - if (resultLen != 64 * sizeof(uint32)) { - DEBUG_LOG(("BigNum Error: bad g_aResult len (%1)").arg(resultLen)); - return false; - } - resultLen = BN_bn2bin(&bnResult, (uchar*)g_aResult); - if (resultLen != 64 * sizeof(uint32)) { - DEBUG_LOG(("BigNum Error: bad g_aResult export len (%1)").arg(resultLen)); - return false; - } - - // check g_a > 2^{2048 - 8} - if (BN_is_negative(&bn_g_a)) { - LOG(("BigNum Error: bad g_a - negative")); - return false; - } - resultLen = BN_num_bytes(&bn_g_a); - if (resultLen != 64 * sizeof(uint32)) { - LOG(("BigNum Error: bad g_a len (%1)").arg(resultLen)); - return false; - } - - // check g_a < dh_prime - 2^{2048 - 8} - BN_sub(&bnTemp, &bnModul, &bn_g_a); - if (BN_is_negative(&bnTemp)) { - LOG(("BigNum Error: bad g_b > dh_prime")); - return false; - } - if (BN_num_bytes(&bnTemp) != 64 * sizeof(uint32)) { - LOG(("BigNum Error: bad g_b > dh_prime - 2^{2048 - 8}")); - return false; - } - - return true; - } - -private: - BIGNUM bnPower, bnModul, bn_g, bn_g_a, bnResult, bnTemp; - BN_CTX *ctx; - -}; - -// Miller-Rabin primality test -class BigNumPrimeTest { -public: - BigNumPrimeTest() : _context(BN_CTX_new()) { - BN_init(&_prime); - } - ~BigNumPrimeTest() { - BN_clear_free(&_prime); - BN_CTX_free(_context); - } - - bool isPrimeAndGood(const QByteArray &data, int g) { - constexpr auto kMillerRabinIterationCount = 30; - constexpr auto kGoodPrimeSize = 256; - - if (data.size() != kGoodPrimeSize) { - LOG(("BigNum PT Error: data size %1").arg(data.size())); - return false; - } - if (!memcmp(data.constData(), "\ -\xC7\x1C\xAE\xB9\xC6\xB1\xC9\x04\x8E\x6C\x52\x2F\x70\xF1\x3F\x73\ -\x98\x0D\x40\x23\x8E\x3E\x21\xC1\x49\x34\xD0\x37\x56\x3D\x93\x0F\ -\x48\x19\x8A\x0A\xA7\xC1\x40\x58\x22\x94\x93\xD2\x25\x30\xF4\xDB\ -\xFA\x33\x6F\x6E\x0A\xC9\x25\x13\x95\x43\xAE\xD4\x4C\xCE\x7C\x37\ -\x20\xFD\x51\xF6\x94\x58\x70\x5A\xC6\x8C\xD4\xFE\x6B\x6B\x13\xAB\ -\xDC\x97\x46\x51\x29\x69\x32\x84\x54\xF1\x8F\xAF\x8C\x59\x5F\x64\ -\x24\x77\xFE\x96\xBB\x2A\x94\x1D\x5B\xCD\x1D\x4A\xC8\xCC\x49\x88\ -\x07\x08\xFA\x9B\x37\x8E\x3C\x4F\x3A\x90\x60\xBE\xE6\x7C\xF9\xA4\ -\xA4\xA6\x95\x81\x10\x51\x90\x7E\x16\x27\x53\xB5\x6B\x0F\x6B\x41\ -\x0D\xBA\x74\xD8\xA8\x4B\x2A\x14\xB3\x14\x4E\x0E\xF1\x28\x47\x54\ -\xFD\x17\xED\x95\x0D\x59\x65\xB4\xB9\xDD\x46\x58\x2D\xB1\x17\x8D\ -\x16\x9C\x6B\xC4\x65\xB0\xD6\xFF\x9C\xA3\x92\x8F\xEF\x5B\x9A\xE4\ -\xE4\x18\xFC\x15\xE8\x3E\xBE\xA0\xF8\x7F\xA9\xFF\x5E\xED\x70\x05\ -\x0D\xED\x28\x49\xF4\x7B\xF9\x59\xD9\x56\x85\x0C\xE9\x29\x85\x1F\ -\x0D\x81\x15\xF6\x35\xB1\x05\xEE\x2E\x4E\x15\xD0\x4B\x24\x54\xBF\ -\x6F\x4F\xAD\xF0\x34\xB1\x04\x03\x11\x9C\xD8\xE3\xB9\x2F\xCC\x5B", kGoodPrimeSize)) { - if (g == 3 || g == 4 || g == 5 || g == 7) { - return true; - } - } - if (!BN_bin2bn((const uchar*)data.constData(), kGoodPrimeSize, &_prime)) { - ERR_load_crypto_strings(); - LOG(("BigNum PT Error: BN_bin2bn failed, error: %1").arg(ERR_error_string(ERR_get_error(), 0))); - DEBUG_LOG(("BigNum PT Error: prime %1").arg(Logs::mb(data.constData(), kGoodPrimeSize).str())); - return false; - } - - auto numBits = BN_num_bits(&_prime); - if (numBits != 2048) { - LOG(("BigNum PT Error: BN_bin2bn failed, bad dh_prime num bits: %1").arg(numBits)); - return false; - } - - if (BN_is_prime_ex(&_prime, kMillerRabinIterationCount, _context, NULL) == 0) { - return false; - } - - switch (g) { - case 2: { - auto mod8 = BN_mod_word(&_prime, 8); - if (mod8 != 7) { - LOG(("BigNum PT Error: bad g value: %1, mod8: %2").arg(g).arg(mod8)); - return false; - } - } break; - case 3: { - auto mod3 = BN_mod_word(&_prime, 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: { - auto mod5 = BN_mod_word(&_prime, 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: { - auto mod24 = BN_mod_word(&_prime, 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: { - auto mod7 = BN_mod_word(&_prime, 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; - } - - BN_sub_word(&_prime, 1); // (p - 1) / 2 - BN_div_word(&_prime, 2); - - if (BN_is_prime_ex(&_prime, kMillerRabinIterationCount, _context, NULL) == 0) { - return false; - } - - return true; - } - -private: - BIGNUM _prime; - BN_CTX *_context; - -}; - } // namespace Connection::Connection(Instance *instance) : _instance(instance) { @@ -2467,11 +2377,11 @@ void ConnectionPrivate::pqAnswered() { req_DH_params.vpublic_key_fingerprint = MTP_long(rsaKey.getFingerPrint()); req_DH_params.vp = p_q_inner.c_p_q_inner_data().vp; req_DH_params.vq = p_q_inner.c_p_q_inner_data().vq; - req_DH_params.vencrypted_data = MTP_string(std::move(dhEncString)); + req_DH_params.vencrypted_data = MTP_bytes(dhEncString); sendRequestNotSecure(req_DH_params); } -std::string ConnectionPrivate::encryptPQInnerRSA(const MTPP_Q_inner_data &data, const MTP::internal::RSAPublicKey &key) { +base::byte_vector ConnectionPrivate::encryptPQInnerRSA(const MTPP_Q_inner_data &data, const MTP::internal::RSAPublicKey &key) { auto p_q_inner_size = data.innerLength(); auto encSize = (p_q_inner_size >> 2) + 6; if (encSize >= 65) { @@ -2480,7 +2390,7 @@ std::string ConnectionPrivate::encryptPQInnerRSA(const MTPP_Q_inner_data &data, data.write(tmp); LOG(("AuthKey Error: too large data for RSA encrypt, size %1").arg(encSize * sizeof(mtpPrime))); DEBUG_LOG(("AuthKey Error: bad data for RSA encrypt %1").arg(Logs::mb(&tmp[0], tmp.size() * 4).str())); - return std::string(); // can't be 255-byte string + return base::byte_vector(); // can't be 255-byte string } auto encBuffer = mtpBuffer(); @@ -2495,11 +2405,9 @@ std::string ConnectionPrivate::encryptPQInnerRSA(const MTPP_Q_inner_data &data, memset_rand(&encBuffer[encSize], (65 - encSize) * sizeof(mtpPrime)); } - auto dhEncString = std::string(); - if (!key.encrypt(reinterpret_cast<const char*>(&encBuffer[0]) + 3, dhEncString)) { - return std::string(); - } - return dhEncString; + auto bytes = gsl::as_bytes(gsl::make_span(encBuffer)); + auto bytesToEncrypt = bytes.subspan(3, 256); + return key.encrypt(bytesToEncrypt); } void ConnectionPrivate::dhParamsAnswered() { @@ -2576,24 +2484,15 @@ void ConnectionPrivate::dhParamsAnswered() { } unixtimeSet(dh_inner_data.vserver_time.v); - auto &dhPrime = dh_inner_data.vdh_prime.v; - auto &g_a = dh_inner_data.vg_a.v; - if (dhPrime.length() != 256 || g_a.length() != 256) { - LOG(("AuthKey Error: bad dh_prime len (%1) or g_a len (%2)").arg(dhPrime.length()).arg(g_a.length())); - DEBUG_LOG(("AuthKey Error: dh_prime %1, g_a %2").arg(Logs::mb(dhPrime.constData(), dhPrime.length()).str()).arg(Logs::mb(g_a.constData(), g_a.length()).str())); + // check that dhPrime and (dhPrime - 1) / 2 are really prime + if (!IsPrimeAndGood(bytesFromMTP(dh_inner_data.vdh_prime), dh_inner_data.vg.v)) { + LOG(("AuthKey Error: bad dh_prime primality!")); return restart(); } - // check that dhPrime and (dhPrime - 1) / 2 are really prime using openssl BIGNUM methods - if (!IsPrimeAndGood(dhPrime, dh_inner_data.vg.v)) { - LOG(("AuthKey Error: bad dh_prime primality!").arg(dhPrime.length()).arg(g_a.length())); - DEBUG_LOG(("AuthKey Error: dh_prime %1").arg(Logs::mb(dhPrime.constData(), dhPrime.length()).str())); - return restart(); - } - - _authKeyStrings->dh_prime = QByteArray(dhPrime.data(), dhPrime.size()); + _authKeyStrings->dh_prime = byteVectorFromMTP(dh_inner_data.vdh_prime); _authKeyData->g = dh_inner_data.vg.v; - _authKeyStrings->g_a = QByteArray(g_a.data(), g_a.size()); + _authKeyStrings->g_a = byteVectorFromMTP(dh_inner_data.vg_a); _authKeyData->retry_id = MTP_long(0); _authKeyData->retries = 0; } return dhClientParamsSend(); @@ -2630,25 +2529,28 @@ void ConnectionPrivate::dhClientParamsSend() { return restart(); } - auto g_b_string = std::string(256, ' '); - // gen rand 'b' - uint32 b[64]; - auto g_b = reinterpret_cast<uint32*>(&g_b_string[0]); - memset_rand(b, sizeof(b)); - - // count g_b and auth_key using openssl BIGNUM methods - MTP::internal::BigNumCounter bnCounter; - if (!bnCounter.count(b, _authKeyStrings->dh_prime.constData(), _authKeyData->g, g_b, _authKeyStrings->g_a.constData(), _authKeyStrings->auth_key.data())) { - return dhClientParamsSend(); + auto randomSeed = std::array<gsl::byte, ModExpFirst::kRandomPowerSize>(); + openssl::FillRandom(randomSeed); + auto g_b_data = CreateModExp(_authKeyData->g, _authKeyStrings->dh_prime, randomSeed); + if (g_b_data.modexp.empty()) { + LOG(("AuthKey Error: could not generate good g_b.")); + return restart(); } + auto computedAuthKey = CreateAuthKey(_authKeyStrings->g_a, g_b_data.randomPower, _authKeyStrings->dh_prime); + if (computedAuthKey.empty()) { + LOG(("AuthKey Error: could not generate auth_key.")); + return restart(); + } + AuthKey::FillData(_authKeyStrings->auth_key, computedAuthKey); + // count auth_key hashes - parts of sha1(auth_key) auto auth_key_sha = hashSha1(_authKeyStrings->auth_key.data(), _authKeyStrings->auth_key.size()); memcpy(&_authKeyData->auth_key_aux_hash, auth_key_sha.data(), 8); memcpy(&_authKeyData->auth_key_hash, auth_key_sha.data() + 12, 8); - auto client_dh_inner = MTP_client_DH_inner_data(_authKeyData->nonce, _authKeyData->server_nonce, _authKeyData->retry_id, MTP_string(std::move(g_b_string))); + auto client_dh_inner = MTP_client_DH_inner_data(_authKeyData->nonce, _authKeyData->server_nonce, _authKeyData->retry_id, MTP_bytes(g_b_data.modexp)); auto sdhEncString = encryptClientDHInner(client_dh_inner); @@ -2826,28 +2728,28 @@ void ConnectionPrivate::authKeyCreated() { } void ConnectionPrivate::clearAuthKeyData() { - auto zeroMemory = [](void *data, int size) { -#ifdef Q_OS_WIN - SecureZeroMemory(data, size); + auto zeroMemory = [](base::byte_span bytes) { +#ifdef Q_OS_WIN2 + SecureZeroMemory(bytes.data(), bytes.size()); #else // Q_OS_WIN - auto end = static_cast<char*>(data) + size; - for (volatile auto p = static_cast<volatile char*>(data); p != end; ++p) { + auto end = reinterpret_cast<char*>(bytes.data()) + bytes.size(); + for (volatile auto p = reinterpret_cast<volatile char*>(bytes.data()); p != end; ++p) { *p = 0; } #endif // Q_OS_WIN }; if (_authKeyData) { - zeroMemory(_authKeyData.get(), sizeof(AuthKeyCreateData)); + zeroMemory(gsl::make_span(reinterpret_cast<gsl::byte*>(_authKeyData.get()), sizeof(AuthKeyCreateData))); _authKeyData.reset(); } if (_authKeyStrings) { - if (!_authKeyStrings->dh_prime.isEmpty()) { - zeroMemory(_authKeyStrings->dh_prime.data(), _authKeyStrings->dh_prime.size()); + if (!_authKeyStrings->dh_prime.empty()) { + zeroMemory(_authKeyStrings->dh_prime); } - if (!_authKeyStrings->g_a.isEmpty()) { - zeroMemory(_authKeyStrings->g_a.data(), _authKeyStrings->g_a.size()); + if (!_authKeyStrings->g_a.empty()) { + zeroMemory(_authKeyStrings->g_a); } - zeroMemory(_authKeyStrings->auth_key.data(), _authKeyStrings->auth_key.size()); + zeroMemory(_authKeyStrings->auth_key); _authKeyStrings.reset(); } } @@ -3082,8 +2984,16 @@ void ConnectionPrivate::stop() { } // namespace internal -bool IsPrimeAndGood(const QByteArray &data, int g) { - return MTP::internal::BigNumPrimeTest().isPrimeAndGood(data, g); +bool IsPrimeAndGood(base::const_byte_span primeBytes, int g) { + return internal::IsPrimeAndGood(primeBytes, g); +} + +ModExpFirst CreateModExp(int g, base::const_byte_span primeBytes, base::const_byte_span randomSeed) { + return internal::CreateModExp(g, primeBytes, randomSeed); +} + +std::vector<gsl::byte> CreateAuthKey(base::const_byte_span firstBytes, base::const_byte_span randomBytes, base::const_byte_span primeBytes) { + return internal::CreateAuthKey(firstBytes, randomBytes, primeBytes); } } // namespace MTP diff --git a/Telegram/SourceFiles/mtproto/connection.h b/Telegram/SourceFiles/mtproto/connection.h index a1354fd92..8ae4aed3d 100644 --- a/Telegram/SourceFiles/mtproto/connection.h +++ b/Telegram/SourceFiles/mtproto/connection.h @@ -29,7 +29,15 @@ namespace MTP { class Instance; -bool IsPrimeAndGood(const QByteArray &data, int g); +bool IsPrimeAndGood(base::const_byte_span primeBytes, int g); +struct ModExpFirst { + static constexpr auto kRandomPowerSize = 256; + + std::vector<gsl::byte> modexp; + std::array<gsl::byte, kRandomPowerSize> randomPower; +}; +ModExpFirst CreateModExp(int g, base::const_byte_span primeBytes, base::const_byte_span randomSeed); +std::vector<gsl::byte> CreateAuthKey(base::const_byte_span firstBytes, base::const_byte_span randomBytes, base::const_byte_span primeBytes); namespace internal { @@ -185,7 +193,7 @@ private: bool setState(int32 state, int32 ifState = Connection::UpdateAlways); - std::string encryptPQInnerRSA(const MTPP_Q_inner_data &data, const MTP::internal::RSAPublicKey &key); + base::byte_vector encryptPQInnerRSA(const MTPP_Q_inner_data &data, const MTP::internal::RSAPublicKey &key); std::string encryptClientDHInner(const MTPClient_DH_Inner_Data &data); Instance *_instance = nullptr; @@ -272,9 +280,9 @@ private: uint32 msgs_sent = 0; }; struct AuthKeyCreateStrings { - QByteArray dh_prime; - QByteArray g_a; - AuthKey::Data auth_key = { { 0 } }; + std::vector<gsl::byte> dh_prime; + std::vector<gsl::byte> g_a; + AuthKey::Data auth_key = { { gsl::byte{} } }; }; std::unique_ptr<AuthKeyCreateData> _authKeyData; std::unique_ptr<AuthKeyCreateStrings> _authKeyStrings; diff --git a/Telegram/SourceFiles/mtproto/dc_options.cpp b/Telegram/SourceFiles/mtproto/dc_options.cpp index dd6b6fb36..d56a8ce62 100644 --- a/Telegram/SourceFiles/mtproto/dc_options.cpp +++ b/Telegram/SourceFiles/mtproto/dc_options.cpp @@ -229,15 +229,15 @@ QByteArray DcOptions::serialize() const { } struct SerializedPublicKey { DcId dcId; - QByteArray n; - QByteArray e; + base::byte_vector n; + base::byte_vector e; }; std::vector<SerializedPublicKey> publicKeys; publicKeys.reserve(count); for (auto &keysInDc : _cdnPublicKeys) { for (auto &entry : keysInDc.second) { publicKeys.push_back({ keysInDc.first, entry.second.getN(), entry.second.getE() }); - size += sizeof(qint32) + Serialize::bytearraySize(publicKeys.back().n) + Serialize::bytearraySize(publicKeys.back().e); + size += sizeof(qint32) + Serialize::bytesSize(publicKeys.back().n) + Serialize::bytesSize(publicKeys.back().e); } } @@ -260,7 +260,7 @@ QByteArray DcOptions::serialize() const { } stream << qint32(publicKeys.size()); for (auto &key : publicKeys) { - stream << qint32(key.dcId) << key.n << key.e; + stream << qint32(key.dcId) << Serialize::bytes(key.n) << Serialize::bytes(key.e); } } return result; @@ -309,8 +309,8 @@ void DcOptions::constructFromSerialized(const QByteArray &serialized) { for (auto i = 0; i != count; ++i) { qint32 dcId = 0; - QByteArray n, e; - stream >> dcId >> n >> e; + base::byte_vector n, e; + stream >> dcId >> Serialize::bytes(n) >> Serialize::bytes(e); if (stream.status() != QDataStream::Ok) { LOG(("MTP Error: Bad data for CDN config inside DcOptions::constructFromSerialized()")); return; diff --git a/Telegram/SourceFiles/mtproto/rsa_public_key.cpp b/Telegram/SourceFiles/mtproto/rsa_public_key.cpp index c0b667cd8..a512d7ad2 100644 --- a/Telegram/SourceFiles/mtproto/rsa_public_key.cpp +++ b/Telegram/SourceFiles/mtproto/rsa_public_key.cpp @@ -20,6 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #include "mtproto/rsa_public_key.h" +#include "base/openssl_help.h" #include <openssl/rsa.h> #include <openssl/pem.h> #include <openssl/bio.h> @@ -37,10 +38,10 @@ public: computeFingerprint(); } } - Private(const QByteArray &n, const QByteArray &e) : _rsa(RSA_new()) { + Private(base::const_byte_span nBytes, base::const_byte_span eBytes) : _rsa(RSA_new()) { if (_rsa) { - _rsa->n = BN_bin2bn((const uchar*)n.data(), n.size(), _rsa->n); - _rsa->e = BN_bin2bn((const uchar*)e.data(), e.size(), _rsa->e); + _rsa->n = BN_dup(openssl::BigNum(nBytes).raw()); + _rsa->e = BN_dup(openssl::BigNum(eBytes).raw()); if (!_rsa->n || !_rsa->e) { RSA_free(base::take(_rsa)); } else { @@ -48,11 +49,11 @@ public: } } } - QByteArray getN() const { + base::byte_vector getN() const { Expects(isValid()); return toBytes(_rsa->n); } - QByteArray getE() const { + base::byte_vector getE() const { Expects(isValid()); return toBytes(_rsa->e); } @@ -62,17 +63,18 @@ public: bool isValid() const { return _rsa != nullptr; } - bool encrypt(const void *data, string &result) const { + base::byte_vector encrypt(base::const_byte_span data) const { Expects(isValid()); - result.resize(256); - auto res = RSA_public_encrypt(256, reinterpret_cast<const unsigned char*>(data), reinterpret_cast<uchar*>(&result[0]), _rsa, RSA_NO_PADDING); - if (res != 256) { + constexpr auto kEncryptSize = 256; + auto result = base::byte_vector(kEncryptSize, gsl::byte {}); + auto res = RSA_public_encrypt(kEncryptSize, reinterpret_cast<const unsigned char*>(data.data()), reinterpret_cast<unsigned char*>(result.data()), _rsa, RSA_NO_PADDING); + if (res != kEncryptSize) { ERR_load_crypto_strings(); LOG(("RSA Error: RSA_public_encrypt failed, key fp: %1, result: %2, error: %3").arg(getFingerPrint()).arg(res).arg(ERR_error_string(ERR_get_error(), 0))); - return false; + return base::byte_vector(); } - return true; + return result; } ~Private() { RSA_free(_rsa); @@ -89,10 +91,10 @@ private: uchar sha1Buffer[20]; _fingerprint = *(uint64*)(hashSha1(&string[0], string.size() * sizeof(mtpPrime), sha1Buffer) + 3); } - static QByteArray toBytes(BIGNUM *number) { - auto size = static_cast<int>(BN_num_bytes(number)); - auto result = QByteArray(size, 0); - BN_bn2bin(number, reinterpret_cast<uchar*>(result.data())); + static base::byte_vector toBytes(BIGNUM *number) { + auto size = BN_num_bytes(number); + auto result = base::byte_vector(size, gsl::byte {}); + BN_bn2bin(number, reinterpret_cast<unsigned char*>(result.data())); return result; } @@ -104,7 +106,7 @@ private: RSAPublicKey::RSAPublicKey(base::const_byte_span key) : _private(std::make_shared<Private>(key)) { } -RSAPublicKey::RSAPublicKey(const QByteArray &n, const QByteArray &e) : _private(std::make_shared<Private>(n, e)) { +RSAPublicKey::RSAPublicKey(base::const_byte_span nBytes, base::const_byte_span eBytes) : _private(std::make_shared<Private>(nBytes, eBytes)) { } bool RSAPublicKey::isValid() const { @@ -116,19 +118,19 @@ uint64 RSAPublicKey::getFingerPrint() const { return _private->getFingerPrint(); } -QByteArray RSAPublicKey::getN() const { +base::byte_vector RSAPublicKey::getN() const { Expects(isValid()); return _private->getN(); } -QByteArray RSAPublicKey::getE() const { +base::byte_vector RSAPublicKey::getE() const { Expects(isValid()); return _private->getE(); } -bool RSAPublicKey::encrypt(const void *data, string &result) const { +base::byte_vector RSAPublicKey::encrypt(base::const_byte_span data) const { Expects(isValid()); - return _private->encrypt(data, result); + return _private->encrypt(data); } } // namespace internal diff --git a/Telegram/SourceFiles/mtproto/rsa_public_key.h b/Telegram/SourceFiles/mtproto/rsa_public_key.h index e5adc183a..7b66e9df7 100644 --- a/Telegram/SourceFiles/mtproto/rsa_public_key.h +++ b/Telegram/SourceFiles/mtproto/rsa_public_key.h @@ -28,8 +28,8 @@ class RSAPublicKey final { public: // key in RSAPublicKey "-----BEGIN RSA PUBLIC KEY----- ..." format RSAPublicKey() = default; - RSAPublicKey(base::const_byte_span key); - RSAPublicKey(const QByteArray &n, const QByteArray &e); + explicit RSAPublicKey(base::const_byte_span key); + RSAPublicKey(base::const_byte_span nBytes, base::const_byte_span eBytes); RSAPublicKey(RSAPublicKey &&other) = default; RSAPublicKey(const RSAPublicKey &other) = default; RSAPublicKey &operator=(RSAPublicKey &&other) = default; @@ -37,11 +37,11 @@ public: bool isValid() const; uint64 getFingerPrint() const; - QByteArray getN() const; - QByteArray getE() const; + base::byte_vector getN() const; + base::byte_vector getE() const; // data has exactly 256 chars to be encrypted - bool encrypt(const void *data, std::string &result) const; + base::byte_vector encrypt(base::const_byte_span data) const; private: class Private; diff --git a/Telegram/SourceFiles/storage/localstorage.cpp b/Telegram/SourceFiles/storage/localstorage.cpp index ed15fbe95..3fa80eeea 100644 --- a/Telegram/SourceFiles/storage/localstorage.cpp +++ b/Telegram/SourceFiles/storage/localstorage.cpp @@ -146,7 +146,7 @@ auto PassKey = MTP::AuthKeyPtr(); auto LocalKey = MTP::AuthKeyPtr(); void createLocalKey(const QByteArray &pass, QByteArray *salt, MTP::AuthKeyPtr *result) { - auto key = MTP::AuthKey::Data { { 0 } }; + auto key = MTP::AuthKey::Data { { gsl::byte{} } }; auto iterCount = pass.size() ? LocalEncryptIterCount : LocalEncryptNoPwdIterCount; // dont slow down for no password auto newSalt = QByteArray(); if (!salt) { @@ -931,9 +931,8 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting case dbiKey: { qint32 dcId; - auto key = MTP::AuthKey::Data { { 0 } }; stream >> dcId; - stream.readRawData(key.data(), key.size()); + auto key = Serialize::read<MTP::AuthKey::Data>(stream); if (!_checkStreamStatus(stream)) return false; Messenger::Instance().setMtpKey(dcId, key); @@ -1900,8 +1899,8 @@ ReadMapState _readMap(const QByteArray &pass) { LOG(("App Info: could not decrypt pass-protected key from map file, maybe bad password...")); return ReadMapPassNeeded; } - auto key = MTP::AuthKey::Data { { 0 } }; - if (keyData.stream.readRawData(key.data(), key.size()) != key.size() || !keyData.stream.atEnd()) { + auto key = Serialize::read<MTP::AuthKey::Data>(keyData.stream); + if (keyData.stream.status() != QDataStream::Ok || !keyData.stream.atEnd()) { LOG(("App Error: could not read pass-protected key from map file")); return ReadMapFailed; } diff --git a/Telegram/SourceFiles/storage/serialize_common.h b/Telegram/SourceFiles/storage/serialize_common.h index 725a8b4eb..26296609c 100644 --- a/Telegram/SourceFiles/storage/serialize_common.h +++ b/Telegram/SourceFiles/storage/serialize_common.h @@ -32,6 +32,67 @@ inline int bytearraySize(const QByteArray &arr) { return sizeof(quint32) + arr.size(); } +inline int bytesSize(base::const_byte_span bytes) { + return sizeof(quint32) + bytes.size(); +} + +struct ReadBytesVectorWrap { + base::byte_vector &bytes; +}; + +inline ReadBytesVectorWrap bytes(base::byte_vector &bytes) { + return ReadBytesVectorWrap { bytes }; +} + +// Compatible with QDataStream &operator>>(QDataStream &, QByteArray &); +inline QDataStream &operator>>(QDataStream &stream, ReadBytesVectorWrap data) { + auto &bytes = data.bytes; + bytes.clear(); + quint32 len; + stream >> len; + if (stream.status() != QDataStream::Ok || len == 0xFFFFFFFF) { + return stream; + } + + constexpr auto kStep = quint32(1024 * 1024); + for (auto allocated = quint32(0); allocated < len;) { + auto blockSize = qMin(kStep, len - allocated); + bytes.resize(allocated + blockSize); + if (stream.readRawData(reinterpret_cast<char*>(bytes.data()) + allocated, blockSize) != blockSize) { + bytes.clear(); + stream.setStatus(QDataStream::ReadPastEnd); + return stream; + } + allocated += blockSize; + } + + return stream; +} + +struct WriteBytesWrap { + base::const_byte_span bytes; +}; + +inline WriteBytesWrap bytes(base::const_byte_span bytes) { + return WriteBytesWrap { bytes }; +} + +inline QDataStream &operator<<(QDataStream &stream, WriteBytesWrap data) { + auto bytes = data.bytes; + if (bytes.empty()) { + stream << quint32(0xFFFFFFFF); + } else { + auto size = quint32(bytes.size()); + stream << size; + stream.writeRawData(reinterpret_cast<const char*>(bytes.data()), size); + } + return stream; +} + +inline QDataStream &operator<<(QDataStream &stream, ReadBytesVectorWrap data) { + return stream << WriteBytesWrap { data.bytes }; +} + inline int dateTimeSize() { return (sizeof(qint64) + sizeof(quint32) + sizeof(qint8)); } @@ -50,7 +111,7 @@ inline T read(QDataStream &stream) { template <> inline MTP::AuthKey::Data read<MTP::AuthKey::Data>(QDataStream &stream) { auto result = MTP::AuthKey::Data(); - stream.readRawData(result.data(), result.size()); + stream.readRawData(reinterpret_cast<char*>(result.data()), result.size()); return result; }