mirror of https://github.com/procxx/kepka.git
First version of working through temp keys.
This commit is contained in:
parent
d9fc3619c2
commit
173ae746a2
|
@ -7,8 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#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_checker.h"
|
||||
#include "mtproto/details/mtproto_dump_to_text.h"
|
||||
#include "mtproto/session.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/unixtime.h"
|
||||
|
||||
#ifdef small
|
||||
#undef small
|
||||
#endif // small
|
||||
|
||||
namespace MTP {
|
||||
namespace internal {
|
||||
namespace {
|
||||
|
@ -42,6 +38,7 @@ constexpr auto kPingDelayDisconnect = 60;
|
|||
constexpr auto kPingSendAfter = 30 * crl::time(1000);
|
||||
constexpr auto kPingSendAfterForce = 45 * crl::time(1000);
|
||||
constexpr auto kCheckKeyExpiresIn = TimeId(3600);
|
||||
constexpr auto kTemporaryExpiresIn = TimeId(10);
|
||||
constexpr auto kTestModeDcIdShift = 10000;
|
||||
|
||||
// If we can't connect for this time we will ask _instance to update config.
|
||||
|
@ -248,6 +245,7 @@ ConnectionPrivate::ConnectionPrivate(
|
|||
|
||||
ConnectionPrivate::~ConnectionPrivate() {
|
||||
clearKeyCreatorOnFail();
|
||||
cancelKeyBinder();
|
||||
|
||||
Expects(_finished);
|
||||
Expects(!_connection);
|
||||
|
@ -548,12 +546,20 @@ void ConnectionPrivate::tryToSend() {
|
|||
return;
|
||||
}
|
||||
|
||||
auto needsLayer = !_sessionData->connectionInited();
|
||||
auto state = getState();
|
||||
auto sendOnlyFirstPing = (state != ConnectedState);
|
||||
const auto needsLayer = !_sessionData->connectionInited();
|
||||
const auto state = getState();
|
||||
const auto sendOnlyFirstPing = (state != ConnectedState);
|
||||
const auto sendAll = !sendOnlyFirstPing && !_keyBinder;
|
||||
const auto isMainSession = (GetDcIdShift(_shiftedDcId) == 0);
|
||||
if (sendOnlyFirstPing && !_pingIdToSend) {
|
||||
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
|
||||
} else if (isMainSession
|
||||
&& !sendOnlyFirstPing
|
||||
&& !_pingIdToSend
|
||||
&& !_pingId
|
||||
&& _pingSendAt <= crl::now()) {
|
||||
_pingIdToSend = openssl::RandomValue<mtpPingId>();
|
||||
}
|
||||
|
||||
auto pingRequest = SecureRequest();
|
||||
|
@ -561,34 +567,31 @@ void ConnectionPrivate::tryToSend() {
|
|||
auto resendRequest = SecureRequest();
|
||||
auto stateRequest = SecureRequest();
|
||||
auto httpWaitRequest = SecureRequest();
|
||||
auto checkDcKeyRequest = SecureRequest();
|
||||
if (_shiftedDcId == BareDcId(_shiftedDcId)) { // main session
|
||||
if (!sendOnlyFirstPing && !_pingIdToSend && !_pingId && _pingSendAt <= crl::now()) {
|
||||
_pingIdToSend = rand_value<mtpPingId>();
|
||||
}
|
||||
}
|
||||
auto bindDcKeyRequest = SecureRequest();
|
||||
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(
|
||||
MTP_long(_pingIdToSend)
|
||||
));
|
||||
DEBUG_LOG(("MTP Info: sending ping, ping_id: %1"
|
||||
).arg(_pingIdToSend));
|
||||
} else {
|
||||
DEBUG_LOG(("MTP Info: sending ping_delay_disconnect, "
|
||||
"ping_id: %1").arg(_pingIdToSend));
|
||||
pingRequest = SecureRequest::Serialize(MTPPing_delay_disconnect(
|
||||
MTP_long(_pingIdToSend),
|
||||
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);
|
||||
}
|
||||
_pingSendAt = pingRequest->msDate + kPingSendAfter;
|
||||
_pingId = base::take(_pingIdToSend);
|
||||
} else if (!sendAll) {
|
||||
DEBUG_LOG(("MTP Info: dc %1 sending only service or bind."
|
||||
).arg(_shiftedDcId));
|
||||
} 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) {
|
||||
|
@ -625,23 +628,34 @@ void ConnectionPrivate::tryToSend() {
|
|||
httpWaitRequest = SecureRequest::Serialize(MTPHttpWait(
|
||||
MTP_http_wait(MTP_int(100), MTP_int(30), MTP_int(25000))));
|
||||
}
|
||||
if (!_keyChecker) {
|
||||
if (const auto &keyForCheck = _sessionData->getKeyForCheck()) {
|
||||
_keyChecker = std::make_unique<details::DcKeyChecker>(
|
||||
_instance,
|
||||
_shiftedDcId,
|
||||
keyForCheck);
|
||||
checkDcKeyRequest = _keyChecker->prepareRequest(
|
||||
_key,
|
||||
_sessionData->getSessionId());
|
||||
if (_keyBinder && !_keyBinder->requested()) {
|
||||
bindDcKeyRequest = _keyBinder->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.
|
||||
checkDcKeyRequest.setSeqNo(
|
||||
_sessionData->nextRequestSeqNumber(
|
||||
checkDcKeyRequest.needAck()));
|
||||
}
|
||||
// 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()));
|
||||
//} 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());
|
||||
|
||||
auto toSendDummy = PreRequestMap();
|
||||
auto &toSend = sendOnlyFirstPing
|
||||
? toSendDummy
|
||||
: _sessionData->toSendMap();
|
||||
if (sendOnlyFirstPing) {
|
||||
auto &toSend = sendAll
|
||||
? _sessionData->toSendMap()
|
||||
: toSendDummy;
|
||||
if (!sendAll) {
|
||||
locker1.unlock();
|
||||
} else {
|
||||
int time = crl::now();
|
||||
int now = crl::now();
|
||||
}
|
||||
|
||||
uint32 toSendCount = toSend.size();
|
||||
|
@ -709,7 +726,7 @@ void ConnectionPrivate::tryToSend() {
|
|||
if (resendRequest) ++toSendCount;
|
||||
if (stateRequest) ++toSendCount;
|
||||
if (httpWaitRequest) ++toSendCount;
|
||||
if (checkDcKeyRequest) ++toSendCount;
|
||||
if (bindDcKeyRequest) ++toSendCount;
|
||||
|
||||
if (!toSendCount) {
|
||||
return; // nothing to send
|
||||
|
@ -725,12 +742,12 @@ void ConnectionPrivate::tryToSend() {
|
|||
? stateRequest
|
||||
: httpWaitRequest
|
||||
? httpWaitRequest
|
||||
: checkDcKeyRequest
|
||||
? checkDcKeyRequest
|
||||
: bindDcKeyRequest
|
||||
? bindDcKeyRequest
|
||||
: toSend.cbegin().value();
|
||||
if (toSendCount == 1 && first->msDate > 0) { // if can send without container
|
||||
toSendRequest = first;
|
||||
if (!sendOnlyFirstPing) {
|
||||
if (sendAll) {
|
||||
toSend.clear();
|
||||
locker1.unlock();
|
||||
}
|
||||
|
@ -753,9 +770,7 @@ void ConnectionPrivate::tryToSend() {
|
|||
auto &haveSent = _sessionData->haveSentMap();
|
||||
haveSent.insert(msgId, toSendRequest);
|
||||
|
||||
if (needsLayer && !toSendRequest->needsLayer) {
|
||||
needsLayer = false;
|
||||
}
|
||||
const auto wrapLayer = needsLayer && toSendRequest->needsLayer;
|
||||
if (toSendRequest->after) {
|
||||
const auto toSendSize = tl::count_length(toSendRequest) >> 2;
|
||||
auto wrappedRequest = SecureRequest::Prepare(
|
||||
|
@ -766,7 +781,7 @@ void ConnectionPrivate::tryToSend() {
|
|||
wrapInvokeAfter(wrappedRequest, toSendRequest, haveSent);
|
||||
toSendRequest = std::move(wrappedRequest);
|
||||
}
|
||||
if (needsLayer) {
|
||||
if (wrapLayer) {
|
||||
const auto noWrapSize = (tl::count_length(toSendRequest) >> 2);
|
||||
const auto toSendSize = noWrapSize + initSizeInInts;
|
||||
auto wrappedRequest = SecureRequest::Prepare(toSendSize);
|
||||
|
@ -793,7 +808,7 @@ void ConnectionPrivate::tryToSend() {
|
|||
if (resendRequest) containerSize += resendRequest.messageSize();
|
||||
if (stateRequest) containerSize += stateRequest.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) {
|
||||
containerSize += i.value().messageSize();
|
||||
if (needsLayer && i.value()->needsLayer) {
|
||||
|
@ -836,7 +851,7 @@ void ConnectionPrivate::tryToSend() {
|
|||
if (pingRequest) {
|
||||
_pingMsgId = placeToContainer(toSendRequest, bigMsgId, haveSentArr, pingRequest);
|
||||
needAnyResponse = true;
|
||||
} else if (resendRequest || stateRequest || checkDcKeyRequest) {
|
||||
} else if (resendRequest || stateRequest || bindDcKeyRequest) {
|
||||
needAnyResponse = true;
|
||||
}
|
||||
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 (ackRequest) placeToContainer(toSendRequest, bigMsgId, haveSentArr, ackRequest);
|
||||
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*)(haveSentIdsWrap->data() + 4) = contMsgId;
|
||||
|
@ -930,7 +945,7 @@ void ConnectionPrivate::connectToServer(bool afterConfig) {
|
|||
_sessionData->connectionOptions());
|
||||
|
||||
// #TODO race.
|
||||
const auto hasKey = (_sessionData->getKey() != nullptr);
|
||||
const auto hasKey = (_sessionData->getTemporaryKey() != nullptr);
|
||||
|
||||
const auto bareDc = BareDcId(_shiftedDcId);
|
||||
_dcType = _instance->dcOptions()->dcType(_shiftedDcId);
|
||||
|
@ -1236,9 +1251,9 @@ void ConnectionPrivate::handleReceived() {
|
|||
auto msgKey = *(MTPint128*)(ints + 2);
|
||||
|
||||
#ifdef TDESKTOP_MTPROTO_OLD
|
||||
aesIgeDecrypt_oldmtp(encryptedInts, decryptedBuffer.data(), encryptedBytesCount, _key, msgKey);
|
||||
aesIgeDecrypt_oldmtp(encryptedInts, decryptedBuffer.data(), encryptedBytesCount, _temporaryKey, msgKey);
|
||||
#else // TDESKTOP_MTPROTO_OLD
|
||||
aesIgeDecrypt(encryptedInts, decryptedBuffer.data(), encryptedBytesCount, _key, msgKey);
|
||||
aesIgeDecrypt(encryptedInts, decryptedBuffer.data(), encryptedBytesCount, _temporaryKey, msgKey);
|
||||
#endif // TDESKTOP_MTPROTO_OLD
|
||||
|
||||
auto decryptedInts = reinterpret_cast<const mtpPrime*>(decryptedBuffer.constData());
|
||||
|
@ -1285,7 +1300,7 @@ void ConnectionPrivate::handleReceived() {
|
|||
|
||||
SHA256_CTX msgKeyLargeContext;
|
||||
SHA256_Init(&msgKeyLargeContext);
|
||||
SHA256_Update(&msgKeyLargeContext, _key->partForMsgKey(false), 32);
|
||||
SHA256_Update(&msgKeyLargeContext, _temporaryKey->partForMsgKey(false), 32);
|
||||
SHA256_Update(&msgKeyLargeContext, decryptedInts, encryptedBytesCount);
|
||||
SHA256_Final(sha256Buffer.data(), &msgKeyLargeContext);
|
||||
|
||||
|
@ -1386,7 +1401,9 @@ void ConnectionPrivate::handleReceived() {
|
|||
|
||||
if (res != HandleResult::Success && res != HandleResult::Ignored) {
|
||||
_needSessionReset = (res == HandleResult::ResetSession);
|
||||
|
||||
if (res == HandleResult::DestroyTemporaryKey) {
|
||||
destroyTemporaryKey();
|
||||
}
|
||||
return restart();
|
||||
}
|
||||
_retryTimeout = 1; // reset restart() timer
|
||||
|
@ -1884,15 +1901,37 @@ ConnectionPrivate::HandleResult ConnectionPrivate::handleOneReceived(const mtpPr
|
|||
response.resize(end - from);
|
||||
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
|
||||
// the initConnection, so we're not sure yet that it was inited.
|
||||
// Wait till a good response is received.
|
||||
_sessionData->notifyConnectionInited(*_connectionOptions);
|
||||
}
|
||||
|
||||
if (_keyChecker && _keyChecker->handleResponse(reqMsgId, response)) {
|
||||
return HandleResult::Success;
|
||||
if (_keyBinder) {
|
||||
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);
|
||||
if (requestId && requestId != mtpRequestId(0xFFFFFFFF)) {
|
||||
|
@ -2341,17 +2380,17 @@ void ConnectionPrivate::checkAuthKey() {
|
|||
}
|
||||
|
||||
void ConnectionPrivate::updateAuthKey() {
|
||||
if (_keyCreator) {
|
||||
if (_keyCreator || _keyBinder) {
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_LOG(("AuthKey Info: Connection updating key from Session, dc %1").arg(_shiftedDcId));
|
||||
applyAuthKey(_sessionData->getKey());
|
||||
applyAuthKey(_sessionData->getTemporaryKey());
|
||||
}
|
||||
|
||||
void ConnectionPrivate::applyAuthKey(AuthKeyPtr &&key) {
|
||||
_key = std::move(key);
|
||||
const auto newKeyId = _key ? _key->keyId() : 0;
|
||||
void ConnectionPrivate::applyAuthKey(AuthKeyPtr &&temporaryKey) {
|
||||
_temporaryKey = std::move(temporaryKey);
|
||||
const auto newKeyId = _temporaryKey ? _temporaryKey->keyId() : 0;
|
||||
if (newKeyId) {
|
||||
if (_keyId == newKeyId) {
|
||||
return;
|
||||
|
@ -2389,19 +2428,39 @@ void ConnectionPrivate::applyAuthKey(AuthKeyPtr &&key) {
|
|||
}
|
||||
|
||||
void ConnectionPrivate::createDcKey() {
|
||||
Expects(_keyCreator == nullptr);
|
||||
Expects(_keyBinder == nullptr);
|
||||
|
||||
using Result = DcKeyCreator::Result;
|
||||
using Error = DcKeyCreator::Error;
|
||||
auto delegate = DcKeyCreator::Delegate();
|
||||
delegate.done = [=](base::expected<Result, Error> 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);
|
||||
|
||||
auto key = result->persistentKey
|
||||
? std::move(result->persistentKey)
|
||||
: _sessionData->getPersistentKey();
|
||||
if (!key) {
|
||||
restart();
|
||||
}
|
||||
_keyCreator = nullptr;
|
||||
_sessionData->releaseKeyCreationOnDone(result->key);
|
||||
applyAuthKey(std::move(result->key));
|
||||
_keyBinder = std::make_unique<DcKeyBinder>(std::move(key));
|
||||
result->temporaryKey->setExpiresAt(
|
||||
base::unixtime::now() + kTemporaryExpiresIn);
|
||||
applyAuthKey(std::move(result->temporaryKey));
|
||||
return;
|
||||
}
|
||||
clearKeyCreatorOnFail();
|
||||
|
@ -2417,16 +2476,23 @@ void ConnectionPrivate::createDcKey() {
|
|||
restart();
|
||||
}
|
||||
};
|
||||
const auto expireIn = (GetDcIdShift(_shiftedDcId) == kCheckKeyDcShift)
|
||||
? kCheckKeyExpiresIn
|
||||
: TimeId(0);
|
||||
delegate.sentSome = [=](uint64 size) {
|
||||
onSentSome(size);
|
||||
};
|
||||
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>(
|
||||
BareDcId(_shiftedDcId),
|
||||
getProtocolDcId(),
|
||||
_connection.get(),
|
||||
_instance->dcOptions(),
|
||||
std::move(delegate),
|
||||
expireIn);
|
||||
request);
|
||||
}
|
||||
|
||||
void ConnectionPrivate::authKeyChecked() {
|
||||
|
@ -2471,28 +2537,26 @@ void ConnectionPrivate::handleError(int errorCode) {
|
|||
_waitForConnectedTimer.cancel();
|
||||
|
||||
if (errorCode == -404) {
|
||||
if (_dcType == DcType::Cdn && !_instance->isKeysDestroyer()) {
|
||||
LOG(("MTP Info: -404 error received in CDN dc %1, assuming it was destroyed, recreating.").arg(_shiftedDcId));
|
||||
destroyCdnKey();
|
||||
return restart();
|
||||
} else {
|
||||
LOG(("MTP Info: -404 error received, informing instance."));
|
||||
if (_instance->isKeysDestroyer()) {
|
||||
LOG(("MTP Info: -404 error received in destroyer %1, assuming key was destroyed.").arg(_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));
|
||||
return restart();
|
||||
}
|
||||
|
||||
void ConnectionPrivate::destroyCdnKey() {
|
||||
if (_key) {
|
||||
_sessionData->destroyCdnKey(_keyId);
|
||||
void ConnectionPrivate::destroyTemporaryKey() {
|
||||
cancelKeyBinder();
|
||||
if (_temporaryKey) {
|
||||
_sessionData->destroyTemporaryKey(_temporaryKey->keyId());
|
||||
}
|
||||
_key = nullptr;
|
||||
_keyId = 0;
|
||||
_needSessionReset = true;
|
||||
applyAuthKey(nullptr);
|
||||
}
|
||||
|
||||
bool ConnectionPrivate::sendSecureRequest(
|
||||
|
@ -2542,7 +2606,7 @@ bool ConnectionPrivate::sendSecureRequest(
|
|||
request->constData(),
|
||||
&packet[prefix],
|
||||
fullSize * sizeof(mtpPrime),
|
||||
_key,
|
||||
_temporaryKey,
|
||||
msgKey);
|
||||
#else // TDESKTOP_MTPROTO_OLD
|
||||
uchar encryptedSHA256[32];
|
||||
|
@ -2550,7 +2614,7 @@ bool ConnectionPrivate::sendSecureRequest(
|
|||
|
||||
SHA256_CTX 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_Final(encryptedSHA256, &msgKeyLargeContext);
|
||||
|
||||
|
@ -2562,7 +2626,7 @@ bool ConnectionPrivate::sendSecureRequest(
|
|||
request->constData(),
|
||||
&packet[prefix],
|
||||
fullSize * sizeof(mtpPrime),
|
||||
_key,
|
||||
_temporaryKey,
|
||||
msgKey);
|
||||
#endif // TDESKTOP_MTPROTO_OLD
|
||||
|
||||
|
@ -2613,6 +2677,14 @@ void ConnectionPrivate::clearKeyCreatorOnFail() {
|
|||
_sessionData->releaseKeyCreationOnFail();
|
||||
}
|
||||
|
||||
void ConnectionPrivate::cancelKeyBinder() {
|
||||
if (!_keyBinder) {
|
||||
return;
|
||||
}
|
||||
_keyBinder = nullptr;
|
||||
_sessionData->releaseKeyCreationOnFail();
|
||||
}
|
||||
|
||||
void ConnectionPrivate::stop() {
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace MTP {
|
||||
namespace details {
|
||||
class DcKeyCreator;
|
||||
class DcKeyChecker;
|
||||
class DcKeyBinder;
|
||||
} // namespace details
|
||||
|
||||
// How much time to wait for some more requests, when sending msg acks.
|
||||
|
@ -103,6 +103,7 @@ private:
|
|||
Ignored,
|
||||
RestartConnection,
|
||||
ResetSession,
|
||||
DestroyTemporaryKey,
|
||||
ParseError,
|
||||
};
|
||||
|
||||
|
@ -180,9 +181,10 @@ private:
|
|||
void resetSession();
|
||||
void checkAuthKey();
|
||||
void authKeyChecked();
|
||||
void destroyCdnKey();
|
||||
void destroyTemporaryKey();
|
||||
void clearKeyCreatorOnFail();
|
||||
void applyAuthKey(AuthKeyPtr &&key);
|
||||
void cancelKeyBinder();
|
||||
void applyAuthKey(AuthKeyPtr &&temporaryKey);
|
||||
|
||||
const not_null<Instance*> _instance;
|
||||
DcType _dcType = DcType::Regular;
|
||||
|
@ -224,13 +226,13 @@ private:
|
|||
bool _restarted = false;
|
||||
bool _finished = false;
|
||||
|
||||
AuthKeyPtr _key;
|
||||
AuthKeyPtr _temporaryKey;
|
||||
uint64 _keyId = 0;
|
||||
std::shared_ptr<SessionData> _sessionData;
|
||||
std::unique_ptr<ConnectionOptions> _connectionOptions;
|
||||
|
||||
std::unique_ptr<details::DcKeyCreator> _keyCreator;
|
||||
std::unique_ptr<details::DcKeyChecker> _keyChecker;
|
||||
std::unique_ptr<details::DcKeyBinder> _keyBinder;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -25,34 +25,40 @@ constexpr auto kSpecialRequestTimeoutMs = 6000; // 4 seconds timeout for it to w
|
|||
|
||||
Dcenter::Dcenter(DcId dcId, AuthKeyPtr &&key)
|
||||
: _id(dcId)
|
||||
, _key(std::move(key)) {
|
||||
, _persistentKey(std::move(key)) {
|
||||
}
|
||||
|
||||
DcId Dcenter::id() const {
|
||||
return _id;
|
||||
}
|
||||
|
||||
AuthKeyPtr Dcenter::getKey() const {
|
||||
AuthKeyPtr Dcenter::getTemporaryKey() const {
|
||||
QReadLocker lock(&_mutex);
|
||||
return _key;
|
||||
return _temporaryKey;
|
||||
}
|
||||
|
||||
bool Dcenter::destroyCdnKey(uint64 keyId) {
|
||||
return destroyKey(keyId);
|
||||
AuthKeyPtr Dcenter::getPersistentKey() const {
|
||||
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) {
|
||||
return destroyKey(keyId);
|
||||
}
|
||||
|
||||
bool Dcenter::destroyKey(uint64 keyId) {
|
||||
Expects(!_creatingKey || !_key);
|
||||
|
||||
QWriteLocker lock(&_mutex);
|
||||
if (_key->keyId() != keyId) {
|
||||
if (!_persistentKey || _persistentKey->keyId() != keyId) {
|
||||
return false;
|
||||
}
|
||||
_key = nullptr;
|
||||
_temporaryKey = nullptr;
|
||||
_persistentKey = nullptr;
|
||||
_connectionInited = false;
|
||||
return true;
|
||||
}
|
||||
|
@ -69,7 +75,7 @@ void Dcenter::setConnectionInited(bool connectionInited) {
|
|||
|
||||
bool Dcenter::acquireKeyCreation() {
|
||||
QReadLocker lock(&_mutex);
|
||||
if (_key != nullptr) {
|
||||
if (_temporaryKey != nullptr) {
|
||||
return false;
|
||||
}
|
||||
auto expected = false;
|
||||
|
@ -78,21 +84,27 @@ bool Dcenter::acquireKeyCreation() {
|
|||
|
||||
void Dcenter::releaseKeyCreationOnFail() {
|
||||
Expects(_creatingKey);
|
||||
Expects(_key == nullptr);
|
||||
Expects(_temporaryKey == nullptr);
|
||||
|
||||
_creatingKey = false;
|
||||
}
|
||||
|
||||
void Dcenter::releaseKeyCreationOnDone(const AuthKeyPtr &key) {
|
||||
void Dcenter::releaseKeyCreationOnDone(
|
||||
const AuthKeyPtr &temporaryKey,
|
||||
const AuthKeyPtr &persistentKey) {
|
||||
Expects(_creatingKey);
|
||||
Expects(_key == nullptr);
|
||||
Expects(_temporaryKey == nullptr);
|
||||
|
||||
QWriteLocker lock(&_mutex);
|
||||
DEBUG_LOG(("AuthKey Info: Dcenter::releaseKeyCreationOnDone(%1), "
|
||||
"emitting authKeyChanged, dc %2"
|
||||
).arg(key ? key->keyId() : 0
|
||||
DEBUG_LOG(("AuthKey Info: Dcenter::releaseKeyCreationOnDone(%1, %2), "
|
||||
"emitting authKeyChanged, dc %3"
|
||||
).arg(temporaryKey ? temporaryKey->keyId() : 0
|
||||
).arg(persistentKey ? persistentKey->keyId() : 0
|
||||
).arg(_id));
|
||||
_key = key;
|
||||
_temporaryKey = temporaryKey;
|
||||
if (persistentKey) {
|
||||
_persistentKey = persistentKey;
|
||||
}
|
||||
_connectionInited = false;
|
||||
_creatingKey = false;
|
||||
}
|
||||
|
|
|
@ -23,10 +23,13 @@ public:
|
|||
// Thread-safe.
|
||||
[[nodiscard]] DcId id() const;
|
||||
|
||||
[[nodiscard]] AuthKeyPtr getKey() const;
|
||||
bool destroyCdnKey(uint64 keyId);
|
||||
[[nodiscard]] AuthKeyPtr getTemporaryKey() const;
|
||||
[[nodiscard]] AuthKeyPtr getPersistentKey() const;
|
||||
bool destroyTemporaryKey(uint64 keyId);
|
||||
bool destroyConfirmedForgottenKey(uint64 keyId);
|
||||
void releaseKeyCreationOnDone(const AuthKeyPtr &key);
|
||||
void releaseKeyCreationOnDone(
|
||||
const AuthKeyPtr &temporaryKey,
|
||||
const AuthKeyPtr &persistentKey);
|
||||
|
||||
[[nodiscard]] bool connectionInited() const;
|
||||
void setConnectionInited(bool connectionInited = true);
|
||||
|
@ -35,12 +38,11 @@ public:
|
|||
void releaseKeyCreationOnFail();
|
||||
|
||||
private:
|
||||
bool destroyKey(uint64 keyId);
|
||||
|
||||
const DcId _id = 0;
|
||||
mutable QReadWriteLock _mutex;
|
||||
|
||||
AuthKeyPtr _key;
|
||||
AuthKeyPtr _temporaryKey;
|
||||
AuthKeyPtr _persistentKey;
|
||||
bool _connectionInited = false;
|
||||
std::atomic<bool> _creatingKey = false;
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ the official desktop application for the Telegram messaging service.
|
|||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "mtproto/details/mtproto_dc_key_checker.h"
|
||||
#include "mtproto/details/mtproto_dc_key_binder.h"
|
||||
|
||||
#include "mtproto/mtp_instance.h"
|
||||
#include "base/unixtime.h"
|
||||
|
@ -17,8 +17,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace MTP::details {
|
||||
namespace {
|
||||
|
||||
constexpr auto kBindKeyExpireTimeout = TimeId(3600);
|
||||
|
||||
[[nodiscard]] QByteArray EncryptBindAuthKeyInner(
|
||||
const AuthKeyPtr &persistentKey,
|
||||
mtpMsgId realMsgId,
|
||||
|
@ -74,26 +72,28 @@ constexpr auto kBindKeyExpireTimeout = TimeId(3600);
|
|||
|
||||
} // namespace
|
||||
|
||||
DcKeyChecker::DcKeyChecker(
|
||||
not_null<Instance*> instance,
|
||||
ShiftedDcId shiftedDcId,
|
||||
const AuthKeyPtr &persistentKey)
|
||||
: _instance(instance)
|
||||
, _shiftedDcId(shiftedDcId)
|
||||
, _persistentKey(persistentKey) {
|
||||
DcKeyBinder::DcKeyBinder(AuthKeyPtr &&persistentKey)
|
||||
: _persistentKey(std::move(persistentKey)) {
|
||||
Expects(_persistentKey != nullptr);
|
||||
}
|
||||
|
||||
SecureRequest DcKeyChecker::prepareRequest(
|
||||
bool DcKeyBinder::requested() const {
|
||||
return _requestMsgId != 0;
|
||||
}
|
||||
|
||||
SecureRequest DcKeyBinder::prepareRequest(
|
||||
const AuthKeyPtr &temporaryKey,
|
||||
uint64 sessionId) {
|
||||
Expects(_requestMsgId == 0);
|
||||
Expects(temporaryKey != nullptr);
|
||||
Expects(temporaryKey->expiresAt() != 0);
|
||||
|
||||
const auto nonce = openssl::RandomValue<uint64>();
|
||||
_requestMsgId = base::unixtime::mtproto_msg_id();
|
||||
auto result = SecureRequest::Serialize(MTPauth_BindTempAuthKey(
|
||||
MTP_long(_persistentKey->keyId()),
|
||||
MTP_long(nonce),
|
||||
MTP_int(kBindKeyExpireTimeout),
|
||||
MTP_int(temporaryKey->expiresAt()),
|
||||
MTP_bytes(EncryptBindAuthKeyInner(
|
||||
_persistentKey,
|
||||
_requestMsgId,
|
||||
|
@ -102,49 +102,56 @@ SecureRequest DcKeyChecker::prepareRequest(
|
|||
MTP_long(temporaryKey->keyId()),
|
||||
MTP_long(_persistentKey->keyId()),
|
||||
MTP_long(sessionId),
|
||||
MTP_int(kBindKeyExpireTimeout))))));
|
||||
MTP_int(temporaryKey->expiresAt()))))));
|
||||
result.setMsgId(_requestMsgId);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool DcKeyChecker::handleResponse(
|
||||
DcKeyBindState DcKeyBinder::handleResponse(
|
||||
MTPlong requestMsgId,
|
||||
const mtpBuffer &response) {
|
||||
Expects(!response.isEmpty());
|
||||
|
||||
if (!_requestMsgId || requestMsgId.v != _requestMsgId) {
|
||||
return false;
|
||||
return DcKeyBindState::Unknown;
|
||||
}
|
||||
_requestMsgId = 0;
|
||||
|
||||
const auto destroyed = [&] {
|
||||
if (response[0] != mtpc_rpc_error) {
|
||||
return false;
|
||||
}
|
||||
auto error = MTPRpcError();
|
||||
auto from = response.begin();
|
||||
const auto end = from + response.size();
|
||||
if (!error.read(from, end)) {
|
||||
return false;
|
||||
}
|
||||
return error.match([&](const MTPDrpc_error &data) {
|
||||
auto from = response.begin();
|
||||
const auto end = from + response.size();
|
||||
auto error = MTPRpcError();
|
||||
auto result = MTPBool();
|
||||
if (response[0] == mtpc_boolTrue) {
|
||||
return DcKeyBindState::Success;
|
||||
} else if (response[0] == mtpc_rpc_error && error.read(from, end)) {
|
||||
const auto destroyed = error.match([&](const MTPDrpc_error &data) {
|
||||
return (data.verror_code().v == 400)
|
||||
&& (data.verror_message().v == "ENCRYPTED_MESSAGE_INVALID");
|
||||
});
|
||||
}();
|
||||
return destroyed
|
||||
? DcKeyBindState::DefinitelyDestroyed
|
||||
: DcKeyBindState::Failed;
|
||||
} else {
|
||||
return DcKeyBindState::Failed;
|
||||
}
|
||||
}
|
||||
|
||||
const auto instance = _instance;
|
||||
const auto shiftedDcId = _shiftedDcId;
|
||||
const auto keyId = _persistentKey->keyId();
|
||||
_persistentKey->setLastCheckTime(crl::now());
|
||||
crl::on_main(instance, [=] {
|
||||
instance->killSession(shiftedDcId);
|
||||
if (destroyed) {
|
||||
instance->keyDestroyedOnServer(BareDcId(shiftedDcId), keyId);
|
||||
}
|
||||
AuthKeyPtr DcKeyBinder::persistentKey() const {
|
||||
return _persistentKey;
|
||||
}
|
||||
|
||||
bool DcKeyBinder::IsDestroyedTemporaryKeyError(
|
||||
const mtpBuffer &buffer) {
|
||||
auto from = buffer.data();
|
||||
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
|
|
@ -16,27 +16,31 @@ class Instance;
|
|||
|
||||
namespace MTP::details {
|
||||
|
||||
enum class DcKeyState {
|
||||
MaybeExisting,
|
||||
enum class DcKeyBindState {
|
||||
Unknown,
|
||||
Success,
|
||||
Failed,
|
||||
DefinitelyDestroyed,
|
||||
};
|
||||
|
||||
class DcKeyChecker final {
|
||||
class DcKeyBinder final {
|
||||
public:
|
||||
DcKeyChecker(
|
||||
not_null<Instance*> instance,
|
||||
ShiftedDcId shiftedDcId,
|
||||
const AuthKeyPtr &persistentKey);
|
||||
DcKeyBinder(AuthKeyPtr &&persistentKey);
|
||||
|
||||
[[nodiscard]] bool requested() const;
|
||||
[[nodiscard]] SecureRequest prepareRequest(
|
||||
const AuthKeyPtr &temporaryKey,
|
||||
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:
|
||||
const not_null<Instance*> _instance;
|
||||
const ShiftedDcId _shiftedDcId = 0;
|
||||
const AuthKeyPtr _persistentKey;
|
||||
AuthKeyPtr _persistentKey;
|
||||
mtpMsgId _requestMsgId = 0;
|
||||
|
||||
};
|
|
@ -153,383 +153,50 @@ MTPint128 NonceDigest(bytes::const_span data) {
|
|||
|
||||
} // 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(
|
||||
DcId dcId,
|
||||
int16 protocolDcId,
|
||||
not_null<AbstractConnection*> connection,
|
||||
not_null<DcOptions*> dcOptions,
|
||||
Delegate delegate,
|
||||
TimeId expireIn)
|
||||
Request request)
|
||||
: _connection(connection)
|
||||
, _dcOptions(dcOptions)
|
||||
, _dcId(dcId)
|
||||
, _protocolDcId(protocolDcId)
|
||||
, _expireIn(expireIn)
|
||||
, _request(request)
|
||||
, _delegate(std::move(delegate)) {
|
||||
Expects(_expireIn >= 0);
|
||||
Expects(_request.temporaryExpiresIn > 0);
|
||||
Expects(_delegate.done != nullptr);
|
||||
|
||||
_data.nonce = openssl::RandomValue<MTPint128>();
|
||||
pqSend();
|
||||
QObject::connect(_connection, &AbstractConnection::receivedData, [=] {
|
||||
answered();
|
||||
});
|
||||
|
||||
pqSend(&_temporary, _request.temporaryExpiresIn);
|
||||
if (_request.persistentNeeded) {
|
||||
pqSend(&_persistent, 0);
|
||||
}
|
||||
}
|
||||
|
||||
DcKeyCreator::~DcKeyCreator() {
|
||||
if (_delegate.done) {
|
||||
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() {
|
||||
QObject::connect(_connection, &AbstractConnection::receivedData, [=] {
|
||||
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) {
|
||||
template <typename RequestType>
|
||||
void DcKeyCreator::sendNotSecureRequest(const RequestType &request) {
|
||||
auto packet = _connection->prepareNotSecurePacket(
|
||||
request,
|
||||
base::unixtime::mtproto_msg_id());
|
||||
|
@ -547,8 +214,18 @@ void DcKeyCreator::sendNotSecureRequest(const Request &request) {
|
|||
}
|
||||
}
|
||||
|
||||
template <typename Response>
|
||||
bool DcKeyCreator::readNotSecureResponse(Response &response) {
|
||||
template <typename RequestType, typename 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) {
|
||||
_delegate.receivedSome();
|
||||
}
|
||||
|
@ -556,7 +233,7 @@ bool DcKeyCreator::readNotSecureResponse(Response &response) {
|
|||
if (_connection->received().empty()) {
|
||||
LOG(("AuthKey Error: "
|
||||
"trying to read response from empty received list"));
|
||||
return false;
|
||||
return failed();
|
||||
}
|
||||
|
||||
const auto buffer = std::move(_connection->received().front());
|
||||
|
@ -564,10 +241,358 @@ bool DcKeyCreator::readNotSecureResponse(Response &response) {
|
|||
|
||||
const auto answer = _connection->parseNotSecureResponse(buffer);
|
||||
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) {
|
||||
|
@ -576,13 +601,28 @@ void DcKeyCreator::failed(Error 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();
|
||||
result.key = std::make_shared<AuthKey>(
|
||||
AuthKey::Type::Generated,
|
||||
result.temporaryKey = std::make_shared<AuthKey>(
|
||||
AuthKey::Type::Temporary,
|
||||
_dcId,
|
||||
_authKey);
|
||||
result.serverSalt = serverSalt;
|
||||
_temporary.authKey);
|
||||
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();
|
||||
auto onstack = base::take(_delegate.done);
|
||||
|
|
|
@ -23,13 +23,19 @@ using namespace ::MTP::internal;
|
|||
|
||||
class DcKeyCreator final {
|
||||
public:
|
||||
struct Request {
|
||||
TimeId temporaryExpiresIn = 0;
|
||||
bool persistentNeeded = false;
|
||||
};
|
||||
enum class Error {
|
||||
UnknownPublicKey,
|
||||
Other,
|
||||
};
|
||||
struct Result {
|
||||
AuthKeyPtr key;
|
||||
uint64 serverSalt = 0;
|
||||
AuthKeyPtr persistentKey;
|
||||
AuthKeyPtr temporaryKey;
|
||||
uint64 temporaryServerSalt = 0;
|
||||
uint64 persistentServerSalt = 0;
|
||||
};
|
||||
struct Delegate {
|
||||
FnMut<void(base::expected<Result, Error>)> done;
|
||||
|
@ -43,11 +49,17 @@ public:
|
|||
not_null<AbstractConnection*> connection,
|
||||
not_null<DcOptions*> dcOptions,
|
||||
Delegate delegate,
|
||||
TimeId expireIn = 0); // 0 - persistent, > 0 - temporary
|
||||
Request request);
|
||||
~DcKeyCreator();
|
||||
|
||||
private:
|
||||
// Auth key creation fields and methods
|
||||
enum class Stage {
|
||||
None,
|
||||
WaitingPQ,
|
||||
WaitingDH,
|
||||
WaitingDone,
|
||||
Ready,
|
||||
};
|
||||
struct Data {
|
||||
Data()
|
||||
: new_nonce(*(MTPint256*)((uchar*)new_nonce_buf.data()))
|
||||
|
@ -61,7 +73,6 @@ private:
|
|||
MTPint256 &new_nonce;
|
||||
MTPlong &auth_key_aux_hash;
|
||||
|
||||
uint32 retries = 0;
|
||||
MTPlong retry_id;
|
||||
|
||||
int32 g = 0;
|
||||
|
@ -69,35 +80,59 @@ private:
|
|||
bytes::array<32> aesKey;
|
||||
bytes::array<32> aesIV;
|
||||
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>
|
||||
void sendNotSecureRequest(const Request &request);
|
||||
template <typename RequestType>
|
||||
void sendNotSecureRequest(const RequestType &request);
|
||||
|
||||
template <typename Response>
|
||||
[[nodiscard]] bool readNotSecureResponse(Response &response);
|
||||
template <
|
||||
typename RequestType,
|
||||
typename Response = typename RequestType::ResponseType>
|
||||
[[nodiscard]] std::optional<Response> readNotSecureResponse(
|
||||
gsl::span<const mtpPrime> answer);
|
||||
|
||||
void pqSend();
|
||||
void pqAnswered();
|
||||
void dhParamsAnswered();
|
||||
void dhClientParamsSend();
|
||||
void dhClientParamsAnswered();
|
||||
Attempt *attemptByNonce(const MTPint128 &nonce);
|
||||
|
||||
void answered();
|
||||
void handleAnswer(gsl::span<const mtpPrime> answer);
|
||||
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 failed(Error error = Error::Other);
|
||||
void done(uint64 serverSalt);
|
||||
void done();
|
||||
|
||||
const not_null<AbstractConnection*> _connection;
|
||||
const not_null<DcOptions*> _dcOptions;
|
||||
const DcId _dcId = 0;
|
||||
const int16 _protocolDcId = 0;
|
||||
const TimeId _expireIn = 0;
|
||||
const Request _request;
|
||||
Delegate _delegate;
|
||||
|
||||
Data _data;
|
||||
bytes::vector _dhPrime;
|
||||
bytes::vector _g_a;
|
||||
AuthKey::Data _authKey = { { gsl::byte{} } };
|
||||
Attempt _temporary;
|
||||
Attempt _persistent;
|
||||
|
||||
FnMut<void(base::expected<Result, Error>)> _done;
|
||||
|
||||
};
|
||||
|
|
|
@ -65,8 +65,9 @@ public:
|
|||
void setMainDcId(DcId mainDcId);
|
||||
[[nodiscard]] DcId mainDcId() const;
|
||||
|
||||
void dcKeyChanged(DcId dcId, const AuthKeyPtr &key);
|
||||
[[nodiscard]] rpl::producer<DcId> dcKeyChanged() const;
|
||||
void dcPersistentKeyChanged(DcId dcId, const AuthKeyPtr &persistentKey);
|
||||
void dcTemporaryKeyChanged(DcId dcId);
|
||||
[[nodiscard]] rpl::producer<DcId> dcTemporaryKeyChanged() const;
|
||||
[[nodiscard]] AuthKeysList getKeysForWrite() const;
|
||||
void addKeysForDestroy(AuthKeysList &&keys);
|
||||
|
||||
|
@ -209,7 +210,7 @@ private:
|
|||
bool _mainDcIdForced = false;
|
||||
base::flat_map<DcId, std::unique_ptr<Dcenter>> _dcenters;
|
||||
std::vector<std::unique_ptr<Dcenter>> _dcentersToDestroy;
|
||||
rpl::event_stream<DcId> _dcKeyChanged;
|
||||
rpl::event_stream<DcId> _dcTemporaryKeyChanged;
|
||||
|
||||
Session *_mainSession = nullptr;
|
||||
base::flat_map<ShiftedDcId, std::unique_ptr<Session>> _sessions;
|
||||
|
@ -225,10 +226,8 @@ private:
|
|||
crl::time _lastConfigLoadedTime = 0;
|
||||
crl::time _configExpiresAt = 0;
|
||||
|
||||
std::map<DcId, AuthKeyPtr> _keysForWrite;
|
||||
mutable QReadWriteLock _keysForWriteLock;
|
||||
|
||||
std::map<ShiftedDcId, mtpRequestId> _logoutGuestRequestIds;
|
||||
base::flat_map<DcId, AuthKeyPtr> _keysForWrite;
|
||||
base::flat_map<ShiftedDcId, mtpRequestId> _logoutGuestRequestIds;
|
||||
|
||||
// holds dcWithShift for request to this dc or -dc for request to main dc
|
||||
std::map<mtpRequestId, ShiftedDcId> _requestsByDc;
|
||||
|
@ -619,23 +618,22 @@ void Instance::Private::logout(
|
|||
|
||||
void Instance::Private::logoutGuestDcs() {
|
||||
auto dcIds = std::vector<DcId>();
|
||||
{
|
||||
QReadLocker lock(&_keysForWriteLock);
|
||||
dcIds.reserve(_keysForWrite.size());
|
||||
for (auto &key : _keysForWrite) {
|
||||
dcIds.push_back(key.first);
|
||||
}
|
||||
dcIds.reserve(_keysForWrite.size());
|
||||
for (const auto &key : _keysForWrite) {
|
||||
dcIds.push_back(key.first);
|
||||
}
|
||||
for (auto dcId : dcIds) {
|
||||
if (dcId != mainDcId() && dcOptions()->dcType(dcId) != DcType::Cdn) {
|
||||
auto shiftedDcId = MTP::logoutDcId(dcId);
|
||||
auto requestId = _instance->send(MTPauth_LogOut(), rpcDone([this](mtpRequestId requestId) {
|
||||
logoutGuestDone(requestId);
|
||||
}), rpcFail([this](mtpRequestId requestId) {
|
||||
return logoutGuestDone(requestId);
|
||||
}), shiftedDcId);
|
||||
_logoutGuestRequestIds.emplace(shiftedDcId, requestId);
|
||||
for (const auto dcId : dcIds) {
|
||||
if (dcId == mainDcId() || dcOptions()->dcType(dcId) == DcType::Cdn) {
|
||||
continue;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
void Instance::Private::dcKeyChanged(DcId dcId, const AuthKeyPtr &key) {
|
||||
_dcKeyChanged.fire_copy(dcId);
|
||||
void Instance::Private::dcPersistentKeyChanged(
|
||||
DcId dcId,
|
||||
const AuthKeyPtr &persistentKey) {
|
||||
dcTemporaryKeyChanged(dcId);
|
||||
|
||||
if (isTemporaryDcId(dcId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
QWriteLocker lock(&_keysForWriteLock);
|
||||
if (key) {
|
||||
_keysForWrite[dcId] = key;
|
||||
} else {
|
||||
_keysForWrite.erase(dcId);
|
||||
const auto i = _keysForWrite.find(dcId);
|
||||
if (i != _keysForWrite.end() && i->second == persistentKey) {
|
||||
return;
|
||||
} else if (i == _keysForWrite.end() && !persistentKey) {
|
||||
return;
|
||||
}
|
||||
crl::on_main(_instance, [=] {
|
||||
DEBUG_LOG(("AuthKey Info: writing auth keys, called by dc %1").arg(dcId));
|
||||
Local::writeMtpData();
|
||||
});
|
||||
if (!persistentKey) {
|
||||
_keysForWrite.erase(i);
|
||||
} 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 {
|
||||
return _dcKeyChanged.events();
|
||||
void Instance::Private::dcTemporaryKeyChanged(DcId dcId) {
|
||||
_dcTemporaryKeyChanged.fire_copy(dcId);
|
||||
}
|
||||
|
||||
rpl::producer<DcId> Instance::Private::dcTemporaryKeyChanged() const {
|
||||
return _dcTemporaryKeyChanged.events();
|
||||
}
|
||||
|
||||
AuthKeysList Instance::Private::getKeysForWrite() const {
|
||||
auto result = AuthKeysList();
|
||||
|
||||
QReadLocker lock(&_keysForWriteLock);
|
||||
result.reserve(_keysForWrite.size());
|
||||
for (auto &key : _keysForWrite) {
|
||||
for (const auto &key : _keysForWrite) {
|
||||
result.push_back(key.second);
|
||||
}
|
||||
return result;
|
||||
|
@ -736,15 +744,12 @@ void Instance::Private::addKeysForDestroy(AuthKeysList &&keys) {
|
|||
const auto dcId = key->dcId();
|
||||
auto shiftedDcId = MTP::destroyKeyNextDcId(dcId);
|
||||
|
||||
{
|
||||
QWriteLocker lock(&_keysForWriteLock);
|
||||
// There could be several keys for one dc if we're destroying them.
|
||||
// Place them all in separate shiftedDcId so that they won't conflict.
|
||||
while (_keysForWrite.find(shiftedDcId) != _keysForWrite.cend()) {
|
||||
shiftedDcId = MTP::destroyKeyNextDcId(shiftedDcId);
|
||||
}
|
||||
_keysForWrite[shiftedDcId] = key;
|
||||
// There could be several keys for one dc if we're destroying them.
|
||||
// Place them all in separate shiftedDcId so that they won't conflict.
|
||||
while (_keysForWrite.find(shiftedDcId) != _keysForWrite.cend()) {
|
||||
shiftedDcId = MTP::destroyKeyNextDcId(shiftedDcId);
|
||||
}
|
||||
_keysForWrite[shiftedDcId] = key;
|
||||
|
||||
addDc(shiftedDcId, std::move(key));
|
||||
startSession(shiftedDcId);
|
||||
|
@ -1352,7 +1357,8 @@ bool Instance::Private::onErrorDefault(mtpRequestId requestId, const RPCError &e
|
|||
checkDelayedRequests();
|
||||
|
||||
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);
|
||||
if (const auto shiftedDcId = queryRequestByDc(requestId)) {
|
||||
dcWithShift = *shiftedDcId;
|
||||
|
@ -1572,10 +1578,7 @@ void Instance::Private::completedKeyDestroy(ShiftedDcId shiftedDcId) {
|
|||
Expects(isKeysDestroyer());
|
||||
|
||||
removeDc(shiftedDcId);
|
||||
{
|
||||
QWriteLocker lock(&_keysForWriteLock);
|
||||
_keysForWrite.erase(shiftedDcId);
|
||||
}
|
||||
_keysForWrite.erase(shiftedDcId);
|
||||
killSession(shiftedDcId);
|
||||
if (_dcenters.empty()) {
|
||||
emit _instance->allKeysDestroyed();
|
||||
|
@ -1588,19 +1591,15 @@ void Instance::Private::checkMainDcKey() {
|
|||
if (findSession(shiftedDcId)) {
|
||||
return;
|
||||
}
|
||||
const auto key = [&] {
|
||||
QReadLocker lock(&_keysForWriteLock);
|
||||
const auto i = _keysForWrite.find(id);
|
||||
return (i != end(_keysForWrite)) ? i->second : AuthKeyPtr();
|
||||
}();
|
||||
if (!key) {
|
||||
const auto i = _keysForWrite.find(id);
|
||||
if (i == end(_keysForWrite)) {
|
||||
return;
|
||||
}
|
||||
const auto lastCheckTime = key->lastCheckTime();
|
||||
const auto lastCheckTime = i->second->lastCheckTime();
|
||||
if (lastCheckTime > 0 && lastCheckTime + kCheckKeyEach >= crl::now()) {
|
||||
return;
|
||||
}
|
||||
_instance->sendDcKeyCheck(shiftedDcId, key);
|
||||
_instance->sendDcKeyCheck(shiftedDcId, i->second);
|
||||
}
|
||||
|
||||
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 (dc->destroyConfirmedForgottenKey(keyId)) {
|
||||
LOG(("Key destroyed!"));
|
||||
dcKeyChanged(dcId, nullptr);
|
||||
dcPersistentKeyChanged(dcId, nullptr);
|
||||
} else {
|
||||
LOG(("Key already is different."));
|
||||
}
|
||||
|
@ -1761,12 +1760,18 @@ void Instance::logout(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail) {
|
|||
_private->logout(onDone, onFail);
|
||||
}
|
||||
|
||||
void Instance::dcKeyChanged(DcId dcId, const AuthKeyPtr &key) {
|
||||
_private->dcKeyChanged(dcId, key);
|
||||
void Instance::dcPersistentKeyChanged(
|
||||
DcId dcId,
|
||||
const AuthKeyPtr &persistentKey) {
|
||||
_private->dcPersistentKeyChanged(dcId, persistentKey);
|
||||
}
|
||||
|
||||
rpl::producer<DcId> Instance::dcKeyChanged() const {
|
||||
return _private->dcKeyChanged();
|
||||
void Instance::dcTemporaryKeyChanged(DcId dcId) {
|
||||
_private->dcTemporaryKeyChanged(dcId);
|
||||
}
|
||||
|
||||
rpl::producer<DcId> Instance::dcTemporaryKeyChanged() const {
|
||||
return _private->dcTemporaryKeyChanged();
|
||||
}
|
||||
|
||||
AuthKeysList Instance::getKeysForWrite() const {
|
||||
|
@ -1881,13 +1886,11 @@ void Instance::sendRequest(
|
|||
}
|
||||
|
||||
void Instance::sendAnything(ShiftedDcId shiftedDcId, crl::time msCanWait) {
|
||||
const auto session = _private->getSession(shiftedDcId);
|
||||
session->sendAnything(msCanWait);
|
||||
_private->getSession(shiftedDcId)->sendAnything(msCanWait);
|
||||
}
|
||||
|
||||
void Instance::sendDcKeyCheck(ShiftedDcId shiftedDcId, const AuthKeyPtr &key) {
|
||||
const auto session = _private->getSession(shiftedDcId);
|
||||
session->sendDcKeyCheck(key);
|
||||
_private->getSession(shiftedDcId)->sendDcKeyCheck(key);
|
||||
}
|
||||
|
||||
Instance::~Instance() {
|
||||
|
|
|
@ -65,8 +65,9 @@ public:
|
|||
[[nodiscard]] QString systemVersion() const;
|
||||
|
||||
// Main thread.
|
||||
void dcKeyChanged(DcId dcId, const AuthKeyPtr &key);
|
||||
[[nodiscard]] rpl::producer<DcId> dcKeyChanged() const;
|
||||
void dcPersistentKeyChanged(DcId dcId, const AuthKeyPtr &persistentKey);
|
||||
void dcTemporaryKeyChanged(DcId dcId);
|
||||
[[nodiscard]] rpl::producer<DcId> dcTemporaryKeyChanged() const;
|
||||
[[nodiscard]] AuthKeysList getKeysForWrite() const;
|
||||
void addKeysForDestroy(AuthKeysList &&keys);
|
||||
|
||||
|
|
|
@ -123,6 +123,16 @@ void AuthKey::setLastCheckTime(crl::time 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) {
|
||||
auto computedAuthKeySize = computedAuthKey.size();
|
||||
Assert(computedAuthKeySize <= kSize);
|
||||
|
|
|
@ -21,6 +21,7 @@ public:
|
|||
|
||||
enum class Type {
|
||||
Generated,
|
||||
Temporary,
|
||||
ReadFromFile,
|
||||
Local,
|
||||
};
|
||||
|
@ -45,6 +46,8 @@ public:
|
|||
|
||||
[[nodiscard]] crl::time lastCheckTime() const;
|
||||
void setLastCheckTime(crl::time time);
|
||||
[[nodiscard]] TimeId expiresAt() const;
|
||||
void setExpiresAt(TimeId expiresAt);
|
||||
|
||||
static void FillData(Data &authKey, bytes::const_span computedAuthKey);
|
||||
|
||||
|
@ -56,6 +59,7 @@ private:
|
|||
Data _key = { { gsl::byte{} } };
|
||||
KeyId _keyId = 0;
|
||||
crl::time _lastCheckTime = 0;
|
||||
TimeId _expiresAt = 0;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "mtproto/session.h"
|
||||
|
||||
#include "mtproto/details/mtproto_dc_key_checker.h"
|
||||
#include "mtproto/connection.h"
|
||||
#include "mtproto/dcenter.h"
|
||||
#include "mtproto/mtproto_auth_key.h"
|
||||
|
@ -222,9 +221,14 @@ bool SessionData::connectionInited() const {
|
|||
return _owner ? _owner->connectionInited() : false;
|
||||
}
|
||||
|
||||
AuthKeyPtr SessionData::getKey() const {
|
||||
AuthKeyPtr SessionData::getTemporaryKey() const {
|
||||
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() {
|
||||
|
@ -232,10 +236,12 @@ bool SessionData::acquireKeyCreation() {
|
|||
return _owner ? _owner->acquireKeyCreation() : false;
|
||||
}
|
||||
|
||||
void SessionData::releaseKeyCreationOnDone(const AuthKeyPtr &key) {
|
||||
void SessionData::releaseKeyCreationOnDone(
|
||||
const AuthKeyPtr &temporaryKey,
|
||||
const AuthKeyPtr &persistentKey) {
|
||||
QMutexLocker lock(&_ownerMutex);
|
||||
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);
|
||||
if (_owner) {
|
||||
_owner->destroyCdnKey(keyId);
|
||||
_owner->destroyTemporaryKey(keyId);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -278,7 +284,7 @@ Session::Session(
|
|||
}
|
||||
|
||||
void Session::watchDcKeyChanges() {
|
||||
_instance->dcKeyChanged(
|
||||
_instance->dcTemporaryKeyChanged(
|
||||
) | rpl::filter([=](DcId dcId) {
|
||||
return (dcId == _shiftedDcId) || (dcId == BareDcId(_shiftedDcId));
|
||||
}) | rpl::start_with_next([=] {
|
||||
|
@ -684,6 +690,29 @@ bool Session::acquireKeyCreation() {
|
|||
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() {
|
||||
Expects(_myKeyCreation);
|
||||
|
||||
|
@ -691,36 +720,20 @@ void Session::releaseKeyCreationOnFail() {
|
|||
_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() {
|
||||
DEBUG_LOG(("MTP Info: emitting MTProtoDC::connectionWasInited(), dcWithShift %1").arg(_shiftedDcId));
|
||||
_dc->setConnectionInited();
|
||||
}
|
||||
|
||||
void Session::destroyCdnKey(uint64 keyId) {
|
||||
if (!_dc->destroyCdnKey(keyId)) {
|
||||
void Session::destroyTemporaryKey(uint64 keyId) {
|
||||
if (!_dc->destroyTemporaryKey(keyId)) {
|
||||
return;
|
||||
}
|
||||
if (sharedDc()) {
|
||||
const auto dcId = _dc->id();
|
||||
const auto instance = _instance;
|
||||
InvokeQueued(instance, [=] {
|
||||
instance->dcKeyChanged(dcId, nullptr);
|
||||
instance->dcTemporaryKeyChanged(dcId);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -729,8 +742,12 @@ int32 Session::getDcWithShift() const {
|
|||
return _shiftedDcId;
|
||||
}
|
||||
|
||||
AuthKeyPtr Session::getKey() const {
|
||||
return _dc->getKey();
|
||||
AuthKeyPtr Session::getTemporaryKey() const {
|
||||
return _dc->getTemporaryKey();
|
||||
}
|
||||
|
||||
AuthKeyPtr Session::getPersistentKey() const {
|
||||
return _dc->getPersistentKey();
|
||||
}
|
||||
|
||||
bool Session::connectionInited() const {
|
||||
|
|
|
@ -283,11 +283,14 @@ public:
|
|||
bool sendMsgStateInfo);
|
||||
|
||||
[[nodiscard]] bool connectionInited() const;
|
||||
[[nodiscard]] AuthKeyPtr getKey() const;
|
||||
[[nodiscard]] AuthKeyPtr getPersistentKey() const;
|
||||
[[nodiscard]] AuthKeyPtr getTemporaryKey() const;
|
||||
[[nodiscard]] bool acquireKeyCreation();
|
||||
void releaseKeyCreationOnDone(const AuthKeyPtr &key);
|
||||
void releaseKeyCreationOnDone(
|
||||
const AuthKeyPtr &temporaryKey,
|
||||
const AuthKeyPtr &persistentKey);
|
||||
void releaseKeyCreationOnFail();
|
||||
void destroyCdnKey(uint64 keyId);
|
||||
void destroyTemporaryKey(uint64 keyId);
|
||||
|
||||
void detach();
|
||||
|
||||
|
@ -351,14 +354,17 @@ public:
|
|||
|
||||
// Thread-safe.
|
||||
[[nodiscard]] ShiftedDcId getDcWithShift() const;
|
||||
[[nodiscard]] AuthKeyPtr getKey() const;
|
||||
[[nodiscard]] AuthKeyPtr getPersistentKey() const;
|
||||
[[nodiscard]] AuthKeyPtr getTemporaryKey() const;
|
||||
[[nodiscard]] bool connectionInited() const;
|
||||
|
||||
// Connection thread.
|
||||
[[nodiscard]] bool acquireKeyCreation();
|
||||
void releaseKeyCreationOnDone(
|
||||
const AuthKeyPtr &temporaryKey,
|
||||
const AuthKeyPtr &persistentKey);
|
||||
void releaseKeyCreationOnFail();
|
||||
void releaseKeyCreationOnDone(const AuthKeyPtr &key);
|
||||
void destroyCdnKey(uint64 keyId);
|
||||
void destroyTemporaryKey(uint64 keyId);
|
||||
|
||||
void notifyDcConnectionInited();
|
||||
|
||||
|
|
|
@ -34,8 +34,8 @@
|
|||
'<(src_loc)',
|
||||
],
|
||||
'sources': [
|
||||
'<(src_loc)/mtproto/details/mtproto_dc_key_checker.cpp',
|
||||
'<(src_loc)/mtproto/details/mtproto_dc_key_checker.h',
|
||||
'<(src_loc)/mtproto/details/mtproto_dc_key_binder.cpp',
|
||||
'<(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.h',
|
||||
'<(src_loc)/mtproto/details/mtproto_dump_to_text.cpp',
|
||||
|
|
Loading…
Reference in New Issue