mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2024-12-25 05:06:45 +00:00
refactor cardapi to generic service
This commit is contained in:
parent
057a9efb1f
commit
300545b289
17 changed files with 163 additions and 79 deletions
|
@ -14,7 +14,6 @@ import com.sparrowwallet.drongo.policy.PolicyType;
|
||||||
import com.sparrowwallet.drongo.wallet.*;
|
import com.sparrowwallet.drongo.wallet.*;
|
||||||
import com.sparrowwallet.sparrow.control.WalletPasswordDialog;
|
import com.sparrowwallet.sparrow.control.WalletPasswordDialog;
|
||||||
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
||||||
import com.sparrowwallet.sparrow.io.ckcard.CardApi;
|
|
||||||
import com.sparrowwallet.sparrow.net.Auth47;
|
import com.sparrowwallet.sparrow.net.Auth47;
|
||||||
import com.sparrowwallet.drongo.protocol.BlockHeader;
|
import com.sparrowwallet.drongo.protocol.BlockHeader;
|
||||||
import com.sparrowwallet.drongo.protocol.ScriptType;
|
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||||
|
|
|
@ -11,9 +11,10 @@ import com.sparrowwallet.sparrow.EventManager;
|
||||||
import com.sparrowwallet.sparrow.event.KeystoreImportEvent;
|
import com.sparrowwallet.sparrow.event.KeystoreImportEvent;
|
||||||
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
||||||
import com.sparrowwallet.sparrow.io.KeystoreCardImport;
|
import com.sparrowwallet.sparrow.io.KeystoreCardImport;
|
||||||
import com.sparrowwallet.sparrow.io.ckcard.CardAuthorizationException;
|
import com.sparrowwallet.sparrow.io.CardAuthorizationException;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
import javafx.concurrent.Service;
|
import javafx.concurrent.Service;
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
|
@ -81,10 +82,11 @@ public class CardImportPane extends TitledDescriptionPane {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CardImportService cardImportService = new CardImportService(importer, pin.get(), derivation);
|
StringProperty messageProperty = new SimpleStringProperty();
|
||||||
cardImportService.messageProperty().addListener((observable, oldValue, newValue) -> {
|
messageProperty.addListener((observable, oldValue, newValue) -> {
|
||||||
setDescription(newValue);
|
Platform.runLater(() -> setDescription(newValue));
|
||||||
});
|
});
|
||||||
|
CardImportService cardImportService = new CardImportService(importer, pin.get(), derivation, messageProperty);
|
||||||
cardImportService.setOnSucceeded(event -> {
|
cardImportService.setOnSucceeded(event -> {
|
||||||
EventManager.get().post(new KeystoreImportEvent(cardImportService.getValue()));
|
EventManager.get().post(new KeystoreImportEvent(cardImportService.getValue()));
|
||||||
});
|
});
|
||||||
|
@ -193,11 +195,13 @@ public class CardImportPane extends TitledDescriptionPane {
|
||||||
private final KeystoreCardImport cardImport;
|
private final KeystoreCardImport cardImport;
|
||||||
private final String pin;
|
private final String pin;
|
||||||
private final List<ChildNumber> derivation;
|
private final List<ChildNumber> derivation;
|
||||||
|
private final StringProperty messageProperty;
|
||||||
|
|
||||||
public CardImportService(KeystoreCardImport cardImport, String pin, List<ChildNumber> derivation) {
|
public CardImportService(KeystoreCardImport cardImport, String pin, List<ChildNumber> derivation, StringProperty messageProperty) {
|
||||||
this.cardImport = cardImport;
|
this.cardImport = cardImport;
|
||||||
this.pin = pin;
|
this.pin = pin;
|
||||||
this.derivation = derivation;
|
this.derivation = derivation;
|
||||||
|
this.messageProperty = messageProperty;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -205,10 +209,7 @@ public class CardImportPane extends TitledDescriptionPane {
|
||||||
return new Task<>() {
|
return new Task<>() {
|
||||||
@Override
|
@Override
|
||||||
protected Keystore call() throws Exception {
|
protected Keystore call() throws Exception {
|
||||||
cardImport.messageProperty().addListener((observable, oldValue, newValue) -> {
|
return cardImport.getKeystore(pin, derivation, messageProperty);
|
||||||
updateMessage(newValue);
|
|
||||||
});
|
|
||||||
return cardImport.getKeystore(pin, derivation);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,13 +14,11 @@ import com.sparrowwallet.drongo.wallet.*;
|
||||||
import com.sparrowwallet.sparrow.AppServices;
|
import com.sparrowwallet.sparrow.AppServices;
|
||||||
import com.sparrowwallet.sparrow.EventManager;
|
import com.sparrowwallet.sparrow.EventManager;
|
||||||
import com.sparrowwallet.sparrow.event.*;
|
import com.sparrowwallet.sparrow.event.*;
|
||||||
|
import com.sparrowwallet.sparrow.io.CardApi;
|
||||||
import com.sparrowwallet.sparrow.io.Device;
|
import com.sparrowwallet.sparrow.io.Device;
|
||||||
import com.sparrowwallet.sparrow.io.Hwi;
|
import com.sparrowwallet.sparrow.io.Hwi;
|
||||||
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
||||||
import com.sparrowwallet.sparrow.io.KeystoreCardImport;
|
import com.sparrowwallet.sparrow.io.CardAuthorizationException;
|
||||||
import com.sparrowwallet.sparrow.io.ckcard.CardApi;
|
|
||||||
import com.sparrowwallet.sparrow.io.ckcard.CardAuthorizationException;
|
|
||||||
import com.sparrowwallet.sparrow.io.ckcard.CkCard;
|
|
||||||
import com.sparrowwallet.sparrow.net.ElectrumServer;
|
import com.sparrowwallet.sparrow.net.ElectrumServer;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
@ -579,19 +577,16 @@ public class DevicePane extends TitledDescriptionPane {
|
||||||
private void importKeystore(List<ChildNumber> derivation) {
|
private void importKeystore(List<ChildNumber> derivation) {
|
||||||
if(device.isCard()) {
|
if(device.isCard()) {
|
||||||
try {
|
try {
|
||||||
CkCard importer = new CkCard();
|
CardApi cardApi = CardApi.getCardApi(device.getModel(), pin.get());
|
||||||
if(!importer.isInitialized()) {
|
if(!cardApi.isInitialized()) {
|
||||||
setDescription("Card not initialized");
|
setDescription("Card not initialized");
|
||||||
setContent(getCardInitializationPanel(importer));
|
setContent(getCardInitializationPanel(cardApi));
|
||||||
showHideLink.setVisible(false);
|
showHideLink.setVisible(false);
|
||||||
setExpanded(true);
|
setExpanded(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Service<Keystore> importService = new CardImportPane.CardImportService(importer, pin.get(), derivation);
|
Service<Keystore> importService = cardApi.getImportService(derivation, messageProperty);
|
||||||
importService.messageProperty().addListener((observable, oldValue, newValue) -> {
|
|
||||||
messageProperty.set(newValue);
|
|
||||||
});
|
|
||||||
handleCardOperation(importService, importButton, "Import", event -> {
|
handleCardOperation(importService, importButton, "Import", event -> {
|
||||||
importKeystore(derivation, importService.getValue());
|
importKeystore(derivation, importService.getValue());
|
||||||
});
|
});
|
||||||
|
@ -669,7 +664,7 @@ public class DevicePane extends TitledDescriptionPane {
|
||||||
private void sign() {
|
private void sign() {
|
||||||
if(device.isCard()) {
|
if(device.isCard()) {
|
||||||
try {
|
try {
|
||||||
CardApi cardApi = new CardApi(pin.get());
|
CardApi cardApi = CardApi.getCardApi(device.getModel(), pin.get());
|
||||||
Service<PSBT> signService = cardApi.getSignService(wallet, psbt, messageProperty);
|
Service<PSBT> signService = cardApi.getSignService(wallet, psbt, messageProperty);
|
||||||
handleCardOperation(signService, signButton, "Signing", event -> {
|
handleCardOperation(signService, signButton, "Signing", event -> {
|
||||||
EventManager.get().post(new PSBTSignedEvent(psbt, signService.getValue()));
|
EventManager.get().post(new PSBTSignedEvent(psbt, signService.getValue()));
|
||||||
|
@ -738,7 +733,7 @@ public class DevicePane extends TitledDescriptionPane {
|
||||||
private void signMessage() {
|
private void signMessage() {
|
||||||
if(device.isCard()) {
|
if(device.isCard()) {
|
||||||
try {
|
try {
|
||||||
CardApi cardApi = new CardApi(pin.get());
|
CardApi cardApi = CardApi.getCardApi(device.getModel(), pin.get());
|
||||||
Service<String> signMessageService = cardApi.getSignMessageService(message, wallet.getScriptType(), keyDerivation.getDerivation(), messageProperty);
|
Service<String> signMessageService = cardApi.getSignMessageService(message, wallet.getScriptType(), keyDerivation.getDerivation(), messageProperty);
|
||||||
handleCardOperation(signMessageService, signMessageButton, "Signing", event -> {
|
handleCardOperation(signMessageService, signMessageButton, "Signing", event -> {
|
||||||
String signature = signMessageService.getValue();
|
String signature = signMessageService.getValue();
|
||||||
|
@ -889,7 +884,7 @@ public class DevicePane extends TitledDescriptionPane {
|
||||||
return contentBox;
|
return contentBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Node getCardInitializationPanel(KeystoreCardImport importer) {
|
private Node getCardInitializationPanel(CardApi cardApi) {
|
||||||
VBox initTypeBox = new VBox(5);
|
VBox initTypeBox = new VBox(5);
|
||||||
RadioButton automatic = new RadioButton("Automatic (Recommended)");
|
RadioButton automatic = new RadioButton("Automatic (Recommended)");
|
||||||
RadioButton advanced = new RadioButton("Advanced");
|
RadioButton advanced = new RadioButton("Advanced");
|
||||||
|
@ -911,7 +906,7 @@ public class DevicePane extends TitledDescriptionPane {
|
||||||
initializeButton.setDefaultButton(true);
|
initializeButton.setDefaultButton(true);
|
||||||
initializeButton.setOnAction(event -> {
|
initializeButton.setOnAction(event -> {
|
||||||
byte[] chainCode = toggleGroup.getSelectedToggle() == automatic ? null : Sha256Hash.hashTwice(entropy.getText().getBytes(StandardCharsets.UTF_8));
|
byte[] chainCode = toggleGroup.getSelectedToggle() == automatic ? null : Sha256Hash.hashTwice(entropy.getText().getBytes(StandardCharsets.UTF_8));
|
||||||
CardImportPane.CardInitializationService cardInitializationService = new CardImportPane.CardInitializationService(importer, chainCode);
|
Service<Void> cardInitializationService = cardApi.getInitializationService(chainCode);
|
||||||
cardInitializationService.setOnSucceeded(event1 -> {
|
cardInitializationService.setOnSucceeded(event1 -> {
|
||||||
AppServices.showSuccessDialog("Card Initialized", "The card was successfully initialized.\n\nYou will now need to enter the PIN code found on the back. You can change the PIN code once it has been imported.");
|
AppServices.showSuccessDialog("Card Initialized", "The card was successfully initialized.\n\nYou will now need to enter the PIN code found on the back. You can change the PIN code once it has been imported.");
|
||||||
setDescription("Enter PIN code");
|
setDescription("Enter PIN code");
|
||||||
|
|
|
@ -3,16 +3,12 @@ package com.sparrowwallet.sparrow.control;
|
||||||
import com.google.common.eventbus.Subscribe;
|
import com.google.common.eventbus.Subscribe;
|
||||||
import com.sparrowwallet.drongo.psbt.PSBT;
|
import com.sparrowwallet.drongo.psbt.PSBT;
|
||||||
import com.sparrowwallet.drongo.wallet.Wallet;
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
import com.sparrowwallet.drongo.wallet.WalletModel;
|
|
||||||
import com.sparrowwallet.sparrow.EventManager;
|
import com.sparrowwallet.sparrow.EventManager;
|
||||||
import com.sparrowwallet.sparrow.event.PSBTSignedEvent;
|
import com.sparrowwallet.sparrow.event.PSBTSignedEvent;
|
||||||
import com.sparrowwallet.sparrow.io.Device;
|
import com.sparrowwallet.sparrow.io.Device;
|
||||||
import com.sparrowwallet.sparrow.io.ckcard.CardApi;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.smartcardio.CardException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class DeviceSignDialog extends DeviceDialog<PSBT> {
|
public class DeviceSignDialog extends DeviceDialog<PSBT> {
|
||||||
|
|
78
src/main/java/com/sparrowwallet/sparrow/io/CardApi.java
Normal file
78
src/main/java/com/sparrowwallet/sparrow/io/CardApi.java
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
package com.sparrowwallet.sparrow.io;
|
||||||
|
|
||||||
|
import com.sparrowwallet.drongo.crypto.ChildNumber;
|
||||||
|
import com.sparrowwallet.drongo.protocol.ScriptType;
|
||||||
|
import com.sparrowwallet.drongo.psbt.PSBT;
|
||||||
|
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||||
|
import com.sparrowwallet.drongo.wallet.Wallet;
|
||||||
|
import com.sparrowwallet.drongo.wallet.WalletModel;
|
||||||
|
import com.sparrowwallet.sparrow.io.ckcard.CkCardApi;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
|
import javafx.concurrent.Service;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.smartcardio.CardException;
|
||||||
|
import javax.smartcardio.TerminalFactory;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public abstract class CardApi {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(CardApi.class);
|
||||||
|
|
||||||
|
public static List<WalletModel> getConnectedCards() throws CardException {
|
||||||
|
try {
|
||||||
|
CkCardApi ckCardApi = new CkCardApi(null, null);
|
||||||
|
return List.of(ckCardApi.getCardType());
|
||||||
|
} catch(Exception e) {
|
||||||
|
//ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CardApi getCardApi(WalletModel walletModel, String pin) throws CardException {
|
||||||
|
if(walletModel == WalletModel.TAPSIGNER || walletModel == WalletModel.SATSCARD) {
|
||||||
|
return new CkCardApi(walletModel, pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("Cannot create card API for " + walletModel.toDisplayString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract boolean isInitialized() throws CardException;
|
||||||
|
|
||||||
|
public abstract void initialize(byte[] entropy) throws CardException;
|
||||||
|
|
||||||
|
public abstract WalletModel getCardType() throws CardException;
|
||||||
|
|
||||||
|
public abstract Service<Void> getAuthDelayService() throws CardException;
|
||||||
|
|
||||||
|
public abstract boolean requiresBackup() throws CardException;
|
||||||
|
|
||||||
|
public abstract Service<String> getBackupService();
|
||||||
|
|
||||||
|
public abstract boolean changePin(String newPin) throws CardException;
|
||||||
|
|
||||||
|
public abstract Keystore getKeystore() throws CardException;
|
||||||
|
|
||||||
|
public abstract Service<Void> getInitializationService(byte[] entropy);
|
||||||
|
|
||||||
|
public abstract Service<Keystore> getImportService(List<ChildNumber> derivation, StringProperty messageProperty);
|
||||||
|
|
||||||
|
public abstract Service<PSBT> getSignService(Wallet wallet, PSBT psbt, StringProperty messageProperty);
|
||||||
|
|
||||||
|
public abstract Service<String> getSignMessageService(String message, ScriptType scriptType, List<ChildNumber> derivation, StringProperty messageProperty);
|
||||||
|
|
||||||
|
public abstract void disconnect();
|
||||||
|
|
||||||
|
public static boolean isReaderAvailable() {
|
||||||
|
try {
|
||||||
|
TerminalFactory tf = TerminalFactory.getDefault();
|
||||||
|
return !tf.terminals().list().isEmpty();
|
||||||
|
} catch(Exception e) {
|
||||||
|
log.error("Error detecting card terminals", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package com.sparrowwallet.sparrow.io.ckcard;
|
package com.sparrowwallet.sparrow.io;
|
||||||
|
|
||||||
import javax.smartcardio.CardException;
|
import javax.smartcardio.CardException;
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
package com.sparrowwallet.sparrow.io;
|
package com.sparrowwallet.sparrow.io;
|
||||||
|
|
||||||
import javafx.beans.property.StringProperty;
|
|
||||||
|
|
||||||
import javax.smartcardio.CardException;
|
import javax.smartcardio.CardException;
|
||||||
|
|
||||||
public interface CardImport extends ImportExport {
|
public interface CardImport extends ImportExport {
|
||||||
boolean isInitialized() throws CardException;
|
boolean isInitialized() throws CardException;
|
||||||
void initialize(byte[] chainCode) throws CardException;
|
void initialize(byte[] chainCode) throws CardException;
|
||||||
StringProperty messageProperty();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.sparrowwallet.sparrow.io.ckcard;
|
package com.sparrowwallet.sparrow.io;
|
||||||
|
|
||||||
import javax.smartcardio.CardException;
|
import javax.smartcardio.CardException;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package com.sparrowwallet.sparrow.io.ckcard;
|
package com.sparrowwallet.sparrow.io;
|
||||||
|
|
||||||
import javax.smartcardio.CardException;
|
import javax.smartcardio.CardException;
|
||||||
|
|
|
@ -10,7 +10,6 @@ import com.sparrowwallet.drongo.psbt.PSBT;
|
||||||
import com.sparrowwallet.drongo.psbt.PSBTParseException;
|
import com.sparrowwallet.drongo.psbt.PSBTParseException;
|
||||||
import com.sparrowwallet.drongo.wallet.StandardAccount;
|
import com.sparrowwallet.drongo.wallet.StandardAccount;
|
||||||
import com.sparrowwallet.drongo.wallet.WalletModel;
|
import com.sparrowwallet.drongo.wallet.WalletModel;
|
||||||
import com.sparrowwallet.sparrow.io.ckcard.CardApi;
|
|
||||||
import javafx.concurrent.ScheduledService;
|
import javafx.concurrent.ScheduledService;
|
||||||
import javafx.concurrent.Service;
|
import javafx.concurrent.Service;
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
|
@ -98,16 +97,19 @@ public class Hwi {
|
||||||
List<Device> devices = new ArrayList<>();
|
List<Device> devices = new ArrayList<>();
|
||||||
if(CardApi.isReaderAvailable()) {
|
if(CardApi.isReaderAvailable()) {
|
||||||
try {
|
try {
|
||||||
CardApi cardApi = new CardApi(null);
|
List<WalletModel> connectedCards = CardApi.getConnectedCards();
|
||||||
WalletModel walletModel = cardApi.getCardType();
|
for(WalletModel card : connectedCards) {
|
||||||
|
CardApi cardApi = CardApi.getCardApi(card, null);
|
||||||
|
WalletModel walletModel = cardApi.getCardType();
|
||||||
|
|
||||||
Device cardDevice = new Device();
|
Device cardDevice = new Device();
|
||||||
cardDevice.setType(walletModel.getType());
|
cardDevice.setType(walletModel.getType());
|
||||||
cardDevice.setModel(walletModel);
|
cardDevice.setModel(walletModel);
|
||||||
cardDevice.setNeedsPassphraseSent(Boolean.FALSE);
|
cardDevice.setNeedsPassphraseSent(Boolean.FALSE);
|
||||||
cardDevice.setNeedsPinSent(Boolean.FALSE);
|
cardDevice.setNeedsPinSent(Boolean.FALSE);
|
||||||
cardDevice.setCard(true);
|
cardDevice.setCard(true);
|
||||||
devices.add(cardDevice);
|
devices.add(cardDevice);
|
||||||
|
}
|
||||||
} catch(CardNotPresentException e) {
|
} catch(CardNotPresentException e) {
|
||||||
//ignore
|
//ignore
|
||||||
} catch(CardException e) {
|
} catch(CardException e) {
|
||||||
|
|
|
@ -2,10 +2,11 @@ package com.sparrowwallet.sparrow.io;
|
||||||
|
|
||||||
import com.sparrowwallet.drongo.crypto.ChildNumber;
|
import com.sparrowwallet.drongo.crypto.ChildNumber;
|
||||||
import com.sparrowwallet.drongo.wallet.Keystore;
|
import com.sparrowwallet.drongo.wallet.Keystore;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface KeystoreCardImport extends CardImport {
|
public interface KeystoreCardImport extends CardImport {
|
||||||
Keystore getKeystore(String pin, List<ChildNumber> derivation) throws ImportException;
|
Keystore getKeystore(String pin, List<ChildNumber> derivation, StringProperty messageProperty) throws ImportException;
|
||||||
String getKeystoreImportDescription(int account);
|
String getKeystoreImportDescription(int account);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ import com.sparrowwallet.drongo.crypto.ChildNumber;
|
||||||
import com.sparrowwallet.drongo.crypto.ECDSASignature;
|
import com.sparrowwallet.drongo.crypto.ECDSASignature;
|
||||||
import com.sparrowwallet.drongo.crypto.ECKey;
|
import com.sparrowwallet.drongo.crypto.ECKey;
|
||||||
import com.sparrowwallet.drongo.protocol.Sha256Hash;
|
import com.sparrowwallet.drongo.protocol.Sha256Hash;
|
||||||
|
import com.sparrowwallet.sparrow.io.CardSignFailedException;
|
||||||
|
import com.sparrowwallet.sparrow.io.CardUnluckyNumberException;
|
||||||
import org.bitcoin.NativeSecp256k1;
|
import org.bitcoin.NativeSecp256k1;
|
||||||
import org.bitcoin.NativeSecp256k1Util;
|
import org.bitcoin.NativeSecp256k1Util;
|
||||||
import org.bitcoin.Secp256k1Context;
|
import org.bitcoin.Secp256k1Context;
|
||||||
|
|
|
@ -12,6 +12,8 @@ import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gson.JsonPrimitive;
|
import com.google.gson.JsonPrimitive;
|
||||||
import com.sparrowwallet.drongo.Utils;
|
import com.sparrowwallet.drongo.Utils;
|
||||||
|
import com.sparrowwallet.sparrow.io.CardAuthorizationException;
|
||||||
|
import com.sparrowwallet.sparrow.io.CardUnluckyNumberException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,8 @@ import com.sparrowwallet.drongo.psbt.PSBT;
|
||||||
import com.sparrowwallet.drongo.psbt.PSBTInput;
|
import com.sparrowwallet.drongo.psbt.PSBTInput;
|
||||||
import com.sparrowwallet.drongo.psbt.PSBTInputSigner;
|
import com.sparrowwallet.drongo.psbt.PSBTInputSigner;
|
||||||
import com.sparrowwallet.drongo.wallet.*;
|
import com.sparrowwallet.drongo.wallet.*;
|
||||||
|
import com.sparrowwallet.sparrow.control.CardImportPane;
|
||||||
|
import com.sparrowwallet.sparrow.io.CardApi;
|
||||||
import javafx.beans.property.IntegerProperty;
|
import javafx.beans.property.IntegerProperty;
|
||||||
import javafx.beans.property.SimpleIntegerProperty;
|
import javafx.beans.property.SimpleIntegerProperty;
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
@ -24,33 +26,36 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
public class CardApi {
|
public class CkCardApi extends CardApi {
|
||||||
private static final Logger log = LoggerFactory.getLogger(CardApi.class);
|
private static final Logger log = LoggerFactory.getLogger(CkCardApi.class);
|
||||||
|
|
||||||
private final WalletModel cardType;
|
private final WalletModel cardType;
|
||||||
private final CardProtocol cardProtocol;
|
private final CardProtocol cardProtocol;
|
||||||
private String cvc;
|
private String cvc;
|
||||||
|
|
||||||
public CardApi(String cvc) throws CardException {
|
public CkCardApi(String cvc) throws CardException {
|
||||||
this(WalletModel.TAPSIGNER, cvc);
|
this(WalletModel.TAPSIGNER, cvc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CardApi(WalletModel cardType, String cvc) throws CardException {
|
public CkCardApi(WalletModel cardType, String cvc) throws CardException {
|
||||||
this.cardType = cardType;
|
this.cardType = cardType;
|
||||||
this.cardProtocol = new CardProtocol();
|
this.cardProtocol = new CardProtocol();
|
||||||
this.cvc = cvc;
|
this.cvc = cvc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isInitialized() throws CardException {
|
public boolean isInitialized() throws CardException {
|
||||||
CardStatus cardStatus = getStatus();
|
CardStatus cardStatus = getStatus();
|
||||||
return cardStatus.isInitialized();
|
return cardStatus.isInitialized();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void initialize(byte[] chainCode) throws CardException {
|
public void initialize(byte[] chainCode) throws CardException {
|
||||||
cardProtocol.verify();
|
cardProtocol.verify();
|
||||||
cardProtocol.setup(cvc, chainCode);
|
cardProtocol.setup(cvc, chainCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public WalletModel getCardType() throws CardException {
|
public WalletModel getCardType() throws CardException {
|
||||||
CardStatus cardStatus = getStatus();
|
CardStatus cardStatus = getStatus();
|
||||||
return cardStatus.getCardType();
|
return cardStatus.getCardType();
|
||||||
|
@ -58,7 +63,7 @@ public class CardApi {
|
||||||
|
|
||||||
CardStatus getStatus() throws CardException {
|
CardStatus getStatus() throws CardException {
|
||||||
CardStatus cardStatus = cardProtocol.getStatus();
|
CardStatus cardStatus = cardProtocol.getStatus();
|
||||||
if(cardStatus.getCardType() != cardType) {
|
if(cardType != null && cardStatus.getCardType() != cardType) {
|
||||||
throw new CardException("Please use a " + cardType.toDisplayString() + " card.");
|
throw new CardException("Please use a " + cardType.toDisplayString() + " card.");
|
||||||
}
|
}
|
||||||
return cardStatus;
|
return cardStatus;
|
||||||
|
@ -78,6 +83,7 @@ public class CardApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Service<Void> getAuthDelayService() throws CardException {
|
public Service<Void> getAuthDelayService() throws CardException {
|
||||||
CardStatus cardStatus = getStatus();
|
CardStatus cardStatus = getStatus();
|
||||||
if(cardStatus.auth_delay != null) {
|
if(cardStatus.auth_delay != null) {
|
||||||
|
@ -87,11 +93,13 @@ public class CardApi {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean requiresBackup() throws CardException {
|
public boolean requiresBackup() throws CardException {
|
||||||
CardStatus cardStatus = getStatus();
|
CardStatus cardStatus = getStatus();
|
||||||
return cardStatus.requiresBackup();
|
return cardStatus.requiresBackup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Service<String> getBackupService() {
|
public Service<String> getBackupService() {
|
||||||
return new BackupService();
|
return new BackupService();
|
||||||
}
|
}
|
||||||
|
@ -101,6 +109,7 @@ public class CardApi {
|
||||||
return Utils.bytesToHex(cardBackup.data);
|
return Utils.bytesToHex(cardBackup.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean changePin(String newCvc) throws CardException {
|
public boolean changePin(String newCvc) throws CardException {
|
||||||
CardChange cardChange = cardProtocol.change(cvc, newCvc);
|
CardChange cardChange = cardProtocol.change(cvc, newCvc);
|
||||||
if(cardChange.success) {
|
if(cardChange.success) {
|
||||||
|
@ -110,10 +119,21 @@ public class CardApi {
|
||||||
return cardChange.success;
|
return cardChange.success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDerivation(List<ChildNumber> derivation) throws CardException {
|
void setDerivation(List<ChildNumber> derivation) throws CardException {
|
||||||
cardProtocol.derive(cvc, derivation);
|
cardProtocol.derive(cvc, derivation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Service<Void> getInitializationService(byte[] entropy) {
|
||||||
|
return new CardImportPane.CardInitializationService(new Tapsigner(), entropy);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Service<Keystore> getImportService(List<ChildNumber> derivation, StringProperty messageProperty) {
|
||||||
|
return new CardImportPane.CardImportService(new Tapsigner(), cvc, derivation, messageProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Keystore getKeystore() throws CardException {
|
public Keystore getKeystore() throws CardException {
|
||||||
KeyDerivation keyDerivation = getKeyDerivation();
|
KeyDerivation keyDerivation = getKeyDerivation();
|
||||||
|
|
||||||
|
@ -141,6 +161,7 @@ public class CardApi {
|
||||||
return Utils.bytesToHex(masterXpubkey.getKey().getFingerprint());
|
return Utils.bytesToHex(masterXpubkey.getKey().getFingerprint());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Service<PSBT> getSignService(Wallet wallet, PSBT psbt, StringProperty messageProperty) {
|
public Service<PSBT> getSignService(Wallet wallet, PSBT psbt, StringProperty messageProperty) {
|
||||||
return new SignService(wallet, psbt, messageProperty);
|
return new SignService(wallet, psbt, messageProperty);
|
||||||
}
|
}
|
||||||
|
@ -183,6 +204,7 @@ public class CardApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Service<String> getSignMessageService(String message, ScriptType scriptType, List<ChildNumber> derivation, StringProperty messageProperty) {
|
public Service<String> getSignMessageService(String message, ScriptType scriptType, List<ChildNumber> derivation, StringProperty messageProperty) {
|
||||||
return new SignMessageService(message, scriptType, derivation, messageProperty);
|
return new SignMessageService(message, scriptType, derivation, messageProperty);
|
||||||
}
|
}
|
||||||
|
@ -217,6 +239,7 @@ public class CardApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void disconnect() {
|
public void disconnect() {
|
||||||
try {
|
try {
|
||||||
cardProtocol.disconnect();
|
cardProtocol.disconnect();
|
||||||
|
@ -225,10 +248,6 @@ public class CardApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isReaderAvailable() {
|
|
||||||
return CardTransport.isReaderAvailable();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AuthDelayService extends Service<Void> {
|
public class AuthDelayService extends Service<Void> {
|
||||||
private final CardStatus cardStatus;
|
private final CardStatus cardStatus;
|
||||||
private final IntegerProperty delayProperty;
|
private final IntegerProperty delayProperty;
|
||||||
|
@ -303,7 +322,7 @@ public class CardApi {
|
||||||
@Override
|
@Override
|
||||||
public TransactionSignature sign(Sha256Hash hash, SigHash sigHash, TransactionSignature.Type signatureType) {
|
public TransactionSignature sign(Sha256Hash hash, SigHash sigHash, TransactionSignature.Type signatureType) {
|
||||||
if(signatureType != TransactionSignature.Type.ECDSA) {
|
if(signatureType != TransactionSignature.Type.ECDSA) {
|
||||||
throw new IllegalStateException(cardType.toDisplayString() + " cannot sign " + signatureType + " transactions.");
|
throw new IllegalStateException(WalletModel.TAPSIGNER.toDisplayString() + " cannot sign " + signatureType + " transactions.");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
|
@ -6,20 +6,17 @@ import com.sparrowwallet.drongo.wallet.WalletModel;
|
||||||
import com.sparrowwallet.sparrow.io.KeystoreCardImport;
|
import com.sparrowwallet.sparrow.io.KeystoreCardImport;
|
||||||
import com.sparrowwallet.sparrow.io.ImportException;
|
import com.sparrowwallet.sparrow.io.ImportException;
|
||||||
import javafx.beans.property.SimpleIntegerProperty;
|
import javafx.beans.property.SimpleIntegerProperty;
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
|
||||||
import javafx.beans.property.StringProperty;
|
import javafx.beans.property.StringProperty;
|
||||||
|
|
||||||
import javax.smartcardio.CardException;
|
import javax.smartcardio.CardException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class CkCard implements KeystoreCardImport {
|
public class Tapsigner implements KeystoreCardImport {
|
||||||
private final StringProperty messageProperty = new SimpleStringProperty("");
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isInitialized() throws CardException {
|
public boolean isInitialized() throws CardException {
|
||||||
CardApi cardApi = null;
|
CkCardApi cardApi = null;
|
||||||
try {
|
try {
|
||||||
cardApi = new CardApi(null);
|
cardApi = new CkCardApi(null);
|
||||||
return cardApi.isInitialized();
|
return cardApi.isInitialized();
|
||||||
} finally {
|
} finally {
|
||||||
if(cardApi != null) {
|
if(cardApi != null) {
|
||||||
|
@ -30,9 +27,9 @@ public class CkCard implements KeystoreCardImport {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(byte[] chainCode) throws CardException {
|
public void initialize(byte[] chainCode) throws CardException {
|
||||||
CardApi cardApi = null;
|
CkCardApi cardApi = null;
|
||||||
try {
|
try {
|
||||||
cardApi = new CardApi(null);
|
cardApi = new CkCardApi(null);
|
||||||
cardApi.initialize(chainCode);
|
cardApi.initialize(chainCode);
|
||||||
} finally {
|
} finally {
|
||||||
if(cardApi != null) {
|
if(cardApi != null) {
|
||||||
|
@ -42,7 +39,7 @@ public class CkCard implements KeystoreCardImport {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Keystore getKeystore(String pin, List<ChildNumber> derivation) throws ImportException {
|
public Keystore getKeystore(String pin, List<ChildNumber> derivation, StringProperty messageProperty) throws ImportException {
|
||||||
if(pin.length() < 6) {
|
if(pin.length() < 6) {
|
||||||
throw new ImportException("PIN too short.");
|
throw new ImportException("PIN too short.");
|
||||||
}
|
}
|
||||||
|
@ -51,9 +48,9 @@ public class CkCard implements KeystoreCardImport {
|
||||||
throw new ImportException("PIN too long.");
|
throw new ImportException("PIN too long.");
|
||||||
}
|
}
|
||||||
|
|
||||||
CardApi cardApi = null;
|
CkCardApi cardApi = null;
|
||||||
try {
|
try {
|
||||||
cardApi = new CardApi(pin);
|
cardApi = new CkCardApi(pin);
|
||||||
CardStatus cardStatus = cardApi.getStatus();
|
CardStatus cardStatus = cardApi.getStatus();
|
||||||
if(!cardStatus.isInitialized()) {
|
if(!cardStatus.isInitialized()) {
|
||||||
throw new IllegalStateException("Card is not initialized.");
|
throw new IllegalStateException("Card is not initialized.");
|
||||||
|
@ -87,9 +84,4 @@ public class CkCard implements KeystoreCardImport {
|
||||||
public WalletModel getWalletModel() {
|
public WalletModel getWalletModel() {
|
||||||
return WalletModel.TAPSIGNER;
|
return WalletModel.TAPSIGNER;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public StringProperty messageProperty() {
|
|
||||||
return messageProperty;
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -5,7 +5,7 @@ import com.sparrowwallet.sparrow.control.CardImportPane;
|
||||||
import com.sparrowwallet.sparrow.control.FileKeystoreImportPane;
|
import com.sparrowwallet.sparrow.control.FileKeystoreImportPane;
|
||||||
import com.sparrowwallet.sparrow.control.TitledDescriptionPane;
|
import com.sparrowwallet.sparrow.control.TitledDescriptionPane;
|
||||||
import com.sparrowwallet.sparrow.io.*;
|
import com.sparrowwallet.sparrow.io.*;
|
||||||
import com.sparrowwallet.sparrow.io.ckcard.CkCard;
|
import com.sparrowwallet.sparrow.io.ckcard.Tapsigner;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.scene.control.Accordion;
|
import javafx.scene.control.Accordion;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
|
@ -15,7 +15,7 @@ import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static com.sparrowwallet.sparrow.io.ckcard.CardApi.isReaderAvailable;
|
import static com.sparrowwallet.sparrow.io.CardApi.isReaderAvailable;
|
||||||
|
|
||||||
public class HwAirgappedController extends KeystoreImportDetailController {
|
public class HwAirgappedController extends KeystoreImportDetailController {
|
||||||
private static final Logger log = LoggerFactory.getLogger(HwAirgappedController.class);
|
private static final Logger log = LoggerFactory.getLogger(HwAirgappedController.class);
|
||||||
|
@ -42,7 +42,7 @@ public class HwAirgappedController extends KeystoreImportDetailController {
|
||||||
|
|
||||||
List<KeystoreCardImport> cardImporters = Collections.emptyList();
|
List<KeystoreCardImport> cardImporters = Collections.emptyList();
|
||||||
if(isReaderAvailable()) {
|
if(isReaderAvailable()) {
|
||||||
cardImporters = List.of(new CkCard());
|
cardImporters = List.of(new Tapsigner());
|
||||||
}
|
}
|
||||||
for(KeystoreCardImport importer : cardImporters) {
|
for(KeystoreCardImport importer : cardImporters) {
|
||||||
if(!importer.isDeprecated() || Config.get().isShowDeprecatedImportExport()) {
|
if(!importer.isDeprecated() || Config.get().isShowDeprecatedImportExport()) {
|
||||||
|
|
|
@ -9,8 +9,8 @@ import com.sparrowwallet.sparrow.control.*;
|
||||||
import com.sparrowwallet.sparrow.event.*;
|
import com.sparrowwallet.sparrow.event.*;
|
||||||
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5;
|
||||||
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5Brands;
|
import com.sparrowwallet.sparrow.glyphfont.FontAwesome5Brands;
|
||||||
|
import com.sparrowwallet.sparrow.io.CardApi;
|
||||||
import com.sparrowwallet.sparrow.io.Storage;
|
import com.sparrowwallet.sparrow.io.Storage;
|
||||||
import com.sparrowwallet.sparrow.io.ckcard.CardApi;
|
|
||||||
import com.sparrowwallet.sparrow.keystoreimport.KeystoreImportDialog;
|
import com.sparrowwallet.sparrow.keystoreimport.KeystoreImportDialog;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.concurrent.Service;
|
import javafx.concurrent.Service;
|
||||||
|
@ -37,7 +37,7 @@ import java.util.Optional;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static com.sparrowwallet.sparrow.io.ckcard.CardApi.isReaderAvailable;
|
import static com.sparrowwallet.sparrow.io.CardApi.isReaderAvailable;
|
||||||
|
|
||||||
public class KeystoreController extends WalletFormController implements Initializable {
|
public class KeystoreController extends WalletFormController implements Initializable {
|
||||||
private static final Logger log = LoggerFactory.getLogger(KeystoreController.class);
|
private static final Logger log = LoggerFactory.getLogger(KeystoreController.class);
|
||||||
|
@ -427,7 +427,7 @@ public class KeystoreController extends WalletFormController implements Initiali
|
||||||
String newPin = optPinChange.get().newPin();
|
String newPin = optPinChange.get().newPin();
|
||||||
boolean backupFirst = optPinChange.get().backupFirst();
|
boolean backupFirst = optPinChange.get().backupFirst();
|
||||||
try {
|
try {
|
||||||
CardApi cardApi = new CardApi(currentPin);
|
CardApi cardApi = CardApi.getCardApi(keystore.getWalletModel(), currentPin);
|
||||||
Service<Void> authDelayService = cardApi.getAuthDelayService();
|
Service<Void> authDelayService = cardApi.getAuthDelayService();
|
||||||
if(authDelayService != null) {
|
if(authDelayService != null) {
|
||||||
authDelayService.setOnSucceeded(event1 -> {
|
authDelayService.setOnSucceeded(event1 -> {
|
||||||
|
|
Loading…
Reference in a new issue