mirror of
https://github.com/feather-wallet/feather.git
synced 2024-11-16 17:27:38 +00:00
dice: add entropy from system
This commit is contained in:
parent
60eda75b55
commit
9056047b1b
6 changed files with 63 additions and 16 deletions
|
@ -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;
|
|
@ -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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,6 @@
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
#define POLYSEED_RANDBYTES 19
|
|
||||||
|
|
||||||
namespace polyseed {
|
namespace polyseed {
|
||||||
|
|
||||||
static std::locale locale;
|
static std::locale locale;
|
||||||
|
|
|
@ -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
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in a new issue