#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);
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;
}
}
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()) {
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();
#include "URDialog.h"
#include "utils/Utils.h"
+#include "WalletManager.h"
ViewOnlyDialog::ViewOnlyDialog(Wallet *wallet, QWidget *parent)
: WindowModalDialog(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();
});
#include "Wallet.h"
#include "utils/ScopeGuard.h"
+#include "serialization/binary_utils.h"
class WalletPassphraseListenerImpl : public Monero::WalletListener, public PassphraseReceiver
{
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
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);