support airgapped keystore import of a tapsigner with a custom derivation path

This commit is contained in:
Craig Raw 2023-05-31 15:50:06 +02:00
parent 98d9a6882b
commit 719cfaa906

View file

@ -26,6 +26,10 @@ import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import org.controlsfx.control.textfield.CustomPasswordField; import org.controlsfx.control.textfield.CustomPasswordField;
import org.controlsfx.glyphfont.Glyph; import org.controlsfx.glyphfont.Glyph;
import org.controlsfx.validation.ValidationResult;
import org.controlsfx.validation.ValidationSupport;
import org.controlsfx.validation.Validator;
import org.controlsfx.validation.decoration.StyleClassValidationDecoration;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -39,7 +43,7 @@ public class CardImportPane extends TitledDescriptionPane {
private static final Logger log = LoggerFactory.getLogger(CardImportPane.class); private static final Logger log = LoggerFactory.getLogger(CardImportPane.class);
private final KeystoreCardImport importer; private final KeystoreCardImport importer;
private final List<ChildNumber> derivation; private List<ChildNumber> derivation;
protected Button importButton; protected Button importButton;
private final SimpleStringProperty pin = new SimpleStringProperty(""); private final SimpleStringProperty pin = new SimpleStringProperty("");
@ -72,7 +76,7 @@ public class CardImportPane extends TitledDescriptionPane {
if(pin.get().length() < 6) { if(pin.get().length() < 6) {
setDescription(pin.get().isEmpty() ? "Enter PIN code" : "PIN code too short"); setDescription(pin.get().isEmpty() ? "Enter PIN code" : "PIN code too short");
setContent(getPinEntry()); setContent(getPinAndDerivationEntry());
showHideLink.setVisible(false); showHideLink.setVisible(false);
setExpanded(true); setExpanded(true);
importButton.setDisable(false); importButton.setDisable(false);
@ -106,7 +110,7 @@ public class CardImportPane extends TitledDescriptionPane {
Throwable rootCause = Throwables.getRootCause(event.getSource().getException()); Throwable rootCause = Throwables.getRootCause(event.getSource().getException());
if(rootCause instanceof CardAuthorizationException) { if(rootCause instanceof CardAuthorizationException) {
setError(rootCause.getMessage(), null); setError(rootCause.getMessage(), null);
setContent(getPinEntry()); setContent(getPinAndDerivationEntry());
} else { } else {
log.error("Error importing keystore from card", event.getSource().getException()); log.error("Error importing keystore from card", event.getSource().getException());
setError("Import Error", rootCause.getMessage()); setError("Import Error", rootCause.getMessage());
@ -169,6 +173,13 @@ public class CardImportPane extends TitledDescriptionPane {
return contentBox; return contentBox;
} }
private Node getPinAndDerivationEntry() {
VBox vBox = new VBox();
vBox.getChildren().add(getPinEntry());
vBox.getChildren().add(getDerivationEntry());
return vBox;
}
private Node getPinEntry() { private Node getPinEntry() {
VBox vBox = new VBox(); VBox vBox = new VBox();
@ -183,7 +194,7 @@ public class CardImportPane extends TitledDescriptionPane {
contentBox.setAlignment(Pos.TOP_RIGHT); contentBox.setAlignment(Pos.TOP_RIGHT);
contentBox.setSpacing(20); contentBox.setSpacing(20);
contentBox.getChildren().add(pinField); contentBox.getChildren().add(pinField);
contentBox.setPadding(new Insets(10, 30, 10, 30)); contentBox.setPadding(new Insets(10, 30, 0, 30));
contentBox.setPrefHeight(50); contentBox.setPrefHeight(50);
vBox.getChildren().add(contentBox); vBox.getChildren().add(contentBox);
@ -191,6 +202,51 @@ public class CardImportPane extends TitledDescriptionPane {
return vBox; return vBox;
} }
private Node getDerivationEntry() {
VBox vBox = new VBox();
CheckBox checkBox = new CheckBox("Use Custom Derivation");
Label customLabel = new Label("Derivation:");
TextField customDerivation = new TextField(KeyDerivation.writePath(derivation));
ValidationSupport validationSupport = new ValidationSupport();
validationSupport.setValidationDecorator(new StyleClassValidationDecoration());
validationSupport.registerValidator(customDerivation, Validator.combine(
Validator.createEmptyValidator("Derivation is required"),
(Control c, String newValue) -> ValidationResult.fromErrorIf( c, "Invalid derivation", !KeyDerivation.isValid(newValue))
));
customDerivation.textProperty().addListener((observable, oldValue, newValue) -> {
if(newValue.isEmpty() || !KeyDerivation.isValid(newValue)) {
importButton.setDisable(true);
} else {
importButton.setDisable(false);
derivation = KeyDerivation.parsePath(newValue);
}
});
checkBox.managedProperty().bind(checkBox.visibleProperty());
customLabel.managedProperty().bind(customLabel.visibleProperty());
customDerivation.managedProperty().bind(customDerivation.visibleProperty());
customLabel.visibleProperty().bind(checkBox.visibleProperty().not());
customDerivation.visibleProperty().bind(checkBox.visibleProperty().not());
checkBox.selectedProperty().addListener((observable, oldValue, newValue) -> {
checkBox.setVisible(false);
});
HBox derivationBox = new HBox();
derivationBox.setAlignment(Pos.CENTER_LEFT);
derivationBox.setSpacing(20);
derivationBox.getChildren().addAll(checkBox, customLabel, customDerivation);
derivationBox.setPadding(new Insets(10, 30, 10, 30));
derivationBox.setPrefHeight(50);
vBox.getChildren().addAll(derivationBox);
return vBox;
}
public static class CardInitializationService extends Service<Void> { public static class CardInitializationService extends Service<Void> {
private final KeystoreCardImport cardImport; private final KeystoreCardImport cardImport;
private final String pin; private final String pin;