#include <cmath>
#include <algorithm>
+#include <sodium/core.h>
+#include <sodium/utils.h>
+#include <sodium/randombytes.h>
+#include "polyseed/polyseed.h"
#include <QPasswordDigestor>
{
ui->setupUi(this);
+ if (sodium_init() == -1) {
+ throw std::runtime_error("sodium_init failed");
+ }
+
ui->frame_dice->hide();
ui->frame_coinflip->hide();
});
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();
});
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() {
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
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");
}
});
}
void PageWalletSeed::initializePage() {
+ ui->frame_invalidSeed->hide();
+ ui->frame_seedDisplay->show();
+
this->generateSeed();
this->setTitle(m_fields->modeText);
}
this->displaySeed(mnemonic);
if (!m_seed.errorString.isEmpty()) {
- ui->frame_invalidSeed->show();
- ui->frame_seedDisplay->hide();
- m_seedError = true;
+ this->onError();
}
}
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;