]> Nutra Git (v1) - gamesguru/feather.git/commitdiff
dice: add entropy from system
authortobtoht <tob@featherwallet.org>
Wed, 6 Dec 2023 15:59:56 +0000 (16:59 +0100)
committertobtoht <tob@featherwallet.org>
Wed, 6 Dec 2023 19:29:02 +0000 (20:29 +0100)
src/dialog/SeedDiceDialog.cpp
src/dialog/SeedDiceDialog.h
src/polyseed/polyseed.cpp
src/polyseed/polyseed.h
src/wizard/PageWalletSeed.cpp
src/wizard/PageWalletSeed.h

index f0bb4ca258b5a8dae9e97e277380d7ab1f9db3c2..9877c966984897b81f206e084a90c240d71a109f 100644 (file)
@@ -6,6 +6,10 @@
 
 #include <cmath>
 #include <algorithm>
+#include <sodium/core.h>
+#include <sodium/utils.h>
+#include <sodium/randombytes.h>
+#include "polyseed/polyseed.h"
 
 #include <QPasswordDigestor>
 
@@ -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
index 228e03b57071a370c06247cf1c68037a72d0b79d..5b10a615371d8c61c38108dfd4263c34433178b9 100644 (file)
@@ -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;
 };
 
 
index b55206ccf72a872ec410ad9ee0e164cad7b2de52..e0e4ad468ab592a42db15299f9c6d206db388457 100644 (file)
@@ -13,8 +13,6 @@
 
 #include <QString>
 
-#define POLYSEED_RANDBYTES 19
-
 namespace polyseed {
 
     static std::locale locale;
index 441b9e37fb333462ee109464d9e747b6a00aa8f3..47f46dcf80ce601f79ebcf6666a514dff73d552d 100644 (file)
@@ -1,11 +1,16 @@
 // SPDX-License-Identifier: BSD-2-Clause
 // SPDX-FileCopyrightText: Copyright 2021 tevador <tevador@gmail.com>
 
+#ifndef FEATHER_POLYSEED_H
+#define FEATHER_POLYSEED_H
+
 #include <polyseed.h>
 #include <vector>
 #include <stdexcept>
 #include <string>
 
+#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
index 33a62b167fc219dc9f779076ae4e0e64e076811a..7dc2e9d340710fad6fbd6ded7457f4eb595b8ef4 100644 (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"
                                                              "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;
index 63c89bde787c39e6fd8fc4037b4985fa6eae7a31..60087619cd26ca414f4d5de347fa3d4dac18487d 100644 (file)
@@ -32,6 +32,7 @@ private:
     void seedRoulette(int count);
     void generateSeed(const char* secret = nullptr);
     void onOptionsClicked();
+    void onError();
 
 signals:
     void createWallet();