refactor cardapi to generic service

This commit is contained in:
Craig Raw 2023-01-30 14:47:00 +02:00
parent 057a9efb1f
commit 300545b289
17 changed files with 163 additions and 79 deletions

View file

@ -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;

View file

@ -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);
} }
}; };
} }

View file

@ -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");

View file

@ -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> {

View 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;
}
}

View file

@ -1,4 +1,4 @@
package com.sparrowwallet.sparrow.io.ckcard; package com.sparrowwallet.sparrow.io;
import javax.smartcardio.CardException; import javax.smartcardio.CardException;

View file

@ -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();
} }

View file

@ -1,4 +1,4 @@
package com.sparrowwallet.sparrow.io.ckcard; package com.sparrowwallet.sparrow.io;
import javax.smartcardio.CardException; import javax.smartcardio.CardException;

View file

@ -1,4 +1,4 @@
package com.sparrowwallet.sparrow.io.ckcard; package com.sparrowwallet.sparrow.io;
import javax.smartcardio.CardException; import javax.smartcardio.CardException;

View file

@ -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,7 +97,9 @@ 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();
for(WalletModel card : connectedCards) {
CardApi cardApi = CardApi.getCardApi(card, null);
WalletModel walletModel = cardApi.getCardType(); WalletModel walletModel = cardApi.getCardType();
Device cardDevice = new Device(); Device cardDevice = new Device();
@ -108,6 +109,7 @@ public class Hwi {
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) {

View file

@ -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);
} }

View file

@ -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;

View file

@ -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;

View file

@ -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 {

View file

@ -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;
}
} }

View file

@ -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()) {

View file

@ -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 -> {