mirror of
https://github.com/feather-wallet/feather.git
synced 2024-12-22 19:49:28 +00:00
airgap: encrypt view-only details UR
This commit is contained in:
parent
5b10ed3ca0
commit
d8e585a902
5 changed files with 90 additions and 20 deletions
|
@ -5,16 +5,18 @@
|
||||||
#include "ui_URDialog.h"
|
#include "ui_URDialog.h"
|
||||||
|
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
#include <QInputDialog>
|
||||||
|
|
||||||
#include "utils/Utils.h"
|
#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)
|
: WindowModalDialog(parent)
|
||||||
, ui(new Ui::URDialog)
|
, ui(new Ui::URDialog)
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
|
|
||||||
if (!data.isEmpty()) {
|
if (!data.empty()) {
|
||||||
ui->btn_loadFile->setVisible(false);
|
ui->btn_loadFile->setVisible(false);
|
||||||
ui->btn_loadClipboard->setVisible(false);
|
ui->btn_loadClipboard->setVisible(false);
|
||||||
ui->tabWidget->setTabVisible(1, 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;
|
int availableHeight = currentScreen->availableGeometry().height() - 200;
|
||||||
this->resize(availableHeight, availableHeight);
|
this->resize(availableHeight, availableHeight);
|
||||||
|
|
||||||
std::string d = data.toStdString();
|
ui->widgetUR->setData("xmr-viewonly", data);
|
||||||
ui->widgetUR->setData("xmr-viewonly", d);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,23 +76,38 @@ URDialog::URDialog(QWidget *parent, const QString &data, bool scanOnly)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ui->widgetScanner->getURType() == "xmr-viewonly") {
|
if (ui->widgetScanner->getURType() == "xmr-viewonly") {
|
||||||
QRegularExpression viewOnlyDetails(
|
std::string urData = ui->widgetScanner->getURData();
|
||||||
"Secret view key: (?<key>[0-9a-f]{64})\nAddress: (?<address>\\w+)\nRestore height: (?<restoreheight>\\d+)\nWallet name: (?<walletname>\\w+)\n",
|
while (true) {
|
||||||
QRegularExpression::CaseInsensitiveOption | QRegularExpression::DotMatchesEverythingOption);
|
bool ok;
|
||||||
QString data = QString::fromStdString(ui->widgetScanner->getURData());
|
QString password = QInputDialog::getText(this, "Encrypt view-only details", "Enter one-time password to decrypt view-only details with", QLineEdit::Password, "", &ok);
|
||||||
QRegularExpressionMatch match = viewOnlyDetails.match(data);
|
if (!ok) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (!match.hasMatch()) {
|
QString data = WalletManager::decryptWithPassword(urData, password);
|
||||||
Utils::showError(this, "Unable to load view-only details", "Unexpected data");
|
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;
|
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()) {
|
if (ui->radio_clipboard->isChecked()) {
|
||||||
|
|
|
@ -24,7 +24,7 @@ class URDialog : public WindowModalDialog
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit URDialog(QWidget *parent, const QString &data = "", bool scanOnly = false);
|
explicit URDialog(QWidget *parent, const std::string &data = "", bool scanOnly = false);
|
||||||
~URDialog() override;
|
~URDialog() override;
|
||||||
|
|
||||||
ViewOnlyDetails getViewOnlyDetails();
|
ViewOnlyDetails getViewOnlyDetails();
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include "URDialog.h"
|
#include "URDialog.h"
|
||||||
#include "utils/Utils.h"
|
#include "utils/Utils.h"
|
||||||
|
#include "WalletManager.h"
|
||||||
|
|
||||||
ViewOnlyDialog::ViewOnlyDialog(Wallet *wallet, QWidget *parent)
|
ViewOnlyDialog::ViewOnlyDialog(Wallet *wallet, QWidget *parent)
|
||||||
: WindowModalDialog(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_Copy, &QPushButton::clicked, this, &ViewOnlyDialog::copyToClipboard);
|
||||||
connect(ui->btn_Save, &QPushButton::clicked, this, &ViewOnlyDialog::onWriteViewOnlyWallet);
|
connect(ui->btn_Save, &QPushButton::clicked, this, &ViewOnlyDialog::onWriteViewOnlyWallet);
|
||||||
connect(ui->btn_transmitOverUR, &QPushButton::clicked, [this] {
|
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();
|
dialog.exec();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "Wallet.h"
|
#include "Wallet.h"
|
||||||
|
|
||||||
#include "utils/ScopeGuard.h"
|
#include "utils/ScopeGuard.h"
|
||||||
|
#include "serialization/binary_utils.h"
|
||||||
|
|
||||||
class WalletPassphraseListenerImpl : public Monero::WalletListener, public PassphraseReceiver
|
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);
|
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);
|
||||||
|
}
|
|
@ -117,6 +117,9 @@ public:
|
||||||
void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort=false);
|
void onPassphraseEntered(const QString &passphrase, bool enter_on_device, bool entry_abort=false);
|
||||||
virtual void onWalletPassphraseNeeded(bool on_device) override;
|
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:
|
signals:
|
||||||
void walletOpened(Wallet *wallet);
|
void walletOpened(Wallet *wallet);
|
||||||
void walletCreated(Wallet *wallet);
|
void walletCreated(Wallet *wallet);
|
||||||
|
|
Loading…
Reference in a new issue