diff --git a/src/dialog/SeedDiceDialog.cpp b/src/dialog/SeedDiceDialog.cpp index f0bb4ca..9877c96 100644 --- a/src/dialog/SeedDiceDialog.cpp +++ b/src/dialog/SeedDiceDialog.cpp @@ -6,6 +6,10 @@ #include #include +#include +#include +#include +#include "polyseed/polyseed.h" #include @@ -17,6 +21,10 @@ SeedDiceDialog::SeedDiceDialog(QWidget *parent) { ui->setupUi(this); + if (sodium_init() == -1) { + throw std::runtime_error("sodium_init failed"); + } + ui->frame_dice->hide(); ui->frame_coinflip->hide(); @@ -77,13 +85,28 @@ SeedDiceDialog::SeedDiceDialog(QWidget *parent) }); connect(ui->btn_createPolyseed, &QPushButton::clicked, [this]{ - QByteArray salt = "POLYSEED"; - QByteArray data = m_rolls.join(" ").toUtf8(); + qsizetype rolls_length = 0; + for (const auto& roll : m_rolls) { + rolls_length += roll.length() + 1; + } + + QByteArray data; + data.reserve(rolls_length + POLYSEED_RANDBYTES); + data = m_rolls.join(" ").toUtf8(); + + // Get 19 bytes of entropy from the system + char random[POLYSEED_RANDBYTES] = {}; + randombytes_buf(&random, POLYSEED_RANDBYTES); + + data.append(random, POLYSEED_RANDBYTES); - // We already have enough entropy assuming unbiased throws, but a few extra rounds can't hurt // Polyseed requests 19 bytes of random data and discards two bits (for a total of 150 bits) + QByteArray salt = "POLYSEED"; m_key = QPasswordDigestor::deriveKeyPbkdf2(QCryptographicHash::Sha256, data, salt, 2048, 19); + sodium_memzero(data.data(), data.size()); + sodium_memzero(&random, POLYSEED_RANDBYTES); + this->accept(); }); @@ -146,7 +169,7 @@ bool SeedDiceDialog::updateEntropy() { double entropy = entropyPerRoll() * m_rolls.length(); ui->label_entropy->setText(QString("%1 / %2 bits").arg(QString::number(entropy, 'f', 2), QString::number(entropyNeeded))); - return entropy > entropyNeeded; + return entropy >= entropyNeeded; } void SeedDiceDialog::updateRolls() { @@ -172,12 +195,16 @@ void SeedDiceDialog::setEnableMethodSelection(bool enabled) { ui->spin_sides->setEnabled(enabled); } +bool SeedDiceDialog::finished() { + return updateEntropy(); +} + const char* SeedDiceDialog::getSecret() { return m_key.data(); } -const QString& SeedDiceDialog::getMnemonic() { - return m_mnemonic; +void SeedDiceDialog::wipeSecret() { + sodium_memzero(m_key.data(), m_key.length()); } SeedDiceDialog::~SeedDiceDialog() = default; \ No newline at end of file diff --git a/src/dialog/SeedDiceDialog.h b/src/dialog/SeedDiceDialog.h index 228e03b..5b10a61 100644 --- a/src/dialog/SeedDiceDialog.h +++ b/src/dialog/SeedDiceDialog.h @@ -20,9 +20,9 @@ public: explicit SeedDiceDialog(QWidget *parent); ~SeedDiceDialog() override; + bool finished(); const char* getSecret(); - - const QString& getMnemonic(); + void wipeSecret(); private: void addFlip(bool heads); @@ -40,7 +40,6 @@ private: QStringList m_rolls; QByteArray m_key; int entropyNeeded = 152; // Polyseed requests 19 bytes of random data - QString m_mnemonic; }; diff --git a/src/polyseed/polyseed.cpp b/src/polyseed/polyseed.cpp index b55206c..e0e4ad4 100644 --- a/src/polyseed/polyseed.cpp +++ b/src/polyseed/polyseed.cpp @@ -13,8 +13,6 @@ #include -#define POLYSEED_RANDBYTES 19 - namespace polyseed { static std::locale locale; diff --git a/src/polyseed/polyseed.h b/src/polyseed/polyseed.h index 441b9e3..47f46dc 100644 --- a/src/polyseed/polyseed.h +++ b/src/polyseed/polyseed.h @@ -1,11 +1,16 @@ // SPDX-License-Identifier: BSD-2-Clause // SPDX-FileCopyrightText: Copyright 2021 tevador +#ifndef FEATHER_POLYSEED_H +#define FEATHER_POLYSEED_H + #include #include #include #include +#define POLYSEED_RANDBYTES 19 + namespace polyseed { class data; @@ -122,4 +127,6 @@ namespace polyseed { polyseed_data* m_data; polyseed_coin m_coin; }; -} \ No newline at end of file +} + +#endif // FEATHER_POLYSEED_H \ No newline at end of file diff --git a/src/wizard/PageWalletSeed.cpp b/src/wizard/PageWalletSeed.cpp index 33a62b1..7dc2e9d 100644 --- a/src/wizard/PageWalletSeed.cpp +++ b/src/wizard/PageWalletSeed.cpp @@ -30,14 +30,21 @@ PageWalletSeed::PageWalletSeed(WizardFields *fields, QWidget *parent) ui->frame_invalidSeed->setInfo(icons()->icon("warning"), "Feather was unable to generate a valid seed.\n" "This should never happen.\n" "Please contact the developers immediately."); - ui->frame_invalidSeed->hide(); QShortcut *shortcut = new QShortcut(QKeySequence("Ctrl+K"), this); QObject::connect(shortcut, &QShortcut::activated, [&](){ SeedDiceDialog dialog{this}; int r = dialog.exec(); if (r == QDialog::Accepted) { + if (!dialog.finished()) { + this->onError(); + Utils::showError(this, "Unable to create polyseed using additional entropy", "Not enough entropy was collected", {"You have found a bug. Please contact the developers."}); + return; + } + this->generateSeed(dialog.getSecret()); + dialog.wipeSecret(); + Utils::showInfo(this, "Polyseed created successfully using additional entropy"); } }); @@ -51,6 +58,9 @@ PageWalletSeed::PageWalletSeed(WizardFields *fields, QWidget *parent) } void PageWalletSeed::initializePage() { + ui->frame_invalidSeed->hide(); + ui->frame_seedDisplay->show(); + this->generateSeed(); this->setTitle(m_fields->modeText); } @@ -77,9 +87,7 @@ void PageWalletSeed::generateSeed(const char* secret) { this->displaySeed(mnemonic); if (!m_seed.errorString.isEmpty()) { - ui->frame_invalidSeed->show(); - ui->frame_seedDisplay->hide(); - m_seedError = true; + this->onError(); } } @@ -122,6 +130,13 @@ void PageWalletSeed::onOptionsClicked() { m_fields->showSetSeedPassphrasePage = checkbox.isChecked(); } +void PageWalletSeed::onError() { + ui->frame_invalidSeed->show(); + ui->frame_seedDisplay->hide(); + m_seedError = true; + this->completeChanged(); +} + int PageWalletSeed::nextId() const { if (m_fields->showSetSeedPassphrasePage) { return WalletWizard::Page_SetSeedPassphrase; diff --git a/src/wizard/PageWalletSeed.h b/src/wizard/PageWalletSeed.h index 63c89bd..6008761 100644 --- a/src/wizard/PageWalletSeed.h +++ b/src/wizard/PageWalletSeed.h @@ -32,6 +32,7 @@ private: void seedRoulette(int count); void generateSeed(const char* secret = nullptr); void onOptionsClicked(); + void onError(); signals: void createWallet();