Merged from master

This commit is contained in:
Olivier Prat 2017-11-03 14:39:42 +01:00
commit e0fd71f246
26 changed files with 550 additions and 222 deletions

View file

@ -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";

View file

@ -41,8 +41,15 @@ EntityServer::EntityServer(ReceivedMessage& message) :
DependencyManager::set<ScriptCache>();
auto& packetReceiver = DependencyManager::get<NodeList>()->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,
PacketType::ChallengeOwnershipReply },
this,
"handleEntityPacket");
connect(&_dynamicDomainVerificationTimer, &QTimer::timeout, this, &EntityServer::startDynamicDomainVerification);
_dynamicDomainVerificationTimer.setSingleShot(true);
@ -459,7 +466,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

View file

@ -96,6 +96,14 @@ void OctreeInboundPacketProcessor::processPacket(QSharedPointer<ReceivedMessage>
_myServer->getOctree()->withWriteLock([&] {
_myServer->getOctree()->processChallengeOwnershipPacket(*message, sendingNode);
});
} else if (packetType == PacketType::ChallengeOwnershipRequest) {
_myServer->getOctree()->withWriteLock([&] {
_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);
_receivedPacketCount++;

View file

@ -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()

View file

@ -830,26 +830,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();
}
}
@ -3007,9 +2987,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<QHostAddress> 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
@ -3022,7 +3013,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;

View file

@ -116,8 +116,6 @@ private slots:
void tokenGrantFinished();
void profileRequestFinished();
void timeoutICEAddressLookup();
signals:
void iceServerChanged();
void userConnected();

View file

@ -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") {
@ -65,6 +67,44 @@ Rectangle {
}
}
}
onUpdateCertificateStatus: {
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 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;
titleBarText.text = "Invalid Certificate";
titleBarText.color = hifi.colors.redHighlight;
popText.text = "";
root.itemOwner = "";
dateOfPurchaseHeader.text = "";
root.dateOfPurchase = "";
root.itemEdition = "Uncertified Copy";
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!");
}
}
}
onCertificateIdChanged: {
@ -216,7 +256,7 @@ Rectangle {
}
AnonymousProRegular {
id: isMyCertText;
visible: root.isMyCert;
visible: root.isMyCert && !root.isCertificateInvalid;
text: "(Private)";
size: 18;
// Anchors

View file

@ -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);

View file

@ -30,6 +30,12 @@ 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);
auto accountManager = DependencyManager::get<AccountManager>();
connect(accountManager.data(), &AccountManager::usernameChanged, [&]() {
setPassphrase("");
});
}
void QmlCommerce::getWalletStatus() {

View file

@ -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();

View file

@ -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<Wallet>();
@ -717,50 +718,86 @@ bool Wallet::changePassphrase(const QString& newPassphrase) {
}
void Wallet::handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode) {
auto nodeList = DependencyManager::get<NodeList>();
bool challengeOriginatedFromClient = packet->getType() == PacketType::ChallengeOwnershipRequest;
unsigned char decryptedText[64];
int certIDByteArraySize;
int encryptedTextByteArraySize;
int challengingNodeUUIDByteArraySize;
packet->readPrimitive(&certIDByteArraySize);
packet->readPrimitive(&encryptedTextByteArraySize);
if (challengeOriginatedFromClient) {
packet->readPrimitive(&challengingNodeUUIDByteArraySize);
}
QByteArray certID = packet->read(certIDByteArraySize);
QByteArray encryptedText = packet->read(encryptedTextByteArraySize);
QByteArray challengingNodeUUID;
if (challengeOriginatedFromClient) {
challengingNodeUUID = packet->read(challengingNodeUUIDByteArraySize);
}
RSA* rsa = readKeys(keyFilePath().toStdString().c_str());
int decryptionStatus = -1;
if (rsa) {
const int decryptionStatus = RSA_private_decrypt(encryptedTextByteArraySize,
ERR_clear_error();
decryptionStatus = RSA_private_decrypt(encryptedTextByteArraySize,
reinterpret_cast<const unsigned char*>(encryptedText.constData()),
decryptedText,
rsa,
RSA_PKCS1_OAEP_PADDING);
RSA_free(rsa);
if (decryptionStatus != -1) {
auto nodeList = DependencyManager::get<NodeList>();
QByteArray decryptedTextByteArray = QByteArray(reinterpret_cast<const char*>(decryptedText), decryptionStatus);
int decryptedTextByteArraySize = decryptedTextByteArray.size();
int certIDSize = certID.size();
// setup the packet
auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnership, certIDSize + decryptedTextByteArraySize + 2 * sizeof(int), true);
decryptedTextPacket->writePrimitive(certIDSize);
decryptedTextPacket->writePrimitive(decryptedTextByteArraySize);
decryptedTextPacket->write(certID);
decryptedTextPacket->write(decryptedTextByteArray);
qCDebug(commerce) << "Sending ChallengeOwnership Packet containing decrypted text" << decryptedTextByteArray << "for CertID" << certID;
nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode);
} else {
qCDebug(commerce) << "During entity ownership challenge, decrypting the encrypted text failed.";
}
} else {
qCDebug(commerce) << "During entity ownership challenge, creating the RSA object failed.";
}
QByteArray decryptedTextByteArray;
if (decryptionStatus > -1) {
decryptedTextByteArray = QByteArray(reinterpret_cast<const char*>(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() {

View file

@ -14,6 +14,10 @@
#include <EntityTreeRenderer.h>
#include <NetworkingConstants.h>
#include <NetworkAccessManager.h>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
#include <commerce/Ledger.h>
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
@ -40,6 +44,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<EntityScriptingInterface>().data();
connect(entityScriptingInterface, &EntityScriptingInterface::mousePressOnEntity, this, &ContextOverlayInterface::createOrDestroyContextOverlay);
@ -68,6 +74,11 @@ ContextOverlayInterface::ContextOverlayInterface() {
_selectionToSceneHandlers[i].initialize(QString("contextOverlayHighlightList") + QString::number(i));
connect(_selectionScriptingInterface.data(), &SelectionScriptingInterface::selectedItemsListChanged, &_selectionToSceneHandlers[i], &SelectionToSceneHandler::selectedItemsListChanged);
}
auto nodeList = DependencyManager::get<NodeList>();
auto& packetReceiver = nodeList->getPacketReceiver();
packetReceiver.registerListener(PacketType::ChallengeOwnershipReply, this, "handleChallengeOwnershipReplyPacket");
_challengeOwnershipTimeoutTimer.setSingleShot(true);
}
static const uint32_t MOUSE_HW_ID = 0;
@ -262,6 +273,89 @@ void ContextOverlayInterface::openInspectionCertificate() {
auto tablet = dynamic_cast<TabletProxy*>(_tabletScriptingInterface->getTablet("com.highfidelity.interface.tablet.system"));
tablet->loadQMLSource(INSPECTION_CERTIFICATE_QML_PATH);
_hmdScriptingInterface->openTablet();
setLastInspectedEntity(_currentEntityWithContextOverlay);
EntityItemProperties entityProperties = _entityScriptingInterface->getEntityProperties(_lastInspectedEntity, _entityPropertyFlags);
auto nodeList = DependencyManager::get<NodeList>();
if (entityProperties.getClientOnly()) {
if (entityProperties.verifyStaticCertificateProperties()) {
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);
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();
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<EntityTreeRenderer>()->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");
return;
} else {
startChallengeOwnershipTimer();
}
}
} else {
qCDebug(entities) << "Call to" << networkReply->url() << "failed with error" << networkReply->error() <<
"More info:" << networkReply->readAll();
}
networkReply->deleteLater();
});
} else {
qCWarning(context_overlay) << "Couldn't get Entity Server!";
}
} else {
auto ledger = DependencyManager::get<Ledger>();
_challengeOwnershipTimeoutTimer.stop();
emit ledger->updateCertificateStatus(entityProperties.getCertificateID(), (uint)(ledger->CERTIFICATE_STATUS_STATIC_VERIFICATION_FAILED));
qCDebug(context_overlay) << "Entity" << _lastInspectedEntity << "failed static certificate verification!";
}
}
}
}
@ -295,3 +389,39 @@ void ContextOverlayInterface::deletingEntity(const EntityItemID& entityID) {
destroyContextOverlay(_currentEntityWithContextOverlay, PointerEvent());
}
}
void ContextOverlayInterface::startChallengeOwnershipTimer() {
auto ledger = DependencyManager::get<Ledger>();
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<ReceivedMessage> packet, SharedNodePointer sendingNode) {
auto ledger = DependencyManager::get<Ledger>();
_challengeOwnershipTimeoutTimer.stop();
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<EntityTreeRenderer>()->getTree()->verifyDecryptedNonce(certID, decryptedText, id);
if (verificationSuccess) {
emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS));
} else {
emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_OWNER_VERIFICATION_FAILED));
}
}

View file

@ -52,6 +52,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; }
@ -71,10 +72,14 @@ public slots:
void contextOverlays_hoverLeaveEntity(const EntityItemID& entityID, const PointerEvent& event);
bool contextOverlayFilterPassed(const EntityItemID& entityItemID);
private slots:
void handleChallengeOwnershipReplyPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode);
private:
bool _verboseLogging { true };
bool _enabled { true };
QUuid _currentEntityWithContextOverlay{};
EntityItemID _currentEntityWithContextOverlay{};
EntityItemID _lastInspectedEntity{};
QString _entityMarketplaceID;
bool _contextOverlayJustClicked { false };
@ -88,6 +93,9 @@ private:
void deletingEntity(const EntityItemID& entityItemID);
SelectionToSceneHandler _selectionToSceneHandlers[render::Scene::MAX_OUTLINE_COUNT];
Q_INVOKABLE void startChallengeOwnershipTimer();
QTimer _challengeOwnershipTimeoutTimer;
};
#endif // hifi_ContextOverlayInterface_h

View file

@ -215,6 +215,7 @@ void RenderableModelEntityItem::updateModelBounds() {
model->setScaleToFit(true, getDimensions());
model->setSnapModelToRegistrationPoint(true, getRegistrationPoint());
updateRenderItems = true;
model->scaleToFit();
}
bool success;

View file

@ -14,10 +14,6 @@
#include <QtCore/QObject>
#include <QtEndian>
#include <QJsonDocument>
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <NetworkingConstants.h>
#include <NetworkAccessManager.h>
#include <QtNetwork/QNetworkReply>
@ -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<const unsigned char*>(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<const unsigned char*>(digestByteArray.constData());
int digestLength = digestByteArray.length();
const QByteArray signatureByteArray = QByteArray::fromBase64(getCertificateID().toUtf8());
const unsigned char* signature = reinterpret_cast<const unsigned char*>(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());

View file

@ -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;

View file

@ -14,6 +14,15 @@
#include <QObject>
#include <QtCore/QJsonDocument>
#include <openssl/err.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include <NetworkingConstants.h>
#include <NetworkAccessManager.h>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>
#include <ByteCountCoding.h>
#include <GLMHelpers.h>
#include <RegisteredMetaTypes.h>
@ -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<const unsigned char*>(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<const unsigned char*>(digestByteArray.constData());
int digestLength = digestByteArray.length();
const QByteArray signatureByteArray = QByteArray::fromBase64(getCertificateID().toUtf8());
const unsigned char* signature = reinterpret_cast<const unsigned char*>(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;
}
}

View file

@ -336,6 +336,10 @@ public:
QByteArray getPackedStrokeColors() const;
QByteArray packStrokeColors(const QVector<glm::vec3>& strokeColors) const;
QByteArray getStaticCertificateJSON() const;
QByteArray getStaticCertificateHash() const;
bool verifyStaticCertificateProperties();
protected:
QString getCollisionMaskAsString() const;
void setCollisionMaskFromString(const QString& maskString);

View file

@ -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();
}
});
}

View file

@ -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 {
@ -1206,9 +1205,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,19 +1218,116 @@ 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.";
} else {
qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded; keeping entity" << id;
qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed."
<< "\nActual nonce:" << actualNonce << "\nDecrypted nonce:" << decryptedNonce;
}
return verificationSuccess;
}
void EntityTree::processChallengeOwnershipRequestPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) {
int certIDByteArraySize;
int encryptedTextByteArraySize;
int nodeToChallengeByteArraySize;
message.readPrimitive(&certIDByteArraySize);
message.readPrimitive(&encryptedTextByteArraySize);
message.readPrimitive(&nodeToChallengeByteArraySize);
QByteArray certID(message.read(certIDByteArraySize));
QByteArray encryptedText(message.read(encryptedTextByteArraySize));
QByteArray nodeToChallenge(message.read(nodeToChallengeByteArraySize));
sendChallengeOwnershipRequestPacket(certID, encryptedText, nodeToChallenge, sourceNode);
}
void EntityTree::processChallengeOwnershipReplyPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) {
auto nodeList = DependencyManager::get<NodeList>();
int certIDByteArraySize;
int decryptedTextByteArraySize;
int challengingNodeUUIDByteArraySize;
message.readPrimitive(&certIDByteArraySize);
message.readPrimitive(&decryptedTextByteArraySize);
message.readPrimitive(&challengingNodeUUIDByteArraySize);
QByteArray certID(message.read(certIDByteArraySize));
QByteArray decryptedText(message.read(decryptedTextByteArraySize));
QUuid challengingNode = QUuid::fromRfc4122(message.read(challengingNodeUUIDByteArraySize));
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<NodeList>();
QByteArray encryptedText = computeEncryptedNonce(certID, ownerKey);
if (encryptedText == "") {
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();
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);
}
}
}
void EntityTree::sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& encryptedText, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode) {
auto nodeList = DependencyManager::get<NodeList>();
// In this case, Client A is challenging Client B. Client A is inspecting a certified entity that it wants
// to make sure belongs to Avatar B.
QByteArray senderNodeUUID = senderNode->getUUID().toRfc4122();
int certIDByteArraySize = certID.length();
int encryptedTextByteArraySize = encryptedText.length();
int senderNodeUUIDSize = senderNodeUUID.length();
auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest,
certIDByteArraySize + encryptedTextByteArraySize + senderNodeUUIDSize + 3 * sizeof(int),
true);
challengeOwnershipPacket->writePrimitive(certIDByteArraySize);
challengeOwnershipPacket->writePrimitive(encryptedTextByteArraySize);
challengeOwnershipPacket->writePrimitive(senderNodeUUIDSize);
challengeOwnershipPacket->write(certID);
challengeOwnershipPacket->write(encryptedText);
challengeOwnershipPacket->write(senderNodeUUID);
nodeList->sendPacket(std::move(challengeOwnershipPacket), *(nodeList->nodeWithUUID(QUuid::fromRfc4122(nodeToChallenge))));
}
void EntityTree::validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation) {
// Start owner verification.
auto nodeList = DependencyManager::get<NodeList>();
@ -1279,33 +1373,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
@ -1329,7 +1401,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,
@ -1528,7 +1605,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.";

View file

@ -93,6 +93,8 @@ public:
void fixupTerseEditLogging(EntityItemProperties& properties, QList<QString>& 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 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,
@ -273,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, EntityItemID& id);
signals:
void deletingEntity(const EntityItemID& entityID);
void deletingEntityPointer(EntityItem* entityID);
@ -375,8 +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);
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);
};

View file

@ -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);

View file

@ -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:
// <hifi repo>\domain-server\resources\web\settings\js\settings.js
// don't forget to ALSO change the Domain Server Metaverse Server URL inside of:
// <hifi repo>\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;

View file

@ -124,6 +124,8 @@ public:
OctreeFileReplacementFromUrl,
ChallengeOwnership,
EntityScriptCallMethod,
ChallengeOwnershipRequest,
ChallengeOwnershipReply,
NUM_PACKET_TYPE
};

View file

@ -212,6 +212,8 @@ 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 processChallengeOwnershipReplyPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { return; }
virtual void processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { return; }
virtual bool recurseChildrenWithData() const { return true; }

View file

@ -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();