mirror of
https://github.com/feather-wallet/feather.git
synced 2025-01-05 18:39:30 +00:00
wizard: add diceware support
This commit is contained in:
parent
c23869cc32
commit
6225e0e389
9 changed files with 583 additions and 12 deletions
183
src/dialog/SeedDiceDialog.cpp
Normal file
183
src/dialog/SeedDiceDialog.cpp
Normal file
|
@ -0,0 +1,183 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
|
||||||
|
|
||||||
|
#include "SeedDiceDialog.h"
|
||||||
|
#include "ui_SeedDiceDialog.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include <QPasswordDigestor>
|
||||||
|
|
||||||
|
#include "utils/Seed.h"
|
||||||
|
|
||||||
|
SeedDiceDialog::SeedDiceDialog(QWidget *parent)
|
||||||
|
: WindowModalDialog(parent)
|
||||||
|
, ui(new Ui::SeedDiceDialog)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
ui->frame_dice->hide();
|
||||||
|
ui->frame_coinflip->hide();
|
||||||
|
|
||||||
|
connect(ui->radio_dice, &QRadioButton::toggled, [this](bool toggled){
|
||||||
|
ui->frame_dice->setVisible(toggled);
|
||||||
|
this->updateRollsLeft();
|
||||||
|
ui->label_rollsLeft2->setText("Rolls left:");
|
||||||
|
ui->label_rolls->setText("Rolls:");
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui->radio_coinflip, &QRadioButton::toggled, [this](bool toggled){
|
||||||
|
ui->frame_coinflip->setVisible(toggled);
|
||||||
|
this->updateRollsLeft();
|
||||||
|
ui->label_rollsLeft2->setText("Flips left:");
|
||||||
|
ui->label_rolls->setText("Flips:");
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui->spin_sides, &QSpinBox::valueChanged, [this](int value){
|
||||||
|
if (!ui->radio_dice->isChecked()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->updateRollsLeft();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui->line_roll, &QLineEdit::textChanged, this, &SeedDiceDialog::validateRollEntry);
|
||||||
|
|
||||||
|
connect(ui->btn_next, &QPushButton::clicked, [this]{
|
||||||
|
this->setEnableMethodSelection(false);
|
||||||
|
|
||||||
|
if (!this->validateRollEntry()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList rolls = ui->line_roll->text().simplified().split(" ");
|
||||||
|
for (const auto &roll : rolls) {
|
||||||
|
this->addRoll(roll);
|
||||||
|
}
|
||||||
|
|
||||||
|
ui->line_roll->clear();
|
||||||
|
ui->line_roll->setFocus();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui->btn_heads, &QPushButton::clicked, [this]{
|
||||||
|
this->setEnableMethodSelection(false);
|
||||||
|
this->addFlip(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui->btn_tails, &QPushButton::clicked, [this]{
|
||||||
|
this->setEnableMethodSelection(false);
|
||||||
|
this->addFlip(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui->btn_reset, &QPushButton::clicked, [this]{
|
||||||
|
m_rolls.clear();
|
||||||
|
this->update();
|
||||||
|
this->setEnableMethodSelection(true);
|
||||||
|
ui->btn_createPolyseed->setEnabled(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui->btn_createPolyseed, &QPushButton::clicked, [this]{
|
||||||
|
QByteArray salt = "POLYSEED";
|
||||||
|
QByteArray data = m_rolls.join(" ").toUtf8();
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
m_key = QPasswordDigestor::deriveKeyPbkdf2(QCryptographicHash::Sha256, data, salt, 2048, 19);
|
||||||
|
|
||||||
|
this->accept();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(ui->btn_cancel, &QPushButton::clicked, [this]{
|
||||||
|
this->reject();
|
||||||
|
});
|
||||||
|
|
||||||
|
ui->radio_dice->setChecked(true);
|
||||||
|
|
||||||
|
this->update();
|
||||||
|
this->adjustSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeedDiceDialog::addFlip(bool heads) {
|
||||||
|
m_rolls << (heads ? "H" : "T");
|
||||||
|
this->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeedDiceDialog::addRoll(const QString &roll) {
|
||||||
|
if (roll.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_rolls << roll;
|
||||||
|
this->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SeedDiceDialog::validateRollEntry() {
|
||||||
|
ui->line_roll->setStyleSheet("");
|
||||||
|
|
||||||
|
QString errStyle = "QLineEdit{border: 1px solid red;}";
|
||||||
|
QStringList rolls = ui->line_roll->text().simplified().split(" ");
|
||||||
|
|
||||||
|
for (const auto &rollstr : rolls) {
|
||||||
|
if (rollstr.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok;
|
||||||
|
int roll = rollstr.toInt(&ok);
|
||||||
|
if (!ok || roll < 1 || roll > ui->spin_sides->value()) {
|
||||||
|
ui->line_roll->setStyleSheet(errStyle);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeedDiceDialog::update() {
|
||||||
|
this->updateRollsLeft();
|
||||||
|
this->updateRolls();
|
||||||
|
|
||||||
|
if (this->updateEntropy()) {
|
||||||
|
ui->btn_createPolyseed->setEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeedDiceDialog::updateRolls() {
|
||||||
|
ui->rolls->setPlainText(m_rolls.join(" "));
|
||||||
|
}
|
||||||
|
|
||||||
|
double SeedDiceDialog::entropyPerRoll() {
|
||||||
|
if (ui->radio_dice->isChecked()) {
|
||||||
|
return log(ui->spin_sides->value()) / log(2);
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeedDiceDialog::updateRollsLeft() {
|
||||||
|
int rollsLeft = std::max((int)(ceil((entropyNeeded - (this->entropyPerRoll() * m_rolls.length())) / this->entropyPerRoll())), 0);
|
||||||
|
ui->label_rollsLeft->setText(QString::number(rollsLeft));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SeedDiceDialog::setEnableMethodSelection(bool enabled) {
|
||||||
|
ui->radio_dice->setEnabled(enabled);
|
||||||
|
ui->radio_coinflip->setEnabled(enabled);
|
||||||
|
ui->spin_sides->setEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* SeedDiceDialog::getSecret() {
|
||||||
|
return m_key.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString& SeedDiceDialog::getMnemonic() {
|
||||||
|
return m_mnemonic;
|
||||||
|
}
|
||||||
|
|
||||||
|
SeedDiceDialog::~SeedDiceDialog() = default;
|
47
src/dialog/SeedDiceDialog.h
Normal file
47
src/dialog/SeedDiceDialog.h
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
// SPDX-FileCopyrightText: 2020-2023 The Monero Project
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
#include "components.h"
|
||||||
|
|
||||||
|
#ifndef FEATHER_SEEDDICEDIALOG_H
|
||||||
|
#define FEATHER_SEEDDICEDIALOG_H
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class SeedDiceDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SeedDiceDialog : public WindowModalDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SeedDiceDialog(QWidget *parent);
|
||||||
|
~SeedDiceDialog() override;
|
||||||
|
|
||||||
|
const char* getSecret();
|
||||||
|
|
||||||
|
const QString& getMnemonic();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void addFlip(bool heads);
|
||||||
|
void addRoll(const QString &roll);
|
||||||
|
double entropyPerRoll();
|
||||||
|
bool validateRollEntry();
|
||||||
|
|
||||||
|
void update();
|
||||||
|
bool updateEntropy();
|
||||||
|
void updateRolls();
|
||||||
|
void updateRollsLeft();
|
||||||
|
void setEnableMethodSelection(bool enabled);
|
||||||
|
|
||||||
|
QScopedPointer<Ui::SeedDiceDialog> ui;
|
||||||
|
QStringList m_rolls;
|
||||||
|
QByteArray m_key;
|
||||||
|
int entropyNeeded = 152; // Polyseed requests 19 bytes of random data
|
||||||
|
QString m_mnemonic;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //FEATHER_SEEDDICEDIALOG_H
|
292
src/dialog/SeedDiceDialog.ui
Normal file
292
src/dialog/SeedDiceDialog.ui
Normal file
|
@ -0,0 +1,292 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>SeedDiceDialog</class>
|
||||||
|
<widget class="QDialog" name="SeedDiceDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>881</width>
|
||||||
|
<height>547</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Diceware</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QGroupBox" name="groupBox">
|
||||||
|
<property name="title">
|
||||||
|
<string>Select method:</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_dice">
|
||||||
|
<property name="text">
|
||||||
|
<string>Dice rolls</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QSpinBox" name="spin_sides">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="minimum">
|
||||||
|
<number>2</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>100</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>-sided die</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QRadioButton" name="radio_coinflip">
|
||||||
|
<property name="text">
|
||||||
|
<string>Coinflips</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Line" name="line">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<property name="fieldGrowthPolicy">
|
||||||
|
<enum>QFormLayout::ExpandingFieldsGrow</enum>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Entropy: </string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QLabel" name="label_entropy">
|
||||||
|
<property name="text">
|
||||||
|
<string>0 bits</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_rollsLeft2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Rolls left:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QLabel" name="label_rollsLeft">
|
||||||
|
<property name="text">
|
||||||
|
<string>0 rolls</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_rolls">
|
||||||
|
<property name="text">
|
||||||
|
<string>Rolls:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QPlainTextEdit" name="rolls">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>500</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeType">
|
||||||
|
<enum>QSizePolicy::Minimum</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>10</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame_dice">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Enter roll:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="line_roll">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_next">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Next roll</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_6">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>(Use a space between each roll to enter multiple rolls in one go)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="frame_coinflip">
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="label_7">
|
||||||
|
<property name="text">
|
||||||
|
<string>Add flip:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_heads">
|
||||||
|
<property name="text">
|
||||||
|
<string>Heads</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_tails">
|
||||||
|
<property name="text">
|
||||||
|
<string>Tails</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_reset">
|
||||||
|
<property name="text">
|
||||||
|
<string>Reset</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_cancel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Cancel</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="horizontalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>40</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="btn_createPolyseed">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Create polyseed</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
|
@ -13,6 +13,8 @@
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
|
||||||
|
#define POLYSEED_RANDBYTES 19
|
||||||
|
|
||||||
namespace polyseed {
|
namespace polyseed {
|
||||||
|
|
||||||
static std::locale locale;
|
static std::locale locale;
|
||||||
|
@ -39,6 +41,18 @@ namespace polyseed {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char seed[POLYSEED_RANDBYTES]{};
|
||||||
|
|
||||||
|
static void randombytes(void* const buf, const size_t size) {
|
||||||
|
assert(size <= POLYSEED_RANDBYTES);
|
||||||
|
if (std::all_of(seed, seed + size, [](char c) { return c == '\0'; })) {
|
||||||
|
randombytes_buf(buf, size);
|
||||||
|
} else {
|
||||||
|
memcpy(buf, seed, size);
|
||||||
|
sodium_memzero(seed, POLYSEED_RANDBYTES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct dependency {
|
struct dependency {
|
||||||
dependency();
|
dependency();
|
||||||
std::vector<language> languages;
|
std::vector<language> languages;
|
||||||
|
@ -55,8 +69,10 @@ namespace polyseed {
|
||||||
gen.locale_cache_enabled(true);
|
gen.locale_cache_enabled(true);
|
||||||
locale = gen("");
|
locale = gen("");
|
||||||
|
|
||||||
|
sodium_memzero(seed, POLYSEED_RANDBYTES);
|
||||||
|
|
||||||
polyseed_dependency pd;
|
polyseed_dependency pd;
|
||||||
pd.randbytes = &randombytes_buf;
|
pd.randbytes = &randombytes;
|
||||||
pd.pbkdf2_sha256 = &crypto_pbkdf2_sha256;
|
pd.pbkdf2_sha256 = &crypto_pbkdf2_sha256;
|
||||||
pd.memzero = &sodium_memzero;
|
pd.memzero = &sodium_memzero;
|
||||||
pd.u8_nfc = &utf8_nfc;
|
pd.u8_nfc = &utf8_nfc;
|
||||||
|
@ -121,6 +137,15 @@ namespace polyseed {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void data::create_from_secret(feature_type features, const char* secret) {
|
||||||
|
check_init();
|
||||||
|
memcpy(seed, secret, POLYSEED_RANDBYTES);
|
||||||
|
auto status = polyseed_create(features, &m_data);
|
||||||
|
if (status != POLYSEED_OK) {
|
||||||
|
throw get_error(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void data::load(polyseed_storage storage) {
|
void data::load(polyseed_storage storage) {
|
||||||
check_init();
|
check_init();
|
||||||
auto status = polyseed_load(storage, &m_data);
|
auto status = polyseed_load(storage, &m_data);
|
||||||
|
|
|
@ -61,6 +61,7 @@ namespace polyseed {
|
||||||
}
|
}
|
||||||
|
|
||||||
void create(feature_type features);
|
void create(feature_type features);
|
||||||
|
void create_from_secret(feature_type features, const char* secret);
|
||||||
|
|
||||||
void load(polyseed_storage storage);
|
void load(polyseed_storage storage);
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,10 @@
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include "Seed.h"
|
#include "Seed.h"
|
||||||
|
|
||||||
Seed::Seed(Type type, NetworkType::Type networkType, QString language)
|
Seed::Seed(Type type, NetworkType::Type networkType, QString language, const char* secret)
|
||||||
: type(type), networkType(networkType), language(std::move(language))
|
: type(type)
|
||||||
|
, networkType(networkType)
|
||||||
|
, language(std::move(language))
|
||||||
{
|
{
|
||||||
// We only support the creation of Polyseeds
|
// We only support the creation of Polyseeds
|
||||||
if (this->type != Type::POLYSEED) {
|
if (this->type != Type::POLYSEED) {
|
||||||
|
@ -23,7 +25,12 @@ Seed::Seed(Type type, NetworkType::Type networkType, QString language)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
polyseed::data seed(POLYSEED_MONERO);
|
polyseed::data seed(POLYSEED_MONERO);
|
||||||
seed.create(0);
|
|
||||||
|
if (secret) {
|
||||||
|
seed.create_from_secret(0, secret);
|
||||||
|
} else {
|
||||||
|
seed.create(0);
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t key[32];
|
uint8_t key[32];
|
||||||
seed.keygen(&key, sizeof(key));
|
seed.keygen(&key, sizeof(key));
|
||||||
|
@ -129,7 +136,6 @@ void Seed::setRestoreHeight(int height) {
|
||||||
void Seed::setRestoreHeight() {
|
void Seed::setRestoreHeight() {
|
||||||
// Ignore the embedded restore date, new wallets should sync from the current block height.
|
// Ignore the embedded restore date, new wallets should sync from the current block height.
|
||||||
this->restoreHeight = appData()->restoreHeights[networkType]->dateToHeight(this->time);
|
this->restoreHeight = appData()->restoreHeights[networkType]->dateToHeight(this->time);
|
||||||
int a = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Seed::Seed() = default;
|
Seed::Seed() = default;
|
|
@ -41,7 +41,7 @@ struct Seed {
|
||||||
bool encrypted = false;
|
bool encrypted = false;
|
||||||
|
|
||||||
explicit Seed();
|
explicit Seed();
|
||||||
explicit Seed(Type type, NetworkType::Type networkType = NetworkType::MAINNET, QString language = "English");
|
explicit Seed(Type type, NetworkType::Type networkType = NetworkType::MAINNET, QString language = "English", const char* secret = nullptr);
|
||||||
explicit Seed(Type type, QStringList mnemonic, NetworkType::Type networkType = NetworkType::MAINNET);
|
explicit Seed(Type type, QStringList mnemonic, NetworkType::Type networkType = NetworkType::MAINNET);
|
||||||
void setRestoreHeight(int height);
|
void setRestoreHeight(int height);
|
||||||
|
|
||||||
|
|
|
@ -8,10 +8,13 @@
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
#include <QDialogButtonBox>
|
#include <QDialogButtonBox>
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QShortcut>
|
||||||
|
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
#include "Seed.h"
|
#include "Seed.h"
|
||||||
#include "Icons.h"
|
#include "Icons.h"
|
||||||
|
#include "dialog/SeedDiceDialog.h"
|
||||||
|
|
||||||
PageWalletSeed::PageWalletSeed(WizardFields *fields, QWidget *parent)
|
PageWalletSeed::PageWalletSeed(WizardFields *fields, QWidget *parent)
|
||||||
: QWizardPage(parent)
|
: QWizardPage(parent)
|
||||||
|
@ -29,6 +32,15 @@ PageWalletSeed::PageWalletSeed(WizardFields *fields, QWidget *parent)
|
||||||
"Please contact the developers immediately.");
|
"Please contact the developers immediately.");
|
||||||
ui->frame_invalidSeed->hide();
|
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) {
|
||||||
|
this->generateSeed(dialog.getSecret());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
connect(ui->btnRoulette, &QPushButton::clicked, [=]{
|
connect(ui->btnRoulette, &QPushButton::clicked, [=]{
|
||||||
this->seedRoulette(0);
|
this->seedRoulette(0);
|
||||||
});
|
});
|
||||||
|
@ -55,10 +67,10 @@ void PageWalletSeed::seedRoulette(int count) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void PageWalletSeed::generateSeed() {
|
void PageWalletSeed::generateSeed(const char* secret) {
|
||||||
QString mnemonic;
|
QString mnemonic;
|
||||||
|
|
||||||
m_seed = Seed(Seed::Type::POLYSEED, constants::networkType);
|
m_seed = Seed(Seed::Type::POLYSEED, constants::networkType, "English", secret);
|
||||||
mnemonic = m_seed.mnemonic.join(" ");
|
mnemonic = m_seed.mnemonic.join(" ");
|
||||||
m_restoreHeight = m_seed.restoreHeight;
|
m_restoreHeight = m_seed.restoreHeight;
|
||||||
|
|
||||||
|
@ -99,6 +111,7 @@ void PageWalletSeed::onOptionsClicked() {
|
||||||
QCheckBox checkbox("Extend this seed with a passphrase");
|
QCheckBox checkbox("Extend this seed with a passphrase");
|
||||||
checkbox.setChecked(m_fields->showSetSeedPassphrasePage);
|
checkbox.setChecked(m_fields->showSetSeedPassphrasePage);
|
||||||
layout.addWidget(&checkbox);
|
layout.addWidget(&checkbox);
|
||||||
|
|
||||||
QDialogButtonBox buttons(QDialogButtonBox::Ok);
|
QDialogButtonBox buttons(QDialogButtonBox::Ok);
|
||||||
layout.addWidget(&buttons);
|
layout.addWidget(&buttons);
|
||||||
dialog.setLayout(&layout);
|
dialog.setLayout(&layout);
|
||||||
|
@ -117,12 +130,16 @@ int PageWalletSeed::nextId() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PageWalletSeed::validatePage() {
|
bool PageWalletSeed::validatePage() {
|
||||||
if (m_seed.mnemonic.isEmpty()) return false;
|
if (m_seed.mnemonic.isEmpty()) {
|
||||||
if (!m_restoreHeight) return false;
|
return false;
|
||||||
|
}
|
||||||
|
if (!m_restoreHeight) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
QMessageBox seedWarning(this);
|
QMessageBox seedWarning(this);
|
||||||
seedWarning.setWindowTitle("Warning!");
|
seedWarning.setWindowTitle("Warning!");
|
||||||
seedWarning.setInformativeText("• Never disclose your seed\n"
|
seedWarning.setText("• Never disclose your seed\n"
|
||||||
"• Never type it on a website\n"
|
"• Never type it on a website\n"
|
||||||
"• Store it safely (offline)\n"
|
"• Store it safely (offline)\n"
|
||||||
"• Do not lose your seed!");
|
"• Do not lose your seed!");
|
||||||
|
|
|
@ -30,7 +30,7 @@ public slots:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void seedRoulette(int count);
|
void seedRoulette(int count);
|
||||||
void generateSeed();
|
void generateSeed(const char* secret = nullptr);
|
||||||
void onOptionsClicked();
|
void onOptionsClicked();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
Loading…
Reference in a new issue