mirror of
https://github.com/overte-org/overte.git
synced 2025-07-23 11:24:10 +02:00
Implement static cert verification correctly
This commit is contained in:
parent
0dc6211a7d
commit
74180bc4cd
6 changed files with 49 additions and 75 deletions
|
@ -459,9 +459,7 @@ void EntityServer::startDynamicDomainVerification() {
|
||||||
EntityItemPointer entity = tree->findEntityByEntityItemID(i.value());
|
EntityItemPointer entity = tree->findEntityByEntityItemID(i.value());
|
||||||
|
|
||||||
if (entity) {
|
if (entity) {
|
||||||
// ZRF FIXME!!!
|
if (!entity->verifyStaticCertificateProperties()) {
|
||||||
//if (!entity->verifyStaticCertificateProperties()) {
|
|
||||||
if (false) {
|
|
||||||
qCDebug(entities) << "During Dynamic Domain Verification, a certified entity with ID" << i.value() << "failed"
|
qCDebug(entities) << "During Dynamic Domain Verification, a certified entity with ID" << i.value() << "failed"
|
||||||
<< "static certificate verification.";
|
<< "static certificate verification.";
|
||||||
// Delete the entity if it doesn't pass static certificate verification
|
// Delete the entity if it doesn't pass static certificate verification
|
||||||
|
|
|
@ -14,9 +14,13 @@
|
||||||
#include <QtCore/QObject>
|
#include <QtCore/QObject>
|
||||||
#include <QtEndian>
|
#include <QtEndian>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <openssl/rsa.h> // see comments for DEBUG_CERT
|
#include <openssl/rsa.h>
|
||||||
#include <openssl/pem.h>
|
#include <openssl/pem.h>
|
||||||
#include <openssl/x509.h>
|
#include <openssl/x509.h>
|
||||||
|
#include <NetworkingConstants.h>
|
||||||
|
#include <NetworkAccessManager.h>
|
||||||
|
#include <QtNetwork/QNetworkReply>
|
||||||
|
#include <QtNetwork/QNetworkRequest>
|
||||||
|
|
||||||
#include <glm/gtx/transform.hpp>
|
#include <glm/gtx/transform.hpp>
|
||||||
|
|
||||||
|
@ -41,6 +45,7 @@ int entityItemPointernMetaTypeId = qRegisterMetaType<EntityItemPointer>();
|
||||||
|
|
||||||
int EntityItem::_maxActionsDataSize = 800;
|
int EntityItem::_maxActionsDataSize = 800;
|
||||||
quint64 EntityItem::_rememberDeletedActionTime = 20 * USECS_PER_SECOND;
|
quint64 EntityItem::_rememberDeletedActionTime = 20 * USECS_PER_SECOND;
|
||||||
|
QString EntityItem::_marketplacePublicKey;
|
||||||
|
|
||||||
EntityItem::EntityItem(const EntityItemID& entityItemID) :
|
EntityItem::EntityItem(const EntityItemID& entityItemID) :
|
||||||
SpatiallyNestable(NestableType::Entity, entityItemID)
|
SpatiallyNestable(NestableType::Entity, entityItemID)
|
||||||
|
@ -1588,16 +1593,16 @@ QByteArray EntityItem::getStaticCertificateJSON() const {
|
||||||
// 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
|
// 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.
|
// to help maintainence in two different code bases.
|
||||||
if (!propertySet.getAnimation().getURL().isEmpty()) {
|
if (!propertySet.getAnimation().getURL().isEmpty()) {
|
||||||
json["animation.url"] = propertySet.getAnimation().getURL();
|
json["animationURL"] = propertySet.getAnimation().getURL();
|
||||||
}
|
}
|
||||||
ADD_STRING_PROPERTY(collisionSoundURL, CollisionSoundURL);
|
ADD_STRING_PROPERTY(collisionSoundURL, CollisionSoundURL);
|
||||||
ADD_STRING_PROPERTY(compoundShapeURL, CompoundShapeURL);
|
ADD_STRING_PROPERTY(compoundShapeURL, CompoundShapeURL);
|
||||||
ADD_INT_PROPERTY(editionNumber, EditionNumber);
|
ADD_INT_PROPERTY(editionNumber, EditionNumber);
|
||||||
ADD_INT_PROPERTY(entityInstanceNumber, EntityInstanceNumber);
|
ADD_INT_PROPERTY(instanceNumber, EntityInstanceNumber);
|
||||||
ADD_STRING_PROPERTY(itemArtist, ItemArtist);
|
ADD_STRING_PROPERTY(itemArtist, ItemArtist);
|
||||||
ADD_STRING_PROPERTY(itemCategories, ItemCategories);
|
ADD_STRING_PROPERTY(itemCategories, ItemCategories);
|
||||||
ADD_STRING_PROPERTY(itemDescription, ItemDescription);
|
ADD_STRING_PROPERTY(itemDescription, ItemDescription);
|
||||||
ADD_STRING_PROPERTY(itemLicense, ItemLicense);
|
ADD_STRING_PROPERTY(itemLicenseUrl, ItemLicense);
|
||||||
ADD_STRING_PROPERTY(itemName, ItemName);
|
ADD_STRING_PROPERTY(itemName, ItemName);
|
||||||
ADD_INT_PROPERTY(limitedRun, LimitedRun);
|
ADD_INT_PROPERTY(limitedRun, LimitedRun);
|
||||||
ADD_STRING_PROPERTY(marketplaceID, MarketplaceID);
|
ADD_STRING_PROPERTY(marketplaceID, MarketplaceID);
|
||||||
|
@ -1612,39 +1617,6 @@ QByteArray EntityItem::getStaticCertificateHash() const {
|
||||||
return QCryptographicHash::hash(getStaticCertificateJSON(), QCryptographicHash::Sha256);
|
return QCryptographicHash::hash(getStaticCertificateJSON(), QCryptographicHash::Sha256);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_CERT
|
|
||||||
QString EntityItem::computeCertificateID() {
|
|
||||||
// Until the marketplace generates it, compute and answer the certificateID here.
|
|
||||||
// Does not set it, as that will have to be done from script engine in order to update server, etc.
|
|
||||||
const auto hash = getStaticCertificateHash();
|
|
||||||
const auto text = reinterpret_cast<const unsigned char*>(hash.constData());
|
|
||||||
const unsigned int textLength = hash.length();
|
|
||||||
|
|
||||||
const char privateKey[] = "-----BEGIN RSA PRIVATE KEY-----\n\
|
|
||||||
MIIBOQIBAAJBALCoBiDAZOClO26tC5pd7JikBL61WIgpAqbcNnrV/TcG6LPI7Zbi\n\
|
|
||||||
MjdUixmTNvYMRZH3Wlqtl2IKG1W68y3stKECAwEAAQJABvOlwhYwIhL+gr12jm2R\n\
|
|
||||||
yPPzZ9nVEQ6kFxLlZfIT09119fd6OU1X5d4sHWfMfSIEgjwQIDS3ZU1kY3XKo87X\n\
|
|
||||||
zQIhAOPHlYa1OC7BLhaTouy68qIU2vCKLP8mt4S31/TT0UOnAiEAxor6gU6yupTQ\n\
|
|
||||||
yuyV3yHvr5LkZKBGqhjmOTmDfgtX7ncCIChGbgX3nQuHVOLhD/nTxHssPNozVGl5\n\
|
|
||||||
KxHof+LmYSYZAiB4U+yEh9SsXdq40W/3fpLMPuNq1PRezJ5jGidGMcvF+wIgUNec\n\
|
|
||||||
3Kg2U+CVZr8/bDT/vXRrsKj1zfobYuvbfVH02QY=\n\
|
|
||||||
-----END RSA PRIVATE KEY-----";
|
|
||||||
BIO* bio = BIO_new_mem_buf((void*)privateKey, sizeof(privateKey));
|
|
||||||
RSA* rsa = PEM_read_bio_RSAPrivateKey(bio, NULL, NULL, NULL);
|
|
||||||
|
|
||||||
QByteArray signature(RSA_size(rsa), 0);
|
|
||||||
unsigned int signatureLength = 0;
|
|
||||||
const int signOK = RSA_sign(NID_sha256, text, textLength, reinterpret_cast<unsigned char*>(signature.data()), &signatureLength, rsa);
|
|
||||||
BIO_free(bio);
|
|
||||||
RSA_free(rsa);
|
|
||||||
if (!signOK) {
|
|
||||||
qCWarning(entities) << "Unable to compute signature for" << getName() << getEntityItemID();
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return signature.toBase64();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EntityItem::verifyStaticCertificateProperties() {
|
bool EntityItem::verifyStaticCertificateProperties() {
|
||||||
// True IIF a non-empty certificateID matches the static certificate json.
|
// 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.
|
// I.e., if we can verify that the certificateID was produced by High Fidelity signing the static certificate hash.
|
||||||
|
@ -1659,13 +1631,8 @@ bool EntityItem::verifyStaticCertificateProperties() {
|
||||||
const auto hash = getStaticCertificateHash();
|
const auto hash = getStaticCertificateHash();
|
||||||
const auto text = reinterpret_cast<const unsigned char*>(hash.constData());
|
const auto text = reinterpret_cast<const unsigned char*>(hash.constData());
|
||||||
const unsigned int textLength = hash.length();
|
const unsigned int textLength = hash.length();
|
||||||
|
|
||||||
// After DEBUG_CERT ends, we will get/cache this once from the marketplace when needed, and it likely won't be RSA.
|
BIO *bio = BIO_new_mem_buf((void*)qPrintable(EntityItem::_marketplacePublicKey), -1);
|
||||||
const char publicKey[] = "-----BEGIN PUBLIC KEY-----\n\
|
|
||||||
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALCoBiDAZOClO26tC5pd7JikBL61WIgp\n\
|
|
||||||
AqbcNnrV/TcG6LPI7ZbiMjdUixmTNvYMRZH3Wlqtl2IKG1W68y3stKECAwEAAQ==\n\
|
|
||||||
-----END PUBLIC KEY-----";
|
|
||||||
BIO *bio = BIO_new_mem_buf((void*)publicKey, sizeof(publicKey));
|
|
||||||
EVP_PKEY* evp_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
|
EVP_PKEY* evp_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
|
||||||
RSA* rsa = EVP_PKEY_get1_RSA(evp_key);
|
RSA* rsa = EVP_PKEY_get1_RSA(evp_key);
|
||||||
bool answer = RSA_verify(NID_sha256, text, textLength, signature, signatureLength, rsa);
|
bool answer = RSA_verify(NID_sha256, text, textLength, signature, signatureLength, rsa);
|
||||||
|
@ -3006,3 +2973,34 @@ void EntityItem::somethingChangedNotification() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EntityItem::retrieveMarketplacePublicKey() {
|
||||||
|
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||||
|
QNetworkRequest networkRequest;
|
||||||
|
networkRequest.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true);
|
||||||
|
QUrl requestURL = NetworkingConstants::METAVERSE_SERVER_URL;
|
||||||
|
requestURL.setPath("/api/v1/commerce/marketplace_key");
|
||||||
|
QJsonObject request;
|
||||||
|
networkRequest.setUrl(requestURL);
|
||||||
|
|
||||||
|
QNetworkReply* networkReply = NULL;
|
||||||
|
networkReply = networkAccessManager.get(networkRequest);
|
||||||
|
|
||||||
|
connect(networkReply, &QNetworkReply::finished, [=]() {
|
||||||
|
QJsonObject jsonObject = QJsonDocument::fromJson(networkReply->readAll()).object();
|
||||||
|
jsonObject = jsonObject["data"].toObject();
|
||||||
|
|
||||||
|
if (networkReply->error() == QNetworkReply::NoError) {
|
||||||
|
if (!jsonObject["public_key"].toString().isEmpty()) {
|
||||||
|
EntityItem::_marketplacePublicKey = jsonObject["public_key"].toString();
|
||||||
|
qCWarning(entities) << "Marketplace public key has been set to" << _marketplacePublicKey;
|
||||||
|
} else {
|
||||||
|
qCWarning(entities) << "Marketplace public key is empty!";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qCWarning(entities) << "Call to" << networkRequest.url() << "failed! Error:" << networkReply->error();
|
||||||
|
}
|
||||||
|
|
||||||
|
networkReply->deleteLater();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
@ -36,9 +36,6 @@
|
||||||
#include "SimulationFlags.h"
|
#include "SimulationFlags.h"
|
||||||
#include "EntityDynamicInterface.h"
|
#include "EntityDynamicInterface.h"
|
||||||
|
|
||||||
// FIXME: The server-side marketplace will soon create the certificateID. At that point, all of the DEBUG_CERT stuff will go away.
|
|
||||||
#define DEBUG_CERT 1
|
|
||||||
|
|
||||||
class EntitySimulation;
|
class EntitySimulation;
|
||||||
class EntityTreeElement;
|
class EntityTreeElement;
|
||||||
class EntityTreeElementExtraEncodeData;
|
class EntityTreeElementExtraEncodeData;
|
||||||
|
@ -331,9 +328,6 @@ public:
|
||||||
QByteArray getStaticCertificateJSON() const;
|
QByteArray getStaticCertificateJSON() const;
|
||||||
QByteArray getStaticCertificateHash() const;
|
QByteArray getStaticCertificateHash() const;
|
||||||
bool verifyStaticCertificateProperties();
|
bool verifyStaticCertificateProperties();
|
||||||
#ifdef DEBUG_CERT
|
|
||||||
QString computeCertificateID();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// TODO: get rid of users of getRadius()...
|
// TODO: get rid of users of getRadius()...
|
||||||
float getRadius() const;
|
float getRadius() const;
|
||||||
|
@ -484,6 +478,9 @@ public:
|
||||||
ChangeHandlerId registerChangeHandler(const ChangeHandlerCallback& handler);
|
ChangeHandlerId registerChangeHandler(const ChangeHandlerCallback& handler);
|
||||||
void deregisterChangeHandler(const ChangeHandlerId& changeHandlerId);
|
void deregisterChangeHandler(const ChangeHandlerId& changeHandlerId);
|
||||||
|
|
||||||
|
static QString _marketplacePublicKey;
|
||||||
|
static void retrieveMarketplacePublicKey();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QHash<ChangeHandlerId, ChangeHandlerCallback> _changeHandlers;
|
QHash<ChangeHandlerId, ChangeHandlerCallback> _changeHandlers;
|
||||||
|
|
||||||
|
@ -635,7 +632,6 @@ protected:
|
||||||
quint64 _lastUpdatedVelocityTimestamp { 0 };
|
quint64 _lastUpdatedVelocityTimestamp { 0 };
|
||||||
quint64 _lastUpdatedAngularVelocityTimestamp { 0 };
|
quint64 _lastUpdatedAngularVelocityTimestamp { 0 };
|
||||||
quint64 _lastUpdatedAccelerationTimestamp { 0 };
|
quint64 _lastUpdatedAccelerationTimestamp { 0 };
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_EntityItem_h
|
#endif // hifi_EntityItem_h
|
||||||
|
|
|
@ -1778,18 +1778,3 @@ bool EntityScriptingInterface::verifyStaticCertificateProperties(const QUuid& en
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG_CERT
|
|
||||||
QString EntityScriptingInterface::computeCertificateID(const QUuid& entityID) {
|
|
||||||
QString result { "" };
|
|
||||||
if (_entityTree) {
|
|
||||||
_entityTree->withReadLock([&] {
|
|
||||||
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(entityID));
|
|
||||||
if (entity) {
|
|
||||||
result = entity->computeCertificateID();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
#endif
|
|
|
@ -387,9 +387,6 @@ public slots:
|
||||||
Q_INVOKABLE glm::mat4 getEntityLocalTransform(const QUuid& entityID);
|
Q_INVOKABLE glm::mat4 getEntityLocalTransform(const QUuid& entityID);
|
||||||
|
|
||||||
Q_INVOKABLE bool verifyStaticCertificateProperties(const QUuid& entityID);
|
Q_INVOKABLE bool verifyStaticCertificateProperties(const QUuid& entityID);
|
||||||
#ifdef DEBUG_CERT
|
|
||||||
Q_INVOKABLE QString computeCertificateID(const QUuid& entityID);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
|
void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);
|
||||||
|
|
|
@ -71,6 +71,8 @@ EntityTree::EntityTree(bool shouldReaverage) :
|
||||||
Octree(shouldReaverage)
|
Octree(shouldReaverage)
|
||||||
{
|
{
|
||||||
resetClientEditStats();
|
resetClientEditStats();
|
||||||
|
|
||||||
|
EntityItem::retrieveMarketplacePublicKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityTree::~EntityTree() {
|
EntityTree::~EntityTree() {
|
||||||
|
@ -1523,9 +1525,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c
|
||||||
_totalCreates++;
|
_totalCreates++;
|
||||||
|
|
||||||
if (newEntity && isCertified && getIsServer()) {
|
if (newEntity && isCertified && getIsServer()) {
|
||||||
// ZRF FIXME!!!
|
if (!newEntity->verifyStaticCertificateProperties()) {
|
||||||
//if (!newEntity->verifyStaticCertificateProperties()) {
|
|
||||||
if (false) {
|
|
||||||
qCDebug(entities) << "User" << senderNode->getUUID()
|
qCDebug(entities) << "User" << senderNode->getUUID()
|
||||||
<< "attempted to add a certified entity with ID" << entityItemID << "which failed"
|
<< "attempted to add a certified entity with ID" << entityItemID << "which failed"
|
||||||
<< "static certificate verification.";
|
<< "static certificate verification.";
|
||||||
|
|
Loading…
Reference in a new issue