From f573f8e03bc6bda51d47aff484a9ab8d12452e20 Mon Sep 17 00:00:00 2001
From: Simon Walton <simon@highfidelity.io>
Date: Fri, 18 Oct 2019 14:37:04 -0700
Subject: [PATCH] Re-use challenge nonce until challenge succeeds

---
 assignment-client/src/avatars/MixerAvatar.cpp | 22 +++++++++++++------
 assignment-client/src/avatars/MixerAvatar.h   |  1 +
 2 files changed, 16 insertions(+), 7 deletions(-)

diff --git a/assignment-client/src/avatars/MixerAvatar.cpp b/assignment-client/src/avatars/MixerAvatar.cpp
index 01f3454292..9ed1d6a9b6 100644
--- a/assignment-client/src/avatars/MixerAvatar.cpp
+++ b/assignment-client/src/avatars/MixerAvatar.cpp
@@ -52,7 +52,7 @@ void MixerAvatar::challengeTimeout() {
         } else {
             _certifyFailed = true;
             _needsIdentityUpdate = true;
-            qCDebug(avatars) << "Dynamic verification TIMED-OUT for" << getDisplayName() << getSessionUUID();
+            qCWarning(avatars) << "ALERT: Dynamic verification TIMED-OUT for" << getDisplayName() << getSessionUUID();
         }
         break;
 
@@ -64,6 +64,7 @@ void MixerAvatar::challengeTimeout() {
 
     default:
         qCDebug(avatars) << "Ignoring timeout of avatar challenge";
+        break;
     }
 }
 
@@ -76,7 +77,7 @@ void MixerAvatar::fetchAvatarFST() {
     _pendingEvent = false;
 
     QUrl avatarURL = getSkeletonModelURL();
-    if (avatarURL.isEmpty() || avatarURL.isLocalFile() || avatarURL.scheme() == "qrc") {
+    if ((avatarURL.isEmpty() || avatarURL.isLocalFile() || avatarURL.scheme() == "qrc") && !isCertifyFailed()) {
         // Not network FST.
         return;
     }
@@ -340,18 +341,24 @@ void MixerAvatar::processCertifyEvents() {
 void MixerAvatar::sendOwnerChallenge() {
     auto nodeList = DependencyManager::get<NodeList>();
     QByteArray avatarID = ("{" + _marketplaceIdFromFST + "}").toUtf8();
-    QByteArray nonce = QUuid::createUuid().toByteArray();
+    if (_challengeNonce.isEmpty()) {
+        _challengeNonce = QUuid::createUuid().toByteArray();
+        QCryptographicHash nonceHash(QCryptographicHash::Sha256);
+        nonceHash.addData(_challengeNonce);
+        _challengeNonceHash = nonceHash.result();
+
+    }
 
     auto challengeOwnershipPacket = NLPacket::create(PacketType::ChallengeOwnership,
-        2 * sizeof(int) + nonce.length() + avatarID.length(), true);
+        2 * sizeof(int) + _challengeNonce.length() + avatarID.length(), true);
     challengeOwnershipPacket->writePrimitive(avatarID.length());
-    challengeOwnershipPacket->writePrimitive(nonce.length());
+    challengeOwnershipPacket->writePrimitive(_challengeNonce.length());
     challengeOwnershipPacket->write(avatarID);
-    challengeOwnershipPacket->write(nonce);
+    challengeOwnershipPacket->write(_challengeNonce);
 
     nodeList->sendPacket(std::move(challengeOwnershipPacket), *(nodeList->nodeWithUUID(getSessionUUID())) );
     QCryptographicHash nonceHash(QCryptographicHash::Sha256);
-    nonceHash.addData(nonce);
+    nonceHash.addData(_challengeNonce);
     _challengeNonceHash = nonceHash.result();
     _pendingEvent = false;
     
@@ -390,6 +397,7 @@ void MixerAvatar::processChallengeResponse(ReceivedMessage& response) {
             emit startChallengeTimer();
         } else {
             qCDebug(avatars) << "Dynamic verification SUCCEEDED for" << getDisplayName() << getSessionUUID();
+            _challengeNonce.clear();
         }
 
     } else {
diff --git a/assignment-client/src/avatars/MixerAvatar.h b/assignment-client/src/avatars/MixerAvatar.h
index bb6b859984..ec24d4e6bc 100644
--- a/assignment-client/src/avatars/MixerAvatar.h
+++ b/assignment-client/src/avatars/MixerAvatar.h
@@ -58,6 +58,7 @@ private:
     QString _certificateIdFromFST;
     QString _dynamicMarketResponse;
     QString _ownerPublicKey;
+    QByteArray _challengeNonce;
     QByteArray _challengeNonceHash;
     QTimer _challengeTimer;
     static constexpr int NUM_CHALLENGES_BEFORE_FAIL = 1;