mirror of
https://github.com/overte-org/overte.git
synced 2025-04-25 14:54:29 +02:00
Merge branch 'ECWallet' of https://github.com/MandatoryMania/hifi into MandatoryMania-ECWallet
This commit is contained in:
commit
ca80c9b764
5 changed files with 151 additions and 182 deletions
|
@ -27,11 +27,11 @@
|
|||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/ecdsa.h>
|
||||
|
||||
// I know, right? But per https://www.openssl.org/docs/faq.html
|
||||
// this avoids OPENSSL_Uplink(00007FF847238000,08): no OPENSSL_Applink
|
||||
|
@ -78,18 +78,19 @@ int passwordCallback(char* password, int maxPasswordSize, int rwFlag, void* u) {
|
|||
}
|
||||
}
|
||||
|
||||
RSA* readKeys(const char* filename) {
|
||||
EC_KEY* readKeys(const char* filename) {
|
||||
FILE* fp;
|
||||
RSA* key = NULL;
|
||||
EC_KEY *key = NULL;
|
||||
if ((fp = fopen(filename, "rt"))) {
|
||||
// file opened successfully
|
||||
qCDebug(commerce) << "opened key file" << filename;
|
||||
if ((key = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL))) {
|
||||
|
||||
if ((key = PEM_read_EC_PUBKEY(fp, NULL, NULL, NULL))) {
|
||||
// now read private key
|
||||
|
||||
qCDebug(commerce) << "read public key";
|
||||
|
||||
if ((key = PEM_read_RSAPrivateKey(fp, &key, passwordCallback, NULL))) {
|
||||
if ((key = PEM_read_ECPrivateKey(fp, &key, passwordCallback, NULL))) {
|
||||
qCDebug(commerce) << "read private key";
|
||||
fclose(fp);
|
||||
return key;
|
||||
|
@ -137,18 +138,18 @@ bool Wallet::writeBackupInstructions() {
|
|||
return retval;
|
||||
}
|
||||
|
||||
bool writeKeys(const char* filename, RSA* keys) {
|
||||
bool writeKeys(const char* filename, EC_KEY* keys) {
|
||||
FILE* fp;
|
||||
bool retval = false;
|
||||
if ((fp = fopen(filename, "wt"))) {
|
||||
if (!PEM_write_RSAPublicKey(fp, keys)) {
|
||||
if (!PEM_write_EC_PUBKEY(fp, keys)) {
|
||||
fclose(fp);
|
||||
qCDebug(commerce) << "failed to write public key";
|
||||
QFile(QString(filename)).remove();
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (!PEM_write_RSAPrivateKey(fp, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) {
|
||||
if (!PEM_write_ECPrivateKey(fp, keys, EVP_des_ede3_cbc(), NULL, 0, passwordCallback, NULL)) {
|
||||
fclose(fp);
|
||||
qCDebug(commerce) << "failed to write private key";
|
||||
QFile(QString(filename)).remove();
|
||||
|
@ -164,50 +165,29 @@ bool writeKeys(const char* filename, RSA* keys) {
|
|||
return retval;
|
||||
}
|
||||
|
||||
// copied (without emits for various signals) from libraries/networking/src/RSAKeypairGenerator.cpp.
|
||||
// We will have a different implementation in practice, but this gives us a start for now
|
||||
//
|
||||
// TODO: we don't really use the private keys returned - we can see how this evolves, but probably
|
||||
// we should just return a list of public keys?
|
||||
// or perhaps return the RSA* instead?
|
||||
QPair<QByteArray*, QByteArray*> generateRSAKeypair() {
|
||||
|
||||
RSA* keyPair = RSA_new();
|
||||
BIGNUM* exponent = BN_new();
|
||||
QPair<QByteArray*, QByteArray*> generateECKeypair() {
|
||||
|
||||
EC_KEY* keyPair = EC_KEY_new_by_curve_name(NID_secp256k1);
|
||||
QPair<QByteArray*, QByteArray*> retval;
|
||||
|
||||
const unsigned long RSA_KEY_EXPONENT = 65537;
|
||||
BN_set_word(exponent, RSA_KEY_EXPONENT);
|
||||
|
||||
// seed the random number generator before we call RSA_generate_key_ex
|
||||
srand(time(NULL));
|
||||
|
||||
const int RSA_KEY_BITS = 2048;
|
||||
|
||||
if (!RSA_generate_key_ex(keyPair, RSA_KEY_BITS, exponent, NULL)) {
|
||||
qCDebug(commerce) << "Error generating 2048-bit RSA Keypair -" << ERR_get_error();
|
||||
|
||||
// we're going to bust out of here but first we cleanup the BIGNUM
|
||||
BN_free(exponent);
|
||||
EC_KEY_set_asn1_flag(keyPair, OPENSSL_EC_NAMED_CURVE);
|
||||
if (!EC_KEY_generate_key(keyPair)) {
|
||||
qCDebug(commerce) << "Error generating EC Keypair -" << ERR_get_error();
|
||||
return retval;
|
||||
}
|
||||
|
||||
// we don't need the BIGNUM anymore so clean that up
|
||||
BN_free(exponent);
|
||||
|
||||
// grab the public key and private key from the file
|
||||
unsigned char* publicKeyDER = NULL;
|
||||
int publicKeyLength = i2d_RSAPublicKey(keyPair, &publicKeyDER);
|
||||
int publicKeyLength = i2d_EC_PUBKEY(keyPair, &publicKeyDER);
|
||||
|
||||
unsigned char* privateKeyDER = NULL;
|
||||
int privateKeyLength = i2d_RSAPrivateKey(keyPair, &privateKeyDER);
|
||||
int privateKeyLength = i2d_ECPrivateKey(keyPair, &privateKeyDER);
|
||||
|
||||
if (publicKeyLength <= 0 || privateKeyLength <= 0) {
|
||||
qCDebug(commerce) << "Error getting DER public or private key from RSA struct -" << ERR_get_error();
|
||||
qCDebug(commerce) << "Error getting DER public or private key from EC struct -" << ERR_get_error();
|
||||
|
||||
|
||||
// cleanup the RSA struct
|
||||
RSA_free(keyPair);
|
||||
// cleanup the EC struct
|
||||
EC_KEY_free(keyPair);
|
||||
|
||||
// cleanup the public and private key DER data, if required
|
||||
if (publicKeyLength > 0) {
|
||||
|
@ -227,7 +207,7 @@ QPair<QByteArray*, QByteArray*> generateRSAKeypair() {
|
|||
return retval;
|
||||
}
|
||||
|
||||
RSA_free(keyPair);
|
||||
EC_KEY_free(keyPair);
|
||||
|
||||
// prepare the return values. TODO: Fix this - we probably don't really even want the
|
||||
// private key at all (better to read it when we need it?). Or maybe we do, when we have
|
||||
|
@ -245,18 +225,18 @@ QPair<QByteArray*, QByteArray*> generateRSAKeypair() {
|
|||
// the public key can just go into a byte array
|
||||
QByteArray readPublicKey(const char* filename) {
|
||||
FILE* fp;
|
||||
RSA* key = NULL;
|
||||
EC_KEY* key = NULL;
|
||||
if ((fp = fopen(filename, "r"))) {
|
||||
// file opened successfully
|
||||
qCDebug(commerce) << "opened key file" << filename;
|
||||
if ((key = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL))) {
|
||||
if ((key = PEM_read_EC_PUBKEY(fp, NULL, NULL, NULL))) {
|
||||
// file read successfully
|
||||
unsigned char* publicKeyDER = NULL;
|
||||
int publicKeyLength = i2d_RSAPublicKey(key, &publicKeyDER);
|
||||
int publicKeyLength = i2d_EC_PUBKEY(key, &publicKeyDER);
|
||||
// TODO: check for 0 length?
|
||||
|
||||
// cleanup
|
||||
RSA_free(key);
|
||||
EC_KEY_free(key);
|
||||
fclose(fp);
|
||||
|
||||
qCDebug(commerce) << "parsed public key file successfully";
|
||||
|
@ -274,15 +254,15 @@ QByteArray readPublicKey(const char* filename) {
|
|||
return QByteArray();
|
||||
}
|
||||
|
||||
// the private key should be read/copied into heap memory. For now, we need the RSA struct
|
||||
// so I'll return that. Note we need to RSA_free(key) later!!!
|
||||
RSA* readPrivateKey(const char* filename) {
|
||||
// the private key should be read/copied into heap memory. For now, we need the EC_KEY struct
|
||||
// so I'll return that.
|
||||
EC_KEY* readPrivateKey(const char* filename) {
|
||||
FILE* fp;
|
||||
RSA* key = NULL;
|
||||
EC_KEY* key = NULL;
|
||||
if ((fp = fopen(filename, "r"))) {
|
||||
// file opened successfully
|
||||
qCDebug(commerce) << "opened key file" << filename;
|
||||
if ((key = PEM_read_RSAPrivateKey(fp, &key, passwordCallback, NULL))) {
|
||||
if ((key = PEM_read_ECPrivateKey(fp, &key, passwordCallback, NULL))) {
|
||||
qCDebug(commerce) << "parsed private key file successfully";
|
||||
|
||||
} else {
|
||||
|
@ -509,7 +489,7 @@ bool Wallet::walletIsAuthenticatedWithPassphrase() {
|
|||
|
||||
if (publicKey.size() > 0) {
|
||||
if (auto key = readPrivateKey(keyFilePath().toStdString().c_str())) {
|
||||
RSA_free(key);
|
||||
EC_KEY_free(key);
|
||||
|
||||
// be sure to add the public key so we don't do this over and over
|
||||
_publicKeys.push_back(publicKey.toBase64());
|
||||
|
@ -525,7 +505,7 @@ bool Wallet::generateKeyPair() {
|
|||
initialize();
|
||||
|
||||
qCInfo(commerce) << "Generating keypair.";
|
||||
auto keyPair = generateRSAKeypair();
|
||||
auto keyPair = generateECKeypair();
|
||||
|
||||
writeBackupInstructions();
|
||||
|
||||
|
@ -558,24 +538,23 @@ QStringList Wallet::listPublicKeys() {
|
|||
// encoded string representing the signature (suitable for http, etc...)
|
||||
QString Wallet::signWithKey(const QByteArray& text, const QString& key) {
|
||||
qCInfo(commerce) << "Signing text.";
|
||||
RSA* rsaPrivateKey = NULL;
|
||||
if ((rsaPrivateKey = readPrivateKey(keyFilePath().toStdString().c_str()))) {
|
||||
QByteArray signature(RSA_size(rsaPrivateKey), 0);
|
||||
EC_KEY* ecPrivateKey = NULL;
|
||||
if ((ecPrivateKey = readPrivateKey(keyFilePath().toStdString().c_str()))) {
|
||||
QByteArray signature(ECDSA_size(ecPrivateKey), 0);
|
||||
unsigned int signatureBytes = 0;
|
||||
|
||||
QByteArray hashedPlaintext = QCryptographicHash::hash(text, QCryptographicHash::Sha256);
|
||||
|
||||
int encryptReturn = RSA_sign(NID_sha256,
|
||||
reinterpret_cast<const unsigned char*>(hashedPlaintext.constData()),
|
||||
hashedPlaintext.size(),
|
||||
reinterpret_cast<unsigned char*>(signature.data()),
|
||||
&signatureBytes,
|
||||
rsaPrivateKey);
|
||||
|
||||
// free the private key RSA struct now that we are done with it
|
||||
RSA_free(rsaPrivateKey);
|
||||
int retrn = ECDSA_sign(0,
|
||||
reinterpret_cast<const unsigned char*>(hashedPlaintext.constData()),
|
||||
hashedPlaintext.size(),
|
||||
reinterpret_cast<unsigned char*>(signature.data()),
|
||||
&signatureBytes, ecPrivateKey);
|
||||
|
||||
if (encryptReturn != -1) {
|
||||
EC_KEY_free(ecPrivateKey);
|
||||
|
||||
if (retrn != -1) {
|
||||
return signature.toBase64();
|
||||
}
|
||||
}
|
||||
|
@ -674,7 +653,7 @@ void Wallet::reset() {
|
|||
keyFile.remove();
|
||||
}
|
||||
bool Wallet::writeWallet(const QString& newPassphrase) {
|
||||
RSA* keys = readKeys(keyFilePath().toStdString().c_str());
|
||||
EC_KEY* keys = readKeys(keyFilePath().toStdString().c_str());
|
||||
if (keys) {
|
||||
// we read successfully, so now write to a new temp file
|
||||
QString tempFileName = QString("%1.%2").arg(keyFilePath(), QString("temp"));
|
||||
|
@ -720,82 +699,86 @@ bool Wallet::changePassphrase(const QString& newPassphrase) {
|
|||
void Wallet::handleChallengeOwnershipPacket(QSharedPointer<ReceivedMessage> packet, SharedNodePointer sendingNode) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
// With EC keys, we receive a nonce from the metaverse server, which is signed
|
||||
// here with the private key and returned. Verification is done at server.
|
||||
|
||||
bool challengeOriginatedFromClient = packet->getType() == PacketType::ChallengeOwnershipRequest;
|
||||
unsigned char decryptedText[64];
|
||||
int status;
|
||||
int certIDByteArraySize;
|
||||
int encryptedTextByteArraySize;
|
||||
int textByteArraySize;
|
||||
int challengingNodeUUIDByteArraySize;
|
||||
|
||||
packet->readPrimitive(&certIDByteArraySize);
|
||||
packet->readPrimitive(&encryptedTextByteArraySize);
|
||||
packet->readPrimitive(&textByteArraySize); // returns a cast char*, size
|
||||
if (challengeOriginatedFromClient) {
|
||||
packet->readPrimitive(&challengingNodeUUIDByteArraySize);
|
||||
}
|
||||
|
||||
// "encryptedText" is now a series of random bytes, a nonce
|
||||
QByteArray certID = packet->read(certIDByteArraySize);
|
||||
QByteArray encryptedText = packet->read(encryptedTextByteArraySize);
|
||||
QByteArray text = packet->read(textByteArraySize);
|
||||
QByteArray challengingNodeUUID;
|
||||
if (challengeOriginatedFromClient) {
|
||||
challengingNodeUUID = packet->read(challengingNodeUUIDByteArraySize);
|
||||
}
|
||||
|
||||
RSA* rsa = readKeys(keyFilePath().toStdString().c_str());
|
||||
int decryptionStatus = -1;
|
||||
EC_KEY* ec = readKeys(keyFilePath().toStdString().c_str());
|
||||
QString sig;
|
||||
|
||||
if (rsa) {
|
||||
if (ec) {
|
||||
ERR_clear_error();
|
||||
decryptionStatus = RSA_private_decrypt(encryptedTextByteArraySize,
|
||||
reinterpret_cast<const unsigned char*>(encryptedText.constData()),
|
||||
decryptedText,
|
||||
rsa,
|
||||
RSA_PKCS1_OAEP_PADDING);
|
||||
|
||||
RSA_free(rsa);
|
||||
sig = signWithKey(text, ""); // base64 signature, QByteArray cast (on return) to QString
|
||||
status = 1;
|
||||
} else {
|
||||
qCDebug(commerce) << "During entity ownership challenge, creating the RSA object failed.";
|
||||
qCDebug(commerce) << "During entity ownership challenge, creating the EC-signed nonce failed.";
|
||||
status = -1;
|
||||
}
|
||||
|
||||
QByteArray decryptedTextByteArray;
|
||||
if (decryptionStatus > -1) {
|
||||
decryptedTextByteArray = QByteArray(reinterpret_cast<const char*>(decryptedText), decryptionStatus);
|
||||
|
||||
EC_KEY_free(ec);
|
||||
QByteArray ba = sig.toLocal8Bit();
|
||||
const char *sigChar = ba.data();
|
||||
|
||||
QByteArray textByteArray;
|
||||
if (status > -1) {
|
||||
textByteArray = QByteArray(sigChar, status);
|
||||
}
|
||||
int decryptedTextByteArraySize = decryptedTextByteArray.size();
|
||||
textByteArraySize = textByteArray.size();
|
||||
int certIDSize = certID.size();
|
||||
// setup the packet
|
||||
if (challengeOriginatedFromClient) {
|
||||
auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnershipReply,
|
||||
certIDSize + decryptedTextByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int),
|
||||
auto textPacket = NLPacket::create(PacketType::ChallengeOwnershipReply,
|
||||
certIDSize + textByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int),
|
||||
true);
|
||||
|
||||
decryptedTextPacket->writePrimitive(certIDSize);
|
||||
decryptedTextPacket->writePrimitive(decryptedTextByteArraySize);
|
||||
decryptedTextPacket->writePrimitive(challengingNodeUUIDByteArraySize);
|
||||
decryptedTextPacket->write(certID);
|
||||
decryptedTextPacket->write(decryptedTextByteArray);
|
||||
decryptedTextPacket->write(challengingNodeUUID);
|
||||
textPacket->writePrimitive(certIDSize);
|
||||
textPacket->writePrimitive(textByteArraySize);
|
||||
textPacket->writePrimitive(challengingNodeUUIDByteArraySize);
|
||||
textPacket->write(certID);
|
||||
textPacket->write(textByteArray);
|
||||
textPacket->write(challengingNodeUUID);
|
||||
|
||||
qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing decrypted text" << decryptedTextByteArray << "for CertID" << certID;
|
||||
qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing signed text" << textByteArray << "for CertID" << certID;
|
||||
|
||||
nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode);
|
||||
nodeList->sendPacket(std::move(textPacket), *sendingNode);
|
||||
} else {
|
||||
auto decryptedTextPacket = NLPacket::create(PacketType::ChallengeOwnership, certIDSize + decryptedTextByteArraySize + 2 * sizeof(int), true);
|
||||
auto textPacket = NLPacket::create(PacketType::ChallengeOwnership, certIDSize + textByteArraySize + 2 * sizeof(int), true);
|
||||
|
||||
decryptedTextPacket->writePrimitive(certIDSize);
|
||||
decryptedTextPacket->writePrimitive(decryptedTextByteArraySize);
|
||||
decryptedTextPacket->write(certID);
|
||||
decryptedTextPacket->write(decryptedTextByteArray);
|
||||
textPacket->writePrimitive(certIDSize);
|
||||
textPacket->writePrimitive(textByteArraySize);
|
||||
textPacket->write(certID);
|
||||
textPacket->write(textByteArray);
|
||||
|
||||
qCDebug(commerce) << "Sending ChallengeOwnership Packet containing decrypted text" << decryptedTextByteArray << "for CertID" << certID;
|
||||
qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << textByteArray << "for CertID" << certID;
|
||||
|
||||
nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode);
|
||||
nodeList->sendPacket(std::move(textPacket), *sendingNode);
|
||||
}
|
||||
|
||||
if (decryptionStatus == -1) {
|
||||
qCDebug(commerce) << "During entity ownership challenge, decrypting the encrypted text failed.";
|
||||
if (status == -1) {
|
||||
qCDebug(commerce) << "During entity ownership challenge, signing the 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;
|
||||
qCWarning(entities) << "EC error:" << error_str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -818,4 +801,4 @@ void Wallet::getWalletStatus() {
|
|||
walletScriptingInterface->setWalletStatus(status);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -324,21 +324,21 @@ void ContextOverlayInterface::openInspectionCertificate() {
|
|||
QString ownerKey = jsonObject["transfer_recipient_key"].toString();
|
||||
|
||||
QByteArray certID = entityProperties.getCertificateID().toUtf8();
|
||||
QByteArray encryptedText = DependencyManager::get<EntityTreeRenderer>()->getTree()->computeEncryptedNonce(certID, ownerKey);
|
||||
QByteArray text = DependencyManager::get<EntityTreeRenderer>()->getTree()->computeNonce(certID, ownerKey);
|
||||
QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122();
|
||||
|
||||
int certIDByteArraySize = certID.length();
|
||||
int encryptedTextByteArraySize = encryptedText.length();
|
||||
int textByteArraySize = text.length();
|
||||
int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length();
|
||||
|
||||
auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest,
|
||||
certIDByteArraySize + encryptedTextByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int),
|
||||
certIDByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int),
|
||||
true);
|
||||
challengeOwnershipPacket->writePrimitive(certIDByteArraySize);
|
||||
challengeOwnershipPacket->writePrimitive(encryptedTextByteArraySize);
|
||||
challengeOwnershipPacket->writePrimitive(textByteArraySize);
|
||||
challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize);
|
||||
challengeOwnershipPacket->write(certID);
|
||||
challengeOwnershipPacket->write(encryptedText);
|
||||
challengeOwnershipPacket->write(text);
|
||||
challengeOwnershipPacket->write(nodeToChallengeByteArray);
|
||||
nodeList->sendPacket(std::move(challengeOwnershipPacket), *entityServer);
|
||||
|
||||
|
@ -419,16 +419,16 @@ void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer
|
|||
_challengeOwnershipTimeoutTimer.stop();
|
||||
|
||||
int certIDByteArraySize;
|
||||
int decryptedTextByteArraySize;
|
||||
int textByteArraySize;
|
||||
|
||||
packet->readPrimitive(&certIDByteArraySize);
|
||||
packet->readPrimitive(&decryptedTextByteArraySize);
|
||||
packet->readPrimitive(&textByteArraySize);
|
||||
|
||||
QString certID(packet->read(certIDByteArraySize));
|
||||
QString decryptedText(packet->read(decryptedTextByteArraySize));
|
||||
QString text(packet->read(textByteArraySize));
|
||||
|
||||
EntityItemID id;
|
||||
bool verificationSuccess = DependencyManager::get<EntityTreeRenderer>()->getTree()->verifyDecryptedNonce(certID, decryptedText, id);
|
||||
bool verificationSuccess = DependencyManager::get<EntityTreeRenderer>()->getTree()->verifyNonce(certID, text, id);
|
||||
|
||||
if (verificationSuccess) {
|
||||
emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS));
|
||||
|
|
|
@ -13,21 +13,18 @@
|
|||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QtCore/QJsonDocument>
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/ecdsa.h>
|
||||
#include <NetworkingConstants.h>
|
||||
#include <NetworkAccessManager.h>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
|
||||
#include <ByteCountCoding.h>
|
||||
#include <GLMHelpers.h>
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <Extents.h>
|
||||
|
||||
#include "EntitiesLogging.h"
|
||||
#include "EntityItem.h"
|
||||
#include "EntityItemProperties.h"
|
||||
|
@ -2523,8 +2520,8 @@ bool EntityItemProperties::verifyStaticCertificateProperties() {
|
|||
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) {
|
||||
EC_KEY* ec = EVP_PKEY_get1_EC_KEY(evp_key);
|
||||
if (ec) {
|
||||
const QByteArray digestByteArray = getStaticCertificateHash();
|
||||
const unsigned char* digest = reinterpret_cast<const unsigned char*>(digestByteArray.constData());
|
||||
int digestLength = digestByteArray.length();
|
||||
|
@ -2534,22 +2531,25 @@ bool EntityItemProperties::verifyStaticCertificateProperties() {
|
|||
int signatureLength = signatureByteArray.length();
|
||||
|
||||
ERR_clear_error();
|
||||
bool answer = RSA_verify(NID_sha256,
|
||||
// ECSDA verification prototype: note that type is currently ignored
|
||||
// int ECDSA_verify(int type, const unsigned char *dgst, int dgstlen,
|
||||
// const unsigned char *sig, int siglen, EC_KEY *eckey);
|
||||
bool answer = ECDSA_verify(0,
|
||||
digest,
|
||||
digestLength,
|
||||
signature,
|
||||
signatureLength,
|
||||
rsa);
|
||||
ec);
|
||||
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
|
||||
qCWarning(entities) << "ERROR while verifying static certificate properties! EC 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);
|
||||
EC_KEY_free(ec);
|
||||
if (bio) {
|
||||
BIO_free(bio);
|
||||
}
|
||||
|
@ -2566,7 +2566,7 @@ bool EntityItemProperties::verifyStaticCertificateProperties() {
|
|||
}
|
||||
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;
|
||||
qCWarning(entities) << "Failed to verify static certificate properties! EC error:" << error_str;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
|
@ -2575,7 +2575,7 @@ bool EntityItemProperties::verifyStaticCertificateProperties() {
|
|||
}
|
||||
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;
|
||||
qCWarning(entities) << "Failed to verify static certificate properties! EC error:" << error_str;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,9 +12,7 @@
|
|||
#include "EntityTree.h"
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QQueue>
|
||||
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <NetworkingConstants.h>
|
||||
|
@ -1167,36 +1165,24 @@ void EntityTree::startPendingTransferStatusTimer(const QString& certID, const En
|
|||
transferStatusRetryTimer->start(90000);
|
||||
}
|
||||
|
||||
QByteArray EntityTree::computeEncryptedNonce(const QString& certID, const QString ownerKey) {
|
||||
QString ownerKeyWithHeaders = ("-----BEGIN RSA PUBLIC KEY-----\n" + ownerKey + "\n-----END RSA PUBLIC KEY-----");
|
||||
QByteArray EntityTree::computeNonce(const QString& certID, const QString ownerKey) {
|
||||
QString ownerKeyWithHeaders = ("-----BEGIN ECDSA PUBLIC KEY-----\n" + ownerKey + "\n-----END ECDSA PUBLIC KEY-----");
|
||||
BIO* bio = BIO_new_mem_buf((void*)ownerKeyWithHeaders.toUtf8().constData(), -1);
|
||||
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); // NO NEWLINE
|
||||
RSA* rsa = PEM_read_bio_RSAPublicKey(bio, NULL, NULL, NULL);
|
||||
EC_KEY* ec = PEM_read_bio_EC_PUBKEY(bio, NULL, NULL, NULL);
|
||||
|
||||
if (ec) {
|
||||
QUuid nonce = QUuid::createUuid(); //random, 5-hex value, separated by "-"
|
||||
QByteArray nonceBytes = nonce.toByteArray();
|
||||
|
||||
if (rsa) {
|
||||
QUuid nonce = QUuid::createUuid();
|
||||
const unsigned int textLength = nonce.toString().length();
|
||||
QByteArray encryptedText(RSA_size(rsa), 0);
|
||||
const int encryptStatus = RSA_public_encrypt(textLength,
|
||||
reinterpret_cast<const unsigned char*>(qPrintable(nonce.toString())),
|
||||
reinterpret_cast<unsigned char*>(encryptedText.data()),
|
||||
rsa,
|
||||
RSA_PKCS1_OAEP_PADDING);
|
||||
if (bio) {
|
||||
BIO_free(bio);
|
||||
}
|
||||
RSA_free(rsa);
|
||||
if (encryptStatus == -1) {
|
||||
long error = ERR_get_error();
|
||||
const char* error_str = ERR_error_string(error, NULL);
|
||||
qCWarning(entities) << "Unable to compute encrypted nonce for" << certID << "\nRSA error:" << error_str;
|
||||
return "";
|
||||
}
|
||||
|
||||
QWriteLocker locker(&_certNonceMapLock);
|
||||
_certNonceMap.insert(certID, nonce);
|
||||
|
||||
return encryptedText;
|
||||
return nonceBytes;
|
||||
} else {
|
||||
if (bio) {
|
||||
BIO_free(bio);
|
||||
|
@ -1205,7 +1191,7 @@ QByteArray EntityTree::computeEncryptedNonce(const QString& certID, const QStrin
|
|||
}
|
||||
}
|
||||
|
||||
bool EntityTree::verifyDecryptedNonce(const QString& certID, const QString& decryptedNonce, EntityItemID& id) {
|
||||
bool EntityTree::verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id) {
|
||||
{
|
||||
QReadLocker certIdMapLocker(&_entityCertificateIDMapLock);
|
||||
id = _entityCertificateIDMap.value(certID);
|
||||
|
@ -1217,13 +1203,13 @@ bool EntityTree::verifyDecryptedNonce(const QString& certID, const QString& decr
|
|||
actualNonce = _certNonceMap.take(certID).toString();
|
||||
}
|
||||
|
||||
bool verificationSuccess = (actualNonce == decryptedNonce);
|
||||
bool verificationSuccess = (actualNonce == nonce);
|
||||
|
||||
if (verificationSuccess) {
|
||||
qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded.";
|
||||
} else {
|
||||
qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed."
|
||||
<< "\nActual nonce:" << actualNonce << "\nDecrypted nonce:" << decryptedNonce;
|
||||
<< "\nActual nonce:" << actualNonce << "\nonce:" << nonce;
|
||||
}
|
||||
|
||||
return verificationSuccess;
|
||||
|
@ -1231,67 +1217,67 @@ bool EntityTree::verifyDecryptedNonce(const QString& certID, const QString& decr
|
|||
|
||||
void EntityTree::processChallengeOwnershipRequestPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) {
|
||||
int certIDByteArraySize;
|
||||
int encryptedTextByteArraySize;
|
||||
int textByteArraySize;
|
||||
int nodeToChallengeByteArraySize;
|
||||
|
||||
message.readPrimitive(&certIDByteArraySize);
|
||||
message.readPrimitive(&encryptedTextByteArraySize);
|
||||
message.readPrimitive(&textByteArraySize);
|
||||
message.readPrimitive(&nodeToChallengeByteArraySize);
|
||||
|
||||
QByteArray certID(message.read(certIDByteArraySize));
|
||||
QByteArray encryptedText(message.read(encryptedTextByteArraySize));
|
||||
QByteArray text(message.read(textByteArraySize));
|
||||
QByteArray nodeToChallenge(message.read(nodeToChallengeByteArraySize));
|
||||
|
||||
sendChallengeOwnershipRequestPacket(certID, encryptedText, nodeToChallenge, sourceNode);
|
||||
sendChallengeOwnershipRequestPacket(certID, text, nodeToChallenge, sourceNode);
|
||||
}
|
||||
|
||||
void EntityTree::processChallengeOwnershipReplyPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
int certIDByteArraySize;
|
||||
int decryptedTextByteArraySize;
|
||||
int textByteArraySize;
|
||||
int challengingNodeUUIDByteArraySize;
|
||||
|
||||
message.readPrimitive(&certIDByteArraySize);
|
||||
message.readPrimitive(&decryptedTextByteArraySize);
|
||||
message.readPrimitive(&textByteArraySize);
|
||||
message.readPrimitive(&challengingNodeUUIDByteArraySize);
|
||||
|
||||
QByteArray certID(message.read(certIDByteArraySize));
|
||||
QByteArray decryptedText(message.read(decryptedTextByteArraySize));
|
||||
QByteArray text(message.read(textByteArraySize));
|
||||
QUuid challengingNode = QUuid::fromRfc4122(message.read(challengingNodeUUIDByteArraySize));
|
||||
|
||||
auto challengeOwnershipReplyPacket = NLPacket::create(PacketType::ChallengeOwnershipReply,
|
||||
certIDByteArraySize + decryptedText.length() + 2 * sizeof(int),
|
||||
certIDByteArraySize + text.length() + 2 * sizeof(int),
|
||||
true);
|
||||
challengeOwnershipReplyPacket->writePrimitive(certIDByteArraySize);
|
||||
challengeOwnershipReplyPacket->writePrimitive(decryptedText.length());
|
||||
challengeOwnershipReplyPacket->writePrimitive(text.length());
|
||||
challengeOwnershipReplyPacket->write(certID);
|
||||
challengeOwnershipReplyPacket->write(decryptedText);
|
||||
challengeOwnershipReplyPacket->write(text);
|
||||
|
||||
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
|
||||
// 1. Obtain a nonce
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
QByteArray encryptedText = computeEncryptedNonce(certID, ownerKey);
|
||||
QByteArray text = computeNonce(certID, ownerKey);
|
||||
|
||||
if (encryptedText == "") {
|
||||
qCDebug(entities) << "CRITICAL ERROR: Couldn't compute encrypted nonce. Deleting entity...";
|
||||
if (text == "") {
|
||||
qCDebug(entities) << "CRITICAL ERROR: Couldn't compute 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
|
||||
// 2. Send the nonce 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),
|
||||
certIDByteArraySize + text.length() + 2 * sizeof(int),
|
||||
true);
|
||||
challengeOwnershipPacket->writePrimitive(certIDByteArraySize);
|
||||
challengeOwnershipPacket->writePrimitive(encryptedText.length());
|
||||
challengeOwnershipPacket->writePrimitive(text.length());
|
||||
challengeOwnershipPacket->write(certIDByteArray);
|
||||
challengeOwnershipPacket->write(encryptedText);
|
||||
challengeOwnershipPacket->write(text);
|
||||
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
|
||||
|
@ -1304,7 +1290,7 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri
|
|||
}
|
||||
}
|
||||
|
||||
void EntityTree::sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& encryptedText, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode) {
|
||||
void EntityTree::sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode) {
|
||||
auto nodeList = DependencyManager::get<NodeList>();
|
||||
|
||||
// In this case, Client A is challenging Client B. Client A is inspecting a certified entity that it wants
|
||||
|
@ -1312,17 +1298,17 @@ void EntityTree::sendChallengeOwnershipRequestPacket(const QByteArray& certID, c
|
|||
QByteArray senderNodeUUID = senderNode->getUUID().toRfc4122();
|
||||
|
||||
int certIDByteArraySize = certID.length();
|
||||
int encryptedTextByteArraySize = encryptedText.length();
|
||||
int TextByteArraySize = text.length();
|
||||
int senderNodeUUIDSize = senderNodeUUID.length();
|
||||
|
||||
auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest,
|
||||
certIDByteArraySize + encryptedTextByteArraySize + senderNodeUUIDSize + 3 * sizeof(int),
|
||||
certIDByteArraySize + TextByteArraySize + senderNodeUUIDSize + 3 * sizeof(int),
|
||||
true);
|
||||
challengeOwnershipPacket->writePrimitive(certIDByteArraySize);
|
||||
challengeOwnershipPacket->writePrimitive(encryptedTextByteArraySize);
|
||||
challengeOwnershipPacket->writePrimitive(TextByteArraySize);
|
||||
challengeOwnershipPacket->writePrimitive(senderNodeUUIDSize);
|
||||
challengeOwnershipPacket->write(certID);
|
||||
challengeOwnershipPacket->write(encryptedText);
|
||||
challengeOwnershipPacket->write(text);
|
||||
challengeOwnershipPacket->write(senderNodeUUID);
|
||||
|
||||
nodeList->sendPacket(std::move(challengeOwnershipPacket), *(nodeList->nodeWithUUID(QUuid::fromRfc4122(nodeToChallenge))));
|
||||
|
@ -1391,18 +1377,18 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt
|
|||
|
||||
void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) {
|
||||
int certIDByteArraySize;
|
||||
int decryptedTextByteArraySize;
|
||||
int textByteArraySize;
|
||||
|
||||
message.readPrimitive(&certIDByteArraySize);
|
||||
message.readPrimitive(&decryptedTextByteArraySize);
|
||||
message.readPrimitive(&textByteArraySize);
|
||||
|
||||
QString certID(message.read(certIDByteArraySize));
|
||||
QString decryptedText(message.read(decryptedTextByteArraySize));
|
||||
QString text(message.read(textByteArraySize));
|
||||
|
||||
emit killChallengeOwnershipTimeoutTimer(certID);
|
||||
|
||||
EntityItemID id;
|
||||
if (!verifyDecryptedNonce(certID, decryptedText, id)) {
|
||||
if (!verifyNonce(certID, text, id)) {
|
||||
if (!id.isNull()) {
|
||||
deleteEntity(id, true);
|
||||
}
|
||||
|
|
|
@ -275,8 +275,8 @@ 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);
|
||||
QByteArray computeNonce(const QString& certID, const QString ownerKey);
|
||||
bool verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id);
|
||||
|
||||
void setMyAvatar(std::shared_ptr<AvatarData> myAvatar) { _myAvatar = myAvatar; }
|
||||
|
||||
|
@ -383,7 +383,7 @@ protected:
|
|||
|
||||
private:
|
||||
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 sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& text, const QByteArray& nodeToChallenge, const SharedNodePointer& senderNode);
|
||||
void validatePop(const QString& certID, const EntityItemID& entityItemID, const SharedNodePointer& senderNode, bool isRetryingValidation);
|
||||
|
||||
std::shared_ptr<AvatarData> _myAvatar{ nullptr };
|
||||
|
|
Loading…
Reference in a new issue