Merge pull request #15275 from howard-stearns/ddv-all

DDV all, including domainUnlimited
This commit is contained in:
Shannon Romano 2019-04-03 11:03:16 -07:00 committed by GitHub
commit f2ff5bebc7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 209 additions and 201 deletions

View file

@ -22,7 +22,6 @@
#include <ScriptCache.h>
#include <EntityEditFilters.h>
#include <NetworkingConstants.h>
#include <AddressManager.h>
#include <hfm/ModelFormatRegistry.h>
#include "../AssignmentDynamicFactory.h"
@ -471,77 +470,7 @@ void EntityServer::startDynamicDomainVerification() {
qCDebug(entities) << "Starting Dynamic Domain Verification...";
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
QHash<QString, EntityItemID> localMap(tree->getEntityCertificateIDMap());
QHashIterator<QString, EntityItemID> i(localMap);
qCDebug(entities) << localMap.size() << "entities in _entityCertificateIDMap";
while (i.hasNext()) {
i.next();
const auto& certificateID = i.key();
const auto& entityID = i.value();
EntityItemPointer entity = tree->findEntityByEntityItemID(entityID);
if (entity) {
if (!entity->getProperties().verifyStaticCertificateProperties()) {
qCDebug(entities) << "During Dynamic Domain Verification, a certified entity with ID" << entityID << "failed"
<< "static certificate verification.";
// Delete the entity if it doesn't pass static certificate verification
tree->withWriteLock([&] {
tree->deleteEntity(entityID, true);
});
} else {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest;
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location");
QJsonObject request;
request["certificate_id"] = certificateID;
networkRequest.setUrl(requestURL);
QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
connect(networkReply, &QNetworkReply::finished, this, [this, entityID, networkReply] {
EntityTreePointer tree = std::static_pointer_cast<EntityTree>(_tree);
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
jsonObject = jsonObject["data"].toObject();
if (networkReply->error() == QNetworkReply::NoError) {
QString thisDomainID = DependencyManager::get<AddressManager>()->getDomainID().remove(QRegExp("\\{|\\}"));
if (jsonObject["domain_id"].toString() != thisDomainID) {
EntityItemPointer entity = tree->findEntityByEntityItemID(entityID);
if (!entity) {
qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID;
networkReply->deleteLater();
return;
}
if (entity->getAge() > (_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS/MSECS_PER_SECOND)) {
qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString()
<< "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID;
tree->withWriteLock([&] {
tree->deleteEntity(entityID, true);
});
} else {
qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << entityID;
}
} else {
qCDebug(entities) << "Entity passed dynamic domain verification:" << entityID;
}
} else {
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; NOT deleting entity" << entityID
<< "More info:" << jsonObject;
}
networkReply->deleteLater();
});
}
} else {
qCWarning(entities) << "During DDV, an entity with ID" << entityID << "was NOT found in the Entity Tree!";
}
}
tree->startDynamicDomainVerificationOnServer((float) _MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS / MSECS_PER_SECOND);
int nextInterval = qrand() % ((_MAXIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS + 1) - _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS) + _MINIMUM_DYNAMIC_DOMAIN_VERIFICATION_TIMER_MS;
qCDebug(entities) << "Restarting Dynamic Domain Verification timer for" << nextInterval / 1000 << "seconds";

View file

@ -17,6 +17,7 @@
#include <QJsonObject>
#include <DependencyManager.h>
#include <QtNetwork/QNetworkReply>
#include <EntityItemID.h>
#include "AccountManager.h"
@ -65,7 +66,7 @@ signals:
void availableUpdatesResult(QJsonObject result);
void updateItemResult(QJsonObject result);
void updateCertificateStatus(const QString& certID, uint certStatus);
void updateCertificateStatus(const EntityItemID& entityID, uint certStatus);
public slots:
void buySuccess(QNetworkReply* reply);

View file

@ -19,6 +19,7 @@
#include <QPixmap>
#include <EntityItemID.h>
#include <DependencyManager.h>
class QmlCommerce : public QObject, public Dependency {
@ -49,7 +50,7 @@ signals:
void availableUpdatesResult(QJsonObject result);
void updateItemResult(QJsonObject result);
void updateCertificateStatus(const QString& certID, uint certStatus);
void updateCertificateStatus(const EntityItemID& entityID, uint certStatus);
void transferAssetToNodeResult(QJsonObject result);
void transferAssetToUsernameResult(QJsonObject result);

View file

@ -816,18 +816,18 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> pack
bool challengeOriginatedFromClient = packet->getType() == PacketType::ChallengeOwnershipRequest;
int status;
int certIDByteArraySize;
int idByteArraySize;
int textByteArraySize;
int challengingNodeUUIDByteArraySize;
packet->readPrimitive(&certIDByteArraySize);
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 certID = packet->read(certIDByteArraySize);
QByteArray id = packet->read(idByteArraySize);
QByteArray text = packet->read(textByteArraySize);
QByteArray challengingNodeUUID;
if (challengeOriginatedFromClient) {
@ -853,32 +853,32 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> pack
textByteArray = sig.toUtf8();
}
textByteArraySize = textByteArray.size();
int certIDSize = certID.size();
int idSize = id.size();
// setup the packet
if (challengeOriginatedFromClient) {
auto textPacket = NLPacket::create(PacketType::ChallengeOwnershipReply,
certIDSize + textByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int),
idSize + textByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int),
true);
textPacket->writePrimitive(certIDSize);
textPacket->writePrimitive(idSize);
textPacket->writePrimitive(textByteArraySize);
textPacket->writePrimitive(challengingNodeUUIDByteArraySize);
textPacket->write(certID);
textPacket->write(id);
textPacket->write(textByteArray);
textPacket->write(challengingNodeUUID);
qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing signed text" << textByteArray << "for CertID" << certID;
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, certIDSize + textByteArraySize + 2 * sizeof(int), true);
auto textPacket = NLPacket::create(PacketType::ChallengeOwnership, idSize + textByteArraySize + 2 * sizeof(int), true);
textPacket->writePrimitive(certIDSize);
textPacket->writePrimitive(idSize);
textPacket->writePrimitive(textByteArraySize);
textPacket->write(certID);
textPacket->write(id);
textPacket->write(textByteArray);
qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << textByteArray << "for CertID" << certID;
qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << textByteArray << "for id" << id;
nodeList->sendPacket(std::move(textPacket), *sendingNode);
}

View file

@ -292,7 +292,7 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID
setLastInspectedEntity(entityID);
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags);
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityID, _entityPropertyFlags);
auto nodeList = DependencyManager::get<NodeList>();
@ -328,31 +328,31 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID
} else {
QString ownerKey = jsonObject["transfer_recipient_key"].toString();
QByteArray certID = entityProperties.getCertificateID().toUtf8();
QByteArray text = DependencyManager::get<EntityTreeRenderer>()->getTree()->computeNonce(certID, ownerKey);
QByteArray id = entityID.toByteArray();
QByteArray text = DependencyManager::get<EntityTreeRenderer>()->getTree()->computeNonce(entityID, ownerKey);
QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122();
int certIDByteArraySize = certID.length();
int idByteArraySize = id.length();
int textByteArraySize = text.length();
int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length();
auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest,
certIDByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int),
idByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int),
true);
challengeOwnershipPacket->writePrimitive(certIDByteArraySize);
challengeOwnershipPacket->writePrimitive(idByteArraySize);
challengeOwnershipPacket->writePrimitive(textByteArraySize);
challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize);
challengeOwnershipPacket->write(certID);
challengeOwnershipPacket->write(id);
challengeOwnershipPacket->write(text);
challengeOwnershipPacket->write(nodeToChallengeByteArray);
nodeList->sendPacket(std::move(challengeOwnershipPacket), *entityServer);
// Kickoff a 10-second timeout timer that marks the cert if we don't get an ownership response in time
if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer");
QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer", Q_ARG(const EntityItemID&, entityID));
return;
} else {
startChallengeOwnershipTimer();
startChallengeOwnershipTimer(entityID);
}
}
} else {
@ -370,14 +370,14 @@ void ContextOverlayInterface::requestOwnershipVerification(const QUuid& entityID
// so they always pass Ownership Verification. It's necessary to emit this signal
// so that the Inspection Certificate can continue its information-grabbing process.
auto ledger = DependencyManager::get<Ledger>();
emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS));
emit ledger->updateCertificateStatus(entityID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS));
}
} else {
auto ledger = DependencyManager::get<Ledger>();
_challengeOwnershipTimeoutTimer.stop();
emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(_lastInspectedEntity);
qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!";
emit ledger->updateCertificateStatus(entityID, (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(entityID);
qCDebug(context_overlay) << "Entity" << entityID << "failed static certificate verification!";
}
}
@ -395,14 +395,14 @@ void ContextOverlayInterface::deletingEntity(const EntityItemID& entityID) {
}
}
void ContextOverlayInterface::startChallengeOwnershipTimer() {
void ContextOverlayInterface::startChallengeOwnershipTimer(const EntityItemID& entityItemID) {
auto ledger = DependencyManager::get<Ledger>();
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags);
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(entityItemID, _entityPropertyFlags);
connect(&_challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() {
qCDebug(entities) << "Ownership challenge timed out for" << _lastInspectedEntity;
emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_TIMEOUT));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(_lastInspectedEntity);
qCDebug(entities) << "Ownership challenge timed out for" << entityItemID;
emit ledger->updateCertificateStatus(entityItemID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_TIMEOUT));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(entityItemID);
});
_challengeOwnershipTimeoutTimer.start(5000);
@ -413,23 +413,22 @@ void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer
_challengeOwnershipTimeoutTimer.stop();
int certIDByteArraySize;
int idByteArraySize;
int textByteArraySize;
packet->readPrimitive(&certIDByteArraySize);
packet->readPrimitive(&idByteArraySize);
packet->readPrimitive(&textByteArraySize);
QString certID(packet->read(certIDByteArraySize));
EntityItemID id(packet->read(idByteArraySize));
QString text(packet->read(textByteArraySize));
EntityItemID id;
bool verificationSuccess = DependencyManager::get<EntityTreeRenderer>()->getTree()->verifyNonce(certID, text, id);
bool verificationSuccess = DependencyManager::get<EntityTreeRenderer>()->getTree()->verifyNonce(id, text);
if (verificationSuccess) {
emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationSuccess(_lastInspectedEntity);
emit ledger->updateCertificateStatus(id, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationSuccess(id);
} else {
emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(_lastInspectedEntity);
emit ledger->updateCertificateStatus(id, (uint)(ledger->CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED));
emit DependencyManager::get<WalletScriptingInterface>()->ownershipVerificationFailed(id);
}
}

View file

@ -46,7 +46,7 @@ public:
ContextOverlayInterface();
Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; }
void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; }
void setLastInspectedEntity(const QUuid& entityID) { _challengeOwnershipTimeoutTimer.stop(); _lastInspectedEntity = entityID; }
void setLastInspectedEntity(const QUuid& entityID) { _challengeOwnershipTimeoutTimer.stop(); }
void setEnabled(bool enabled);
bool getEnabled() { return _enabled; }
bool getIsInMarketplaceInspectionMode() { return _isInMarketplaceInspectionMode; }
@ -83,7 +83,6 @@ private:
EntityItemID _mouseDownEntity;
quint64 _mouseDownEntityTimestamp;
EntityItemID _currentEntityWithContextOverlay;
EntityItemID _lastInspectedEntity;
QString _entityMarketplaceID;
bool _contextOverlayJustClicked { false };
@ -94,7 +93,7 @@ private:
void deletingEntity(const EntityItemID& entityItemID);
Q_INVOKABLE void startChallengeOwnershipTimer();
Q_INVOKABLE void startChallengeOwnershipTimer(const EntityItemID& entityItemID);
QTimer _challengeOwnershipTimeoutTimer;
};

View file

@ -26,6 +26,7 @@
#include <Extents.h>
#include <PerfStat.h>
#include <Profile.h>
#include <AddressManager.h>
#include "EntitySimulation.h"
#include "VariantMapToScriptValue.h"
@ -286,27 +287,7 @@ void EntityTree::postAddEntity(EntityItemPointer entity) {
assert(entity);
if (getIsServer()) {
QString certID(entity->getCertificateID());
EntityItemID entityItemID = entity->getEntityItemID();
EntityItemID existingEntityItemID;
{
QWriteLocker locker(&_entityCertificateIDMapLock);
existingEntityItemID = _entityCertificateIDMap.value(certID);
if (!certID.isEmpty()) {
_entityCertificateIDMap.insert(certID, entityItemID);
qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID;
}
}
// Delete an already-existing entity from the tree if it has the same
// CertificateID as the entity we're trying to add.
if (!existingEntityItemID.isNull() && !entity->getCertificateType().contains(DOMAIN_UNLIMITED)) {
qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID"
<< existingEntityItemID << ". Deleting existing entity.";
deleteEntity(existingEntityItemID, true);
return;
}
addCertifiedEntityOnServer(entity);
}
// check to see if we need to simulate this entity..
@ -764,13 +745,7 @@ void EntityTree::processRemovedEntities(const DeleteEntityOperator& theOperator)
theEntity->die();
if (getIsServer()) {
{
QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock);
QString certID = theEntity->getCertificateID();
if (theEntity->getEntityItemID() == _entityCertificateIDMap.value(certID)) {
_entityCertificateIDMap.remove(certID);
}
}
removeCertifiedEntityOnServer(theEntity);
// set up the deleted entities ID
QWriteLocker recentlyDeletedEntitiesLocker(&_recentlyDeletedEntitiesLock);
@ -1421,11 +1396,123 @@ bool EntityTree::isScriptInWhitelist(const QString& scriptProperty) {
return false;
}
void EntityTree::addCertifiedEntityOnServer(EntityItemPointer entity) {
QString certID(entity->getCertificateID());
EntityItemID existingEntityItemID;
if (!certID.isEmpty()) {
EntityItemID entityItemID = entity->getEntityItemID();
QWriteLocker locker(&_entityCertificateIDMapLock);
QList<EntityItemID>& entityList = _entityCertificateIDMap[certID]; // inserts it if needed.
if (!entityList.isEmpty() && !entity->getCertificateType().contains(DOMAIN_UNLIMITED)) {
existingEntityItemID = entityList.first(); // we will only care about the first, if any, below.
entityList.removeOne(existingEntityItemID);
}
entityList << entityItemID; // adds to list within hash because entityList is a reference.
qCDebug(entities) << "Certificate ID" << certID << "belongs to" << entityItemID << "total" << entityList.size() << "entities.";
}
// Delete an already-existing entity from the tree if it has the same
// CertificateID as the entity we're trying to add.
if (!existingEntityItemID.isNull()) {
qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID"
<< existingEntityItemID << ". Deleting existing entity.";
withWriteLock([&] {
deleteEntity(existingEntityItemID, true);
});
}
}
void EntityTree::removeCertifiedEntityOnServer(EntityItemPointer entity) {
QString certID = entity->getCertificateID();
if (!certID.isEmpty()) {
QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock);
QList<EntityItemID>& entityList = _entityCertificateIDMap[certID];
entityList.removeOne(entity->getEntityItemID());
if (entityList.isEmpty()) {
// hmmm, do we to make it be a hash instead of a list, so that this is faster if you stamp out 1000 of a domainUnlimited?
_entityCertificateIDMap.remove(certID);
}
}
}
void EntityTree::startDynamicDomainVerificationOnServer(float minimumAgeToRemove) {
QReadLocker locker(&_entityCertificateIDMapLock);
QHashIterator<QString, QList<EntityItemID>> i(_entityCertificateIDMap);
qCDebug(entities) << _entityCertificateIDMap.size() << "certificates present.";
while (i.hasNext()) {
i.next();
const auto& certificateID = i.key();
const auto& entityIDs = i.value();
if (entityIDs.isEmpty()) {
continue;
}
// Examine each cert:
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
QNetworkRequest networkRequest;
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL();
requestURL.setPath("/api/v1/commerce/proof_of_purchase_status/location");
QJsonObject request;
request["certificate_id"] = certificateID;
networkRequest.setUrl(requestURL);
QNetworkReply* networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson());
connect(networkReply, &QNetworkReply::finished, this, [this, entityIDs, networkReply, minimumAgeToRemove, &certificateID] {
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
jsonObject = jsonObject["data"].toObject();
bool failure = networkReply->error() != QNetworkReply::NoError;
auto failureReason = networkReply->error();
networkReply->deleteLater();
if (failure) {
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << failureReason
<< "; NOT deleting cert" << certificateID << "More info:" << jsonObject;
return;
}
QString thisDomainID = DependencyManager::get<AddressManager>()->getDomainID().remove(QRegExp("\\{|\\}"));
if (jsonObject["domain_id"].toString() == thisDomainID) {
// Entity belongs here. Nothing to do.
return;
}
// Entity does not belong here:
QList<EntityItemID> retained;
for (int i = 0; i < entityIDs.size(); i++) {
EntityItemID entityID = entityIDs.at(i);
EntityItemPointer entity = findEntityByEntityItemID(entityID);
if (!entity) {
qCDebug(entities) << "Entity undergoing dynamic domain verification is no longer available:" << entityID;
continue;
}
if (entity->getAge() <= minimumAgeToRemove) {
qCDebug(entities) << "Entity failed dynamic domain verification, but was created too recently to necessitate deletion:" << entityID;
retained << entityID;
continue;
}
qCDebug(entities) << "Entity's cert's domain ID" << jsonObject["domain_id"].toString()
<< "doesn't match the current Domain ID" << thisDomainID << "; deleting entity" << entityID;
withWriteLock([&] {
deleteEntity(entityID, true);
});
}
{
QWriteLocker entityCertificateIDMapLocker(&_entityCertificateIDMapLock);
if (retained.isEmpty()) {
qCDebug(entities) << "Removed" << certificateID;
_entityCertificateIDMap.remove(certificateID);
} else {
qCDebug(entities) << "Retained" << retained.size() << "young entities for" << certificateID;
_entityCertificateIDMap[certificateID] = retained;
}
}
});
}
}
void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID) {
QTimer* _challengeOwnershipTimeoutTimer = new QTimer(this);
connect(this, &EntityTree::killChallengeOwnershipTimeoutTimer, this, [=](const QString& certID) {
QReadLocker locker(&_entityCertificateIDMapLock);
EntityItemID id = _entityCertificateIDMap.value(certID);
connect(this, &EntityTree::killChallengeOwnershipTimeoutTimer, this, [=](const EntityItemID& id) {
if (entityItemID == id && _challengeOwnershipTimeoutTimer) {
_challengeOwnershipTimeoutTimer->stop();
_challengeOwnershipTimeoutTimer->deleteLater();
@ -1445,26 +1532,21 @@ void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID)
_challengeOwnershipTimeoutTimer->start(5000);
}
QByteArray EntityTree::computeNonce(const QString& certID, const QString ownerKey) {
QByteArray EntityTree::computeNonce(const EntityItemID& entityID, const QString ownerKey) {
QUuid nonce = QUuid::createUuid(); //random, 5-hex value, separated by "-"
QByteArray nonceBytes = nonce.toByteArray();
QWriteLocker locker(&_certNonceMapLock);
_certNonceMap.insert(certID, QPair<QUuid, QString>(nonce, ownerKey));
QWriteLocker locker(&_entityNonceMapLock);
_entityNonceMap.insert(entityID, QPair<QUuid, QString>(nonce, ownerKey));
return nonceBytes;
}
bool EntityTree::verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id) {
{
QReadLocker certIdMapLocker(&_entityCertificateIDMapLock);
id = _entityCertificateIDMap.value(certID);
}
bool EntityTree::verifyNonce(const EntityItemID& entityID, const QString& nonce) {
QString actualNonce, key;
{
QWriteLocker locker(&_certNonceMapLock);
QPair<QUuid, QString> sent = _certNonceMap.take(certID);
QWriteLocker locker(&_entityNonceMapLock);
QPair<QUuid, QString> sent = _entityNonceMap.take(entityID);
actualNonce = sent.first.toString();
key = sent.second;
}
@ -1474,9 +1556,9 @@ bool EntityTree::verifyNonce(const QString& certID, const QString& nonce, Entity
bool verificationSuccess = EntityItemProperties::verifySignature(annotatedKey.toUtf8(), hashedActualNonce, QByteArray::fromBase64(nonce.toUtf8()));
if (verificationSuccess) {
qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded.";
qCDebug(entities) << "Ownership challenge for Entity ID" << entityID << "succeeded.";
} else {
qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed. Actual nonce:" << actualNonce <<
qCDebug(entities) << "Ownership challenge for Entity ID" << entityID << "failed. Actual nonce:" << actualNonce <<
"\nHashed actual nonce (digest):" << hashedActualNonce << "\nSent nonce (signature)" << nonce << "\nKey" << key;
}
@ -1484,42 +1566,42 @@ bool EntityTree::verifyNonce(const QString& certID, const QString& nonce, Entity
}
void EntityTree::processChallengeOwnershipRequestPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) {
int certIDByteArraySize;
int idByteArraySize;
int textByteArraySize;
int nodeToChallengeByteArraySize;
message.readPrimitive(&certIDByteArraySize);
message.readPrimitive(&idByteArraySize);
message.readPrimitive(&textByteArraySize);
message.readPrimitive(&nodeToChallengeByteArraySize);
QByteArray certID(message.read(certIDByteArraySize));
QByteArray id(message.read(idByteArraySize));
QByteArray text(message.read(textByteArraySize));
QByteArray nodeToChallenge(message.read(nodeToChallengeByteArraySize));
sendChallengeOwnershipRequestPacket(certID, text, nodeToChallenge, sourceNode);
sendChallengeOwnershipRequestPacket(id, text, nodeToChallenge, sourceNode);
}
void EntityTree::processChallengeOwnershipReplyPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) {
auto nodeList = DependencyManager::get<NodeList>();
int certIDByteArraySize;
int idByteArraySize;
int textByteArraySize;
int challengingNodeUUIDByteArraySize;
message.readPrimitive(&certIDByteArraySize);
message.readPrimitive(&idByteArraySize);
message.readPrimitive(&textByteArraySize);
message.readPrimitive(&challengingNodeUUIDByteArraySize);
QByteArray certID(message.read(certIDByteArraySize));
QByteArray id(message.read(idByteArraySize));
QByteArray text(message.read(textByteArraySize));
QUuid challengingNode = QUuid::fromRfc4122(message.read(challengingNodeUUIDByteArraySize));
auto challengeOwnershipReplyPacket = NLPacket::create(PacketType::ChallengeOwnershipReply,
certIDByteArraySize + text.length() + 2 * sizeof(int),
idByteArraySize + text.length() + 2 * sizeof(int),
true);
challengeOwnershipReplyPacket->writePrimitive(certIDByteArraySize);
challengeOwnershipReplyPacket->writePrimitive(idByteArraySize);
challengeOwnershipReplyPacket->writePrimitive(text.length());
challengeOwnershipReplyPacket->write(certID);
challengeOwnershipReplyPacket->write(id);
challengeOwnershipReplyPacket->write(text);
nodeList->sendPacket(std::move(challengeOwnershipReplyPacket), *(nodeList->nodeWithUUID(challengingNode)));
@ -1529,7 +1611,7 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri
// 1. Obtain a nonce
auto nodeList = DependencyManager::get<NodeList>();
QByteArray text = computeNonce(certID, ownerKey);
QByteArray text = computeNonce(entityItemID, ownerKey);
if (text == "") {
qCDebug(entities) << "CRITICAL ERROR: Couldn't compute nonce. Deleting entity...";
@ -1539,14 +1621,14 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri
} else {
qCDebug(entities) << "Challenging ownership of Cert ID" << certID;
// 2. Send the nonce to the rezzing avatar's node
QByteArray certIDByteArray = certID.toUtf8();
int certIDByteArraySize = certIDByteArray.size();
QByteArray idByteArray = entityItemID.toByteArray();
int idByteArraySize = idByteArray.size();
auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership,
certIDByteArraySize + text.length() + 2 * sizeof(int),
idByteArraySize + text.length() + 2 * sizeof(int),
true);
challengeOwnershipPacket->writePrimitive(certIDByteArraySize);
challengeOwnershipPacket->writePrimitive(idByteArraySize);
challengeOwnershipPacket->writePrimitive(text.length());
challengeOwnershipPacket->write(certIDByteArray);
challengeOwnershipPacket->write(idByteArray);
challengeOwnershipPacket->write(text);
nodeList->sendPacket(std::move(challengeOwnershipPacket), *senderNode);
@ -1560,24 +1642,24 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri
}
}
void EntityTree::sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode) {
void EntityTree::sendChallengeOwnershipRequestPacket(const QByteArray& id, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode) {
auto nodeList = DependencyManager::get<NodeList>();
// In this case, Client A is challenging Client B. Client A is inspecting a certified entity that it wants
// to make sure belongs to Avatar B.
QByteArray senderNodeUUID = senderNode->getUUID().toRfc4122();
int certIDByteArraySize = certID.length();
int idByteArraySize = id.length();
int TextByteArraySize = text.length();
int senderNodeUUIDSize = senderNodeUUID.length();
auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest,
certIDByteArraySize + TextByteArraySize + senderNodeUUIDSize + 3 * sizeof(int),
idByteArraySize + TextByteArraySize + senderNodeUUIDSize + 3 * sizeof(int),
true);
challengeOwnershipPacket->writePrimitive(certIDByteArraySize);
challengeOwnershipPacket->writePrimitive(idByteArraySize);
challengeOwnershipPacket->writePrimitive(TextByteArraySize);
challengeOwnershipPacket->writePrimitive(senderNodeUUIDSize);
challengeOwnershipPacket->write(certID);
challengeOwnershipPacket->write(id);
challengeOwnershipPacket->write(text);
challengeOwnershipPacket->write(senderNodeUUID);
@ -1636,22 +1718,21 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt
}
void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) {
int certIDByteArraySize;
int idByteArraySize;
int textByteArraySize;
message.readPrimitive(&certIDByteArraySize);
message.readPrimitive(&idByteArraySize);
message.readPrimitive(&textByteArraySize);
QString certID(message.read(certIDByteArraySize));
EntityItemID id(message.read(idByteArraySize));
QString text(message.read(textByteArraySize));
emit killChallengeOwnershipTimeoutTimer(certID);
emit killChallengeOwnershipTimeoutTimer(id);
EntityItemID id;
if (!verifyNonce(certID, text, id)) {
if (!id.isNull()) {
if (!verifyNonce(id, text)) {
withWriteLock([&] {
deleteEntity(id, true);
}
});
}
}

View file

@ -157,11 +157,6 @@ public:
return _recentlyDeletedEntityItemIDs;
}
QHash<QString, EntityItemID> getEntityCertificateIDMap() const {
QReadLocker locker(&_entityCertificateIDMapLock);
return _entityCertificateIDMap;
}
void forgetEntitiesDeletedBefore(quint64 sinceTime);
int processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode);
@ -252,8 +247,8 @@ public:
static const float DEFAULT_MAX_TMP_ENTITY_LIFETIME;
QByteArray computeNonce(const QString& certID, const QString ownerKey);
bool verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id);
QByteArray computeNonce(const EntityItemID& entityID, const QString ownerKey);
bool verifyNonce(const EntityItemID& entityID, const QString& nonce);
QUuid getMyAvatarSessionUUID() { return _myAvatar ? _myAvatar->getSessionUUID() : QUuid(); }
void setMyAvatar(std::shared_ptr<AvatarData> myAvatar) { _myAvatar = myAvatar; }
@ -279,6 +274,7 @@ public:
void updateEntityQueryAACube(SpatiallyNestablePointer object, EntityEditPacketSender* packetSender,
bool force, bool tellServer);
void startDynamicDomainVerificationOnServer(float minimumAgeToRemove);
signals:
void deletingEntity(const EntityItemID& entityID);
@ -290,7 +286,7 @@ signals:
void entityServerScriptChanging(const EntityItemID& entityItemID, const bool reload);
void newCollisionSoundURL(const QUrl& url, const EntityItemID& entityID);
void clearingEntities();
void killChallengeOwnershipTimeoutTimer(const QString& certID);
void killChallengeOwnershipTimeoutTimer(const EntityItemID& certID);
protected:
@ -327,10 +323,10 @@ protected:
QHash<EntityItemID, EntityItemPointer> _entityMap;
mutable QReadWriteLock _entityCertificateIDMapLock;
QHash<QString, EntityItemID> _entityCertificateIDMap;
QHash<QString, QList<EntityItemID>> _entityCertificateIDMap;
mutable QReadWriteLock _certNonceMapLock;
QHash<QString, QPair<QUuid, QString>> _certNonceMap;
mutable QReadWriteLock _entityNonceMapLock;
QHash<EntityItemID, QPair<QUuid, QString>> _entityNonceMap;
EntitySimulationPointer _simulation;
@ -377,8 +373,10 @@ protected:
Q_INVOKABLE void startChallengeOwnershipTimer(const EntityItemID& entityItemID);
private:
void addCertifiedEntityOnServer(EntityItemPointer entity);
void removeCertifiedEntityOnServer(EntityItemPointer entity);
void sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode);
void sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode);
void sendChallengeOwnershipRequestPacket(const QByteArray& id, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode);
void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode);
std::shared_ptr<AvatarData> _myAvatar{ nullptr };