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

View file

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