Skip to content

Commit

Permalink
airgap: encrypt view-only details UR
Browse files Browse the repository at this point in the history
  • Loading branch information
tobtoht committed Dec 7, 2023
1 parent 5b10ed3 commit d8e585a
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 21 deletions.
54 changes: 35 additions & 19 deletions src/dialog/URDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
}

Expand Down Expand Up @@ -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()) {
Expand Down
2 changes: 1 addition & 1 deletion src/dialog/URDialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
10 changes: 9 additions & 1 deletion src/dialog/ViewOnlyDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "URDialog.h"
#include "utils/Utils.h"
#include "WalletManager.h"

ViewOnlyDialog::ViewOnlyDialog(Wallet *wallet, QWidget *parent)
: WindowModalDialog(parent)
Expand All @@ -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();
});

Expand Down
43 changes: 43 additions & 0 deletions src/libwalletqt/WalletManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "Wallet.h"

#include "utils/ScopeGuard.h"
#include "serialization/binary_utils.h"

class WalletPassphraseListenerImpl : public Monero::WalletListener, public PassphraseReceiver
{
Expand Down Expand Up @@ -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);
}
3 changes: 3 additions & 0 deletions src/libwalletqt/WalletManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ class WalletManager : public QObject, public PassphrasePrompter
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);
Expand Down

0 comments on commit d8e585a

Please sign in to comment.