From f997ad4628bf95b18f4ced5390adc23f989cab1b Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 7 Nov 2017 16:15:48 -0800 Subject: [PATCH 01/13] changed Wallet from RSA to ECSDA cryto --- interface/src/commerce/Wallet.cpp | 150 ++++++++++++++++-------------- 1 file changed, 79 insertions(+), 71 deletions(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 85632ff8f1..8c606f0418 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -32,6 +32,7 @@ #include #include #include +#include // I know, right? But per https://www.openssl.org/docs/faq.html // this avoids OPENSSL_Uplink(00007FF847238000,08): no OPENSSL_Applink @@ -78,18 +79,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 +139,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(); @@ -170,44 +172,28 @@ bool writeKeys(const char* filename, RSA* keys) { // 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 generateRSAKeypair() { - - RSA* keyPair = RSA_new(); - BIGNUM* exponent = BN_new(); +QPair generateECKeypair() { + + EC_KEY* keyPair = EC_KEY_new_by_curve_name(NID_secp256k1); QPair 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); + 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(); // cleanup the RSA struct - RSA_free(keyPair); + EC_KEY_free(keyPair); // cleanup the public and private key DER data, if required if (publicKeyLength > 0) { @@ -227,7 +213,7 @@ QPair 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 +231,18 @@ QPair 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 +260,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 +// the private key should be read/copied into heap memory. For now, we need the EC_KEY struct // so I'll return that. Note we need to RSA_free(key) later!!! -RSA* readPrivateKey(const char* filename) { +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 +495,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 +511,7 @@ bool Wallet::generateKeyPair() { initialize(); qCInfo(commerce) << "Generating keypair."; - auto keyPair = generateRSAKeypair(); + auto keyPair = generateECKeypair(); writeBackupInstructions(); @@ -558,22 +544,30 @@ 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(hashedPlaintext.constData()), - hashedPlaintext.size(), - reinterpret_cast(signature.data()), - &signatureBytes, - rsaPrivateKey); + + int encryptReturn = ECDSA_sign(0, + reinterpret_cast(hashedPlaintext.constData()), + hashedPlaintext.size(), + reinterpret_cast(signature.data()), + &signatureBytes, ecPrivateKey); + + //Previous pattern, retained by ECDSA_sign + //int encryptReturn = RSA_sign(NID_sha256, + // reinterpret_cast(hashedPlaintext.constData()), + // hashedPlaintext.size(), + // reinterpret_cast(signature.data()), + // &signatureBytes, + // rsaPrivateKey); // free the private key RSA struct now that we are done with it - RSA_free(rsaPrivateKey); + EC_KEY_free(ecPrivateKey); if (encryptReturn != -1) { return signature.toBase64(); @@ -674,7 +668,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,18 +714,23 @@ bool Wallet::changePassphrase(const QString& newPassphrase) { void Wallet::handleChallengeOwnershipPacket(QSharedPointer packet, SharedNodePointer sendingNode) { auto nodeList = DependencyManager::get(); + //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]; + //unsigned char decryptedText[64]; + int decryptionStatus; int certIDByteArraySize; int encryptedTextByteArraySize; int challengingNodeUUIDByteArraySize; packet->readPrimitive(&certIDByteArraySize); - packet->readPrimitive(&encryptedTextByteArraySize); + packet->readPrimitive(&encryptedTextByteArraySize); //rerturns 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 challengingNodeUUID; @@ -739,25 +738,34 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack challengingNodeUUID = packet->read(challengingNodeUUIDByteArraySize); } - RSA* rsa = readKeys(keyFilePath().toStdString().c_str()); - int decryptionStatus = -1; + EC_KEY* ec = readKeys(keyFilePath().toStdString().c_str()); + QString sig; + // int decryptionStatus = -1; - if (rsa) { + if (ec) { ERR_clear_error(); - decryptionStatus = RSA_private_decrypt(encryptedTextByteArraySize, - reinterpret_cast(encryptedText.constData()), - decryptedText, - rsa, - RSA_PKCS1_OAEP_PADDING); + sig = signWithKey(encryptedText, ""); //base64 signature, QByteArray cast + //upon return to QString - RSA_free(rsa); + // decryptionStatus = RSA_private_decrypt(encryptedTextByteArraySize, + // reinterpret_cast(encryptedText.constData()), + // decryptedText, //unsigned char* + // ec, + // RSA_PKCS1_OAEP_PADDING); + + EC_KEY_free(ec); + decryptionStatus = 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."; + decryptionStatus = -1; } + QByteArray ba = sig.toLocal8Bit(); + const char *sigChar = ba.data(); + QByteArray decryptedTextByteArray; if (decryptionStatus > -1) { - decryptedTextByteArray = QByteArray(reinterpret_cast(decryptedText), decryptionStatus); + decryptedTextByteArray = QByteArray(sigChar, decryptionStatus); } int decryptedTextByteArraySize = decryptedTextByteArray.size(); int certIDSize = certID.size(); @@ -774,7 +782,7 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack decryptedTextPacket->write(decryptedTextByteArray); decryptedTextPacket->write(challengingNodeUUID); - qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing decrypted text" << decryptedTextByteArray << "for CertID" << certID; + qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing signed text" << decryptedTextByteArray << "for CertID" << certID; nodeList->sendPacket(std::move(decryptedTextPacket), *sendingNode); } else { @@ -785,17 +793,17 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack decryptedTextPacket->write(certID); decryptedTextPacket->write(decryptedTextByteArray); - qCDebug(commerce) << "Sending ChallengeOwnership Packet containing decrypted text" << decryptedTextByteArray << "for CertID" << certID; + qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed 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."; + 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 +826,4 @@ void Wallet::getWalletStatus() { walletScriptingInterface->setWalletStatus(status); return; } -} +} \ No newline at end of file From 44f2c92d30dae2d30626efcc03bd49703466c9d8 Mon Sep 17 00:00:00 2001 From: H Q Date: Tue, 14 Nov 2017 15:40:29 -0800 Subject: [PATCH 02/13] Final ECSDA and nonce modifications for this dev cycle --- interface/src/commerce/Wallet.cpp | 71 ++++++------ .../ui/overlays/ContextOverlayInterface.cpp | 18 +-- .../entities/src/EntityItemProperties.cpp | 23 ++-- libraries/entities/src/EntityTree.cpp | 104 +++++++++--------- libraries/entities/src/EntityTree.h | 6 +- 5 files changed, 113 insertions(+), 109 deletions(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 8c606f0418..59b2f03ed8 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -27,7 +27,6 @@ #include #include -#include #include #include #include @@ -552,7 +551,7 @@ QString Wallet::signWithKey(const QByteArray& text, const QString& key) { QByteArray hashedPlaintext = QCryptographicHash::hash(text, QCryptographicHash::Sha256); - int encryptReturn = ECDSA_sign(0, + int Return = ECDSA_sign(0, reinterpret_cast(hashedPlaintext.constData()), hashedPlaintext.size(), reinterpret_cast(signature.data()), @@ -569,7 +568,7 @@ QString Wallet::signWithKey(const QByteArray& text, const QString& key) { // free the private key RSA struct now that we are done with it EC_KEY_free(ecPrivateKey); - if (encryptReturn != -1) { + if (Return != -1) { return signature.toBase64(); } } @@ -719,20 +718,20 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack bool challengeOriginatedFromClient = packet->getType() == PacketType::ChallengeOwnershipRequest; //unsigned char decryptedText[64]; - int decryptionStatus; + int Status; int certIDByteArraySize; - int encryptedTextByteArraySize; + int TextByteArraySize; int challengingNodeUUIDByteArraySize; packet->readPrimitive(&certIDByteArraySize); - packet->readPrimitive(&encryptedTextByteArraySize); //rerturns a cast char*, size + packet->readPrimitive(&TextByteArraySize); //rerturns 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); @@ -742,10 +741,10 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack QString sig; // int decryptionStatus = -1; - if (ec) { + if (ec) { ERR_clear_error(); - sig = signWithKey(encryptedText, ""); //base64 signature, QByteArray cast - //upon return to QString + sig = signWithKey(Text, ""); //base64 signature, QByteArray cast + //upon return to QString // decryptionStatus = RSA_private_decrypt(encryptedTextByteArraySize, // reinterpret_cast(encryptedText.constData()), @@ -753,52 +752,52 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack // ec, // RSA_PKCS1_OAEP_PADDING); - EC_KEY_free(ec); - decryptionStatus = 1; + // EC_KEY_free(ec); + Status = 1; } else { qCDebug(commerce) << "During entity ownership challenge, creating the EC-signed nonce failed."; - decryptionStatus = -1; + Status = -1; } QByteArray ba = sig.toLocal8Bit(); const char *sigChar = ba.data(); - QByteArray decryptedTextByteArray; - if (decryptionStatus > -1) { - decryptedTextByteArray = QByteArray(sigChar, decryptionStatus); - } - int decryptedTextByteArraySize = decryptedTextByteArray.size(); + QByteArray TextByteArray; + // if (decryptionStatus > -1) { + TextByteArray = QByteArray(sigChar, Status); + //} + 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 signed 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 signed 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) { + if (Status == -1) { qCDebug(commerce) << "During entity ownership challenge, signing the text failed."; long error = ERR_get_error(); if (error != 0) { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index b5af529f2b..dee8eaad0b 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -311,21 +311,21 @@ void ContextOverlayInterface::openInspectionCertificate() { QString ownerKey = jsonObject["transfer_recipient_key"].toString(); QByteArray certID = entityProperties.getCertificateID().toUtf8(); - QByteArray encryptedText = DependencyManager::get()->getTree()->computeEncryptedNonce(certID, ownerKey); + QByteArray Text = DependencyManager::get()->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); @@ -406,16 +406,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()->getTree()->verifyDecryptedNonce(certID, decryptedText, id); + bool verificationSuccess = DependencyManager::get()->getTree()->verifyNonce(certID, Text, id); if (verificationSuccess) { emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index f774b208c4..9435e456a2 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -15,14 +15,14 @@ #include #include -#include + #include #include #include #include #include #include - +#include #include #include #include @@ -2531,8 +2531,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(digestByteArray.constData()); int digestLength = digestByteArray.length(); @@ -2542,22 +2542,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); } @@ -2574,7 +2577,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 { @@ -2583,7 +2586,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; } } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 333a514377..d2be600022 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -14,7 +14,7 @@ #include #include -#include +//#include #include #include #include @@ -1167,36 +1167,38 @@ 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 (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(qPrintable(nonce.toString())), - reinterpret_cast(encryptedText.data()), - rsa, - RSA_PKCS1_OAEP_PADDING); + if (ec) { + QUuid nonce = QUuid::createUuid(); //random, 5-hex value, separated by "-" + QByteArray nonceBytes = nonce.toByteArray(); + + //const unsigned int textLength = nonce.toString().length(); + //QByteArray encryptedText(ECDSA_size(ec), 0); + //const int encryptStatus = RSA_public_encrypt(textLength, + // reinterpret_cast(qPrintable(nonce.toString())), + // reinterpret_cast(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 ""; - } + //rsa_free(ec); + // if (encryptStatus == -1) { + // long error = ERR_get_error(); + // const char* error_str = ERR_error_string(error, NULL); + // qCWarning(entities) << "Unable to compute nonce for" << certID << "\nECDSA error:" << error_str; + // return ""; + //} QWriteLocker locker(&_certNonceMapLock); _certNonceMap.insert(certID, nonce); - return encryptedText; + return nonceBytes; } else { if (bio) { BIO_free(bio); @@ -1205,7 +1207,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 +1219,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 +1233,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(); 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(); - 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 +1306,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(); // In this case, Client A is challenging Client B. Client A is inspecting a certified entity that it wants @@ -1312,17 +1314,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 +1393,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); } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 86bfe984f6..fc89a2f65f 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -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); signals: void deletingEntity(const EntityItemID& entityID); @@ -381,7 +381,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); }; From 27350ddbfc846878653d5292a53b8dc820314bb4 Mon Sep 17 00:00:00 2001 From: H Q Date: Thu, 16 Nov 2017 12:35:00 -0800 Subject: [PATCH 03/13] Coding style corrections --- interface/src/commerce/Wallet.cpp | 5 +++-- libraries/entities/src/EntityTree.cpp | 22 +++++++++++----------- libraries/entities/src/EntityTree.h | 4 ++-- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 59b2f03ed8..03cb9ff893 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -175,6 +175,7 @@ QPair generateECKeypair() { EC_KEY* keyPair = EC_KEY_new_by_curve_name(NID_secp256k1); QPair retval; + 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; @@ -188,10 +189,10 @@ QPair generateECKeypair() { 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 + // cleanup the EC struct EC_KEY_free(keyPair); // cleanup the public and private key DER data, if required diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index d2be600022..a5027e68a4 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1233,18 +1233,18 @@ bool EntityTree::verifyNonce(const QString& certID, const QString& Nonce, Entity void EntityTree::processChallengeOwnershipRequestPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { int certIDByteArraySize; - int TextByteArraySize; + int textByteArraySize; int nodeToChallengeByteArraySize; message.readPrimitive(&certIDByteArraySize); - message.readPrimitive(&TextByteArraySize); + message.readPrimitive(&textByteArraySize); message.readPrimitive(&nodeToChallengeByteArraySize); QByteArray certID(message.read(certIDByteArraySize)); - QByteArray Text(message.read(TextByteArraySize)); + QByteArray text(message.read(textByteArraySize)); QByteArray nodeToChallenge(message.read(nodeToChallengeByteArraySize)); - sendChallengeOwnershipRequestPacket(certID, Text, nodeToChallenge, sourceNode); + sendChallengeOwnershipRequestPacket(certID, text, nodeToChallenge, sourceNode); } void EntityTree::processChallengeOwnershipReplyPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { @@ -1306,7 +1306,7 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri } } -void EntityTree::sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& Text, 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(); // In this case, Client A is challenging Client B. Client A is inspecting a certified entity that it wants @@ -1314,7 +1314,7 @@ void EntityTree::sendChallengeOwnershipRequestPacket(const QByteArray& certID, c QByteArray senderNodeUUID = senderNode->getUUID().toRfc4122(); int certIDByteArraySize = certID.length(); - int TextByteArraySize = Text.length(); + int TextByteArraySize = text.length(); int senderNodeUUIDSize = senderNodeUUID.length(); auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, @@ -1324,7 +1324,7 @@ void EntityTree::sendChallengeOwnershipRequestPacket(const QByteArray& certID, c challengeOwnershipPacket->writePrimitive(TextByteArraySize); challengeOwnershipPacket->writePrimitive(senderNodeUUIDSize); challengeOwnershipPacket->write(certID); - challengeOwnershipPacket->write(Text); + challengeOwnershipPacket->write(text); challengeOwnershipPacket->write(senderNodeUUID); nodeList->sendPacket(std::move(challengeOwnershipPacket), *(nodeList->nodeWithUUID(QUuid::fromRfc4122(nodeToChallenge)))); @@ -1393,18 +1393,18 @@ void EntityTree::validatePop(const QString& certID, const EntityItemID& entityIt void EntityTree::processChallengeOwnershipPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { int certIDByteArraySize; - int TextByteArraySize; + int textByteArraySize; message.readPrimitive(&certIDByteArraySize); - message.readPrimitive(&TextByteArraySize); + message.readPrimitive(&textByteArraySize); QString certID(message.read(certIDByteArraySize)); - QString Text(message.read(TextByteArraySize)); + QString text(message.read(textByteArraySize)); emit killChallengeOwnershipTimeoutTimer(certID); EntityItemID id; - if (!verifyNonce(certID, Text, id)) { + if (!verifyNonce(certID, text, id)) { if (!id.isNull()) { deleteEntity(id, true); } diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index fc89a2f65f..b516536e01 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -276,7 +276,7 @@ public: static const float DEFAULT_MAX_TMP_ENTITY_LIFETIME; QByteArray computeNonce(const QString& certID, const QString ownerKey); - bool verifyNonce(const QString& certID, const QString& Nonce, EntityItemID& id); + bool verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id); signals: void deletingEntity(const EntityItemID& entityID); @@ -381,7 +381,7 @@ protected: private: void sendChallengeOwnershipPacket(const QString& certID, const QString& ownerKey, const EntityItemID& entityItemID, const SharedNodePointer& senderNode); - void sendChallengeOwnershipRequestPacket(const QByteArray& certID, const QByteArray& Text, 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); }; From 21a28827c90840420af310640e701fa2abc582b7 Mon Sep 17 00:00:00 2001 From: H Q Date: Fri, 17 Nov 2017 07:59:52 -0800 Subject: [PATCH 04/13] Remaining coding style changes, other small fixes --- interface/src/commerce/Wallet.cpp | 101 +++++++----------- .../ui/overlays/ContextOverlayInterface.cpp | 18 ++-- .../entities/src/EntityItemProperties.cpp | 9 +- libraries/entities/src/EntityTree.cpp | 44 +++----- 4 files changed, 64 insertions(+), 108 deletions(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 03cb9ff893..17e788c9b4 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -165,12 +165,6 @@ bool writeKeys(const char* filename, EC_KEY* 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 generateECKeypair() { EC_KEY* keyPair = EC_KEY_new_by_curve_name(NID_secp256k1); @@ -261,7 +255,7 @@ QByteArray readPublicKey(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. Note we need to RSA_free(key) later!!! +// so I'll return that. EC_KEY* readPrivateKey(const char* filename) { FILE* fp; EC_KEY* key = NULL; @@ -552,24 +546,15 @@ QString Wallet::signWithKey(const QByteArray& text, const QString& key) { QByteArray hashedPlaintext = QCryptographicHash::hash(text, QCryptographicHash::Sha256); - int Return = ECDSA_sign(0, + int retrn = ECDSA_sign(0, reinterpret_cast(hashedPlaintext.constData()), hashedPlaintext.size(), reinterpret_cast(signature.data()), &signatureBytes, ecPrivateKey); - //Previous pattern, retained by ECDSA_sign - //int encryptReturn = RSA_sign(NID_sha256, - // reinterpret_cast(hashedPlaintext.constData()), - // hashedPlaintext.size(), - // reinterpret_cast(signature.data()), - // &signatureBytes, - // rsaPrivateKey); - - // free the private key RSA struct now that we are done with it EC_KEY_free(ecPrivateKey); - if (Return != -1) { + if (retrn != -1) { return signature.toBase64(); } } @@ -714,25 +699,24 @@ bool Wallet::changePassphrase(const QString& newPassphrase) { void Wallet::handleChallengeOwnershipPacket(QSharedPointer packet, SharedNodePointer sendingNode) { auto nodeList = DependencyManager::get(); - //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. + // 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 status; int certIDByteArraySize; - int TextByteArraySize; + int textByteArraySize; int challengingNodeUUIDByteArraySize; packet->readPrimitive(&certIDByteArraySize); - packet->readPrimitive(&TextByteArraySize); //rerturns a cast char*, size + packet->readPrimitive(&textByteArraySize); // returns a cast char*, size if (challengeOriginatedFromClient) { packet->readPrimitive(&challengingNodeUUIDByteArraySize); } - //"encryptedText" is now a series of random bytes, a nonce + // "encryptedText" is now a series of random bytes, a nonce QByteArray certID = packet->read(certIDByteArraySize); - QByteArray Text = packet->read(TextByteArraySize); + QByteArray text = packet->read(textByteArraySize); QByteArray challengingNodeUUID; if (challengeOriginatedFromClient) { challengingNodeUUID = packet->read(challengingNodeUUIDByteArraySize); @@ -740,65 +724,56 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack EC_KEY* ec = readKeys(keyFilePath().toStdString().c_str()); QString sig; - // int decryptionStatus = -1; if (ec) { ERR_clear_error(); - sig = signWithKey(Text, ""); //base64 signature, QByteArray cast - //upon return to QString - - // decryptionStatus = RSA_private_decrypt(encryptedTextByteArraySize, - // reinterpret_cast(encryptedText.constData()), - // decryptedText, //unsigned char* - // ec, - // RSA_PKCS1_OAEP_PADDING); - - // EC_KEY_free(ec); - Status = 1; + sig = signWithKey(text, ""); // base64 signature, QByteArray cast (on return) to QString + status = 1; } else { qCDebug(commerce) << "During entity ownership challenge, creating the EC-signed nonce failed."; - Status = -1; + status = -1; } - + + EC_KEY_free(ec); QByteArray ba = sig.toLocal8Bit(); const char *sigChar = ba.data(); - QByteArray TextByteArray; - // if (decryptionStatus > -1) { - TextByteArray = QByteArray(sigChar, Status); - //} - TextByteArraySize = TextByteArray.size(); + QByteArray textByteArray; + if (status > -1) { + textByteArray = QByteArray(sigChar, status); + } + textByteArraySize = textByteArray.size(); int certIDSize = certID.size(); // setup the packet if (challengeOriginatedFromClient) { - auto TextPacket = NLPacket::create(PacketType::ChallengeOwnershipReply, - certIDSize + TextByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int), + auto textPacket = NLPacket::create(PacketType::ChallengeOwnershipReply, + certIDSize + textByteArraySize + challengingNodeUUIDByteArraySize + 3 * sizeof(int), true); - TextPacket->writePrimitive(certIDSize); - TextPacket->writePrimitive(TextByteArraySize); - TextPacket->writePrimitive(challengingNodeUUIDByteArraySize); - TextPacket->write(certID); - TextPacket->write(TextByteArray); - TextPacket->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 signed text" << TextByteArray << "for CertID" << certID; + qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing signed text" << textByteArray << "for CertID" << certID; - nodeList->sendPacket(std::move(TextPacket), *sendingNode); + nodeList->sendPacket(std::move(textPacket), *sendingNode); } else { - auto TextPacket = NLPacket::create(PacketType::ChallengeOwnership, certIDSize + TextByteArraySize + 2 * sizeof(int), true); + auto textPacket = NLPacket::create(PacketType::ChallengeOwnership, certIDSize + textByteArraySize + 2 * sizeof(int), true); - TextPacket->writePrimitive(certIDSize); - TextPacket->writePrimitive(TextByteArraySize); - TextPacket->write(certID); - TextPacket->write(TextByteArray); + textPacket->writePrimitive(certIDSize); + textPacket->writePrimitive(textByteArraySize); + textPacket->write(certID); + textPacket->write(textByteArray); - qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << TextByteArray << "for CertID" << certID; + qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << textByteArray << "for CertID" << certID; - nodeList->sendPacket(std::move(TextPacket), *sendingNode); + nodeList->sendPacket(std::move(textPacket), *sendingNode); } - if (Status == -1) { + if (status == -1) { qCDebug(commerce) << "During entity ownership challenge, signing the text failed."; long error = ERR_get_error(); if (error != 0) { diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 2b2496b32a..8e7bca814d 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -323,21 +323,21 @@ void ContextOverlayInterface::openInspectionCertificate() { QString ownerKey = jsonObject["transfer_recipient_key"].toString(); QByteArray certID = entityProperties.getCertificateID().toUtf8(); - QByteArray Text = DependencyManager::get()->getTree()->computeNonce(certID, ownerKey); + QByteArray text = DependencyManager::get()->getTree()->computeNonce(certID, ownerKey); QByteArray nodeToChallengeByteArray = entityProperties.getOwningAvatarID().toRfc4122(); int certIDByteArraySize = certID.length(); - int TextByteArraySize = Text.length(); + int textByteArraySize = text.length(); int nodeToChallengeByteArraySize = nodeToChallengeByteArray.length(); auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnershipRequest, - certIDByteArraySize + TextByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int), + certIDByteArraySize + textByteArraySize + nodeToChallengeByteArraySize + 3 * sizeof(int), true); challengeOwnershipPacket->writePrimitive(certIDByteArraySize); - challengeOwnershipPacket->writePrimitive(TextByteArraySize); + challengeOwnershipPacket->writePrimitive(textByteArraySize); challengeOwnershipPacket->writePrimitive(nodeToChallengeByteArraySize); challengeOwnershipPacket->write(certID); - challengeOwnershipPacket->write(Text); + challengeOwnershipPacket->write(text); challengeOwnershipPacket->write(nodeToChallengeByteArray); nodeList->sendPacket(std::move(challengeOwnershipPacket), *entityServer); @@ -418,16 +418,16 @@ void ContextOverlayInterface::handleChallengeOwnershipReplyPacket(QSharedPointer _challengeOwnershipTimeoutTimer.stop(); int certIDByteArraySize; - int TextByteArraySize; + int textByteArraySize; packet->readPrimitive(&certIDByteArraySize); - packet->readPrimitive(&TextByteArraySize); + packet->readPrimitive(&textByteArraySize); QString certID(packet->read(certIDByteArraySize)); - QString Text(packet->read(TextByteArraySize)); + QString text(packet->read(textByteArraySize)); EntityItemID id; - bool verificationSuccess = DependencyManager::get()->getTree()->verifyNonce(certID, Text, id); + bool verificationSuccess = DependencyManager::get()->getTree()->verifyNonce(certID, text, id); if (verificationSuccess) { emit ledger->updateCertificateStatus(certID, (uint)(ledger->CERTIFICATE_STATUS_VERIFICATION_SUCCESS)); diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 2922be3cd4..4da27e15c0 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -13,9 +13,7 @@ #include #include #include - #include - #include #include #include @@ -27,7 +25,6 @@ #include #include #include - #include "EntitiesLogging.h" #include "EntityItem.h" #include "EntityItemProperties.h" @@ -2534,9 +2531,9 @@ bool EntityItemProperties::verifyStaticCertificateProperties() { int signatureLength = signatureByteArray.length(); ERR_clear_error(); - //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); + // 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, diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index a5027e68a4..e152eea049 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -12,9 +12,7 @@ #include "EntityTree.h" #include #include - #include -//#include #include #include #include @@ -1177,23 +1175,9 @@ QByteArray EntityTree::computeNonce(const QString& certID, const QString ownerKe QUuid nonce = QUuid::createUuid(); //random, 5-hex value, separated by "-" QByteArray nonceBytes = nonce.toByteArray(); - //const unsigned int textLength = nonce.toString().length(); - //QByteArray encryptedText(ECDSA_size(ec), 0); - //const int encryptStatus = RSA_public_encrypt(textLength, - // reinterpret_cast(qPrintable(nonce.toString())), - // reinterpret_cast(encryptedText.data()), - // rsa, - // RSA_PKCS1_OAEP_PADDING); if (bio) { BIO_free(bio); } - //rsa_free(ec); - // if (encryptStatus == -1) { - // long error = ERR_get_error(); - // const char* error_str = ERR_error_string(error, NULL); - // qCWarning(entities) << "Unable to compute nonce for" << certID << "\nECDSA error:" << error_str; - // return ""; - //} QWriteLocker locker(&_certNonceMapLock); _certNonceMap.insert(certID, nonce); @@ -1207,7 +1191,7 @@ QByteArray EntityTree::computeNonce(const QString& certID, const QString ownerKe } } -bool EntityTree::verifyNonce(const QString& certID, const QString& Nonce, EntityItemID& id) { +bool EntityTree::verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id) { { QReadLocker certIdMapLocker(&_entityCertificateIDMapLock); id = _entityCertificateIDMap.value(certID); @@ -1219,13 +1203,13 @@ bool EntityTree::verifyNonce(const QString& certID, const QString& Nonce, Entity actualNonce = _certNonceMap.take(certID).toString(); } - bool verificationSuccess = (actualNonce == Nonce); + 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 << "\nonce:" << Nonce; + << "\nActual nonce:" << actualNonce << "\nonce:" << nonce; } return verificationSuccess; @@ -1251,24 +1235,24 @@ void EntityTree::processChallengeOwnershipReplyPacket(ReceivedMessage& message, auto nodeList = DependencyManager::get(); int certIDByteArraySize; - int TextByteArraySize; + int textByteArraySize; int challengingNodeUUIDByteArraySize; message.readPrimitive(&certIDByteArraySize); - message.readPrimitive(&TextByteArraySize); + message.readPrimitive(&textByteArraySize); message.readPrimitive(&challengingNodeUUIDByteArraySize); QByteArray certID(message.read(certIDByteArraySize)); - QByteArray Text(message.read(TextByteArraySize)); + QByteArray text(message.read(textByteArraySize)); QUuid challengingNode = QUuid::fromRfc4122(message.read(challengingNodeUUIDByteArraySize)); auto challengeOwnershipReplyPacket = NLPacket::create(PacketType::ChallengeOwnershipReply, - certIDByteArraySize + Text.length() + 2 * sizeof(int), + certIDByteArraySize + text.length() + 2 * sizeof(int), true); challengeOwnershipReplyPacket->writePrimitive(certIDByteArraySize); - challengeOwnershipReplyPacket->writePrimitive(Text.length()); + challengeOwnershipReplyPacket->writePrimitive(text.length()); challengeOwnershipReplyPacket->write(certID); - challengeOwnershipReplyPacket->write(Text); + challengeOwnershipReplyPacket->write(text); nodeList->sendPacket(std::move(challengeOwnershipReplyPacket), *(nodeList->nodeWithUUID(challengingNode))); } @@ -1277,9 +1261,9 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri // 1. Obtain a nonce auto nodeList = DependencyManager::get(); - QByteArray Text = computeNonce(certID, ownerKey); + QByteArray text = computeNonce(certID, ownerKey); - if (Text == "") { + if (text == "") { qCDebug(entities) << "CRITICAL ERROR: Couldn't compute nonce. Deleting entity..."; deleteEntity(entityItemID, true); } else { @@ -1288,12 +1272,12 @@ void EntityTree::sendChallengeOwnershipPacket(const QString& certID, const QStri QByteArray certIDByteArray = certID.toUtf8(); int certIDByteArraySize = certIDByteArray.size(); auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership, - certIDByteArraySize + Text.length() + 2 * sizeof(int), + certIDByteArraySize + text.length() + 2 * sizeof(int), true); challengeOwnershipPacket->writePrimitive(certIDByteArraySize); - challengeOwnershipPacket->writePrimitive(Text.length()); + challengeOwnershipPacket->writePrimitive(text.length()); challengeOwnershipPacket->write(certIDByteArray); - challengeOwnershipPacket->write(Text); + 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 From 28eedd1a94b6ad066d2822da760a21429078e59d Mon Sep 17 00:00:00 2001 From: H Q Date: Fri, 17 Nov 2017 11:30:21 -0800 Subject: [PATCH 05/13] Small style mod --- libraries/entities/src/EntityItemProperties.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 4da27e15c0..762e13d8a1 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -16,11 +16,11 @@ #include #include #include +#include #include #include #include #include -#include #include #include #include From 899999a4c6a00b6d415056ace7b301c639e94ec1 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 22 Nov 2017 15:58:30 -0800 Subject: [PATCH 06/13] fix up dynamic ownership challenge --- interface/src/commerce/Wallet.cpp | 8 +- libraries/entities/src/EntityTree.cpp | 111 ++++++++++++++++++++------ libraries/entities/src/EntityTree.h | 3 +- 3 files changed, 91 insertions(+), 31 deletions(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 17e788c9b4..98a27cd5cc 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -537,7 +537,7 @@ QStringList Wallet::listPublicKeys() { // the horror of code pages and so on (changing the bytes) by just returning a base64 // encoded string representing the signature (suitable for http, etc...) QString Wallet::signWithKey(const QByteArray& text, const QString& key) { - qCInfo(commerce) << "Signing text."; + qCInfo(commerce) << "Signing text" << text << "with key" << key; EC_KEY* ecPrivateKey = NULL; if ((ecPrivateKey = readPrivateKey(keyFilePath().toStdString().c_str()))) { QByteArray signature(ECDSA_size(ecPrivateKey), 0); @@ -727,7 +727,7 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack if (ec) { ERR_clear_error(); - sig = signWithKey(text, ""); // base64 signature, QByteArray cast (on return) to QString + sig = signWithKey(text, ""); // base64 signature, QByteArray cast (on return) to QString FIXME should pass ec as string so we can tell which key to sign with status = 1; } else { qCDebug(commerce) << "During entity ownership challenge, creating the EC-signed nonce failed."; @@ -740,7 +740,7 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack QByteArray textByteArray; if (status > -1) { - textByteArray = QByteArray(sigChar, status); + textByteArray = QByteArray(sigChar, (int) strlen(sigChar)); } textByteArraySize = textByteArray.size(); int certIDSize = certID.size(); @@ -768,7 +768,7 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack textPacket->write(certID); textPacket->write(textByteArray); - qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << textByteArray << "for CertID" << certID; + qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing signed text" << textByteArray << "for CertID" << certID; nodeList->sendPacket(std::move(textPacket), *sendingNode); } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 2ef0d319ee..bd446ab332 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1166,29 +1166,13 @@ void EntityTree::startPendingTransferStatusTimer(const QString& certID, const En } 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 - EC_KEY* ec = PEM_read_bio_EC_PUBKEY(bio, NULL, NULL, NULL); + QUuid nonce = QUuid::createUuid(); //random, 5-hex value, separated by "-" + QByteArray nonceBytes = nonce.toByteArray(); - if (ec) { - QUuid nonce = QUuid::createUuid(); //random, 5-hex value, separated by "-" - QByteArray nonceBytes = nonce.toByteArray(); + QWriteLocker locker(&_certNonceMapLock); + _certNonceMap.insert(certID, QPair(nonce, ownerKey)); - if (bio) { - BIO_free(bio); - } - - QWriteLocker locker(&_certNonceMapLock); - _certNonceMap.insert(certID, nonce); - - return nonceBytes; - } else { - if (bio) { - BIO_free(bio); - } - return ""; - } + return nonceBytes; } bool EntityTree::verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id) { @@ -1197,24 +1181,99 @@ bool EntityTree::verifyNonce(const QString& certID, const QString& nonce, Entity id = _entityCertificateIDMap.value(certID); } - QString actualNonce; + QString actualNonce, key; { QWriteLocker locker(&_certNonceMapLock); - actualNonce = _certNonceMap.take(certID).toString(); + QPair sent = _certNonceMap.take(certID); + actualNonce = sent.first.toString(); + key = sent.second; } - bool verificationSuccess = (actualNonce == nonce); + QString annotatedKey = "-----BEGIN PUBLIC KEY-----\n" + key + "\n-----END PUBLIC KEY-----"; + bool verificationSuccess = verifySignature(annotatedKey.toUtf8(), actualNonce.toUtf8(), nonce.toUtf8()); if (verificationSuccess) { qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded."; } else { - qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed." - << "\nActual nonce:" << actualNonce << "\nonce:" << nonce; + qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed for nonce" << actualNonce << "key" << key << "signature" << nonce; } return verificationSuccess; } +// FIXME: This is largely copied from EntityItemProperties::verifyStaticCertificateProperties, which should be refactored to use this. +// I also don't like the nested-if style, but for this step I'm deliberately preserving the similarity. +bool EntityTree::verifySignature(const QString& publicKey, const QByteArray& digestByteArray, const QByteArray& signatureByteArray) { + + if (digestByteArray.isEmpty()) { + return false; + } + + const unsigned char* key = reinterpret_cast(publicKey.toUtf8().constData()); + int keyLength = publicKey.length(); + + BIO *bio = BIO_new_mem_buf((void*)key, keyLength); + EVP_PKEY* evp_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); + if (evp_key) { + EC_KEY* ec = EVP_PKEY_get1_EC_KEY(evp_key); + if (ec) { + const unsigned char* digest = reinterpret_cast(digestByteArray.constData()); + int digestLength = digestByteArray.length(); + + const unsigned char* signature = reinterpret_cast(signatureByteArray.constData()); + int signatureLength = signatureByteArray.length(); + + ERR_clear_error(); + // 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, + ec); + long error = ERR_get_error(); + if (error != 0) { + const char* error_str = ERR_error_string(error, NULL); + qCWarning(entities) << "ERROR while verifying signature! EC error:" << error_str + << "\nKey:" << publicKey << "\nutf8 Key Length:" << keyLength + << "\nDigest:" << digest << "\nDigest Length:" << digestLength + << "\nSignature:" << signature << "\nSignature Length:" << signatureLength; + } + EC_KEY_free(ec); + 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 signature! key" << publicKey << " EC key 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 signature! key" << publicKey << " EC PEM error:" << error_str; + return false; + } +} + void EntityTree::processChallengeOwnershipRequestPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { int certIDByteArraySize; int textByteArraySize; diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index a8bca1fdef..747a73ebd7 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -277,6 +277,7 @@ public: QByteArray computeNonce(const QString& certID, const QString ownerKey); bool verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id); + static bool verifySignature(const QString& key, const QByteArray& text, const QByteArray& signature); void setMyAvatar(std::shared_ptr myAvatar) { _myAvatar = myAvatar; } @@ -334,7 +335,7 @@ protected: QHash _entityCertificateIDMap; mutable QReadWriteLock _certNonceMapLock; - QHash _certNonceMap; + QHash> _certNonceMap; EntitySimulationPointer _simulation; From abf41300dd6417fb8ee1e516a29b689ace09f0d4 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 22 Nov 2017 16:29:56 -0800 Subject: [PATCH 07/13] refactor --- .../entities/src/EntityItemProperties.cpp | 38 +++++----- libraries/entities/src/EntityItemProperties.h | 1 + libraries/entities/src/EntityTree.cpp | 75 +------------------ libraries/entities/src/EntityTree.h | 1 - 4 files changed, 23 insertions(+), 92 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 762e13d8a1..11b83e1052 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -2505,28 +2505,25 @@ 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. +// FIXME: This is largely copied from EntityItemProperties::verifyStaticCertificateProperties, which should be refactored to use this. +// I also don't like the nested-if style, but for this step I'm deliberately preserving the similarity. +bool EntityItemProperties::verifySignature(const QString& publicKey, const QByteArray& digestByteArray, const QByteArray& signatureByteArray) { - if (getCertificateID().isEmpty()) { + if (digestByteArray.isEmpty()) { return false; } - const QByteArray marketplacePublicKeyByteArray = EntityItem::_marketplacePublicKey.toUtf8(); - const unsigned char* marketplacePublicKey = reinterpret_cast(marketplacePublicKeyByteArray.constData()); - int marketplacePublicKeyLength = marketplacePublicKeyByteArray.length(); + const unsigned char* key = reinterpret_cast(publicKey.toUtf8().constData()); + int keyLength = publicKey.length(); - BIO *bio = BIO_new_mem_buf((void*)marketplacePublicKey, marketplacePublicKeyLength); + BIO *bio = BIO_new_mem_buf((void*)key, keyLength); EVP_PKEY* evp_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); if (evp_key) { EC_KEY* ec = EVP_PKEY_get1_EC_KEY(evp_key); if (ec) { - const QByteArray digestByteArray = getStaticCertificateHash(); const unsigned char* digest = reinterpret_cast(digestByteArray.constData()); int digestLength = digestByteArray.length(); - const QByteArray signatureByteArray = QByteArray::fromBase64(getCertificateID().toUtf8()); const unsigned char* signature = reinterpret_cast(signatureByteArray.constData()); int signatureLength = signatureByteArray.length(); @@ -2543,9 +2540,8 @@ bool EntityItemProperties::verifyStaticCertificateProperties() { 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! EC error:" << error_str - << "\nStatic Cert JSON:" << getStaticCertificateJSON() - << "\nKey:" << EntityItem::_marketplacePublicKey << "\nKey Length:" << marketplacePublicKeyLength + qCWarning(entities) << "ERROR while verifying signature! EC error:" << error_str + << "\nKey:" << publicKey << "\nutf8 Key Length:" << keyLength << "\nDigest:" << digest << "\nDigest Length:" << digestLength << "\nSignature:" << signature << "\nSignature Length:" << signatureLength; } @@ -2557,7 +2553,8 @@ bool EntityItemProperties::verifyStaticCertificateProperties() { EVP_PKEY_free(evp_key); } return answer; - } else { + } + else { if (bio) { BIO_free(bio); } @@ -2566,16 +2563,23 @@ 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! EC error:" << error_str; + qCWarning(entities) << "Failed to verify signature! key" << publicKey << " EC key error:" << error_str; return false; } - } else { + } + 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! EC error:" << error_str; + qCWarning(entities) << "Failed to verify signature! key" << publicKey << " EC PEM error:" << error_str; return false; } } + +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. + return verifySignature(EntityItem::_marketplacePublicKey, getStaticCertificateHash(), QByteArray::fromBase64(getCertificateID().toUtf8())); +} diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 732dbdf69f..ec192d7c9f 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -339,6 +339,7 @@ public: QByteArray getStaticCertificateJSON() const; QByteArray getStaticCertificateHash() const; bool verifyStaticCertificateProperties(); + static bool verifySignature(const QString& key, const QByteArray& text, const QByteArray& signature); protected: QString getCollisionMaskAsString() const; diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index bd446ab332..fb27209de4 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1190,7 +1190,7 @@ bool EntityTree::verifyNonce(const QString& certID, const QString& nonce, Entity } QString annotatedKey = "-----BEGIN PUBLIC KEY-----\n" + key + "\n-----END PUBLIC KEY-----"; - bool verificationSuccess = verifySignature(annotatedKey.toUtf8(), actualNonce.toUtf8(), nonce.toUtf8()); + bool verificationSuccess = EntityItemProperties::verifySignature(annotatedKey.toUtf8(), actualNonce.toUtf8(), nonce.toUtf8()); if (verificationSuccess) { qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "succeeded."; @@ -1201,79 +1201,6 @@ bool EntityTree::verifyNonce(const QString& certID, const QString& nonce, Entity return verificationSuccess; } -// FIXME: This is largely copied from EntityItemProperties::verifyStaticCertificateProperties, which should be refactored to use this. -// I also don't like the nested-if style, but for this step I'm deliberately preserving the similarity. -bool EntityTree::verifySignature(const QString& publicKey, const QByteArray& digestByteArray, const QByteArray& signatureByteArray) { - - if (digestByteArray.isEmpty()) { - return false; - } - - const unsigned char* key = reinterpret_cast(publicKey.toUtf8().constData()); - int keyLength = publicKey.length(); - - BIO *bio = BIO_new_mem_buf((void*)key, keyLength); - EVP_PKEY* evp_key = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); - if (evp_key) { - EC_KEY* ec = EVP_PKEY_get1_EC_KEY(evp_key); - if (ec) { - const unsigned char* digest = reinterpret_cast(digestByteArray.constData()); - int digestLength = digestByteArray.length(); - - const unsigned char* signature = reinterpret_cast(signatureByteArray.constData()); - int signatureLength = signatureByteArray.length(); - - ERR_clear_error(); - // 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, - ec); - long error = ERR_get_error(); - if (error != 0) { - const char* error_str = ERR_error_string(error, NULL); - qCWarning(entities) << "ERROR while verifying signature! EC error:" << error_str - << "\nKey:" << publicKey << "\nutf8 Key Length:" << keyLength - << "\nDigest:" << digest << "\nDigest Length:" << digestLength - << "\nSignature:" << signature << "\nSignature Length:" << signatureLength; - } - EC_KEY_free(ec); - 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 signature! key" << publicKey << " EC key 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 signature! key" << publicKey << " EC PEM error:" << error_str; - return false; - } -} - void EntityTree::processChallengeOwnershipRequestPacket(ReceivedMessage& message, const SharedNodePointer& sourceNode) { int certIDByteArraySize; int textByteArraySize; diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 747a73ebd7..11a747d624 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -277,7 +277,6 @@ public: QByteArray computeNonce(const QString& certID, const QString ownerKey); bool verifyNonce(const QString& certID, const QString& nonce, EntityItemID& id); - static bool verifySignature(const QString& key, const QByteArray& text, const QByteArray& signature); void setMyAvatar(std::shared_ptr myAvatar) { _myAvatar = myAvatar; } From a6b437a79f01b3e76ce068f6b595b700cc77f608 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 22 Nov 2017 16:55:57 -0800 Subject: [PATCH 08/13] get rid of "error" warning because the key wasn't broken into 64 char lines --- libraries/entities/src/EntityTree.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index fb27209de4..e62399ce95 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -1189,7 +1189,7 @@ bool EntityTree::verifyNonce(const QString& certID, const QString& nonce, Entity key = sent.second; } - QString annotatedKey = "-----BEGIN PUBLIC KEY-----\n" + key + "\n-----END PUBLIC KEY-----"; + QString annotatedKey = "-----BEGIN PUBLIC KEY-----\n" + key.insert(64, "\n") + "\n-----END PUBLIC KEY-----"; bool verificationSuccess = EntityItemProperties::verifySignature(annotatedKey.toUtf8(), actualNonce.toUtf8(), nonce.toUtf8()); if (verificationSuccess) { From 1575119327717ffbe2166dcd02984233b06293ea Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 27 Nov 2017 10:46:57 -0800 Subject: [PATCH 09/13] PR feedback --- interface/src/commerce/Wallet.cpp | 2 +- libraries/entities/src/EntityItemProperties.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 98a27cd5cc..d6190aa7a0 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -768,7 +768,7 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack textPacket->write(certID); textPacket->write(textByteArray); - qCDebug(commerce) << "Sending ChallengeOwnershipReply Packet containing signed text" << textByteArray << "for CertID" << certID; + qCDebug(commerce) << "Sending ChallengeOwnership Packet containing signed text" << textByteArray << "for CertID" << certID; nodeList->sendPacket(std::move(textPacket), *sendingNode); } diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 11b83e1052..6ec5e8eb05 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -2579,7 +2579,7 @@ bool EntityItemProperties::verifySignature(const QString& publicKey, const QByte } bool EntityItemProperties::verifyStaticCertificateProperties() { - // True IIF a non-empty certificateID matches the static certificate json. + // True IFF 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. return verifySignature(EntityItem::_marketplacePublicKey, getStaticCertificateHash(), QByteArray::fromBase64(getCertificateID().toUtf8())); } From 55589312f13bec1ab2ae12e332097883d3ea7921 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 29 Nov 2017 09:53:48 -0800 Subject: [PATCH 10/13] new padding/initialization that matters on linux server --- interface/src/commerce/Wallet.cpp | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index d6190aa7a0..d4611d3e9a 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -166,7 +166,7 @@ bool writeKeys(const char* filename, EC_KEY* keys) { } QPair generateECKeypair() { - + EC_KEY* keyPair = EC_KEY_new_by_curve_name(NID_secp256k1); QPair retval; EC_KEY_set_asn1_flag(keyPair, OPENSSL_EC_NAMED_CURVE); @@ -212,8 +212,8 @@ QPair generateECKeypair() { // 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 // multiple keys? - retval.first = new QByteArray(reinterpret_cast(publicKeyDER), publicKeyLength ), - retval.second = new QByteArray(reinterpret_cast(privateKeyDER), privateKeyLength ); + retval.first = new QByteArray(reinterpret_cast(publicKeyDER), publicKeyLength); + retval.second = new QByteArray(reinterpret_cast(privateKeyDER), privateKeyLength); // cleanup the publicKeyDER and publicKeyDER data OPENSSL_free(publicKeyDER); @@ -255,7 +255,7 @@ QByteArray readPublicKey(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. +// so I'll return that. EC_KEY* readPrivateKey(const char* filename) { FILE* fp; EC_KEY* key = NULL; @@ -540,20 +540,21 @@ QString Wallet::signWithKey(const QByteArray& text, const QString& key) { qCInfo(commerce) << "Signing text" << text << "with key" << key; EC_KEY* ecPrivateKey = NULL; if ((ecPrivateKey = readPrivateKey(keyFilePath().toStdString().c_str()))) { - QByteArray signature(ECDSA_size(ecPrivateKey), 0); + unsigned char* sig = new unsigned char[ECDSA_size(ecPrivateKey)]; + unsigned int signatureBytes = 0; QByteArray hashedPlaintext = QCryptographicHash::hash(text, QCryptographicHash::Sha256); - int retrn = ECDSA_sign(0, - reinterpret_cast(hashedPlaintext.constData()), + int retrn = ECDSA_sign(0, + reinterpret_cast(hashedPlaintext.constData()), hashedPlaintext.size(), - reinterpret_cast(signature.data()), + sig, &signatureBytes, ecPrivateKey); EC_KEY_free(ecPrivateKey); - + QByteArray signature(reinterpret_cast(sig), signatureBytes); if (retrn != -1) { return signature.toBase64(); } @@ -733,11 +734,11 @@ void Wallet::handleChallengeOwnershipPacket(QSharedPointer pack qCDebug(commerce) << "During entity ownership challenge, creating the EC-signed nonce failed."; status = -1; } - + EC_KEY_free(ec); QByteArray ba = sig.toLocal8Bit(); const char *sigChar = ba.data(); - + QByteArray textByteArray; if (status > -1) { textByteArray = QByteArray(sigChar, (int) strlen(sigChar)); @@ -801,4 +802,4 @@ void Wallet::getWalletStatus() { walletScriptingInterface->setWalletStatus(status); return; } -} \ No newline at end of file +} From fb038c17ee27fd563685d0f46b37a77bdbf3d0ca Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 29 Nov 2017 09:54:34 -0800 Subject: [PATCH 11/13] for now, point this PR at staging so that it can be tested as built by jenkins --- libraries/networking/src/NetworkingConstants.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h index 0c210e4360..1edd842958 100644 --- a/libraries/networking/src/NetworkingConstants.h +++ b/libraries/networking/src/NetworkingConstants.h @@ -20,7 +20,7 @@ namespace NetworkingConstants { // \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; + const QUrl METAVERSE_SERVER_URL = METAVERSE_SERVER_URL_STAGING; } #endif // hifi_NetworkingConstants_h From 115bc27557911eebb7a335a63195fad8d71a466c Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 29 Nov 2017 13:39:58 -0800 Subject: [PATCH 12/13] back to stable, rather than staging --- libraries/networking/src/NetworkingConstants.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/NetworkingConstants.h b/libraries/networking/src/NetworkingConstants.h index 1edd842958..0c210e4360 100644 --- a/libraries/networking/src/NetworkingConstants.h +++ b/libraries/networking/src/NetworkingConstants.h @@ -20,7 +20,7 @@ namespace NetworkingConstants { // \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_STAGING; + const QUrl METAVERSE_SERVER_URL = METAVERSE_SERVER_URL_STABLE; } #endif // hifi_NetworkingConstants_h From 57ec73bb13675cdbf928871e83e293eb8a2ae516 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 29 Nov 2017 14:37:20 -0800 Subject: [PATCH 13/13] spacing --- libraries/entities/src/EntityItemProperties.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 6ec5e8eb05..5ab4bdee01 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -2553,8 +2553,7 @@ bool EntityItemProperties::verifySignature(const QString& publicKey, const QByte EVP_PKEY_free(evp_key); } return answer; - } - else { + } else { if (bio) { BIO_free(bio); } @@ -2566,8 +2565,7 @@ bool EntityItemProperties::verifySignature(const QString& publicKey, const QByte qCWarning(entities) << "Failed to verify signature! key" << publicKey << " EC key error:" << error_str; return false; } - } - else { + } else { if (bio) { BIO_free(bio); }