From 7d34469679c7145252d10bf39d1089a3128f8b95 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 28 Nov 2017 12:19:16 -0800 Subject: [PATCH 01/21] Head tracking bug fix for vive HMD users (cherry picked from commit 8364700b7aa8a7b8311c04235c6570d4610f2771) --- plugins/openvr/src/ViveControllerManager.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index 6767aafad2..772facc21f 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -194,7 +194,7 @@ private: bool _overrideHands { false }; mutable std::recursive_mutex _lock; - bool _hmdTrackingEnabled { false }; + bool _hmdTrackingEnabled { true }; QString configToString(Config config); friend class ViveControllerManager; From 982fad50507ec8408fbcb218814945dfba878bdd Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Wed, 29 Nov 2017 10:42:35 -0800 Subject: [PATCH 02/21] fix laser interaction on HUD in HMD mode --- .../system/controllers/controllerModules/hudOverlayPointer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerModules/hudOverlayPointer.js b/scripts/system/controllers/controllerModules/hudOverlayPointer.js index 38ee29ae3b..a1faacbdd6 100644 --- a/scripts/system/controllers/controllerModules/hudOverlayPointer.js +++ b/scripts/system/controllers/controllerModules/hudOverlayPointer.js @@ -62,7 +62,7 @@ this.pointingAtTablet = function(controllerData) { var rayPick = controllerData.rayPicks[this.hand]; - return (rayPick.objectID === HMD.tabletScreenID || rayPick.objectID === HMD.homeButtonID); + return (HMD.tabletScreenID && HMD.homeButtonID && (rayPick.objectID === HMD.tabletScreenID || rayPick.objectID === HMD.homeButtonID)); }; this.getOtherModule = function() { From 39afb4ab6a8ad06476f816b132c370fa6ea20a0c Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 29 Nov 2017 15:46:22 -0800 Subject: [PATCH 03/21] Merge pull request #11882 from howard-stearns/the-lost-ECDSA The lost ecdsa --- interface/src/commerce/Wallet.cpp | 196 ++++++++---------- .../ui/overlays/ContextOverlayInterface.cpp | 18 +- .../entities/src/EntityItemProperties.cpp | 50 ++--- libraries/entities/src/EntityItemProperties.h | 1 + libraries/entities/src/EntityTree.cpp | 110 ++++------ libraries/entities/src/EntityTree.h | 8 +- 6 files changed, 171 insertions(+), 212 deletions(-) diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 85632ff8f1..d4611d3e9a 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -27,11 +27,11 @@ #include #include -#include #include #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 +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 generateRSAKeypair() { +QPair generateECKeypair() { - RSA* keyPair = RSA_new(); - BIGNUM* exponent = BN_new(); + 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); + 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,13 +207,13 @@ 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 // 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); @@ -245,18 +225,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 +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(); @@ -557,25 +537,25 @@ 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."; - RSA* rsaPrivateKey = NULL; - if ((rsaPrivateKey = readPrivateKey(keyFilePath().toStdString().c_str()))) { - QByteArray signature(RSA_size(rsaPrivateKey), 0); + qCInfo(commerce) << "Signing text" << text << "with key" << key; + EC_KEY* ecPrivateKey = NULL; + if ((ecPrivateKey = readPrivateKey(keyFilePath().toStdString().c_str()))) { + unsigned char* sig = new unsigned char[ECDSA_size(ecPrivateKey)]; + 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); - // free the private key RSA struct now that we are done with it - RSA_free(rsaPrivateKey); + int retrn = ECDSA_sign(0, + reinterpret_cast(hashedPlaintext.constData()), + hashedPlaintext.size(), + sig, + &signatureBytes, ecPrivateKey); - if (encryptReturn != -1) { + EC_KEY_free(ecPrivateKey); + QByteArray signature(reinterpret_cast(sig), signatureBytes); + if (retrn != -1) { return signature.toBase64(); } } @@ -674,7 +654,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 +700,86 @@ 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]; + 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(encryptedText.constData()), - decryptedText, - rsa, - RSA_PKCS1_OAEP_PADDING); - - RSA_free(rsa); + 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 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(decryptedText), decryptionStatus); + EC_KEY_free(ec); + QByteArray ba = sig.toLocal8Bit(); + const char *sigChar = ba.data(); + + QByteArray textByteArray; + if (status > -1) { + textByteArray = QByteArray(sigChar, (int) strlen(sigChar)); } - 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; } } } diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 1ea0631d1f..3681f42381 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -326,21 +326,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); @@ -421,16 +421,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 108fc14e30..5ab4bdee01 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -13,21 +13,18 @@ #include #include #include - #include -#include #include #include +#include #include #include #include #include - #include #include #include #include - #include "EntitiesLogging.h" #include "EntityItem.h" #include "EntityItemProperties.h" @@ -2508,48 +2505,47 @@ 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) { - RSA* rsa = EVP_PKEY_get1_RSA(evp_key); - if (rsa) { - const QByteArray digestByteArray = getStaticCertificateHash(); + 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 QByteArray signatureByteArray = QByteArray::fromBase64(getCertificateID().toUtf8()); const unsigned char* signature = reinterpret_cast(signatureByteArray.constData()); int signatureLength = signatureByteArray.length(); ERR_clear_error(); - bool answer = RSA_verify(NID_sha256, + // 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 - << "\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; } - RSA_free(rsa); + EC_KEY_free(ec); if (bio) { BIO_free(bio); } @@ -2566,7 +2562,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 signature! key" << publicKey << " EC key error:" << error_str; return false; } } else { @@ -2575,7 +2571,13 @@ 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 signature! key" << publicKey << " EC PEM error:" << error_str; return false; } } + +bool EntityItemProperties::verifyStaticCertificateProperties() { + // 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())); +} 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 22a4d3a2dc..e62399ce95 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 @@ -1167,63 +1165,37 @@ 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-----"); - 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); +QByteArray EntityTree::computeNonce(const QString& certID, const QString ownerKey) { + 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(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 ""; - } + QWriteLocker locker(&_certNonceMapLock); + _certNonceMap.insert(certID, QPair(nonce, ownerKey)); - QWriteLocker locker(&_certNonceMapLock); - _certNonceMap.insert(certID, nonce); - - return encryptedText; - } else { - if (bio) { - BIO_free(bio); - } - return ""; - } + return nonceBytes; } -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); } - 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 == decryptedNonce); + 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) { 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; + qCDebug(entities) << "Ownership challenge for Cert ID" << certID << "failed for nonce" << actualNonce << "key" << key << "signature" << nonce; } return verificationSuccess; @@ -1231,67 +1203,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 +1276,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 +1284,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 +1363,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 d8981903d0..11a747d624 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); void setMyAvatar(std::shared_ptr myAvatar) { _myAvatar = myAvatar; } @@ -334,7 +334,7 @@ protected: QHash _entityCertificateIDMap; mutable QReadWriteLock _certNonceMapLock; - QHash _certNonceMap; + QHash> _certNonceMap; EntitySimulationPointer _simulation; @@ -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 _myAvatar{ nullptr }; From b817143e2ecb3b4c8c345a90e137bf7ebfe03c67 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Wed, 29 Nov 2017 18:16:21 -0700 Subject: [PATCH 04/21] Fixes on web and QML audio sync --- .../resources/html/createGlobalEventBridge.js | 17 +++++++++++------ libraries/ui/src/ui/OffscreenQmlSurface.cpp | 7 +++---- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/interface/resources/html/createGlobalEventBridge.js b/interface/resources/html/createGlobalEventBridge.js index bccbdfaf7c..3355ca87c8 100644 --- a/interface/resources/html/createGlobalEventBridge.js +++ b/interface/resources/html/createGlobalEventBridge.js @@ -65,21 +65,26 @@ var EventBridge; // we need to listen to events that might precede the addition of this elements. // A more robust hack will be to add a setInterval that look for DOM changes every 100-300 ms (low performance?) - window.onload = function(){ + window.addEventListener("load",function(event) { setTimeout(function() { EventBridge.forceHtmlAudioOutputDeviceUpdate(); + console.log(":: Window Loaded"); }, 1200); - }; - document.onclick = function(){ + }, false); + + document.addEventListener("click",function(){ setTimeout(function() { EventBridge.forceHtmlAudioOutputDeviceUpdate(); + console.log(":: Window Clicked"); }, 1200); - }; - document.onchange = function(){ + }, false); + + document.addEventListener("change",function(){ setTimeout(function() { EventBridge.forceHtmlAudioOutputDeviceUpdate(); + console.log(":: Window Changes"); }, 1200); - }; + }, false); tempEventBridge._callbacks.forEach(function (callback) { EventBridge.scriptEventReceived.connect(callback); diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 7bc88d73f6..a585b80090 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -139,7 +139,6 @@ public: QThread::msleep(_runDelayMs); } auto audioIO = DependencyManager::get(); - QString deviceName = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName(); for (auto player : _container->findChildren()) { auto mediaState = player->state(); QMediaService *svc = player->service(); @@ -156,7 +155,7 @@ public: for (int i = 0; i < outputs.size(); i++) { QString output = outputs[i]; QString description = out->outputDescription(output); - if (description == deviceName) { + if (description == _newTargetDevice) { deviceOuput = output; break; } @@ -171,7 +170,7 @@ public: player->stop(); } } - qDebug() << "QML Audio changed to " << deviceName; + qDebug() << "QML Audio changed to " << _newTargetDevice; } private: @@ -700,7 +699,7 @@ void OffscreenQmlSurface::forceQmlAudioOutputDeviceUpdate() { } else { auto audioIO = DependencyManager::get(); QString deviceName = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName(); - int waitForAudioQmlMs = 500; + int waitForAudioQmlMs = 200; // The audio device need to be change using oth new AudioHandler(_rootItem, deviceName, waitForAudioQmlMs); } From b890a29b3a8cf87f8f67b2d1b057be59d12be10c Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Wed, 29 Nov 2017 19:05:01 -0700 Subject: [PATCH 05/21] Clean console log --- interface/resources/html/createGlobalEventBridge.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/interface/resources/html/createGlobalEventBridge.js b/interface/resources/html/createGlobalEventBridge.js index 3355ca87c8..b85aa33e33 100644 --- a/interface/resources/html/createGlobalEventBridge.js +++ b/interface/resources/html/createGlobalEventBridge.js @@ -68,21 +68,18 @@ var EventBridge; window.addEventListener("load",function(event) { setTimeout(function() { EventBridge.forceHtmlAudioOutputDeviceUpdate(); - console.log(":: Window Loaded"); }, 1200); }, false); document.addEventListener("click",function(){ setTimeout(function() { EventBridge.forceHtmlAudioOutputDeviceUpdate(); - console.log(":: Window Clicked"); }, 1200); }, false); document.addEventListener("change",function(){ setTimeout(function() { EventBridge.forceHtmlAudioOutputDeviceUpdate(); - console.log(":: Window Changes"); }, 1200); }, false); From e02e81039976ba32feb7c266e4331f9bb7eaab29 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 30 Nov 2017 15:06:02 -0700 Subject: [PATCH 06/21] Audio Handler fix --- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 69 +++++++++++---------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index a585b80090..7c100ebb1b 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -121,10 +121,10 @@ uint64_t uvec2ToUint64(const uvec2& v) { class AudioHandler : public QObject, QRunnable { Q_OBJECT public: - AudioHandler(QObject* container, const QString& deviceName, int runDelayMs = 0, QObject* parent = nullptr) : QObject(parent) { - _container = container; + AudioHandler(OffscreenQmlSurface* surface, const QString& deviceName, int runDelayMs = 0, QObject* parent = nullptr) : QObject(parent) { _newTargetDevice = deviceName; _runDelayMs = runDelayMs; + _surface = surface; setAutoDelete(true); QThreadPool::globalInstance()->start(this); } @@ -139,35 +139,40 @@ public: QThread::msleep(_runDelayMs); } auto audioIO = DependencyManager::get(); - for (auto player : _container->findChildren()) { - auto mediaState = player->state(); - QMediaService *svc = player->service(); - if (nullptr == svc) { - return; - } - QAudioOutputSelectorControl *out = qobject_cast - (svc->requestControl(QAudioOutputSelectorControl_iid)); - if (nullptr == out) { - return; - } - QString deviceOuput; - auto outputs = out->availableOutputs(); - for (int i = 0; i < outputs.size(); i++) { - QString output = outputs[i]; - QString description = out->outputDescription(output); - if (description == _newTargetDevice) { - deviceOuput = output; - break; + + auto rootItem = _surface->getRootItem(); + if (rootItem) { + for (auto player : rootItem->findChildren()) { + auto mediaState = player->state(); + QMediaService *svc = player->service(); + if (nullptr == svc) { + return; + } + QAudioOutputSelectorControl *out = qobject_cast + (svc->requestControl(QAudioOutputSelectorControl_iid)); + if (nullptr == out) { + return; + } + QString deviceOuput; + auto outputs = out->availableOutputs(); + for (int i = 0; i < outputs.size(); i++) { + QString output = outputs[i]; + QString description = out->outputDescription(output); + if (description == _newTargetDevice) { + deviceOuput = output; + break; + } + } + out->setActiveOutput(deviceOuput); + svc->releaseControl(out); + // if multimedia was paused, it will start playing automatically after changing audio device + // this will reset it back to a paused state + if (mediaState == QMediaPlayer::State::PausedState) { + player->pause(); + } + else if (mediaState == QMediaPlayer::State::StoppedState) { + player->stop(); } - } - out->setActiveOutput(deviceOuput); - svc->releaseControl(out); - // if multimedia was paused, it will start playing automatically after changing audio device - // this will reset it back to a paused state - if (mediaState == QMediaPlayer::State::PausedState) { - player->pause(); - } else if (mediaState == QMediaPlayer::State::StoppedState) { - player->stop(); } } qDebug() << "QML Audio changed to " << _newTargetDevice; @@ -175,7 +180,7 @@ public: private: QString _newTargetDevice; - QObject* _container; + OffscreenQmlSurface* _surface; int _runDelayMs; }; @@ -701,7 +706,7 @@ void OffscreenQmlSurface::forceQmlAudioOutputDeviceUpdate() { QString deviceName = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName(); int waitForAudioQmlMs = 200; // The audio device need to be change using oth - new AudioHandler(_rootItem, deviceName, waitForAudioQmlMs); + new AudioHandler(this, deviceName, waitForAudioQmlMs); } } From 58e829dbda1d64100913352a145f9293418868f2 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 30 Nov 2017 15:11:38 -0700 Subject: [PATCH 07/21] Delete leftover line --- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 7c100ebb1b..019897e5c3 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -138,8 +138,6 @@ public: if (_runDelayMs > 0) { QThread::msleep(_runDelayMs); } - auto audioIO = DependencyManager::get(); - auto rootItem = _surface->getRootItem(); if (rootItem) { for (auto player : rootItem->findChildren()) { From 0729579266b167c2d832362fca0234118809c9a1 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 30 Nov 2017 15:21:43 -0700 Subject: [PATCH 08/21] fix for loop --- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 019897e5c3..2f5d0197bb 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -140,7 +140,9 @@ public: } auto rootItem = _surface->getRootItem(); if (rootItem) { - for (auto player : rootItem->findChildren()) { + auto children = rootItem->findChildren(); + for (int i = 0; i < children.size(); i++) { + auto player = children[i]; auto mediaState = player->state(); QMediaService *svc = player->service(); if (nullptr == svc) { @@ -702,7 +704,7 @@ void OffscreenQmlSurface::forceQmlAudioOutputDeviceUpdate() { } else { auto audioIO = DependencyManager::get(); QString deviceName = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName(); - int waitForAudioQmlMs = 200; + int waitForAudioQmlMs = 500; // The audio device need to be change using oth new AudioHandler(this, deviceName, waitForAudioQmlMs); } From 9cd2dc921aa013b493db7f0a7216e4ff1b59dfaa Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 30 Nov 2017 15:31:29 -0700 Subject: [PATCH 09/21] back to for loop --- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 2f5d0197bb..019897e5c3 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -140,9 +140,7 @@ public: } auto rootItem = _surface->getRootItem(); if (rootItem) { - auto children = rootItem->findChildren(); - for (int i = 0; i < children.size(); i++) { - auto player = children[i]; + for (auto player : rootItem->findChildren()) { auto mediaState = player->state(); QMediaService *svc = player->service(); if (nullptr == svc) { @@ -704,7 +702,7 @@ void OffscreenQmlSurface::forceQmlAudioOutputDeviceUpdate() { } else { auto audioIO = DependencyManager::get(); QString deviceName = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName(); - int waitForAudioQmlMs = 500; + int waitForAudioQmlMs = 200; // The audio device need to be change using oth new AudioHandler(this, deviceName, waitForAudioQmlMs); } From db374b8c6d0814dc3750d255efb93d906275c7dd Mon Sep 17 00:00:00 2001 From: Dante Ruiz Date: Thu, 30 Nov 2017 14:49:06 -0800 Subject: [PATCH 10/21] fixing adjusting equipped entities --- .../controllers/controllerModules/nearParentGrabEntity.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index 26bebc9247..01c8424e0c 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -81,7 +81,8 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); }; this.otherHandIsParent = function(props) { - return this.getOtherModule().thisHandIsParent(props); + var otherModule = this.getOtherModule(); + return (otherModule.thisHandIsParent(props) && otherModule.grabbing); }; this.startNearParentingGrabEntity = function (controllerData, targetProps) { From 98e836169005e7f143092c171c59bf3ecc044d39 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 30 Nov 2017 16:07:43 -0700 Subject: [PATCH 11/21] Fix and rebuild --- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 10 +++------- libraries/ui/src/ui/OffscreenQmlSurface.h | 2 ++ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 019897e5c3..c9fe738940 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -139,7 +139,7 @@ public: QThread::msleep(_runDelayMs); } auto rootItem = _surface->getRootItem(); - if (rootItem) { + if (rootItem && !_surface->getCleaned()) { for (auto player : rootItem->findChildren()) { auto mediaState = player->state(); QMediaService *svc = player->service(); @@ -165,12 +165,7 @@ public: svc->releaseControl(out); // if multimedia was paused, it will start playing automatically after changing audio device // this will reset it back to a paused state - if (mediaState == QMediaPlayer::State::PausedState) { - player->pause(); - } - else if (mediaState == QMediaPlayer::State::StoppedState) { - player->stop(); - } + } } qDebug() << "QML Audio changed to " << _newTargetDevice; @@ -504,6 +499,7 @@ QOpenGLContext* OffscreenQmlSurface::getSharedContext() { } void OffscreenQmlSurface::cleanup() { + _isCleaned = true; _canvas->makeCurrent(); _renderControl->invalidate(); diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.h b/libraries/ui/src/ui/OffscreenQmlSurface.h index 44c6c3c77b..371b110aa9 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.h +++ b/libraries/ui/src/ui/OffscreenQmlSurface.h @@ -75,6 +75,7 @@ public: void pause(); void resume(); bool isPaused() const; + bool getCleaned() { return _isCleaned; } void setBaseUrl(const QUrl& baseUrl); QQuickItem* getRootItem(); @@ -177,6 +178,7 @@ private: bool _polish { true }; bool _paused { true }; bool _focusText { false }; + bool _isCleaned{ false }; uint8_t _maxFps { 60 }; MouseTranslator _mouseTranslator { [](const QPointF& p) { return p.toPoint(); } }; QWindow* _proxyWindow { nullptr }; From 5d1f2a04c1f8101df9d3ed48fd5efb4a7564d42b Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 30 Nov 2017 16:43:38 -0700 Subject: [PATCH 12/21] restate deleted lines --- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index c9fe738940..98a307e497 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -165,7 +165,12 @@ public: svc->releaseControl(out); // if multimedia was paused, it will start playing automatically after changing audio device // this will reset it back to a paused state - + if (mediaState == QMediaPlayer::State::PausedState) { + player->pause(); + } + else if (mediaState == QMediaPlayer::State::StoppedState) { + player->stop(); + } } } qDebug() << "QML Audio changed to " << _newTargetDevice; From 83e370d9ded2657f7e1613cb8b7f9a6be8bd7bad Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 30 Nov 2017 17:40:49 -0700 Subject: [PATCH 13/21] add shared pointer --- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 11 +++++------ libraries/ui/src/ui/OffscreenQmlSurface.h | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 98a307e497..5bcf06ce82 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -121,7 +121,7 @@ uint64_t uvec2ToUint64(const uvec2& v) { class AudioHandler : public QObject, QRunnable { Q_OBJECT public: - AudioHandler(OffscreenQmlSurface* surface, const QString& deviceName, int runDelayMs = 0, QObject* parent = nullptr) : QObject(parent) { + AudioHandler(QSharedPointer surface, const QString& deviceName, int runDelayMs = 0, QObject* parent = nullptr) : QObject(parent) { _newTargetDevice = deviceName; _runDelayMs = runDelayMs; _surface = surface; @@ -138,9 +138,8 @@ public: if (_runDelayMs > 0) { QThread::msleep(_runDelayMs); } - auto rootItem = _surface->getRootItem(); - if (rootItem && !_surface->getCleaned()) { - for (auto player : rootItem->findChildren()) { + if (!_surface.isNull() && _surface->getRootItem() && !_surface->getCleaned()) { + for (auto player : _surface->getRootItem()->findChildren()) { auto mediaState = player->state(); QMediaService *svc = player->service(); if (nullptr == svc) { @@ -178,7 +177,7 @@ public: private: QString _newTargetDevice; - OffscreenQmlSurface* _surface; + QSharedPointer _surface; int _runDelayMs; }; @@ -705,7 +704,7 @@ void OffscreenQmlSurface::forceQmlAudioOutputDeviceUpdate() { QString deviceName = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName(); int waitForAudioQmlMs = 200; // The audio device need to be change using oth - new AudioHandler(this, deviceName, waitForAudioQmlMs); + new AudioHandler(sharedFromThis(), deviceName, waitForAudioQmlMs); } } diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.h b/libraries/ui/src/ui/OffscreenQmlSurface.h index 371b110aa9..34da11c66c 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.h +++ b/libraries/ui/src/ui/OffscreenQmlSurface.h @@ -40,7 +40,7 @@ class QQuickItem; using QmlContextCallback = std::function; -class OffscreenQmlSurface : public QObject { +class OffscreenQmlSurface : public QObject, public QEnableSharedFromThis { Q_OBJECT Q_PROPERTY(bool focusText READ isFocusText NOTIFY focusTextChanged) public: From d1017d81bd801ae737c260e4670bc88c868efd5f Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Thu, 30 Nov 2017 17:41:36 -0800 Subject: [PATCH 14/21] fix issues in model import sizing caused by recent refactor --- .../entities-renderer/src/RenderableModelEntityItem.cpp | 8 +++++--- libraries/entities/src/EntityItem.cpp | 6 ++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index ed6ce10b50..06b81ff428 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -75,8 +75,11 @@ RenderableModelEntityItem::RenderableModelEntityItem(const EntityItemID& entityI RenderableModelEntityItem::~RenderableModelEntityItem() { } void RenderableModelEntityItem::setDimensions(const glm::vec3& value) { - _dimensionsInitialized = true; - ModelEntityItem::setDimensions(value); + glm::vec3 newDimensions = glm::max(value, glm::vec3(0.0f)); // can never have negative dimensions + if (getDimensions() != newDimensions) { + _dimensionsInitialized = true; + ModelEntityItem::setDimensions(value); + } } QVariantMap parseTexturesToMap(QString textures, const QVariantMap& defaultTextures) { @@ -1160,7 +1163,6 @@ bool ModelEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoin return true; } - if (model->getScaleToFitDimensions() != entity->getDimensions() || model->getRegistrationPoint() != entity->getRegistrationPoint()) { return true; diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 4f2b290635..c8e7ce6c11 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1622,11 +1622,13 @@ void EntityItem::setDimensions(const glm::vec3& value) { if (getDimensions() != newDimensions) { withWriteLock([&] { _dimensions = newDimensions; - _dirtyFlags |= (Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS); - _queryAACubeSet = false; }); locationChanged(); dimensionsChanged(); + withWriteLock([&] { + _dirtyFlags |= (Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS); + _queryAACubeSet = false; + }); } } From b8f7ba9aa692865077599d8f578dfdea6501d109 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Fri, 1 Dec 2017 09:29:19 -0800 Subject: [PATCH 15/21] Fix month display in commerce... --- .../commerce/inspectionCertificate/InspectionCertificate.qml | 2 +- interface/resources/qml/hifi/commerce/wallet/WalletHome.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml index 14ed9ece67..d98c453a0c 100644 --- a/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml +++ b/interface/resources/qml/hifi/commerce/inspectionCertificate/InspectionCertificate.qml @@ -430,7 +430,7 @@ Rectangle { var a = new Date(timestamp); var year = a.getFullYear(); - var month = addLeadingZero(a.getMonth()); + var month = addLeadingZero(a.getMonth() + 1); var day = addLeadingZero(a.getDate()); var hour = a.getHours(); var drawnHour = hour; diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml index fd7ce0fdfd..4d8dde0331 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml @@ -339,7 +339,7 @@ Item { var a = new Date(timestamp); var year = a.getFullYear(); - var month = addLeadingZero(a.getMonth()); + var month = addLeadingZero(a.getMonth() + 1); var day = addLeadingZero(a.getDate()); var hour = a.getHours(); var drawnHour = hour; From d271f619870163045927ea4d7b8298a9fddd96ed Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Fri, 1 Dec 2017 14:37:59 -0700 Subject: [PATCH 16/21] Proper fix --- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 40 ++++++++++++++------- libraries/ui/src/ui/OffscreenQmlSurface.h | 5 +++ 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index 5bcf06ce82..b3ebf30a94 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -121,23 +121,18 @@ uint64_t uvec2ToUint64(const uvec2& v) { class AudioHandler : public QObject, QRunnable { Q_OBJECT public: - AudioHandler(QSharedPointer surface, const QString& deviceName, int runDelayMs = 0, QObject* parent = nullptr) : QObject(parent) { + AudioHandler(QSharedPointer surface, const QString& deviceName, QObject* parent = nullptr) : QObject(parent) { _newTargetDevice = deviceName; - _runDelayMs = runDelayMs; _surface = surface; setAutoDelete(true); - QThreadPool::globalInstance()->start(this); + if (deviceName.size() > 0) { + QThreadPool::globalInstance()->start(this); + } } virtual ~AudioHandler() { qDebug() << "Audio Handler Destroyed"; } void run() override { - if (_newTargetDevice.isEmpty()) { - return; - } - if (_runDelayMs > 0) { - QThread::msleep(_runDelayMs); - } if (!_surface.isNull() && _surface->getRootItem() && !_surface->getCleaned()) { for (auto player : _surface->getRootItem()->findChildren()) { auto mediaState = player->state(); @@ -602,6 +597,7 @@ OffscreenQmlSurface::OffscreenQmlSurface() { OffscreenQmlSurface::~OffscreenQmlSurface() { QObject::disconnect(&_updateTimer); + disconnectAudioOutputTimer(); QObject::disconnect(qApp); cleanup(); @@ -615,6 +611,15 @@ OffscreenQmlSurface::~OffscreenQmlSurface() { void OffscreenQmlSurface::onAboutToQuit() { _paused = true; QObject::disconnect(&_updateTimer); + disconnectAudioOutputTimer(); + +} + +void OffscreenQmlSurface::disconnectAudioOutputTimer() { + if (_audioOutputUpdateTimer.isActive()) { + _audioOutputUpdateTimer.stop(); + } + QObject::disconnect(&_audioOutputUpdateTimer); } void OffscreenQmlSurface::create() { @@ -673,6 +678,14 @@ void OffscreenQmlSurface::create() { } }); + // Setup the update of the QML media components with the current audio output device + QObject::connect(&_audioOutputUpdateTimer, &QTimer::timeout, this, [this]() { + new AudioHandler(sharedFromThis(), _currentAudioOutputDevice); + }); + int waitForAudioQmlMs = 200; + _audioOutputUpdateTimer.setInterval(waitForAudioQmlMs); + _audioOutputUpdateTimer.setSingleShot(true); + // When Quick says there is a need to render, we will not render immediately. Instead, // a timer with a small interval is used to get better performance. QObject::connect(&_updateTimer, &QTimer::timeout, this, &OffscreenQmlSurface::updateQuick); @@ -701,10 +714,11 @@ void OffscreenQmlSurface::forceQmlAudioOutputDeviceUpdate() { QMetaObject::invokeMethod(this, "forceQmlAudioOutputDeviceUpdate", Qt::QueuedConnection); } else { auto audioIO = DependencyManager::get(); - QString deviceName = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName(); - int waitForAudioQmlMs = 200; - // The audio device need to be change using oth - new AudioHandler(sharedFromThis(), deviceName, waitForAudioQmlMs); + _currentAudioOutputDevice = audioIO->getActiveAudioDevice(QAudio::AudioOutput).deviceName(); + if (_audioOutputUpdateTimer.isActive()) { + _audioOutputUpdateTimer.stop(); + } + _audioOutputUpdateTimer.start(); } } diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.h b/libraries/ui/src/ui/OffscreenQmlSurface.h index 34da11c66c..4c23c62c12 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.h +++ b/libraries/ui/src/ui/OffscreenQmlSurface.h @@ -117,6 +117,7 @@ public slots: void changeAudioOutputDevice(const QString& deviceName, bool isHtmlUpdate = false); void forceHtmlAudioOutputDeviceUpdate(); void forceQmlAudioOutputDeviceUpdate(); + signals: void audioOutputDeviceChanged(const QString& deviceName); @@ -148,6 +149,7 @@ private: void render(); void cleanup(); QJsonObject getGLContextData(); + void disconnectAudioOutputTimer(); private slots: void updateQuick(); @@ -171,6 +173,9 @@ private: uint64_t _lastRenderTime { 0 }; uvec2 _size; + QTimer _audioOutputUpdateTimer; + QString _currentAudioOutputDevice; + // Texture management TextureAndFence _latestTextureAndFence { 0, 0 }; From fefedc11c80fde2fd181088a61bd41c0976029c6 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Fri, 1 Dec 2017 15:17:23 -0700 Subject: [PATCH 17/21] mac fix --- libraries/ui/src/ui/OffscreenQmlSurface.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/ui/src/ui/OffscreenQmlSurface.cpp b/libraries/ui/src/ui/OffscreenQmlSurface.cpp index b3ebf30a94..9a591018f5 100644 --- a/libraries/ui/src/ui/OffscreenQmlSurface.cpp +++ b/libraries/ui/src/ui/OffscreenQmlSurface.cpp @@ -173,7 +173,6 @@ public: private: QString _newTargetDevice; QSharedPointer _surface; - int _runDelayMs; }; class OffscreenTextures { From c67ff3b5d8450436dad40b8aefb0995ececee335 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Tue, 5 Dec 2017 00:03:48 +0300 Subject: [PATCH 18/21] 10024 Wallet: TextField Focus Problems note: removed excessive MouseArea-s --- .../hifi/commerce/wallet/PassphraseModal.qml | 10 ------- .../commerce/wallet/PassphraseSelection.qml | 27 ------------------- 2 files changed, 37 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml index d967a36b68..249645e76f 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml @@ -206,16 +206,6 @@ Item { root.isPasswordField = (focus && passphraseField.echoMode === TextInput.Password); } - MouseArea { - anchors.fill: parent; - - onClicked: { - root.keyboardRaised = true; - root.isPasswordField = (passphraseField.echoMode === TextInput.Password); - mouse.accepted = false; - } - } - onAccepted: { submitPassphraseInputButton.enabled = false; commerce.setPassphrase(passphraseField.text); diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml index ffeedde8f0..166388afee 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml @@ -87,15 +87,6 @@ Item { } } - MouseArea { - anchors.fill: parent; - onPressed: { - var hidePassword = (currentPassphraseField.echoMode === TextInput.Password); - sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword}); - mouse.accepted = false; - } - } - onAccepted: { passphraseField.focus = true; } @@ -115,15 +106,6 @@ Item { activeFocusOnPress: true; activeFocusOnTab: true; - MouseArea { - anchors.fill: parent; - onPressed: { - var hidePassword = (passphraseField.echoMode === TextInput.Password); - sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword}); - mouse.accepted = false; - } - } - onFocusChanged: { if (focus) { var hidePassword = (passphraseField.echoMode === TextInput.Password); @@ -151,15 +133,6 @@ Item { activeFocusOnPress: true; activeFocusOnTab: true; - MouseArea { - anchors.fill: parent; - onPressed: { - var hidePassword = (passphraseFieldAgain.echoMode === TextInput.Password); - sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword}); - mouse.accepted = false; - } - } - onFocusChanged: { if (focus) { var hidePassword = (passphraseFieldAgain.echoMode === TextInput.Password); From 8a5904373b13915cfca294e119ef51ab07658270 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Tue, 5 Dec 2017 00:04:46 +0300 Subject: [PATCH 19/21] remove 'collapse keyboard' hack as now there is dedicated 'collapse keyboard' button --- .../hifi/commerce/wallet/PassphraseModal.qml | 19 ------------------- .../qml/hifi/commerce/wallet/Wallet.qml | 19 ------------------- 2 files changed, 38 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml index 249645e76f..582052c4c3 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseModal.qml @@ -352,25 +352,6 @@ Item { right: parent.right; } - Image { - id: lowerKeyboardButton; - z: 999; - source: "images/lowerKeyboard.png"; - anchors.right: keyboard.right; - anchors.top: keyboard.showMirrorText ? keyboard.top : undefined; - anchors.bottom: keyboard.showMirrorText ? undefined : keyboard.bottom; - height: 50; - width: 60; - - MouseArea { - anchors.fill: parent; - - onClicked: { - root.keyboardRaised = false; - } - } - } - HifiControlsUit.Keyboard { id: keyboard; raised: HMD.mounted && root.keyboardRaised; diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 759d7a37eb..97fee72968 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -666,25 +666,6 @@ Rectangle { right: parent.right; } - Image { - id: lowerKeyboardButton; - z: 999; - source: "images/lowerKeyboard.png"; - anchors.right: keyboard.right; - anchors.top: keyboard.showMirrorText ? keyboard.top : undefined; - anchors.bottom: keyboard.showMirrorText ? undefined : keyboard.bottom; - height: 50; - width: 60; - - MouseArea { - anchors.fill: parent; - - onClicked: { - root.keyboardRaised = false; - } - } - } - HifiControlsUit.Keyboard { id: keyboard; raised: HMD.mounted && root.keyboardRaised; From f7328f05fb3c3fc5af3fc2ba11763af8d2e1840d Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Tue, 5 Dec 2017 00:51:42 +0300 Subject: [PATCH 20/21] remove keyboard-hiding logic as keyboard hides should be handled by focus change listener in 'onFocusObjectChanged' in the OffscreenQmlSurface --- .../qml/hifi/commerce/wallet/PassphraseSelection.qml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml index 166388afee..9ba066a9fd 100644 --- a/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml +++ b/interface/resources/qml/hifi/commerce/wallet/PassphraseSelection.qml @@ -82,8 +82,6 @@ Item { if (focus) { var hidePassword = (currentPassphraseField.echoMode === TextInput.Password); sendSignalToWallet({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword}); - } else if (!passphraseFieldAgain.focus) { - sendSignalToWallet({method: 'walletSetup_lowerKeyboard', isPasswordField: false}); } } @@ -110,8 +108,6 @@ Item { if (focus) { var hidePassword = (passphraseField.echoMode === TextInput.Password); sendMessageToLightbox({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword}); - } else if (!passphraseFieldAgain.focus) { - sendMessageToLightbox({method: 'walletSetup_lowerKeyboard', isPasswordField: false}); } } @@ -137,8 +133,6 @@ Item { if (focus) { var hidePassword = (passphraseFieldAgain.echoMode === TextInput.Password); sendMessageToLightbox({method: 'walletSetup_raiseKeyboard', isPasswordField: hidePassword}); - } else if (!passphraseField.focus) { - sendMessageToLightbox({method: 'walletSetup_lowerKeyboard', isPasswordField: false}); } } From d59c9bd73aac2bb802e0c3f29f9e700fb1eb9b32 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 5 Dec 2017 10:26:49 -0800 Subject: [PATCH 21/21] Fix gray/incorrect security image after relog --- .../resources/qml/hifi/commerce/wallet/WalletHome.qml | 1 + interface/src/commerce/Wallet.cpp | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml index ec7468651d..d23079d3f3 100644 --- a/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml +++ b/interface/resources/qml/hifi/commerce/wallet/WalletHome.qml @@ -68,6 +68,7 @@ Item { Connections { target: GlobalServices onMyUsernameChanged: { + transactionHistoryModel.clear(); usernameText.text = Account.username; } } diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index d4611d3e9a..69914e97a4 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -321,6 +321,16 @@ Wallet::Wallet() { auto accountManager = DependencyManager::get(); connect(accountManager.data(), &AccountManager::usernameChanged, this, [&]() { getWalletStatus(); + _publicKeys.clear(); + + if (_securityImage) { + delete _securityImage; + } + _securityImage = nullptr; + + // tell the provider we got nothing + updateImageProvider(); + _passphrase->clear(); }); }