add separate button to backup tapsigner without changing pin

This commit is contained in:
Craig Raw 2023-06-22 10:41:59 +02:00
parent bcbb531414
commit f8fa7f4cf2
3 changed files with 66 additions and 28 deletions

View file

@ -22,20 +22,25 @@ public class CardPinDialog extends Dialog<CardPinDialog.CardPinChange> {
private final CheckBox backupFirst; private final CheckBox backupFirst;
private final ButtonType okButtonType; private final ButtonType okButtonType;
public CardPinDialog() { public CardPinDialog(boolean backupOnly) {
this.existingPin = new ViewPasswordField(); this.existingPin = new ViewPasswordField();
this.newPin = new ViewPasswordField(); this.newPin = new ViewPasswordField();
this.newPinConfirm = new ViewPasswordField(); this.newPinConfirm = new ViewPasswordField();
this.backupFirst = new CheckBox(); this.backupFirst = new CheckBox();
if(backupOnly) {
newPin.textProperty().bind(existingPin.textProperty());
newPinConfirm.textProperty().bind(existingPin.textProperty());
}
final DialogPane dialogPane = getDialogPane(); final DialogPane dialogPane = getDialogPane();
setTitle("Change Card PIN"); setTitle(backupOnly ? "Backup Card" : "Change Card PIN");
dialogPane.setHeaderText("Enter the current PIN, and then the new PIN twice. PIN must be between 6 and 32 digits."); dialogPane.setHeaderText(backupOnly ? "Enter the current card PIN." : "Enter the current PIN, and then the new PIN twice. PIN must be between 6 and 32 digits.");
dialogPane.getStylesheets().add(AppServices.class.getResource("general.css").toExternalForm()); dialogPane.getStylesheets().add(AppServices.class.getResource("general.css").toExternalForm());
AppServices.setStageIcon(dialogPane.getScene().getWindow()); AppServices.setStageIcon(dialogPane.getScene().getWindow());
dialogPane.getButtonTypes().addAll(ButtonType.CANCEL); dialogPane.getButtonTypes().addAll(ButtonType.CANCEL);
dialogPane.setPrefWidth(380); dialogPane.setPrefWidth(380);
dialogPane.setPrefHeight(260); dialogPane.setPrefHeight(backupOnly ? 135 : 260);
AppServices.moveToActiveWindowScreen(this); AppServices.moveToActiveWindowScreen(this);
Glyph lock = new Glyph("FontAwesome", FontAwesome.Glyph.LOCK); Glyph lock = new Glyph("FontAwesome", FontAwesome.Glyph.LOCK);
@ -63,7 +68,12 @@ public class CardPinDialog extends Dialog<CardPinDialog.CardPinChange> {
backupField.setText("Backup First:"); backupField.setText("Backup First:");
backupField.getInputs().add(backupFirst); backupField.getInputs().add(backupFirst);
if(backupOnly) {
fieldset.getChildren().addAll(currentField);
} else {
fieldset.getChildren().addAll(currentField, newField, confirmField, backupField); fieldset.getChildren().addAll(currentField, newField, confirmField, backupField);
}
form.getChildren().add(fieldset); form.getChildren().add(fieldset);
dialogPane.setContent(form); dialogPane.setContent(form);
@ -75,7 +85,7 @@ public class CardPinDialog extends Dialog<CardPinDialog.CardPinChange> {
validationSupport.registerValidator(newPinConfirm, (Control c, String newValue) -> ValidationResult.fromErrorIf(c, "PIN confirmation does not match", !newPinConfirm.getText().equals(newPin.getText()))); validationSupport.registerValidator(newPinConfirm, (Control c, String newValue) -> ValidationResult.fromErrorIf(c, "PIN confirmation does not match", !newPinConfirm.getText().equals(newPin.getText())));
}); });
okButtonType = new javafx.scene.control.ButtonType("Change", ButtonBar.ButtonData.OK_DONE); okButtonType = new javafx.scene.control.ButtonType(backupOnly ? "Backup" : "Change", ButtonBar.ButtonData.OK_DONE);
dialogPane.getButtonTypes().addAll(okButtonType); dialogPane.getButtonTypes().addAll(okButtonType);
Button okButton = (Button) dialogPane.lookupButton(okButtonType); Button okButton = (Button) dialogPane.lookupButton(okButtonType);
okButton.setPrefWidth(130); okButton.setPrefWidth(130);

View file

@ -24,6 +24,7 @@ import javafx.scene.control.*;
import javafx.scene.input.Clipboard; import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent; import javafx.scene.input.ClipboardContent;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import org.controlsfx.control.SegmentedButton;
import org.controlsfx.glyphfont.Glyph; import org.controlsfx.glyphfont.Glyph;
import org.controlsfx.validation.ValidationResult; import org.controlsfx.validation.ValidationResult;
import org.controlsfx.validation.ValidationSupport; import org.controlsfx.validation.ValidationSupport;
@ -68,7 +69,10 @@ public class KeystoreController extends WalletFormController implements Initiali
private Button viewKeyButton; private Button viewKeyButton;
@FXML @FXML
private Button changePinButton; private ToggleGroup cardServiceToggleGroup;
@FXML
private SegmentedButton cardServiceButtons;
@FXML @FXML
private Button importButton; private Button importButton;
@ -137,7 +141,7 @@ public class KeystoreController extends WalletFormController implements Initiali
exportButton.managedProperty().bind(exportButton.visibleProperty()); exportButton.managedProperty().bind(exportButton.visibleProperty());
viewSeedButton.managedProperty().bind(viewSeedButton.visibleProperty()); viewSeedButton.managedProperty().bind(viewSeedButton.visibleProperty());
viewKeyButton.managedProperty().bind(viewKeyButton.visibleProperty()); viewKeyButton.managedProperty().bind(viewKeyButton.visibleProperty());
changePinButton.managedProperty().bind(changePinButton.visibleProperty()); cardServiceButtons.managedProperty().bind(cardServiceButtons.visibleProperty());
scanXpubQR.managedProperty().bind(scanXpubQR.visibleProperty()); scanXpubQR.managedProperty().bind(scanXpubQR.visibleProperty());
displayXpubQR.managedProperty().bind(displayXpubQR.visibleProperty()); displayXpubQR.managedProperty().bind(displayXpubQR.visibleProperty());
displayXpubQR.visibleProperty().bind(scanXpubQR.visibleProperty().not()); displayXpubQR.visibleProperty().bind(scanXpubQR.visibleProperty().not());
@ -294,7 +298,7 @@ public class KeystoreController extends WalletFormController implements Initiali
exportButton.setVisible(showExport && getWalletForm().getWallet().getPolicyType() == PolicyType.MULTI); exportButton.setVisible(showExport && getWalletForm().getWallet().getPolicyType() == PolicyType.MULTI);
viewSeedButton.setVisible(keystore.getSource() == KeystoreSource.SW_SEED && keystore.hasSeed()); viewSeedButton.setVisible(keystore.getSource() == KeystoreSource.SW_SEED && keystore.hasSeed());
viewKeyButton.setVisible(keystore.getSource() == KeystoreSource.SW_SEED && keystore.hasMasterPrivateExtendedKey()); viewKeyButton.setVisible(keystore.getSource() == KeystoreSource.SW_SEED && keystore.hasMasterPrivateExtendedKey());
changePinButton.setVisible(keystore.getWalletModel().isCard()); cardServiceButtons.setVisible(keystore.getWalletModel().isCard());
importButton.setText(keystore.getSource() == KeystoreSource.SW_WATCH ? "Import..." : "Replace..."); importButton.setText(keystore.getSource() == KeystoreSource.SW_WATCH ? "Import..." : "Replace...");
importButton.setTooltip(new Tooltip(keystore.getSource() == KeystoreSource.SW_WATCH ? "Import a keystore from an external source" : "Replace this keystore with another source")); importButton.setTooltip(new Tooltip(keystore.getSource() == KeystoreSource.SW_WATCH ? "Import a keystore from an external source" : "Replace this keystore with another source"));
@ -432,12 +436,22 @@ public class KeystoreController extends WalletFormController implements Initiali
} }
public void changeCardPin(ActionEvent event) { public void changeCardPin(ActionEvent event) {
cardServiceToggleGroup.selectToggle(null);
changeCardPinOrBackup(false);
}
public void backupCard(ActionEvent event) {
cardServiceToggleGroup.selectToggle(null);
changeCardPinOrBackup(true);
}
public void changeCardPinOrBackup(boolean backupOnly) {
if(!isReaderAvailable()) { if(!isReaderAvailable()) {
AppServices.showErrorDialog("No card reader", "Connect a card reader to change the card PIN."); AppServices.showErrorDialog("No card reader", "Connect a card reader to change the card PIN.");
return; return;
} }
CardPinDialog cardPinDialog = new CardPinDialog(); CardPinDialog cardPinDialog = new CardPinDialog(backupOnly);
Optional<CardPinDialog.CardPinChange> optPinChange = cardPinDialog.showAndWait(); Optional<CardPinDialog.CardPinChange> optPinChange = cardPinDialog.showAndWait();
if(optPinChange.isPresent()) { if(optPinChange.isPresent()) {
String currentPin = optPinChange.get().currentPin(); String currentPin = optPinChange.get().currentPin();
@ -449,7 +463,7 @@ public class KeystoreController extends WalletFormController implements Initiali
if(authDelayService != null) { if(authDelayService != null) {
authDelayService.setOnSucceeded(event1 -> { authDelayService.setOnSucceeded(event1 -> {
try { try {
changeCardPin(newPin, backupFirst, cardApi); changeCardPin(newPin, backupFirst, backupOnly, cardApi);
} catch(CardException e) { } catch(CardException e) {
log.error("Error communicating with card", e); log.error("Error communicating with card", e);
AppServices.showErrorDialog("Error communicating with card", e.getMessage()); AppServices.showErrorDialog("Error communicating with card", e.getMessage());
@ -464,7 +478,7 @@ public class KeystoreController extends WalletFormController implements Initiali
AppServices.moveToActiveWindowScreen(serviceProgressDialog); AppServices.moveToActiveWindowScreen(serviceProgressDialog);
authDelayService.start(); authDelayService.start();
} else { } else {
changeCardPin(newPin, backupFirst, cardApi); changeCardPin(newPin, backupFirst, backupOnly, cardApi);
} }
} catch(CardException e) { } catch(CardException e) {
log.error("Error communicating with card", e); log.error("Error communicating with card", e);
@ -473,23 +487,25 @@ public class KeystoreController extends WalletFormController implements Initiali
} }
} }
private void changeCardPin(String newPin, boolean backupFirst, CardApi cardApi) throws CardException { private void changeCardPin(String newPin, boolean backupFirst, boolean backupOnly, CardApi cardApi) throws CardException {
boolean requiresBackup = cardApi.requiresBackup(); boolean requiresBackup = cardApi.requiresBackup();
if(backupFirst || requiresBackup) { if(backupOnly || backupFirst || requiresBackup) {
Service<String> backupService = cardApi.getBackupService(); Service<String> backupService = cardApi.getBackupService();
backupService.setOnSucceeded(event -> { backupService.setOnSucceeded(event -> {
String backup = backupService.getValue(); String backup = backupService.getValue();
String filename = fingerprint.getText() + ".aes"; String filename = fingerprint.getText() + ".aes";
TextAreaDialog backupDialog = new TextAreaDialog(backup, false, filename, Base64.getDecoder().decode(backup)); TextAreaDialog backupDialog = new TextAreaDialog(backup, false, filename, Base64.getDecoder().decode(backup));
backupDialog.setTitle("Backup Private Key"); backupDialog.setTitle("Backup Private Key");
backupDialog.getDialogPane().setHeaderText((requiresBackup ? "Please backup first by saving" : "Save") + " the following text in a safe place. It contains an encrypted copy of the card's private key, and can be decrypted using the backup key written on the back of the card."); backupDialog.getDialogPane().setHeaderText((requiresBackup && !backupOnly ? "Please backup first by saving" : "Save") + " the following text in a safe place. It contains an encrypted copy of the card's private key, and can be decrypted using the backup key written on the back of the card.");
backupDialog.showAndWait(); backupDialog.showAndWait();
if(!backupOnly) {
try { try {
changePin(newPin, cardApi); changePin(newPin, cardApi);
} catch(Exception e) { } catch(Exception e) {
log.error("Error communicating with card", e); log.error("Error communicating with card", e);
AppServices.showErrorDialog("Error communicating with card", e.getMessage()); AppServices.showErrorDialog("Error communicating with card", e.getMessage());
} }
}
}); });
backupService.setOnFailed(event -> { backupService.setOnFailed(event -> {
Throwable e = event.getSource().getException(); Throwable e = event.getSource().getException();

View file

@ -41,14 +41,26 @@
<Tooltip text="View master private key"/> <Tooltip text="View master private key"/>
</tooltip> </tooltip>
</Button> </Button>
<Button fx:id="changePinButton" text="Change Pin..." graphicTextGap="5" onAction="#changeCardPin"> <SegmentedButton fx:id="cardServiceButtons">
<toggleGroup>
<ToggleGroup fx:id="cardServiceToggleGroup" />
</toggleGroup>
<buttons>
<ToggleButton toggleGroup="$cardServiceToggleGroup" text="Change Pin" graphicTextGap="5" onAction="#changeCardPin">
<graphic> <graphic>
<Glyph fontFamily="Font Awesome 5 Free Solid" fontSize="12" icon="WIFI" /> <Glyph fontFamily="Font Awesome 5 Free Solid" fontSize="12" icon="WIFI" />
</graphic> </graphic>
<tooltip> <tooltip>
<Tooltip text="Change the PIN of current card"/> <Tooltip text="Change the PIN of the current card"/>
</tooltip> </tooltip>
</Button> </ToggleButton>
<ToggleButton toggleGroup="$cardServiceToggleGroup" text="Backup" onAction="#backupCard">
<tooltip>
<Tooltip text="Backup the current card"/>
</tooltip>
</ToggleButton>
</buttons>
</SegmentedButton>
<Button fx:id="exportButton" text="Export..." onAction="#export"> <Button fx:id="exportButton" text="Export..." onAction="#export">
<tooltip> <tooltip>
<Tooltip text="Export this keystore as a signer for a multisig wallet"/> <Tooltip text="Export this keystore as a signer for a multisig wallet"/>