Always choose correct address for key creation.

This commit is contained in:
John Preston 2019-11-20 12:16:53 +03:00
parent 43bab3eeaa
commit 885738ac32
9 changed files with 267 additions and 145 deletions

View File

@ -7,8 +7,7 @@ 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_bound_key_creator.h"
#include "mtproto/details/mtproto_dump_to_text.h"
#include "mtproto/session.h"
#include "mtproto/mtproto_rsa_public_key.h"
@ -202,7 +201,7 @@ int16 ConnectionPrivate::getProtocolDcId() const {
}
void ConnectionPrivate::destroyAllConnections() {
clearKeyCreatorOnFail();
clearUnboundKeyCreator();
_waitForBetterTimer.cancel();
_waitForReceivedTimer.cancel();
_waitForConnectedTimer.cancel();
@ -244,8 +243,7 @@ ConnectionPrivate::ConnectionPrivate(
}
ConnectionPrivate::~ConnectionPrivate() {
clearKeyCreatorOnFail();
cancelKeyBinder();
releaseKeyCreationOnFail();
Expects(_finished);
Expects(!_connection);
@ -445,7 +443,7 @@ void ConnectionPrivate::tryToSend() {
const auto needsLayer = !_sessionData->connectionInited();
const auto state = getState();
const auto sendOnlyFirstPing = (state != ConnectedState);
const auto sendAll = !sendOnlyFirstPing && !_keyBinder;
const auto sendAll = !sendOnlyFirstPing && !_keyCreator;
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));
@ -459,10 +457,10 @@ void ConnectionPrivate::tryToSend() {
}
const auto forceNewMsgId = sendAll
&& _sessionData->markSessionAsStarted();
if (forceNewMsgId) {
int a = 0;
if (forceNewMsgId && _keyCreator) {
_keyCreator->restartBinder();
}
auto pingRequest = SecureRequest();
auto ackRequest = SecureRequest();
auto resendRequest = SecureRequest();
@ -529,8 +527,8 @@ void ConnectionPrivate::tryToSend() {
httpWaitRequest = SecureRequest::Serialize(MTPHttpWait(
MTP_http_wait(MTP_int(100), MTP_int(30), MTP_int(25000))));
}
if (_keyBinder && !_keyBinder->requested()) {
bindDcKeyRequest = _keyBinder->prepareRequest(
if (_keyCreator && _keyCreator->bindReadyToRequest()) {
bindDcKeyRequest = _keyCreator->prepareBindRequest(
_temporaryKey,
_sessionData->getSessionId());
@ -857,14 +855,13 @@ void ConnectionPrivate::connectToServer(bool afterConfig) {
_connectionOptions = std::make_unique<ConnectionOptions>(
_sessionData->connectionOptions());
// #TODO race.
const auto hasKey = (_sessionData->getTemporaryKey() != nullptr);
tryAcquireKeyCreation();
const auto bareDc = BareDcId(_shiftedDcId);
_dcType = _instance->dcOptions()->dcType(_shiftedDcId);
// Use media_only addresses only if key for this dc is already created.
if (_dcType == DcType::MediaDownload && !hasKey) {
if (_dcType == DcType::MediaDownload && _keyCreator) {
_dcType = DcType::Regular;
} else if (_dcType == DcType::Cdn && !_instance->isKeysDestroyer()) {
if (!_instance->dcOptions()->hasCDNKeysForDc(bareDc)) {
@ -1790,7 +1787,7 @@ ConnectionPrivate::HandleResult ConnectionPrivate::handleOneReceived(const mtpPr
memcpy(response.data(), from, (end - from) * sizeof(mtpPrime));
}
if (typeId == mtpc_rpc_error) {
if (DcKeyBinder::IsDestroyedTemporaryKeyError(response)) {
if (IsDestroyedTemporaryKeyError(response)) {
return HandleResult::DestroyTemporaryKey;
}
// An error could be some RPC_CALL_FAIL or other error inside
@ -1801,22 +1798,22 @@ ConnectionPrivate::HandleResult ConnectionPrivate::handleOneReceived(const mtpPr
}
requestsAcked(ids, true);
if (_keyBinder) {
const auto result = _keyBinder->handleResponse(
if (_keyCreator) {
const auto result = _keyCreator->handleBindResponse(
reqMsgId,
response);
if (result == DcKeyBindState::Success) {
_sessionData->releaseKeyCreationOnDone(
_temporaryKey,
base::take(_keyBinder)->persistentKey());
base::take(_keyCreator)->bindPersistentKey());
_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());
// _keyCreator->bindPersistentKey()->setLastCheckTime(crl::now());
// instance->keyDestroyedOnServer(BareDcId(shiftedDcId), base::take(_keyCreator)->bindPersistentKey()->keyId());
// )
_sessionData->queueNeedToResumeAndSend();
return HandleResult::Success;
@ -2251,9 +2248,6 @@ void ConnectionPrivate::removeTestConnection(
}
void ConnectionPrivate::checkAuthKey() {
Expects(_keyCreator == nullptr);
Expects(_keyBinder == nullptr || _keyId != 0);
if (_keyId) {
authKeyChecked();
} else {
@ -2262,7 +2256,7 @@ void ConnectionPrivate::checkAuthKey() {
}
void ConnectionPrivate::updateAuthKey() {
if (_keyCreator || _keyBinder) {
if (_keyCreator) {
return;
}
@ -2316,67 +2310,69 @@ void ConnectionPrivate::applyAuthKey(AuthKeyPtr &&temporaryKey) {
// We are here to destroy an old key, so we're done.
LOG(("MTP Error: No key %1 in updateAuthKey() for destroying.").arg(_shiftedDcId));
_instance->checkIfKeyWasDestroyed(_shiftedDcId);
return;
} else if (!_sessionData->acquireKeyCreation()) {
} else if (_keyCreator) {
DEBUG_LOG(("AuthKey Info: No key in updateAuthKey(), creating."));
_keyCreator->start(
BareDcId(_shiftedDcId),
getProtocolDcId(),
_connection.get(),
_instance->dcOptions());
} else {
DEBUG_LOG(("AuthKey Info: No key in updateAuthKey(), but someone is creating already."));
}
}
void ConnectionPrivate::tryAcquireKeyCreation() {
if (_keyCreator || !_sessionData->acquireKeyCreation()) {
return;
}
DEBUG_LOG(("AuthKey Info: No key in updateAuthKey(), creating."));
createDcKey();
}
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, "
"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->temporaryServerSalt);
if (result->persistentKey) {
_sessionData->clearForNewKey(_instance);
using Result = DcKeyResult;
using Error = DcKeyError;
auto delegate = BoundKeyCreator::Delegate();
delegate.unboundReady = [=](base::expected<Result, Error> result) {
if (!result) {
releaseKeyCreationOnFail();
if (result.error() == Error::UnknownPublicKey) {
if (_dcType == DcType::Cdn) {
LOG(("Warning: CDN public RSA key not found"));
requestCDNConfig();
return;
}
LOG(("AuthKey Error: could not choose public RSA key"));
}
auto key = result->persistentKey
? std::move(result->persistentKey)
: _sessionData->getPersistentKey();
if (!key) {
restart();
}
_keyCreator = nullptr;
_keyBinder = std::make_unique<DcKeyBinder>(std::move(key));
result->temporaryKey->setExpiresAt(base::unixtime::now()
+ kTemporaryExpiresIn
+ kBindKeyAdditionalExpiresTimeout);
applyAuthKey(std::move(result->temporaryKey));
restart();
return;
}
clearKeyCreatorOnFail();
if (result.error() == Error::UnknownPublicKey) {
if (_dcType == DcType::Cdn) {
LOG(("Warning: CDN public RSA key not found"));
requestCDNConfig();
} else {
LOG(("AuthKey Error: could not choose public RSA key"));
restart();
}
} else {
restart();
DEBUG_LOG(("AuthKey Info: unbound key creation 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->temporaryServerSalt);
if (result->persistentKey) {
_sessionData->clearForNewKey(_instance);
}
auto key = result->persistentKey
? std::move(result->persistentKey)
: _sessionData->getPersistentKey();
if (!key) {
releaseKeyCreationOnFail();
restart();
return;
}
result->temporaryKey->setExpiresAt(base::unixtime::now()
+ kTemporaryExpiresIn
+ kBindKeyAdditionalExpiresTimeout);
_keyCreator->bind(std::move(key));
applyAuthKey(std::move(result->temporaryKey));
};
delegate.sentSome = [=](uint64 size) {
onSentSome(size);
@ -2384,17 +2380,14 @@ void ConnectionPrivate::createDcKey() {
delegate.receivedSome = [=] {
onReceivedSome();
};
// const auto check = (GetDcIdShift(_shiftedDcId) == kCheckKeyDcShift); // #TODO remove kCheckKeyDcShift
auto request = DcKeyCreator::Request();
// const auto check = (GetDcIdShift(_shiftedDcId) == kCheckKeyDcShift); // #TODO remove kCheckKeyDcShift
auto request = DcKeyRequest();
request.persistentNeeded = !_sessionData->getPersistentKey();
request.temporaryExpiresIn = kTemporaryExpiresIn;
_keyCreator = std::make_unique<DcKeyCreator>(
BareDcId(_shiftedDcId),
getProtocolDcId(),
_connection.get(),
_instance->dcOptions(),
std::move(delegate),
request);
_keyCreator = std::make_unique<BoundKeyCreator>(
request,
std::move(delegate));
}
void ConnectionPrivate::authKeyChecked() {
@ -2449,7 +2442,7 @@ void ConnectionPrivate::handleError(int errorCode) {
}
void ConnectionPrivate::destroyTemporaryKey() {
cancelKeyBinder();
releaseKeyCreationOnFail();
if (_temporaryKey) {
_sessionData->destroyTemporaryKey(_temporaryKey->keyId());
}
@ -2566,7 +2559,13 @@ mtpRequestId ConnectionPrivate::wasSent(mtpMsgId msgId) const {
return 0;
}
void ConnectionPrivate::clearKeyCreatorOnFail() {
void ConnectionPrivate::clearUnboundKeyCreator() {
if (_keyCreator) {
_keyCreator->stop();
}
}
void ConnectionPrivate::releaseKeyCreationOnFail() {
if (!_keyCreator) {
return;
}
@ -2574,14 +2573,6 @@ void ConnectionPrivate::clearKeyCreatorOnFail() {
_sessionData->releaseKeyCreationOnFail();
}
void ConnectionPrivate::cancelKeyBinder() {
if (!_keyBinder) {
return;
}
_keyBinder = nullptr;
_sessionData->releaseKeyCreationOnFail();
}
void ConnectionPrivate::stop() {
}

View File

@ -16,8 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace MTP {
namespace details {
class DcKeyCreator;
class DcKeyBinder;
class BoundKeyCreator;
} // namespace details
// How much time to wait for some more requests, when sending msg acks.
@ -179,13 +178,13 @@ private:
crl::time msCanWait = 0,
bool forceContainer = false);
void createDcKey();
void tryAcquireKeyCreation();
void resetSession();
void checkAuthKey();
void authKeyChecked();
void destroyTemporaryKey();
void clearKeyCreatorOnFail();
void cancelKeyBinder();
void clearUnboundKeyCreator();
void releaseKeyCreationOnFail();
void applyAuthKey(AuthKeyPtr &&temporaryKey);
const not_null<Instance*> _instance;
@ -232,8 +231,7 @@ private:
std::shared_ptr<SessionData> _sessionData;
std::unique_ptr<ConnectionOptions> _connectionOptions;
std::unique_ptr<details::DcKeyCreator> _keyCreator;
std::unique_ptr<details::DcKeyBinder> _keyBinder;
std::unique_ptr<details::BoundKeyCreator> _keyCreator;
};

View File

@ -0,0 +1,92 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "mtproto/details/mtproto_bound_key_creator.h"
namespace MTP::details {
BoundKeyCreator::BoundKeyCreator(DcKeyRequest request, Delegate delegate)
: _request(request)
, _delegate(std::move(delegate)) {
}
void BoundKeyCreator::start(
DcId dcId,
int16 protocolDcId,
not_null<AbstractConnection*> connection,
not_null<DcOptions*> dcOptions) {
Expects(!_creator.has_value());
auto delegate = DcKeyCreator::Delegate();
delegate.done = _delegate.unboundReady;
delegate.sentSome = _delegate.sentSome;
delegate.receivedSome = _delegate.receivedSome;
_creator.emplace(
dcId,
protocolDcId,
connection,
dcOptions,
std::move(delegate),
_request);
}
void BoundKeyCreator::stop() {
_creator = std::nullopt;
}
void BoundKeyCreator::bind(AuthKeyPtr &&persistentKey) {
stop();
_binder.emplace(std::move(persistentKey));
}
void BoundKeyCreator::restartBinder() {
if (_binder) {
_binder.emplace(_binder->persistentKey());
}
}
bool BoundKeyCreator::bindReadyToRequest() const {
return _binder ? !_binder->requested() : false;
}
SecureRequest BoundKeyCreator::prepareBindRequest(
const AuthKeyPtr &temporaryKey,
uint64 sessionId) {
Expects(_binder.has_value());
return _binder->prepareRequest(temporaryKey, sessionId);
}
DcKeyBindState BoundKeyCreator::handleBindResponse(
MTPlong requestMsgId,
const mtpBuffer &response) {
return _binder
? _binder->handleResponse(requestMsgId, response)
: DcKeyBindState::Unknown;
}
AuthKeyPtr BoundKeyCreator::bindPersistentKey() const {
Expects(_binder.has_value());
return _binder->persistentKey();
}
bool 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");
});
}
} // namespace MTP::details

View File

@ -0,0 +1,55 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "mtproto/details/mtproto_dc_key_creator.h"
#include "mtproto/details/mtproto_dc_key_binder.h"
namespace MTP::details {
class BoundKeyCreator final {
public:
struct Delegate {
Fn<void(base::expected<DcKeyResult, DcKeyError>)> unboundReady;
Fn<void(uint64)> sentSome;
Fn<void()> receivedSome;
};
BoundKeyCreator(DcKeyRequest request, Delegate delegate);
void start(
DcId dcId,
int16 protocolDcId,
not_null<AbstractConnection*> connection,
not_null<DcOptions*> dcOptions);
void stop();
void bind(AuthKeyPtr &&persistentKey);
void restartBinder();
[[nodiscard]] bool bindReadyToRequest() const;
[[nodiscard]] SecureRequest prepareBindRequest(
const AuthKeyPtr &temporaryKey,
uint64 sessionId);
[[nodiscard]] DcKeyBindState handleBindResponse(
MTPlong requestMsgId,
const mtpBuffer &response);
[[nodiscard]] AuthKeyPtr bindPersistentKey() const;
private:
const DcKeyRequest _request;
Delegate _delegate;
std::optional<DcKeyCreator> _creator;
std::optional<DcKeyBinder> _binder;
};
[[nodiscard]] bool IsDestroyedTemporaryKeyError(const mtpBuffer &buffer);
} // namespace MTP::details

View File

@ -140,18 +140,4 @@ 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");
});
}
} // namespace MTP::details

View File

@ -25,7 +25,7 @@ enum class DcKeyBindState {
class DcKeyBinder final {
public:
DcKeyBinder(AuthKeyPtr &&persistentKey);
explicit DcKeyBinder(AuthKeyPtr &&persistentKey);
[[nodiscard]] bool requested() const;
[[nodiscard]] SecureRequest prepareRequest(
@ -36,9 +36,6 @@ public:
const mtpBuffer &response);
[[nodiscard]] AuthKeyPtr persistentKey() const;
[[nodiscard]] static bool IsDestroyedTemporaryKeyError(
const mtpBuffer &buffer);
private:
AuthKeyPtr _persistentKey;
mtpMsgId _requestMsgId = 0;

View File

@ -169,7 +169,7 @@ DcKeyCreator::DcKeyCreator(
not_null<AbstractConnection*> connection,
not_null<DcOptions*> dcOptions,
Delegate delegate,
Request request)
DcKeyRequest request)
: _connection(connection)
, _dcOptions(dcOptions)
, _dcId(dcId)
@ -313,7 +313,7 @@ void DcKeyCreator::pqAnswered(
_dcId,
data.vserver_public_key_fingerprints().v);
if (!rsaKey.valid()) {
return failed(Error::UnknownPublicKey);
return failed(DcKeyError::UnknownPublicKey);
}
attempt->data.server_nonce = data.vserver_nonce();
@ -596,7 +596,7 @@ void DcKeyCreator::dhClientParamsAnswered(
});
}
void DcKeyCreator::failed(Error error) {
void DcKeyCreator::failed(DcKeyError error) {
stopReceiving();
auto onstack = base::take(_delegate.done);
onstack(tl::unexpected(error));
@ -610,7 +610,7 @@ void DcKeyCreator::done() {
Assert(_temporary.stage == Stage::Ready);
Assert(_persistent.stage == Stage::Ready || _persistent.stage == Stage::None);
auto result = Result();
auto result = DcKeyResult();
result.temporaryKey = std::make_shared<AuthKey>(
AuthKey::Type::Temporary,
_dcId,

View File

@ -21,24 +21,27 @@ namespace MTP::details {
using namespace ::MTP::internal;
struct DcKeyRequest {
TimeId temporaryExpiresIn = 0;
bool persistentNeeded = false;
};
enum class DcKeyError {
UnknownPublicKey,
Other,
};
struct DcKeyResult {
AuthKeyPtr persistentKey;
AuthKeyPtr temporaryKey;
uint64 temporaryServerSalt = 0;
uint64 persistentServerSalt = 0;
};
class DcKeyCreator final {
public:
struct Request {
TimeId temporaryExpiresIn = 0;
bool persistentNeeded = false;
};
enum class Error {
UnknownPublicKey,
Other,
};
struct Result {
AuthKeyPtr persistentKey;
AuthKeyPtr temporaryKey;
uint64 temporaryServerSalt = 0;
uint64 persistentServerSalt = 0;
};
struct Delegate {
FnMut<void(base::expected<Result, Error>)> done;
Fn<void(base::expected<DcKeyResult, DcKeyError>)> done;
Fn<void(uint64)> sentSome;
Fn<void()> receivedSome;
};
@ -49,7 +52,7 @@ public:
not_null<AbstractConnection*> connection,
not_null<DcOptions*> dcOptions,
Delegate delegate,
Request request);
DcKeyRequest request);
~DcKeyCreator();
private:
@ -120,21 +123,19 @@ private:
const MTPset_client_DH_params_answer &data);
void stopReceiving();
void failed(Error error = Error::Other);
void failed(DcKeyError error = DcKeyError::Other);
void done();
const not_null<AbstractConnection*> _connection;
const not_null<DcOptions*> _dcOptions;
const DcId _dcId = 0;
const int16 _protocolDcId = 0;
const Request _request;
const DcKeyRequest _request;
Delegate _delegate;
Attempt _temporary;
Attempt _persistent;
FnMut<void(base::expected<Result, Error>)> _done;
};
} // namespace MTP::details

View File

@ -34,6 +34,8 @@
'<(src_loc)',
],
'sources': [
'<(src_loc)/mtproto/details/mtproto_bound_key_creator.cpp',
'<(src_loc)/mtproto/details/mtproto_bound_key_creator.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',