generate new bip39 word list

This commit is contained in:
Craig Raw 2020-05-10 16:26:15 +02:00
parent 6872e069a9
commit 82a113afc3
4 changed files with 97 additions and 37 deletions

2
drongo

@ -1 +1 @@
Subproject commit be0c4d1176da41671c2629e65f812fd28fab202b Subproject commit 0f008b8985253c4fd0025c136d0a23ac3082537f

View file

@ -40,10 +40,10 @@ public class MainApp extends Application {
wallet.setPolicyType(PolicyType.SINGLE); wallet.setPolicyType(PolicyType.SINGLE);
wallet.setScriptType(ScriptType.P2WPKH); wallet.setScriptType(ScriptType.P2WPKH);
// KeystoreImportDialog dlg = new KeystoreImportDialog(wallet); KeystoreImportDialog dlg = new KeystoreImportDialog(wallet);
// dlg.showAndWait(); dlg.showAndWait();
stage.show(); // stage.show();
} }
public static void main(String[] args) { public static void main(String[] args) {

View file

@ -2,17 +2,15 @@ package com.sparrowwallet.sparrow.control;
import com.sparrowwallet.drongo.KeyDerivation; import com.sparrowwallet.drongo.KeyDerivation;
import com.sparrowwallet.drongo.crypto.ChildNumber; import com.sparrowwallet.drongo.crypto.ChildNumber;
import com.sparrowwallet.drongo.wallet.Bip39Calculator; import com.sparrowwallet.drongo.wallet.Bip39MnemonicCode;
import com.sparrowwallet.drongo.wallet.DeterministicSeed;
import com.sparrowwallet.drongo.wallet.Keystore; import com.sparrowwallet.drongo.wallet.Keystore;
import com.sparrowwallet.drongo.wallet.Wallet; import com.sparrowwallet.drongo.wallet.Wallet;
import com.sparrowwallet.sparrow.EventManager; import com.sparrowwallet.sparrow.EventManager;
import com.sparrowwallet.sparrow.event.KeystoreImportEvent; import com.sparrowwallet.sparrow.event.KeystoreImportEvent;
import com.sparrowwallet.sparrow.io.*; import com.sparrowwallet.sparrow.io.*;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleListProperty; import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener; import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
@ -24,7 +22,6 @@ import javafx.scene.control.*;
import javafx.scene.layout.*; import javafx.scene.layout.*;
import javafx.util.Callback; import javafx.util.Callback;
import org.controlsfx.control.textfield.AutoCompletionBinding; import org.controlsfx.control.textfield.AutoCompletionBinding;
import org.controlsfx.control.textfield.CustomPasswordField;
import org.controlsfx.control.textfield.CustomTextField; import org.controlsfx.control.textfield.CustomTextField;
import org.controlsfx.control.textfield.TextFields; import org.controlsfx.control.textfield.TextFields;
import org.controlsfx.validation.ValidationResult; import org.controlsfx.validation.ValidationResult;
@ -32,6 +29,7 @@ import org.controlsfx.validation.ValidationSupport;
import org.controlsfx.validation.Validator; import org.controlsfx.validation.Validator;
import org.controlsfx.validation.decoration.StyleClassValidationDecoration; import org.controlsfx.validation.decoration.StyleClassValidationDecoration;
import java.security.SecureRandom;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -42,6 +40,11 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
private SplitMenuButton enterMnemonicButton; private SplitMenuButton enterMnemonicButton;
private SplitMenuButton importButton; private SplitMenuButton importButton;
private TilePane wordsPane;
private Button verifyButton;
private Button confirmButton;
private List<String> generatedMnemonicCode;
private SimpleListProperty<String> wordEntriesProperty; private SimpleListProperty<String> wordEntriesProperty;
private final SimpleStringProperty passphraseProperty = new SimpleStringProperty(); private final SimpleStringProperty passphraseProperty = new SimpleStringProperty();
@ -107,6 +110,7 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
} }
private void enterMnemonic(int numWords) { private void enterMnemonic(int numWords) {
generatedMnemonicCode = null;
setDescription("Enter mnemonic word list"); setDescription("Enter mnemonic word list");
showHideLink.setVisible(false); showHideLink.setVisible(false);
setContent(getMnemonicWordsEntry(numWords)); setContent(getMnemonicWordsEntry(numWords));
@ -117,11 +121,11 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
VBox vBox = new VBox(); VBox vBox = new VBox();
vBox.setSpacing(10); vBox.setSpacing(10);
TilePane tilePane = new TilePane(); wordsPane = new TilePane();
tilePane.setPrefRows(numWords/3); wordsPane.setPrefRows(numWords/3);
tilePane.setHgap(10); wordsPane.setHgap(10);
tilePane.setVgap(10); wordsPane.setVgap(10);
tilePane.setOrientation(Orientation.VERTICAL); wordsPane.setOrientation(Orientation.VERTICAL);
List<String> words = new ArrayList<>(); List<String> words = new ArrayList<>();
for(int i = 0; i < numWords; i++) { for(int i = 0; i < numWords; i++) {
@ -132,44 +136,95 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
wordEntriesProperty = new SimpleListProperty<>(wordEntryList); wordEntriesProperty = new SimpleListProperty<>(wordEntryList);
for(int i = 0; i < numWords; i++) { for(int i = 0; i < numWords; i++) {
WordEntry wordEntry = new WordEntry(i, wordEntryList); WordEntry wordEntry = new WordEntry(i, wordEntryList);
tilePane.getChildren().add(wordEntry); wordsPane.getChildren().add(wordEntry);
} }
vBox.getChildren().add(tilePane); vBox.getChildren().add(wordsPane);
AnchorPane anchorPane = new AnchorPane();
anchorPane.setPadding(new Insets(0, 32, 0, 10));
PassphraseEntry passphraseEntry = new PassphraseEntry(); PassphraseEntry passphraseEntry = new PassphraseEntry();
AnchorPane.setLeftAnchor(passphraseEntry, 0.0); passphraseEntry.setPadding(new Insets(0, 32, 0, 10));
vBox.getChildren().add(passphraseEntry);
Button okButton = new Button("Ok"); AnchorPane buttonPane = new AnchorPane();
okButton.setPrefWidth(70); buttonPane.setPadding(new Insets(0, 32, 0, 10));
okButton.setDisable(true);
okButton.setOnAction(event -> { Button generateButton = new Button("Generate New");
generateButton.setOnAction(event -> {
generateNew();
});
buttonPane.getChildren().add(generateButton);
AnchorPane.setLeftAnchor(generateButton, 0.0);
confirmButton = new Button("Confirm Backup");
confirmButton.setOnAction(event -> {
confirmBackup();
});
confirmButton.managedProperty().bind(confirmButton.visibleProperty());
confirmButton.setVisible(false);
buttonPane.getChildren().add(confirmButton);
AnchorPane.setRightAnchor(confirmButton, 0.0);
verifyButton = new Button("Verify");
verifyButton.setDisable(true);
verifyButton.setOnAction(event -> {
prepareImport(); prepareImport();
}); });
verifyButton.managedProperty().bind(verifyButton.visibleProperty());
wordEntriesProperty.addListener((ListChangeListener<String>) c -> { wordEntriesProperty.addListener((ListChangeListener<String>) c -> {
for(String word : wordEntryList) { for(String word : wordEntryList) {
if(!WordEntry.isValid(word)) { if(!WordEntry.isValid(word)) {
okButton.setDisable(true); verifyButton.setDisable(true);
return; return;
} }
} }
okButton.setDisable(false); verifyButton.setDisable(false);
}); });
buttonPane.getChildren().add(verifyButton);
AnchorPane.setRightAnchor(verifyButton, 0.0);
AnchorPane.setRightAnchor(okButton, 0.0); vBox.getChildren().add(buttonPane);
anchorPane.getChildren().addAll(passphraseEntry, okButton);
vBox.getChildren().add(anchorPane);
return vBox; return vBox;
} }
private void generateNew() {
setDescription("Write down word list to confirm backup");
showHideLink.setVisible(false);
int mnemonicSeedLength = wordEntriesProperty.get().size() * 11;
int entropyLength = mnemonicSeedLength - (mnemonicSeedLength/33);
DeterministicSeed deterministicSeed = new DeterministicSeed(new SecureRandom(), entropyLength, "");
generatedMnemonicCode = deterministicSeed.getMnemonicCode();
if(generatedMnemonicCode.size() != wordsPane.getChildren().size()) {
throw new IllegalStateException("Generated mnemonic words list not same size as displayed words list");
}
for (int i = 0; i < wordsPane.getChildren().size(); i++) {
WordEntry wordEntry = (WordEntry)wordsPane.getChildren().get(i);
wordEntry.getEditor().setText(generatedMnemonicCode.get(i));
wordEntry.getEditor().setEditable(false);
}
verifyButton.setVisible(false);
confirmButton.setVisible(true);
}
private void confirmBackup() {
setDescription("Confirm backup by re-entering words");
showHideLink.setVisible(false);
setContent(getMnemonicWordsEntry(wordEntriesProperty.get().size()));
setExpanded(true);
}
private void prepareImport() { private void prepareImport() {
if(generatedMnemonicCode != null && !generatedMnemonicCode.equals(wordEntriesProperty.get())) {
setError("Import Error", "Confirmation words did not match generated mnemonic");
return;
}
if(importKeystore(wallet.getScriptType().getDefaultDerivation(), true)) { if(importKeystore(wallet.getScriptType().getDefaultDerivation(), true)) {
setExpanded(false); setExpanded(false);
enterMnemonicButton.setVisible(false); enterMnemonicButton.setVisible(false);
@ -203,6 +258,7 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
private static class WordEntry extends HBox { private static class WordEntry extends HBox {
private static List<String> wordList; private static List<String> wordList;
private final TextField wordField;
public WordEntry(int wordNumber, ObservableList<String> wordEntryList) { public WordEntry(int wordNumber, ObservableList<String> wordEntryList) {
super(); super();
@ -212,11 +268,10 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
Label label = new Label((wordNumber+1) + "."); Label label = new Label((wordNumber+1) + ".");
label.setPrefWidth(20); label.setPrefWidth(20);
label.setAlignment(Pos.CENTER_RIGHT); label.setAlignment(Pos.CENTER_RIGHT);
TextField wordField = new TextField(); wordField = new TextField();
wordField.setMaxWidth(100); wordField.setMaxWidth(100);
Bip39Calculator bip39Calculator = new Bip39Calculator(); wordList = Bip39MnemonicCode.INSTANCE.getWordList();
wordList = bip39Calculator.getWordList();
TextFields.bindAutoCompletion(wordField, new WordlistSuggestionProvider(wordList)); TextFields.bindAutoCompletion(wordField, new WordlistSuggestionProvider(wordList));
ValidationSupport validationSupport = new ValidationSupport(); ValidationSupport validationSupport = new ValidationSupport();
@ -233,6 +288,10 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
this.getChildren().addAll(label, wordField); this.getChildren().addAll(label, wordField);
} }
public TextField getEditor() {
return wordField;
}
public static boolean isValid(String word) { public static boolean isValid(String word) {
return wordList.contains(word); return wordList.contains(word);
} }
@ -264,11 +323,12 @@ public class MnemonicKeystoreImportPane extends KeystoreImportPane {
public PassphraseEntry() { public PassphraseEntry() {
super(); super();
setAlignment(Pos.CENTER_RIGHT); setAlignment(Pos.CENTER_LEFT);
setSpacing(10); setSpacing(10);
Label passphraseLabel = new Label("Passphrase:"); Label passphraseLabel = new Label("Passphrase:");
CustomTextField passphraseField = (CustomTextField) TextFields.createClearableTextField(); CustomTextField passphraseField = (CustomTextField) TextFields.createClearableTextField();
passphraseProperty.bind(passphraseField.textProperty()); passphraseProperty.bind(passphraseField.textProperty());
passphraseField.setPromptText("Leave blank for none");
getChildren().addAll(passphraseLabel, passphraseField); getChildren().addAll(passphraseLabel, passphraseField);
} }

View file

@ -1,7 +1,7 @@
package com.sparrowwallet.sparrow.io; package com.sparrowwallet.sparrow.io;
import com.sparrowwallet.drongo.crypto.ChildNumber; import com.sparrowwallet.drongo.crypto.ChildNumber;
import com.sparrowwallet.drongo.wallet.Bip39Calculator; import com.sparrowwallet.drongo.wallet.Bip39MnemonicCode;
import com.sparrowwallet.drongo.wallet.Keystore; import com.sparrowwallet.drongo.wallet.Keystore;
import com.sparrowwallet.drongo.wallet.WalletModel; import com.sparrowwallet.drongo.wallet.WalletModel;
@ -26,8 +26,8 @@ public class Bip39 implements KeystoreMnemonicImport {
@Override @Override
public Keystore getKeystore(List<ChildNumber> derivation, List<String> mnemonicWords, String passphrase) throws ImportException { public Keystore getKeystore(List<ChildNumber> derivation, List<String> mnemonicWords, String passphrase) throws ImportException {
try { try {
Bip39Calculator bip39Calculator = new Bip39Calculator(); Bip39MnemonicCode.INSTANCE.check(mnemonicWords);
byte[] seed = bip39Calculator.getSeed(mnemonicWords, passphrase); byte[] seed = Bip39MnemonicCode.toSeed(mnemonicWords, passphrase);
return Keystore.fromSeed(seed, derivation); return Keystore.fromSeed(seed, derivation);
} catch (Exception e) { } catch (Exception e) {
throw new ImportException(e); throw new ImportException(e);