mirror of
https://github.com/overte-org/overte.git
synced 2025-04-14 14:07:22 +02:00
Allow for delayed challenges if salt is empty
This commit is contained in:
parent
0eea6d1988
commit
e768beb9b1
6 changed files with 118 additions and 84 deletions
|
@ -27,6 +27,12 @@
|
||||||
#include "ClientTraitsHandler.h"
|
#include "ClientTraitsHandler.h"
|
||||||
#include "AvatarLogging.h"
|
#include "AvatarLogging.h"
|
||||||
|
|
||||||
|
MixerAvatar::~MixerAvatar() {
|
||||||
|
if (_challengeTimeout) {
|
||||||
|
_challengeTimeout->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MixerAvatar::fetchAvatarFST() {
|
void MixerAvatar::fetchAvatarFST() {
|
||||||
_verifyState = nonCertified;
|
_verifyState = nonCertified;
|
||||||
|
|
||||||
|
@ -229,6 +235,7 @@ void MixerAvatar::processCertifyEvents() {
|
||||||
QJsonDocument responseJson = QJsonDocument::fromJson(_dynamicMarketResponse.toUtf8());
|
QJsonDocument responseJson = QJsonDocument::fromJson(_dynamicMarketResponse.toUtf8());
|
||||||
QString ownerPublicKey;
|
QString ownerPublicKey;
|
||||||
bool ownerValid = false;
|
bool ownerValid = false;
|
||||||
|
_pendingEvent = false;
|
||||||
if (responseJson["status"].toString() == "success") {
|
if (responseJson["status"].toString() == "success") {
|
||||||
QJsonValue jsonData = responseJson["data"];
|
QJsonValue jsonData = responseJson["data"];
|
||||||
if (jsonData.isObject()) {
|
if (jsonData.isObject()) {
|
||||||
|
@ -251,6 +258,7 @@ void MixerAvatar::processCertifyEvents() {
|
||||||
}
|
}
|
||||||
sendOwnerChallenge();
|
sendOwnerChallenge();
|
||||||
_verifyState = challengeClient;
|
_verifyState = challengeClient;
|
||||||
|
_pendingEvent = true;
|
||||||
} else {
|
} else {
|
||||||
_verifyState = error;
|
_verifyState = error;
|
||||||
}
|
}
|
||||||
|
@ -259,7 +267,6 @@ void MixerAvatar::processCertifyEvents() {
|
||||||
"message:" << responseJson["message"].toString();
|
"message:" << responseJson["message"].toString();
|
||||||
_verifyState = error;
|
_verifyState = error;
|
||||||
}
|
}
|
||||||
_pendingEvent = false;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,6 +302,7 @@ void MixerAvatar::processCertifyEvents() {
|
||||||
}
|
}
|
||||||
|
|
||||||
case requestingOwner:
|
case requestingOwner:
|
||||||
|
case challengeClient:
|
||||||
{ // Qt networking done on this thread:
|
{ // Qt networking done on this thread:
|
||||||
QCoreApplication::processEvents();
|
QCoreApplication::processEvents();
|
||||||
break;
|
break;
|
||||||
|
@ -324,12 +332,21 @@ void MixerAvatar::sendOwnerChallenge() {
|
||||||
nonceHash.addData(nonce);
|
nonceHash.addData(nonce);
|
||||||
_challengeNonceHash = nonceHash.result();
|
_challengeNonceHash = nonceHash.result();
|
||||||
|
|
||||||
static constexpr int CHALLENGE_TIMEOUT_MS = 10 * 1000; // 10 s
|
static constexpr int CHALLENGE_TIMEOUT_MS = 5 * 1000; // 5 s
|
||||||
_challengeTimeout.setInterval(CHALLENGE_TIMEOUT_MS);
|
if (_challengeTimeout) {
|
||||||
_challengeTimeout.connect(&_challengeTimeout, &QTimer::timeout, [this]() {
|
_challengeTimeout->deleteLater();
|
||||||
_verifyState = verificationFailed;
|
}
|
||||||
_needsIdentityUpdate = true;
|
_challengeTimeout = new QTimer();
|
||||||
|
_challengeTimeout->setInterval(CHALLENGE_TIMEOUT_MS);
|
||||||
|
_challengeTimeout->setSingleShot(true);
|
||||||
|
_challengeTimeout->connect(_challengeTimeout, &QTimer::timeout, this, [this]() {
|
||||||
|
if (_verifyState == challengeClient) {
|
||||||
|
_pendingEvent = false;
|
||||||
|
_verifyState = verificationFailed;
|
||||||
|
_needsIdentityUpdate = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
_challengeTimeout->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MixerAvatar::handleChallengeResponse(ReceivedMessage* response) {
|
void MixerAvatar::handleChallengeResponse(ReceivedMessage* response) {
|
||||||
|
@ -337,7 +354,6 @@ void MixerAvatar::handleChallengeResponse(ReceivedMessage* response) {
|
||||||
QByteArray encryptedNonce;
|
QByteArray encryptedNonce;
|
||||||
QMutexLocker certifyLocker(&_avatarCertifyLock);
|
QMutexLocker certifyLocker(&_avatarCertifyLock);
|
||||||
if (_verifyState == challengeClient) {
|
if (_verifyState == challengeClient) {
|
||||||
_challengeTimeout.stop();
|
|
||||||
_challengeResponse = response->readAll();
|
_challengeResponse = response->readAll();
|
||||||
_verifyState = challengeResponse;
|
_verifyState = challengeResponse;
|
||||||
_pendingEvent = true;
|
_pendingEvent = true;
|
||||||
|
|
|
@ -21,6 +21,7 @@ class ResourceRequest;
|
||||||
|
|
||||||
class MixerAvatar : public AvatarData {
|
class MixerAvatar : public AvatarData {
|
||||||
public:
|
public:
|
||||||
|
~MixerAvatar();
|
||||||
bool getNeedsHeroCheck() const { return _needsHeroCheck; }
|
bool getNeedsHeroCheck() const { return _needsHeroCheck; }
|
||||||
void setNeedsHeroCheck(bool needsHeroCheck = true) { _needsHeroCheck = needsHeroCheck; }
|
void setNeedsHeroCheck(bool needsHeroCheck = true) { _needsHeroCheck = needsHeroCheck; }
|
||||||
|
|
||||||
|
@ -53,7 +54,7 @@ private:
|
||||||
QString _ownerPublicKey;
|
QString _ownerPublicKey;
|
||||||
QByteArray _challengeNonceHash;
|
QByteArray _challengeNonceHash;
|
||||||
QByteArray _challengeResponse;
|
QByteArray _challengeResponse;
|
||||||
QTimer _challengeTimeout;
|
QTimer* _challengeTimeout { nullptr };
|
||||||
bool _needsIdentityUpdate { false };
|
bool _needsIdentityUpdate { false };
|
||||||
|
|
||||||
bool generateFSTHash();
|
bool generateFSTHash();
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 42 KiB |
|
@ -317,13 +317,13 @@ void Ledger::accountSuccess(QNetworkReply* reply) {
|
||||||
const QByteArray locker = data["locker"].toString().toUtf8();
|
const QByteArray locker = data["locker"].toString().toUtf8();
|
||||||
bool isOverride = wallet->wasSoftReset();
|
bool isOverride = wallet->wasSoftReset();
|
||||||
|
|
||||||
wallet->setSalt(salt);
|
|
||||||
wallet->setIv(iv);
|
wallet->setIv(iv);
|
||||||
wallet->setCKey(ckey);
|
wallet->setCKey(ckey);
|
||||||
if (!locker.isEmpty()) {
|
if (!locker.isEmpty()) {
|
||||||
wallet->setWallet(locker);
|
wallet->setWallet(locker);
|
||||||
wallet->setPassphrase("ACCOUNT"); // We only locker wallets that have been converted to account-based auth.
|
wallet->setPassphrase("ACCOUNT"); // We only locker wallets that have been converted to account-based auth.
|
||||||
}
|
}
|
||||||
|
wallet->setSalt(salt);
|
||||||
|
|
||||||
QString keyStatus = "ok";
|
QString keyStatus = "ok";
|
||||||
QStringList localPublicKeys = wallet->listPublicKeys();
|
QStringList localPublicKeys = wallet->listPublicKeys();
|
||||||
|
|
|
@ -313,6 +313,8 @@ Wallet::Wallet() {
|
||||||
walletScriptingInterface->setWalletStatus(status);
|
walletScriptingInterface->setWalletStatus(status);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(ledger.data(), &Ledger::accountResult, this, &Wallet::sendChallengeOwnershipResponses);
|
||||||
|
|
||||||
auto accountManager = DependencyManager::get<AccountManager>();
|
auto accountManager = DependencyManager::get<AccountManager>();
|
||||||
connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() {
|
connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() {
|
||||||
getWalletStatus();
|
getWalletStatus();
|
||||||
|
@ -823,88 +825,101 @@ bool Wallet::changePassphrase(const QString& newPassphrase) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Wallet::handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode) {
|
void Wallet::handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode) {
|
||||||
|
_pendingChallenges.push_back(packet);
|
||||||
|
sendChallengeOwnershipResponses();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Wallet::sendChallengeOwnershipResponses() {
|
||||||
|
if (_pendingChallenges.size() == 0 || getSalt().length() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
||||||
// With EC keys, we receive a nonce from the metaverse server, which is signed
|
|
||||||
// here with the private key and returned. Verification is done at server.
|
|
||||||
|
|
||||||
bool challengeOriginatedFromClient = packet->getType() == PacketType::ChallengeOwnershipRequest;
|
|
||||||
int status;
|
|
||||||
int idByteArraySize;
|
|
||||||
int textByteArraySize;
|
|
||||||
int challengingNodeUUIDByteArraySize;
|
|
||||||
|
|
||||||
packet->readPrimitive(&idByteArraySize);
|
|
||||||
packet->readPrimitive(&textByteArraySize); // returns a cast char*, size
|
|
||||||
if (challengeOriginatedFromClient) {
|
|
||||||
packet->readPrimitive(&challengingNodeUUIDByteArraySize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// "encryptedText" is now a series of random bytes, a nonce
|
|
||||||
QByteArray id = packet->read(idByteArraySize);
|
|
||||||
QByteArray text = packet->read(textByteArraySize);
|
|
||||||
QByteArray challengingNodeUUID;
|
|
||||||
if (challengeOriginatedFromClient) {
|
|
||||||
challengingNodeUUID = packet->read(challengingNodeUUIDByteArraySize);
|
|
||||||
}
|
|
||||||
|
|
||||||
EC_KEY* ec = readKeys(keyFilePath());
|
EC_KEY* ec = readKeys(keyFilePath());
|
||||||
QString sig;
|
|
||||||
|
|
||||||
if (ec) {
|
for (const auto& packet: _pendingChallenges) {
|
||||||
ERR_clear_error();
|
|
||||||
sig = signWithKey(text, ""); // base64 signature, QByteArray cast (on return) to QString FIXME should pass ec as string so we can tell which key to sign with
|
// With EC keys, we receive a nonce from the metaverse server, which is signed
|
||||||
status = 1;
|
// here with the private key and returned. Verification is done at server.
|
||||||
} else {
|
|
||||||
qCDebug(commerce) << "During entity ownership challenge, creating the EC-signed nonce failed.";
|
QString sig;
|
||||||
status = -1;
|
bool challengeOriginatedFromClient = packet->getType() == PacketType::ChallengeOwnershipRequest;
|
||||||
|
int status;
|
||||||
|
int idByteArraySize;
|
||||||
|
int textByteArraySize;
|
||||||
|
int challengingNodeUUIDByteArraySize;
|
||||||
|
|
||||||
|
packet->readPrimitive(&idByteArraySize);
|
||||||
|
packet->readPrimitive(&textByteArraySize); // returns a cast char*, size
|
||||||
|
if (challengeOriginatedFromClient) {
|
||||||
|
packet->readPrimitive(&challengingNodeUUIDByteArraySize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// "encryptedText" is now a series of random bytes, a nonce
|
||||||
|
QByteArray id = packet->read(idByteArraySize);
|
||||||
|
QByteArray text = packet->read(textByteArraySize);
|
||||||
|
QByteArray challengingNodeUUID;
|
||||||
|
if (challengeOriginatedFromClient) {
|
||||||
|
challengingNodeUUID = packet->read(challengingNodeUUIDByteArraySize);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ec) {
|
||||||
|
ERR_clear_error();
|
||||||
|
sig = signWithKey(text, ""); // base64 signature, QByteArray cast (on return) to QString FIXME should pass ec as string so we can tell which key to sign with
|
||||||
|
status = 1;
|
||||||
|
} else {
|
||||||
|
qCDebug(commerce) << "During entity ownership challenge, creating the EC-signed nonce failed.";
|
||||||
|
status = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray textByteArray;
|
||||||
|
if (status > -1) {
|
||||||
|
textByteArray = sig.toUtf8();
|
||||||
|
}
|
||||||
|
textByteArraySize = textByteArray.size();
|
||||||
|
int idSize = id.size();
|
||||||
|
// setup the packet
|
||||||
|
Node& sendingNode = *nodeList->nodeWithLocalID(packet->getSourceID());
|
||||||
|
if (challengeOriginatedFromClient) {
|
||||||
|
auto textPacket = NLPacket::create(PacketType::ChallengeOwnershipReply,
|
||||||
|
idSize + textByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int),
|
||||||
|
true);
|
||||||
|
|
||||||
|
textPacket->writePrimitive(idSize);
|
||||||
|
textPacket->writePrimitive(textByteArraySize);
|
||||||
|
textPacket->writePrimitive(challengingNodeUUIDByteArraySize);
|
||||||
|
textPacket->write(id);
|
||||||
|
textPacket->write(textByteArray);
|
||||||
|
textPacket->write(challengingNodeUUID);
|
||||||
|
|
||||||
|
qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing signed text" << textByteArray << "for id" << id;
|
||||||
|
|
||||||
|
nodeList->sendPacket(std::move(textPacket), sendingNode);
|
||||||
|
} else {
|
||||||
|
auto textPacket = NLPacket::create(PacketType::ChallengeOwnership, idSize + textByteArraySize + 2 * sizeof(int), true);
|
||||||
|
|
||||||
|
textPacket->writePrimitive(idSize);
|
||||||
|
textPacket->writePrimitive(textByteArraySize);
|
||||||
|
textPacket->write(id);
|
||||||
|
textPacket->write(textByteArray);
|
||||||
|
|
||||||
|
qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << textByteArray << "for id" << id;
|
||||||
|
|
||||||
|
nodeList->sendPacket(std::move(textPacket), sendingNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == -1) {
|
||||||
|
qCDebug(commerce) << "During entity ownership challenge, signing the text failed.";
|
||||||
|
long error = ERR_get_error();
|
||||||
|
if (error != 0) {
|
||||||
|
const char* error_str = ERR_error_string(error, NULL);
|
||||||
|
qCWarning(entities) << "EC error:" << error_str;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EC_KEY_free(ec);
|
EC_KEY_free(ec);
|
||||||
|
_pendingChallenges.clear();
|
||||||
QByteArray textByteArray;
|
|
||||||
if (status > -1) {
|
|
||||||
textByteArray = sig.toUtf8();
|
|
||||||
}
|
|
||||||
textByteArraySize = textByteArray.size();
|
|
||||||
int idSize = id.size();
|
|
||||||
// setup the packet
|
|
||||||
if (challengeOriginatedFromClient) {
|
|
||||||
auto textPacket = NLPacket::create(PacketType::ChallengeOwnershipReply,
|
|
||||||
idSize + textByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int),
|
|
||||||
true);
|
|
||||||
|
|
||||||
textPacket->writePrimitive(idSize);
|
|
||||||
textPacket->writePrimitive(textByteArraySize);
|
|
||||||
textPacket->writePrimitive(challengingNodeUUIDByteArraySize);
|
|
||||||
textPacket->write(id);
|
|
||||||
textPacket->write(textByteArray);
|
|
||||||
textPacket->write(challengingNodeUUID);
|
|
||||||
|
|
||||||
qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing signed text" << textByteArray << "for id" << id;
|
|
||||||
|
|
||||||
nodeList->sendPacket(std::move(textPacket), *sendingNode);
|
|
||||||
} else {
|
|
||||||
auto textPacket = NLPacket::create(PacketType::ChallengeOwnership, idSize + textByteArraySize + 2 * sizeof(int), true);
|
|
||||||
|
|
||||||
textPacket->writePrimitive(idSize);
|
|
||||||
textPacket->writePrimitive(textByteArraySize);
|
|
||||||
textPacket->write(id);
|
|
||||||
textPacket->write(textByteArray);
|
|
||||||
|
|
||||||
qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << textByteArray << "for id" << id;
|
|
||||||
|
|
||||||
nodeList->sendPacket(std::move(textPacket), *sendingNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status == -1) {
|
|
||||||
qCDebug(commerce) << "During entity ownership challenge, signing the text failed.";
|
|
||||||
long error = ERR_get_error();
|
|
||||||
if (error != 0) {
|
|
||||||
const char* error_str = ERR_error_string(error, NULL);
|
|
||||||
qCWarning(entities) << "EC error:" << error_str;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Wallet::account() {
|
void Wallet::account() {
|
||||||
|
|
|
@ -94,6 +94,7 @@ signals:
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
void handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
|
||||||
|
void sendChallengeOwnershipResponses();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class Ledger;
|
friend class Ledger;
|
||||||
|
@ -104,6 +105,7 @@ private:
|
||||||
QByteArray _ckey;
|
QByteArray _ckey;
|
||||||
QString* _passphrase { nullptr };
|
QString* _passphrase { nullptr };
|
||||||
bool _isOverridingServer { false };
|
bool _isOverridingServer { false };
|
||||||
|
std::vector<QSharedPointer<ReceivedMessage>> _pendingChallenges;
|
||||||
|
|
||||||
bool writeWallet(const QString& newPassphrase = QString(""));
|
bool writeWallet(const QString& newPassphrase = QString(""));
|
||||||
void updateImageProvider();
|
void updateImageProvider();
|
||||||
|
|
Loading…
Reference in a new issue