]> Nutra Git (v1) - gamesguru/feather.git/commitdiff
airgap: encrypt view-only details UR
authortobtoht <tob@featherwallet.org>
Thu, 7 Dec 2023 11:09:23 +0000 (12:09 +0100)
committertobtoht <tob@featherwallet.org>
Thu, 7 Dec 2023 11:12:26 +0000 (12:12 +0100)
src/dialog/URDialog.cpp
src/dialog/URDialog.h
src/dialog/ViewOnlyDialog.cpp
src/libwalletqt/WalletManager.cpp
src/libwalletqt/WalletManager.h

index 370f968899ed4d5cabe23af12ed154037fa2cadc..8d624b239cbc9e65cc34ef5a7cb58a2169322c5d 100644 (file)
@@ -5,16 +5,18 @@
 #include "ui_URDialog.h"
 
 #include <QFileDialog>
+#include <QInputDialog>
 
 #include "utils/Utils.h"
+#include "WalletManager.h"
 
-URDialog::URDialog(QWidget *parent, const QString &data, bool scanOnly)
+URDialog::URDialog(QWidget *parent, const std::string &data, bool scanOnly)
         : WindowModalDialog(parent)
         , ui(new Ui::URDialog)
 {
     ui->setupUi(this);
 
-    if (!data.isEmpty()) {
+    if (!data.empty()) {
         ui->btn_loadFile->setVisible(false);
         ui->btn_loadClipboard->setVisible(false);
         ui->tabWidget->setTabVisible(1, false);
@@ -26,8 +28,7 @@ URDialog::URDialog(QWidget *parent, const QString &data, bool scanOnly)
         int availableHeight = currentScreen->availableGeometry().height() - 200;
         this->resize(availableHeight, availableHeight);
 
-        std::string d = data.toStdString();
-        ui->widgetUR->setData("xmr-viewonly", d);
+        ui->widgetUR->setData("xmr-viewonly", data);
         return;
     }
 
@@ -75,23 +76,38 @@ URDialog::URDialog(QWidget *parent, const QString &data, bool scanOnly)
         }
 
         if (ui->widgetScanner->getURType() == "xmr-viewonly") {
-            QRegularExpression viewOnlyDetails(
-                "Secret view key: (?<key>[0-9a-f]{64})\nAddress: (?<address>\\w+)\nRestore height: (?<restoreheight>\\d+)\nWallet name: (?<walletname>\\w+)\n",
-                QRegularExpression::CaseInsensitiveOption | QRegularExpression::DotMatchesEverythingOption);
-            QString data = QString::fromStdString(ui->widgetScanner->getURData());
-            QRegularExpressionMatch match = viewOnlyDetails.match(data);
-
-            if (!match.hasMatch()) {
-                Utils::showError(this, "Unable to load view-only details", "Unexpected data");
+            std::string urData = ui->widgetScanner->getURData();
+            while (true) {
+                bool ok;
+                QString password = QInputDialog::getText(this, "Encrypt view-only details", "Enter one-time password to decrypt view-only details with", QLineEdit::Password, "", &ok);
+                if (!ok) {
+                    break;
+                }
+
+                QString data = WalletManager::decryptWithPassword(urData, password);
+                if (!data.startsWith("Secret view key")) {
+                    Utils::showError(this, "Unable to load view-only details", "Invalid password");
+                    continue;
+                }
+
+                QRegularExpression viewOnlyDetails(
+                        "Secret view key: (?<key>[0-9a-f]{64})\nAddress: (?<address>\\w+)\nRestore height: (?<restoreheight>\\d+)\nWallet name: (?<walletname>\\w+)\n",
+                        QRegularExpression::CaseInsensitiveOption | QRegularExpression::DotMatchesEverythingOption);
+                QRegularExpressionMatch match = viewOnlyDetails.match(data);
+
+                if (!match.hasMatch()) {
+                    Utils::showError(this, "Unable to load view-only details", "Unexpected data");
+                    continue;
+                }
+
+                m_viewOnlyDetails.address = match.captured("address");
+                m_viewOnlyDetails.key = match.captured("key").toLower();
+                m_viewOnlyDetails.restoreHeight = match.captured("restoreheight").toInt();
+                m_viewOnlyDetails.walletName = QString("%1_view_only").arg(match.captured("walletname"));
+
+                this->accept();
                 return;
             }
-
-            m_viewOnlyDetails.address = match.captured("address");
-            m_viewOnlyDetails.key = match.captured("key").toLower();
-            m_viewOnlyDetails.restoreHeight = match.captured("restoreheight").toInt();
-            m_viewOnlyDetails.walletName = QString("%1_view_only").arg(match.captured("walletname"));
-
-            this->accept();
         }
 
         if (ui->radio_clipboard->isChecked()) {
index 85aa965cd4593ca252cd34e65ad2042c91bdc2b0..faa3d97326a91e2aa42dba147e0a678da278ff5f 100644 (file)
@@ -24,7 +24,7 @@ class URDialog : public WindowModalDialog
     Q_OBJECT
 
 public:
-    explicit URDialog(QWidget *parent, const QString &data = "", bool scanOnly = false);
+    explicit URDialog(QWidget *parent, const std::string &data = "", bool scanOnly = false);
     ~URDialog() override;
 
     ViewOnlyDetails getViewOnlyDetails();
index 364f6c4e9b2f1f451644fe0a64a7d7945553f255..12438b203a1f04554747c0d1648338580a7cf4e3 100644 (file)
@@ -10,6 +10,7 @@
 
 #include "URDialog.h"
 #include "utils/Utils.h"
+#include "WalletManager.h"
 
 ViewOnlyDialog::ViewOnlyDialog(Wallet *wallet, QWidget *parent)
     : WindowModalDialog(parent)
@@ -25,7 +26,14 @@ ViewOnlyDialog::ViewOnlyDialog(Wallet *wallet, QWidget *parent)
     connect(ui->btn_Copy, &QPushButton::clicked, this, &ViewOnlyDialog::copyToClipboard);
     connect(ui->btn_Save, &QPushButton::clicked, this, &ViewOnlyDialog::onWriteViewOnlyWallet);
     connect(ui->btn_transmitOverUR, &QPushButton::clicked, [this] {
-        URDialog dialog{this, this->toString()};
+        bool ok;
+        QString password = QInputDialog::getText(this, "Encrypt view-only details", "Enter one-time password to encrypt view-only details with", QLineEdit::Password, "", &ok);
+        if (!ok) {
+            return;
+        }
+
+        std::string encrypted = WalletManager::encryptWithPassword(this->toString(), password);
+        URDialog dialog{this, encrypted};
         dialog.exec();
     });
 
index 211f27a6f3577bfd978d5b2e2365d99aa2e5f7bc..b3b56737722bc0fe0111042c94b705848319b643 100644 (file)
@@ -5,6 +5,7 @@
 #include "Wallet.h"
 
 #include "utils/ScopeGuard.h"
+#include "serialization/binary_utils.h"
 
 class WalletPassphraseListenerImpl : public Monero::WalletListener, public PassphraseReceiver
 {
@@ -328,3 +329,45 @@ void WalletManager::onPassphraseEntered(const QString &passphrase, bool enter_on
         m_passphraseReceiver->onPassphraseEntered(passphrase, enter_on_device, entry_abort);
     }
 }
+
+std::string WalletManager::encryptWithPassword(const QString &q_plain, const QString &q_password) {
+    std::string plain = q_plain.toStdString();
+    std::string password = q_password.toStdString();
+
+    crypto::chacha_key key;
+    crypto::generate_chacha_key(password.data(), password.size(), key, 1);
+
+    std::string cipher;
+    cipher.resize(plain.size());
+
+    // Repurposing this struct
+    tools::wallet2::keys_file_data s_data = {};
+    s_data.iv = crypto::rand<crypto::chacha_iv>();
+
+    crypto::chacha20(plain.data(), plain.size(), key, s_data.iv, &cipher[0]);
+    s_data.account_data = cipher;
+
+    std::string buf;
+    ::serialization::dump_binary(s_data, buf);
+
+    return buf;
+}
+
+QString WalletManager::decryptWithPassword(const std::string &cipher, const QString &q_password) {
+    std::string password = q_password.toStdString();
+
+    tools::wallet2::keys_file_data s_data;
+    bool r = ::serialization::parse_binary(cipher, s_data);
+    if (!r) {
+        return {};
+    }
+
+    crypto::chacha_key key;
+    crypto::generate_chacha_key(password.data(), password.size(), key, 1);
+
+    std::string plaintext;
+    plaintext.resize(s_data.account_data.size());
+    crypto::chacha20(s_data.account_data.data(), s_data.account_data.size(), key, s_data.iv, &plaintext[0]);
+
+    return QString::fromStdString(plaintext);
+}
\ No newline at end of file
index ea8216aeb95c2e09e113a02a653a910a1b2fd12f..2cb92a6a915c6e7d46a17a78d3c5b5af96a731d4 100644 (file)
@@ -117,6 +117,9 @@ public:
     void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort=false);
     virtual void onWalletPassphraseNeeded(bool on_device) override;
 
+    static std::string encryptWithPassword(const QString &plain, const QString &password);
+    static QString decryptWithPassword(const std::string &cipher, const QString &password);
+
 signals:
     void walletOpened(Wallet *wallet);
     void walletCreated(Wallet *wallet);