From b35eaf2cc80db190487c5072a76d15bb01f9ff66 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 4 Oct 2017 15:06:55 -0700 Subject: [PATCH] Throw a bunch of code at the wall and hope some sticks --- .../octree/OctreeInboundPacketProcessor.cpp | 4 +- interface/src/commerce/Wallet.cpp | 29 +++-- libraries/entities/src/EntityTree.cpp | 106 ++++++++++++++---- libraries/entities/src/EntityTree.h | 2 + libraries/octree/src/Octree.h | 1 + 5 files changed, 112 insertions(+), 30 deletions(-) diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp index 04409b3b21..2723e8acd5 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp @@ -92,7 +92,9 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer // Ask our tree subclass if it can handle the incoming packet... PacketType packetType = message->getType(); - if (_myServer->getOctree()->handlesEditPacketType(packetType)) { + if (packetType == PacketType::ChallengeOwnership) { + _myServer->getOctree()->processChallengeOwnershipPacket(*message, sendingNode); + } else if (_myServer->getOctree()->handlesEditPacketType(packetType)) { PerformanceWarning warn(debugProcessPacket, "processPacket KNOWN TYPE", debugProcessPacket); _receivedPacketCount++; diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index cc2039da48..93e3f7acda 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -656,16 +656,29 @@ bool Wallet::changePassphrase(const QString& newPassphrase) { void Wallet::handleChallengeOwnershipPacket(QSharedPointer packet, SharedNodePointer sendingNode) { QString decryptedText; - quint64 encryptedTextSize; - quint64 publicKeySize; + int certIDByteArraySize; + int encryptedTextByteArraySize; + int ownerKeyByteArraySize; - if (verifyOwnerChallenge(packet->read(packet->readPrimitive(&encryptedTextSize)), packet->read(packet->readPrimitive(&publicKeySize)), decryptedText)) { + packet->readPrimitive(&certIDByteArraySize); + packet->readPrimitive(&encryptedTextByteArraySize); + packet->readPrimitive(&ownerKeyByteArraySize); + + QByteArray certID = packet->read(certIDByteArraySize); + + if (verifyOwnerChallenge(packet->read(encryptedTextByteArraySize), packet->read(ownerKeyByteArraySize), decryptedText)) { auto nodeList = DependencyManager::get(); - // setup the packet - auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnership, NUM_BYTES_RFC4122_UUID + decryptedText.size(), true); - // write the decrypted text to the packet - decryptedTextPacket->write(decryptedText.toUtf8()); + QByteArray decryptedTextByteArray = decryptedText.toUtf8(); + int decryptedTextByteArraySize = decryptedTextByteArray.size(); + int certIDSize = certID.size(); + // setup the packet + auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnership, certIDSize + decryptedTextByteArraySize + 2*sizeof(int), true); + + decryptedTextPacket->writePrimitive(certIDSize); + decryptedTextPacket->writePrimitive(decryptedTextByteArraySize); + decryptedTextPacket->write(certID); + decryptedTextPacket->write(decryptedTextByteArray); qCDebug(commerce) << "Sending ChallengeOwnership Packet containing decrypted text"; @@ -677,6 +690,6 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack bool Wallet::verifyOwnerChallenge(const QByteArray& encryptedText, const QString& publicKey, QString& decryptedText) { // I have no idea how to do this yet, so here's some dummy code that may not even work. - decryptedText = QString("hello"); + decryptedText = QString("fail"); return true; } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index bb320317c6..29d0e0cc84 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1097,6 +1097,30 @@ bool EntityTree::isScriptInWhitelist(const QString& scriptProperty) { return false; } +void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { + int certIDByteArraySize; + int decryptedTextByteArraySize; + + message.readPrimitive(&certIDByteArraySize); + message.readPrimitive(&decryptedTextByteArraySize); + + QString certID(message.read(certIDByteArraySize)); + QString decryptedText(message.read(decryptedTextByteArraySize)); + + emit killChallengeOwnershipTimeoutTimer(certID); + + if (decryptedText == "fail") { + QReadLocker certIdMapLocker(&_entityCertificateIDMapLock); + EntityItemID id = _entityCertificateIDMap.value(certID); + + qCDebug(entities) << "Ownership challenge failed, deleting entity" << id; + deleteEntity(id, true); + QWriteLocker recentlyDeletedLocker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), id); + } else { + } +} + int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode) { @@ -1299,6 +1323,8 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c << "static certificate verification."; // Delete the entity we just added if it doesn't pass static certificate verification deleteEntity(entityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); } else { QString certID = properties.getCertificateID(); EntityItemID existingEntityItemID; @@ -1314,6 +1340,8 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c qCDebug(entities) << "Certificate ID" << certID << "already exists on entity with ID" << existingEntityItemID << ". Deleting existing entity."; deleteEntity(existingEntityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), existingEntityItemID); } { @@ -1323,6 +1351,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c } // Start owner verification. + auto nodeList = DependencyManager::get(); // First, asynchronously hit "proof_of_purchase_status?transaction_type=transfer" endpoint. QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance(); QNetworkRequest networkRequest; @@ -1331,7 +1360,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c requestURL.setPath("/api/v1/commerce/proof_of_purchase_status?transaction_type=transfer"); QJsonObject request; request["certificate_id"] = certID; - request["domain_id"] = DependencyManager::get()->getDomainHandler().getUUID().toString(); + request["domain_id"] = nodeList->getDomainHandler().getUUID().toString(); networkRequest.setUrl(requestURL); QNetworkReply* networkReply = NULL; @@ -1340,33 +1369,68 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c connect(networkReply, &QNetworkReply::finished, [=]() { QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); QJsonDocument doc(jsonObject); - qCDebug(entities) << "ZRF FIXME" << networkReply->error(); qCDebug(entities) << "ZRF FIXME" << doc.toJson(QJsonDocument::Compact); if (networkReply->error() == QNetworkReply::NoError) { - //QJsonObject jsonObject = QJsonDocument::fromJson(reply.readAll()).object(); - //qCDebug(entities) << "ZRF FIXME" << jsonObject; - //if (!jsonObject["invalid_reason"].toString().isEmpty()) { - // qCDebug(entities) << "invalid_reason not empty, deleting entity" << entityItemID; - // deleteEntity(entityItemID, true); - //} else if (jsonObject["transfer_status"].toString() == "failed") { - // qCDebug(entities) << "'transfer_status' is 'failed', deleting entity" << entityItemID; - // deleteEntity(entityItemID, true); - //} else { - // // Second, challenge ownership of the PoP cert - // // 1. Encrypt a nonce with the owner's public key - // QString ownerKey(jsonObject["owner_public_key"].toString()); - // QString encryptedText(""); + if (!jsonObject["invalid_reason"].toString().isEmpty()) { + qCDebug(entities) << "invalid_reason not empty, deleting entity" << entityItemID; + deleteEntity(entityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + } else if (jsonObject["transfer_status"].toString() == "failed") { + qCDebug(entities) << "'transfer_status' is 'failed', deleting entity" << entityItemID; + deleteEntity(entityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + } else { + // Second, challenge ownership of the PoP cert + // 1. Encrypt a nonce with the owner's public key + QString ownerKey(jsonObject["owner_public_key"].toString()); + QString encryptedText("test"); - // // 2. Send the encrypted text to the rezzing avatar's node - // auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership, NUM_BYTES_RFC4122_UUID + sizeof(encryptedText)); - // challengeOwnershipPacket->write(senderNode->getUUID()); - // challengeOwnershipPacket->writePrimitive(encryptedText); - // // 3. Kickoff a 10-second timeout timer that deletes the entity if we don't get an ownership response in time - //} + // 2. Send the encrypted text to the rezzing avatar's node + QByteArray certIDByteArray = certID.toUtf8(); + int certIDByteArraySize = certIDByteArray.size(); + QByteArray encryptedTextByteArray = encryptedText.toUtf8(); + int encryptedTextByteArraySize = encryptedTextByteArray.size(); + QByteArray ownerKeyByteArray = ownerKey.toUtf8(); + int ownerKeyByteArraySize = ownerKeyByteArray.size(); + auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership, + certIDByteArraySize + encryptedTextByteArraySize + ownerKeyByteArraySize + 3*sizeof(int), + true); + challengeOwnershipPacket->writePrimitive(certIDByteArraySize); + challengeOwnershipPacket->writePrimitive(encryptedTextByteArraySize); + challengeOwnershipPacket->writePrimitive(ownerKeyByteArraySize); + challengeOwnershipPacket->write(certIDByteArray); + challengeOwnershipPacket->write(encryptedTextByteArray); + challengeOwnershipPacket->write(ownerKeyByteArray); + nodeList->sendPacket(std::move(challengeOwnershipPacket), *senderNode); + + // 3. Kickoff a 10-second timeout timer that deletes the entity if we don't get an ownership response in time + QTimer* challengeOwnershipTimeoutTimer = new QTimer(this); + connect(this, &EntityTree::killChallengeOwnershipTimeoutTimer, this, [&](const QString& certID) { + QReadLocker locker(&_entityCertificateIDMapLock); + EntityItemID id = _entityCertificateIDMap.value(certID); + if (entityItemID == id) { + challengeOwnershipTimeoutTimer->stop(); + challengeOwnershipTimeoutTimer->deleteLater(); + } + }); + connect(challengeOwnershipTimeoutTimer, &QTimer::timeout, this, [=]() { + qCDebug(entities) << "Ownership challenge timed out, deleting entity" << entityItemID; + deleteEntity(entityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); + }); + challengeOwnershipTimeoutTimer->setSingleShot(false); + challengeOwnershipTimeoutTimer->setInterval(10000); + challengeOwnershipTimeoutTimer->start(); + } } else { qCDebug(entities) << "Call to proof_of_purchase_status endpoint failed; deleting entity" << entityItemID; deleteEntity(entityItemID, true); + QWriteLocker locker(&_recentlyDeletedEntitiesLock); + _recentlyDeletedEntityItemIDs.insert(usecTimestampNow(), entityItemID); } networkReply->deleteLater(); diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index da202c0df9..68f2c605af 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -100,6 +100,7 @@ public: void fixupTerseEditLogging(EntityItemProperties& properties, QList& changedProperties); virtual int processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode) override; + virtual void processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) override; virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, QVector entityIdsToInclude, QVector entityIdsToDiscard, @@ -281,6 +282,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); protected: diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index a2df5f44e5..250577f351 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -214,6 +214,7 @@ public: virtual bool handlesEditPacketType(PacketType packetType) const { return false; } virtual int processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength, const SharedNodePointer& sourceNode) { return 0; } + virtual void processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { return; } virtual bool recurseChildrenWithData() const { return true; } virtual bool rootElementHasData() const { return false; }