mirror of
https://github.com/sparrowwallet/sparrow.git
synced 2025-11-05 11:56:37 +00:00
Satochip: remove debug logs, trailing whitespaces & clean code
This commit is contained in:
parent
cbcb40c973
commit
6f028d720b
13 changed files with 531 additions and 598 deletions
|
|
@ -674,7 +674,7 @@ public class DevicePane extends TitledDescriptionPane {
|
||||||
try {
|
try {
|
||||||
CardApi cardApi = CardApi.getCardApi(device.getModel(), pin.get());
|
CardApi cardApi = CardApi.getCardApi(device.getModel(), pin.get());
|
||||||
if(!cardApi.isInitialized()) {
|
if(!cardApi.isInitialized()) {
|
||||||
if(pin.get().length() < device.getModel().getMinPinLength()) {
|
if(pin.get().length() < device.getModel().getMinPinLength()) {
|
||||||
setDescription(pin.get().isEmpty() ? "Enter PIN code" : "PIN code too short");
|
setDescription(pin.get().isEmpty() ? "Enter PIN code" : "PIN code too short");
|
||||||
setContent(getCardPinEntry(importButton));
|
setContent(getCardPinEntry(importButton));
|
||||||
showHideLink.setVisible(false);
|
showHideLink.setVisible(false);
|
||||||
|
|
@ -1074,17 +1074,10 @@ public class DevicePane extends TitledDescriptionPane {
|
||||||
initializeButton.setDefaultButton(true);
|
initializeButton.setDefaultButton(true);
|
||||||
initializeButton.setOnAction(event -> {
|
initializeButton.setOnAction(event -> {
|
||||||
initializeButton.setDisable(true);
|
initializeButton.setDisable(true);
|
||||||
/*
|
|
||||||
log.trace("SATOCHIP DevicePane getCardInitializationPanel() pin.get(): " + pin.get());
|
|
||||||
log.trace("SATOCHIP DevicePane getCardInitializationPanel() repeatedPin.getText(): " + repeatedPin.getText());
|
|
||||||
log.trace("SATOCHIP DevicePane getCardInitializationPanel() mnemonic.getText(): "+ mnemonic.getText());
|
|
||||||
log.trace("SATOCHIP DevicePane getCardInitializationPanel() passphrase.getText(): "+ passphrase.getText());
|
|
||||||
*/
|
|
||||||
|
|
||||||
byte[] seedBytes;
|
byte[] seedBytes;
|
||||||
// check that pin and previous pin match
|
// check that pin and previous pin match
|
||||||
if ( !pin.get().equals(repeatedPin.getText()) ){
|
if ( !pin.get().equals(repeatedPin.getText()) ){
|
||||||
seedBytes = null; // will display a proper error later in cardApi.getInitializationService()
|
seedBytes = null; // will display a proper error later in cardApi.getInitializationService()
|
||||||
messageProperty.set("The two entered Pin values do not correspond!");
|
messageProperty.set("The two entered Pin values do not correspond!");
|
||||||
} else {
|
} else {
|
||||||
try{
|
try{
|
||||||
|
|
@ -1098,7 +1091,6 @@ public class DevicePane extends TitledDescriptionPane {
|
||||||
messageProperty.set("Failed to parse the seed with error: " + e);
|
messageProperty.set("Failed to parse the seed with error: " + e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Service<Void> cardInitializationService = cardApi.getInitializationService(seedBytes, messageProperty);
|
Service<Void> cardInitializationService = cardApi.getInitializationService(seedBytes, messageProperty);
|
||||||
cardInitializationService.setOnSucceeded(successEvent -> {
|
cardInitializationService.setOnSucceeded(successEvent -> {
|
||||||
log.debug("SATOCHIP DevicePane getCardInitializationPanel() Card initialized!");
|
log.debug("SATOCHIP DevicePane getCardInitializationPanel() Card initialized!");
|
||||||
|
|
@ -1112,17 +1104,9 @@ public class DevicePane extends TitledDescriptionPane {
|
||||||
setExpanded(false);
|
setExpanded(false);
|
||||||
});
|
});
|
||||||
cardInitializationService.setOnFailed(failEvent -> {
|
cardInitializationService.setOnFailed(failEvent -> {
|
||||||
log.error("SATOCHIP DevicePane getCardInitializationPanel() failed to initialize card!");
|
log.error("Error initializing card", failEvent.getSource().getException());
|
||||||
/*Throwable rootCause = Throwables.getRootCause(failEvent.getSource().getException());
|
AppServices.showErrorDialog("Card Initialization Failed", "The card was not initialized.\n\n" + failEvent.getSource().getException().getMessage());
|
||||||
if(rootCause instanceof CardAuthorizationException) {
|
initializeButton.setDisable(false);
|
||||||
setError(rootCause.getMessage(), null);
|
|
||||||
setContent(getCardPinEntry(operationButton));
|
|
||||||
operationButton.setDisable(false);
|
|
||||||
} else {*/
|
|
||||||
log.error("Error initializing card", failEvent.getSource().getException());
|
|
||||||
AppServices.showErrorDialog("Card Initialization Failed", "The card was not initialized.\n\n" + failEvent.getSource().getException().getMessage());
|
|
||||||
initializeButton.setDisable(false);
|
|
||||||
//}
|
|
||||||
});
|
});
|
||||||
cardInitializationService.start();
|
cardInitializationService.start();
|
||||||
});
|
});
|
||||||
|
|
@ -1190,7 +1174,7 @@ public class DevicePane extends TitledDescriptionPane {
|
||||||
|
|
||||||
return contentBox;
|
return contentBox;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Node getCardPinEntry(ButtonBase operationButton) {
|
private Node getCardPinEntry(ButtonBase operationButton) {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ public class APDUCommand {
|
||||||
protected byte[] data;
|
protected byte[] data;
|
||||||
protected boolean needsLE;
|
protected boolean needsLE;
|
||||||
public static final String HEXES = "0123456789ABCDEF";
|
public static final String HEXES = "0123456789ABCDEF";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an APDU with no response data length field. The data field cannot be null, but can be a zero-length array.
|
* Constructs an APDU with no response data length field. The data field cannot be null, but can be a zero-length array.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ public class APDUResponse {
|
||||||
public static final int SW_CONDITIONS_OF_USE_NOT_SATISFIED = 0x6985; // applet may be already installed
|
public static final int SW_CONDITIONS_OF_USE_NOT_SATISFIED = 0x6985; // applet may be already installed
|
||||||
public static final int SW_WRONG_PIN_MASK = 0x63C0;
|
public static final int SW_WRONG_PIN_MASK = 0x63C0;
|
||||||
public static final String HEXES = "0123456789ABCDEF";
|
public static final String HEXES = "0123456789ABCDEF";
|
||||||
|
|
||||||
private byte[] apdu;
|
private byte[] apdu;
|
||||||
private byte[] data;
|
private byte[] data;
|
||||||
private int sw;
|
private int sw;
|
||||||
|
|
@ -33,7 +33,7 @@ public class APDUResponse {
|
||||||
this.apdu = apdu;
|
this.apdu = apdu;
|
||||||
this.parse();
|
this.parse();
|
||||||
}
|
}
|
||||||
|
|
||||||
public APDUResponse(byte[] data, byte sw1, byte sw2) {
|
public APDUResponse(byte[] data, byte sw1, byte sw2) {
|
||||||
byte[] apdu= new byte[data.length + 2];
|
byte[] apdu= new byte[data.length + 2];
|
||||||
System.arraycopy(data, 0, apdu, 0, data.length);
|
System.arraycopy(data, 0, apdu, 0, data.length);
|
||||||
|
|
@ -42,7 +42,7 @@ public class APDUResponse {
|
||||||
this.apdu = apdu;
|
this.apdu = apdu;
|
||||||
this.parse();
|
this.parse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the APDU response, separating the response data from SW.
|
* Parses the APDU response, separating the response data from SW.
|
||||||
|
|
@ -100,7 +100,7 @@ public class APDUResponse {
|
||||||
public byte[] getBytes() {
|
public byte[] getBytes() {
|
||||||
return this.apdu;
|
return this.apdu;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serializes the APDU to human readable hex string format
|
* Serializes the APDU to human readable hex string format
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -1,149 +1,149 @@
|
||||||
package com.sparrowwallet.sparrow.io.satochip;
|
package com.sparrowwallet.sparrow.io.satochip;
|
||||||
|
|
||||||
public final class Constants {
|
public final class Constants {
|
||||||
|
|
||||||
// Prevents instanciation of class
|
// Prevents instanciation of class
|
||||||
private Constants() {}
|
private Constants() {}
|
||||||
|
|
||||||
/****************************************
|
/****************************************
|
||||||
* Instruction codes *
|
* Instruction codes *
|
||||||
****************************************/
|
****************************************/
|
||||||
public final static byte CLA = (byte)0xB0;
|
public final static byte CLA = (byte)0xB0;
|
||||||
// Applet initialization
|
// Applet initialization
|
||||||
public final static byte INS_SETUP = (byte) 0x2A;
|
public final static byte INS_SETUP = (byte) 0x2A;
|
||||||
// Keys' use and management
|
// Keys' use and management
|
||||||
public final static byte INS_IMPORT_KEY = (byte) 0x32;
|
public final static byte INS_IMPORT_KEY = (byte) 0x32;
|
||||||
public final static byte INS_RESET_KEY = (byte) 0x33;
|
public final static byte INS_RESET_KEY = (byte) 0x33;
|
||||||
public final static byte INS_GET_PUBLIC_FROM_PRIVATE= (byte)0x35;
|
public final static byte INS_GET_PUBLIC_FROM_PRIVATE= (byte)0x35;
|
||||||
// External authentication
|
// External authentication
|
||||||
public final static byte INS_CREATE_PIN = (byte) 0x40; //TODO: remove?
|
public final static byte INS_CREATE_PIN = (byte) 0x40; //TODO: remove?
|
||||||
public final static byte INS_VERIFY_PIN = (byte) 0x42;
|
public final static byte INS_VERIFY_PIN = (byte) 0x42;
|
||||||
public final static byte INS_CHANGE_PIN = (byte) 0x44;
|
public final static byte INS_CHANGE_PIN = (byte) 0x44;
|
||||||
public final static byte INS_UNBLOCK_PIN = (byte) 0x46;
|
public final static byte INS_UNBLOCK_PIN = (byte) 0x46;
|
||||||
public final static byte INS_LOGOUT_ALL = (byte) 0x60;
|
public final static byte INS_LOGOUT_ALL = (byte) 0x60;
|
||||||
// Status information
|
// Status information
|
||||||
public final static byte INS_LIST_PINS = (byte) 0x48;
|
public final static byte INS_LIST_PINS = (byte) 0x48;
|
||||||
public final static byte INS_GET_STATUS = (byte) 0x3C;
|
public final static byte INS_GET_STATUS = (byte) 0x3C;
|
||||||
public final static byte INS_CARD_LABEL = (byte) 0x3D;
|
public final static byte INS_CARD_LABEL = (byte) 0x3D;
|
||||||
// HD wallet
|
// HD wallet
|
||||||
public final static byte INS_BIP32_IMPORT_SEED= (byte) 0x6C;
|
public final static byte INS_BIP32_IMPORT_SEED= (byte) 0x6C;
|
||||||
public final static byte INS_BIP32_RESET_SEED= (byte) 0x77;
|
public final static byte INS_BIP32_RESET_SEED= (byte) 0x77;
|
||||||
public final static byte INS_BIP32_GET_AUTHENTIKEY= (byte) 0x73;
|
public final static byte INS_BIP32_GET_AUTHENTIKEY= (byte) 0x73;
|
||||||
public final static byte INS_BIP32_SET_AUTHENTIKEY_PUBKEY= (byte)0x75;
|
public final static byte INS_BIP32_SET_AUTHENTIKEY_PUBKEY= (byte)0x75;
|
||||||
public final static byte INS_BIP32_GET_EXTENDED_KEY= (byte) 0x6D;
|
public final static byte INS_BIP32_GET_EXTENDED_KEY= (byte) 0x6D;
|
||||||
public final static byte INS_BIP32_SET_EXTENDED_PUBKEY= (byte) 0x74;
|
public final static byte INS_BIP32_SET_EXTENDED_PUBKEY= (byte) 0x74;
|
||||||
public final static byte INS_SIGN_MESSAGE= (byte) 0x6E;
|
public final static byte INS_SIGN_MESSAGE= (byte) 0x6E;
|
||||||
public final static byte INS_SIGN_SHORT_MESSAGE= (byte) 0x72;
|
public final static byte INS_SIGN_SHORT_MESSAGE= (byte) 0x72;
|
||||||
public final static byte INS_SIGN_TRANSACTION= (byte) 0x6F;
|
public final static byte INS_SIGN_TRANSACTION= (byte) 0x6F;
|
||||||
public final static byte INS_PARSE_TRANSACTION = (byte) 0x71;
|
public final static byte INS_PARSE_TRANSACTION = (byte) 0x71;
|
||||||
public final static byte INS_CRYPT_TRANSACTION_2FA = (byte) 0x76;
|
public final static byte INS_CRYPT_TRANSACTION_2FA = (byte) 0x76;
|
||||||
public final static byte INS_SET_2FA_KEY = (byte) 0x79;
|
public final static byte INS_SET_2FA_KEY = (byte) 0x79;
|
||||||
public final static byte INS_RESET_2FA_KEY = (byte) 0x78;
|
public final static byte INS_RESET_2FA_KEY = (byte) 0x78;
|
||||||
public final static byte INS_SIGN_TRANSACTION_HASH= (byte) 0x7A;
|
public final static byte INS_SIGN_TRANSACTION_HASH= (byte) 0x7A;
|
||||||
// secure channel
|
// secure channel
|
||||||
public final static byte INS_INIT_SECURE_CHANNEL = (byte) 0x81;
|
public final static byte INS_INIT_SECURE_CHANNEL = (byte) 0x81;
|
||||||
public final static byte INS_PROCESS_SECURE_CHANNEL = (byte) 0x82;
|
public final static byte INS_PROCESS_SECURE_CHANNEL = (byte) 0x82;
|
||||||
// secure import from SeedKeeper
|
// secure import from SeedKeeper
|
||||||
public final static byte INS_IMPORT_ENCRYPTED_SECRET = (byte) 0xAC;
|
public final static byte INS_IMPORT_ENCRYPTED_SECRET = (byte) 0xAC;
|
||||||
public final static byte INS_IMPORT_TRUSTED_PUBKEY = (byte) 0xAA;
|
public final static byte INS_IMPORT_TRUSTED_PUBKEY = (byte) 0xAA;
|
||||||
public final static byte INS_EXPORT_TRUSTED_PUBKEY = (byte) 0xAB;
|
public final static byte INS_EXPORT_TRUSTED_PUBKEY = (byte) 0xAB;
|
||||||
public final static byte INS_EXPORT_AUTHENTIKEY= (byte) 0xAD;
|
public final static byte INS_EXPORT_AUTHENTIKEY= (byte) 0xAD;
|
||||||
// Personalization PKI support
|
// Personalization PKI support
|
||||||
public final static byte INS_IMPORT_PKI_CERTIFICATE = (byte) 0x92;
|
public final static byte INS_IMPORT_PKI_CERTIFICATE = (byte) 0x92;
|
||||||
public final static byte INS_EXPORT_PKI_CERTIFICATE = (byte) 0x93;
|
public final static byte INS_EXPORT_PKI_CERTIFICATE = (byte) 0x93;
|
||||||
public final static byte INS_SIGN_PKI_CSR = (byte) 0x94;
|
public final static byte INS_SIGN_PKI_CSR = (byte) 0x94;
|
||||||
public final static byte INS_EXPORT_PKI_PUBKEY = (byte) 0x98;
|
public final static byte INS_EXPORT_PKI_PUBKEY = (byte) 0x98;
|
||||||
public final static byte INS_LOCK_PKI = (byte) 0x99;
|
public final static byte INS_LOCK_PKI = (byte) 0x99;
|
||||||
public final static byte INS_CHALLENGE_RESPONSE_PKI= (byte) 0x9A;
|
public final static byte INS_CHALLENGE_RESPONSE_PKI= (byte) 0x9A;
|
||||||
// reset to factory settings
|
// reset to factory settings
|
||||||
public final static byte INS_RESET_TO_FACTORY = (byte) 0xFF;
|
public final static byte INS_RESET_TO_FACTORY = (byte) 0xFF;
|
||||||
|
|
||||||
/****************************************
|
/****************************************
|
||||||
* Error codes *
|
* Error codes *
|
||||||
****************************************/
|
****************************************/
|
||||||
|
|
||||||
/** Entered PIN is not correct */
|
/** Entered PIN is not correct */
|
||||||
public final static short SW_PIN_FAILED = (short)0x63C0;// includes number of tries remaining
|
public final static short SW_PIN_FAILED = (short)0x63C0;// includes number of tries remaining
|
||||||
///** DEPRECATED - Entered PIN is not correct */
|
///** DEPRECATED - Entered PIN is not correct */
|
||||||
//public final static short SW_AUTH_FAILED = (short) 0x9C02;
|
//public final static short SW_AUTH_FAILED = (short) 0x9C02;
|
||||||
/** Required operation is not allowed in actual circumstances */
|
/** Required operation is not allowed in actual circumstances */
|
||||||
public final static short SW_OPERATION_NOT_ALLOWED = (short) 0x9C03;
|
public final static short SW_OPERATION_NOT_ALLOWED = (short) 0x9C03;
|
||||||
/** Required setup is not not done */
|
/** Required setup is not not done */
|
||||||
public final static short SW_SETUP_NOT_DONE = (short) 0x9C04;
|
public final static short SW_SETUP_NOT_DONE = (short) 0x9C04;
|
||||||
/** Required setup is already done */
|
/** Required setup is already done */
|
||||||
public final static short SW_SETUP_ALREADY_DONE = (short) 0x9C07;
|
public final static short SW_SETUP_ALREADY_DONE = (short) 0x9C07;
|
||||||
/** Required feature is not (yet) supported */
|
/** Required feature is not (yet) supported */
|
||||||
final static short SW_UNSUPPORTED_FEATURE = (short) 0x9C05;
|
final static short SW_UNSUPPORTED_FEATURE = (short) 0x9C05;
|
||||||
/** Required operation was not authorized because of a lack of privileges */
|
/** Required operation was not authorized because of a lack of privileges */
|
||||||
public final static short SW_UNAUTHORIZED = (short) 0x9C06;
|
public final static short SW_UNAUTHORIZED = (short) 0x9C06;
|
||||||
/** Algorithm specified is not correct */
|
/** Algorithm specified is not correct */
|
||||||
public final static short SW_INCORRECT_ALG = (short) 0x9C09;
|
public final static short SW_INCORRECT_ALG = (short) 0x9C09;
|
||||||
|
|
||||||
/** There have been memory problems on the card */
|
/** There have been memory problems on the card */
|
||||||
public final static short SW_NO_MEMORY_LEFT = (short) 0x9C01;
|
public final static short SW_NO_MEMORY_LEFT = (short) 0x9C01;
|
||||||
///** DEPRECATED - Required object is missing */
|
///** DEPRECATED - Required object is missing */
|
||||||
//public final static short SW_OBJECT_NOT_FOUND= (short) 0x9C07;
|
//public final static short SW_OBJECT_NOT_FOUND= (short) 0x9C07;
|
||||||
|
|
||||||
/** Incorrect P1 parameter */
|
/** Incorrect P1 parameter */
|
||||||
public final static short SW_INCORRECT_P1 = (short) 0x9C10;
|
public final static short SW_INCORRECT_P1 = (short) 0x9C10;
|
||||||
/** Incorrect P2 parameter */
|
/** Incorrect P2 parameter */
|
||||||
public final static short SW_INCORRECT_P2 = (short) 0x9C11;
|
public final static short SW_INCORRECT_P2 = (short) 0x9C11;
|
||||||
/** Invalid input parameter to command */
|
/** Invalid input parameter to command */
|
||||||
public final static short SW_INVALID_PARAMETER = (short) 0x9C0F;
|
public final static short SW_INVALID_PARAMETER = (short) 0x9C0F;
|
||||||
|
|
||||||
/** Eckeys initialized */
|
/** Eckeys initialized */
|
||||||
public final static short SW_ECKEYS_INITIALIZED_KEY = (short) 0x9C1A;
|
public final static short SW_ECKEYS_INITIALIZED_KEY = (short) 0x9C1A;
|
||||||
|
|
||||||
/** Verify operation detected an invalid signature */
|
/** Verify operation detected an invalid signature */
|
||||||
public final static short SW_SIGNATURE_INVALID = (short) 0x9C0B;
|
public final static short SW_SIGNATURE_INVALID = (short) 0x9C0B;
|
||||||
/** Operation has been blocked for security reason */
|
/** Operation has been blocked for security reason */
|
||||||
public final static short SW_IDENTITY_BLOCKED = (short) 0x9C0C;
|
public final static short SW_IDENTITY_BLOCKED = (short) 0x9C0C;
|
||||||
/** For debugging purposes */
|
/** For debugging purposes */
|
||||||
public final static short SW_INTERNAL_ERROR = (short) 0x9CFF;
|
public final static short SW_INTERNAL_ERROR = (short) 0x9CFF;
|
||||||
/** Very low probability error */
|
/** Very low probability error */
|
||||||
public final static short SW_BIP32_DERIVATION_ERROR = (short) 0x9C0E;
|
public final static short SW_BIP32_DERIVATION_ERROR = (short) 0x9C0E;
|
||||||
/** Incorrect initialization of method */
|
/** Incorrect initialization of method */
|
||||||
public final static short SW_INCORRECT_INITIALIZATION = (short) 0x9C13;
|
public final static short SW_INCORRECT_INITIALIZATION = (short) 0x9C13;
|
||||||
/** Bip32 seed is not initialized*/
|
/** Bip32 seed is not initialized*/
|
||||||
public final static short SW_BIP32_UNINITIALIZED_SEED = (short) 0x9C14;
|
public final static short SW_BIP32_UNINITIALIZED_SEED = (short) 0x9C14;
|
||||||
/** Bip32 seed is already initialized (must be reset before change)*/
|
/** Bip32 seed is already initialized (must be reset before change)*/
|
||||||
public final static short SW_BIP32_INITIALIZED_SEED = (short) 0x9C17;
|
public final static short SW_BIP32_INITIALIZED_SEED = (short) 0x9C17;
|
||||||
//** DEPRECATED - Bip32 authentikey pubkey is not initialized*/
|
//** DEPRECATED - Bip32 authentikey pubkey is not initialized*/
|
||||||
//public final static short SW_BIP32_UNINITIALIZED_AUTHENTIKEY_PUBKEY= (short) 0x9C16;
|
//public final static short SW_BIP32_UNINITIALIZED_AUTHENTIKEY_PUBKEY= (short) 0x9C16;
|
||||||
/** Incorrect transaction hash */
|
/** Incorrect transaction hash */
|
||||||
public final static short SW_INCORRECT_TXHASH = (short) 0x9C15;
|
public final static short SW_INCORRECT_TXHASH = (short) 0x9C15;
|
||||||
|
|
||||||
/** 2FA already initialized*/
|
/** 2FA already initialized*/
|
||||||
public final static short SW_2FA_INITIALIZED_KEY = (short) 0x9C18;
|
public final static short SW_2FA_INITIALIZED_KEY = (short) 0x9C18;
|
||||||
/** 2FA uninitialized*/
|
/** 2FA uninitialized*/
|
||||||
public final static short SW_2FA_UNINITIALIZED_KEY = (short) 0x9C19;
|
public final static short SW_2FA_UNINITIALIZED_KEY = (short) 0x9C19;
|
||||||
|
|
||||||
/** HMAC errors */
|
/** HMAC errors */
|
||||||
static final short SW_HMAC_UNSUPPORTED_KEYSIZE = (short) 0x9c1E;
|
static final short SW_HMAC_UNSUPPORTED_KEYSIZE = (short) 0x9c1E;
|
||||||
static final short SW_HMAC_UNSUPPORTED_MSGSIZE = (short) 0x9c1F;
|
static final short SW_HMAC_UNSUPPORTED_MSGSIZE = (short) 0x9c1F;
|
||||||
|
|
||||||
/** Secure channel */
|
/** Secure channel */
|
||||||
public final static short SW_SECURE_CHANNEL_REQUIRED = (short) 0x9C20;
|
public final static short SW_SECURE_CHANNEL_REQUIRED = (short) 0x9C20;
|
||||||
public final static short SW_SECURE_CHANNEL_UNINITIALIZED = (short) 0x9C21;
|
public final static short SW_SECURE_CHANNEL_UNINITIALIZED = (short) 0x9C21;
|
||||||
public final static short SW_SECURE_CHANNEL_WRONG_IV= (short) 0x9C22;
|
public final static short SW_SECURE_CHANNEL_WRONG_IV= (short) 0x9C22;
|
||||||
public final static short SW_SECURE_CHANNEL_WRONG_MAC= (short) 0x9C23;
|
public final static short SW_SECURE_CHANNEL_WRONG_MAC= (short) 0x9C23;
|
||||||
|
|
||||||
/** Secret data is too long for import **/
|
/** Secret data is too long for import **/
|
||||||
public final static short SW_IMPORTED_DATA_TOO_LONG = (short) 0x9C32;
|
public final static short SW_IMPORTED_DATA_TOO_LONG = (short) 0x9C32;
|
||||||
/** Wrong HMAC when importing Secret through Secure import **/
|
/** Wrong HMAC when importing Secret through Secure import **/
|
||||||
public final static short SW_SECURE_IMPORT_WRONG_MAC = (short) 0x9C33;
|
public final static short SW_SECURE_IMPORT_WRONG_MAC = (short) 0x9C33;
|
||||||
/** Wrong Fingerprint when importing Secret through Secure import **/
|
/** Wrong Fingerprint when importing Secret through Secure import **/
|
||||||
public final static short SW_SECURE_IMPORT_WRONG_FINGERPRINT = (short) 0x9C34;
|
public final static short SW_SECURE_IMPORT_WRONG_FINGERPRINT = (short) 0x9C34;
|
||||||
/** No Trusted Pubkey when importing Secret through Secure import **/
|
/** No Trusted Pubkey when importing Secret through Secure import **/
|
||||||
public final static short SW_SECURE_IMPORT_NO_TRUSTEDPUBKEY = (short) 0x9C35;
|
public final static short SW_SECURE_IMPORT_NO_TRUSTEDPUBKEY = (short) 0x9C35;
|
||||||
|
|
||||||
/** PKI perso error */
|
/** PKI perso error */
|
||||||
public final static short SW_PKI_ALREADY_LOCKED = (short) 0x9C40;
|
public final static short SW_PKI_ALREADY_LOCKED = (short) 0x9C40;
|
||||||
/** CARD HAS BEEN RESET TO FACTORY */
|
/** CARD HAS BEEN RESET TO FACTORY */
|
||||||
public final static short SW_RESET_TO_FACTORY = (short) 0xFF00;
|
public final static short SW_RESET_TO_FACTORY = (short) 0xFF00;
|
||||||
/** For instructions that have been deprecated*/
|
/** For instructions that have been deprecated*/
|
||||||
public final static short SW_INS_DEPRECATED = (short) 0x9C26;
|
public final static short SW_INS_DEPRECATED = (short) 0x9C26;
|
||||||
/** For debugging purposes 2 */
|
/** For debugging purposes 2 */
|
||||||
public final static short SW_DEBUG_FLAG = (short) 0x9FFF;
|
public final static short SW_DEBUG_FLAG = (short) 0x9FFF;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,97 +6,97 @@ import java.util.StringTokenizer;
|
||||||
* Keypath object to be used with the SatochipCommandSet
|
* Keypath object to be used with the SatochipCommandSet
|
||||||
*/
|
*/
|
||||||
public class KeyPath {
|
public class KeyPath {
|
||||||
private byte[] data;
|
private byte[] data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses a keypath into a byte array to be used with the SatochipCommandSet object.
|
* Parses a keypath into a byte array to be used with the SatochipCommandSet object.
|
||||||
*
|
*
|
||||||
* A valid string is composed of a minimum of one and a maximum of 11 components separated by "/".
|
* A valid string is composed of a minimum of one and a maximum of 11 components separated by "/".
|
||||||
*
|
*
|
||||||
* The first component should be "m", indicating the master key.
|
* The first component should be "m", indicating the master key.
|
||||||
*
|
*
|
||||||
* All other components are positive integers fitting in 31 bit, eventually suffixed by an apostrophe (') sign,
|
* All other components are positive integers fitting in 31 bit, eventually suffixed by an apostrophe (') sign,
|
||||||
* which indicates an hardened key.
|
* which indicates an hardened key.
|
||||||
*
|
*
|
||||||
* An example of a valid path is "m/44'/0'/0'/0/0"
|
* An example of a valid path is "m/44'/0'/0'/0/0"
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @param keypath the keypath as a string
|
* @param keypath the keypath as a string
|
||||||
*/
|
*/
|
||||||
public KeyPath(String keypath) {
|
public KeyPath(String keypath) {
|
||||||
StringTokenizer tokenizer = new StringTokenizer(keypath, "/");
|
StringTokenizer tokenizer = new StringTokenizer(keypath, "/");
|
||||||
|
|
||||||
String sourceOrFirstElement = tokenizer.nextToken(); // m
|
String sourceOrFirstElement = tokenizer.nextToken(); // m
|
||||||
|
|
||||||
int componentCount = tokenizer.countTokens();
|
int componentCount = tokenizer.countTokens();
|
||||||
if (componentCount > 10) {
|
if (componentCount > 10) {
|
||||||
throw new IllegalArgumentException("Too many components");
|
throw new IllegalArgumentException("Too many components");
|
||||||
|
}
|
||||||
|
|
||||||
|
data = new byte[4 * componentCount];
|
||||||
|
|
||||||
|
for (int i = 0; i < componentCount; i++) {
|
||||||
|
long component = parseComponent(tokenizer.nextToken());
|
||||||
|
writeComponent(component, i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data = new byte[4 * componentCount];
|
public KeyPath(byte[] data) {
|
||||||
|
this.data = data;
|
||||||
for (int i = 0; i < componentCount; i++) {
|
|
||||||
long component = parseComponent(tokenizer.nextToken());
|
|
||||||
writeComponent(component, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public KeyPath(byte[] data) {
|
|
||||||
this.data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
private long parseComponent(String num) {
|
|
||||||
long sign;
|
|
||||||
|
|
||||||
if (num.endsWith("'")) {
|
|
||||||
sign = 0x80000000L;
|
|
||||||
num = num.substring(0, (num.length() - 1));
|
|
||||||
} else {
|
|
||||||
sign = 0L;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (num.startsWith("+") || num.startsWith("-")) {
|
private long parseComponent(String num) {
|
||||||
throw new NumberFormatException("No sign allowed");
|
long sign;
|
||||||
}
|
|
||||||
return (sign | Long.parseLong(num));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeComponent(long component, int i) {
|
if (num.endsWith("'")) {
|
||||||
int off = (i*4);
|
sign = 0x80000000L;
|
||||||
data[off] = (byte)((component >> 24) & 0xff);
|
num = num.substring(0, (num.length() - 1));
|
||||||
data[off + 1] = (byte)((component >> 16) & 0xff);
|
} else {
|
||||||
data[off + 2] = (byte)((component >> 8) & 0xff);
|
sign = 0L;
|
||||||
data[off + 3] = (byte)(component & 0xff);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
if (num.startsWith("+") || num.startsWith("-")) {
|
||||||
* The byte encoded key path.
|
throw new NumberFormatException("No sign allowed");
|
||||||
*
|
}
|
||||||
* @return byte encoded key path
|
return (sign | Long.parseLong(num));
|
||||||
*/
|
|
||||||
public byte[] getData() {
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
StringBuffer sb = new StringBuffer();
|
|
||||||
sb.append('m');
|
|
||||||
|
|
||||||
for (int i = 0; i < this.data.length; i += 4) {
|
|
||||||
sb.append('/');
|
|
||||||
appendComponent(sb, i);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return sb.toString();
|
private void writeComponent(long component, int i) {
|
||||||
}
|
int off = (i*4);
|
||||||
|
data[off] = (byte)((component >> 24) & 0xff);
|
||||||
private void appendComponent(StringBuffer sb, int i) {
|
data[off + 1] = (byte)((component >> 16) & 0xff);
|
||||||
int num = ((this.data[i] & 0x7f) << 24) | ((this.data[i+1] & 0xff) << 16) | ((this.data[i+2] & 0xff) << 8) | (this.data[i+3] & 0xff);
|
data[off + 2] = (byte)((component >> 8) & 0xff);
|
||||||
sb.append(num);
|
data[off + 3] = (byte)(component & 0xff);
|
||||||
|
}
|
||||||
if ((this.data[i] & 0x80) == 0x80) {
|
|
||||||
sb.append('\'');
|
/**
|
||||||
|
* The byte encoded key path.
|
||||||
|
*
|
||||||
|
* @return byte encoded key path
|
||||||
|
*/
|
||||||
|
public byte[] getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
sb.append('m');
|
||||||
|
|
||||||
|
for (int i = 0; i < this.data.length; i += 4) {
|
||||||
|
sb.append('/');
|
||||||
|
appendComponent(sb, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void appendComponent(StringBuffer sb, int i) {
|
||||||
|
int num = ((this.data[i] & 0x7f) << 24) | ((this.data[i+1] & 0xff) << 16) | ((this.data[i+2] & 0xff) << 8) | (this.data[i+3] & 0xff);
|
||||||
|
sb.append(num);
|
||||||
|
|
||||||
|
if ((this.data[i] & 0x80) == 0x80) {
|
||||||
|
sb.append('\'');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ public class SatoCardApi extends CardApi {
|
||||||
log.debug("SATOCHIP SatoCardApi initialize() START");
|
log.debug("SATOCHIP SatoCardApi initialize() START");
|
||||||
// TODO check device certificate
|
// TODO check device certificate
|
||||||
SatoCardStatus cardStatus = this.getStatus();
|
SatoCardStatus cardStatus = this.getStatus();
|
||||||
|
|
||||||
APDUResponse rapdu;
|
APDUResponse rapdu;
|
||||||
if (!cardStatus.isSetupDone()){
|
if (!cardStatus.isSetupDone()){
|
||||||
byte maxPinTries = 5;
|
byte maxPinTries = 5;
|
||||||
|
|
@ -134,7 +134,7 @@ public class SatoCardApi extends CardApi {
|
||||||
String derivationString = "m";
|
String derivationString = "m";
|
||||||
for(int i=0;i<derivation.size();i++){
|
for(int i=0;i<derivation.size();i++){
|
||||||
derivationString += "/" + derivation.get(i).toString();
|
derivationString += "/" + derivation.get(i).toString();
|
||||||
}
|
}
|
||||||
log.debug("SATOCHIP SatoCardApi setDerivation() derivationString: " + derivationString);
|
log.debug("SATOCHIP SatoCardApi setDerivation() derivationString: " + derivationString);
|
||||||
// this basePath will be used when deriving keys
|
// this basePath will be used when deriving keys
|
||||||
this.basePath = KeyDerivation.writePath(derivation);
|
this.basePath = KeyDerivation.writePath(derivation);
|
||||||
|
|
@ -157,8 +157,8 @@ public class SatoCardApi extends CardApi {
|
||||||
|
|
||||||
/* todo: provide derivation path?
|
/* todo: provide derivation path?
|
||||||
* Satochip derives BIP32 keys based on the fullPath (from masterseed to leaf), not the partial path from a given xpub.
|
* Satochip derives BIP32 keys based on the fullPath (from masterseed to leaf), not the partial path from a given xpub.
|
||||||
* the basePath (from masterseed to xpub) is only provided in Satochip.java:getKeystore(String pin, List<ChildNumber> derivation, StringProperty messageProperty)
|
* the basePath (from masterseed to xpub) is only provided in Satochip.java:getKeystore(String pin, List<ChildNumber> derivation, StringProperty messageProperty)
|
||||||
* In SatoCardApi:getKeystore(), no derivation path (i.e. basePath from masterSeed to xpub or relative path) is given and no derivation is reliably available as a object field.
|
* In SatoCardApi:getKeystore(), no derivation path (i.e. basePath from masterSeed to xpub or relative path) is given and no derivation is reliably available as a object field.
|
||||||
* currently, we try to get the path from this.basePath if available (or use a default value) but it's not reliable enough
|
* currently, we try to get the path from this.basePath if available (or use a default value) but it's not reliable enough
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -174,7 +174,7 @@ public class SatoCardApi extends CardApi {
|
||||||
ExtendedKey.Header xtype = Network.get().getXpubHeader();
|
ExtendedKey.Header xtype = Network.get().getXpubHeader();
|
||||||
String xpub = this.cardProtocol.cardBip32GetXpub(keyDerivationString, xtype);
|
String xpub = this.cardProtocol.cardBip32GetXpub(keyDerivationString, xtype);
|
||||||
log.debug("SATOCHIP SatoCardApi getKeystore() xpub: " + xpub);
|
log.debug("SATOCHIP SatoCardApi getKeystore() xpub: " + xpub);
|
||||||
|
|
||||||
ExtendedKey extendedKey = ExtendedKey.fromDescriptor(xpub);
|
ExtendedKey extendedKey = ExtendedKey.fromDescriptor(xpub);
|
||||||
log.debug("SATOCHIP SatoCardApi getKeystore() extendedKey: " + extendedKey);
|
log.debug("SATOCHIP SatoCardApi getKeystore() extendedKey: " + extendedKey);
|
||||||
|
|
||||||
|
|
@ -207,56 +207,14 @@ public class SatoCardApi extends CardApi {
|
||||||
log.debug("SATOCHIP SatoCardApi sign() wallet: " + wallet);
|
log.debug("SATOCHIP SatoCardApi sign() wallet: " + wallet);
|
||||||
log.debug("SATOCHIP SatoCardApi sign() psbt: " + psbt);
|
log.debug("SATOCHIP SatoCardApi sign() psbt: " + psbt);
|
||||||
|
|
||||||
// debug psbt
|
|
||||||
/*log.debug("SATOCHIP SatoCardApi sign() psbt.hasSignatures(): " + psbt.hasSignatures());
|
|
||||||
log.debug("SATOCHIP SatoCardApi sign() psbt.isSigned(): " + psbt.isSigned());
|
|
||||||
log.debug("SATOCHIP SatoCardApi sign() psbt.isFinalized(): " + psbt.isFinalized());
|
|
||||||
log.debug("SATOCHIP SatoCardApi sign() psbt.verifySignatures:");*/
|
|
||||||
/*try{
|
|
||||||
psbt.verifySignatures();
|
|
||||||
log.debug("SATOCHIP SatoCardApi sign() psbt signature verified!");
|
|
||||||
} catch(Exception e) {
|
|
||||||
log.debug("SATOCHIP SatoCardApi sign() failed to verify signatures with error: " + e);
|
|
||||||
}
|
|
||||||
log.debug("SATOCHIP SatoCardApi sign() psbt.parse(false):");
|
|
||||||
try{
|
|
||||||
psbt.parse(false);
|
|
||||||
log.debug("SATOCHIP SatoCardApi sign() psbt.parse(false) finished!");
|
|
||||||
} catch(Exception e) {
|
|
||||||
log.debug("SATOCHIP SatoCardApi sign() failed psbt.parse(false) with error: " + e);
|
|
||||||
}
|
|
||||||
log.debug("SATOCHIP SatoCardApi sign() psbt.parse(false) end:");
|
|
||||||
log.debug("SATOCHIP SatoCardApi sign() psbt.parse(true):");
|
|
||||||
try{
|
|
||||||
psbt.parse(true);
|
|
||||||
log.debug("SATOCHIP SatoCardApi sign() psbt.parse(true) finished!");
|
|
||||||
} catch(Exception e) {
|
|
||||||
log.debug("SATOCHIP SatoCardApi sign() failed psbt.parse(true) with error: " + e);
|
|
||||||
}
|
|
||||||
log.debug("SATOCHIP SatoCardApi sign() psbt.parse(true) end:");*/
|
|
||||||
// endbug psbt
|
|
||||||
|
|
||||||
Map<PSBTInput, WalletNode> signingNodes = wallet.getSigningNodes(psbt);
|
Map<PSBTInput, WalletNode> signingNodes = wallet.getSigningNodes(psbt);
|
||||||
//log.debug("SATOCHIP SatoCardApi sign() signingNodes: " + signingNodes);
|
//log.debug("SATOCHIP SatoCardApi sign() signingNodes: " + signingNodes);
|
||||||
for(PSBTInput psbtInput : psbt.getPsbtInputs()) {
|
for(PSBTInput psbtInput : psbt.getPsbtInputs()) {
|
||||||
if(!psbtInput.isSigned()) {
|
if(!psbtInput.isSigned()) {
|
||||||
WalletNode signingNode = signingNodes.get(psbtInput);
|
WalletNode signingNode = signingNodes.get(psbtInput);
|
||||||
log.debug("SATOCHIP SatoCardApi sign() signingNode: " + signingNode);
|
log.debug("SATOCHIP SatoCardApi sign() signingNode: " + signingNode);
|
||||||
log.debug("SATOCHIP SatoCardApi sign() signingNode.getDerivationPath(): " + signingNode.getDerivationPath()); // m/0/0
|
log.debug("SATOCHIP SatoCardApi sign() signingNode.getDerivationPath(): " + signingNode.getDerivationPath()); // m/0/0
|
||||||
try {
|
try {
|
||||||
/*// debug
|
|
||||||
Map<ECKey, KeyDerivation> mapPubKey = psbtInput.getDerivedPublicKeys();
|
|
||||||
log.debug("SATOCHIP SatoCardApi sign() mapPubKey.size(): " + mapPubKey.size());
|
|
||||||
log.debug("SATOCHIP SatoCardApi sign() mapPubKey: " + mapPubKey);
|
|
||||||
for (Map.Entry<ECKey, KeyDerivation> entry : mapPubKey.entrySet()) {
|
|
||||||
ECKey key = entry.getKey();
|
|
||||||
KeyDerivation value = entry.getValue();
|
|
||||||
log.debug("SATOCHIP SatoCardApi sign() mapPubKey pubkey: " + Utils.bytesToHex(key.getPubKey()));
|
|
||||||
log.debug("SATOCHIP SatoCardApi sign() mapPubKey derivation: " + value.getDerivationPath());
|
|
||||||
}
|
|
||||||
log.debug("SATOCHIP SatoCardApi sign() mapPubKey END");
|
|
||||||
// endbug*/
|
|
||||||
|
|
||||||
String fullPath= null;
|
String fullPath= null;
|
||||||
List<Keystore> keystores = wallet.getKeystores();
|
List<Keystore> keystores = wallet.getKeystores();
|
||||||
log.debug("SATOCHIP SatoCardApi sign() keystores.size(): " + keystores.size());
|
log.debug("SATOCHIP SatoCardApi sign() keystores.size(): " + keystores.size());
|
||||||
|
|
@ -276,23 +234,19 @@ public class SatoCardApi extends CardApi {
|
||||||
log.debug("SATOCHIP SatoCardApi sign() extendedPath: " + extendedPath);
|
log.debug("SATOCHIP SatoCardApi sign() extendedPath: " + extendedPath);
|
||||||
fullPath = basePath + extendedPath;
|
fullPath = basePath + extendedPath;
|
||||||
log.debug("SATOCHIP SatoCardApi sign() fullPath: " + fullPath);
|
log.debug("SATOCHIP SatoCardApi sign() fullPath: " + fullPath);
|
||||||
|
|
||||||
ECKey keystorePubkey = keystore.getPubKey(signingNode);
|
ECKey keystorePubkey = keystore.getPubKey(signingNode);
|
||||||
log.debug("SATOCHIP SatoCardApi sign() keystore.getPubKey(signingNode): " + Utils.bytesToHex(keystorePubkey.getPubKey()));
|
log.debug("SATOCHIP SatoCardApi sign() keystore.getPubKey(signingNode): " + Utils.bytesToHex(keystorePubkey.getPubKey()));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//psbtInput.printDebugInfo();
|
|
||||||
psbtInput.sign(new CardPSBTInputSigner(signingNode, fullPath));
|
psbtInput.sign(new CardPSBTInputSigner(signingNode, fullPath));
|
||||||
//psbtInput.printDebugInfo();
|
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
}
|
}
|
||||||
}// endif
|
}// endif
|
||||||
else {
|
else {
|
||||||
log.debug("SATOCHIP SatoCardApi sign() psbtInput already signed!");
|
log.debug("SATOCHIP SatoCardApi sign() psbtInput already signed!");
|
||||||
//psbtInput.printDebugInfo();
|
|
||||||
}
|
}
|
||||||
} // endfor
|
} // endfor
|
||||||
}
|
}
|
||||||
|
|
@ -313,7 +267,7 @@ public class SatoCardApi extends CardApi {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
APDUResponse rapdu0 = cardProtocol.cardVerifyPIN(0, pin);
|
APDUResponse rapdu0 = cardProtocol.cardVerifyPIN(0, pin);
|
||||||
|
|
||||||
// 2FA is optionnal, currently not supported in sparrow as it requires to send 2FA to a mobile app through a server.
|
// 2FA is optionnal, currently not supported in sparrow as it requires to send 2FA to a mobile app through a server.
|
||||||
SatoCardStatus cardStatus = this.getStatus();
|
SatoCardStatus cardStatus = this.getStatus();
|
||||||
if (cardStatus.needs2FA()){
|
if (cardStatus.needs2FA()){
|
||||||
|
|
@ -452,7 +406,7 @@ public class SatoCardApi extends CardApi {
|
||||||
try {
|
try {
|
||||||
log.debug("SATOCHIP SatoCardApi.CardPSBTInputSigner sign() fullPath:" + this.fullPath);
|
log.debug("SATOCHIP SatoCardApi.CardPSBTInputSigner sign() fullPath:" + this.fullPath);
|
||||||
log.debug("SATOCHIP SatoCardApi.CardPSBTInputSigner sign() signingNode.getDerivationPath():" + signingNode.getDerivationPath());
|
log.debug("SATOCHIP SatoCardApi.CardPSBTInputSigner sign() signingNode.getDerivationPath():" + signingNode.getDerivationPath());
|
||||||
|
|
||||||
// 2FA is optionnal, currently not supported in sparrow as it requires to send 2FA to a mobile app through a server.
|
// 2FA is optionnal, currently not supported in sparrow as it requires to send 2FA to a mobile app through a server.
|
||||||
SatoCardStatus cardStatus = getStatus();
|
SatoCardStatus cardStatus = getStatus();
|
||||||
if (cardStatus.needs2FA()){
|
if (cardStatus.needs2FA()){
|
||||||
|
|
@ -461,7 +415,7 @@ public class SatoCardApi extends CardApi {
|
||||||
|
|
||||||
// verify PIN
|
// verify PIN
|
||||||
APDUResponse rapdu0 = cardProtocol.cardVerifyPIN(0, pin);
|
APDUResponse rapdu0 = cardProtocol.cardVerifyPIN(0, pin);
|
||||||
|
|
||||||
// derive the correct key in satochip and recover pubkey
|
// derive the correct key in satochip and recover pubkey
|
||||||
APDUResponse rapdu = cardProtocol.cardBip32GetExtendedKey(fullPath);
|
APDUResponse rapdu = cardProtocol.cardBip32GetExtendedKey(fullPath);
|
||||||
SatochipParser parser= new SatochipParser();
|
SatochipParser parser= new SatochipParser();
|
||||||
|
|
@ -485,7 +439,7 @@ public class SatoCardApi extends CardApi {
|
||||||
boolean isCorrect = pubkey.verify(hash, txSig);
|
boolean isCorrect = pubkey.verify(hash, txSig);
|
||||||
log.debug("SATOCHIP SatoCardApi.CardPSBTInputSigner sign() ECDSA verify with pubkey: " + isCorrect);
|
log.debug("SATOCHIP SatoCardApi.CardPSBTInputSigner sign() ECDSA verify with pubkey: " + isCorrect);
|
||||||
|
|
||||||
return txSig;
|
return txSig;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Satochip supports schnorr signature only for version >= 0.14 !
|
// Satochip supports schnorr signature only for version >= 0.14 !
|
||||||
|
|
@ -510,14 +464,14 @@ public class SatoCardApi extends CardApi {
|
||||||
log.debug("SATOCHIP SatoCardApi.CardPSBTInputSigner sign() SCHNORR sigBytes: " + Utils.bytesToHex(sigBytes));
|
log.debug("SATOCHIP SatoCardApi.CardPSBTInputSigner sign() SCHNORR sigBytes: " + Utils.bytesToHex(sigBytes));
|
||||||
SchnorrSignature schnorrSig = SchnorrSignature.decode(sigBytes);
|
SchnorrSignature schnorrSig = SchnorrSignature.decode(sigBytes);
|
||||||
TransactionSignature txSig = new TransactionSignature(schnorrSig, sigHash);
|
TransactionSignature txSig = new TransactionSignature(schnorrSig, sigHash);
|
||||||
|
|
||||||
// verify sig with outputPubkey...
|
// verify sig with outputPubkey...
|
||||||
boolean isCorrect2 = pubkey.verify(hash, txSig);
|
boolean isCorrect2 = pubkey.verify(hash, txSig);
|
||||||
log.debug("SATOCHIP SatoCardApi.CardPSBTInputSigner sign() SCHNORR verify with outputPubkey: " + isCorrect2);
|
log.debug("SATOCHIP SatoCardApi.CardPSBTInputSigner sign() SCHNORR verify with outputPubkey: " + isCorrect2);
|
||||||
|
|
||||||
return txSig; //new TransactionSignature(schnorrSig, sigHash);
|
return txSig; //new TransactionSignature(schnorrSig, sigHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
log.error("SATOCHIP SatoCardApi.CardPSBTInputSigner sign() Exception: " + e);
|
log.error("SATOCHIP SatoCardApi.CardPSBTInputSigner sign() Exception: " + e);
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ package com.sparrowwallet.sparrow.io.satochip;
|
||||||
* Parses the result of a GET STATUS command retrieving application status.
|
* Parses the result of a GET STATUS command retrieving application status.
|
||||||
*/
|
*/
|
||||||
public class SatoCardStatus {
|
public class SatoCardStatus {
|
||||||
|
|
||||||
private boolean setup_done= false;
|
private boolean setup_done= false;
|
||||||
private boolean is_seeded= false;
|
private boolean is_seeded= false;
|
||||||
private boolean needs_secure_channel= false;
|
private boolean needs_secure_channel= false;
|
||||||
|
|
@ -22,7 +22,7 @@ public class SatoCardStatus {
|
||||||
private byte PIN1_remaining_tries= (byte)0;
|
private byte PIN1_remaining_tries= (byte)0;
|
||||||
private byte PUK1_remaining_tries= (byte)0;
|
private byte PUK1_remaining_tries= (byte)0;
|
||||||
|
|
||||||
private int protocol_version= 0; //(d["protocol_major_version"]<<8)+d["protocol_minor_version"]
|
private int protocol_version= 0; //(d["protocol_major_version"]<<8)+d["protocol_minor_version"]
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor from TLV data
|
* Constructor from TLV data
|
||||||
|
|
@ -30,18 +30,18 @@ public class SatoCardStatus {
|
||||||
* @throws IllegalArgumentException if the TLV does not follow the expected format
|
* @throws IllegalArgumentException if the TLV does not follow the expected format
|
||||||
*/
|
*/
|
||||||
public SatoCardStatus(APDUResponse rapdu) {
|
public SatoCardStatus(APDUResponse rapdu) {
|
||||||
|
|
||||||
int sw= rapdu.getSw();
|
int sw= rapdu.getSw();
|
||||||
|
|
||||||
if (sw==0x9000){
|
if (sw==0x9000){
|
||||||
|
|
||||||
byte[] data= rapdu.getData();
|
byte[] data= rapdu.getData();
|
||||||
protocol_major_version= data[0];
|
protocol_major_version= data[0];
|
||||||
protocol_minor_version= data[1];
|
protocol_minor_version= data[1];
|
||||||
applet_major_version= data[2];
|
applet_major_version= data[2];
|
||||||
applet_minor_version= data[3];
|
applet_minor_version= data[3];
|
||||||
protocol_version= (protocol_major_version<<8) + protocol_minor_version;
|
protocol_version= (protocol_major_version<<8) + protocol_minor_version;
|
||||||
|
|
||||||
if (data.length >=8){
|
if (data.length >=8){
|
||||||
PIN0_remaining_tries= data[4];
|
PIN0_remaining_tries= data[4];
|
||||||
PUK0_remaining_tries= data[5];
|
PUK0_remaining_tries= data[5];
|
||||||
|
|
@ -54,14 +54,14 @@ public class SatoCardStatus {
|
||||||
}
|
}
|
||||||
if (data.length >=10){
|
if (data.length >=10){
|
||||||
is_seeded= (data[9]==0X00)? false : true;
|
is_seeded= (data[9]==0X00)? false : true;
|
||||||
}
|
}
|
||||||
if (data.length >=11){
|
if (data.length >=11){
|
||||||
setup_done= (data[10]==0X00)? false : true;
|
setup_done= (data[10]==0X00)? false : true;
|
||||||
} else {
|
} else {
|
||||||
setup_done= true;
|
setup_done= true;
|
||||||
}
|
}
|
||||||
if (data.length >=12){
|
if (data.length >=12){
|
||||||
needs_secure_channel= (data[11]==0X00)? false : true;
|
needs_secure_channel= (data[11]==0X00)? false : true;
|
||||||
} else {
|
} else {
|
||||||
needs_secure_channel= false;
|
needs_secure_channel= false;
|
||||||
needs_2FA= false; //default value
|
needs_2FA= false; //default value
|
||||||
|
|
@ -74,7 +74,7 @@ public class SatoCardStatus {
|
||||||
//throws IllegalArgumentException("Wrong getStatus data!"); // should not happen
|
//throws IllegalArgumentException("Wrong getStatus data!"); // should not happen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getters
|
// getters
|
||||||
public boolean isSeeded() {
|
public boolean isSeeded() {
|
||||||
return is_seeded;
|
return is_seeded;
|
||||||
|
|
@ -98,7 +98,7 @@ public class SatoCardStatus {
|
||||||
public byte getPin0RemainingCounter(){
|
public byte getPin0RemainingCounter(){
|
||||||
return PIN0_remaining_tries;
|
return PIN0_remaining_tries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public byte[] getCardVersion(){
|
public byte[] getCardVersion(){
|
||||||
byte[] versionBytes= new byte[4];
|
byte[] versionBytes= new byte[4];
|
||||||
|
|
@ -120,5 +120,5 @@ public class SatoCardStatus {
|
||||||
"applet_minor_version: " + applet_minor_version;
|
"applet_minor_version: " + applet_minor_version;
|
||||||
return status_info;
|
return status_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,7 @@ public class SatoCardTransport {
|
||||||
// convert back to APDUResponse... (todo?)
|
// convert back to APDUResponse... (todo?)
|
||||||
APDUResponse rapdu = new APDUResponse(resp.getBytes());
|
APDUResponse rapdu = new APDUResponse(resp.getBytes());
|
||||||
log.trace("SATOCHIP SatoCardTransport send rapdu:" + rapdu.toHexString());
|
log.trace("SATOCHIP SatoCardTransport send rapdu:" + rapdu.toHexString());
|
||||||
return rapdu;
|
return rapdu;
|
||||||
}
|
}
|
||||||
|
|
||||||
void disconnect() throws CardException {
|
void disconnect() throws CardException {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ import javax.smartcardio.*;
|
||||||
* pre/post processing.
|
* pre/post processing.
|
||||||
*/
|
*/
|
||||||
public class SatochipCommandSet {
|
public class SatochipCommandSet {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(SatochipCommandSet.class);
|
private static final Logger log = LoggerFactory.getLogger(SatochipCommandSet.class);
|
||||||
|
|
||||||
private final SatoCardTransport cardTransport;
|
private final SatoCardTransport cardTransport;
|
||||||
|
|
@ -52,7 +52,7 @@ public class SatochipCommandSet {
|
||||||
this.secureChannel = new SecureChannelSession();
|
this.secureChannel = new SecureChannelSession();
|
||||||
this.parser= new SatochipParser();
|
this.parser= new SatochipParser();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the application info as stored from the last sent SELECT command. Returns null if no succesful SELECT
|
* Returns the application info as stored from the last sent SELECT command. Returns null if no succesful SELECT
|
||||||
* command has been sent using this command set.
|
* command has been sent using this command set.
|
||||||
|
|
@ -65,11 +65,11 @@ public class SatochipCommandSet {
|
||||||
}
|
}
|
||||||
return this.status;
|
return this.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************
|
/****************************************
|
||||||
* AUTHENTIKEY *
|
* AUTHENTIKEY *
|
||||||
****************************************/
|
****************************************/
|
||||||
|
|
||||||
public APDUResponse cardTransmit(APDUCommand plainApdu) {
|
public APDUResponse cardTransmit(APDUCommand plainApdu) {
|
||||||
log.trace("SATOCHIP: SatochipCommandSet cardTransmit() START");
|
log.trace("SATOCHIP: SatochipCommandSet cardTransmit() START");
|
||||||
|
|
||||||
|
|
@ -115,17 +115,17 @@ public class SatochipCommandSet {
|
||||||
// check answer
|
// check answer
|
||||||
if (sw12==0x9000){ // ok!
|
if (sw12==0x9000){ // ok!
|
||||||
if (isEncrypted){
|
if (isEncrypted){
|
||||||
// decrypt
|
// decrypt
|
||||||
//log.info("SATOCHIP Rapdu encrypted:"+ rapdu.toHexString());
|
//log.info("SATOCHIP Rapdu encrypted:"+ rapdu.toHexString());
|
||||||
rapdu = secureChannel.decrypt_secure_channel(rapdu);
|
rapdu = secureChannel.decrypt_secure_channel(rapdu);
|
||||||
//log.info("SATOCHIP Rapdu decrypted:"+ rapdu.toHexString());
|
//log.info("SATOCHIP Rapdu decrypted:"+ rapdu.toHexString());
|
||||||
}
|
}
|
||||||
isApduTransmitted= true; // leave loop
|
isApduTransmitted= true; // leave loop
|
||||||
return rapdu;
|
return rapdu;
|
||||||
}
|
}
|
||||||
// PIN authentication is required
|
// PIN authentication is required
|
||||||
else if (sw12==0x9C06){
|
else if (sw12==0x9C06){
|
||||||
//cardVerifyPIN();
|
//cardVerifyPIN();
|
||||||
log.error("SATOCHIP: SatochipCommandSet cardTransmit() sw12==0x9C06: PIN required!");
|
log.error("SATOCHIP: SatochipCommandSet cardTransmit() sw12==0x9C06: PIN required!");
|
||||||
//TODO: throw?
|
//TODO: throw?
|
||||||
//TODO: verify PIN?
|
//TODO: verify PIN?
|
||||||
|
|
@ -133,7 +133,7 @@ public class SatochipCommandSet {
|
||||||
// SecureChannel is not initialized
|
// SecureChannel is not initialized
|
||||||
else if (sw12==0x9C21){
|
else if (sw12==0x9C21){
|
||||||
log.error("SATOCHIP: SatochipCommandSet cardTransmit() sw12==0x9C21: secureChannel required!");
|
log.error("SATOCHIP: SatochipCommandSet cardTransmit() sw12==0x9C21: secureChannel required!");
|
||||||
secureChannel.resetSecureChannel();
|
secureChannel.resetSecureChannel();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// cannot resolve issue at this point
|
// cannot resolve issue at this point
|
||||||
|
|
@ -145,12 +145,12 @@ public class SatochipCommandSet {
|
||||||
log.warn("SATOCHIP: SatochipCommandSet cardTransmit() Exception: "+ e);
|
log.warn("SATOCHIP: SatochipCommandSet cardTransmit() Exception: "+ e);
|
||||||
return new APDUResponse(new byte[0], (byte)0x00, (byte)0x00); // return empty APDUResponse
|
return new APDUResponse(new byte[0], (byte)0x00, (byte)0x00); // return empty APDUResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
} while(!isApduTransmitted);
|
} while(!isApduTransmitted);
|
||||||
|
|
||||||
return new APDUResponse(new byte[0], (byte)0x00, (byte)0x00); // should not happen
|
return new APDUResponse(new byte[0], (byte)0x00, (byte)0x00); // should not happen
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cardDisconnect(){
|
public void cardDisconnect(){
|
||||||
secureChannel.resetSecureChannel();
|
secureChannel.resetSecureChannel();
|
||||||
status= null;
|
status= null;
|
||||||
|
|
@ -161,37 +161,37 @@ public class SatochipCommandSet {
|
||||||
log.error("SATOCHIP SatochipCommandSet cardDisconnect() Exception: " + e);
|
log.error("SATOCHIP SatochipCommandSet cardDisconnect() Exception: " + e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public APDUResponse cardGetStatus() {
|
public APDUResponse cardGetStatus() {
|
||||||
APDUCommand plainApdu = new APDUCommand(0xB0, INS_GET_STATUS, 0x00, 0x00, new byte[0]);
|
APDUCommand plainApdu = new APDUCommand(0xB0, INS_GET_STATUS, 0x00, 0x00, new byte[0]);
|
||||||
|
|
||||||
log.trace("SATOCHIP SatochipCommandSet cardGetStatus() capdu: "+ plainApdu.toHexString());
|
log.trace("SATOCHIP SatochipCommandSet cardGetStatus() capdu: "+ plainApdu.toHexString());
|
||||||
APDUResponse respApdu = this.cardTransmit(plainApdu);
|
APDUResponse respApdu = this.cardTransmit(plainApdu);
|
||||||
log.trace("SATOCHIP SatochipCommandSet cardGetStatus() rapdu: "+ respApdu.toHexString());
|
log.trace("SATOCHIP SatochipCommandSet cardGetStatus() rapdu: "+ respApdu.toHexString());
|
||||||
|
|
||||||
this.status= new SatoCardStatus(respApdu);
|
this.status= new SatoCardStatus(respApdu);
|
||||||
log.debug("SATOCHIP SatochipCommandSet cardGetStatus(): "+ this.status.toString());
|
log.debug("SATOCHIP SatochipCommandSet cardGetStatus(): "+ this.status.toString());
|
||||||
|
|
||||||
return respApdu;
|
return respApdu;
|
||||||
}
|
}
|
||||||
|
|
||||||
public APDUResponse cardInitiateSecureChannel() throws CardException{
|
public APDUResponse cardInitiateSecureChannel() throws CardException{
|
||||||
|
|
||||||
byte[] pubkey= secureChannel.getPublicKey();
|
byte[] pubkey= secureChannel.getPublicKey();
|
||||||
|
|
||||||
APDUCommand plainApdu = new APDUCommand(0xB0, INS_INIT_SECURE_CHANNEL, 0x00, 0x00, pubkey);
|
APDUCommand plainApdu = new APDUCommand(0xB0, INS_INIT_SECURE_CHANNEL, 0x00, 0x00, pubkey);
|
||||||
|
|
||||||
log.trace("SATOCHIP SatochipCommandSet cardInitiateSecureChannel capdu: "+ plainApdu.toHexString());
|
log.trace("SATOCHIP SatochipCommandSet cardInitiateSecureChannel capdu: "+ plainApdu.toHexString());
|
||||||
APDUResponse respApdu = this.cardTransport.send(plainApdu);
|
APDUResponse respApdu = this.cardTransport.send(plainApdu);
|
||||||
log.trace("SATOCHIP SatochipCommandSet cardInitiateSecureChannel rapdu: "+ respApdu.toHexString());
|
log.trace("SATOCHIP SatochipCommandSet cardInitiateSecureChannel rapdu: "+ respApdu.toHexString());
|
||||||
|
|
||||||
return respApdu;
|
return respApdu;
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************
|
/****************************************
|
||||||
* CARD MGMT *
|
* CARD MGMT *
|
||||||
****************************************/
|
****************************************/
|
||||||
|
|
||||||
public APDUResponse cardSetup(byte pin_tries0, byte[] pin0){
|
public APDUResponse cardSetup(byte pin_tries0, byte[] pin0){
|
||||||
log.debug("SATOCHIP SatochipCommandSet cardSetup()");
|
log.debug("SATOCHIP SatochipCommandSet cardSetup()");
|
||||||
// use random values for pin1, ublk0, ublk1
|
// use random values for pin1, ublk0, ublk1
|
||||||
|
|
@ -202,19 +202,19 @@ public class SatochipCommandSet {
|
||||||
random.nextBytes(ublk0);
|
random.nextBytes(ublk0);
|
||||||
random.nextBytes(ublk1);
|
random.nextBytes(ublk1);
|
||||||
random.nextBytes(pin1);
|
random.nextBytes(pin1);
|
||||||
|
|
||||||
byte ublk_tries0=(byte)0x01;
|
byte ublk_tries0=(byte)0x01;
|
||||||
byte ublk_tries1=(byte)0x01;
|
byte ublk_tries1=(byte)0x01;
|
||||||
byte pin_tries1=(byte)0x01;
|
byte pin_tries1=(byte)0x01;
|
||||||
|
|
||||||
return cardSetup(pin_tries0, ublk_tries0, pin0, ublk0, pin_tries1, ublk_tries1, pin1, ublk1);
|
return cardSetup(pin_tries0, ublk_tries0, pin0, ublk0, pin_tries1, ublk_tries1, pin1, ublk1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public APDUResponse cardSetup(
|
public APDUResponse cardSetup(
|
||||||
byte pin_tries0, byte ublk_tries0, byte[] pin0, byte[] ublk0,
|
byte pin_tries0, byte ublk_tries0, byte[] pin0, byte[] ublk0,
|
||||||
byte pin_tries1, byte ublk_tries1, byte[] pin1, byte[] ublk1){
|
byte pin_tries1, byte ublk_tries1, byte[] pin1, byte[] ublk1){
|
||||||
log.debug("SATOCHIP SatochipCommandSet cardSetup()");
|
log.debug("SATOCHIP SatochipCommandSet cardSetup()");
|
||||||
|
|
||||||
byte[] pin={0x4D, 0x75, 0x73, 0x63, 0x6C, 0x65, 0x30, 0x30}; //default pin
|
byte[] pin={0x4D, 0x75, 0x73, 0x63, 0x6C, 0x65, 0x30, 0x30}; //default pin
|
||||||
byte cla= (byte) 0xB0;
|
byte cla= (byte) 0xB0;
|
||||||
byte ins= INS_SETUP;
|
byte ins= INS_SETUP;
|
||||||
|
|
@ -269,25 +269,25 @@ public class SatochipCommandSet {
|
||||||
log.trace("SATOCHIP SatochipCommandSet cardSetup capdu: "+ plainApdu.toHexString());
|
log.trace("SATOCHIP SatochipCommandSet cardSetup capdu: "+ plainApdu.toHexString());
|
||||||
APDUResponse respApdu = this.cardTransmit(plainApdu);
|
APDUResponse respApdu = this.cardTransmit(plainApdu);
|
||||||
log.trace("SATOCHIP SatochipCommandSet cardSetup rapdu:"+ respApdu.toHexString());
|
log.trace("SATOCHIP SatochipCommandSet cardSetup rapdu:"+ respApdu.toHexString());
|
||||||
|
|
||||||
if (respApdu.getSw() == 0x9000){
|
if (respApdu.getSw() == 0x9000){
|
||||||
//setPin0(pin0); // todo: cache value...
|
//setPin0(pin0); // todo: cache value...
|
||||||
} else {
|
} else {
|
||||||
log.error("SATOCHIP SatochipCommandSet cardSetup error:"+ respApdu.toHexString());
|
log.error("SATOCHIP SatochipCommandSet cardSetup error:"+ respApdu.toHexString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return respApdu;
|
return respApdu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/****************************************
|
/****************************************
|
||||||
* PIN MGMT *
|
* PIN MGMT *
|
||||||
****************************************/
|
****************************************/
|
||||||
|
|
||||||
public APDUResponse cardVerifyPIN() {
|
public APDUResponse cardVerifyPIN() {
|
||||||
return this.cardVerifyPIN((byte)0, pinCached);
|
return this.cardVerifyPIN((byte)0, pinCached);
|
||||||
}
|
}
|
||||||
|
|
||||||
public APDUResponse cardVerifyPIN(int pinNbr, String pin) {
|
public APDUResponse cardVerifyPIN(int pinNbr, String pin) {
|
||||||
log.debug("SATOCHIP SatochipCommandSet cardVerifyPIN()");
|
log.debug("SATOCHIP SatochipCommandSet cardVerifyPIN()");
|
||||||
if (pin == null){
|
if (pin == null){
|
||||||
|
|
@ -296,34 +296,34 @@ public class SatochipCommandSet {
|
||||||
throw new RuntimeException("PIN required!");
|
throw new RuntimeException("PIN required!");
|
||||||
}
|
}
|
||||||
pin = this.pinCached;
|
pin = this.pinCached;
|
||||||
}
|
}
|
||||||
byte[] pinBytes = pin.getBytes(StandardCharsets.UTF_8);
|
byte[] pinBytes = pin.getBytes(StandardCharsets.UTF_8);
|
||||||
|
|
||||||
APDUCommand capdu = new APDUCommand(0xB0, INS_VERIFY_PIN, (byte)pinNbr, 0x00, pinBytes);
|
APDUCommand capdu = new APDUCommand(0xB0, INS_VERIFY_PIN, (byte)pinNbr, 0x00, pinBytes);
|
||||||
log.trace("SATOCHIP SatochipCommandSet cardVerifyPIN() capdu:"+ capdu.toHexString());
|
log.trace("SATOCHIP SatochipCommandSet cardVerifyPIN() capdu:"+ capdu.toHexString());
|
||||||
APDUResponse rapdu = this.cardTransmit(capdu);
|
APDUResponse rapdu = this.cardTransmit(capdu);
|
||||||
log.trace("SATOCHIP SatochipCommandSet cardVerifyPIN() rapdu: "+ rapdu.toHexString());
|
log.trace("SATOCHIP SatochipCommandSet cardVerifyPIN() rapdu: "+ rapdu.toHexString());
|
||||||
|
|
||||||
// correct PIN: cache PIN value
|
// correct PIN: cache PIN value
|
||||||
int sw = rapdu.getSw();
|
int sw = rapdu.getSw();
|
||||||
if (sw == 0x9000){
|
if (sw == 0x9000){
|
||||||
this.pinCached = pin; //set cached PIN value
|
this.pinCached = pin; //set cached PIN value
|
||||||
}
|
}
|
||||||
// wrong PIN, get remaining tries available (since v0.11)
|
// wrong PIN, get remaining tries available (since v0.11)
|
||||||
else if ((sw & 0xffc0) == 0x63c0){
|
else if ((sw & 0xffc0) == 0x63c0){
|
||||||
this.pinCached = null; //reset cached PIN value
|
this.pinCached = null; //reset cached PIN value
|
||||||
int pinLeft= (sw & ~0xffc0);
|
int pinLeft= (sw & ~0xffc0);
|
||||||
throw new RuntimeException("Wrong PIN, remaining tries: " + pinLeft);
|
throw new RuntimeException("Wrong PIN, remaining tries: " + pinLeft);
|
||||||
}
|
}
|
||||||
// wrong PIN (legacy before v0.11)
|
// wrong PIN (legacy before v0.11)
|
||||||
else if (sw == 0x9c02){
|
else if (sw == 0x9c02){
|
||||||
this.pinCached = null; //reset cached PIN value
|
this.pinCached = null; //reset cached PIN value
|
||||||
SatoCardStatus cardStatus = this.getApplicationStatus();
|
SatoCardStatus cardStatus = this.getApplicationStatus();
|
||||||
int pinLeft= cardStatus.getPin0RemainingCounter();
|
int pinLeft= cardStatus.getPin0RemainingCounter();
|
||||||
throw new RuntimeException("Wrong PIN, remaining tries: " + pinLeft);
|
throw new RuntimeException("Wrong PIN, remaining tries: " + pinLeft);
|
||||||
}
|
}
|
||||||
// blocked PIN
|
// blocked PIN
|
||||||
else if (sw == 0x9c0c){
|
else if (sw == 0x9c0c){
|
||||||
throw new RuntimeException("Card is blocked!");
|
throw new RuntimeException("Card is blocked!");
|
||||||
}
|
}
|
||||||
return rapdu;
|
return rapdu;
|
||||||
|
|
@ -348,19 +348,19 @@ public class SatochipCommandSet {
|
||||||
log.trace("SATOCHIP SatochipCommandSet cardChangePIN() capdu: "+ capdu.toHexString());
|
log.trace("SATOCHIP SatochipCommandSet cardChangePIN() capdu: "+ capdu.toHexString());
|
||||||
APDUResponse rapdu = this.cardTransmit(capdu);
|
APDUResponse rapdu = this.cardTransmit(capdu);
|
||||||
log.trace("SATOCHIP SatochipCommandSet cardChangePIN() rapdu: "+ rapdu.toHexString());
|
log.trace("SATOCHIP SatochipCommandSet cardChangePIN() rapdu: "+ rapdu.toHexString());
|
||||||
|
|
||||||
// correct PIN: cache PIN value
|
// correct PIN: cache PIN value
|
||||||
int sw = rapdu.getSw();
|
int sw = rapdu.getSw();
|
||||||
if (sw == 0x9000){
|
if (sw == 0x9000){
|
||||||
this.pinCached = newPin;
|
this.pinCached = newPin;
|
||||||
}
|
}
|
||||||
// wrong PIN, get remaining tries available (since v0.11)
|
// wrong PIN, get remaining tries available (since v0.11)
|
||||||
else if ((sw & 0xffc0) == 0x63c0){
|
else if ((sw & 0xffc0) == 0x63c0){
|
||||||
int pinLeft= (sw & ~0xffc0);
|
int pinLeft= (sw & ~0xffc0);
|
||||||
throw new RuntimeException("Wrong PIN, remaining tries: " + pinLeft);
|
throw new RuntimeException("Wrong PIN, remaining tries: " + pinLeft);
|
||||||
}
|
}
|
||||||
// wrong PIN (legacy before v0.11)
|
// wrong PIN (legacy before v0.11)
|
||||||
else if (sw == 0x9c02){
|
else if (sw == 0x9c02){
|
||||||
SatoCardStatus cardStatus = this.getApplicationStatus();
|
SatoCardStatus cardStatus = this.getApplicationStatus();
|
||||||
int pinLeft= cardStatus.getPin0RemainingCounter();
|
int pinLeft= cardStatus.getPin0RemainingCounter();
|
||||||
throw new RuntimeException("Wrong PIN, remaining tries: " + pinLeft);
|
throw new RuntimeException("Wrong PIN, remaining tries: " + pinLeft);
|
||||||
|
|
@ -375,7 +375,7 @@ public class SatochipCommandSet {
|
||||||
/****************************************
|
/****************************************
|
||||||
* BIP32 *
|
* BIP32 *
|
||||||
****************************************/
|
****************************************/
|
||||||
|
|
||||||
public APDUResponse cardBip32ImportSeed(byte[] masterseed){
|
public APDUResponse cardBip32ImportSeed(byte[] masterseed){
|
||||||
//TODO: check seed (length...)
|
//TODO: check seed (length...)
|
||||||
APDUCommand plainApdu = new APDUCommand(0xB0, INS_BIP32_IMPORT_SEED, masterseed.length, 0x00, masterseed);
|
APDUCommand plainApdu = new APDUCommand(0xB0, INS_BIP32_IMPORT_SEED, masterseed.length, 0x00, masterseed);
|
||||||
|
|
@ -386,10 +386,10 @@ public class SatochipCommandSet {
|
||||||
|
|
||||||
return respApdu;
|
return respApdu;
|
||||||
}
|
}
|
||||||
|
|
||||||
public APDUResponse cardBip32GetExtendedKey(String stringPath){
|
public APDUResponse cardBip32GetExtendedKey(String stringPath){
|
||||||
log.debug("SATOCHIP SatochipCommandSet cardBip32GetExtendedKey() stringPath: " + stringPath);
|
log.debug("SATOCHIP SatochipCommandSet cardBip32GetExtendedKey() stringPath: " + stringPath);
|
||||||
KeyPath keyPath = new KeyPath(stringPath);
|
KeyPath keyPath = new KeyPath(stringPath);
|
||||||
byte[] bytePath = keyPath.getData();
|
byte[] bytePath = keyPath.getData();
|
||||||
//log.trace("SATOCHIP SatochipCommandSet cardBip32GetExtendedKey() bytePath: " + Utils.bytesToHex(bytePath));
|
//log.trace("SATOCHIP SatochipCommandSet cardBip32GetExtendedKey() bytePath: " + Utils.bytesToHex(bytePath));
|
||||||
return cardBip32GetExtendedKey(bytePath);
|
return cardBip32GetExtendedKey(bytePath);
|
||||||
|
|
@ -398,38 +398,38 @@ public class SatochipCommandSet {
|
||||||
public APDUResponse cardBip32GetExtendedKey(byte[] bytePath){
|
public APDUResponse cardBip32GetExtendedKey(byte[] bytePath){
|
||||||
log.debug("SATOCHIP SatochipCommandSet cardBip32GetExtendedKey() bytePath: " + Utils.bytesToHex(bytePath));
|
log.debug("SATOCHIP SatochipCommandSet cardBip32GetExtendedKey() bytePath: " + Utils.bytesToHex(bytePath));
|
||||||
byte p1= (byte) (bytePath.length/4);
|
byte p1= (byte) (bytePath.length/4);
|
||||||
|
|
||||||
APDUCommand plainApdu = new APDUCommand(0xB0, INS_BIP32_GET_EXTENDED_KEY, p1, 0x40, bytePath);
|
APDUCommand plainApdu = new APDUCommand(0xB0, INS_BIP32_GET_EXTENDED_KEY, p1, 0x40, bytePath);
|
||||||
log.trace("SATOCHIP SatochipCommandSet cardBip32GetExtendedKey() capdu: "+ plainApdu.toHexString());
|
log.trace("SATOCHIP SatochipCommandSet cardBip32GetExtendedKey() capdu: "+ plainApdu.toHexString());
|
||||||
APDUResponse respApdu = this.cardTransmit(plainApdu);
|
APDUResponse respApdu = this.cardTransmit(plainApdu);
|
||||||
log.trace("SATOCHIP SatochipCommandSet cardBip32GetExtendedKey() rapdu: "+ respApdu.toHexString());
|
log.trace("SATOCHIP SatochipCommandSet cardBip32GetExtendedKey() rapdu: "+ respApdu.toHexString());
|
||||||
// TODO: check SW code for particular status
|
// TODO: check SW code for particular status
|
||||||
|
|
||||||
// TODO: parse apdu to extract data?
|
// TODO: parse apdu to extract data?
|
||||||
|
|
||||||
return respApdu;
|
return respApdu;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the BIP32 xpub for given path.
|
* Get the BIP32 xpub for given path.
|
||||||
*
|
*
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* path (str): the path; if given as a string, it will be converted to bytes (4 bytes for each path index)
|
* path (str): the path; if given as a string, it will be converted to bytes (4 bytes for each path index)
|
||||||
* xtype (str): the type of transaction such as 'standard', 'p2wpkh-p2sh', 'p2wpkh', 'p2wsh-p2sh', 'p2wsh'
|
* xtype (str): the type of transaction such as 'standard', 'p2wpkh-p2sh', 'p2wpkh', 'p2wsh-p2sh', 'p2wsh'
|
||||||
* is_mainnet (bool): is mainnet or testnet
|
* is_mainnet (bool): is mainnet or testnet
|
||||||
*
|
*
|
||||||
* Return:
|
* Return:
|
||||||
* xpub (str): the corresponding xpub value
|
* xpub (str): the corresponding xpub value
|
||||||
*/
|
*/
|
||||||
public String cardBip32GetXpub(String stringPath, ExtendedKey.Header xtype){
|
public String cardBip32GetXpub(String stringPath, ExtendedKey.Header xtype){
|
||||||
|
|
||||||
// path is of the form 44'/0'/1'
|
// path is of the form 44'/0'/1'
|
||||||
log.debug("SATOCHIP SatochipCommandSet cardBip32GetXpub() path: " + stringPath);
|
log.debug("SATOCHIP SatochipCommandSet cardBip32GetXpub() path: " + stringPath);
|
||||||
KeyPath keyPath = new KeyPath(stringPath);
|
KeyPath keyPath = new KeyPath(stringPath);
|
||||||
byte[] bytePath = keyPath.getData();
|
byte[] bytePath = keyPath.getData();
|
||||||
int depth = bytePath.length/4;
|
int depth = bytePath.length/4;
|
||||||
log.debug("SATOCHIP SatochipCommandSet cardBip32GetXpub() bytePath: " + Utils.bytesToHex(bytePath));
|
log.debug("SATOCHIP SatochipCommandSet cardBip32GetXpub() bytePath: " + Utils.bytesToHex(bytePath));
|
||||||
|
|
||||||
APDUResponse rapdu = this.cardBip32GetExtendedKey(bytePath);
|
APDUResponse rapdu = this.cardBip32GetExtendedKey(bytePath);
|
||||||
byte[][] extendedkey = this.parser.parseBip32GetExtendedKey(rapdu);
|
byte[][] extendedkey = this.parser.parseBip32GetExtendedKey(rapdu);
|
||||||
|
|
||||||
|
|
@ -452,7 +452,7 @@ public class SatochipCommandSet {
|
||||||
|
|
||||||
log.debug("SATOCHIP SatochipCommandSet cardBip32GetXpub() xtype: " + xtype);
|
log.debug("SATOCHIP SatochipCommandSet cardBip32GetXpub() xtype: " + xtype);
|
||||||
log.debug("SATOCHIP SatochipCommandSet cardBip32GetXpub() Network.get().getXpubHeader(): " + Network.get().getXpubHeader());
|
log.debug("SATOCHIP SatochipCommandSet cardBip32GetXpub() Network.get().getXpubHeader(): " + Network.get().getXpubHeader());
|
||||||
|
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(78);
|
ByteBuffer buffer = ByteBuffer.allocate(78);
|
||||||
buffer.putInt(xtype.getHeader());
|
buffer.putInt(xtype.getHeader());
|
||||||
buffer.put((byte) depth);
|
buffer.put((byte) depth);
|
||||||
|
|
@ -494,24 +494,24 @@ public class SatochipCommandSet {
|
||||||
throw new RuntimeException("Wrong challenge-response length (should be 20)");
|
throw new RuntimeException("Wrong challenge-response length (should be 20)");
|
||||||
}
|
}
|
||||||
APDUCommand plainApdu = new APDUCommand(0xB0, INS_SIGN_TRANSACTION_HASH, keynbr, 0x00, data);
|
APDUCommand plainApdu = new APDUCommand(0xB0, INS_SIGN_TRANSACTION_HASH, keynbr, 0x00, data);
|
||||||
|
|
||||||
log.trace("SATOCHIP SatochipCommandSet cardSignTransactionHash() capdu: "+ plainApdu.toHexString());
|
log.trace("SATOCHIP SatochipCommandSet cardSignTransactionHash() capdu: "+ plainApdu.toHexString());
|
||||||
APDUResponse respApdu = this.cardTransmit(plainApdu);
|
APDUResponse respApdu = this.cardTransmit(plainApdu);
|
||||||
log.trace("SATOCHIP SatochipCommandSet cardSignTransactionHash() rapdu: "+ respApdu.toHexString());
|
log.trace("SATOCHIP SatochipCommandSet cardSignTransactionHash() rapdu: "+ respApdu.toHexString());
|
||||||
// TODO: check SW code for particular status
|
// TODO: check SW code for particular status
|
||||||
|
|
||||||
return respApdu;
|
return respApdu;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function signs a given hash with a std or the last extended key
|
* This function signs a given hash with a std or the last extended key
|
||||||
* If 2FA is enabled, a HMAC must be provided as an additional security layer. *
|
* If 2FA is enabled, a HMAC must be provided as an additional security layer. *
|
||||||
* ins: 0x7B
|
* ins: 0x7B
|
||||||
* p1: key number or 0xFF for the last derived Bip32 extended key
|
* p1: key number or 0xFF for the last derived Bip32 extended key
|
||||||
* p2: 0x00
|
* p2: 0x00
|
||||||
* data: [hash(32b) | option: 2FA-flag(2b)|hmac(20b)]
|
* data: [hash(32b) | option: 2FA-flag(2b)|hmac(20b)]
|
||||||
* return: [sig]
|
* return: [sig]
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public APDUResponse cardSignSchnorrHash(byte keynbr, byte[] txhash, byte[] chalresponse){
|
public APDUResponse cardSignSchnorrHash(byte keynbr, byte[] txhash, byte[] chalresponse){
|
||||||
log.debug("SATOCHIP SatochipCommandSet cardSignSchnorrHash()");
|
log.debug("SATOCHIP SatochipCommandSet cardSignSchnorrHash()");
|
||||||
|
|
@ -535,23 +535,23 @@ public class SatochipCommandSet {
|
||||||
throw new RuntimeException("Wrong challenge-response length (should be 20)");
|
throw new RuntimeException("Wrong challenge-response length (should be 20)");
|
||||||
}
|
}
|
||||||
APDUCommand plainApdu = new APDUCommand(0xB0, 0x7B, keynbr, 0x00, data);
|
APDUCommand plainApdu = new APDUCommand(0xB0, 0x7B, keynbr, 0x00, data);
|
||||||
|
|
||||||
log.trace("SATOCHIP SatochipCommandSet cardSignSchnorrHash() capdu: "+ plainApdu.toHexString());
|
log.trace("SATOCHIP SatochipCommandSet cardSignSchnorrHash() capdu: "+ plainApdu.toHexString());
|
||||||
APDUResponse respApdu = this.cardTransmit(plainApdu);
|
APDUResponse respApdu = this.cardTransmit(plainApdu);
|
||||||
log.trace("SATOCHIP SatochipCommandSet cardSignSchnorrHash() rapdu: "+ respApdu.toHexString());
|
log.trace("SATOCHIP SatochipCommandSet cardSignSchnorrHash() rapdu: "+ respApdu.toHexString());
|
||||||
// TODO: check SW code for particular status
|
// TODO: check SW code for particular status
|
||||||
|
|
||||||
return respApdu;
|
return respApdu;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function tweak the currently available private stored in the Satochip.
|
* This function tweak the currently available private stored in the Satochip.
|
||||||
* Tweaking is based on the 'taproot_tweak_seckey(seckey0, h)' algorithm specification defined here:
|
* Tweaking is based on the 'taproot_tweak_seckey(seckey0, h)' algorithm specification defined here:
|
||||||
* https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs
|
* https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs
|
||||||
*
|
*
|
||||||
* ins: 0x7C
|
* ins: 0x7C
|
||||||
* p1: key number or 0xFF for the last derived Bip32 extended key
|
* p1: key number or 0xFF for the last derived Bip32 extended key
|
||||||
* p2: 0x00
|
* p2: 0x00
|
||||||
* data: [hash(32b) | option: 2FA-flag(2b)|hmac(20b)]
|
* data: [hash(32b) | option: 2FA-flag(2b)|hmac(20b)]
|
||||||
* return: [sig]
|
* return: [sig]
|
||||||
*/
|
*/
|
||||||
|
|
@ -567,14 +567,14 @@ public class SatochipCommandSet {
|
||||||
data= new byte[33];
|
data= new byte[33];
|
||||||
data[0]= (byte)32;
|
data[0]= (byte)32;
|
||||||
System.arraycopy(tweak, 0, data, 1, tweak.length);
|
System.arraycopy(tweak, 0, data, 1, tweak.length);
|
||||||
|
|
||||||
APDUCommand plainApdu = new APDUCommand(0xB0, 0x7C, keynbr, 0x00, data);
|
APDUCommand plainApdu = new APDUCommand(0xB0, 0x7C, keynbr, 0x00, data);
|
||||||
|
|
||||||
log.trace("SATOCHIP SatochipCommandSet cardTaprootTweakPrivkey() capdu: "+ plainApdu.toHexString());
|
log.trace("SATOCHIP SatochipCommandSet cardTaprootTweakPrivkey() capdu: "+ plainApdu.toHexString());
|
||||||
APDUResponse respApdu = this.cardTransmit(plainApdu);
|
APDUResponse respApdu = this.cardTransmit(plainApdu);
|
||||||
log.trace("SATOCHIP SatochipCommandSet cardTaprootTweakPrivkey() rapdu: "+ respApdu.toHexString());
|
log.trace("SATOCHIP SatochipCommandSet cardTaprootTweakPrivkey() rapdu: "+ respApdu.toHexString());
|
||||||
// TODO: check SW code for particular status
|
// TODO: check SW code for particular status
|
||||||
|
|
||||||
return respApdu;
|
return respApdu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -582,10 +582,10 @@ public class SatochipCommandSet {
|
||||||
* 2FA commands *
|
* 2FA commands *
|
||||||
****************************************/
|
****************************************/
|
||||||
//todo
|
//todo
|
||||||
|
|
||||||
|
|
||||||
/****************************************
|
/****************************************
|
||||||
* PKI commands *
|
* PKI commands *
|
||||||
****************************************/
|
****************************************/
|
||||||
// todo
|
// todo
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,155 +1,155 @@
|
||||||
package com.sparrowwallet.sparrow.io.satochip;
|
package com.sparrowwallet.sparrow.io.satochip;
|
||||||
|
|
||||||
import com.sparrowwallet.drongo.protocol.Sha256Hash;
|
import com.sparrowwallet.drongo.protocol.Sha256Hash;
|
||||||
import com.sparrowwallet.drongo.crypto.ECKey;
|
import com.sparrowwallet.drongo.crypto.ECKey;
|
||||||
import com.sparrowwallet.drongo.crypto.ECDSASignature;
|
import com.sparrowwallet.drongo.crypto.ECDSASignature;
|
||||||
import com.sparrowwallet.drongo.Utils;
|
import com.sparrowwallet.drongo.Utils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
|
||||||
public class SatochipParser{
|
public class SatochipParser{
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(SatochipParser.class);
|
private static final Logger log = LoggerFactory.getLogger(SatochipParser.class);
|
||||||
|
|
||||||
private byte[] authentikey= null;
|
private byte[] authentikey= null;
|
||||||
|
|
||||||
public SatochipParser(){
|
public SatochipParser(){
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************
|
/****************************************
|
||||||
* PARSER *
|
* PARSER *
|
||||||
****************************************/
|
****************************************/
|
||||||
|
|
||||||
public byte[] parseInitiateSecureChannel(APDUResponse rapdu){
|
public byte[] parseInitiateSecureChannel(APDUResponse rapdu){
|
||||||
|
|
||||||
try{
|
try{
|
||||||
byte[] data= rapdu.getData();
|
byte[] data= rapdu.getData();
|
||||||
log.trace("SATOCHIP SatochipParser parseInitiateSecureChannel() data: " + Utils.bytesToHex(data));
|
log.trace("SATOCHIP SatochipParser parseInitiateSecureChannel() data: " + Utils.bytesToHex(data));
|
||||||
|
|
||||||
// data= [coordxSize | coordx | sig1Size | sig1 | sig2Size | sig2]
|
// data= [coordxSize | coordx | sig1Size | sig1 | sig2Size | sig2]
|
||||||
int offset=0;
|
int offset=0;
|
||||||
int coordxSize= 256*data[offset++] + data[offset++];
|
int coordxSize= 256*data[offset++] + data[offset++];
|
||||||
|
|
||||||
byte[] coordx= new byte[coordxSize];
|
byte[] coordx= new byte[coordxSize];
|
||||||
System.arraycopy(data, offset, coordx, 0, coordxSize);
|
System.arraycopy(data, offset, coordx, 0, coordxSize);
|
||||||
offset+=coordxSize;
|
offset+=coordxSize;
|
||||||
|
|
||||||
// msg1 is [coordx_size | coordx]
|
// msg1 is [coordx_size | coordx]
|
||||||
byte[] msg1= new byte[2+coordxSize];
|
byte[] msg1= new byte[2+coordxSize];
|
||||||
System.arraycopy(data, 0, msg1, 0, msg1.length);
|
System.arraycopy(data, 0, msg1, 0, msg1.length);
|
||||||
|
|
||||||
int sig1Size= 256*data[offset++] + data[offset++];
|
int sig1Size= 256*data[offset++] + data[offset++];
|
||||||
byte[] sig1= new byte[sig1Size];
|
byte[] sig1= new byte[sig1Size];
|
||||||
System.arraycopy(data, offset, sig1, 0, sig1Size);
|
System.arraycopy(data, offset, sig1, 0, sig1Size);
|
||||||
offset+=sig1Size;
|
offset+=sig1Size;
|
||||||
|
|
||||||
// msg2 is [coordxSize | coordx | sig1Size | sig1]
|
// msg2 is [coordxSize | coordx | sig1Size | sig1]
|
||||||
byte[] msg2= new byte[2+coordxSize + 2 + sig1Size];
|
byte[] msg2= new byte[2+coordxSize + 2 + sig1Size];
|
||||||
System.arraycopy(data, 0, msg2, 0, msg2.length);
|
System.arraycopy(data, 0, msg2, 0, msg2.length);
|
||||||
|
|
||||||
int sig2Size= 256*data[offset++] + data[offset++];
|
int sig2Size= 256*data[offset++] + data[offset++];
|
||||||
byte[] sig2= new byte[sig2Size];
|
byte[] sig2= new byte[sig2Size];
|
||||||
System.arraycopy(data, offset, sig2, 0, sig2Size);
|
System.arraycopy(data, offset, sig2, 0, sig2Size);
|
||||||
offset+=sig2Size;
|
offset+=sig2Size;
|
||||||
|
|
||||||
byte[] pubkey= recoverPubkey(msg1, sig1, coordx, false); // false: uncompressed
|
byte[] pubkey= recoverPubkey(msg1, sig1, coordx, false); // false: uncompressed
|
||||||
log.trace("SATOCHIP SatochipParser parseInitiateSecureChannel() pubkey: " + Utils.bytesToHex(pubkey));
|
log.trace("SATOCHIP SatochipParser parseInitiateSecureChannel() pubkey: " + Utils.bytesToHex(pubkey));
|
||||||
|
|
||||||
// todo: recover from sig2
|
// todo: recover from sig2
|
||||||
|
|
||||||
return pubkey;
|
return pubkey;
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
log.error("SATOCHIP SatochipParser parseInitiateSecureChannel() exception: " + e);
|
log.error("SATOCHIP SatochipParser parseInitiateSecureChannel() exception: " + e);
|
||||||
throw new RuntimeException("SATOCHIP SatochipParser parseInitiateSecureChannel() exception: ", e);
|
throw new RuntimeException("SATOCHIP SatochipParser parseInitiateSecureChannel() exception: ", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[][] parseBip32GetExtendedKey(APDUResponse rapdu){
|
public byte[][] parseBip32GetExtendedKey(APDUResponse rapdu){
|
||||||
log.trace("SATOCHIP SatochipParser parseBip32GetExtendedKey()");
|
log.trace("SATOCHIP SatochipParser parseBip32GetExtendedKey()");
|
||||||
try{
|
try{
|
||||||
byte[][] extendedkey = new byte[2][];
|
byte[][] extendedkey = new byte[2][];
|
||||||
extendedkey[0] = new byte[33]; // pubkey
|
extendedkey[0] = new byte[33]; // pubkey
|
||||||
extendedkey[1] = new byte[32]; // chaincode
|
extendedkey[1] = new byte[32]; // chaincode
|
||||||
|
|
||||||
byte[] data= rapdu.getData();
|
byte[] data= rapdu.getData();
|
||||||
log.trace("SATOCHIP SatochipParser parseBip32GetExtendedKey data: " + Utils.bytesToHex(data));
|
log.trace("SATOCHIP SatochipParser parseBip32GetExtendedKey data: " + Utils.bytesToHex(data));
|
||||||
//data: [chaincode(32b) | coordx_size(2b) | coordx | sig_size(2b) | sig | sig_size(2b) | sig2]
|
//data: [chaincode(32b) | coordx_size(2b) | coordx | sig_size(2b) | sig | sig_size(2b) | sig2]
|
||||||
|
|
||||||
int offset=0;
|
int offset=0;
|
||||||
byte[] chaincode= new byte[32];
|
byte[] chaincode= new byte[32];
|
||||||
System.arraycopy(data, offset, chaincode, 0, chaincode.length);
|
System.arraycopy(data, offset, chaincode, 0, chaincode.length);
|
||||||
offset+=32;
|
offset+=32;
|
||||||
|
|
||||||
int coordxSize= 256*(data[offset++] & 0x7f) + data[offset++]; // (data[32] & 0x80) is ignored (optimization flag)
|
int coordxSize= 256*(data[offset++] & 0x7f) + data[offset++]; // (data[32] & 0x80) is ignored (optimization flag)
|
||||||
byte[] coordx= new byte[coordxSize];
|
byte[] coordx= new byte[coordxSize];
|
||||||
System.arraycopy(data, offset, coordx, 0, coordxSize);
|
System.arraycopy(data, offset, coordx, 0, coordxSize);
|
||||||
offset+=coordxSize;
|
offset+=coordxSize;
|
||||||
|
|
||||||
// msg1 is [chaincode | coordx_size | coordx]
|
// msg1 is [chaincode | coordx_size | coordx]
|
||||||
byte[] msg1= new byte[32+2+coordxSize];
|
byte[] msg1= new byte[32+2+coordxSize];
|
||||||
System.arraycopy(data, 0, msg1, 0, msg1.length);
|
System.arraycopy(data, 0, msg1, 0, msg1.length);
|
||||||
|
|
||||||
int sig1Size= 256*data[offset++] + data[offset++];
|
int sig1Size= 256*data[offset++] + data[offset++];
|
||||||
byte[] sig1= new byte[sig1Size];
|
byte[] sig1= new byte[sig1Size];
|
||||||
System.arraycopy(data, offset, sig1, 0, sig1Size);
|
System.arraycopy(data, offset, sig1, 0, sig1Size);
|
||||||
offset+=sig1Size;
|
offset+=sig1Size;
|
||||||
|
|
||||||
// msg2 is [chaincode | coordxSize | coordx | sig1Size | sig1]
|
// msg2 is [chaincode | coordxSize | coordx | sig1Size | sig1]
|
||||||
byte[] msg2= new byte[32 + 2+coordxSize + 2 + sig1Size];
|
byte[] msg2= new byte[32 + 2+coordxSize + 2 + sig1Size];
|
||||||
System.arraycopy(data, 0, msg2, 0, msg2.length);
|
System.arraycopy(data, 0, msg2, 0, msg2.length);
|
||||||
|
|
||||||
int sig2Size= 256*data[offset++] + data[offset++];
|
int sig2Size= 256*data[offset++] + data[offset++];
|
||||||
byte[] sig2= new byte[sig2Size];
|
byte[] sig2= new byte[sig2Size];
|
||||||
System.arraycopy(data, offset, sig2, 0, sig2Size);
|
System.arraycopy(data, offset, sig2, 0, sig2Size);
|
||||||
offset+=sig2Size;
|
offset+=sig2Size;
|
||||||
|
|
||||||
byte[] pubkey= recoverPubkey(msg1, sig1, coordx, true); // true: compressed (33 bytes)
|
byte[] pubkey= recoverPubkey(msg1, sig1, coordx, true); // true: compressed (33 bytes)
|
||||||
log.trace("SATOCHIP SatochipParser parseBip32GetExtendedKey pubkey: " + Utils.bytesToHex(pubkey));
|
log.trace("SATOCHIP SatochipParser parseBip32GetExtendedKey pubkey: " + Utils.bytesToHex(pubkey));
|
||||||
|
|
||||||
// todo: recover from si2
|
// todo: recover from si2
|
||||||
System.arraycopy(pubkey, 0, extendedkey[0], 0, pubkey.length);
|
System.arraycopy(pubkey, 0, extendedkey[0], 0, pubkey.length);
|
||||||
System.arraycopy(chaincode, 0, extendedkey[1], 0, chaincode.length);
|
System.arraycopy(chaincode, 0, extendedkey[1], 0, chaincode.length);
|
||||||
return extendedkey;
|
return extendedkey;
|
||||||
|
|
||||||
} catch(Exception e) {
|
} catch(Exception e) {
|
||||||
log.error("SATOCHIP SatochipParser parseBip32GetExtendedKey() exception: " + e);
|
log.error("SATOCHIP SatochipParser parseBip32GetExtendedKey() exception: " + e);
|
||||||
throw new RuntimeException("SATOCHIP SatochipParser parseBip32GetExtendedKey() exception: ", e);
|
throw new RuntimeException("SATOCHIP SatochipParser parseBip32GetExtendedKey() exception: ", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/****************************************
|
/****************************************
|
||||||
* recovery methods *
|
* recovery methods *
|
||||||
****************************************/
|
****************************************/
|
||||||
|
|
||||||
public byte[] recoverPubkey(byte[] msg, byte[] dersig, byte[] coordx, Boolean compressed) {
|
public byte[] recoverPubkey(byte[] msg, byte[] dersig, byte[] coordx, Boolean compressed) {
|
||||||
log.trace("SATOCHIP SatochipParser recoverPubkey() coordx: " + Utils.bytesToHex(coordx));
|
log.trace("SATOCHIP SatochipParser recoverPubkey() coordx: " + Utils.bytesToHex(coordx));
|
||||||
|
|
||||||
// convert msg to hash
|
// convert msg to hash
|
||||||
//byte[] hash = Sha256Hash.hash(msg);
|
//byte[] hash = Sha256Hash.hash(msg);
|
||||||
ECDSASignature ecdsaSig = ECDSASignature.decodeFromDER(dersig);
|
ECDSASignature ecdsaSig = ECDSASignature.decodeFromDER(dersig);
|
||||||
|
|
||||||
byte recId = -1;
|
byte recId = -1;
|
||||||
ECKey k = null;
|
ECKey k = null;
|
||||||
for(byte i = 0; i < 4; i++) {
|
for(byte i = 0; i < 4; i++) {
|
||||||
k = ECKey.recoverFromSignature(i, ecdsaSig, Sha256Hash.of(msg), compressed);
|
k = ECKey.recoverFromSignature(i, ecdsaSig, Sha256Hash.of(msg), compressed);
|
||||||
if(k != null && Arrays.equals(k.getPubKeyXCoord(),coordx)) {
|
if(k != null && Arrays.equals(k.getPubKeyXCoord(),coordx)) {
|
||||||
recId = i;
|
recId = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(recId == -1) {
|
if(recId == -1) {
|
||||||
throw new RuntimeException("SATOCHIP SatochipParser recoverPubkey() Exception: Could not construct a recoverable key. This should never happen.");
|
throw new RuntimeException("SATOCHIP SatochipParser recoverPubkey() Exception: Could not construct a recoverable key. This should never happen.");
|
||||||
}
|
}
|
||||||
|
|
||||||
log.trace("SATOCHIP SatochipParser recoverPubkey() recoveredPubkey: " + Utils.bytesToHex(k.getPubKey()));
|
log.trace("SATOCHIP SatochipParser recoverPubkey() recoveredPubkey: " + Utils.bytesToHex(k.getPubKey()));
|
||||||
return k.getPubKey();
|
return k.getPubKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -27,14 +27,14 @@ import org.slf4j.LoggerFactory;
|
||||||
* Handles a SecureChannel session with the card.
|
* Handles a SecureChannel session with the card.
|
||||||
*/
|
*/
|
||||||
public class SecureChannelSession {
|
public class SecureChannelSession {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(SecureChannelSession.class);
|
private static final Logger log = LoggerFactory.getLogger(SecureChannelSession.class);
|
||||||
|
|
||||||
public static final int SC_SECRET_LENGTH = 16;
|
public static final int SC_SECRET_LENGTH = 16;
|
||||||
public static final int SC_BLOCK_SIZE = 16;
|
public static final int SC_BLOCK_SIZE = 16;
|
||||||
public static final int IV_SIZE = 16;
|
public static final int IV_SIZE = 16;
|
||||||
public static final int MAC_SIZE= 20;
|
public static final int MAC_SIZE= 20;
|
||||||
|
|
||||||
// secure channel constants
|
// secure channel constants
|
||||||
private final static byte INS_INIT_SECURE_CHANNEL = (byte) 0x81;
|
private final static byte INS_INIT_SECURE_CHANNEL = (byte) 0x81;
|
||||||
private final static byte INS_PROCESS_SECURE_CHANNEL = (byte) 0x82;
|
private final static byte INS_PROCESS_SECURE_CHANNEL = (byte) 0x82;
|
||||||
|
|
@ -44,28 +44,28 @@ public class SecureChannelSession {
|
||||||
private final static short SW_SECURE_CHANNEL_WRONG_MAC= (short) 0x9C23;
|
private final static short SW_SECURE_CHANNEL_WRONG_MAC= (short) 0x9C23;
|
||||||
|
|
||||||
private boolean initialized_secure_channel= false;
|
private boolean initialized_secure_channel= false;
|
||||||
|
|
||||||
// secure channel keys
|
// secure channel keys
|
||||||
private byte[] secret;
|
private byte[] secret;
|
||||||
private byte[] iv;
|
private byte[] iv;
|
||||||
private int ivCounter;
|
private int ivCounter;
|
||||||
byte[] derived_key;
|
byte[] derived_key;
|
||||||
byte[] mac_key;
|
byte[] mac_key;
|
||||||
|
|
||||||
// for ECDH
|
// for ECDH
|
||||||
private SecretPoint secretPoint;
|
private SecretPoint secretPoint;
|
||||||
private ECKey eckey;
|
private ECKey eckey;
|
||||||
|
|
||||||
// for session encryption
|
// for session encryption
|
||||||
private SecureRandom random;
|
private SecureRandom random;
|
||||||
private AESKeyCrypter aesCipher;
|
private AESKeyCrypter aesCipher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a SecureChannel session on the client.
|
* Constructs a SecureChannel session on the client.
|
||||||
*/
|
*/
|
||||||
public SecureChannelSession() {
|
public SecureChannelSession() {
|
||||||
random = new SecureRandom();
|
random = new SecureRandom();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// generate keypair
|
// generate keypair
|
||||||
eckey = new ECKey();
|
eckey = new ECKey();
|
||||||
|
|
@ -75,8 +75,8 @@ public class SecureChannelSession {
|
||||||
log.error("SATOCHIP SecureChannelSession() exception: " + e);
|
log.error("SATOCHIP SecureChannelSession() exception: " + e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a pairing secret. This should be called before each session. The public key of the card is used as input
|
* Generates a pairing secret. This should be called before each session. The public key of the card is used as input
|
||||||
* for the EC-DH algorithm. The output is stored as the secret.
|
* for the EC-DH algorithm. The output is stored as the secret.
|
||||||
|
|
@ -86,7 +86,7 @@ public class SecureChannelSession {
|
||||||
public void initiateSecureChannel(byte[] pubkeyData) { //TODO: check keyData format
|
public void initiateSecureChannel(byte[] pubkeyData) { //TODO: check keyData format
|
||||||
log.trace("SATOCHIP SecureChannelSession initiateSecureChannel()");
|
log.trace("SATOCHIP SecureChannelSession initiateSecureChannel()");
|
||||||
try {
|
try {
|
||||||
|
|
||||||
byte[] privkeyData = this.eckey.getPrivKeyBytes();
|
byte[] privkeyData = this.eckey.getPrivKeyBytes();
|
||||||
secretPoint = new SecretPoint(privkeyData, pubkeyData);
|
secretPoint = new SecretPoint(privkeyData, pubkeyData);
|
||||||
secret = secretPoint.ECDHSecretAsBytes();
|
secret = secretPoint.ECDHSecretAsBytes();
|
||||||
|
|
@ -109,17 +109,17 @@ public class SecureChannelSession {
|
||||||
log.error("SATOCHIP SecureChannelSession initiateSecureChannel() exception:" + e);
|
log.error("SATOCHIP SecureChannelSession initiateSecureChannel() exception:" + e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public APDUCommand encrypt_secure_channel(APDUCommand plainApdu){
|
public APDUCommand encrypt_secure_channel(APDUCommand plainApdu){
|
||||||
log.trace("SATOCHIP: SecureChannelSession encrypt_secure_channel()");
|
log.trace("SATOCHIP: SecureChannelSession encrypt_secure_channel()");
|
||||||
try {
|
try {
|
||||||
|
|
||||||
byte[] plainBytes= plainApdu.serialize();
|
byte[] plainBytes= plainApdu.serialize();
|
||||||
|
|
||||||
// set iv
|
// set iv
|
||||||
iv = new byte[SC_BLOCK_SIZE];
|
iv = new byte[SC_BLOCK_SIZE];
|
||||||
random.nextBytes(iv);
|
random.nextBytes(iv);
|
||||||
ByteBuffer bb = ByteBuffer.allocate(4);
|
ByteBuffer bb = ByteBuffer.allocate(4);
|
||||||
bb.putInt(ivCounter); // big endian
|
bb.putInt(ivCounter); // big endian
|
||||||
byte[] ivCounterBytes= bb.array();
|
byte[] ivCounterBytes= bb.array();
|
||||||
System.arraycopy(ivCounterBytes, 0, iv, 12, 4);
|
System.arraycopy(ivCounterBytes, 0, iv, 12, 4);
|
||||||
|
|
@ -158,23 +158,23 @@ public class SecureChannelSession {
|
||||||
data[offset++]= (byte)(mac.length%256);
|
data[offset++]= (byte)(mac.length%256);
|
||||||
System.arraycopy(mac, 0, data, offset, mac.length);
|
System.arraycopy(mac, 0, data, offset, mac.length);
|
||||||
log.trace("SATOCHIP: SecureChannelSession encrypt_secure_channel() full_encrypted_data: " + Utils.bytesToHex(data));
|
log.trace("SATOCHIP: SecureChannelSession encrypt_secure_channel() full_encrypted_data: " + Utils.bytesToHex(data));
|
||||||
|
|
||||||
// convert to C-APDU
|
// convert to C-APDU
|
||||||
APDUCommand encryptedApdu= new APDUCommand(0xB0, INS_PROCESS_SECURE_CHANNEL, 0x00, 0x00, data);
|
APDUCommand encryptedApdu= new APDUCommand(0xB0, INS_PROCESS_SECURE_CHANNEL, 0x00, 0x00, data);
|
||||||
return encryptedApdu;
|
return encryptedApdu;
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
log.error("SATOCHIP: Exception in encrypt_secure_channel: "+ e);
|
log.error("SATOCHIP: Exception in encrypt_secure_channel: "+ e);
|
||||||
throw new RuntimeException("SATOCHIP: Exception in encrypt_secure_channel:", e);
|
throw new RuntimeException("SATOCHIP: Exception in encrypt_secure_channel:", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public APDUResponse decrypt_secure_channel(APDUResponse encryptedApdu){
|
public APDUResponse decrypt_secure_channel(APDUResponse encryptedApdu){
|
||||||
log.trace("SATOCHIP SecureChannelSession decrypt_secure_channel()");
|
log.trace("SATOCHIP SecureChannelSession decrypt_secure_channel()");
|
||||||
try {
|
try {
|
||||||
|
|
||||||
byte[] encryptedBytes= encryptedApdu.getData();
|
byte[] encryptedBytes= encryptedApdu.getData();
|
||||||
if (encryptedBytes.length==0){
|
if (encryptedBytes.length==0){
|
||||||
return encryptedApdu; // no decryption needed
|
return encryptedApdu; // no decryption needed
|
||||||
|
|
@ -204,30 +204,30 @@ public class SecureChannelSession {
|
||||||
|
|
||||||
APDUResponse plainResponse= new APDUResponse(decrypted, (byte)0x90, (byte)0x00);
|
APDUResponse plainResponse= new APDUResponse(decrypted, (byte)0x90, (byte)0x00);
|
||||||
return plainResponse;
|
return plainResponse;
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
log.error("SATOCHIP SecureChannelSession decrypt_secure_channel() Exception: " + e);
|
log.error("SATOCHIP SecureChannelSession decrypt_secure_channel() Exception: " + e);
|
||||||
throw new RuntimeException("Exception during secure channel decryption: ", e);
|
throw new RuntimeException("Exception during secure channel decryption: ", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean initializedSecureChannel(){
|
public boolean initializedSecureChannel(){
|
||||||
return initialized_secure_channel;
|
return initialized_secure_channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getPublicKey(){
|
public byte[] getPublicKey(){
|
||||||
log.trace("SATOCHIP SecureChannelSession getPublicKey() eckey.getPubKey(): " + Utils.bytesToHex(eckey.getPubKey(false)));
|
log.trace("SATOCHIP SecureChannelSession getPublicKey() eckey.getPubKey(): " + Utils.bytesToHex(eckey.getPubKey(false)));
|
||||||
return eckey.getPubKey(false); // false: uncompressed
|
return eckey.getPubKey(false); // false: uncompressed
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resetSecureChannel(){
|
public void resetSecureChannel(){
|
||||||
initialized_secure_channel= false;
|
initialized_secure_channel= false;
|
||||||
// todo: generate new eckey?
|
// todo: generate new eckey?
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] getHmacSha1Hash(byte[] key, byte[] data) {
|
public static byte[] getHmacSha1Hash(byte[] key, byte[] data) {
|
||||||
try{
|
try{
|
||||||
//log.trace("SATOCHIP SecureChannelSession getHmacSha1Hash() START");
|
//log.trace("SATOCHIP SecureChannelSession getHmacSha1Hash() START");
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ public class HwAirgappedController extends KeystoreImportDetailController {
|
||||||
private Accordion importAccordion;
|
private Accordion importAccordion;
|
||||||
|
|
||||||
public void initializeView() {
|
public void initializeView() {
|
||||||
log.debug("SATOCHIP HwAirgappedController START");
|
|
||||||
List<KeystoreFileImport> fileImporters = Collections.emptyList();
|
List<KeystoreFileImport> fileImporters = Collections.emptyList();
|
||||||
if(getMasterController().getWallet().getPolicyType().equals(PolicyType.SINGLE)) {
|
if(getMasterController().getWallet().getPolicyType().equals(PolicyType.SINGLE)) {
|
||||||
fileImporters = List.of(new ColdcardSinglesig(), new CoboVaultSinglesig(), new Jade(), new KeystoneSinglesig(), new PassportSinglesig(), new SeedSigner(), new GordianSeedTool(), new SpecterDIY());
|
fileImporters = List.of(new ColdcardSinglesig(), new CoboVaultSinglesig(), new Jade(), new KeystoneSinglesig(), new PassportSinglesig(), new SeedSigner(), new GordianSeedTool(), new SpecterDIY());
|
||||||
|
|
@ -40,7 +39,6 @@ public class HwAirgappedController extends KeystoreImportDetailController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.debug("SATOCHIP HwAirgappedController initializeView() - BEFORE cardImporters");
|
|
||||||
List<KeystoreCardImport> cardImporters = List.of(new Tapsigner(), new Satochip());
|
List<KeystoreCardImport> cardImporters = List.of(new Tapsigner(), new Satochip());
|
||||||
for(KeystoreCardImport importer : cardImporters) {
|
for(KeystoreCardImport importer : cardImporters) {
|
||||||
if(!importer.isDeprecated() || Config.get().isShowDeprecatedImportExport()) {
|
if(!importer.isDeprecated() || Config.get().isShowDeprecatedImportExport()) {
|
||||||
|
|
@ -50,7 +48,6 @@ public class HwAirgappedController extends KeystoreImportDetailController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
log.debug("SATOCHIP HwAirgappedController initializeView() - AFTER cardImporters");
|
|
||||||
|
|
||||||
importAccordion.getPanes().sort(Comparator.comparing(o -> ((TitledDescriptionPane) o).getTitle()));
|
importAccordion.getPanes().sort(Comparator.comparing(o -> ((TitledDescriptionPane) o).getTitle()));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,7 @@ public class HwUsbDevicesController extends KeystoreImportDetailController {
|
||||||
private Accordion deviceAccordion;
|
private Accordion deviceAccordion;
|
||||||
|
|
||||||
public void initializeView(List<Device> devices) {
|
public void initializeView(List<Device> devices) {
|
||||||
//log.debug("SATOCHIP HwUsbDevicesController initializeView START");
|
|
||||||
for(Device device : devices) {
|
for(Device device : devices) {
|
||||||
//log.debug("SATOCHIP HwUsbDevicesController initializeView device: " + device);
|
|
||||||
DevicePane devicePane = new DevicePane(getMasterController().getWallet(), device, devices.size() == 1, getMasterController().getRequiredDerivation());
|
DevicePane devicePane = new DevicePane(getMasterController().getWallet(), device, devices.size() == 1, getMasterController().getRequiredDerivation());
|
||||||
if(getMasterController().getRequiredModel() == null || getMasterController().getRequiredModel() == device.getModel()) {
|
if(getMasterController().getRequiredModel() == null || getMasterController().getRequiredModel() == device.getModel()) {
|
||||||
deviceAccordion.getPanes().add(devicePane);
|
deviceAccordion.getPanes().add(devicePane);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue