diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 880fe74..1cbd876 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -127,6 +127,8 @@ void MainWindow::closeEvent(QCloseEvent* event) { s.setValue("baltablegeometry", ui->balancesTable->horizontalHeader()->saveState()); s.setValue("tratablegeometry", ui->transactionsTable->horizontalHeader()->saveState()); + s.sync(); + // Let the RPC know to shut down any running service. rpc->shutdownZcashd(); diff --git a/src/websockets.cpp b/src/websockets.cpp index 211551c..d6f351b 100644 --- a/src/websockets.cpp +++ b/src/websockets.cpp @@ -83,8 +83,12 @@ QString AppDataServer::getSecretHex() { void AppDataServer::saveNewSecret(QString secretHex) { QSettings s; s.setValue("mobileapp/secret", secretHex); + + s.sync(); } +QString AppDataServer::tempSecret; + void AppDataServer::connectAppDialog(QWidget* parent) { QDialog d(parent); Ui_MobileAppConnector con; @@ -115,26 +119,23 @@ void AppDataServer::connectAppDialog(QWidget* parent) { char* secretHex = new char[crypto_secretbox_KEYBYTES*2 + 1]; sodium_bin2hex(secretHex, crypto_secretbox_KEYBYTES*2+1, secretBin, crypto_secretbox_KEYBYTES); - saveNewSecret(secretHex); - QString secretStr(secretHex); + tempSecret = secretStr; - QString codeStr = uri + "," + secretHex; + QString codeStr = uri + "," + secretStr; con.lblConnStr->setText(codeStr); con.qrcode->setQrcodeString(codeStr); con.lblRemoteNonce->setText(AppDataServer::getNonceHex(NonceType::REMOTE)); con.lblLocalNonce->setText(AppDataServer::getNonceHex(NonceType::LOCAL)); - AppDataServer::saveNonceHex(NonceType::REMOTE, QString("00").repeated(24)); - AppDataServer::saveNonceHex(NonceType::LOCAL, QString("00").repeated(24)); - QObject::connect(con.btnDisconnect, &QPushButton::clicked, [=]() { AppDataServer::saveNonceHex(NonceType::REMOTE, QString("00").repeated(24)); AppDataServer::saveNonceHex(NonceType::LOCAL, QString("00").repeated(24)); }); d.exec(); + tempSecret = ""; } QString AppDataServer::getNonceHex(NonceType nt) { @@ -158,6 +159,7 @@ void AppDataServer::saveNonceHex(NonceType nt, QString noncehex) { else { s.setValue("mobileapp/remotenonce", noncehex); } + s.sync(); } QString AppDataServer::encryptOutgoing(QString msg) { @@ -212,7 +214,7 @@ QString AppDataServer::encryptOutgoing(QString msg) { return json.toJson(); } -QString AppDataServer::decryptMessage(QJsonDocument msg, QString secretHex) { +QString AppDataServer::decryptMessage(QJsonDocument msg, QString secretHex, bool skipNonceCheck) { // Decrypt and then process QString noncehex = msg.object().value("nonce").toString(); QString encryptedhex = msg.object().value("payload").toString(); @@ -227,8 +229,15 @@ QString AppDataServer::decryptMessage(QJsonDocument msg, QString secretHex) { sodium_hex2bin(noncebin, crypto_secretbox_NONCEBYTES, noncehex.toStdString().c_str(), noncehex.length(), NULL, NULL, NULL); - assert(sodium_compare(lastRemoteBin, noncebin, crypto_secretbox_NONCEBYTES) == -1); assert(crypto_secretbox_KEYBYTES == crypto_hash_sha256_BYTES); + if (!skipNonceCheck) { + if (sodium_compare(lastRemoteBin, noncebin, crypto_secretbox_NONCEBYTES) != -1) { + // Refuse to accept a lower nonce, return an error + delete[] lastRemoteBin; + delete[] noncebin; + return "error"; + } + } // Update the last seem remote hex saveNonceHex(NonceType::REMOTE, noncehex); @@ -247,8 +256,7 @@ QString AppDataServer::decryptMessage(QJsonDocument msg, QString secretHex) { QString payload; if (result == -1) { - payload = "error"; - + payload = "error"; } else { char* decryptedStr = new char[decryptedLen + 1]; sodium_memzero(decryptedStr, decryptedLen + 1); @@ -280,14 +288,42 @@ void AppDataServer::processMessage(QString message, MainWindow* mainWindow, QWeb } auto decrypted = decryptMessage(msg, getSecretHex()); - if (decrypted == "error") { - // If the dialog is open, then there might be a temporary, new secret key. Attempt to decrypt - // with that. + + // If the decryption failed, maybe this is a new connection, so see if the dialog is open and a + // temp secret is in place + + auto replyWithError = [=]() { auto r = QJsonDocument(QJsonObject{ - {"error", "Encrption error"} + {"error", "Encryption error"} }).toJson(); pClient->sendTextMessage(r); return; + }; + + if (decrypted == "error") { + // If the dialog is open, then there might be a temporary, new secret key. Attempt to decrypt + // with that. + if (!tempSecret.isEmpty()) { + decrypted = decryptMessage(msg, tempSecret, true); + if (decrypted == "error") { + // Oh, well. Just return an error + replyWithError(); + return; + } + else { + // This is a new connection. So, update the nonces and the secret + saveNewSecret(tempSecret); + AppDataServer::saveNonceHex(NonceType::REMOTE, QString("00").repeated(24)); + AppDataServer::saveNonceHex(NonceType::LOCAL, QString("00").repeated(24)); + + // Fall through to processDecryptedMessage + } + } + else { + replyWithError(); + return; + } + } processDecryptedMessage(decrypted, mainWindow, pClient); diff --git a/src/websockets.h b/src/websockets.h index 7ad647b..624bb3d 100644 --- a/src/websockets.h +++ b/src/websockets.h @@ -46,7 +46,7 @@ public: static void processGetInfo(MainWindow* mainWindow, QWebSocket* pClient); static void processGetTransactions(MainWindow* mainWindow, QWebSocket* pClient); - static QString decryptMessage(QJsonDocument msg, QString secretHex); + static QString decryptMessage(QJsonDocument msg, QString secretHex, bool skipNonceCheck = false); static QString encryptOutgoing(QString msg); static QString getSecretHex(); @@ -54,6 +54,9 @@ public: static QString getNonceHex(NonceType nt); static void saveNonceHex(NonceType nt, QString noncehex); + +private: + static QString tempSecret; }; class AppDataModel {