First version of working through temp keys.

This commit is contained in:
John Preston 2019-11-19 13:10:51 +03:00
parent d9fc3619c2
commit 173ae746a2
15 changed files with 883 additions and 668 deletions

View File

@ -7,8 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "mtproto/connection.h" #include "mtproto/connection.h"
#include "mtproto/details/mtproto_dc_key_binder.h"
#include "mtproto/details/mtproto_dc_key_creator.h" #include "mtproto/details/mtproto_dc_key_creator.h"
#include "mtproto/details/mtproto_dc_key_checker.h"
#include "mtproto/details/mtproto_dump_to_text.h" #include "mtproto/details/mtproto_dump_to_text.h"
#include "mtproto/session.h" #include "mtproto/session.h"
#include "mtproto/mtproto_rsa_public_key.h" #include "mtproto/mtproto_rsa_public_key.h"
@ -23,10 +23,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/qthelp_url.h" #include "base/qthelp_url.h"
#include "base/unixtime.h" #include "base/unixtime.h"
#ifdef small
#undef small
#endif // small
namespace MTP { namespace MTP {
namespace internal { namespace internal {
namespace { namespace {
@ -42,6 +38,7 @@ constexpr auto kPingDelayDisconnect = 60;
constexpr auto kPingSendAfter = 30 * crl::time(1000); constexpr auto kPingSendAfter = 30 * crl::time(1000);
constexpr auto kPingSendAfterForce = 45 * crl::time(1000); constexpr auto kPingSendAfterForce = 45 * crl::time(1000);
constexpr auto kCheckKeyExpiresIn = TimeId(3600); constexpr auto kCheckKeyExpiresIn = TimeId(3600);
constexpr auto kTemporaryExpiresIn = TimeId(10);
constexpr auto kTestModeDcIdShift = 10000; constexpr auto kTestModeDcIdShift = 10000;
// If we can't connect for this time we will ask _instance to update config. // If we can't connect for this time we will ask _instance to update config.
@ -248,6 +245,7 @@ ConnectionPrivate::ConnectionPrivate(
ConnectionPrivate::~ConnectionPrivate() { ConnectionPrivate::~ConnectionPrivate() {
clearKeyCreatorOnFail(); clearKeyCreatorOnFail();
cancelKeyBinder();
Expects(_finished); Expects(_finished);
Expects(!_connection); Expects(!_connection);
@ -548,12 +546,20 @@ void ConnectionPrivate::tryToSend() {
return; return;
} }
auto needsLayer = !_sessionData->connectionInited(); const auto needsLayer = !_sessionData->connectionInited();
auto state = getState(); const auto state = getState();
auto sendOnlyFirstPing = (state != ConnectedState); const auto sendOnlyFirstPing = (state != ConnectedState);
const auto sendAll = !sendOnlyFirstPing && !_keyBinder;
const auto isMainSession = (GetDcIdShift(_shiftedDcId) == 0);
if (sendOnlyFirstPing && !_pingIdToSend) { if (sendOnlyFirstPing && !_pingIdToSend) {
DEBUG_LOG(("MTP Info: dc %1 not sending, waiting for Connected state, state: %2").arg(_shiftedDcId).arg(state)); DEBUG_LOG(("MTP Info: dc %1 not sending, waiting for Connected state, state: %2").arg(_shiftedDcId).arg(state));
return; // just do nothing, if is not connected yet return; // just do nothing, if is not connected yet
} else if (isMainSession
&& !sendOnlyFirstPing
&& !_pingIdToSend
&& !_pingId
&& _pingSendAt <= crl::now()) {
_pingIdToSend = openssl::RandomValue<mtpPingId>();
} }
auto pingRequest = SecureRequest(); auto pingRequest = SecureRequest();
@ -561,34 +567,31 @@ void ConnectionPrivate::tryToSend() {
auto resendRequest = SecureRequest(); auto resendRequest = SecureRequest();
auto stateRequest = SecureRequest(); auto stateRequest = SecureRequest();
auto httpWaitRequest = SecureRequest(); auto httpWaitRequest = SecureRequest();
auto checkDcKeyRequest = SecureRequest(); auto bindDcKeyRequest = SecureRequest();
if (_shiftedDcId == BareDcId(_shiftedDcId)) { // main session
if (!sendOnlyFirstPing && !_pingIdToSend && !_pingId && _pingSendAt <= crl::now()) {
_pingIdToSend = rand_value<mtpPingId>();
}
}
if (_pingIdToSend) { if (_pingIdToSend) {
if (sendOnlyFirstPing || _shiftedDcId != BareDcId(_shiftedDcId)) { if (sendOnlyFirstPing || !isMainSession) {
DEBUG_LOG(("MTP Info: sending ping, ping_id: %1"
).arg(_pingIdToSend));
pingRequest = SecureRequest::Serialize(MTPPing( pingRequest = SecureRequest::Serialize(MTPPing(
MTP_long(_pingIdToSend) MTP_long(_pingIdToSend)
)); ));
DEBUG_LOG(("MTP Info: sending ping, ping_id: %1"
).arg(_pingIdToSend));
} else { } else {
DEBUG_LOG(("MTP Info: sending ping_delay_disconnect, "
"ping_id: %1").arg(_pingIdToSend));
pingRequest = SecureRequest::Serialize(MTPPing_delay_disconnect( pingRequest = SecureRequest::Serialize(MTPPing_delay_disconnect(
MTP_long(_pingIdToSend), MTP_long(_pingIdToSend),
MTP_int(kPingDelayDisconnect))); MTP_int(kPingDelayDisconnect)));
DEBUG_LOG(("MTP Info: sending ping_delay_disconnect, "
"ping_id: %1").arg(_pingIdToSend));
}
_pingSendAt = pingRequest->msDate + kPingSendAfter;
if (_shiftedDcId == BareDcId(_shiftedDcId) && !sendOnlyFirstPing) { // main session
_pingSender.callOnce(kPingSendAfterForce); _pingSender.callOnce(kPingSendAfterForce);
} }
_pingSendAt = pingRequest->msDate + kPingSendAfter;
_pingId = base::take(_pingIdToSend); _pingId = base::take(_pingIdToSend);
} else if (!sendAll) {
DEBUG_LOG(("MTP Info: dc %1 sending only service or bind."
).arg(_shiftedDcId));
} else { } else {
DEBUG_LOG(("MTP Info: dc %1 trying to send after ping, state: %2").arg(_shiftedDcId).arg(state)); DEBUG_LOG(("MTP Info: dc %1 trying to send after ping, state: %2"
).arg(_shiftedDcId
).arg(state));
} }
if (!sendOnlyFirstPing) { if (!sendOnlyFirstPing) {
@ -625,23 +628,34 @@ void ConnectionPrivate::tryToSend() {
httpWaitRequest = SecureRequest::Serialize(MTPHttpWait( httpWaitRequest = SecureRequest::Serialize(MTPHttpWait(
MTP_http_wait(MTP_int(100), MTP_int(30), MTP_int(25000)))); MTP_http_wait(MTP_int(100), MTP_int(30), MTP_int(25000))));
} }
if (!_keyChecker) { if (_keyBinder && !_keyBinder->requested()) {
if (const auto &keyForCheck = _sessionData->getKeyForCheck()) { bindDcKeyRequest = _keyBinder->prepareRequest(
_keyChecker = std::make_unique<details::DcKeyChecker>( _temporaryKey,
_instance, _sessionData->getSessionId());
_shiftedDcId,
keyForCheck);
checkDcKeyRequest = _keyChecker->prepareRequest(
_key,
_sessionData->getSessionId());
// This is a special request with msgId used inside the message // This is a special request with msgId used inside the message
// body, so it is prepared already with a msgId and we place // body, so it is prepared already with a msgId and we place
// seqNo for it manually here. // seqNo for it manually here.
checkDcKeyRequest.setSeqNo( bindDcKeyRequest.setSeqNo(
_sessionData->nextRequestSeqNumber( _sessionData->nextRequestSeqNumber(
checkDcKeyRequest.needAck())); bindDcKeyRequest.needAck()));
} //} else if (!_keyChecker) {
// if (const auto &keyForCheck = _sessionData->getKeyForCheck()) {
// _keyChecker = std::make_unique<details::DcKeyChecker>(
// _instance,
// _shiftedDcId,
// keyForCheck);
// bindDcKeyRequest = _keyChecker->prepareRequest(
// _temporaryKey,
// _sessionData->getSessionId());
// // This is a special request with msgId used inside the message
// // body, so it is prepared already with a msgId and we place
// // seqNo for it manually here.
// bindDcKeyRequest.setSeqNo(
// _sessionData->nextRequestSeqNumber(
// bindDcKeyRequest.needAck()));
// }
} }
} }
@ -696,11 +710,14 @@ void ConnectionPrivate::tryToSend() {
QWriteLocker locker1(_sessionData->toSendMutex()); QWriteLocker locker1(_sessionData->toSendMutex());
auto toSendDummy = PreRequestMap(); auto toSendDummy = PreRequestMap();
auto &toSend = sendOnlyFirstPing auto &toSend = sendAll
? toSendDummy ? _sessionData->toSendMap()
: _sessionData->toSendMap(); : toSendDummy;
if (sendOnlyFirstPing) { if (!sendAll) {
locker1.unlock(); locker1.unlock();
} else {
int time = crl::now();
int now = crl::now();
} }
uint32 toSendCount = toSend.size(); uint32 toSendCount = toSend.size();
@ -709,7 +726,7 @@ void ConnectionPrivate::tryToSend() {
if (resendRequest) ++toSendCount; if (resendRequest) ++toSendCount;
if (stateRequest) ++toSendCount; if (stateRequest) ++toSendCount;
if (httpWaitRequest) ++toSendCount; if (httpWaitRequest) ++toSendCount;
if (checkDcKeyRequest) ++toSendCount; if (bindDcKeyRequest) ++toSendCount;
if (!toSendCount) { if (!toSendCount) {
return; // nothing to send return; // nothing to send
@ -725,12 +742,12 @@ void ConnectionPrivate::tryToSend() {
? stateRequest ? stateRequest
: httpWaitRequest : httpWaitRequest
? httpWaitRequest ? httpWaitRequest
: checkDcKeyRequest : bindDcKeyRequest
? checkDcKeyRequest ? bindDcKeyRequest
: toSend.cbegin().value(); : toSend.cbegin().value();
if (toSendCount == 1 && first->msDate > 0) { // if can send without container if (toSendCount == 1 && first->msDate > 0) { // if can send without container
toSendRequest = first; toSendRequest = first;
if (!sendOnlyFirstPing) { if (sendAll) {
toSend.clear(); toSend.clear();
locker1.unlock(); locker1.unlock();
} }
@ -753,9 +770,7 @@ void ConnectionPrivate::tryToSend() {
auto &haveSent = _sessionData->haveSentMap(); auto &haveSent = _sessionData->haveSentMap();
haveSent.insert(msgId, toSendRequest); haveSent.insert(msgId, toSendRequest);
if (needsLayer && !toSendRequest->needsLayer) { const auto wrapLayer = needsLayer && toSendRequest->needsLayer;
needsLayer = false;
}
if (toSendRequest->after) { if (toSendRequest->after) {
const auto toSendSize = tl::count_length(toSendRequest) >> 2; const auto toSendSize = tl::count_length(toSendRequest) >> 2;
auto wrappedRequest = SecureRequest::Prepare( auto wrappedRequest = SecureRequest::Prepare(
@ -766,7 +781,7 @@ void ConnectionPrivate::tryToSend() {
wrapInvokeAfter(wrappedRequest, toSendRequest, haveSent); wrapInvokeAfter(wrappedRequest, toSendRequest, haveSent);
toSendRequest = std::move(wrappedRequest); toSendRequest = std::move(wrappedRequest);
} }
if (needsLayer) { if (wrapLayer) {
const auto noWrapSize = (tl::count_length(toSendRequest) >> 2); const auto noWrapSize = (tl::count_length(toSendRequest) >> 2);
const auto toSendSize = noWrapSize + initSizeInInts; const auto toSendSize = noWrapSize + initSizeInInts;
auto wrappedRequest = SecureRequest::Prepare(toSendSize); auto wrappedRequest = SecureRequest::Prepare(toSendSize);
@ -793,7 +808,7 @@ void ConnectionPrivate::tryToSend() {
if (resendRequest) containerSize += resendRequest.messageSize(); if (resendRequest) containerSize += resendRequest.messageSize();
if (stateRequest) containerSize += stateRequest.messageSize(); if (stateRequest) containerSize += stateRequest.messageSize();
if (httpWaitRequest) containerSize += httpWaitRequest.messageSize(); if (httpWaitRequest) containerSize += httpWaitRequest.messageSize();
if (checkDcKeyRequest) containerSize += checkDcKeyRequest.messageSize(); if (bindDcKeyRequest) containerSize += bindDcKeyRequest.messageSize();
for (auto i = toSend.begin(), e = toSend.end(); i != e; ++i) { for (auto i = toSend.begin(), e = toSend.end(); i != e; ++i) {
containerSize += i.value().messageSize(); containerSize += i.value().messageSize();
if (needsLayer && i.value()->needsLayer) { if (needsLayer && i.value()->needsLayer) {
@ -836,7 +851,7 @@ void ConnectionPrivate::tryToSend() {
if (pingRequest) { if (pingRequest) {
_pingMsgId = placeToContainer(toSendRequest, bigMsgId, haveSentArr, pingRequest); _pingMsgId = placeToContainer(toSendRequest, bigMsgId, haveSentArr, pingRequest);
needAnyResponse = true; needAnyResponse = true;
} else if (resendRequest || stateRequest || checkDcKeyRequest) { } else if (resendRequest || stateRequest || bindDcKeyRequest) {
needAnyResponse = true; needAnyResponse = true;
} }
for (auto i = toSend.begin(), e = toSend.end(); i != e; ++i) { for (auto i = toSend.begin(), e = toSend.end(); i != e; ++i) {
@ -890,7 +905,7 @@ void ConnectionPrivate::tryToSend() {
if (resendRequest) placeToContainer(toSendRequest, bigMsgId, haveSentArr, resendRequest); if (resendRequest) placeToContainer(toSendRequest, bigMsgId, haveSentArr, resendRequest);
if (ackRequest) placeToContainer(toSendRequest, bigMsgId, haveSentArr, ackRequest); if (ackRequest) placeToContainer(toSendRequest, bigMsgId, haveSentArr, ackRequest);
if (httpWaitRequest) placeToContainer(toSendRequest, bigMsgId, haveSentArr, httpWaitRequest); if (httpWaitRequest) placeToContainer(toSendRequest, bigMsgId, haveSentArr, httpWaitRequest);
if (checkDcKeyRequest) placeToContainer(toSendRequest, bigMsgId, haveSentArr, checkDcKeyRequest); if (bindDcKeyRequest) placeToContainer(toSendRequest, bigMsgId, haveSentArr, bindDcKeyRequest);
mtpMsgId contMsgId = prepareToSend(toSendRequest, bigMsgId); mtpMsgId contMsgId = prepareToSend(toSendRequest, bigMsgId);
*(mtpMsgId*)(haveSentIdsWrap->data() + 4) = contMsgId; *(mtpMsgId*)(haveSentIdsWrap->data() + 4) = contMsgId;
@ -930,7 +945,7 @@ void ConnectionPrivate::connectToServer(bool afterConfig) {
_sessionData->connectionOptions()); _sessionData->connectionOptions());
// #TODO race. // #TODO race.
const auto hasKey = (_sessionData->getKey() != nullptr); const auto hasKey = (_sessionData->getTemporaryKey() != nullptr);
const auto bareDc = BareDcId(_shiftedDcId); const auto bareDc = BareDcId(_shiftedDcId);
_dcType = _instance->dcOptions()->dcType(_shiftedDcId); _dcType = _instance->dcOptions()->dcType(_shiftedDcId);
@ -1236,9 +1251,9 @@ void ConnectionPrivate::handleReceived() {
auto msgKey = *(MTPint128*)(ints + 2); auto msgKey = *(MTPint128*)(ints + 2);
#ifdef TDESKTOP_MTPROTO_OLD #ifdef TDESKTOP_MTPROTO_OLD
aesIgeDecrypt_oldmtp(encryptedInts, decryptedBuffer.data(), encryptedBytesCount, _key, msgKey); aesIgeDecrypt_oldmtp(encryptedInts, decryptedBuffer.data(), encryptedBytesCount, _temporaryKey, msgKey);
#else // TDESKTOP_MTPROTO_OLD #else // TDESKTOP_MTPROTO_OLD
aesIgeDecrypt(encryptedInts, decryptedBuffer.data(), encryptedBytesCount, _key, msgKey); aesIgeDecrypt(encryptedInts, decryptedBuffer.data(), encryptedBytesCount, _temporaryKey, msgKey);
#endif // TDESKTOP_MTPROTO_OLD #endif // TDESKTOP_MTPROTO_OLD
auto decryptedInts = reinterpret_cast<const mtpPrime*>(decryptedBuffer.constData()); auto decryptedInts = reinterpret_cast<const mtpPrime*>(decryptedBuffer.constData());
@ -1285,7 +1300,7 @@ void ConnectionPrivate::handleReceived() {
SHA256_CTX msgKeyLargeContext; SHA256_CTX msgKeyLargeContext;
SHA256_Init(&msgKeyLargeContext); SHA256_Init(&msgKeyLargeContext);
SHA256_Update(&msgKeyLargeContext, _key->partForMsgKey(false), 32); SHA256_Update(&msgKeyLargeContext, _temporaryKey->partForMsgKey(false), 32);
SHA256_Update(&msgKeyLargeContext, decryptedInts, encryptedBytesCount); SHA256_Update(&msgKeyLargeContext, decryptedInts, encryptedBytesCount);
SHA256_Final(sha256Buffer.data(), &msgKeyLargeContext); SHA256_Final(sha256Buffer.data(), &msgKeyLargeContext);
@ -1386,7 +1401,9 @@ void ConnectionPrivate::handleReceived() {
if (res != HandleResult::Success && res != HandleResult::Ignored) { if (res != HandleResult::Success && res != HandleResult::Ignored) {
_needSessionReset = (res == HandleResult::ResetSession); _needSessionReset = (res == HandleResult::ResetSession);
if (res == HandleResult::DestroyTemporaryKey) {
destroyTemporaryKey();
}
return restart(); return restart();
} }
_retryTimeout = 1; // reset restart() timer _retryTimeout = 1; // reset restart() timer
@ -1884,15 +1901,37 @@ ConnectionPrivate::HandleResult ConnectionPrivate::handleOneReceived(const mtpPr
response.resize(end - from); response.resize(end - from);
memcpy(response.data(), from, (end - from) * sizeof(mtpPrime)); memcpy(response.data(), from, (end - from) * sizeof(mtpPrime));
} }
if (typeId != mtpc_rpc_error) { if (typeId == mtpc_rpc_error) {
if (DcKeyBinder::IsDestroyedTemporaryKeyError(response)) {
return HandleResult::DestroyTemporaryKey;
}
} else {
// An error could be some RPC_CALL_FAIL or other error inside // An error could be some RPC_CALL_FAIL or other error inside
// the initConnection, so we're not sure yet that it was inited. // the initConnection, so we're not sure yet that it was inited.
// Wait till a good response is received. // Wait till a good response is received.
_sessionData->notifyConnectionInited(*_connectionOptions); _sessionData->notifyConnectionInited(*_connectionOptions);
} }
if (_keyChecker && _keyChecker->handleResponse(reqMsgId, response)) { if (_keyBinder) {
return HandleResult::Success; const auto result = _keyBinder->handleResponse(
reqMsgId,
response);
if (result == DcKeyBindState::Success) {
_sessionData->releaseKeyCreationOnDone(
_temporaryKey,
base::take(_keyBinder)->persistentKey());
_sessionData->queueNeedToResumeAndSend();
return HandleResult::Success;
} else if (result == DcKeyBindState::Failed
|| result == DcKeyBindState::DefinitelyDestroyed) {
// #TODO maybe destroy persistent key
// crl::on_main(
// _keyBinder->persistentKey()->setLastCheckTime(crl::now());
// instance->keyDestroyedOnServer(BareDcId(shiftedDcId), base::take(_keyBinder)->persistentKey()->keyId());
// )
_sessionData->queueNeedToResumeAndSend();
return HandleResult::Success;
}
} }
auto requestId = wasSent(reqMsgId.v); auto requestId = wasSent(reqMsgId.v);
if (requestId && requestId != mtpRequestId(0xFFFFFFFF)) { if (requestId && requestId != mtpRequestId(0xFFFFFFFF)) {
@ -2341,17 +2380,17 @@ void ConnectionPrivate::checkAuthKey() {
} }
void ConnectionPrivate::updateAuthKey() { void ConnectionPrivate::updateAuthKey() {
if (_keyCreator) { if (_keyCreator || _keyBinder) {
return; return;
} }
DEBUG_LOG(("AuthKey Info: Connection updating key from Session, dc %1").arg(_shiftedDcId)); DEBUG_LOG(("AuthKey Info: Connection updating key from Session, dc %1").arg(_shiftedDcId));
applyAuthKey(_sessionData->getKey()); applyAuthKey(_sessionData->getTemporaryKey());
} }
void ConnectionPrivate::applyAuthKey(AuthKeyPtr &&key) { void ConnectionPrivate::applyAuthKey(AuthKeyPtr &&temporaryKey) {
_key = std::move(key); _temporaryKey = std::move(temporaryKey);
const auto newKeyId = _key ? _key->keyId() : 0; const auto newKeyId = _temporaryKey ? _temporaryKey->keyId() : 0;
if (newKeyId) { if (newKeyId) {
if (_keyId == newKeyId) { if (_keyId == newKeyId) {
return; return;
@ -2389,19 +2428,39 @@ void ConnectionPrivate::applyAuthKey(AuthKeyPtr &&key) {
} }
void ConnectionPrivate::createDcKey() { void ConnectionPrivate::createDcKey() {
Expects(_keyCreator == nullptr);
Expects(_keyBinder == nullptr);
using Result = DcKeyCreator::Result; using Result = DcKeyCreator::Result;
using Error = DcKeyCreator::Error; using Error = DcKeyCreator::Error;
auto delegate = DcKeyCreator::Delegate(); auto delegate = DcKeyCreator::Delegate();
delegate.done = [=](base::expected<Result, Error> result) { delegate.done = [=](base::expected<Result, Error> result) {
if (result) { if (result) {
DEBUG_LOG(("AuthKey Info: auth key gen succeed, id: %1, server salt: %2").arg(result->key->keyId()).arg(result->serverSalt)); DEBUG_LOG(("AuthKey Info: auth key gen succeed, "
"ids: (%1, %2) server salts: (%3, %4)"
).arg(result->temporaryKey
? result->temporaryKey->keyId()
: 0
).arg(result->persistentKey
? result->persistentKey->keyId()
: 0
).arg(result->temporaryServerSalt
).arg(result->persistentServerSalt));
_sessionData->setSalt(result->serverSalt); _sessionData->setSalt(result->temporaryServerSalt);
_sessionData->clearForNewKey(_instance); _sessionData->clearForNewKey(_instance);
auto key = result->persistentKey
? std::move(result->persistentKey)
: _sessionData->getPersistentKey();
if (!key) {
restart();
}
_keyCreator = nullptr; _keyCreator = nullptr;
_sessionData->releaseKeyCreationOnDone(result->key); _keyBinder = std::make_unique<DcKeyBinder>(std::move(key));
applyAuthKey(std::move(result->key)); result->temporaryKey->setExpiresAt(
base::unixtime::now() + kTemporaryExpiresIn);
applyAuthKey(std::move(result->temporaryKey));
return; return;
} }
clearKeyCreatorOnFail(); clearKeyCreatorOnFail();
@ -2417,16 +2476,23 @@ void ConnectionPrivate::createDcKey() {
restart(); restart();
} }
}; };
const auto expireIn = (GetDcIdShift(_shiftedDcId) == kCheckKeyDcShift) delegate.sentSome = [=](uint64 size) {
? kCheckKeyExpiresIn onSentSome(size);
: TimeId(0); };
delegate.receivedSome = [=] {
onReceivedSome();
};
// const auto check = (GetDcIdShift(_shiftedDcId) == kCheckKeyDcShift); // #TODO remove kCheckKeyDcShift
auto request = DcKeyCreator::Request();
request.persistentNeeded = !_sessionData->getPersistentKey();
request.temporaryExpiresIn = kTemporaryExpiresIn;
_keyCreator = std::make_unique<DcKeyCreator>( _keyCreator = std::make_unique<DcKeyCreator>(
BareDcId(_shiftedDcId), BareDcId(_shiftedDcId),
getProtocolDcId(), getProtocolDcId(),
_connection.get(), _connection.get(),
_instance->dcOptions(), _instance->dcOptions(),
std::move(delegate), std::move(delegate),
expireIn); request);
} }
void ConnectionPrivate::authKeyChecked() { void ConnectionPrivate::authKeyChecked() {
@ -2471,28 +2537,26 @@ void ConnectionPrivate::handleError(int errorCode) {
_waitForConnectedTimer.cancel(); _waitForConnectedTimer.cancel();
if (errorCode == -404) { if (errorCode == -404) {
if (_dcType == DcType::Cdn && !_instance->isKeysDestroyer()) { if (_instance->isKeysDestroyer()) {
LOG(("MTP Info: -404 error received in CDN dc %1, assuming it was destroyed, recreating.").arg(_shiftedDcId)); LOG(("MTP Info: -404 error received in destroyer %1, assuming key was destroyed.").arg(_shiftedDcId));
destroyCdnKey();
return restart();
} else {
LOG(("MTP Info: -404 error received, informing instance."));
_instance->checkIfKeyWasDestroyed(_shiftedDcId); _instance->checkIfKeyWasDestroyed(_shiftedDcId);
if (_instance->isKeysDestroyer()) { return;
return; } else if (_temporaryKey) {
} LOG(("MTP Info: -404 error received in %1 with temporary key, assuming it was destroyed.").arg(_shiftedDcId));
destroyTemporaryKey();
} }
} }
MTP_LOG(_shiftedDcId, ("Restarting after error in connection, error code: %1...").arg(errorCode)); MTP_LOG(_shiftedDcId, ("Restarting after error in connection, error code: %1...").arg(errorCode));
return restart(); return restart();
} }
void ConnectionPrivate::destroyCdnKey() { void ConnectionPrivate::destroyTemporaryKey() {
if (_key) { cancelKeyBinder();
_sessionData->destroyCdnKey(_keyId); if (_temporaryKey) {
_sessionData->destroyTemporaryKey(_temporaryKey->keyId());
} }
_key = nullptr; _needSessionReset = true;
_keyId = 0; applyAuthKey(nullptr);
} }
bool ConnectionPrivate::sendSecureRequest( bool ConnectionPrivate::sendSecureRequest(
@ -2542,7 +2606,7 @@ bool ConnectionPrivate::sendSecureRequest(
request->constData(), request->constData(),
&packet[prefix], &packet[prefix],
fullSize * sizeof(mtpPrime), fullSize * sizeof(mtpPrime),
_key, _temporaryKey,
msgKey); msgKey);
#else // TDESKTOP_MTPROTO_OLD #else // TDESKTOP_MTPROTO_OLD
uchar encryptedSHA256[32]; uchar encryptedSHA256[32];
@ -2550,7 +2614,7 @@ bool ConnectionPrivate::sendSecureRequest(
SHA256_CTX msgKeyLargeContext; SHA256_CTX msgKeyLargeContext;
SHA256_Init(&msgKeyLargeContext); SHA256_Init(&msgKeyLargeContext);
SHA256_Update(&msgKeyLargeContext, _key->partForMsgKey(true), 32); SHA256_Update(&msgKeyLargeContext, _temporaryKey->partForMsgKey(true), 32);
SHA256_Update(&msgKeyLargeContext, request->constData(), fullSize * sizeof(mtpPrime)); SHA256_Update(&msgKeyLargeContext, request->constData(), fullSize * sizeof(mtpPrime));
SHA256_Final(encryptedSHA256, &msgKeyLargeContext); SHA256_Final(encryptedSHA256, &msgKeyLargeContext);
@ -2562,7 +2626,7 @@ bool ConnectionPrivate::sendSecureRequest(
request->constData(), request->constData(),
&packet[prefix], &packet[prefix],
fullSize * sizeof(mtpPrime), fullSize * sizeof(mtpPrime),
_key, _temporaryKey,
msgKey); msgKey);
#endif // TDESKTOP_MTPROTO_OLD #endif // TDESKTOP_MTPROTO_OLD
@ -2613,6 +2677,14 @@ void ConnectionPrivate::clearKeyCreatorOnFail() {
_sessionData->releaseKeyCreationOnFail(); _sessionData->releaseKeyCreationOnFail();
} }
void ConnectionPrivate::cancelKeyBinder() {
if (!_keyBinder) {
return;
}
_keyBinder = nullptr;
_sessionData->releaseKeyCreationOnFail();
}
void ConnectionPrivate::stop() { void ConnectionPrivate::stop() {
} }

View File

@ -17,7 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace MTP { namespace MTP {
namespace details { namespace details {
class DcKeyCreator; class DcKeyCreator;
class DcKeyChecker; class DcKeyBinder;
} // namespace details } // namespace details
// How much time to wait for some more requests, when sending msg acks. // How much time to wait for some more requests, when sending msg acks.
@ -103,6 +103,7 @@ private:
Ignored, Ignored,
RestartConnection, RestartConnection,
ResetSession, ResetSession,
DestroyTemporaryKey,
ParseError, ParseError,
}; };
@ -180,9 +181,10 @@ private:
void resetSession(); void resetSession();
void checkAuthKey(); void checkAuthKey();
void authKeyChecked(); void authKeyChecked();
void destroyCdnKey(); void destroyTemporaryKey();
void clearKeyCreatorOnFail(); void clearKeyCreatorOnFail();
void applyAuthKey(AuthKeyPtr &&key); void cancelKeyBinder();
void applyAuthKey(AuthKeyPtr &&temporaryKey);
const not_null<Instance*> _instance; const not_null<Instance*> _instance;
DcType _dcType = DcType::Regular; DcType _dcType = DcType::Regular;
@ -224,13 +226,13 @@ private:
bool _restarted = false; bool _restarted = false;
bool _finished = false; bool _finished = false;
AuthKeyPtr _key; AuthKeyPtr _temporaryKey;
uint64 _keyId = 0; uint64 _keyId = 0;
std::shared_ptr<SessionData> _sessionData; std::shared_ptr<SessionData> _sessionData;
std::unique_ptr<ConnectionOptions> _connectionOptions; std::unique_ptr<ConnectionOptions> _connectionOptions;
std::unique_ptr<details::DcKeyCreator> _keyCreator; std::unique_ptr<details::DcKeyCreator> _keyCreator;
std::unique_ptr<details::DcKeyChecker> _keyChecker; std::unique_ptr<details::DcKeyBinder> _keyBinder;
}; };

View File

@ -25,34 +25,40 @@ constexpr auto kSpecialRequestTimeoutMs = 6000; // 4 seconds timeout for it to w
Dcenter::Dcenter(DcId dcId, AuthKeyPtr &&key) Dcenter::Dcenter(DcId dcId, AuthKeyPtr &&key)
: _id(dcId) : _id(dcId)
, _key(std::move(key)) { , _persistentKey(std::move(key)) {
} }
DcId Dcenter::id() const { DcId Dcenter::id() const {
return _id; return _id;
} }
AuthKeyPtr Dcenter::getKey() const { AuthKeyPtr Dcenter::getTemporaryKey() const {
QReadLocker lock(&_mutex); QReadLocker lock(&_mutex);
return _key; return _temporaryKey;
} }
bool Dcenter::destroyCdnKey(uint64 keyId) { AuthKeyPtr Dcenter::getPersistentKey() const {
return destroyKey(keyId); QReadLocker lock(&_mutex);
return _persistentKey;
}
bool Dcenter::destroyTemporaryKey(uint64 keyId) {
QWriteLocker lock(&_mutex);
if (!_temporaryKey || _temporaryKey->keyId() != keyId) {
return false;
}
_temporaryKey = nullptr;
_connectionInited = false;
return true;
} }
bool Dcenter::destroyConfirmedForgottenKey(uint64 keyId) { bool Dcenter::destroyConfirmedForgottenKey(uint64 keyId) {
return destroyKey(keyId);
}
bool Dcenter::destroyKey(uint64 keyId) {
Expects(!_creatingKey || !_key);
QWriteLocker lock(&_mutex); QWriteLocker lock(&_mutex);
if (_key->keyId() != keyId) { if (!_persistentKey || _persistentKey->keyId() != keyId) {
return false; return false;
} }
_key = nullptr; _temporaryKey = nullptr;
_persistentKey = nullptr;
_connectionInited = false; _connectionInited = false;
return true; return true;
} }
@ -69,7 +75,7 @@ void Dcenter::setConnectionInited(bool connectionInited) {
bool Dcenter::acquireKeyCreation() { bool Dcenter::acquireKeyCreation() {
QReadLocker lock(&_mutex); QReadLocker lock(&_mutex);
if (_key != nullptr) { if (_temporaryKey != nullptr) {
return false; return false;
} }
auto expected = false; auto expected = false;
@ -78,21 +84,27 @@ bool Dcenter::acquireKeyCreation() {
void Dcenter::releaseKeyCreationOnFail() { void Dcenter::releaseKeyCreationOnFail() {
Expects(_creatingKey); Expects(_creatingKey);
Expects(_key == nullptr); Expects(_temporaryKey == nullptr);
_creatingKey = false; _creatingKey = false;
} }
void Dcenter::releaseKeyCreationOnDone(const AuthKeyPtr &key) { void Dcenter::releaseKeyCreationOnDone(
const AuthKeyPtr &temporaryKey,
const AuthKeyPtr &persistentKey) {
Expects(_creatingKey); Expects(_creatingKey);
Expects(_key == nullptr); Expects(_temporaryKey == nullptr);
QWriteLocker lock(&_mutex); QWriteLocker lock(&_mutex);
DEBUG_LOG(("AuthKey Info: Dcenter::releaseKeyCreationOnDone(%1), " DEBUG_LOG(("AuthKey Info: Dcenter::releaseKeyCreationOnDone(%1, %2), "
"emitting authKeyChanged, dc %2" "emitting authKeyChanged, dc %3"
).arg(key ? key->keyId() : 0 ).arg(temporaryKey ? temporaryKey->keyId() : 0
).arg(persistentKey ? persistentKey->keyId() : 0
).arg(_id)); ).arg(_id));
_key = key; _temporaryKey = temporaryKey;
if (persistentKey) {
_persistentKey = persistentKey;
}
_connectionInited = false; _connectionInited = false;
_creatingKey = false; _creatingKey = false;
} }

View File

@ -23,10 +23,13 @@ public:
// Thread-safe. // Thread-safe.
[[nodiscard]] DcId id() const; [[nodiscard]] DcId id() const;
[[nodiscard]] AuthKeyPtr getKey() const; [[nodiscard]] AuthKeyPtr getTemporaryKey() const;
bool destroyCdnKey(uint64 keyId); [[nodiscard]] AuthKeyPtr getPersistentKey() const;
bool destroyTemporaryKey(uint64 keyId);
bool destroyConfirmedForgottenKey(uint64 keyId); bool destroyConfirmedForgottenKey(uint64 keyId);
void releaseKeyCreationOnDone(const AuthKeyPtr &key); void releaseKeyCreationOnDone(
const AuthKeyPtr &temporaryKey,
const AuthKeyPtr &persistentKey);
[[nodiscard]] bool connectionInited() const; [[nodiscard]] bool connectionInited() const;
void setConnectionInited(bool connectionInited = true); void setConnectionInited(bool connectionInited = true);
@ -35,12 +38,11 @@ public:
void releaseKeyCreationOnFail(); void releaseKeyCreationOnFail();
private: private:
bool destroyKey(uint64 keyId);
const DcId _id = 0; const DcId _id = 0;
mutable QReadWriteLock _mutex; mutable QReadWriteLock _mutex;
AuthKeyPtr _key; AuthKeyPtr _temporaryKey;
AuthKeyPtr _persistentKey;
bool _connectionInited = false; bool _connectionInited = false;
std::atomic<bool> _creatingKey = false; std::atomic<bool> _creatingKey = false;

View File

@ -5,7 +5,7 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link: For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "mtproto/details/mtproto_dc_key_checker.h" #include "mtproto/details/mtproto_dc_key_binder.h"
#include "mtproto/mtp_instance.h" #include "mtproto/mtp_instance.h"
#include "base/unixtime.h" #include "base/unixtime.h"
@ -17,8 +17,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace MTP::details { namespace MTP::details {
namespace { namespace {
constexpr auto kBindKeyExpireTimeout = TimeId(3600);
[[nodiscard]] QByteArray EncryptBindAuthKeyInner( [[nodiscard]] QByteArray EncryptBindAuthKeyInner(
const AuthKeyPtr &persistentKey, const AuthKeyPtr &persistentKey,
mtpMsgId realMsgId, mtpMsgId realMsgId,
@ -74,26 +72,28 @@ constexpr auto kBindKeyExpireTimeout = TimeId(3600);
} // namespace } // namespace
DcKeyChecker::DcKeyChecker( DcKeyBinder::DcKeyBinder(AuthKeyPtr &&persistentKey)
not_null<Instance*> instance, : _persistentKey(std::move(persistentKey)) {
ShiftedDcId shiftedDcId, Expects(_persistentKey != nullptr);
const AuthKeyPtr &persistentKey)
: _instance(instance)
, _shiftedDcId(shiftedDcId)
, _persistentKey(persistentKey) {
} }
SecureRequest DcKeyChecker::prepareRequest( bool DcKeyBinder::requested() const {
return _requestMsgId != 0;
}
SecureRequest DcKeyBinder::prepareRequest(
const AuthKeyPtr &temporaryKey, const AuthKeyPtr &temporaryKey,
uint64 sessionId) { uint64 sessionId) {
Expects(_requestMsgId == 0); Expects(_requestMsgId == 0);
Expects(temporaryKey != nullptr);
Expects(temporaryKey->expiresAt() != 0);
const auto nonce = openssl::RandomValue<uint64>(); const auto nonce = openssl::RandomValue<uint64>();
_requestMsgId = base::unixtime::mtproto_msg_id(); _requestMsgId = base::unixtime::mtproto_msg_id();
auto result = SecureRequest::Serialize(MTPauth_BindTempAuthKey( auto result = SecureRequest::Serialize(MTPauth_BindTempAuthKey(
MTP_long(_persistentKey->keyId()), MTP_long(_persistentKey->keyId()),
MTP_long(nonce), MTP_long(nonce),
MTP_int(kBindKeyExpireTimeout), MTP_int(temporaryKey->expiresAt()),
MTP_bytes(EncryptBindAuthKeyInner( MTP_bytes(EncryptBindAuthKeyInner(
_persistentKey, _persistentKey,
_requestMsgId, _requestMsgId,
@ -102,49 +102,56 @@ SecureRequest DcKeyChecker::prepareRequest(
MTP_long(temporaryKey->keyId()), MTP_long(temporaryKey->keyId()),
MTP_long(_persistentKey->keyId()), MTP_long(_persistentKey->keyId()),
MTP_long(sessionId), MTP_long(sessionId),
MTP_int(kBindKeyExpireTimeout)))))); MTP_int(temporaryKey->expiresAt()))))));
result.setMsgId(_requestMsgId); result.setMsgId(_requestMsgId);
return result; return result;
} }
bool DcKeyChecker::handleResponse( DcKeyBindState DcKeyBinder::handleResponse(
MTPlong requestMsgId, MTPlong requestMsgId,
const mtpBuffer &response) { const mtpBuffer &response) {
Expects(!response.isEmpty()); Expects(!response.isEmpty());
if (!_requestMsgId || requestMsgId.v != _requestMsgId) { if (!_requestMsgId || requestMsgId.v != _requestMsgId) {
return false; return DcKeyBindState::Unknown;
} }
_requestMsgId = 0;
const auto destroyed = [&] { auto from = response.begin();
if (response[0] != mtpc_rpc_error) { const auto end = from + response.size();
return false; auto error = MTPRpcError();
} auto result = MTPBool();
auto error = MTPRpcError(); if (response[0] == mtpc_boolTrue) {
auto from = response.begin(); return DcKeyBindState::Success;
const auto end = from + response.size(); } else if (response[0] == mtpc_rpc_error && error.read(from, end)) {
if (!error.read(from, end)) { const auto destroyed = error.match([&](const MTPDrpc_error &data) {
return false;
}
return error.match([&](const MTPDrpc_error &data) {
return (data.verror_code().v == 400) return (data.verror_code().v == 400)
&& (data.verror_message().v == "ENCRYPTED_MESSAGE_INVALID"); && (data.verror_message().v == "ENCRYPTED_MESSAGE_INVALID");
}); });
}(); return destroyed
? DcKeyBindState::DefinitelyDestroyed
: DcKeyBindState::Failed;
} else {
return DcKeyBindState::Failed;
}
}
const auto instance = _instance; AuthKeyPtr DcKeyBinder::persistentKey() const {
const auto shiftedDcId = _shiftedDcId; return _persistentKey;
const auto keyId = _persistentKey->keyId(); }
_persistentKey->setLastCheckTime(crl::now());
crl::on_main(instance, [=] { bool DcKeyBinder::IsDestroyedTemporaryKeyError(
instance->killSession(shiftedDcId); const mtpBuffer &buffer) {
if (destroyed) { auto from = buffer.data();
instance->keyDestroyedOnServer(BareDcId(shiftedDcId), keyId); const auto end = from + buffer.size();
} auto error = MTPRpcError();
if (!error.read(from, from + buffer.size())) {
return false;
}
return error.match([&](const MTPDrpc_error &data) {
return (data.verror_code().v == 401)
&& (data.verror_message().v == "AUTH_KEY_PERM_EMPTY");
}); });
_requestMsgId = 0;
return true;
} }
} // namespace MTP::details } // namespace MTP::details

View File

@ -16,27 +16,31 @@ class Instance;
namespace MTP::details { namespace MTP::details {
enum class DcKeyState { enum class DcKeyBindState {
MaybeExisting, Unknown,
Success,
Failed,
DefinitelyDestroyed, DefinitelyDestroyed,
}; };
class DcKeyChecker final { class DcKeyBinder final {
public: public:
DcKeyChecker( DcKeyBinder(AuthKeyPtr &&persistentKey);
not_null<Instance*> instance,
ShiftedDcId shiftedDcId,
const AuthKeyPtr &persistentKey);
[[nodiscard]] bool requested() const;
[[nodiscard]] SecureRequest prepareRequest( [[nodiscard]] SecureRequest prepareRequest(
const AuthKeyPtr &temporaryKey, const AuthKeyPtr &temporaryKey,
uint64 sessionId); uint64 sessionId);
bool handleResponse(MTPlong requestMsgId, const mtpBuffer &response); [[nodiscard]] DcKeyBindState handleResponse(
MTPlong requestMsgId,
const mtpBuffer &response);
[[nodiscard]] AuthKeyPtr persistentKey() const;
[[nodiscard]] static bool IsDestroyedTemporaryKeyError(
const mtpBuffer &buffer);
private: private:
const not_null<Instance*> _instance; AuthKeyPtr _persistentKey;
const ShiftedDcId _shiftedDcId = 0;
const AuthKeyPtr _persistentKey;
mtpMsgId _requestMsgId = 0; mtpMsgId _requestMsgId = 0;
}; };

View File

@ -153,383 +153,50 @@ MTPint128 NonceDigest(bytes::const_span data) {
} // namespace } // namespace
DcKeyCreator::Attempt::~Attempt() {
const auto clearBytes = [](bytes::span bytes) {
OPENSSL_cleanse(bytes.data(), bytes.size());
};
OPENSSL_cleanse(&data, sizeof(data));
clearBytes(dhPrime);
clearBytes(g_a);
clearBytes(authKey);
}
DcKeyCreator::DcKeyCreator( DcKeyCreator::DcKeyCreator(
DcId dcId, DcId dcId,
int16 protocolDcId, int16 protocolDcId,
not_null<AbstractConnection*> connection, not_null<AbstractConnection*> connection,
not_null<DcOptions*> dcOptions, not_null<DcOptions*> dcOptions,
Delegate delegate, Delegate delegate,
TimeId expireIn) Request request)
: _connection(connection) : _connection(connection)
, _dcOptions(dcOptions) , _dcOptions(dcOptions)
, _dcId(dcId) , _dcId(dcId)
, _protocolDcId(protocolDcId) , _protocolDcId(protocolDcId)
, _expireIn(expireIn) , _request(request)
, _delegate(std::move(delegate)) { , _delegate(std::move(delegate)) {
Expects(_expireIn >= 0); Expects(_request.temporaryExpiresIn > 0);
Expects(_delegate.done != nullptr); Expects(_delegate.done != nullptr);
_data.nonce = openssl::RandomValue<MTPint128>(); QObject::connect(_connection, &AbstractConnection::receivedData, [=] {
pqSend(); answered();
});
pqSend(&_temporary, _request.temporaryExpiresIn);
if (_request.persistentNeeded) {
pqSend(&_persistent, 0);
}
} }
DcKeyCreator::~DcKeyCreator() { DcKeyCreator::~DcKeyCreator() {
if (_delegate.done) { if (_delegate.done) {
stopReceiving(); stopReceiving();
} }
const auto clearBytes = [](bytes::span bytes) {
OPENSSL_cleanse(bytes.data(), bytes.size());
};
OPENSSL_cleanse(&_data, sizeof(_data));
clearBytes(_dhPrime);
clearBytes(_g_a);
clearBytes(_authKey);
} }
void DcKeyCreator::pqSend() { template <typename RequestType>
QObject::connect(_connection, &AbstractConnection::receivedData, [=] { void DcKeyCreator::sendNotSecureRequest(const RequestType &request) {
pqAnswered();
});
DEBUG_LOG(("AuthKey Info: sending Req_pq..."));
sendNotSecureRequest(MTPReq_pq_multi(_data.nonce));
}
void DcKeyCreator::pqAnswered() {
stopReceiving();
DEBUG_LOG(("AuthKey Info: receiving Req_pq answer..."));
MTPReq_pq::ResponseType res_pq;
if (!readNotSecureResponse(res_pq)) {
return failed();
}
auto &res_pq_data = res_pq.c_resPQ();
if (res_pq_data.vnonce() != _data.nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in res_pq)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&res_pq_data.vnonce(), 16).str()).arg(Logs::mb(&_data.nonce, 16).str()));
return failed();
}
const auto rsaKey = _dcOptions->getDcRSAKey(
_dcId,
res_pq.c_resPQ().vserver_public_key_fingerprints().v);
if (!rsaKey.valid()) {
return failed(Error::UnknownPublicKey);
}
_data.server_nonce = res_pq_data.vserver_nonce();
_data.new_nonce = openssl::RandomValue<MTPint256>();
const auto &pq = res_pq_data.vpq().v;
const auto parsed = ParsePQ(res_pq_data.vpq().v);
if (parsed.p.isEmpty() || parsed.q.isEmpty()) {
LOG(("AuthKey Error: could not factor pq!"));
DEBUG_LOG(("AuthKey Error: problematic pq: %1").arg(Logs::mb(pq.constData(), pq.length()).str()));
return failed();
}
const auto dhEncString = [&] {
return (_expireIn == 0)
? EncryptPQInnerRSA(
MTP_p_q_inner_data_dc(
res_pq_data.vpq(),
MTP_bytes(parsed.p),
MTP_bytes(parsed.q),
_data.nonce,
_data.server_nonce,
_data.new_nonce,
MTP_int(_protocolDcId)),
rsaKey)
: EncryptPQInnerRSA(
MTP_p_q_inner_data_temp_dc(
res_pq_data.vpq(),
MTP_bytes(parsed.p),
MTP_bytes(parsed.q),
_data.nonce,
_data.server_nonce,
_data.new_nonce,
MTP_int(_protocolDcId),
MTP_int(_expireIn)),
rsaKey);
}();
if (dhEncString.empty()) {
return failed();
}
QObject::connect(_connection, &AbstractConnection::receivedData, [=] {
dhParamsAnswered();
});
DEBUG_LOG(("AuthKey Info: sending Req_DH_params..."));
sendNotSecureRequest(MTPReq_DH_params(
_data.nonce,
_data.server_nonce,
MTP_bytes(parsed.p),
MTP_bytes(parsed.q),
MTP_long(rsaKey.fingerprint()),
MTP_bytes(dhEncString)));
}
void DcKeyCreator::dhParamsAnswered() {
stopReceiving();
DEBUG_LOG(("AuthKey Info: receiving Req_DH_params answer..."));
MTPReq_DH_params::ResponseType res_DH_params;
if (!readNotSecureResponse(res_DH_params)) {
return failed();
}
switch (res_DH_params.type()) {
case mtpc_server_DH_params_ok: {
const auto &encDH(res_DH_params.c_server_DH_params_ok());
if (encDH.vnonce() != _data.nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in server_DH_params_ok)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&encDH.vnonce(), 16).str()).arg(Logs::mb(&_data.nonce, 16).str()));
return failed();
}
if (encDH.vserver_nonce() != _data.server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_params_ok)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&encDH.vserver_nonce(), 16).str()).arg(Logs::mb(&_data.server_nonce, 16).str()));
return failed();
}
auto &encDHStr = encDH.vencrypted_answer().v;
uint32 encDHLen = encDHStr.length(), encDHBufLen = encDHLen >> 2;
if ((encDHLen & 0x03) || encDHBufLen < 6) {
LOG(("AuthKey Error: bad encrypted data length %1 (in server_DH_params_ok)!").arg(encDHLen));
DEBUG_LOG(("AuthKey Error: received encrypted data %1").arg(Logs::mb(encDHStr.constData(), encDHLen).str()));
return failed();
}
const auto nlen = sizeof(_data.new_nonce);
const auto slen = sizeof(_data.server_nonce);
auto tmp_aes_buffer = bytes::array<1024>();
const auto tmp_aes = bytes::make_span(tmp_aes_buffer);
bytes::copy(tmp_aes, bytes::object_as_span(&_data.new_nonce));
bytes::copy(tmp_aes.subspan(nlen), bytes::object_as_span(&_data.server_nonce));
bytes::copy(tmp_aes.subspan(nlen + slen), bytes::object_as_span(&_data.new_nonce));
bytes::copy(tmp_aes.subspan(nlen + slen + nlen), bytes::object_as_span(&_data.new_nonce));
const auto sha1ns = openssl::Sha1(tmp_aes.subspan(0, nlen + slen));
const auto sha1sn = openssl::Sha1(tmp_aes.subspan(nlen, nlen + slen));
const auto sha1nn = openssl::Sha1(tmp_aes.subspan(nlen + slen, nlen + nlen));
mtpBuffer decBuffer;
decBuffer.resize(encDHBufLen);
const auto aesKey = bytes::make_span(_data.aesKey);
const auto aesIV = bytes::make_span(_data.aesIV);
bytes::copy(aesKey, bytes::make_span(sha1ns).subspan(0, 20));
bytes::copy(aesKey.subspan(20), bytes::make_span(sha1sn).subspan(0, 12));
bytes::copy(aesIV, bytes::make_span(sha1sn).subspan(12, 8));
bytes::copy(aesIV.subspan(8), bytes::make_span(sha1nn).subspan(0, 20));
bytes::copy(aesIV.subspan(28), bytes::object_as_span(&_data.new_nonce).subspan(0, 4));
aesIgeDecryptRaw(encDHStr.constData(), &decBuffer[0], encDHLen, aesKey.data(), aesIV.data());
const mtpPrime *from(&decBuffer[5]), *to(from), *end(from + (encDHBufLen - 5));
MTPServer_DH_inner_data dh_inner;
if (!dh_inner.read(to, end)) {
LOG(("AuthKey Error: could not decrypt server_DH_inner_data!"));
return failed();
}
const auto &dh_inner_data(dh_inner.c_server_DH_inner_data());
if (dh_inner_data.vnonce() != _data.nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in server_DH_inner_data)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&dh_inner_data.vnonce(), 16).str()).arg(Logs::mb(&_data.nonce, 16).str()));
return failed();
}
if (dh_inner_data.vserver_nonce() != _data.server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_inner_data)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&dh_inner_data.vserver_nonce(), 16).str()).arg(Logs::mb(&_data.server_nonce, 16).str()));
return failed();
}
const auto sha1Buffer = openssl::Sha1(
bytes::make_span(decBuffer).subspan(
5 * sizeof(mtpPrime),
(to - from) * sizeof(mtpPrime)));
const auto sha1Dec = bytes::make_span(decBuffer).subspan(
0,
openssl::kSha1Size);
if (bytes::compare(sha1Dec, sha1Buffer)) {
LOG(("AuthKey Error: sha1 hash of encrypted part did not match!"));
DEBUG_LOG(("AuthKey Error: sha1 did not match, server_nonce: %1, new_nonce %2, encrypted data %3").arg(Logs::mb(&_data.server_nonce, 16).str()).arg(Logs::mb(&_data.new_nonce, 16).str()).arg(Logs::mb(encDHStr.constData(), encDHLen).str()));
return failed();
}
base::unixtime::update(dh_inner_data.vserver_time().v);
// check that dhPrime and (dhPrime - 1) / 2 are really prime
if (!IsPrimeAndGood(bytes::make_span(dh_inner_data.vdh_prime().v), dh_inner_data.vg().v)) {
LOG(("AuthKey Error: bad dh_prime primality!"));
return failed();
}
_dhPrime = bytes::make_vector(
dh_inner_data.vdh_prime().v);
_data.g = dh_inner_data.vg().v;
_g_a = bytes::make_vector(dh_inner_data.vg_a().v);
_data.retry_id = MTP_long(0);
_data.retries = 0;
} return dhClientParamsSend();
case mtpc_server_DH_params_fail: {
const auto &encDH(res_DH_params.c_server_DH_params_fail());
if (encDH.vnonce() != _data.nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in server_DH_params_fail)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&encDH.vnonce(), 16).str()).arg(Logs::mb(&_data.nonce, 16).str()));
return failed();
}
if (encDH.vserver_nonce() != _data.server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_params_fail)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&encDH.vserver_nonce(), 16).str()).arg(Logs::mb(&_data.server_nonce, 16).str()));
return failed();
}
if (encDH.vnew_nonce_hash() != NonceDigest(bytes::object_as_span(&_data.new_nonce))) {
LOG(("AuthKey Error: received new_nonce_hash did not match!"));
DEBUG_LOG(("AuthKey Error: received new_nonce_hash: %1, new_nonce: %2").arg(Logs::mb(&encDH.vnew_nonce_hash(), 16).str()).arg(Logs::mb(&_data.new_nonce, 32).str()));
return failed();
}
LOG(("AuthKey Error: server_DH_params_fail received!"));
} return failed();
}
LOG(("AuthKey Error: unknown server_DH_params received, typeId = %1").arg(res_DH_params.type()));
return failed();
}
void DcKeyCreator::dhClientParamsSend() {
if (++_data.retries > 5) {
LOG(("AuthKey Error: could not create auth_key for %1 retries").arg(_data.retries - 1));
return failed();
}
// gen rand 'b'
auto randomSeed = bytes::vector(ModExpFirst::kRandomPowerSize);
bytes::set_random(randomSeed);
auto g_b_data = CreateModExp(_data.g, _dhPrime, randomSeed);
if (g_b_data.modexp.empty()) {
LOG(("AuthKey Error: could not generate good g_b."));
return failed();
}
auto computedAuthKey = CreateAuthKey(_g_a, g_b_data.randomPower, _dhPrime);
if (computedAuthKey.empty()) {
LOG(("AuthKey Error: could not generate auth_key."));
return failed();
}
AuthKey::FillData(_authKey, computedAuthKey);
auto auth_key_sha = openssl::Sha1(_authKey);
memcpy(&_data.auth_key_aux_hash, auth_key_sha.data(), 8);
memcpy(&_data.auth_key_hash, auth_key_sha.data() + 12, 8);
const auto client_dh_inner = MTP_client_DH_inner_data(
_data.nonce,
_data.server_nonce,
_data.retry_id,
MTP_bytes(g_b_data.modexp));
auto sdhEncString = EncryptClientDHInner(
client_dh_inner,
_data.aesKey.data(),
_data.aesIV.data());
QObject::connect(_connection, &AbstractConnection::receivedData, [=] {
dhClientParamsAnswered();
});
DEBUG_LOG(("AuthKey Info: sending Req_client_DH_params..."));
sendNotSecureRequest(MTPSet_client_DH_params(
_data.nonce,
_data.server_nonce,
MTP_string(std::move(sdhEncString))));
}
void DcKeyCreator::dhClientParamsAnswered() {
stopReceiving();
DEBUG_LOG(("AuthKey Info: receiving Req_client_DH_params answer..."));
MTPSet_client_DH_params::ResponseType res_client_DH_params;
if (!readNotSecureResponse(res_client_DH_params)) {
return failed();
}
switch (res_client_DH_params.type()) {
case mtpc_dh_gen_ok: {
const auto &resDH(res_client_DH_params.c_dh_gen_ok());
if (resDH.vnonce() != _data.nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_ok)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&resDH.vnonce(), 16).str()).arg(Logs::mb(&_data.nonce, 16).str()));
return failed();
}
if (resDH.vserver_nonce() != _data.server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_ok)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&resDH.vserver_nonce(), 16).str()).arg(Logs::mb(&_data.server_nonce, 16).str()));
return failed();
}
_data.new_nonce_buf[32] = bytes::type(1);
if (resDH.vnew_nonce_hash1() != NonceDigest(_data.new_nonce_buf)) {
LOG(("AuthKey Error: received new_nonce_hash1 did not match!"));
DEBUG_LOG(("AuthKey Error: received new_nonce_hash1: %1, new_nonce_buf: %2").arg(Logs::mb(&resDH.vnew_nonce_hash1(), 16).str()).arg(Logs::mb(_data.new_nonce_buf.data(), 41).str()));
return failed();
}
uint64 salt1 = _data.new_nonce.l.l, salt2 = _data.server_nonce.l;
done(salt1 ^ salt2);
} return;
case mtpc_dh_gen_retry: {
const auto &resDH(res_client_DH_params.c_dh_gen_retry());
if (resDH.vnonce() != _data.nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_retry)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&resDH.vnonce(), 16).str()).arg(Logs::mb(&_data.nonce, 16).str()));
return failed();
}
if (resDH.vserver_nonce() != _data.server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_retry)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&resDH.vserver_nonce(), 16).str()).arg(Logs::mb(&_data.server_nonce, 16).str()));
return failed();
}
_data.new_nonce_buf[32] = bytes::type(2);
uchar sha1Buffer[20];
if (resDH.vnew_nonce_hash2() != NonceDigest(_data.new_nonce_buf)) {
LOG(("AuthKey Error: received new_nonce_hash2 did not match!"));
DEBUG_LOG(("AuthKey Error: received new_nonce_hash2: %1, new_nonce_buf: %2").arg(Logs::mb(&resDH.vnew_nonce_hash2(), 16).str()).arg(Logs::mb(_data.new_nonce_buf.data(), 41).str()));
return failed();
}
_data.retry_id = _data.auth_key_aux_hash;
} return dhClientParamsSend();
case mtpc_dh_gen_fail: {
const auto &resDH(res_client_DH_params.c_dh_gen_fail());
if (resDH.vnonce() != _data.nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_fail)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&resDH.vnonce(), 16).str()).arg(Logs::mb(&_data.nonce, 16).str()));
return failed();
}
if (resDH.vserver_nonce() != _data.server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_fail)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&resDH.vserver_nonce(), 16).str()).arg(Logs::mb(&_data.server_nonce, 16).str()));
return failed();
}
_data.new_nonce_buf[32] = bytes::type(3);
uchar sha1Buffer[20];
if (resDH.vnew_nonce_hash3() != NonceDigest(_data.new_nonce_buf)) {
LOG(("AuthKey Error: received new_nonce_hash3 did not match!"));
DEBUG_LOG(("AuthKey Error: received new_nonce_hash3: %1, new_nonce_buf: %2").arg(Logs::mb(&resDH.vnew_nonce_hash3(), 16).str()).arg(Logs::mb(_data.new_nonce_buf.data(), 41).str()));
return failed();
}
LOG(("AuthKey Error: dh_gen_fail received!"));
} return failed();
}
LOG(("AuthKey Error: unknown set_client_DH_params_answer received, typeId = %1").arg(res_client_DH_params.type()));
return failed();
}
template <typename Request>
void DcKeyCreator::sendNotSecureRequest(const Request &request) {
auto packet = _connection->prepareNotSecurePacket( auto packet = _connection->prepareNotSecurePacket(
request, request,
base::unixtime::mtproto_msg_id()); base::unixtime::mtproto_msg_id());
@ -547,8 +214,18 @@ void DcKeyCreator::sendNotSecureRequest(const Request &request) {
} }
} }
template <typename Response> template <typename RequestType, typename Response>
bool DcKeyCreator::readNotSecureResponse(Response &response) { std::optional<Response> DcKeyCreator::readNotSecureResponse(
gsl::span<const mtpPrime> answer) {
auto from = answer.data();
auto result = Response();
if (result.read(from, from + answer.size())) {
return result;
}
return std::nullopt;
}
void DcKeyCreator::answered() {
if (_delegate.receivedSome) { if (_delegate.receivedSome) {
_delegate.receivedSome(); _delegate.receivedSome();
} }
@ -556,7 +233,7 @@ bool DcKeyCreator::readNotSecureResponse(Response &response) {
if (_connection->received().empty()) { if (_connection->received().empty()) {
LOG(("AuthKey Error: " LOG(("AuthKey Error: "
"trying to read response from empty received list")); "trying to read response from empty received list"));
return false; return failed();
} }
const auto buffer = std::move(_connection->received().front()); const auto buffer = std::move(_connection->received().front());
@ -564,10 +241,358 @@ bool DcKeyCreator::readNotSecureResponse(Response &response) {
const auto answer = _connection->parseNotSecureResponse(buffer); const auto answer = _connection->parseNotSecureResponse(buffer);
if (answer.empty()) { if (answer.empty()) {
return false; return failed();
} }
auto from = answer.data();
return response.read(from, from + answer.size()); handleAnswer(answer);
}
DcKeyCreator::Attempt *DcKeyCreator::attemptByNonce(const MTPint128 &nonce) {
if (_temporary.data.nonce == nonce) {
DEBUG_LOG(("AuthKey Info: receiving answer for temporary..."));
return &_temporary;
} else if (_persistent.data.nonce == nonce) {
DEBUG_LOG(("AuthKey Info: receiving answer for persistent..."));
return &_persistent;
}
LOG(("AuthKey Error: attempt by nonce not found."));
return nullptr;
}
void DcKeyCreator::handleAnswer(gsl::span<const mtpPrime> answer) {
if (const auto resPQ = readNotSecureResponse<MTPReq_pq>(answer)) {
const auto nonce = resPQ->match([](const auto &data) {
return data.vnonce();
});
if (const auto attempt = attemptByNonce(nonce)) {
DEBUG_LOG(("AuthKey Info: receiving Req_pq answer..."));
return pqAnswered(attempt, *resPQ);
}
} else if (const auto resDH = readNotSecureResponse<MTPReq_DH_params>(answer)) {
const auto nonce = resDH->match([](const auto &data) {
return data.vnonce();
});
if (const auto attempt = attemptByNonce(nonce)) {
DEBUG_LOG(("AuthKey Info: receiving Req_DH_params answer..."));
return dhParamsAnswered(attempt, *resDH);
}
} else if (const auto result = readNotSecureResponse<MTPSet_client_DH_params>(answer)) {
const auto nonce = result->match([](const auto &data) {
return data.vnonce();
});
if (const auto attempt = attemptByNonce(nonce)) {
DEBUG_LOG(("AuthKey Info: receiving Req_client_DH_params answer..."));
return dhClientParamsAnswered(attempt, *result);
}
}
LOG(("AuthKey Error: Unknown answer received."));
failed();
}
void DcKeyCreator::pqSend(not_null<Attempt*> attempt, TimeId expiresIn) {
DEBUG_LOG(("AuthKey Info: sending Req_pq for %1..."
).arg(expiresIn ? "temporary" : "persistent"));
attempt->stage = Stage::WaitingPQ;
attempt->expiresIn = expiresIn;
attempt->data.nonce = openssl::RandomValue<MTPint128>();
sendNotSecureRequest(MTPReq_pq_multi(attempt->data.nonce));
}
void DcKeyCreator::pqAnswered(
not_null<Attempt*> attempt,
const MTPresPQ &data) {
data.match([&](const MTPDresPQ &data) {
Expects(data.vnonce() == attempt->data.nonce);
if (attempt->stage != Stage::WaitingPQ) {
LOG(("AuthKey Error: Unexpected stage %1").arg(int(attempt->stage)));
return failed();
}
const auto rsaKey = _dcOptions->getDcRSAKey(
_dcId,
data.vserver_public_key_fingerprints().v);
if (!rsaKey.valid()) {
return failed(Error::UnknownPublicKey);
}
attempt->data.server_nonce = data.vserver_nonce();
attempt->data.new_nonce = openssl::RandomValue<MTPint256>();
const auto &pq = data.vpq().v;
const auto parsed = ParsePQ(data.vpq().v);
if (parsed.p.isEmpty() || parsed.q.isEmpty()) {
LOG(("AuthKey Error: could not factor pq!"));
DEBUG_LOG(("AuthKey Error: problematic pq: %1").arg(Logs::mb(pq.constData(), pq.length()).str()));
return failed();
}
const auto dhEncString = [&] {
return (attempt->expiresIn == 0)
? EncryptPQInnerRSA(
MTP_p_q_inner_data_dc(
data.vpq(),
MTP_bytes(parsed.p),
MTP_bytes(parsed.q),
attempt->data.nonce,
attempt->data.server_nonce,
attempt->data.new_nonce,
MTP_int(_protocolDcId)),
rsaKey)
: EncryptPQInnerRSA(
MTP_p_q_inner_data_temp_dc(
data.vpq(),
MTP_bytes(parsed.p),
MTP_bytes(parsed.q),
attempt->data.nonce,
attempt->data.server_nonce,
attempt->data.new_nonce,
MTP_int(_protocolDcId),
MTP_int(attempt->expiresIn)),
rsaKey);
}();
if (dhEncString.empty()) {
return failed();
}
attempt->stage = Stage::WaitingDH;
DEBUG_LOG(("AuthKey Info: sending Req_DH_params..."));
sendNotSecureRequest(MTPReq_DH_params(
attempt->data.nonce,
attempt->data.server_nonce,
MTP_bytes(parsed.p),
MTP_bytes(parsed.q),
MTP_long(rsaKey.fingerprint()),
MTP_bytes(dhEncString)));
});
}
void DcKeyCreator::dhParamsAnswered(
not_null<Attempt*> attempt,
const MTPserver_DH_Params &data) {
if (attempt->stage != Stage::WaitingDH) {
LOG(("AuthKey Error: Unexpected stage %1").arg(int(attempt->stage)));
return failed();
}
data.match([&](const MTPDserver_DH_params_ok &data) {
Expects(data.vnonce() == attempt->data.nonce);
if (data.vserver_nonce() != attempt->data.server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_params_ok)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&data.vserver_nonce(), 16).str()).arg(Logs::mb(&attempt->data.server_nonce, 16).str()));
return failed();
}
auto &encDHStr = data.vencrypted_answer().v;
uint32 encDHLen = encDHStr.length(), encDHBufLen = encDHLen >> 2;
if ((encDHLen & 0x03) || encDHBufLen < 6) {
LOG(("AuthKey Error: bad encrypted data length %1 (in server_DH_params_ok)!").arg(encDHLen));
DEBUG_LOG(("AuthKey Error: received encrypted data %1").arg(Logs::mb(encDHStr.constData(), encDHLen).str()));
return failed();
}
const auto nlen = sizeof(attempt->data.new_nonce);
const auto slen = sizeof(attempt->data.server_nonce);
auto tmp_aes_buffer = bytes::array<1024>();
const auto tmp_aes = bytes::make_span(tmp_aes_buffer);
bytes::copy(tmp_aes, bytes::object_as_span(&attempt->data.new_nonce));
bytes::copy(tmp_aes.subspan(nlen), bytes::object_as_span(&attempt->data.server_nonce));
bytes::copy(tmp_aes.subspan(nlen + slen), bytes::object_as_span(&attempt->data.new_nonce));
bytes::copy(tmp_aes.subspan(nlen + slen + nlen), bytes::object_as_span(&attempt->data.new_nonce));
const auto sha1ns = openssl::Sha1(tmp_aes.subspan(0, nlen + slen));
const auto sha1sn = openssl::Sha1(tmp_aes.subspan(nlen, nlen + slen));
const auto sha1nn = openssl::Sha1(tmp_aes.subspan(nlen + slen, nlen + nlen));
mtpBuffer decBuffer;
decBuffer.resize(encDHBufLen);
const auto aesKey = bytes::make_span(attempt->data.aesKey);
const auto aesIV = bytes::make_span(attempt->data.aesIV);
bytes::copy(aesKey, bytes::make_span(sha1ns).subspan(0, 20));
bytes::copy(aesKey.subspan(20), bytes::make_span(sha1sn).subspan(0, 12));
bytes::copy(aesIV, bytes::make_span(sha1sn).subspan(12, 8));
bytes::copy(aesIV.subspan(8), bytes::make_span(sha1nn).subspan(0, 20));
bytes::copy(aesIV.subspan(28), bytes::object_as_span(&attempt->data.new_nonce).subspan(0, 4));
aesIgeDecryptRaw(encDHStr.constData(), &decBuffer[0], encDHLen, aesKey.data(), aesIV.data());
const mtpPrime *from(&decBuffer[5]), *to(from), *end(from + (encDHBufLen - 5));
MTPServer_DH_inner_data dh_inner;
if (!dh_inner.read(to, end)) {
LOG(("AuthKey Error: could not decrypt server_DH_inner_data!"));
return failed();
}
const auto &dh_inner_data(dh_inner.c_server_DH_inner_data());
if (dh_inner_data.vnonce() != attempt->data.nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in server_DH_inner_data)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&dh_inner_data.vnonce(), 16).str()).arg(Logs::mb(&attempt->data.nonce, 16).str()));
return failed();
}
if (dh_inner_data.vserver_nonce() != attempt->data.server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_inner_data)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&dh_inner_data.vserver_nonce(), 16).str()).arg(Logs::mb(&attempt->data.server_nonce, 16).str()));
return failed();
}
const auto sha1Buffer = openssl::Sha1(
bytes::make_span(decBuffer).subspan(
5 * sizeof(mtpPrime),
(to - from) * sizeof(mtpPrime)));
const auto sha1Dec = bytes::make_span(decBuffer).subspan(
0,
openssl::kSha1Size);
if (bytes::compare(sha1Dec, sha1Buffer)) {
LOG(("AuthKey Error: sha1 hash of encrypted part did not match!"));
DEBUG_LOG(("AuthKey Error: sha1 did not match, server_nonce: %1, new_nonce %2, encrypted data %3").arg(Logs::mb(&attempt->data.server_nonce, 16).str()).arg(Logs::mb(&attempt->data.new_nonce, 16).str()).arg(Logs::mb(encDHStr.constData(), encDHLen).str()));
return failed();
}
base::unixtime::update(dh_inner_data.vserver_time().v);
// check that dhPrime and (dhPrime - 1) / 2 are really prime
if (!IsPrimeAndGood(bytes::make_span(dh_inner_data.vdh_prime().v), dh_inner_data.vg().v)) {
LOG(("AuthKey Error: bad dh_prime primality!"));
return failed();
}
attempt->dhPrime = bytes::make_vector(
dh_inner_data.vdh_prime().v);
attempt->data.g = dh_inner_data.vg().v;
attempt->g_a = bytes::make_vector(dh_inner_data.vg_a().v);
attempt->data.retry_id = MTP_long(0);
attempt->retries = 0;
dhClientParamsSend(attempt);
}, [&](const MTPDserver_DH_params_fail &data) {
Expects(data.vnonce() == attempt->data.nonce);
if (data.vserver_nonce() != attempt->data.server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in server_DH_params_fail)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&data.vserver_nonce(), 16).str()).arg(Logs::mb(&attempt->data.server_nonce, 16).str()));
return failed();
}
if (data.vnew_nonce_hash() != NonceDigest(bytes::object_as_span(&attempt->data.new_nonce))) {
LOG(("AuthKey Error: received new_nonce_hash did not match!"));
DEBUG_LOG(("AuthKey Error: received new_nonce_hash: %1, new_nonce: %2").arg(Logs::mb(&data.vnew_nonce_hash(), 16).str()).arg(Logs::mb(&attempt->data.new_nonce, 32).str()));
return failed();
}
LOG(("AuthKey Error: server_DH_params_fail received!"));
failed();
});
}
void DcKeyCreator::dhClientParamsSend(not_null<Attempt*> attempt) {
if (++attempt->retries > 5) {
LOG(("AuthKey Error: could not create auth_key for %1 retries").arg(attempt->retries - 1));
return failed();
}
// gen rand 'b'
auto randomSeed = bytes::vector(ModExpFirst::kRandomPowerSize);
bytes::set_random(randomSeed);
auto g_b_data = CreateModExp(attempt->data.g, attempt->dhPrime, randomSeed);
if (g_b_data.modexp.empty()) {
LOG(("AuthKey Error: could not generate good g_b."));
return failed();
}
auto computedAuthKey = CreateAuthKey(attempt->g_a, g_b_data.randomPower, attempt->dhPrime);
if (computedAuthKey.empty()) {
LOG(("AuthKey Error: could not generate auth_key."));
return failed();
}
AuthKey::FillData(attempt->authKey, computedAuthKey);
auto auth_key_sha = openssl::Sha1(attempt->authKey);
memcpy(&attempt->data.auth_key_aux_hash, auth_key_sha.data(), 8);
memcpy(&attempt->data.auth_key_hash, auth_key_sha.data() + 12, 8);
const auto client_dh_inner = MTP_client_DH_inner_data(
attempt->data.nonce,
attempt->data.server_nonce,
attempt->data.retry_id,
MTP_bytes(g_b_data.modexp));
auto sdhEncString = EncryptClientDHInner(
client_dh_inner,
attempt->data.aesKey.data(),
attempt->data.aesIV.data());
attempt->stage = Stage::WaitingDone;
DEBUG_LOG(("AuthKey Info: sending Req_client_DH_params..."));
sendNotSecureRequest(MTPSet_client_DH_params(
attempt->data.nonce,
attempt->data.server_nonce,
MTP_string(std::move(sdhEncString))));
}
void DcKeyCreator::dhClientParamsAnswered(
not_null<Attempt*> attempt,
const MTPset_client_DH_params_answer &data) {
if (attempt->stage != Stage::WaitingDone) {
LOG(("AuthKey Error: Unexpected stage %1").arg(int(attempt->stage)));
return failed();
}
data.match([&](const MTPDdh_gen_ok &data) {
if (data.vnonce() != attempt->data.nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_ok)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&data.vnonce(), 16).str()).arg(Logs::mb(&attempt->data.nonce, 16).str()));
return failed();
}
if (data.vserver_nonce() != attempt->data.server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_ok)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&data.vserver_nonce(), 16).str()).arg(Logs::mb(&attempt->data.server_nonce, 16).str()));
return failed();
}
attempt->data.new_nonce_buf[32] = bytes::type(1);
if (data.vnew_nonce_hash1() != NonceDigest(attempt->data.new_nonce_buf)) {
LOG(("AuthKey Error: received new_nonce_hash1 did not match!"));
DEBUG_LOG(("AuthKey Error: received new_nonce_hash1: %1, new_nonce_buf: %2").arg(Logs::mb(&data.vnew_nonce_hash1(), 16).str()).arg(Logs::mb(attempt->data.new_nonce_buf.data(), 41).str()));
return failed();
}
uint64 salt1 = attempt->data.new_nonce.l.l, salt2 = attempt->data.server_nonce.l;
attempt->data.doneSalt = salt1 ^ salt2;
attempt->stage = Stage::Ready;
done();
}, [&](const MTPDdh_gen_retry &data) {
if (data.vnonce() != attempt->data.nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_retry)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&data.vnonce(), 16).str()).arg(Logs::mb(&attempt->data.nonce, 16).str()));
return failed();
}
if (data.vserver_nonce() != attempt->data.server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_retry)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&data.vserver_nonce(), 16).str()).arg(Logs::mb(&attempt->data.server_nonce, 16).str()));
return failed();
}
attempt->data.new_nonce_buf[32] = bytes::type(2);
uchar sha1Buffer[20];
if (data.vnew_nonce_hash2() != NonceDigest(attempt->data.new_nonce_buf)) {
LOG(("AuthKey Error: received new_nonce_hash2 did not match!"));
DEBUG_LOG(("AuthKey Error: received new_nonce_hash2: %1, new_nonce_buf: %2").arg(Logs::mb(&data.vnew_nonce_hash2(), 16).str()).arg(Logs::mb(attempt->data.new_nonce_buf.data(), 41).str()));
return failed();
}
attempt->data.retry_id = attempt->data.auth_key_aux_hash;
dhClientParamsSend(attempt);
}, [&](const MTPDdh_gen_fail &data) {
if (data.vnonce() != attempt->data.nonce) {
LOG(("AuthKey Error: received nonce <> sent nonce (in dh_gen_fail)!"));
DEBUG_LOG(("AuthKey Error: received nonce: %1, sent nonce: %2").arg(Logs::mb(&data.vnonce(), 16).str()).arg(Logs::mb(&attempt->data.nonce, 16).str()));
return failed();
}
if (data.vserver_nonce() != attempt->data.server_nonce) {
LOG(("AuthKey Error: received server_nonce <> sent server_nonce (in dh_gen_fail)!"));
DEBUG_LOG(("AuthKey Error: received server_nonce: %1, sent server_nonce: %2").arg(Logs::mb(&data.vserver_nonce(), 16).str()).arg(Logs::mb(&attempt->data.server_nonce, 16).str()));
return failed();
}
attempt->data.new_nonce_buf[32] = bytes::type(3);
uchar sha1Buffer[20];
if (data.vnew_nonce_hash3() != NonceDigest(attempt->data.new_nonce_buf)) {
LOG(("AuthKey Error: received new_nonce_hash3 did not match!"));
DEBUG_LOG(("AuthKey Error: received new_nonce_hash3: %1, new_nonce_buf: %2").arg(Logs::mb(&data.vnew_nonce_hash3(), 16).str()).arg(Logs::mb(attempt->data.new_nonce_buf.data(), 41).str()));
return failed();
}
LOG(("AuthKey Error: dh_gen_fail received!"));
failed();
});
} }
void DcKeyCreator::failed(Error error) { void DcKeyCreator::failed(Error error) {
@ -576,13 +601,28 @@ void DcKeyCreator::failed(Error error) {
onstack(tl::unexpected(error)); onstack(tl::unexpected(error));
} }
void DcKeyCreator::done(uint64 serverSalt) { void DcKeyCreator::done() {
Expects(_temporary.stage != Stage::None);
if (_persistent.stage != Stage::None && _persistent.stage != Stage::Ready) {
return;
} else if (_temporary.stage != Stage::Ready) {
return;
}
auto result = Result(); auto result = Result();
result.key = std::make_shared<AuthKey>( result.temporaryKey = std::make_shared<AuthKey>(
AuthKey::Type::Generated, AuthKey::Type::Temporary,
_dcId, _dcId,
_authKey); _temporary.authKey);
result.serverSalt = serverSalt; result.temporaryServerSalt = _temporary.data.doneSalt;
if (_persistent.stage == Stage::Ready) {
result.persistentKey = std::make_shared<AuthKey>(
AuthKey::Type::Generated,
_dcId,
_persistent.authKey);
result.persistentServerSalt = _persistent.data.doneSalt;
}
stopReceiving(); stopReceiving();
auto onstack = base::take(_delegate.done); auto onstack = base::take(_delegate.done);

View File

@ -23,13 +23,19 @@ using namespace ::MTP::internal;
class DcKeyCreator final { class DcKeyCreator final {
public: public:
struct Request {
TimeId temporaryExpiresIn = 0;
bool persistentNeeded = false;
};
enum class Error { enum class Error {
UnknownPublicKey, UnknownPublicKey,
Other, Other,
}; };
struct Result { struct Result {
AuthKeyPtr key; AuthKeyPtr persistentKey;
uint64 serverSalt = 0; AuthKeyPtr temporaryKey;
uint64 temporaryServerSalt = 0;
uint64 persistentServerSalt = 0;
}; };
struct Delegate { struct Delegate {
FnMut<void(base::expected<Result, Error>)> done; FnMut<void(base::expected<Result, Error>)> done;
@ -43,11 +49,17 @@ public:
not_null<AbstractConnection*> connection, not_null<AbstractConnection*> connection,
not_null<DcOptions*> dcOptions, not_null<DcOptions*> dcOptions,
Delegate delegate, Delegate delegate,
TimeId expireIn = 0); // 0 - persistent, > 0 - temporary Request request);
~DcKeyCreator(); ~DcKeyCreator();
private: private:
// Auth key creation fields and methods enum class Stage {
None,
WaitingPQ,
WaitingDH,
WaitingDone,
Ready,
};
struct Data { struct Data {
Data() Data()
: new_nonce(*(MTPint256*)((uchar*)new_nonce_buf.data())) : new_nonce(*(MTPint256*)((uchar*)new_nonce_buf.data()))
@ -61,7 +73,6 @@ private:
MTPint256 &new_nonce; MTPint256 &new_nonce;
MTPlong &auth_key_aux_hash; MTPlong &auth_key_aux_hash;
uint32 retries = 0;
MTPlong retry_id; MTPlong retry_id;
int32 g = 0; int32 g = 0;
@ -69,35 +80,59 @@ private:
bytes::array<32> aesKey; bytes::array<32> aesKey;
bytes::array<32> aesIV; bytes::array<32> aesIV;
MTPlong auth_key_hash; MTPlong auth_key_hash;
uint64 doneSalt = 0;
};
struct Attempt {
~Attempt();
Data data;
bytes::vector dhPrime;
bytes::vector g_a;
AuthKey::Data authKey = { { gsl::byte{} } };
TimeId expiresIn = 0;
uint32 retries = 0;
Stage stage = Stage::None;
}; };
template <typename Request> template <typename RequestType>
void sendNotSecureRequest(const Request &request); void sendNotSecureRequest(const RequestType &request);
template <typename Response> template <
[[nodiscard]] bool readNotSecureResponse(Response &response); typename RequestType,
typename Response = typename RequestType::ResponseType>
[[nodiscard]] std::optional<Response> readNotSecureResponse(
gsl::span<const mtpPrime> answer);
void pqSend(); Attempt *attemptByNonce(const MTPint128 &nonce);
void pqAnswered();
void dhParamsAnswered(); void answered();
void dhClientParamsSend(); void handleAnswer(gsl::span<const mtpPrime> answer);
void dhClientParamsAnswered(); void pqSend(not_null<Attempt*> attempt, TimeId expiresIn);
void pqAnswered(
not_null<Attempt*> attempt,
const MTPresPQ &data);
void dhParamsAnswered(
not_null<Attempt*> attempt,
const MTPserver_DH_Params &data);
void dhClientParamsSend(not_null<Attempt*> attempt);
void dhClientParamsAnswered(
not_null<Attempt*> attempt,
const MTPset_client_DH_params_answer &data);
void stopReceiving(); void stopReceiving();
void failed(Error error = Error::Other); void failed(Error error = Error::Other);
void done(uint64 serverSalt); void done();
const not_null<AbstractConnection*> _connection; const not_null<AbstractConnection*> _connection;
const not_null<DcOptions*> _dcOptions; const not_null<DcOptions*> _dcOptions;
const DcId _dcId = 0; const DcId _dcId = 0;
const int16 _protocolDcId = 0; const int16 _protocolDcId = 0;
const TimeId _expireIn = 0; const Request _request;
Delegate _delegate; Delegate _delegate;
Data _data; Attempt _temporary;
bytes::vector _dhPrime; Attempt _persistent;
bytes::vector _g_a;
AuthKey::Data _authKey = { { gsl::byte{} } };
FnMut<void(base::expected<Result, Error>)> _done; FnMut<void(base::expected<Result, Error>)> _done;
}; };

View File

@ -65,8 +65,9 @@ public:
void setMainDcId(DcId mainDcId); void setMainDcId(DcId mainDcId);
[[nodiscard]] DcId mainDcId() const; [[nodiscard]] DcId mainDcId() const;
void dcKeyChanged(DcId dcId, const AuthKeyPtr &key); void dcPersistentKeyChanged(DcId dcId, const AuthKeyPtr &persistentKey);
[[nodiscard]] rpl::producer<DcId> dcKeyChanged() const; void dcTemporaryKeyChanged(DcId dcId);
[[nodiscard]] rpl::producer<DcId> dcTemporaryKeyChanged() const;
[[nodiscard]] AuthKeysList getKeysForWrite() const; [[nodiscard]] AuthKeysList getKeysForWrite() const;
void addKeysForDestroy(AuthKeysList &&keys); void addKeysForDestroy(AuthKeysList &&keys);
@ -209,7 +210,7 @@ private:
bool _mainDcIdForced = false; bool _mainDcIdForced = false;
base::flat_map<DcId, std::unique_ptr<Dcenter>> _dcenters; base::flat_map<DcId, std::unique_ptr<Dcenter>> _dcenters;
std::vector<std::unique_ptr<Dcenter>> _dcentersToDestroy; std::vector<std::unique_ptr<Dcenter>> _dcentersToDestroy;
rpl::event_stream<DcId> _dcKeyChanged; rpl::event_stream<DcId> _dcTemporaryKeyChanged;
Session *_mainSession = nullptr; Session *_mainSession = nullptr;
base::flat_map<ShiftedDcId, std::unique_ptr<Session>> _sessions; base::flat_map<ShiftedDcId, std::unique_ptr<Session>> _sessions;
@ -225,10 +226,8 @@ private:
crl::time _lastConfigLoadedTime = 0; crl::time _lastConfigLoadedTime = 0;
crl::time _configExpiresAt = 0; crl::time _configExpiresAt = 0;
std::map<DcId, AuthKeyPtr> _keysForWrite; base::flat_map<DcId, AuthKeyPtr> _keysForWrite;
mutable QReadWriteLock _keysForWriteLock; base::flat_map<ShiftedDcId, mtpRequestId> _logoutGuestRequestIds;
std::map<ShiftedDcId, mtpRequestId> _logoutGuestRequestIds;
// holds dcWithShift for request to this dc or -dc for request to main dc // holds dcWithShift for request to this dc or -dc for request to main dc
std::map<mtpRequestId, ShiftedDcId> _requestsByDc; std::map<mtpRequestId, ShiftedDcId> _requestsByDc;
@ -619,23 +618,22 @@ void Instance::Private::logout(
void Instance::Private::logoutGuestDcs() { void Instance::Private::logoutGuestDcs() {
auto dcIds = std::vector<DcId>(); auto dcIds = std::vector<DcId>();
{ dcIds.reserve(_keysForWrite.size());
QReadLocker lock(&_keysForWriteLock); for (const auto &key : _keysForWrite) {
dcIds.reserve(_keysForWrite.size()); dcIds.push_back(key.first);
for (auto &key : _keysForWrite) {
dcIds.push_back(key.first);
}
} }
for (auto dcId : dcIds) { for (const auto dcId : dcIds) {
if (dcId != mainDcId() && dcOptions()->dcType(dcId) != DcType::Cdn) { if (dcId == mainDcId() || dcOptions()->dcType(dcId) == DcType::Cdn) {
auto shiftedDcId = MTP::logoutDcId(dcId); continue;
auto requestId = _instance->send(MTPauth_LogOut(), rpcDone([this](mtpRequestId requestId) {
logoutGuestDone(requestId);
}), rpcFail([this](mtpRequestId requestId) {
return logoutGuestDone(requestId);
}), shiftedDcId);
_logoutGuestRequestIds.emplace(shiftedDcId, requestId);
} }
const auto shiftedDcId = MTP::logoutDcId(dcId);
const auto requestId = _instance->send(MTPauth_LogOut(), rpcDone([=](
mtpRequestId requestId) {
logoutGuestDone(requestId);
}), rpcFail([=](mtpRequestId requestId) {
return logoutGuestDone(requestId);
}), shiftedDcId);
_logoutGuestRequestIds.emplace(shiftedDcId, requestId);
} }
} }
@ -695,35 +693,45 @@ not_null<Dcenter*> Instance::Private::getDcById(
return addDc(dcId); return addDc(dcId);
} }
void Instance::Private::dcKeyChanged(DcId dcId, const AuthKeyPtr &key) { void Instance::Private::dcPersistentKeyChanged(
_dcKeyChanged.fire_copy(dcId); DcId dcId,
const AuthKeyPtr &persistentKey) {
dcTemporaryKeyChanged(dcId);
if (isTemporaryDcId(dcId)) { if (isTemporaryDcId(dcId)) {
return; return;
} }
QWriteLocker lock(&_keysForWriteLock); const auto i = _keysForWrite.find(dcId);
if (key) { if (i != _keysForWrite.end() && i->second == persistentKey) {
_keysForWrite[dcId] = key; return;
} else { } else if (i == _keysForWrite.end() && !persistentKey) {
_keysForWrite.erase(dcId); return;
} }
crl::on_main(_instance, [=] { if (!persistentKey) {
DEBUG_LOG(("AuthKey Info: writing auth keys, called by dc %1").arg(dcId)); _keysForWrite.erase(i);
Local::writeMtpData(); } else if (i != _keysForWrite.end()) {
}); i->second = persistentKey;
} else {
_keysForWrite.emplace(dcId, persistentKey);
}
DEBUG_LOG(("AuthKey Info: writing auth keys, called by dc %1").arg(dcId));
Local::writeMtpData();
} }
rpl::producer<DcId> Instance::Private::dcKeyChanged() const { void Instance::Private::dcTemporaryKeyChanged(DcId dcId) {
return _dcKeyChanged.events(); _dcTemporaryKeyChanged.fire_copy(dcId);
}
rpl::producer<DcId> Instance::Private::dcTemporaryKeyChanged() const {
return _dcTemporaryKeyChanged.events();
} }
AuthKeysList Instance::Private::getKeysForWrite() const { AuthKeysList Instance::Private::getKeysForWrite() const {
auto result = AuthKeysList(); auto result = AuthKeysList();
QReadLocker lock(&_keysForWriteLock);
result.reserve(_keysForWrite.size()); result.reserve(_keysForWrite.size());
for (auto &key : _keysForWrite) { for (const auto &key : _keysForWrite) {
result.push_back(key.second); result.push_back(key.second);
} }
return result; return result;
@ -736,15 +744,12 @@ void Instance::Private::addKeysForDestroy(AuthKeysList &&keys) {
const auto dcId = key->dcId(); const auto dcId = key->dcId();
auto shiftedDcId = MTP::destroyKeyNextDcId(dcId); auto shiftedDcId = MTP::destroyKeyNextDcId(dcId);
{ // There could be several keys for one dc if we're destroying them.
QWriteLocker lock(&_keysForWriteLock); // Place them all in separate shiftedDcId so that they won't conflict.
// There could be several keys for one dc if we're destroying them. while (_keysForWrite.find(shiftedDcId) != _keysForWrite.cend()) {
// Place them all in separate shiftedDcId so that they won't conflict. shiftedDcId = MTP::destroyKeyNextDcId(shiftedDcId);
while (_keysForWrite.find(shiftedDcId) != _keysForWrite.cend()) {
shiftedDcId = MTP::destroyKeyNextDcId(shiftedDcId);
}
_keysForWrite[shiftedDcId] = key;
} }
_keysForWrite[shiftedDcId] = key;
addDc(shiftedDcId, std::move(key)); addDc(shiftedDcId, std::move(key));
startSession(shiftedDcId); startSession(shiftedDcId);
@ -1352,7 +1357,8 @@ bool Instance::Private::onErrorDefault(mtpRequestId requestId, const RPCError &e
checkDelayedRequests(); checkDelayedRequests();
return true; return true;
} else if (code == 401 || (badGuestDc && _badGuestDcRequests.find(requestId) == _badGuestDcRequests.cend())) { } else if ((code == 401 && err != "AUTH_KEY_PERM_EMPTY")
|| (badGuestDc && _badGuestDcRequests.find(requestId) == _badGuestDcRequests.cend())) {
auto dcWithShift = ShiftedDcId(0); auto dcWithShift = ShiftedDcId(0);
if (const auto shiftedDcId = queryRequestByDc(requestId)) { if (const auto shiftedDcId = queryRequestByDc(requestId)) {
dcWithShift = *shiftedDcId; dcWithShift = *shiftedDcId;
@ -1572,10 +1578,7 @@ void Instance::Private::completedKeyDestroy(ShiftedDcId shiftedDcId) {
Expects(isKeysDestroyer()); Expects(isKeysDestroyer());
removeDc(shiftedDcId); removeDc(shiftedDcId);
{ _keysForWrite.erase(shiftedDcId);
QWriteLocker lock(&_keysForWriteLock);
_keysForWrite.erase(shiftedDcId);
}
killSession(shiftedDcId); killSession(shiftedDcId);
if (_dcenters.empty()) { if (_dcenters.empty()) {
emit _instance->allKeysDestroyed(); emit _instance->allKeysDestroyed();
@ -1588,19 +1591,15 @@ void Instance::Private::checkMainDcKey() {
if (findSession(shiftedDcId)) { if (findSession(shiftedDcId)) {
return; return;
} }
const auto key = [&] { const auto i = _keysForWrite.find(id);
QReadLocker lock(&_keysForWriteLock); if (i == end(_keysForWrite)) {
const auto i = _keysForWrite.find(id);
return (i != end(_keysForWrite)) ? i->second : AuthKeyPtr();
}();
if (!key) {
return; return;
} }
const auto lastCheckTime = key->lastCheckTime(); const auto lastCheckTime = i->second->lastCheckTime();
if (lastCheckTime > 0 && lastCheckTime + kCheckKeyEach >= crl::now()) { if (lastCheckTime > 0 && lastCheckTime + kCheckKeyEach >= crl::now()) {
return; return;
} }
_instance->sendDcKeyCheck(shiftedDcId, key); _instance->sendDcKeyCheck(shiftedDcId, i->second);
} }
void Instance::Private::keyDestroyedOnServer(DcId dcId, uint64 keyId) { void Instance::Private::keyDestroyedOnServer(DcId dcId, uint64 keyId) {
@ -1608,7 +1607,7 @@ void Instance::Private::keyDestroyedOnServer(DcId dcId, uint64 keyId) {
if (const auto dc = findDc(dcId)) { if (const auto dc = findDc(dcId)) {
if (dc->destroyConfirmedForgottenKey(keyId)) { if (dc->destroyConfirmedForgottenKey(keyId)) {
LOG(("Key destroyed!")); LOG(("Key destroyed!"));
dcKeyChanged(dcId, nullptr); dcPersistentKeyChanged(dcId, nullptr);
} else { } else {
LOG(("Key already is different.")); LOG(("Key already is different."));
} }
@ -1761,12 +1760,18 @@ void Instance::logout(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail) {
_private->logout(onDone, onFail); _private->logout(onDone, onFail);
} }
void Instance::dcKeyChanged(DcId dcId, const AuthKeyPtr &key) { void Instance::dcPersistentKeyChanged(
_private->dcKeyChanged(dcId, key); DcId dcId,
const AuthKeyPtr &persistentKey) {
_private->dcPersistentKeyChanged(dcId, persistentKey);
} }
rpl::producer<DcId> Instance::dcKeyChanged() const { void Instance::dcTemporaryKeyChanged(DcId dcId) {
return _private->dcKeyChanged(); _private->dcTemporaryKeyChanged(dcId);
}
rpl::producer<DcId> Instance::dcTemporaryKeyChanged() const {
return _private->dcTemporaryKeyChanged();
} }
AuthKeysList Instance::getKeysForWrite() const { AuthKeysList Instance::getKeysForWrite() const {
@ -1881,13 +1886,11 @@ void Instance::sendRequest(
} }
void Instance::sendAnything(ShiftedDcId shiftedDcId, crl::time msCanWait) { void Instance::sendAnything(ShiftedDcId shiftedDcId, crl::time msCanWait) {
const auto session = _private->getSession(shiftedDcId); _private->getSession(shiftedDcId)->sendAnything(msCanWait);
session->sendAnything(msCanWait);
} }
void Instance::sendDcKeyCheck(ShiftedDcId shiftedDcId, const AuthKeyPtr &key) { void Instance::sendDcKeyCheck(ShiftedDcId shiftedDcId, const AuthKeyPtr &key) {
const auto session = _private->getSession(shiftedDcId); _private->getSession(shiftedDcId)->sendDcKeyCheck(key);
session->sendDcKeyCheck(key);
} }
Instance::~Instance() { Instance::~Instance() {

View File

@ -65,8 +65,9 @@ public:
[[nodiscard]] QString systemVersion() const; [[nodiscard]] QString systemVersion() const;
// Main thread. // Main thread.
void dcKeyChanged(DcId dcId, const AuthKeyPtr &key); void dcPersistentKeyChanged(DcId dcId, const AuthKeyPtr &persistentKey);
[[nodiscard]] rpl::producer<DcId> dcKeyChanged() const; void dcTemporaryKeyChanged(DcId dcId);
[[nodiscard]] rpl::producer<DcId> dcTemporaryKeyChanged() const;
[[nodiscard]] AuthKeysList getKeysForWrite() const; [[nodiscard]] AuthKeysList getKeysForWrite() const;
void addKeysForDestroy(AuthKeysList &&keys); void addKeysForDestroy(AuthKeysList &&keys);

View File

@ -123,6 +123,16 @@ void AuthKey::setLastCheckTime(crl::time time) {
_lastCheckTime = time; _lastCheckTime = time;
} }
TimeId AuthKey::expiresAt() const {
return _expiresAt;
}
void AuthKey::setExpiresAt(TimeId expiresAt) {
Expects(_type == Type::Temporary);
_expiresAt = expiresAt;
}
void AuthKey::FillData(Data &authKey, bytes::const_span computedAuthKey) { void AuthKey::FillData(Data &authKey, bytes::const_span computedAuthKey) {
auto computedAuthKeySize = computedAuthKey.size(); auto computedAuthKeySize = computedAuthKey.size();
Assert(computedAuthKeySize <= kSize); Assert(computedAuthKeySize <= kSize);

View File

@ -21,6 +21,7 @@ public:
enum class Type { enum class Type {
Generated, Generated,
Temporary,
ReadFromFile, ReadFromFile,
Local, Local,
}; };
@ -45,6 +46,8 @@ public:
[[nodiscard]] crl::time lastCheckTime() const; [[nodiscard]] crl::time lastCheckTime() const;
void setLastCheckTime(crl::time time); void setLastCheckTime(crl::time time);
[[nodiscard]] TimeId expiresAt() const;
void setExpiresAt(TimeId expiresAt);
static void FillData(Data &authKey, bytes::const_span computedAuthKey); static void FillData(Data &authKey, bytes::const_span computedAuthKey);
@ -56,6 +59,7 @@ private:
Data _key = { { gsl::byte{} } }; Data _key = { { gsl::byte{} } };
KeyId _keyId = 0; KeyId _keyId = 0;
crl::time _lastCheckTime = 0; crl::time _lastCheckTime = 0;
TimeId _expiresAt = 0;
}; };

View File

@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "mtproto/session.h" #include "mtproto/session.h"
#include "mtproto/details/mtproto_dc_key_checker.h"
#include "mtproto/connection.h" #include "mtproto/connection.h"
#include "mtproto/dcenter.h" #include "mtproto/dcenter.h"
#include "mtproto/mtproto_auth_key.h" #include "mtproto/mtproto_auth_key.h"
@ -222,9 +221,14 @@ bool SessionData::connectionInited() const {
return _owner ? _owner->connectionInited() : false; return _owner ? _owner->connectionInited() : false;
} }
AuthKeyPtr SessionData::getKey() const { AuthKeyPtr SessionData::getTemporaryKey() const {
QMutexLocker lock(&_ownerMutex); QMutexLocker lock(&_ownerMutex);
return _owner ? _owner->getKey() : nullptr; return _owner ? _owner->getTemporaryKey() : nullptr;
}
AuthKeyPtr SessionData::getPersistentKey() const {
QMutexLocker lock(&_ownerMutex);
return _owner ? _owner->getPersistentKey() : nullptr;
} }
bool SessionData::acquireKeyCreation() { bool SessionData::acquireKeyCreation() {
@ -232,10 +236,12 @@ bool SessionData::acquireKeyCreation() {
return _owner ? _owner->acquireKeyCreation() : false; return _owner ? _owner->acquireKeyCreation() : false;
} }
void SessionData::releaseKeyCreationOnDone(const AuthKeyPtr &key) { void SessionData::releaseKeyCreationOnDone(
const AuthKeyPtr &temporaryKey,
const AuthKeyPtr &persistentKey) {
QMutexLocker lock(&_ownerMutex); QMutexLocker lock(&_ownerMutex);
if (_owner) { if (_owner) {
_owner->releaseKeyCreationOnDone(key); _owner->releaseKeyCreationOnDone(temporaryKey, persistentKey);
} }
} }
@ -246,10 +252,10 @@ void SessionData::releaseKeyCreationOnFail() {
} }
} }
void SessionData::destroyCdnKey(uint64 keyId) { void SessionData::destroyTemporaryKey(uint64 keyId) {
QMutexLocker lock(&_ownerMutex); QMutexLocker lock(&_ownerMutex);
if (_owner) { if (_owner) {
_owner->destroyCdnKey(keyId); _owner->destroyTemporaryKey(keyId);
} }
} }
@ -278,7 +284,7 @@ Session::Session(
} }
void Session::watchDcKeyChanges() { void Session::watchDcKeyChanges() {
_instance->dcKeyChanged( _instance->dcTemporaryKeyChanged(
) | rpl::filter([=](DcId dcId) { ) | rpl::filter([=](DcId dcId) {
return (dcId == _shiftedDcId) || (dcId == BareDcId(_shiftedDcId)); return (dcId == _shiftedDcId) || (dcId == BareDcId(_shiftedDcId));
}) | rpl::start_with_next([=] { }) | rpl::start_with_next([=] {
@ -684,6 +690,29 @@ bool Session::acquireKeyCreation() {
return true; return true;
} }
void Session::releaseKeyCreationOnDone(
const AuthKeyPtr &temporaryKey,
const AuthKeyPtr &persistentKey) {
Expects(_myKeyCreation);
DEBUG_LOG(("AuthKey Info: Session key bound, setting, dcWithShift %1"
).arg(_shiftedDcId));
_dc->releaseKeyCreationOnDone(temporaryKey, persistentKey);
_myKeyCreation = false;
if (sharedDc()) {
const auto dcId = _dc->id();
const auto instance = _instance;
InvokeQueued(instance, [=] {
if (persistentKey) {
instance->dcPersistentKeyChanged(dcId, persistentKey);
} else {
instance->dcTemporaryKeyChanged(dcId);
}
});
}
}
void Session::releaseKeyCreationOnFail() { void Session::releaseKeyCreationOnFail() {
Expects(_myKeyCreation); Expects(_myKeyCreation);
@ -691,36 +720,20 @@ void Session::releaseKeyCreationOnFail() {
_myKeyCreation = false; _myKeyCreation = false;
} }
void Session::releaseKeyCreationOnDone(const AuthKeyPtr &key) {
Expects(_myKeyCreation);
DEBUG_LOG(("AuthKey Info: Session key created, setting, dcWithShift %1").arg(_shiftedDcId));
_dc->releaseKeyCreationOnDone(key);
_myKeyCreation = false;
if (sharedDc()) {
const auto dcId = _dc->id();
const auto instance = _instance;
InvokeQueued(instance, [=] {
instance->dcKeyChanged(dcId, key);
});
}
}
void Session::notifyDcConnectionInited() { void Session::notifyDcConnectionInited() {
DEBUG_LOG(("MTP Info: emitting MTProtoDC::connectionWasInited(), dcWithShift %1").arg(_shiftedDcId)); DEBUG_LOG(("MTP Info: emitting MTProtoDC::connectionWasInited(), dcWithShift %1").arg(_shiftedDcId));
_dc->setConnectionInited(); _dc->setConnectionInited();
} }
void Session::destroyCdnKey(uint64 keyId) { void Session::destroyTemporaryKey(uint64 keyId) {
if (!_dc->destroyCdnKey(keyId)) { if (!_dc->destroyTemporaryKey(keyId)) {
return; return;
} }
if (sharedDc()) { if (sharedDc()) {
const auto dcId = _dc->id(); const auto dcId = _dc->id();
const auto instance = _instance; const auto instance = _instance;
InvokeQueued(instance, [=] { InvokeQueued(instance, [=] {
instance->dcKeyChanged(dcId, nullptr); instance->dcTemporaryKeyChanged(dcId);
}); });
} }
} }
@ -729,8 +742,12 @@ int32 Session::getDcWithShift() const {
return _shiftedDcId; return _shiftedDcId;
} }
AuthKeyPtr Session::getKey() const { AuthKeyPtr Session::getTemporaryKey() const {
return _dc->getKey(); return _dc->getTemporaryKey();
}
AuthKeyPtr Session::getPersistentKey() const {
return _dc->getPersistentKey();
} }
bool Session::connectionInited() const { bool Session::connectionInited() const {

View File

@ -283,11 +283,14 @@ public:
bool sendMsgStateInfo); bool sendMsgStateInfo);
[[nodiscard]] bool connectionInited() const; [[nodiscard]] bool connectionInited() const;
[[nodiscard]] AuthKeyPtr getKey() const; [[nodiscard]] AuthKeyPtr getPersistentKey() const;
[[nodiscard]] AuthKeyPtr getTemporaryKey() const;
[[nodiscard]] bool acquireKeyCreation(); [[nodiscard]] bool acquireKeyCreation();
void releaseKeyCreationOnDone(const AuthKeyPtr &key); void releaseKeyCreationOnDone(
const AuthKeyPtr &temporaryKey,
const AuthKeyPtr &persistentKey);
void releaseKeyCreationOnFail(); void releaseKeyCreationOnFail();
void destroyCdnKey(uint64 keyId); void destroyTemporaryKey(uint64 keyId);
void detach(); void detach();
@ -351,14 +354,17 @@ public:
// Thread-safe. // Thread-safe.
[[nodiscard]] ShiftedDcId getDcWithShift() const; [[nodiscard]] ShiftedDcId getDcWithShift() const;
[[nodiscard]] AuthKeyPtr getKey() const; [[nodiscard]] AuthKeyPtr getPersistentKey() const;
[[nodiscard]] AuthKeyPtr getTemporaryKey() const;
[[nodiscard]] bool connectionInited() const; [[nodiscard]] bool connectionInited() const;
// Connection thread. // Connection thread.
[[nodiscard]] bool acquireKeyCreation(); [[nodiscard]] bool acquireKeyCreation();
void releaseKeyCreationOnDone(
const AuthKeyPtr &temporaryKey,
const AuthKeyPtr &persistentKey);
void releaseKeyCreationOnFail(); void releaseKeyCreationOnFail();
void releaseKeyCreationOnDone(const AuthKeyPtr &key); void destroyTemporaryKey(uint64 keyId);
void destroyCdnKey(uint64 keyId);
void notifyDcConnectionInited(); void notifyDcConnectionInited();

View File

@ -34,8 +34,8 @@
'<(src_loc)', '<(src_loc)',
], ],
'sources': [ 'sources': [
'<(src_loc)/mtproto/details/mtproto_dc_key_checker.cpp', '<(src_loc)/mtproto/details/mtproto_dc_key_binder.cpp',
'<(src_loc)/mtproto/details/mtproto_dc_key_checker.h', '<(src_loc)/mtproto/details/mtproto_dc_key_binder.h',
'<(src_loc)/mtproto/details/mtproto_dc_key_creator.cpp', '<(src_loc)/mtproto/details/mtproto_dc_key_creator.cpp',
'<(src_loc)/mtproto/details/mtproto_dc_key_creator.h', '<(src_loc)/mtproto/details/mtproto_dc_key_creator.h',
'<(src_loc)/mtproto/details/mtproto_dump_to_text.cpp', '<(src_loc)/mtproto/details/mtproto_dump_to_text.cpp',