From 9345f4545f29cb24112bd76a012142a479769391 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 25 Oct 2017 13:35:42 -0700 Subject: [PATCH 01/23] sanitize the ice-server addresses before using --- domain-server/src/DomainServer.cpp | 37 +++++++++++------------------- domain-server/src/DomainServer.h | 2 -- 2 files changed, 14 insertions(+), 25 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 436f49c7ca..46eda163d8 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -754,26 +754,6 @@ void DomainServer::setupICEHeartbeatForFullNetworking() { void DomainServer::updateICEServerAddresses() { if (_iceAddressLookupID == INVALID_ICE_LOOKUP_ID) { _iceAddressLookupID = QHostInfo::lookupHost(_iceServerAddr, this, SLOT(handleICEHostInfo(QHostInfo))); - - // there seems to be a 5.9 bug where lookupHost never calls our slot - // so we add a single shot manual "timeout" to fire it off again if it hasn't called back yet - static const int ICE_ADDRESS_LOOKUP_TIMEOUT_MS = 5000; - QTimer::singleShot(ICE_ADDRESS_LOOKUP_TIMEOUT_MS, this, &DomainServer::timeoutICEAddressLookup); - } -} - -void DomainServer::timeoutICEAddressLookup() { - if (_iceAddressLookupID != INVALID_ICE_LOOKUP_ID) { - // we waited 5s and didn't hear back for our ICE DNS lookup - // so time that one out and kick off another - - qDebug() << "IP address lookup timed out for" << _iceServerAddr << "- retrying"; - - QHostInfo::abortHostLookup(_iceAddressLookupID); - - _iceAddressLookupID = INVALID_ICE_LOOKUP_ID; - - updateICEServerAddresses(); } } @@ -2799,9 +2779,20 @@ void DomainServer::handleKeypairChange() { void DomainServer::handleICEHostInfo(const QHostInfo& hostInfo) { // clear the ICE address lookup ID so that it can fire again - _iceAddressLookupID = -1; + _iceAddressLookupID = INVALID_ICE_LOOKUP_ID; - if (hostInfo.error() != QHostInfo::NoError) { + // enumerate the returned addresses and collect only valid IPv4 addresses + QList sanitizedAddresses = hostInfo.addresses(); + auto it = sanitizedAddresses.begin(); + while (it != sanitizedAddresses.end()) { + if (!it->isNull() && it->protocol() == QAbstractSocket::IPv4Protocol) { + ++it; + } else { + it = sanitizedAddresses.erase(it); + } + } + + if (hostInfo.error() != QHostInfo::NoError || sanitizedAddresses.empty()) { qWarning() << "IP address lookup failed for" << _iceServerAddr << ":" << hostInfo.errorString(); // if we don't have an ICE server to use yet, trigger a retry @@ -2814,7 +2805,7 @@ void DomainServer::handleICEHostInfo(const QHostInfo& hostInfo) { } else { int countBefore = _iceServerAddresses.count(); - _iceServerAddresses = hostInfo.addresses(); + _iceServerAddresses = sanitizedAddresses; if (countBefore == 0) { qInfo() << "Found" << _iceServerAddresses.count() << "ice-server IP addresses for" << _iceServerAddr; diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 52ac435517..982d39d046 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -116,8 +116,6 @@ private slots: void tokenGrantFinished(); void profileRequestFinished(); - void timeoutICEAddressLookup(); - signals: void iceServerChanged(); void userConnected(); From 60fbbe4bc0e4f725353202650b93d7f44675a6c1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 25 Oct 2017 13:12:53 -0700 Subject: [PATCH 02/23] disable baking of JS files --- assignment-client/src/assets/AssetServer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/assets/AssetServer.cpp b/assignment-client/src/assets/AssetServer.cpp index 9c03bdd3bb..d13e031c03 100644 --- a/assignment-client/src/assets/AssetServer.cpp +++ b/assignment-client/src/assets/AssetServer.cpp @@ -50,9 +50,9 @@ static const int INTERFACE_RUNNING_CHECK_FREQUENCY_MS = 1000; const QString ASSET_SERVER_LOGGING_TARGET_NAME = "asset-server"; -static const QStringList BAKEABLE_MODEL_EXTENSIONS = {"fbx"}; +static const QStringList BAKEABLE_MODEL_EXTENSIONS = { "fbx" }; static QStringList BAKEABLE_TEXTURE_EXTENSIONS; -static const QStringList BAKEABLE_SCRIPT_EXTENSIONS = {"js"}; +static const QStringList BAKEABLE_SCRIPT_EXTENSIONS = {}; static const QString BAKED_MODEL_SIMPLE_NAME = "asset.fbx"; static const QString BAKED_TEXTURE_SIMPLE_NAME = "texture.ktx"; static const QString BAKED_SCRIPT_SIMPLE_NAME = "asset.js"; From e450ac5bd3b5fd13357b85a1d31ded73aa9dbe7a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 25 Oct 2017 15:28:30 -0700 Subject: [PATCH 03/23] don't use COW-less SpecialAddress assignment --- libraries/networking/src/HifiSockAddr.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/HifiSockAddr.h b/libraries/networking/src/HifiSockAddr.h index b582198139..3c753f0434 100644 --- a/libraries/networking/src/HifiSockAddr.h +++ b/libraries/networking/src/HifiSockAddr.h @@ -34,7 +34,7 @@ public: HifiSockAddr(const sockaddr* sockaddr); bool isNull() const { return _address.isNull() && _port == 0; } - void clear() { _address = QHostAddress::Null; _port = 0;} + void clear() { _address.clear(); _port = 0;} HifiSockAddr& operator=(const HifiSockAddr& rhsSockAddr); void swap(HifiSockAddr& otherSockAddr); From 19420a0ff26b691ee7e0eeb2e1a734302c244be8 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 30 Oct 2017 17:01:55 -0700 Subject: [PATCH 04/23] Move static cert verify to properties --- .../src/entities/EntityServer.cpp | 2 +- libraries/entities/src/EntityItem.cpp | 114 ----------------- libraries/entities/src/EntityItem.h | 3 - .../entities/src/EntityItemProperties.cpp | 116 ++++++++++++++++++ libraries/entities/src/EntityItemProperties.h | 4 + .../entities/src/EntityScriptingInterface.cpp | 2 +- libraries/entities/src/EntityTree.cpp | 2 +- 7 files changed, 123 insertions(+), 120 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index fa9c73b12d..702ca88787 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -459,7 +459,7 @@ void EntityServer::startDynamicDomainVerification() { EntityItemPointer entity = tree->findEntityByEntityItemID(i.value()); if (entity) { - if (!entity->verifyStaticCertificateProperties()) { + if (!entity->getProperties().verifyStaticCertificateProperties()) { qCDebug(entities) << "During Dynamic Domain Verification, a certified entity with ID" << i.value() << "failed" << "static certificate verification."; // Delete the entity if it doesn't pass static certificate verification diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 4f5db991c8..c355ce90fc 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -14,10 +14,6 @@ #include #include #include -#include -#include -#include -#include #include #include #include @@ -1575,116 +1571,6 @@ float EntityItem::getRadius() const { return 0.5f * glm::length(getDimensions()); } -// Checking Certifiable Properties -#define ADD_STRING_PROPERTY(n, N) if (!propertySet.get##N().isEmpty()) json[#n] = propertySet.get##N() -#define ADD_ENUM_PROPERTY(n, N) json[#n] = propertySet.get##N##AsString() -#define ADD_INT_PROPERTY(n, N) if (propertySet.get##N() != 0) json[#n] = (propertySet.get##N() == (quint32) -1) ? -1.0 : ((double) propertySet.get##N()) -QByteArray EntityItem::getStaticCertificateJSON() const { - // Produce a compact json of every non-default static certificate property, with the property names in alphabetical order. - // The static certificate properties include all an only those properties that cannot be changed without altering the identity - // of the entity as reviewed during the certification submission. - - QJsonObject json; - EntityItemProperties propertySet = getProperties(); // Note: neither EntityItem nor EntityitemProperties "properties" are QObject "properties"! - // It is important that this be reproducible in the same order each time. Since we also generate these on the server, we do it alphabetically - // to help maintainence in two different code bases. - if (!propertySet.getAnimation().getURL().isEmpty()) { - json["animationURL"] = propertySet.getAnimation().getURL(); - } - ADD_STRING_PROPERTY(collisionSoundURL, CollisionSoundURL); - ADD_STRING_PROPERTY(compoundShapeURL, CompoundShapeURL); - ADD_INT_PROPERTY(editionNumber, EditionNumber); - ADD_INT_PROPERTY(instanceNumber, EntityInstanceNumber); - ADD_STRING_PROPERTY(itemArtist, ItemArtist); - ADD_STRING_PROPERTY(itemCategories, ItemCategories); - ADD_STRING_PROPERTY(itemDescription, ItemDescription); - ADD_STRING_PROPERTY(itemLicenseUrl, ItemLicense); - ADD_STRING_PROPERTY(itemName, ItemName); - ADD_INT_PROPERTY(limitedRun, LimitedRun); - ADD_STRING_PROPERTY(marketplaceID, MarketplaceID); - ADD_STRING_PROPERTY(modelURL, ModelURL); - ADD_STRING_PROPERTY(script, Script); - ADD_ENUM_PROPERTY(shapeType, ShapeType); - json["type"] = EntityTypes::getEntityTypeName(propertySet.getType()); - - return QJsonDocument(json).toJson(QJsonDocument::Compact); -} -QByteArray EntityItem::getStaticCertificateHash() const { - return QCryptographicHash::hash(getStaticCertificateJSON(), QCryptographicHash::Sha256); -} - -bool EntityItem::verifyStaticCertificateProperties() { - // True IIF a non-empty certificateID matches the static certificate json. - // I.e., if we can verify that the certificateID was produced by High Fidelity signing the static certificate hash. - - if (getCertificateID().isEmpty()) { - return false; - } - - const QByteArray marketplacePublicKeyByteArray = EntityItem::_marketplacePublicKey.toUtf8(); - const unsigned char* marketplacePublicKey = reinterpret_cast(marketplacePublicKeyByteArray.constData()); - int marketplacePublicKeyLength = marketplacePublicKeyByteArray.length(); - - BIO *bio = BIO_new_mem_buf((void*)marketplacePublicKey, marketplacePublicKeyLength); - EVP_PKEY* evp_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); - if (evp_key) { - RSA* rsa = EVP_PKEY_get1_RSA(evp_key); - if (rsa) { - const QByteArray digestByteArray = getStaticCertificateHash(); - const unsigned char* digest = reinterpret_cast(digestByteArray.constData()); - int digestLength = digestByteArray.length(); - - const QByteArray signatureByteArray = QByteArray::fromBase64(getCertificateID().toUtf8()); - const unsigned char* signature = reinterpret_cast(signatureByteArray.constData()); - int signatureLength = signatureByteArray.length(); - - ERR_clear_error(); - bool answer = RSA_verify(NID_sha256, - digest, - digestLength, - signature, - signatureLength, - rsa); - long error = ERR_get_error(); - if (error != 0) { - const char* error_str = ERR_error_string(error, NULL); - qCWarning(entities) << "ERROR while verifying static certificate properties! RSA error:" << error_str - << "\nStatic Cert JSON:" << getStaticCertificateJSON() - << "\nKey:" << EntityItem::_marketplacePublicKey << "\nKey Length:" << marketplacePublicKeyLength - << "\nDigest:" << digest << "\nDigest Length:" << digestLength - << "\nSignature:" << signature << "\nSignature Length:" << signatureLength; - } - RSA_free(rsa); - if (bio) { - BIO_free(bio); - } - if (evp_key) { - EVP_PKEY_free(evp_key); - } - return answer; - } else { - if (bio) { - BIO_free(bio); - } - if (evp_key) { - EVP_PKEY_free(evp_key); - } - long error = ERR_get_error(); - const char* error_str = ERR_error_string(error, NULL); - qCWarning(entities) << "Failed to verify static certificate properties! RSA error:" << error_str; - return false; - } - } else { - if (bio) { - BIO_free(bio); - } - long error = ERR_get_error(); - const char* error_str = ERR_error_string(error, NULL); - qCWarning(entities) << "Failed to verify static certificate properties! RSA error:" << error_str; - return false; - } -} - void EntityItem::adjustShapeInfoByRegistration(ShapeInfo& info) const { if (_registrationPoint != ENTITY_ITEM_DEFAULT_REGISTRATION_POINT) { glm::mat4 scale = glm::scale(getDimensions()); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 79862b9bd2..76a5205960 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -328,9 +328,6 @@ public: void setEntityInstanceNumber(const quint32&); QString getCertificateID() const; void setCertificateID(const QString& value); - QByteArray getStaticCertificateJSON() const; - QByteArray getStaticCertificateHash() const; - bool verifyStaticCertificateProperties(); // TODO: get rid of users of getRadius()... float getRadius() const; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 3bbd6ce8e6..f774b208c4 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -14,6 +14,15 @@ #include #include +#include +#include +#include +#include +#include +#include +#include +#include + #include #include #include @@ -2471,3 +2480,110 @@ bool EntityItemProperties::parentRelatedPropertyChanged() const { bool EntityItemProperties::queryAACubeRelatedPropertyChanged() const { return parentRelatedPropertyChanged() || dimensionsChanged(); } + +// Checking Certifiable Properties +#define ADD_STRING_PROPERTY(n, N) if (!get##N().isEmpty()) json[#n] = get##N() +#define ADD_ENUM_PROPERTY(n, N) json[#n] = get##N##AsString() +#define ADD_INT_PROPERTY(n, N) if (get##N() != 0) json[#n] = (get##N() == (quint32) -1) ? -1.0 : ((double) get##N()) +QByteArray EntityItemProperties::getStaticCertificateJSON() const { + // Produce a compact json of every non-default static certificate property, with the property names in alphabetical order. + // The static certificate properties include all an only those properties that cannot be changed without altering the identity + // of the entity as reviewed during the certification submission. + + QJsonObject json; + if (!getAnimation().getURL().isEmpty()) { + json["animationURL"] = getAnimation().getURL(); + } + ADD_STRING_PROPERTY(collisionSoundURL, CollisionSoundURL); + ADD_STRING_PROPERTY(compoundShapeURL, CompoundShapeURL); + ADD_INT_PROPERTY(editionNumber, EditionNumber); + ADD_INT_PROPERTY(instanceNumber, EntityInstanceNumber); + ADD_STRING_PROPERTY(itemArtist, ItemArtist); + ADD_STRING_PROPERTY(itemCategories, ItemCategories); + ADD_STRING_PROPERTY(itemDescription, ItemDescription); + ADD_STRING_PROPERTY(itemLicenseUrl, ItemLicense); + ADD_STRING_PROPERTY(itemName, ItemName); + ADD_INT_PROPERTY(limitedRun, LimitedRun); + ADD_STRING_PROPERTY(marketplaceID, MarketplaceID); + ADD_STRING_PROPERTY(modelURL, ModelURL); + ADD_STRING_PROPERTY(script, Script); + ADD_ENUM_PROPERTY(shapeType, ShapeType); + json["type"] = EntityTypes::getEntityTypeName(getType()); + + return QJsonDocument(json).toJson(QJsonDocument::Compact); +} +QByteArray EntityItemProperties::getStaticCertificateHash() const { + return QCryptographicHash::hash(getStaticCertificateJSON(), QCryptographicHash::Sha256); +} + +bool EntityItemProperties::verifyStaticCertificateProperties() { + // True IIF a non-empty certificateID matches the static certificate json. + // I.e., if we can verify that the certificateID was produced by High Fidelity signing the static certificate hash. + + if (getCertificateID().isEmpty()) { + return false; + } + + const QByteArray marketplacePublicKeyByteArray = EntityItem::_marketplacePublicKey.toUtf8(); + const unsigned char* marketplacePublicKey = reinterpret_cast(marketplacePublicKeyByteArray.constData()); + int marketplacePublicKeyLength = marketplacePublicKeyByteArray.length(); + + BIO *bio = BIO_new_mem_buf((void*)marketplacePublicKey, marketplacePublicKeyLength); + EVP_PKEY* evp_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); + if (evp_key) { + RSA* rsa = EVP_PKEY_get1_RSA(evp_key); + if (rsa) { + const QByteArray digestByteArray = getStaticCertificateHash(); + const unsigned char* digest = reinterpret_cast(digestByteArray.constData()); + int digestLength = digestByteArray.length(); + + const QByteArray signatureByteArray = QByteArray::fromBase64(getCertificateID().toUtf8()); + const unsigned char* signature = reinterpret_cast(signatureByteArray.constData()); + int signatureLength = signatureByteArray.length(); + + ERR_clear_error(); + bool answer = RSA_verify(NID_sha256, + digest, + digestLength, + signature, + signatureLength, + rsa); + long error = ERR_get_error(); + if (error != 0) { + const char* error_str = ERR_error_string(error, NULL); + qCWarning(entities) << "ERROR while verifying static certificate properties! RSA error:" << error_str + << "\nStatic Cert JSON:" << getStaticCertificateJSON() + << "\nKey:" << EntityItem::_marketplacePublicKey << "\nKey Length:" << marketplacePublicKeyLength + << "\nDigest:" << digest << "\nDigest Length:" << digestLength + << "\nSignature:" << signature << "\nSignature Length:" << signatureLength; + } + RSA_free(rsa); + if (bio) { + BIO_free(bio); + } + if (evp_key) { + EVP_PKEY_free(evp_key); + } + return answer; + } else { + if (bio) { + BIO_free(bio); + } + if (evp_key) { + EVP_PKEY_free(evp_key); + } + long error = ERR_get_error(); + const char* error_str = ERR_error_string(error, NULL); + qCWarning(entities) << "Failed to verify static certificate properties! RSA error:" << error_str; + return false; + } + } else { + if (bio) { + BIO_free(bio); + } + long error = ERR_get_error(); + const char* error_str = ERR_error_string(error, NULL); + qCWarning(entities) << "Failed to verify static certificate properties! RSA error:" << error_str; + return false; + } +} diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 6547026e5c..a8bb063934 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -336,6 +336,10 @@ public: QByteArray getPackedStrokeColors() const; QByteArray packStrokeColors(const QVector& strokeColors) const; + QByteArray getStaticCertificateJSON() const; + QByteArray getStaticCertificateHash() const; + bool verifyStaticCertificateProperties(); + protected: QString getCollisionMaskAsString() const; void setCollisionMaskFromString(const QString& maskString); diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index c83a5f60a1..998a2d4dfe 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -1827,7 +1827,7 @@ bool EntityScriptingInterface::verifyStaticCertificateProperties(const QUuid& en _entityTree->withReadLock([&] { EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(entityID)); if (entity) { - result = entity->verifyStaticCertificateProperties(); + result = entity->getProperties().verifyStaticCertificateProperties(); } }); } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 9397f38cdd..22348360ed 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1528,7 +1528,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c _totalCreates++; if (newEntity && isCertified && getIsServer()) { - if (!newEntity->verifyStaticCertificateProperties()) { + if (!properties.verifyStaticCertificateProperties()) { qCDebug(entities) << "User" << senderNode->getUUID() << "attempted to add a certified entity with ID" << entityItemID << "which failed" << "static certificate verification."; From 90e92511767a5bbe3f248afd4d474e423bac5791 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 31 Oct 2017 11:08:02 -0700 Subject: [PATCH 05/23] It's a start --- .../octree/OctreeInboundPacketProcessor.cpp | 4 + .../ui/overlays/ContextOverlayInterface.cpp | 37 +++++++ .../src/ui/overlays/ContextOverlayInterface.h | 2 +- libraries/entities/src/EntityTree.cpp | 101 +++++++++++++----- libraries/entities/src/EntityTree.h | 2 + libraries/networking/src/udt/PacketHeaders.h | 2 + libraries/octree/src/Octree.h | 1 + 7 files changed, 119 insertions(+), 30 deletions(-) diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp index 3f835678ac..50c95ffc5d 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp @@ -96,6 +96,10 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer _myServer->getOctree()->withWriteLock([&] { _myServer->getOctree()->processChallengeOwnershipPacket(*message, sendingNode); }); + } else if (packetType == PacketType::ChallengeOwnershipRequest) { + _myServer->getOctree()->withWriteLock([&] { + _myServer->getOctree()->processChallengeOwnershipPacket(*message, sendingNode); + }); } else if (_myServer->getOctree()->handlesEditPacketType(packetType)) { PerformanceWarning warn(debugProcessPacket, "processPacket KNOWN TYPE", debugProcessPacket); _receivedPacketCount++; diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 70b75a0b17..402e72f2cf 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -42,6 +42,8 @@ ContextOverlayInterface::ContextOverlayInterface() { _entityPropertyFlags += PROP_DIMENSIONS; _entityPropertyFlags += PROP_REGISTRATION_POINT; _entityPropertyFlags += PROP_CERTIFICATE_ID; + _entityPropertyFlags += PROP_CLIENT_ONLY; + _entityPropertyFlags += PROP_OWNING_AVATAR_ID; auto entityScriptingInterface = DependencyManager::get().data(); connect(entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity, this, &ContextOverlayInterface::createOrDestroyContextOverlay); @@ -260,6 +262,41 @@ void ContextOverlayInterface::openInspectionCertificate() { auto tablet = dynamic_cast(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system")); tablet->loadQMLSource(INSPECTION_CERTIFICATE_QML_PATH); _hmdScriptingInterface->openTablet(); + + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_currentEntityWithContextOverlay, _entityPropertyFlags); + + QUuid nodeToChallenge = entityProperties.getOwningAvatarID(); + auto nodeList = DependencyManager::get(); + + qDebug() << "ZRF FIXME" << entityProperties.getClientOnly() << nodeToChallenge << nodeList->getSessionUUID(); + + // Don't challenge ownership of avatar entities that I own + if (entityProperties.getClientOnly() && nodeToChallenge != nodeList->getSessionUUID()) { + SharedNodePointer entityServer = nodeList->soloNodeOfType(NodeType::EntityServer); + + if (entityServer) { + QByteArray certID = entityProperties.getCertificateID().toUtf8(); + QByteArray ownerKey; // ZRF FIXME! + QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122(); + + int certIDByteArraySize = certID.length(); + int ownerKeyByteArraySize = ownerKey.length(); + int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length(); + + auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, + certIDByteArraySize + ownerKeyByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int), + true); + challengeOwnershipPacket->writePrimitive(certIDByteArraySize); + challengeOwnershipPacket->writePrimitive(ownerKeyByteArraySize); + challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize); + challengeOwnershipPacket->write(certID); + challengeOwnershipPacket->write(ownerKey); + challengeOwnershipPacket->write(nodeToChallengeByteArray); + nodeList->sendPacket(std::move(challengeOwnershipPacket), *entityServer); + } else { + qCWarning(context_overlay) << "Couldn't get Entity Server!"; + } + } } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index b4d3ddc0c2..a063fdde23 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -73,7 +73,7 @@ public slots: private: bool _verboseLogging { true }; bool _enabled { true }; - QUuid _currentEntityWithContextOverlay{}; + EntityItemID _currentEntityWithContextOverlay{}; QString _entityMarketplaceID; bool _contextOverlayJustClicked { false }; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 22348360ed..90ee1190d5 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1153,8 +1153,8 @@ void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID) _challengeOwnershipTimeoutTimer->deleteLater(); } }); - _challengeOwnershipTimeoutTimer->setSingleShot(true); - _challengeOwnershipTimeoutTimer->start(5000); +_challengeOwnershipTimeoutTimer->setSingleShot(true); +_challengeOwnershipTimeoutTimer->start(5000); } void EntityTree::startPendingTransferStatusTimer(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode) { @@ -1225,7 +1225,7 @@ bool EntityTree::verifyDecryptedNonce(const QString& certID, const QString& decr if (!id.isNull()) { qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed; deleting entity" << id << "\nActual nonce:" << actualNonce << "\nDecrypted nonce:" << decryptedNonce; - deleteEntity(id, true); + deleteEntity(id, true); } } else { qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded; keeping entity" << id; @@ -1234,6 +1234,71 @@ bool EntityTree::verifyDecryptedNonce(const QString& certID, const QString& decr return verificationSuccess; } +void EntityTree::processChallengeOwnershipRequestPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { + int certIDByteArraySize; + int ownerKeyByteArraySize; + int nodeToChallengeByteArraySize; + + message.readPrimitive(&certIDByteArraySize); + message.readPrimitive(&ownerKeyByteArraySize); + message.readPrimitive(&nodeToChallengeByteArraySize); + + QString certID(message.read(certIDByteArraySize)); + QString ownerKey(message.read(ownerKeyByteArraySize)); + QUuid nodeToChallenge = QUuid::fromRfc4122(message.read(nodeToChallengeByteArraySize)); + + sendChallengeOwnershipPacket(certID, ownerKey, EntityItemID(), sourceNode, nodeToChallenge); +} + +void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, const QUuid& nodeToChallenge) { + // 1. Encrypt a nonce with the owner's public key + auto nodeList = DependencyManager::get(); + QByteArray encryptedText = computeEncryptedNonce(certID, ownerKey); + + if (encryptedText == "") { + qCDebug(entities) << "CRITICAL ERROR: Couldn't compute encrypted nonce."; + } else { + // In this case, the server is challenging a client. That client has just rezzed a new certified entity. + if (nodeToChallenge.isNull()) { + // 2. Send the encrypted text to the rezzing avatar's node + QByteArray certIDByteArray = certID.toUtf8(); + int certIDByteArraySize = certIDByteArray.size(); + auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership, + certIDByteArraySize + encryptedText.length() + 2 * sizeof(int), + true); + challengeOwnershipPacket->writePrimitive(certIDByteArraySize); + challengeOwnershipPacket->writePrimitive(encryptedText.length()); + challengeOwnershipPacket->write(certIDByteArray); + challengeOwnershipPacket->write(encryptedText); + 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 + if (thread() != QThread::currentThread()) { + QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer", Q_ARG(const EntityItemID&, entityItemID)); + return; + } else { + startChallengeOwnershipTimer(entityItemID); + } + // 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. + } else { + QByteArray senderNodeUUID = senderNode->getUUID().toRfc4122(); + QByteArray certIDByteArray = certID.toUtf8(); + int certIDByteArraySize = certIDByteArray.size(); + auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, + certIDByteArraySize + encryptedText.length() + senderNodeUUID.length() + 3 * sizeof(int), + true); + challengeOwnershipPacket->writePrimitive(certIDByteArraySize); + challengeOwnershipPacket->writePrimitive(encryptedText.length()); + challengeOwnershipPacket->writePrimitive(senderNodeUUID.length()); + challengeOwnershipPacket->write(certIDByteArray); + challengeOwnershipPacket->write(encryptedText); + challengeOwnershipPacket->write(senderNodeUUID); + nodeList->sendPacket(std::move(challengeOwnershipPacket), *(nodeList->nodeWithUUID(nodeToChallenge))); + } + } +} + void EntityTree::validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation) { // Start owner verification. auto nodeList = DependencyManager::get(); @@ -1279,33 +1344,11 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt } } else { // Second, challenge ownership of the PoP cert - // 1. Encrypt a nonce with the owner's public key - QByteArray encryptedText = computeEncryptedNonce(certID, jsonObject["transfer_recipient_key"].toString()); + sendChallengeOwnershipPacket(certID, + jsonObject["transfer_recipient_key"].toString(), + entityItemID, + senderNode); - if (encryptedText == "") { - qCDebug(entities) << "CRITICAL ERROR: Couldn't compute encrypted nonce. Deleting entity..."; - deleteEntity(entityItemID, true); - } else { - // 2. Send the encrypted text to the rezzing avatar's node - QByteArray certIDByteArray = certID.toUtf8(); - int certIDByteArraySize = certIDByteArray.size(); - auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership, - certIDByteArraySize + encryptedText.length() + 2 * sizeof(int), - true); - challengeOwnershipPacket->writePrimitive(certIDByteArraySize); - challengeOwnershipPacket->writePrimitive(encryptedText.length()); - challengeOwnershipPacket->write(certIDByteArray); - challengeOwnershipPacket->write(encryptedText); - 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 - if (thread() != QThread::currentThread()) { - QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer", Q_ARG(const EntityItemID&, entityItemID)); - return; - } else { - startChallengeOwnershipTimer(entityItemID); - } - } } } else { qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << "; deleting entity" << entityItemID diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 518cde9a59..3c11557dac 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -93,6 +93,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 processChallengeOwnershipRequestPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) override; virtual void processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) override; virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, @@ -377,6 +378,7 @@ protected: private: QByteArray computeEncryptedNonce(const QString& certID, const QString ownerKey); bool verifyDecryptedNonce(const QString& certID, const QString& decryptedNonce); + void sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, const QUuid& nodeToChallenge = NULL); void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation); }; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 049cb0f1a8..8d48b106e4 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -124,6 +124,8 @@ public: OctreeFileReplacementFromUrl, ChallengeOwnership, EntityScriptCallMethod, + ChallengeOwnershipRequest, + ChallengeOwnershipReply, NUM_PACKET_TYPE }; diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index 2761dffb1b..fe8868b342 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -212,6 +212,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 processChallengeOwnershipRequestPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { return; } virtual void processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { return; } virtual bool recurseChildrenWithData() const { return true; } From 6f96e0c7bd30623c33add2fbaebfad6e8f4ea6e3 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 31 Oct 2017 12:14:12 -0700 Subject: [PATCH 06/23] More progress --- .../octree/OctreeInboundPacketProcessor.cpp | 6 +- interface/src/commerce/Wallet.cpp | 41 +++++-- interface/src/commerce/Wallet.h | 1 + .../ui/overlays/ContextOverlayInterface.cpp | 85 +++++++++---- libraries/entities/src/EntityTree.cpp | 112 ++++++++++++------ libraries/entities/src/EntityTree.h | 9 +- libraries/octree/src/Octree.h | 1 + 7 files changed, 186 insertions(+), 69 deletions(-) diff --git a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp index 50c95ffc5d..bce6e7fe44 100644 --- a/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp +++ b/assignment-client/src/octree/OctreeInboundPacketProcessor.cpp @@ -98,7 +98,11 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer }); } else if (packetType == PacketType::ChallengeOwnershipRequest) { _myServer->getOctree()->withWriteLock([&] { - _myServer->getOctree()->processChallengeOwnershipPacket(*message, sendingNode); + _myServer->getOctree()->processChallengeOwnershipRequestPacket(*message, sendingNode); + }); + } else if (packetType == PacketType::ChallengeOwnershipReply) { + _myServer->getOctree()->withWriteLock([&] { + _myServer->getOctree()->processChallengeOwnershipReplyPacket(*message, sendingNode); }); } else if (_myServer->getOctree()->handlesEditPacketType(packetType)) { PerformanceWarning warn(debugProcessPacket, "processPacket KNOWN TYPE", debugProcessPacket); diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index c7c09d8b03..2389e8e39d 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -319,6 +319,7 @@ Wallet::Wallet() { auto& packetReceiver = nodeList->getPacketReceiver(); packetReceiver.registerListener(PacketType::ChallengeOwnership, this, "handleChallengeOwnershipPacket"); + packetReceiver.registerListener(PacketType::ChallengeOwnershipRequest, this, "handleChallengeOwnershipPacket"); connect(ledger.data(), &Ledger::accountResult, this, [&]() { auto wallet = DependencyManager::get(); @@ -717,15 +718,24 @@ bool Wallet::changePassphrase(const QString& newPassphrase) { } void Wallet::handleChallengeOwnershipPacket(QSharedPointer packet, SharedNodePointer sendingNode) { + bool challengeOriginatedFromClient = packet->getType() == PacketType::ChallengeOwnershipRequest; unsigned char decryptedText[64]; int certIDByteArraySize; int encryptedTextByteArraySize; + int senderNodeUUIDByteArraySize; packet->readPrimitive(&certIDByteArraySize); packet->readPrimitive(&encryptedTextByteArraySize); + if (challengeOriginatedFromClient) { + packet->readPrimitive(&senderNodeUUIDByteArraySize); + } QByteArray certID = packet->read(certIDByteArraySize); QByteArray encryptedText = packet->read(encryptedTextByteArraySize); + QByteArray senderNodeUUID; + if (challengeOriginatedFromClient) { + packet->readPrimitive(&senderNodeUUID); + } RSA* rsa = readKeys(keyFilePath().toStdString().c_str()); @@ -745,16 +755,33 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack int decryptedTextByteArraySize = decryptedTextByteArray.size(); int certIDSize = certID.size(); // setup the packet - auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnership, certIDSize + decryptedTextByteArraySize + 2 * sizeof(int), true); + if (challengeOriginatedFromClient) { + auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnershipReply, + certIDSize + decryptedTextByteArraySize + senderNodeUUIDByteArraySize + 3 * sizeof(int), + true); - decryptedTextPacket->writePrimitive(certIDSize); - decryptedTextPacket->writePrimitive(decryptedTextByteArraySize); - decryptedTextPacket->write(certID); - decryptedTextPacket->write(decryptedTextByteArray); + decryptedTextPacket->writePrimitive(certIDSize); + decryptedTextPacket->writePrimitive(decryptedTextByteArraySize); + decryptedTextPacket->writePrimitive(senderNodeUUIDByteArraySize); + decryptedTextPacket->write(certID); + decryptedTextPacket->write(decryptedTextByteArray); + decryptedTextPacket->write(senderNodeUUID); - qCDebug(commerce) << "Sending ChallengeOwnership Packet containing decrypted text" << decryptedTextByteArray << "for CertID" << certID; + qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing decrypted text" << decryptedTextByteArray << "for CertID" << certID; - nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode); + nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode); + } else { + 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" << decryptedTextByteArray << "for CertID" << certID; + + nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode); + } } else { qCDebug(commerce) << "During entity ownership challenge, decrypting the encrypted text failed."; } diff --git a/interface/src/commerce/Wallet.h b/interface/src/commerce/Wallet.h index ed145df451..fcc508b4e9 100644 --- a/interface/src/commerce/Wallet.h +++ b/interface/src/commerce/Wallet.h @@ -67,6 +67,7 @@ signals: private slots: void handleChallengeOwnershipPacket(QSharedPointer packet, SharedNodePointer sendingNode); + void handleChallengeOwnershipRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); private: QStringList _publicKeys{}; diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 402e72f2cf..cfc2156a0f 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -14,6 +14,9 @@ #include #include +#include +#include +#include #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) @@ -268,31 +271,73 @@ void ContextOverlayInterface::openInspectionCertificate() { QUuid nodeToChallenge = entityProperties.getOwningAvatarID(); auto nodeList = DependencyManager::get(); - qDebug() << "ZRF FIXME" << entityProperties.getClientOnly() << nodeToChallenge << nodeList->getSessionUUID(); - - // Don't challenge ownership of avatar entities that I own - if (entityProperties.getClientOnly() && nodeToChallenge != nodeList->getSessionUUID()) { + // ZRF FIXME: Don't challenge ownership of avatar entities that I own + if (entityProperties.getClientOnly()/* && nodeToChallenge != nodeList->getSessionUUID()*/) { SharedNodePointer entityServer = nodeList->soloNodeOfType(NodeType::EntityServer); if (entityServer) { - QByteArray certID = entityProperties.getCertificateID().toUtf8(); - QByteArray ownerKey; // ZRF FIXME! - QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122(); + 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/transfer"); + QJsonObject request; + request["certificate_id"] = entityProperties.getCertificateID(); + networkRequest.setUrl(requestURL); - int certIDByteArraySize = certID.length(); - int ownerKeyByteArraySize = ownerKey.length(); - int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length(); + QNetworkReply* networkReply = NULL; + networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); - auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, - certIDByteArraySize + ownerKeyByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int), - true); - challengeOwnershipPacket->writePrimitive(certIDByteArraySize); - challengeOwnershipPacket->writePrimitive(ownerKeyByteArraySize); - challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize); - challengeOwnershipPacket->write(certID); - challengeOwnershipPacket->write(ownerKey); - challengeOwnershipPacket->write(nodeToChallengeByteArray); - nodeList->sendPacket(std::move(challengeOwnershipPacket), *entityServer); + connect(networkReply, &QNetworkReply::finished, [=]() { + QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); + jsonObject = jsonObject["data"].toObject(); + + if (networkReply->error() == QNetworkReply::NoError) { + if (!jsonObject["invalid_reason"].toString().isEmpty()) { + qCDebug(entities) << "invalid_reason not empty"; + } else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") { + qCDebug(entities) << "'transfer_status' is 'failed'";; + } else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") { + qCDebug(entities) << "'transfer_status' is 'pending'";; + } else { + QByteArray certID = entityProperties.getCertificateID().toUtf8(); + QByteArray ownerKey = jsonObject["transfer_recipient_key"].toString().toUtf8(); + QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122(); + QByteArray encryptedText = DependencyManager::get()->getTree()->computeEncryptedNonce(certID, ownerKey); + + int certIDByteArraySize = certID.length(); + int ownerKeyByteArraySize = ownerKey.length(); + int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length(); + int encryptedTextByteArraySize = encryptedText.length(); + + auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, + certIDByteArraySize + ownerKeyByteArraySize + nodeToChallengeByteArraySize + encryptedTextByteArraySize + 4 * sizeof(int), + true); + challengeOwnershipPacket->writePrimitive(certIDByteArraySize); + challengeOwnershipPacket->writePrimitive(ownerKeyByteArraySize); + challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize); + challengeOwnershipPacket->writePrimitive(encryptedTextByteArraySize); + challengeOwnershipPacket->write(certID); + challengeOwnershipPacket->write(ownerKey); + challengeOwnershipPacket->write(nodeToChallengeByteArray); + challengeOwnershipPacket->write(encryptedText); + 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", Q_ARG(const EntityItemID&, entityItemID)); + // return; + //} else { + // startChallengeOwnershipTimer(entityItemID); + //} + } + } else { + qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error(); + } + + networkReply->deleteLater(); + }); } else { qCWarning(context_overlay) << "Couldn't get Entity Server!"; } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 90ee1190d5..71c6864b4f 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1237,68 +1237,104 @@ bool EntityTree::verifyDecryptedNonce(const QString& certID, const QString& decr void EntityTree::processChallengeOwnershipRequestPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { int certIDByteArraySize; int ownerKeyByteArraySize; + int encryptedTextByteArraySize; int nodeToChallengeByteArraySize; message.readPrimitive(&certIDByteArraySize); message.readPrimitive(&ownerKeyByteArraySize); + message.readPrimitive(&encryptedTextByteArraySize); message.readPrimitive(&nodeToChallengeByteArraySize); QString certID(message.read(certIDByteArraySize)); QString ownerKey(message.read(ownerKeyByteArraySize)); + QString encryptedText(message.read(encryptedTextByteArraySize)); QUuid nodeToChallenge = QUuid::fromRfc4122(message.read(nodeToChallengeByteArraySize)); - sendChallengeOwnershipPacket(certID, ownerKey, EntityItemID(), sourceNode, nodeToChallenge); + sendChallengeOwnershipRequestPacket(certID, ownerKey, encryptedText, sourceNode, nodeToChallenge); } -void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, const QUuid& nodeToChallenge) { +void EntityTree::processChallengeOwnershipReplyPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { + auto nodeList = DependencyManager::get(); + + int certIDByteArraySize; + int decryptedTextByteArraySize; + int senderNodeUUIDByteArraySize; + + message.readPrimitive(&certIDByteArraySize); + message.readPrimitive(&decryptedTextByteArraySize); + message.readPrimitive(&senderNodeUUIDByteArraySize); + + QByteArray certID(message.read(certIDByteArraySize)); + QByteArray decryptedText(message.read(decryptedTextByteArraySize)); + QUuid challengingNode = QUuid::fromRfc4122(message.read(senderNodeUUIDByteArraySize)); + + + + auto challengeOwnershipReplyPacket = NLPacket::create(PacketType::ChallengeOwnershipReply, + certIDByteArraySize + decryptedText.length() + 2 * sizeof(int), + true); + challengeOwnershipReplyPacket->writePrimitive(certIDByteArraySize); + challengeOwnershipReplyPacket->writePrimitive(decryptedText.length()); + challengeOwnershipReplyPacket->write(certID); + challengeOwnershipReplyPacket->write(decryptedText); + + nodeList->sendPacket(std::move(challengeOwnershipReplyPacket), *(nodeList->nodeWithUUID(challengingNode))); +} + +void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode) { // 1. Encrypt a nonce with the owner's public key auto nodeList = DependencyManager::get(); + QByteArray encryptedText = computeEncryptedNonce(certID, ownerKey); if (encryptedText == "") { - qCDebug(entities) << "CRITICAL ERROR: Couldn't compute encrypted nonce."; + qCDebug(entities) << "CRITICAL ERROR: Couldn't compute encrypted nonce. Deleting entity..."; + deleteEntity(entityItemID, true); } else { - // In this case, the server is challenging a client. That client has just rezzed a new certified entity. - if (nodeToChallenge.isNull()) { - // 2. Send the encrypted text to the rezzing avatar's node - QByteArray certIDByteArray = certID.toUtf8(); - int certIDByteArraySize = certIDByteArray.size(); - auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership, - certIDByteArraySize + encryptedText.length() + 2 * sizeof(int), - true); - challengeOwnershipPacket->writePrimitive(certIDByteArraySize); - challengeOwnershipPacket->writePrimitive(encryptedText.length()); - challengeOwnershipPacket->write(certIDByteArray); - challengeOwnershipPacket->write(encryptedText); - nodeList->sendPacket(std::move(challengeOwnershipPacket), *senderNode); + // 2. Send the encrypted text to the rezzing avatar's node + QByteArray certIDByteArray = certID.toUtf8(); + int certIDByteArraySize = certIDByteArray.size(); + auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership, + certIDByteArraySize + encryptedText.length() + 2 * sizeof(int), + true); + challengeOwnershipPacket->writePrimitive(certIDByteArraySize); + challengeOwnershipPacket->writePrimitive(encryptedText.length()); + challengeOwnershipPacket->write(certIDByteArray); + challengeOwnershipPacket->write(encryptedText); + 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 - if (thread() != QThread::currentThread()) { - QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer", Q_ARG(const EntityItemID&, entityItemID)); - return; - } else { - startChallengeOwnershipTimer(entityItemID); - } - // 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. + // 3. Kickoff a 10-second timeout timer that deletes the entity if we don't get an ownership response in time + if (thread() != QThread::currentThread()) { + QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer", Q_ARG(const EntityItemID&, entityItemID)); + return; } else { - QByteArray senderNodeUUID = senderNode->getUUID().toRfc4122(); - QByteArray certIDByteArray = certID.toUtf8(); - int certIDByteArraySize = certIDByteArray.size(); - auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, - certIDByteArraySize + encryptedText.length() + senderNodeUUID.length() + 3 * sizeof(int), - true); - challengeOwnershipPacket->writePrimitive(certIDByteArraySize); - challengeOwnershipPacket->writePrimitive(encryptedText.length()); - challengeOwnershipPacket->writePrimitive(senderNodeUUID.length()); - challengeOwnershipPacket->write(certIDByteArray); - challengeOwnershipPacket->write(encryptedText); - challengeOwnershipPacket->write(senderNodeUUID); - nodeList->sendPacket(std::move(challengeOwnershipPacket), *(nodeList->nodeWithUUID(nodeToChallenge))); + startChallengeOwnershipTimer(entityItemID); } } } +void EntityTree::sendChallengeOwnershipRequestPacket(const QString& certID, const QString& ownerKey, const QString& encryptedText, const SharedNodePointer& senderNode, const QUuid& nodeToChallenge) { + // 1. Encrypt a nonce with the owner's public key + auto nodeList = DependencyManager::get(); + + // 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(); + QByteArray encryptedTextByteArray = encryptedText.toUtf8(); + QByteArray certIDByteArray = certID.toUtf8(); + int certIDByteArraySize = certIDByteArray.size(); + auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, + certIDByteArraySize + encryptedTextByteArray.length() + senderNodeUUID.length() + 3 * sizeof(int), + true); + challengeOwnershipPacket->writePrimitive(certIDByteArraySize); + challengeOwnershipPacket->writePrimitive(encryptedTextByteArray.length()); + challengeOwnershipPacket->writePrimitive(senderNodeUUID.length()); + challengeOwnershipPacket->write(certIDByteArray); + challengeOwnershipPacket->write(encryptedTextByteArray); + challengeOwnershipPacket->write(senderNodeUUID); + nodeList->sendPacket(std::move(challengeOwnershipPacket), *(nodeList->nodeWithUUID(nodeToChallenge))); +} + void EntityTree::validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation) { // Start owner verification. auto nodeList = DependencyManager::get(); diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 3c11557dac..05b32676ed 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -94,6 +94,7 @@ public: virtual int processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength, const SharedNodePointer& senderNode) override; virtual void processChallengeOwnershipRequestPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) override; + virtual void processChallengeOwnershipReplyPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) override; virtual void processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) override; virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, @@ -274,6 +275,9 @@ public: static const float DEFAULT_MAX_TMP_ENTITY_LIFETIME; + QByteArray computeEncryptedNonce(const QString& certID, const QString ownerKey); + bool verifyDecryptedNonce(const QString& certID, const QString& decryptedNonce); + signals: void deletingEntity(const EntityItemID& entityID); void deletingEntityPointer(EntityItem* entityID); @@ -376,9 +380,8 @@ protected: Q_INVOKABLE void startPendingTransferStatusTimer(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode); private: - QByteArray computeEncryptedNonce(const QString& certID, const QString ownerKey); - bool verifyDecryptedNonce(const QString& certID, const QString& decryptedNonce); - void sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, const QUuid& nodeToChallenge = NULL); + void sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode); + void sendChallengeOwnershipRequestPacket(const QString& certID, const QString& ownerKey, const QString& encryptedText, const SharedNodePointer& senderNode, const QUuid& nodeToChallenge); void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation); }; diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index fe8868b342..ec6a0e810d 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -213,6 +213,7 @@ public: virtual int processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength, const SharedNodePointer& sourceNode) { return 0; } virtual void processChallengeOwnershipRequestPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { return; } + virtual void processChallengeOwnershipReplyPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { return; } virtual void processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { return; } virtual bool recurseChildrenWithData() const { return true; } From f5ada4fe62b94bb1703c18c19a977a51e4ac0bf9 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 31 Oct 2017 12:31:42 -0700 Subject: [PATCH 07/23] Will it compile? --- interface/src/commerce/Wallet.h | 1 - .../ui/overlays/ContextOverlayInterface.cpp | 20 +++++++++ .../src/ui/overlays/ContextOverlayInterface.h | 3 ++ libraries/entities/src/EntityTree.cpp | 42 ++++++++++--------- libraries/entities/src/EntityTree.h | 2 +- 5 files changed, 47 insertions(+), 21 deletions(-) diff --git a/interface/src/commerce/Wallet.h b/interface/src/commerce/Wallet.h index fcc508b4e9..ed145df451 100644 --- a/interface/src/commerce/Wallet.h +++ b/interface/src/commerce/Wallet.h @@ -67,7 +67,6 @@ signals: private slots: void handleChallengeOwnershipPacket(QSharedPointer packet, SharedNodePointer sendingNode); - void handleChallengeOwnershipRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); private: QStringList _publicKeys{}; diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index cfc2156a0f..8f6a38ce09 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -71,6 +71,10 @@ ContextOverlayInterface::ContextOverlayInterface() { connect(&qApp->getOverlays(), &Overlays::hoverLeaveOverlay, this, &ContextOverlayInterface::contextOverlays_hoverLeaveOverlay); connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &_selectionToSceneHandler, &SelectionToSceneHandler::selectedItemsListChanged); + + auto nodeList = DependencyManager::get(); + auto& packetReceiver = nodeList->getPacketReceiver(); + packetReceiver.registerListener(PacketType::ChallengeOwnershipReply, this, "handleChallengeOwnershipReplyPacket"); } static const uint32_t MOUSE_HW_ID = 0; @@ -375,3 +379,19 @@ void ContextOverlayInterface::deletingEntity(const EntityItemID& entityID) { destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent()); } } + +void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer packet, SharedNodePointer sendingNode) { + int certIDByteArraySize; + int decryptedTextByteArraySize; + + packet->readPrimitive(&certIDByteArraySize); + packet->readPrimitive(&decryptedTextByteArraySize); + + QString certID(packet->read(certIDByteArraySize)); + QString decryptedText(packet->read(decryptedTextByteArraySize)); + + EntityItemID id; + bool verificationSuccess = DependencyManager::get()->getTree()->verifyDecryptedNonce(certID, decryptedText, id); + + qDebug() << "ZRF VERIFICATION STATUS:" << verificationSuccess; +} diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index a063fdde23..eedc1790d3 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -70,6 +70,9 @@ public slots: void contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event); bool contextOverlayFilterPassed(const EntityItemID& entityItemID); +private slots: + void handleChallengeOwnershipReplyPacket(QSharedPointer packet, SharedNodePointer sendingNode); + private: bool _verboseLogging { true }; bool _enabled { true }; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 71c6864b4f..dc99a7b6c9 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1206,9 +1206,7 @@ QByteArray EntityTree::computeEncryptedNonce(const QString& certID, const QStrin } } -bool EntityTree::verifyDecryptedNonce(const QString& certID, const QString& decryptedNonce) { - - EntityItemID id; +bool EntityTree::verifyDecryptedNonce(const QString& certID, const QString& decryptedNonce, EntityItemID& id) { { QReadLocker certIdMapLocker(&_entityCertificateIDMapLock); id = _entityCertificateIDMap.value(certID); @@ -1221,14 +1219,12 @@ bool EntityTree::verifyDecryptedNonce(const QString& certID, const QString& decr } bool verificationSuccess = (actualNonce == decryptedNonce); - if (!verificationSuccess) { - if (!id.isNull()) { - qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed; deleting entity" << id - << "\nActual nonce:" << actualNonce << "\nDecrypted nonce:" << decryptedNonce; - deleteEntity(id, true); - } + + if (verificationSuccess) { + qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded for entity" << id; } else { - qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded; keeping entity" << id; + qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed for entity" << id + << "\nActual nonce:" << actualNonce << "\nDecrypted nonce:" << decryptedNonce; } return verificationSuccess; @@ -1268,8 +1264,6 @@ void EntityTree::processChallengeOwnershipReplyPacket(ReceivedMessage& message, QByteArray decryptedText(message.read(decryptedTextByteArraySize)); QUuid challengingNode = QUuid::fromRfc4122(message.read(senderNodeUUIDByteArraySize)); - - auto challengeOwnershipReplyPacket = NLPacket::create(PacketType::ChallengeOwnershipReply, certIDByteArraySize + decryptedText.length() + 2 * sizeof(int), true); @@ -1319,19 +1313,24 @@ void EntityTree::sendChallengeOwnershipRequestPacket(const QString& certID, cons // 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(); - QByteArray encryptedTextByteArray = encryptedText.toUtf8(); QByteArray certIDByteArray = certID.toUtf8(); - int certIDByteArraySize = certIDByteArray.size(); + QByteArray encryptedTextByteArray = encryptedText.toUtf8(); + QByteArray senderNodeUUID = senderNode->getUUID().toRfc4122(); + + int certIDByteArraySize = certIDByteArray.length(); + int encryptedTextByteArraySize = encryptedTextByteArray.length(); + int senderNodeUUIDSize = senderNodeUUID.length(); + auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, - certIDByteArraySize + encryptedTextByteArray.length() + senderNodeUUID.length() + 3 * sizeof(int), + certIDByteArraySize + encryptedTextByteArraySize + senderNodeUUIDSize + 3 * sizeof(int), true); challengeOwnershipPacket->writePrimitive(certIDByteArraySize); - challengeOwnershipPacket->writePrimitive(encryptedTextByteArray.length()); - challengeOwnershipPacket->writePrimitive(senderNodeUUID.length()); + challengeOwnershipPacket->writePrimitive(encryptedTextByteArraySize); + challengeOwnershipPacket->writePrimitive(senderNodeUUIDSize); challengeOwnershipPacket->write(certIDByteArray); challengeOwnershipPacket->write(encryptedTextByteArray); challengeOwnershipPacket->write(senderNodeUUID); + nodeList->sendPacket(std::move(challengeOwnershipPacket), *(nodeList->nodeWithUUID(nodeToChallenge))); } @@ -1408,7 +1407,12 @@ void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const emit killChallengeOwnershipTimeoutTimer(certID); - verifyDecryptedNonce(certID, decryptedText); + EntityItemID id; + if (!verifyDecryptedNonce(certID, decryptedText, id)) { + if (!id.isNull()) { + deleteEntity(id, true); + } + } } int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned char* editData, int maxLength, diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 05b32676ed..3048917a49 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -276,7 +276,7 @@ public: static const float DEFAULT_MAX_TMP_ENTITY_LIFETIME; QByteArray computeEncryptedNonce(const QString& certID, const QString ownerKey); - bool verifyDecryptedNonce(const QString& certID, const QString& decryptedNonce); + bool verifyDecryptedNonce(const QString& certID, const QString& decryptedNonce, EntityItemID& id); signals: void deletingEntity(const EntityItemID& entityID); From e9c144892a6d1d3d7e9c17f799b3824097ece56d Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 31 Oct 2017 12:55:41 -0700 Subject: [PATCH 08/23] Fixes --- assignment-client/src/entities/EntityServer.cpp | 10 ++++++++-- interface/src/commerce/Wallet.cpp | 2 +- interface/src/ui/overlays/ContextOverlayInterface.cpp | 3 ++- libraries/entities/src/EntityTree.cpp | 4 ++-- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 702ca88787..3a31b21f08 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -41,8 +41,14 @@ EntityServer::EntityServer(ReceivedMessage& message) : DependencyManager::set(); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); - packetReceiver.registerListenerForTypes({ PacketType::EntityAdd, PacketType::EntityEdit, PacketType::EntityErase, PacketType::EntityPhysics, PacketType::ChallengeOwnership }, - this, "handleEntityPacket"); + packetReceiver.registerListenerForTypes({ PacketType::EntityAdd, + PacketType::EntityEdit, + PacketType::EntityErase, + PacketType::EntityPhysics, + PacketType::ChallengeOwnership, + PacketType::ChallengeOwnershipRequest }, + this, + "handleEntityPacket"); connect(&_dynamicDomainVerificationTimer, &QTimer::timeout, this, &EntityServer::startDynamicDomainVerification); _dynamicDomainVerificationTimer.setSingleShot(true); diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 2389e8e39d..4414c7b206 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -734,7 +734,7 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack QByteArray encryptedText = packet->read(encryptedTextByteArraySize); QByteArray senderNodeUUID; if (challengeOriginatedFromClient) { - packet->readPrimitive(&senderNodeUUID); + senderNodeUUID = packet->read(senderNodeUUIDByteArraySize); } RSA* rsa = readKeys(keyFilePath().toStdString().c_str()); diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 8f6a38ce09..85bdef22ca 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -337,7 +337,8 @@ void ContextOverlayInterface::openInspectionCertificate() { //} } } else { - qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error(); + qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << + "More info:" << networkReply->readAll(); } networkReply->deleteLater(); diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index dc99a7b6c9..ba84c45311 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1238,13 +1238,13 @@ void EntityTree::processChallengeOwnershipRequestPacket(ReceivedMessage& message message.readPrimitive(&certIDByteArraySize); message.readPrimitive(&ownerKeyByteArraySize); - message.readPrimitive(&encryptedTextByteArraySize); message.readPrimitive(&nodeToChallengeByteArraySize); + message.readPrimitive(&encryptedTextByteArraySize); QString certID(message.read(certIDByteArraySize)); QString ownerKey(message.read(ownerKeyByteArraySize)); - QString encryptedText(message.read(encryptedTextByteArraySize)); QUuid nodeToChallenge = QUuid::fromRfc4122(message.read(nodeToChallengeByteArraySize)); + QString encryptedText(message.read(encryptedTextByteArraySize)); sendChallengeOwnershipRequestPacket(certID, ownerKey, encryptedText, sourceNode, nodeToChallenge); } From d49e281fabfffeb6fede7d7e0dff13d03f3981cf Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 31 Oct 2017 14:12:30 -0700 Subject: [PATCH 09/23] Add RSA error handling --- interface/src/commerce/Wallet.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 4414c7b206..acc0b08afb 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -740,6 +740,7 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack RSA* rsa = readKeys(keyFilePath().toStdString().c_str()); if (rsa) { + ERR_clear_error(); const int decryptionStatus = RSA_private_decrypt(encryptedTextByteArraySize, reinterpret_cast(encryptedText.constData()), decryptedText, @@ -784,6 +785,11 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack } } else { qCDebug(commerce) << "During entity ownership challenge, decrypting the encrypted text failed."; + long error = ERR_get_error(); + if (error != 0) { + const char* error_str = ERR_error_string(error, NULL); + qCWarning(entities) << "RSA error:" << error_str; + } } } else { qCDebug(commerce) << "During entity ownership challenge, creating the RSA object failed."; From 93308dfcd3c70e6799ff6fc482f9e661e1584c15 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 31 Oct 2017 15:59:18 -0700 Subject: [PATCH 10/23] Why did I think this would work before? --- interface/src/commerce/Wallet.cpp | 1 + .../ui/overlays/ContextOverlayInterface.cpp | 124 +++++++++--------- libraries/entities/src/EntityTree.cpp | 18 +-- libraries/entities/src/EntityTree.h | 2 +- 4 files changed, 73 insertions(+), 72 deletions(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index acc0b08afb..d0ef3fcaae 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -732,6 +732,7 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack QByteArray certID = packet->read(certIDByteArraySize); QByteArray encryptedText = packet->read(encryptedTextByteArraySize); + qDebug() << "ZRF encryptedText Inbound:" << QString(encryptedText); QByteArray senderNodeUUID; if (challengeOriginatedFromClient) { senderNodeUUID = packet->read(senderNodeUUIDByteArraySize); diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 85bdef22ca..eecf93745a 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -277,74 +277,78 @@ void ContextOverlayInterface::openInspectionCertificate() { // ZRF FIXME: Don't challenge ownership of avatar entities that I own if (entityProperties.getClientOnly()/* && nodeToChallenge != nodeList->getSessionUUID()*/) { - SharedNodePointer entityServer = nodeList->soloNodeOfType(NodeType::EntityServer); + // ZRF FIXME! + //if (entityProperties.verifyStaticCertificateProperties()) { + if (true) { + SharedNodePointer entityServer = nodeList->soloNodeOfType(NodeType::EntityServer); - if (entityServer) { - 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/transfer"); - QJsonObject request; - request["certificate_id"] = entityProperties.getCertificateID(); - networkRequest.setUrl(requestURL); + if (entityServer) { + 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/transfer"); + QJsonObject request; + request["certificate_id"] = entityProperties.getCertificateID(); + networkRequest.setUrl(requestURL); - QNetworkReply* networkReply = NULL; - networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); + QNetworkReply* networkReply = NULL; + networkReply = networkAccessManager.put(networkRequest, QJsonDocument(request).toJson()); - connect(networkReply, &QNetworkReply::finished, [=]() { - QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); - jsonObject = jsonObject["data"].toObject(); + connect(networkReply, &QNetworkReply::finished, [=]() { + QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object(); + jsonObject = jsonObject["data"].toObject(); - if (networkReply->error() == QNetworkReply::NoError) { - if (!jsonObject["invalid_reason"].toString().isEmpty()) { - qCDebug(entities) << "invalid_reason not empty"; - } else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") { - qCDebug(entities) << "'transfer_status' is 'failed'";; - } else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") { - qCDebug(entities) << "'transfer_status' is 'pending'";; + if (networkReply->error() == QNetworkReply::NoError) { + if (!jsonObject["invalid_reason"].toString().isEmpty()) { + qCDebug(entities) << "invalid_reason not empty"; + } else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") { + qCDebug(entities) << "'transfer_status' is 'failed'";; + } else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") { + qCDebug(entities) << "'transfer_status' is 'pending'";; + } else { + QString ownerKey = jsonObject["transfer_recipient_key"].toString(); + + QByteArray certID = entityProperties.getCertificateID().toUtf8(); + QByteArray encryptedText = DependencyManager::get()->getTree()->computeEncryptedNonce(certID, ownerKey); + QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122(); + + int certIDByteArraySize = certID.length(); + int encryptedTextByteArraySize = encryptedText.length(); + int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length(); + + auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, + certIDByteArraySize + encryptedTextByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int), + true); + challengeOwnershipPacket->writePrimitive(certIDByteArraySize); + challengeOwnershipPacket->writePrimitive(encryptedTextByteArraySize); + challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize); + challengeOwnershipPacket->write(certID); + challengeOwnershipPacket->write(encryptedText); + 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", Q_ARG(const EntityItemID&, entityItemID)); + // return; + //} else { + // startChallengeOwnershipTimer(entityItemID); + //} + } } else { - QByteArray certID = entityProperties.getCertificateID().toUtf8(); - QByteArray ownerKey = jsonObject["transfer_recipient_key"].toString().toUtf8(); - QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122(); - QByteArray encryptedText = DependencyManager::get()->getTree()->computeEncryptedNonce(certID, ownerKey); - - int certIDByteArraySize = certID.length(); - int ownerKeyByteArraySize = ownerKey.length(); - int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length(); - int encryptedTextByteArraySize = encryptedText.length(); - - auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, - certIDByteArraySize + ownerKeyByteArraySize + nodeToChallengeByteArraySize + encryptedTextByteArraySize + 4 * sizeof(int), - true); - challengeOwnershipPacket->writePrimitive(certIDByteArraySize); - challengeOwnershipPacket->writePrimitive(ownerKeyByteArraySize); - challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize); - challengeOwnershipPacket->writePrimitive(encryptedTextByteArraySize); - challengeOwnershipPacket->write(certID); - challengeOwnershipPacket->write(ownerKey); - challengeOwnershipPacket->write(nodeToChallengeByteArray); - challengeOwnershipPacket->write(encryptedText); - 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", Q_ARG(const EntityItemID&, entityItemID)); - // return; - //} else { - // startChallengeOwnershipTimer(entityItemID); - //} + qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << + "More info:" << networkReply->readAll(); } - } else { - qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << - "More info:" << networkReply->readAll(); - } - networkReply->deleteLater(); - }); + networkReply->deleteLater(); + }); + } else { + qCWarning(context_overlay) << "Couldn't get Entity Server!"; + } } else { - qCWarning(context_overlay) << "Couldn't get Entity Server!"; + qCDebug(context_overlay) << "Entity" << _currentEntityWithContextOverlay << "failed static certificate verification!"; } } } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index ba84c45311..1cf9fdfb26 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1153,8 +1153,8 @@ void EntityTree::startChallengeOwnershipTimer(const EntityItemID& entityItemID) _challengeOwnershipTimeoutTimer->deleteLater(); } }); -_challengeOwnershipTimeoutTimer->setSingleShot(true); -_challengeOwnershipTimeoutTimer->start(5000); + _challengeOwnershipTimeoutTimer->setSingleShot(true); + _challengeOwnershipTimeoutTimer->start(5000); } void EntityTree::startPendingTransferStatusTimer(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode) { @@ -1195,7 +1195,6 @@ QByteArray EntityTree::computeEncryptedNonce(const QString& certID, const QStrin QWriteLocker locker(&_certNonceMapLock); _certNonceMap.insert(certID, nonce); - qCDebug(entities) << "Challenging ownership of Cert ID" << certID << "by encrypting and sending nonce" << nonce << "to owner."; return encryptedText; } else { @@ -1232,21 +1231,18 @@ bool EntityTree::verifyDecryptedNonce(const QString& certID, const QString& decr void EntityTree::processChallengeOwnershipRequestPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { int certIDByteArraySize; - int ownerKeyByteArraySize; int encryptedTextByteArraySize; int nodeToChallengeByteArraySize; message.readPrimitive(&certIDByteArraySize); - message.readPrimitive(&ownerKeyByteArraySize); - message.readPrimitive(&nodeToChallengeByteArraySize); message.readPrimitive(&encryptedTextByteArraySize); + message.readPrimitive(&nodeToChallengeByteArraySize); QString certID(message.read(certIDByteArraySize)); - QString ownerKey(message.read(ownerKeyByteArraySize)); - QUuid nodeToChallenge = QUuid::fromRfc4122(message.read(nodeToChallengeByteArraySize)); QString encryptedText(message.read(encryptedTextByteArraySize)); + QUuid nodeToChallenge = QUuid::fromRfc4122(message.read(nodeToChallengeByteArraySize)); - sendChallengeOwnershipRequestPacket(certID, ownerKey, encryptedText, sourceNode, nodeToChallenge); + sendChallengeOwnershipRequestPacket(certID, encryptedText, sourceNode, nodeToChallenge); } void EntityTree::processChallengeOwnershipReplyPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { @@ -1285,6 +1281,7 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri qCDebug(entities) << "CRITICAL ERROR: Couldn't compute encrypted nonce. Deleting entity..."; deleteEntity(entityItemID, true); } else { + qCDebug(entities) << "Challenging ownership of Cert ID" << certID; // 2. Send the encrypted text to the rezzing avatar's node QByteArray certIDByteArray = certID.toUtf8(); int certIDByteArraySize = certIDByteArray.size(); @@ -1307,8 +1304,7 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri } } -void EntityTree::sendChallengeOwnershipRequestPacket(const QString& certID, const QString& ownerKey, const QString& encryptedText, const SharedNodePointer& senderNode, const QUuid& nodeToChallenge) { - // 1. Encrypt a nonce with the owner's public key +void EntityTree::sendChallengeOwnershipRequestPacket(const QString& certID, const QString& encryptedText, const SharedNodePointer& senderNode, const QUuid& nodeToChallenge) { auto nodeList = DependencyManager::get(); // In this case, Client A is challenging Client B. Client A is inspecting a certified entity that it wants diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 3048917a49..5eb5d5b569 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -381,7 +381,7 @@ protected: private: void sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode); - void sendChallengeOwnershipRequestPacket(const QString& certID, const QString& ownerKey, const QString& encryptedText, const SharedNodePointer& senderNode, const QUuid& nodeToChallenge); + void sendChallengeOwnershipRequestPacket(const QString& certID, const QString& encryptedText, const SharedNodePointer& senderNode, const QUuid& nodeToChallenge); void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation); }; From 8a6a744099ed41a56afd89f2628a6f2e4aa45300 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 1 Nov 2017 10:46:13 -0700 Subject: [PATCH 11/23] It's working --- .../src/entities/EntityServer.cpp | 3 +- interface/src/commerce/Ledger.h | 8 ++++++ interface/src/commerce/Wallet.cpp | 15 +++++----- .../ui/overlays/ContextOverlayInterface.cpp | 11 +++++++- libraries/entities/src/EntityTree.cpp | 28 +++++++++---------- libraries/entities/src/EntityTree.h | 2 +- 6 files changed, 41 insertions(+), 26 deletions(-) diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 3a31b21f08..995a5bad27 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -46,7 +46,8 @@ EntityServer::EntityServer(ReceivedMessage& message) : PacketType::EntityErase, PacketType::EntityPhysics, PacketType::ChallengeOwnership, - PacketType::ChallengeOwnershipRequest }, + PacketType::ChallengeOwnershipRequest, + PacketType::ChallengeOwnershipReply }, this, "handleEntityPacket"); diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index ae001010f0..75f6fb1ba8 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -65,6 +65,14 @@ public slots: void certificateInfoSuccess(QNetworkReply& reply); void certificateInfoFailure(QNetworkReply& reply); + void updateCertificateStatus(const QString& certID, uint certStatus); + enum CertificateStatus { + CERTIFICATE_STATUS_UNKNOWN = 0, + CERTIFICATE_STATUS_VERIFICATION_SUCCESS, + CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED, + CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED, + }; + private: QJsonObject apiResponse(const QString& label, QNetworkReply& reply); QJsonObject failResponse(const QString& label, QNetworkReply& reply); diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index d0ef3fcaae..c31184eb56 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -722,20 +722,19 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack unsigned char decryptedText[64]; int certIDByteArraySize; int encryptedTextByteArraySize; - int senderNodeUUIDByteArraySize; + int challengingNodeUUIDByteArraySize; packet->readPrimitive(&certIDByteArraySize); packet->readPrimitive(&encryptedTextByteArraySize); if (challengeOriginatedFromClient) { - packet->readPrimitive(&senderNodeUUIDByteArraySize); + packet->readPrimitive(&challengingNodeUUIDByteArraySize); } QByteArray certID = packet->read(certIDByteArraySize); QByteArray encryptedText = packet->read(encryptedTextByteArraySize); - qDebug() << "ZRF encryptedText Inbound:" << QString(encryptedText); - QByteArray senderNodeUUID; + QByteArray challengingNodeUUID; if (challengeOriginatedFromClient) { - senderNodeUUID = packet->read(senderNodeUUIDByteArraySize); + challengingNodeUUID = packet->read(challengingNodeUUIDByteArraySize); } RSA* rsa = readKeys(keyFilePath().toStdString().c_str()); @@ -759,15 +758,15 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack // setup the packet if (challengeOriginatedFromClient) { auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnershipReply, - certIDSize + decryptedTextByteArraySize + senderNodeUUIDByteArraySize + 3 * sizeof(int), + certIDSize + decryptedTextByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int), true); decryptedTextPacket->writePrimitive(certIDSize); decryptedTextPacket->writePrimitive(decryptedTextByteArraySize); - decryptedTextPacket->writePrimitive(senderNodeUUIDByteArraySize); + decryptedTextPacket->writePrimitive(challengingNodeUUIDByteArraySize); decryptedTextPacket->write(certID); decryptedTextPacket->write(decryptedTextByteArray); - decryptedTextPacket->write(senderNodeUUID); + decryptedTextPacket->write(challengingNodeUUID); qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing decrypted text" << decryptedTextByteArray << "for CertID" << certID; diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index eecf93745a..48f2394ca3 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) @@ -348,6 +349,8 @@ void ContextOverlayInterface::openInspectionCertificate() { qCWarning(context_overlay) << "Couldn't get Entity Server!"; } } else { + auto ledger = DependencyManager::get(); + emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED)); qCDebug(context_overlay) << "Entity" << _currentEntityWithContextOverlay << "failed static certificate verification!"; } } @@ -386,6 +389,8 @@ void ContextOverlayInterface::deletingEntity(const EntityItemID& entityID) { } void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer packet, SharedNodePointer sendingNode) { + auto ledger = DependencyManager::get(); + int certIDByteArraySize; int decryptedTextByteArraySize; @@ -398,5 +403,9 @@ void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer EntityItemID id; bool verificationSuccess = DependencyManager::get()->getTree()->verifyDecryptedNonce(certID, decryptedText, id); - qDebug() << "ZRF VERIFICATION STATUS:" << verificationSuccess; + if (verificationSuccess) { + emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); + } else { + emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED)); + } } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 1cf9fdfb26..f748291077 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1238,11 +1238,11 @@ void EntityTree::processChallengeOwnershipRequestPacket(ReceivedMessage& message message.readPrimitive(&encryptedTextByteArraySize); message.readPrimitive(&nodeToChallengeByteArraySize); - QString certID(message.read(certIDByteArraySize)); - QString encryptedText(message.read(encryptedTextByteArraySize)); - QUuid nodeToChallenge = QUuid::fromRfc4122(message.read(nodeToChallengeByteArraySize)); + QByteArray certID(message.read(certIDByteArraySize)); + QByteArray encryptedText(message.read(encryptedTextByteArraySize)); + QByteArray nodeToChallenge(message.read(nodeToChallengeByteArraySize)); - sendChallengeOwnershipRequestPacket(certID, encryptedText, sourceNode, nodeToChallenge); + sendChallengeOwnershipRequestPacket(certID, encryptedText, nodeToChallenge, sourceNode); } void EntityTree::processChallengeOwnershipReplyPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { @@ -1250,15 +1250,15 @@ void EntityTree::processChallengeOwnershipReplyPacket(ReceivedMessage& message, int certIDByteArraySize; int decryptedTextByteArraySize; - int senderNodeUUIDByteArraySize; + int challengingNodeUUIDByteArraySize; message.readPrimitive(&certIDByteArraySize); message.readPrimitive(&decryptedTextByteArraySize); - message.readPrimitive(&senderNodeUUIDByteArraySize); + message.readPrimitive(&challengingNodeUUIDByteArraySize); QByteArray certID(message.read(certIDByteArraySize)); QByteArray decryptedText(message.read(decryptedTextByteArraySize)); - QUuid challengingNode = QUuid::fromRfc4122(message.read(senderNodeUUIDByteArraySize)); + QUuid challengingNode = QUuid::fromRfc4122(message.read(challengingNodeUUIDByteArraySize)); auto challengeOwnershipReplyPacket = NLPacket::create(PacketType::ChallengeOwnershipReply, certIDByteArraySize + decryptedText.length() + 2 * sizeof(int), @@ -1304,17 +1304,15 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri } } -void EntityTree::sendChallengeOwnershipRequestPacket(const QString& certID, const QString& encryptedText, const SharedNodePointer& senderNode, const QUuid& nodeToChallenge) { +void EntityTree::sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& encryptedText, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode) { auto nodeList = DependencyManager::get(); // 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 certIDByteArray = certID.toUtf8(); - QByteArray encryptedTextByteArray = encryptedText.toUtf8(); QByteArray senderNodeUUID = senderNode->getUUID().toRfc4122(); - int certIDByteArraySize = certIDByteArray.length(); - int encryptedTextByteArraySize = encryptedTextByteArray.length(); + int certIDByteArraySize = certID.length(); + int encryptedTextByteArraySize = encryptedText.length(); int senderNodeUUIDSize = senderNodeUUID.length(); auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, @@ -1323,11 +1321,11 @@ void EntityTree::sendChallengeOwnershipRequestPacket(const QString& certID, cons challengeOwnershipPacket->writePrimitive(certIDByteArraySize); challengeOwnershipPacket->writePrimitive(encryptedTextByteArraySize); challengeOwnershipPacket->writePrimitive(senderNodeUUIDSize); - challengeOwnershipPacket->write(certIDByteArray); - challengeOwnershipPacket->write(encryptedTextByteArray); + challengeOwnershipPacket->write(certID); + challengeOwnershipPacket->write(encryptedText); challengeOwnershipPacket->write(senderNodeUUID); - nodeList->sendPacket(std::move(challengeOwnershipPacket), *(nodeList->nodeWithUUID(nodeToChallenge))); + nodeList->sendPacket(std::move(challengeOwnershipPacket), *(nodeList->nodeWithUUID(QUuid::fromRfc4122(nodeToChallenge)))); } void EntityTree::validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation) { diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 5eb5d5b569..86bfe984f6 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -381,7 +381,7 @@ protected: private: void sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode); - void sendChallengeOwnershipRequestPacket(const QString& certID, const QString& encryptedText, const SharedNodePointer& senderNode, const QUuid& nodeToChallenge); + void sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& encryptedText, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode); void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation); }; From b335ba9a75f92c50a2fdde765201f5203b16da66 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 1 Nov 2017 11:34:29 -0700 Subject: [PATCH 12/23] Timeout timer --- .../InspectionCertificate.qml | 25 ++++++++ interface/src/commerce/Ledger.h | 18 +++--- interface/src/commerce/QmlCommerce.cpp | 1 + interface/src/commerce/QmlCommerce.h | 2 + interface/src/commerce/Wallet.cpp | 62 +++++++++---------- .../ui/overlays/ContextOverlayInterface.cpp | 40 ++++++++---- .../src/ui/overlays/ContextOverlayInterface.h | 5 ++ libraries/entities/src/EntityTree.cpp | 4 +- 8 files changed, 105 insertions(+), 52 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml index aa1372494f..06e04d6929 100644 --- a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml +++ b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml @@ -65,6 +65,31 @@ Rectangle { } } } + + onUpdateCertificateStatus: { + if (root.certificateId === certID) { + if (certStatus === 1) { // CERTIFICATE_STATUS_VERIFICATION_SUCCESS + + } else if (certStatus === 2) { // CERTIFICATE_STATUS_VERIFICATION_TIMEOUT + errorText.text = "Verification of this certificate timed out."; + errorText.color = hifi.colors.redHighlight; + } else if (certStatus === 3) { // CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED + titleBarText.text = "Invalid Certificate"; + titleBarText.color = hifi.colors.redHighlight; + root.itemEdition = "Uncertified Copy"; + errorText.text = "The certificate associated with this entity is invalid."; + errorText.color = hifi.colors.redHighlight; + } else if (certStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED + titleBarText.text = "Invalid Certificate"; + titleBarText.color = hifi.colors.redHighlight; + root.itemEdition = "Uncertified Copy"; + errorText.text = "The certificate associated with this entity is invalid."; + errorText.color = hifi.colors.redHighlight; + } else { + console.log("Unknown certificate status received from ledger signal!"); + } + } + } } onCertificateIdChanged: { diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index 75f6fb1ba8..42eb0ffc49 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -35,6 +35,14 @@ public: void updateLocation(const QString& asset_id, const QString location, const bool controlledFailure = false); void certificateInfo(const QString& certificateId); + enum CertificateStatus { + CERTIFICATE_STATUS_UNKNOWN = 0, + CERTIFICATE_STATUS_VERIFICATION_SUCCESS, + CERTIFICATE_STATUS_VERIFICATION_TIMEOUT, + CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED, + CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED, + }; + signals: void buyResult(QJsonObject result); void receiveAtResult(QJsonObject result); @@ -45,6 +53,8 @@ signals: void locationUpdateResult(QJsonObject result); void certificateInfoResult(QJsonObject result); + void updateCertificateStatus(const QString& certID, uint certStatus); + public slots: void buySuccess(QNetworkReply& reply); void buyFailure(QNetworkReply& reply); @@ -65,14 +75,6 @@ public slots: void certificateInfoSuccess(QNetworkReply& reply); void certificateInfoFailure(QNetworkReply& reply); - void updateCertificateStatus(const QString& certID, uint certStatus); - enum CertificateStatus { - CERTIFICATE_STATUS_UNKNOWN = 0, - CERTIFICATE_STATUS_VERIFICATION_SUCCESS, - CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED, - CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED, - }; - private: QJsonObject apiResponse(const QString& label, QNetworkReply& reply); QJsonObject failResponse(const QString& label, QNetworkReply& reply); diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index 803264fa9f..ac5d0e6a2d 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -30,6 +30,7 @@ QmlCommerce::QmlCommerce(QQuickItem* parent) : OffscreenQmlDialog(parent) { connect(ledger.data(), &Ledger::accountResult, this, &QmlCommerce::accountResult); connect(wallet.data(), &Wallet::walletStatusResult, this, &QmlCommerce::walletStatusResult); connect(ledger.data(), &Ledger::certificateInfoResult, this, &QmlCommerce::certificateInfoResult); + connect(ledger.data(), &Ledger::updateCertificateStatus, this, &QmlCommerce::updateCertificateStatus); } void QmlCommerce::getWalletStatus() { diff --git a/interface/src/commerce/QmlCommerce.h b/interface/src/commerce/QmlCommerce.h index ae63133425..d4f4aa35d2 100644 --- a/interface/src/commerce/QmlCommerce.h +++ b/interface/src/commerce/QmlCommerce.h @@ -45,6 +45,8 @@ signals: void accountResult(QJsonObject result); void certificateInfoResult(QJsonObject result); + void updateCertificateStatus(const QString& certID, uint certStatus); + protected: Q_INVOKABLE void getWalletStatus(); diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index c31184eb56..c6d77982b8 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -740,6 +740,8 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack RSA* rsa = readKeys(keyFilePath().toStdString().c_str()); if (rsa) { + auto nodeList = DependencyManager::get(); + ERR_clear_error(); const int decryptionStatus = RSA_private_decrypt(encryptedTextByteArraySize, reinterpret_cast(encryptedText.constData()), @@ -749,41 +751,39 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack RSA_free(rsa); - if (decryptionStatus != -1) { - auto nodeList = DependencyManager::get(); + QByteArray decryptedTextByteArray = QByteArray(reinterpret_cast(decryptedText), decryptionStatus); + int decryptedTextByteArraySize = decryptedTextByteArray.size(); + int certIDSize = certID.size(); + // setup the packet + if (challengeOriginatedFromClient) { + auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnershipReply, + certIDSize + decryptedTextByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int), + true); - QByteArray decryptedTextByteArray = QByteArray(reinterpret_cast(decryptedText), decryptionStatus); - int decryptedTextByteArraySize = decryptedTextByteArray.size(); - int certIDSize = certID.size(); - // setup the packet - if (challengeOriginatedFromClient) { - auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnershipReply, - certIDSize + decryptedTextByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int), - true); + decryptedTextPacket->writePrimitive(certIDSize); + decryptedTextPacket->writePrimitive(decryptedTextByteArraySize); + decryptedTextPacket->writePrimitive(challengingNodeUUIDByteArraySize); + decryptedTextPacket->write(certID); + decryptedTextPacket->write(decryptedTextByteArray); + decryptedTextPacket->write(challengingNodeUUID); - decryptedTextPacket->writePrimitive(certIDSize); - decryptedTextPacket->writePrimitive(decryptedTextByteArraySize); - decryptedTextPacket->writePrimitive(challengingNodeUUIDByteArraySize); - decryptedTextPacket->write(certID); - decryptedTextPacket->write(decryptedTextByteArray); - decryptedTextPacket->write(challengingNodeUUID); + qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing decrypted text" << decryptedTextByteArray << "for CertID" << certID; - qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing decrypted text" << decryptedTextByteArray << "for CertID" << certID; - - nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode); - } else { - 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" << decryptedTextByteArray << "for CertID" << certID; - - nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode); - } + nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode); } else { + 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" << decryptedTextByteArray << "for CertID" << certID; + + nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode); + } + + if (decryptionStatus == -1) { qCDebug(commerce) << "During entity ownership challenge, decrypting the encrypted text failed."; long error = ERR_get_error(); if (error != 0) { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 48f2394ca3..75aefdc585 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -76,6 +76,7 @@ ContextOverlayInterface::ContextOverlayInterface() { auto nodeList = DependencyManager::get(); auto& packetReceiver = nodeList->getPacketReceiver(); packetReceiver.registerListener(PacketType::ChallengeOwnershipReply, this, "handleChallengeOwnershipReplyPacket"); + _challengeOwnershipTimeoutTimer.setSingleShot(true); } static const uint32_t MOUSE_HW_ID = 0; @@ -271,16 +272,16 @@ void ContextOverlayInterface::openInspectionCertificate() { tablet->loadQMLSource(INSPECTION_CERTIFICATE_QML_PATH); _hmdScriptingInterface->openTablet(); - EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_currentEntityWithContextOverlay, _entityPropertyFlags); + setLastInspectedEntity(_currentEntityWithContextOverlay); + + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags); QUuid nodeToChallenge = entityProperties.getOwningAvatarID(); auto nodeList = DependencyManager::get(); // ZRF FIXME: Don't challenge ownership of avatar entities that I own if (entityProperties.getClientOnly()/* && nodeToChallenge != nodeList->getSessionUUID()*/) { - // ZRF FIXME! - //if (entityProperties.verifyStaticCertificateProperties()) { - if (true) { + if (entityProperties.verifyStaticCertificateProperties()) { SharedNodePointer entityServer = nodeList->soloNodeOfType(NodeType::EntityServer); if (entityServer) { @@ -331,12 +332,12 @@ void ContextOverlayInterface::openInspectionCertificate() { 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", Q_ARG(const EntityItemID&, entityItemID)); - // return; - //} else { - // startChallengeOwnershipTimer(entityItemID); - //} + if (thread() != QThread::currentThread()) { + QMetaObject::invokeMethod(this, "startChallengeOwnershipTimer"); + return; + } else { + startChallengeOwnershipTimer(); + } } } else { qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() << @@ -350,8 +351,9 @@ void ContextOverlayInterface::openInspectionCertificate() { } } else { auto ledger = DependencyManager::get(); + _challengeOwnershipTimeoutTimer.stop(); emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED)); - qCDebug(context_overlay) << "Entity" << _currentEntityWithContextOverlay << "failed static certificate verification!"; + qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!"; } } } @@ -388,9 +390,23 @@ void ContextOverlayInterface::deletingEntity(const EntityItemID& entityID) { } } +void ContextOverlayInterface::startChallengeOwnershipTimer() { + auto ledger = DependencyManager::get(); + EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _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)); + }); + + _challengeOwnershipTimeoutTimer.start(5000); +} + void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer packet, SharedNodePointer sendingNode) { auto ledger = DependencyManager::get(); + _challengeOwnershipTimeoutTimer.stop(); + int certIDByteArraySize; int decryptedTextByteArraySize; @@ -403,6 +419,8 @@ void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer EntityItemID id; bool verificationSuccess = DependencyManager::get()->getTree()->verifyDecryptedNonce(certID, decryptedText, id); + qDebug() << "ZRF" << verificationSuccess; + if (verificationSuccess) { emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); } else { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h index eedc1790d3..8f0a40ef8e 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.h +++ b/interface/src/ui/overlays/ContextOverlayInterface.h @@ -51,6 +51,7 @@ public: Q_INVOKABLE QUuid getCurrentEntityWithContextOverlay() { return _currentEntityWithContextOverlay; } void setCurrentEntityWithContextOverlay(const QUuid& entityID) { _currentEntityWithContextOverlay = entityID; } + void setLastInspectedEntity(const QUuid& entityID) { _challengeOwnershipTimeoutTimer.stop(); _lastInspectedEntity = entityID; } void setEnabled(bool enabled); bool getEnabled() { return _enabled; } bool getIsInMarketplaceInspectionMode() { return _isInMarketplaceInspectionMode; } @@ -77,6 +78,7 @@ private: bool _verboseLogging { true }; bool _enabled { true }; EntityItemID _currentEntityWithContextOverlay{}; + EntityItemID _lastInspectedEntity{}; QString _entityMarketplaceID; bool _contextOverlayJustClicked { false }; @@ -90,6 +92,9 @@ private: void deletingEntity(const EntityItemID& entityItemID); SelectionToSceneHandler _selectionToSceneHandler; + + Q_INVOKABLE void startChallengeOwnershipTimer(); + QTimer _challengeOwnershipTimeoutTimer; }; #endif // hifi_ContextOverlayInterface_h diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index f748291077..333a514377 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1220,9 +1220,9 @@ bool EntityTree::verifyDecryptedNonce(const QString& certID, const QString& decr bool verificationSuccess = (actualNonce == decryptedNonce); if (verificationSuccess) { - qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded for entity" << id; + qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded."; } else { - qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed for entity" << id + qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed." << "\nActual nonce:" << actualNonce << "\nDecrypted nonce:" << decryptedNonce; } From 56cb98d96f64f9f3f4458ae072c3507a4db0b268 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 1 Nov 2017 12:02:09 -0700 Subject: [PATCH 13/23] Make inspection cert work --- .../InspectionCertificate.qml | 40 +++++++++---------- .../ui/overlays/ContextOverlayInterface.cpp | 3 +- 2 files changed, 20 insertions(+), 23 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml index 06e04d6929..bd8808f05f 100644 --- a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml +++ b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml @@ -67,27 +67,25 @@ Rectangle { } onUpdateCertificateStatus: { - if (root.certificateId === certID) { - if (certStatus === 1) { // CERTIFICATE_STATUS_VERIFICATION_SUCCESS - - } else if (certStatus === 2) { // CERTIFICATE_STATUS_VERIFICATION_TIMEOUT - errorText.text = "Verification of this certificate timed out."; - errorText.color = hifi.colors.redHighlight; - } else if (certStatus === 3) { // CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED - titleBarText.text = "Invalid Certificate"; - titleBarText.color = hifi.colors.redHighlight; - root.itemEdition = "Uncertified Copy"; - errorText.text = "The certificate associated with this entity is invalid."; - errorText.color = hifi.colors.redHighlight; - } else if (certStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED - titleBarText.text = "Invalid Certificate"; - titleBarText.color = hifi.colors.redHighlight; - root.itemEdition = "Uncertified Copy"; - errorText.text = "The certificate associated with this entity is invalid."; - errorText.color = hifi.colors.redHighlight; - } else { - console.log("Unknown certificate status received from ledger signal!"); - } + if (certStatus === 1) { // CERTIFICATE_STATUS_VERIFICATION_SUCCESS + // NOP + } else if (certStatus === 2) { // CERTIFICATE_STATUS_VERIFICATION_TIMEOUT + errorText.text = "Verification of this certificate timed out."; + errorText.color = hifi.colors.redHighlight; + } else if (certStatus === 3) { // CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED + titleBarText.text = "Invalid Certificate"; + titleBarText.color = hifi.colors.redHighlight; + root.itemEdition = "Uncertified Copy"; + errorText.text = "The certificate associated with this entity is invalid."; + errorText.color = hifi.colors.redHighlight; + } else if (certStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED + titleBarText.text = "Invalid Certificate"; + titleBarText.color = hifi.colors.redHighlight; + root.itemEdition = "Uncertified Copy"; + errorText.text = "The certificate associated with this entity is invalid."; + errorText.color = hifi.colors.redHighlight; + } else { + console.log("Unknown certificate status received from ledger signal!"); } } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 75aefdc585..5990c710ad 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -279,8 +279,7 @@ void ContextOverlayInterface::openInspectionCertificate() { QUuid nodeToChallenge = entityProperties.getOwningAvatarID(); auto nodeList = DependencyManager::get(); - // ZRF FIXME: Don't challenge ownership of avatar entities that I own - if (entityProperties.getClientOnly()/* && nodeToChallenge != nodeList->getSessionUUID()*/) { + if (entityProperties.getClientOnly()) { if (entityProperties.verifyStaticCertificateProperties()) { SharedNodePointer entityServer = nodeList->soloNodeOfType(NodeType::EntityServer); From 72d61f1825c83023029936930b1437c626c12715 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 1 Nov 2017 12:03:44 -0700 Subject: [PATCH 14/23] Remove unnecessary log - static verif isn't working --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 5990c710ad..e6568b56dd 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -418,8 +418,6 @@ void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer EntityItemID id; bool verificationSuccess = DependencyManager::get()->getTree()->verifyDecryptedNonce(certID, decryptedText, id); - qDebug() << "ZRF" << verificationSuccess; - if (verificationSuccess) { emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); } else { From edc7b91a425353b47f38a98ad94ad8ced981229d Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Wed, 1 Nov 2017 12:57:07 -0700 Subject: [PATCH 15/23] Fix comment in NetworkingConstants referencing shared.js location --- libraries/networking/src/NetworkingConstants.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h index 0bb0cee5d2..0c210e4360 100644 --- a/libraries/networking/src/NetworkingConstants.h +++ b/libraries/networking/src/NetworkingConstants.h @@ -16,8 +16,8 @@ namespace NetworkingConstants { // If you want to use STAGING instead of STABLE, - // don't forget to ALSO change the Domain Server Metaverse Server URL, which is at the top of: - // \domain-server\resources\web\settings\js\settings.js + // don't forget to ALSO change the Domain Server Metaverse Server URL inside of: + // \domain-server\resources\web\js\shared.js const QUrl METAVERSE_SERVER_URL_STABLE("https://metaverse.highfidelity.com"); const QUrl METAVERSE_SERVER_URL_STAGING("https://staging.highfidelity.com"); const QUrl METAVERSE_SERVER_URL = METAVERSE_SERVER_URL_STABLE; From f8718efe25dbeb5e6f3a8162e3bdaa5be19e4cd3 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 2 Nov 2017 09:42:33 -0700 Subject: [PATCH 16/23] Fix warnings --- interface/src/ui/overlays/ContextOverlayInterface.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index e6568b56dd..f83427a1a1 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -276,7 +276,6 @@ void ContextOverlayInterface::openInspectionCertificate() { EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags); - QUuid nodeToChallenge = entityProperties.getOwningAvatarID(); auto nodeList = DependencyManager::get(); if (entityProperties.getClientOnly()) { From 19945c5991425745be0355d953f6537248837dd3 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 2 Nov 2017 10:22:00 -0700 Subject: [PATCH 17/23] Update cert UI --- .../InspectionCertificate.qml | 31 ++++++++++++++----- .../ui/overlays/ContextOverlayInterface.cpp | 4 +-- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml index bd8808f05f..6a7a181b92 100644 --- a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml +++ b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml @@ -33,6 +33,7 @@ Rectangle { property string dateOfPurchase: "--"; property bool isLightbox: false; property bool isMyCert: false; + property bool isCertificateInvalid: false; // Style color: hifi.colors.faintGray; Hifi.QmlCommerce { @@ -44,10 +45,11 @@ Rectangle { } else { root.marketplaceUrl = result.data.marketplace_item_url; root.isMyCert = result.isMyCert ? result.isMyCert : false; - root.itemOwner = root.isMyCert ? Account.username : - "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"; - root.itemEdition = result.data.edition_number + "/" + (result.data.limited_run === -1 ? "\u221e" : result.data.limited_run); - root.dateOfPurchase = getFormattedDate(result.data.transfer_created_at * 1000); + root.itemOwner = root.isCertificateInvalid ? "--" : (root.isMyCert ? Account.username : + "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022"); + root.itemEdition = root.isCertificateInvalid ? "Uncertified Copy" : + (result.data.edition_number + "/" + (result.data.limited_run === -1 ? "\u221e" : result.data.limited_run)); + root.dateOfPurchase = root.isCertificateInvalid ? "" : getFormattedDate(result.data.transfer_created_at * 1000); root.itemName = result.data.marketplace_item_name; if (result.data.invalid_reason || result.data.transfer_status[0] === "failed") { @@ -70,20 +72,35 @@ Rectangle { if (certStatus === 1) { // CERTIFICATE_STATUS_VERIFICATION_SUCCESS // NOP } else if (certStatus === 2) { // CERTIFICATE_STATUS_VERIFICATION_TIMEOUT + root.isCertificateInvalid = true; errorText.text = "Verification of this certificate timed out."; errorText.color = hifi.colors.redHighlight; } else if (certStatus === 3) { // CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED + root.isCertificateInvalid = true; titleBarText.text = "Invalid Certificate"; titleBarText.color = hifi.colors.redHighlight; + + popText.text = ""; + root.itemOwner = ""; + dateOfPurchaseHeader.text = ""; + root.dateOfPurchase = ""; root.itemEdition = "Uncertified Copy"; + errorText.text = "The certificate associated with this entity is invalid."; - errorText.color = hifi.colors.redHighlight; + errorText.color = hifi.colors.baseGray; } else if (certStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED + root.isCertificateInvalid = true; titleBarText.text = "Invalid Certificate"; titleBarText.color = hifi.colors.redHighlight; + + popText.text = ""; + root.itemOwner = ""; + dateOfPurchaseHeader.text = ""; + root.dateOfPurchase = ""; root.itemEdition = "Uncertified Copy"; + errorText.text = "The certificate associated with this entity is invalid."; - errorText.color = hifi.colors.redHighlight; + errorText.color = hifi.colors.baseGray; } else { console.log("Unknown certificate status received from ledger signal!"); } @@ -239,7 +256,7 @@ Rectangle { } AnonymousProRegular { id: isMyCertText; - visible: root.isMyCert; + visible: root.isMyCert && !root.isCertificateInvalid; text: "(Private)"; size: 18; // Anchors diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index f83427a1a1..b5af529f2b 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -304,9 +304,9 @@ void ContextOverlayInterface::openInspectionCertificate() { if (!jsonObject["invalid_reason"].toString().isEmpty()) { qCDebug(entities) << "invalid_reason not empty"; } else if (jsonObject["transfer_status"].toArray().first().toString() == "failed") { - qCDebug(entities) << "'transfer_status' is 'failed'";; + qCDebug(entities) << "'transfer_status' is 'failed'"; } else if (jsonObject["transfer_status"].toArray().first().toString() == "pending") { - qCDebug(entities) << "'transfer_status' is 'pending'";; + qCDebug(entities) << "'transfer_status' is 'pending'"; } else { QString ownerKey = jsonObject["transfer_recipient_key"].toString(); From db9c3cc103ef37f9a40c4e404fd03de0b0e865f8 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 2 Nov 2017 12:31:36 -0700 Subject: [PATCH 18/23] make Model::scaleToFit() public not protected --- libraries/render-utils/src/Model.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 8bce976b4e..3abf7e2758 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -262,6 +262,8 @@ public: Q_INVOKABLE MeshProxyList getMeshes() const; + void scaleToFit(); + public slots: void loadURLFinished(bool success); @@ -320,7 +322,6 @@ protected: virtual void initJointStates(); void setScaleInternal(const glm::vec3& scale); - void scaleToFit(); void snapToRegistrationPoint(); void computeMeshPartLocalBounds(); From 490483fde889e31ddee4c2697e4716eccc4bb792 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 2 Nov 2017 12:32:51 -0700 Subject: [PATCH 19/23] call Model::scaleToFit() before update render items --- libraries/entities-renderer/src/RenderableModelEntityItem.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 7db19704b4..03380ad321 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -215,6 +215,7 @@ void RenderableModelEntityItem::updateModelBounds() { model->setScaleToFit(true, getDimensions()); model->setSnapModelToRegistrationPoint(true, getRegistrationPoint()); updateRenderItems = true; + model->scaleToFit(); } bool success; From 23e627a46f740cb3f4e5ccb2384cde0876c6799f Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 2 Nov 2017 13:46:24 -0700 Subject: [PATCH 20/23] Send empty decrypted text if impossible to decrypt --- interface/src/commerce/Wallet.cpp | 92 ++++++++++++++++--------------- 1 file changed, 48 insertions(+), 44 deletions(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index c6d77982b8..85632ff8f1 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -718,6 +718,8 @@ bool Wallet::changePassphrase(const QString& newPassphrase) { } void Wallet::handleChallengeOwnershipPacket(QSharedPointer packet, SharedNodePointer sendingNode) { + auto nodeList = DependencyManager::get(); + bool challengeOriginatedFromClient = packet->getType() == PacketType::ChallengeOwnershipRequest; unsigned char decryptedText[64]; int certIDByteArraySize; @@ -738,62 +740,64 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack } RSA* rsa = readKeys(keyFilePath().toStdString().c_str()); + int decryptionStatus = -1; if (rsa) { - auto nodeList = DependencyManager::get(); - ERR_clear_error(); - const int decryptionStatus = RSA_private_decrypt(encryptedTextByteArraySize, + decryptionStatus = RSA_private_decrypt(encryptedTextByteArraySize, reinterpret_cast(encryptedText.constData()), decryptedText, rsa, RSA_PKCS1_OAEP_PADDING); RSA_free(rsa); - - QByteArray decryptedTextByteArray = QByteArray(reinterpret_cast(decryptedText), decryptionStatus); - int decryptedTextByteArraySize = decryptedTextByteArray.size(); - int certIDSize = certID.size(); - // setup the packet - if (challengeOriginatedFromClient) { - auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnershipReply, - certIDSize + decryptedTextByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int), - true); - - decryptedTextPacket->writePrimitive(certIDSize); - decryptedTextPacket->writePrimitive(decryptedTextByteArraySize); - decryptedTextPacket->writePrimitive(challengingNodeUUIDByteArraySize); - decryptedTextPacket->write(certID); - decryptedTextPacket->write(decryptedTextByteArray); - decryptedTextPacket->write(challengingNodeUUID); - - qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing decrypted text" << decryptedTextByteArray << "for CertID" << certID; - - nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode); - } else { - 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" << decryptedTextByteArray << "for CertID" << certID; - - nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode); - } - - if (decryptionStatus == -1) { - qCDebug(commerce) << "During entity ownership challenge, decrypting the encrypted text failed."; - long error = ERR_get_error(); - if (error != 0) { - const char* error_str = ERR_error_string(error, NULL); - qCWarning(entities) << "RSA error:" << error_str; - } - } } else { qCDebug(commerce) << "During entity ownership challenge, creating the RSA object failed."; } + + QByteArray decryptedTextByteArray; + if (decryptionStatus > -1) { + decryptedTextByteArray = QByteArray(reinterpret_cast(decryptedText), decryptionStatus); + } + int decryptedTextByteArraySize = decryptedTextByteArray.size(); + int certIDSize = certID.size(); + // setup the packet + if (challengeOriginatedFromClient) { + auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnershipReply, + certIDSize + decryptedTextByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int), + true); + + decryptedTextPacket->writePrimitive(certIDSize); + decryptedTextPacket->writePrimitive(decryptedTextByteArraySize); + decryptedTextPacket->writePrimitive(challengingNodeUUIDByteArraySize); + decryptedTextPacket->write(certID); + decryptedTextPacket->write(decryptedTextByteArray); + decryptedTextPacket->write(challengingNodeUUID); + + qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing decrypted text" << decryptedTextByteArray << "for CertID" << certID; + + nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode); + } else { + 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" << decryptedTextByteArray << "for CertID" << certID; + + nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode); + } + + if (decryptionStatus == -1) { + qCDebug(commerce) << "During entity ownership challenge, decrypting the encrypted text failed."; + long error = ERR_get_error(); + if (error != 0) { + const char* error_str = ERR_error_string(error, NULL); + qCWarning(entities) << "RSA error:" << error_str; + } + } } void Wallet::account() { From 56b1c7d0eeb03bc6ba1504ac71dd1ebbc26b2bec Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 2 Nov 2017 14:51:40 -0700 Subject: [PATCH 21/23] Fix finding the SSL binary path --- cmake/modules/FindOpenSSL.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/FindOpenSSL.cmake b/cmake/modules/FindOpenSSL.cmake index 338dee7bc8..0619c4d587 100644 --- a/cmake/modules/FindOpenSSL.cmake +++ b/cmake/modules/FindOpenSSL.cmake @@ -60,7 +60,7 @@ if (WIN32 AND NOT CYGWIN) select_library_configurations(LIB_EAY) select_library_configurations(SSL_EAY) set(OPENSSL_LIBRARIES ${SSL_EAY_LIBRARY} ${LIB_EAY_LIBRARY}) - find_path(OPENSSL_DLL_PATH NAMES ssleay32.dll PATH_SUFFIXES "bin" ${_OPENSSL_ROOT_HINTS_AND_PATHS}) + find_path(OPENSSL_DLL_PATH NAMES ssleay32.dll PATH_SUFFIXES "bin" HINTS ${_OPENSSL_ROOT_HINTS_AND_PATHS} NO_DEFAULT_PATH) endif() else() From 6173ad810b3c9207d649734a741c062459f99a53 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 2 Nov 2017 15:48:59 -0700 Subject: [PATCH 22/23] Reset wallet passphrase when login username changes --- interface/src/commerce/QmlCommerce.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/interface/src/commerce/QmlCommerce.cpp b/interface/src/commerce/QmlCommerce.cpp index ac5d0e6a2d..9d07ddb4ab 100644 --- a/interface/src/commerce/QmlCommerce.cpp +++ b/interface/src/commerce/QmlCommerce.cpp @@ -31,6 +31,11 @@ QmlCommerce::QmlCommerce(QQuickItem* parent) : OffscreenQmlDialog(parent) { connect(wallet.data(), &Wallet::walletStatusResult, this, &QmlCommerce::walletStatusResult); connect(ledger.data(), &Ledger::certificateInfoResult, this, &QmlCommerce::certificateInfoResult); connect(ledger.data(), &Ledger::updateCertificateStatus, this, &QmlCommerce::updateCertificateStatus); + + auto accountManager = DependencyManager::get(); + connect(accountManager.data(), &AccountManager::usernameChanged, [&]() { + setPassphrase(""); + }); } void QmlCommerce::getWalletStatus() { From 6f3c61c4e0314baa448cc09a8ff31470f21bdae6 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 2 Nov 2017 17:57:52 -0700 Subject: [PATCH 23/23] Slightly modify invalid cert language --- .../commerce/inspectionCertificate/InspectionCertificate.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml index 6a7a181b92..b6c29a1fad 100644 --- a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml +++ b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml @@ -86,7 +86,7 @@ Rectangle { root.dateOfPurchase = ""; root.itemEdition = "Uncertified Copy"; - errorText.text = "The certificate associated with this entity is invalid."; + errorText.text = "The information associated with this item has been modified and it no longer matches the original certified item."; errorText.color = hifi.colors.baseGray; } else if (certStatus === 4) { // CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED root.isCertificateInvalid = true; @@ -99,7 +99,7 @@ Rectangle { root.dateOfPurchase = ""; root.itemEdition = "Uncertified Copy"; - errorText.text = "The certificate associated with this entity is invalid."; + errorText.text = "The avatar who rezzed this item doesn't own it."; errorText.color = hifi.colors.baseGray; } else { console.log("Unknown certificate status received from ledger signal!");