dice: add entropy from system

This commit is contained in:
tobtoht 2023-12-06 16:59:56 +01:00
parent 60eda75b55
commit 9056047b1b
No known key found for this signature in database
GPG key ID: E45B10DD027D2472
6 changed files with 63 additions and 16 deletions

View file

@ -6,6 +6,10 @@
#include <cmath> #include <cmath>
#include <algorithm> #include <algorithm>
#include <sodium/core.h>
#include <sodium/utils.h>
#include <sodium/randombytes.h>
#include "polyseed/polyseed.h"
#include <QPasswordDigestor> #include <QPasswordDigestor>
@ -17,6 +21,10 @@ SeedDiceDialog::SeedDiceDialog(QWidget *parent)
{ {
ui->setupUi(this); ui->setupUi(this);
if (sodium_init() == -1) {
throw std::runtime_error("sodium_init failed");
}
ui->frame_dice->hide(); ui->frame_dice->hide();
ui->frame_coinflip->hide(); ui->frame_coinflip->hide();
@ -77,13 +85,28 @@ SeedDiceDialog::SeedDiceDialog(QWidget *parent)
}); });
connect(ui->btn_createPolyseed, &QPushButton::clicked, [this]{ connect(ui->btn_createPolyseed, &QPushButton::clicked, [this]{
QByteArray salt = "POLYSEED"; qsizetype rolls_length = 0;
QByteArray data = m_rolls.join(" ").toUtf8(); 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) // 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); m_key = QPasswordDigestor::deriveKeyPbkdf2(QCryptographicHash::Sha256, data, salt, 2048, 19);
sodium_memzero(data.data(), data.size());
sodium_memzero(&random, POLYSEED_RANDBYTES);
this->accept(); this->accept();
}); });
@ -146,7 +169,7 @@ bool SeedDiceDialog::updateEntropy() {
double entropy = entropyPerRoll() * m_rolls.length(); double entropy = entropyPerRoll() * m_rolls.length();
ui->label_entropy->setText(QString("%1 / %2 bits").arg(QString::number(entropy, 'f', 2), QString::number(entropyNeeded))); 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() { void SeedDiceDialog::updateRolls() {
@ -172,12 +195,16 @@ void SeedDiceDialog::setEnableMethodSelection(bool enabled) {
ui->spin_sides->setEnabled(enabled); ui->spin_sides->setEnabled(enabled);
} }
bool SeedDiceDialog::finished() {
return updateEntropy();
}
const char* SeedDiceDialog::getSecret() { const char* SeedDiceDialog::getSecret() {
return m_key.data(); return m_key.data();
} }
const QString& SeedDiceDialog::getMnemonic() { void SeedDiceDialog::wipeSecret() {
return m_mnemonic; sodium_memzero(m_key.data(), m_key.length());
} }
SeedDiceDialog::~SeedDiceDialog() = default; SeedDiceDialog::~SeedDiceDialog() = default;

View file

@ -20,9 +20,9 @@ public:
explicit SeedDiceDialog(QWidget *parent); explicit SeedDiceDialog(QWidget *parent);
~SeedDiceDialog() override; ~SeedDiceDialog() override;
bool finished();
const char* getSecret(); const char* getSecret();
void wipeSecret();
const QString& getMnemonic();
private: private:
void addFlip(bool heads); void addFlip(bool heads);
@ -40,7 +40,6 @@ private:
QStringList m_rolls; QStringList m_rolls;
QByteArray m_key; QByteArray m_key;
int entropyNeeded = 152; // Polyseed requests 19 bytes of random data int entropyNeeded = 152; // Polyseed requests 19 bytes of random data
QString m_mnemonic;
}; };

View file

@ -13,8 +13,6 @@
#include <QString> #include <QString>
#define POLYSEED_RANDBYTES 19
namespace polyseed { namespace polyseed {
static std::locale locale; static std::locale locale;

View file

@ -1,11 +1,16 @@
// SPDX-License-Identifier: BSD-2-Clause // SPDX-License-Identifier: BSD-2-Clause
// SPDX-FileCopyrightText: Copyright 2021 tevador <tevador@gmail.com> // SPDX-FileCopyrightText: Copyright 2021 tevador <tevador@gmail.com>
#ifndef FEATHER_POLYSEED_H
#define FEATHER_POLYSEED_H
#include <polyseed.h> #include <polyseed.h>
#include <vector> #include <vector>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#define POLYSEED_RANDBYTES 19
namespace polyseed { namespace polyseed {
class data; class data;
@ -122,4 +127,6 @@ namespace polyseed {
polyseed_data* m_data; polyseed_data* m_data;
polyseed_coin m_coin; polyseed_coin m_coin;
}; };
} }
#endif // FEATHER_POLYSEED_H

View file

@ -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" ui->frame_invalidSeed->setInfo(icons()->icon("warning"), "Feather was unable to generate a valid seed.\n"
"This should never happen.\n" "This should never happen.\n"
"Please contact the developers immediately."); "Please contact the developers immediately.");
ui->frame_invalidSeed->hide();
QShortcut *shortcut = new QShortcut(QKeySequence("Ctrl+K"), this); QShortcut *shortcut = new QShortcut(QKeySequence("Ctrl+K"), this);
QObject::connect(shortcut, &QShortcut::activated, [&](){ QObject::connect(shortcut, &QShortcut::activated, [&](){
SeedDiceDialog dialog{this}; SeedDiceDialog dialog{this};
int r = dialog.exec(); int r = dialog.exec();
if (r == QDialog::Accepted) { 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()); 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() { void PageWalletSeed::initializePage() {
ui->frame_invalidSeed->hide();
ui->frame_seedDisplay->show();
this->generateSeed(); this->generateSeed();
this->setTitle(m_fields->modeText); this->setTitle(m_fields->modeText);
} }
@ -77,9 +87,7 @@ void PageWalletSeed::generateSeed(const char* secret) {
this->displaySeed(mnemonic); this->displaySeed(mnemonic);
if (!m_seed.errorString.isEmpty()) { if (!m_seed.errorString.isEmpty()) {
ui->frame_invalidSeed->show(); this->onError();
ui->frame_seedDisplay->hide();
m_seedError = true;
} }
} }
@ -122,6 +130,13 @@ void PageWalletSeed::onOptionsClicked() {
m_fields->showSetSeedPassphrasePage = checkbox.isChecked(); 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 { int PageWalletSeed::nextId() const {
if (m_fields->showSetSeedPassphrasePage) { if (m_fields->showSetSeedPassphrasePage) {
return WalletWizard::Page_SetSeedPassphrase; return WalletWizard::Page_SetSeedPassphrase;

View file

@ -32,6 +32,7 @@ private:
void seedRoulette(int count); void seedRoulette(int count);
void generateSeed(const char* secret = nullptr); void generateSeed(const char* secret = nullptr);
void onOptionsClicked(); void onOptionsClicked();
void onError();
signals: signals:
void createWallet(); void createWallet();