diff --git a/src/dialog/URDialog.cpp b/src/dialog/URDialog.cpp index 370f968..8d624b2 100644 --- a/src/dialog/URDialog.cpp +++ b/src/dialog/URDialog.cpp @@ -5,16 +5,18 @@ #include "ui_URDialog.h" #include +#include #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: (?[0-9a-f]{64})\nAddress: (?
\\w+)\nRestore height: (?\\d+)\nWallet name: (?\\w+)\n", - QRegularExpression::CaseInsensitiveOption | QRegularExpression::DotMatchesEverythingOption); - QString data = QString::fromStdString(ui->widgetScanner->getURData()); - QRegularExpressionMatch match = viewOnlyDetails.match(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; + } - if (!match.hasMatch()) { - Utils::showError(this, "Unable to load view-only details", "Unexpected data"); + 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: (?[0-9a-f]{64})\nAddress: (?
\\w+)\nRestore height: (?\\d+)\nWallet name: (?\\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()) { diff --git a/src/dialog/URDialog.h b/src/dialog/URDialog.h index 85aa965..faa3d97 100644 --- a/src/dialog/URDialog.h +++ b/src/dialog/URDialog.h @@ -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(); diff --git a/src/dialog/ViewOnlyDialog.cpp b/src/dialog/ViewOnlyDialog.cpp index 364f6c4..12438b2 100644 --- a/src/dialog/ViewOnlyDialog.cpp +++ b/src/dialog/ViewOnlyDialog.cpp @@ -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(); }); diff --git a/src/libwalletqt/WalletManager.cpp b/src/libwalletqt/WalletManager.cpp index 211f27a..b3b5673 100644 --- a/src/libwalletqt/WalletManager.cpp +++ b/src/libwalletqt/WalletManager.cpp @@ -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::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 diff --git a/src/libwalletqt/WalletManager.h b/src/libwalletqt/WalletManager.h index ea8216a..2cb92a6 100644 --- a/src/libwalletqt/WalletManager.h +++ b/src/libwalletqt/WalletManager.h @@ -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);