From 6f028d720b32410a3f951ea1307ddadc423100c4 Mon Sep 17 00:00:00 2001 From: Toporin Date: Wed, 6 Sep 2023 13:33:51 +0100 Subject: [PATCH] Satochip: remove debug logs, trailing whitespaces & clean code --- .../sparrow/control/DevicePane.java | 28 +- .../sparrow/io/satochip/APDUCommand.java | 2 +- .../sparrow/io/satochip/APDUResponse.java | 8 +- .../sparrow/io/satochip/Constants.java | 298 ++++++++--------- .../sparrow/io/satochip/KeyPath.java | 162 ++++----- .../sparrow/io/satochip/SatoCardApi.java | 74 +---- .../sparrow/io/satochip/SatoCardStatus.java | 22 +- .../io/satochip/SatoCardTransport.java | 2 +- .../io/satochip/SatochipCommandSet.java | 166 +++++----- .../sparrow/io/satochip/SatochipParser.java | 308 +++++++++--------- .../io/satochip/SecureChannelSession.java | 54 +-- .../keystoreimport/HwAirgappedController.java | 3 - .../HwUsbDevicesController.java | 2 - 13 files changed, 531 insertions(+), 598 deletions(-) diff --git a/src/main/java/com/sparrowwallet/sparrow/control/DevicePane.java b/src/main/java/com/sparrowwallet/sparrow/control/DevicePane.java index bb0c49bb..fe1d9677 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/DevicePane.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/DevicePane.java @@ -674,7 +674,7 @@ public class DevicePane extends TitledDescriptionPane { try { CardApi cardApi = CardApi.getCardApi(device.getModel(), pin.get()); 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"); setContent(getCardPinEntry(importButton)); showHideLink.setVisible(false); @@ -1074,17 +1074,10 @@ public class DevicePane extends TitledDescriptionPane { initializeButton.setDefaultButton(true); initializeButton.setOnAction(event -> { 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; // check that pin and previous pin match 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!"); } else { try{ @@ -1098,7 +1091,6 @@ public class DevicePane extends TitledDescriptionPane { messageProperty.set("Failed to parse the seed with error: " + e); } } - Service cardInitializationService = cardApi.getInitializationService(seedBytes, messageProperty); cardInitializationService.setOnSucceeded(successEvent -> { log.debug("SATOCHIP DevicePane getCardInitializationPanel() Card initialized!"); @@ -1112,17 +1104,9 @@ public class DevicePane extends TitledDescriptionPane { setExpanded(false); }); cardInitializationService.setOnFailed(failEvent -> { - log.error("SATOCHIP DevicePane getCardInitializationPanel() failed to initialize card!"); - /*Throwable rootCause = Throwables.getRootCause(failEvent.getSource().getException()); - if(rootCause instanceof CardAuthorizationException) { - 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); - //} + 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(); }); @@ -1190,7 +1174,7 @@ public class DevicePane extends TitledDescriptionPane { return contentBox; } - + } private Node getCardPinEntry(ButtonBase operationButton) { diff --git a/src/main/java/com/sparrowwallet/sparrow/io/satochip/APDUCommand.java b/src/main/java/com/sparrowwallet/sparrow/io/satochip/APDUCommand.java index b645e58d..9bd3b698 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/satochip/APDUCommand.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/satochip/APDUCommand.java @@ -16,7 +16,7 @@ public class APDUCommand { protected byte[] data; protected boolean needsLE; 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. * diff --git a/src/main/java/com/sparrowwallet/sparrow/io/satochip/APDUResponse.java b/src/main/java/com/sparrowwallet/sparrow/io/satochip/APDUResponse.java index a3f136ef..eace4fb7 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/satochip/APDUResponse.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/satochip/APDUResponse.java @@ -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_WRONG_PIN_MASK = 0x63C0; public static final String HEXES = "0123456789ABCDEF"; - + private byte[] apdu; private byte[] data; private int sw; @@ -33,7 +33,7 @@ public class APDUResponse { this.apdu = apdu; this.parse(); } - + public APDUResponse(byte[] data, byte sw1, byte sw2) { byte[] apdu= new byte[data.length + 2]; System.arraycopy(data, 0, apdu, 0, data.length); @@ -42,7 +42,7 @@ public class APDUResponse { this.apdu = apdu; this.parse(); } - + /** * Parses the APDU response, separating the response data from SW. @@ -100,7 +100,7 @@ public class APDUResponse { public byte[] getBytes() { return this.apdu; } - + /** * Serializes the APDU to human readable hex string format * diff --git a/src/main/java/com/sparrowwallet/sparrow/io/satochip/Constants.java b/src/main/java/com/sparrowwallet/sparrow/io/satochip/Constants.java index 16c57723..54df13dc 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/satochip/Constants.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/satochip/Constants.java @@ -1,149 +1,149 @@ -package com.sparrowwallet.sparrow.io.satochip; - -public final class Constants { - - // Prevents instanciation of class - private Constants() {} - - /**************************************** - * Instruction codes * - ****************************************/ - public final static byte CLA = (byte)0xB0; - // Applet initialization - public final static byte INS_SETUP = (byte) 0x2A; - // Keys' use and management - public final static byte INS_IMPORT_KEY = (byte) 0x32; - public final static byte INS_RESET_KEY = (byte) 0x33; - public final static byte INS_GET_PUBLIC_FROM_PRIVATE= (byte)0x35; - // External authentication - 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_CHANGE_PIN = (byte) 0x44; - public final static byte INS_UNBLOCK_PIN = (byte) 0x46; - public final static byte INS_LOGOUT_ALL = (byte) 0x60; - // Status information - public final static byte INS_LIST_PINS = (byte) 0x48; - public final static byte INS_GET_STATUS = (byte) 0x3C; - public final static byte INS_CARD_LABEL = (byte) 0x3D; - // HD wallet - 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_GET_AUTHENTIKEY= (byte) 0x73; - 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_SET_EXTENDED_PUBKEY= (byte) 0x74; - 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_TRANSACTION= (byte) 0x6F; - public final static byte INS_PARSE_TRANSACTION = (byte) 0x71; - 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_RESET_2FA_KEY = (byte) 0x78; - public final static byte INS_SIGN_TRANSACTION_HASH= (byte) 0x7A; - // secure channel - public final static byte INS_INIT_SECURE_CHANNEL = (byte) 0x81; - public final static byte INS_PROCESS_SECURE_CHANNEL = (byte) 0x82; - // secure import from SeedKeeper - 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_EXPORT_TRUSTED_PUBKEY = (byte) 0xAB; - public final static byte INS_EXPORT_AUTHENTIKEY= (byte) 0xAD; - // Personalization PKI support - 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_SIGN_PKI_CSR = (byte) 0x94; - public final static byte INS_EXPORT_PKI_PUBKEY = (byte) 0x98; - public final static byte INS_LOCK_PKI = (byte) 0x99; - public final static byte INS_CHALLENGE_RESPONSE_PKI= (byte) 0x9A; - // reset to factory settings - public final static byte INS_RESET_TO_FACTORY = (byte) 0xFF; - - /**************************************** - * Error codes * - ****************************************/ - - /** Entered PIN is not correct */ - public final static short SW_PIN_FAILED = (short)0x63C0;// includes number of tries remaining - ///** DEPRECATED - Entered PIN is not correct */ - //public final static short SW_AUTH_FAILED = (short) 0x9C02; - /** Required operation is not allowed in actual circumstances */ - public final static short SW_OPERATION_NOT_ALLOWED = (short) 0x9C03; - /** Required setup is not not done */ - public final static short SW_SETUP_NOT_DONE = (short) 0x9C04; - /** Required setup is already done */ - public final static short SW_SETUP_ALREADY_DONE = (short) 0x9C07; - /** Required feature is not (yet) supported */ - final static short SW_UNSUPPORTED_FEATURE = (short) 0x9C05; - /** Required operation was not authorized because of a lack of privileges */ - public final static short SW_UNAUTHORIZED = (short) 0x9C06; - /** Algorithm specified is not correct */ - public final static short SW_INCORRECT_ALG = (short) 0x9C09; - - /** There have been memory problems on the card */ - public final static short SW_NO_MEMORY_LEFT = (short) 0x9C01; - ///** DEPRECATED - Required object is missing */ - //public final static short SW_OBJECT_NOT_FOUND= (short) 0x9C07; - - /** Incorrect P1 parameter */ - public final static short SW_INCORRECT_P1 = (short) 0x9C10; - /** Incorrect P2 parameter */ - public final static short SW_INCORRECT_P2 = (short) 0x9C11; - /** Invalid input parameter to command */ - public final static short SW_INVALID_PARAMETER = (short) 0x9C0F; - - /** Eckeys initialized */ - public final static short SW_ECKEYS_INITIALIZED_KEY = (short) 0x9C1A; - - /** Verify operation detected an invalid signature */ - public final static short SW_SIGNATURE_INVALID = (short) 0x9C0B; - /** Operation has been blocked for security reason */ - public final static short SW_IDENTITY_BLOCKED = (short) 0x9C0C; - /** For debugging purposes */ - public final static short SW_INTERNAL_ERROR = (short) 0x9CFF; - /** Very low probability error */ - public final static short SW_BIP32_DERIVATION_ERROR = (short) 0x9C0E; - /** Incorrect initialization of method */ - public final static short SW_INCORRECT_INITIALIZATION = (short) 0x9C13; - /** Bip32 seed is not initialized*/ - public final static short SW_BIP32_UNINITIALIZED_SEED = (short) 0x9C14; - /** Bip32 seed is already initialized (must be reset before change)*/ - public final static short SW_BIP32_INITIALIZED_SEED = (short) 0x9C17; - //** DEPRECATED - Bip32 authentikey pubkey is not initialized*/ - //public final static short SW_BIP32_UNINITIALIZED_AUTHENTIKEY_PUBKEY= (short) 0x9C16; - /** Incorrect transaction hash */ - public final static short SW_INCORRECT_TXHASH = (short) 0x9C15; - - /** 2FA already initialized*/ - public final static short SW_2FA_INITIALIZED_KEY = (short) 0x9C18; - /** 2FA uninitialized*/ - public final static short SW_2FA_UNINITIALIZED_KEY = (short) 0x9C19; - - /** HMAC errors */ - static final short SW_HMAC_UNSUPPORTED_KEYSIZE = (short) 0x9c1E; - static final short SW_HMAC_UNSUPPORTED_MSGSIZE = (short) 0x9c1F; - - /** Secure channel */ - 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_WRONG_IV= (short) 0x9C22; - public final static short SW_SECURE_CHANNEL_WRONG_MAC= (short) 0x9C23; - - /** Secret data is too long for import **/ - public final static short SW_IMPORTED_DATA_TOO_LONG = (short) 0x9C32; - /** Wrong HMAC when importing Secret through Secure import **/ - public final static short SW_SECURE_IMPORT_WRONG_MAC = (short) 0x9C33; - /** Wrong Fingerprint when importing Secret through Secure import **/ - public final static short SW_SECURE_IMPORT_WRONG_FINGERPRINT = (short) 0x9C34; - /** No Trusted Pubkey when importing Secret through Secure import **/ - public final static short SW_SECURE_IMPORT_NO_TRUSTEDPUBKEY = (short) 0x9C35; - - /** PKI perso error */ - public final static short SW_PKI_ALREADY_LOCKED = (short) 0x9C40; - /** CARD HAS BEEN RESET TO FACTORY */ - public final static short SW_RESET_TO_FACTORY = (short) 0xFF00; - /** For instructions that have been deprecated*/ - public final static short SW_INS_DEPRECATED = (short) 0x9C26; - /** For debugging purposes 2 */ - public final static short SW_DEBUG_FLAG = (short) 0x9FFF; - -} +package com.sparrowwallet.sparrow.io.satochip; + +public final class Constants { + + // Prevents instanciation of class + private Constants() {} + + /**************************************** + * Instruction codes * + ****************************************/ + public final static byte CLA = (byte)0xB0; + // Applet initialization + public final static byte INS_SETUP = (byte) 0x2A; + // Keys' use and management + public final static byte INS_IMPORT_KEY = (byte) 0x32; + public final static byte INS_RESET_KEY = (byte) 0x33; + public final static byte INS_GET_PUBLIC_FROM_PRIVATE= (byte)0x35; + // External authentication + 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_CHANGE_PIN = (byte) 0x44; + public final static byte INS_UNBLOCK_PIN = (byte) 0x46; + public final static byte INS_LOGOUT_ALL = (byte) 0x60; + // Status information + public final static byte INS_LIST_PINS = (byte) 0x48; + public final static byte INS_GET_STATUS = (byte) 0x3C; + public final static byte INS_CARD_LABEL = (byte) 0x3D; + // HD wallet + 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_GET_AUTHENTIKEY= (byte) 0x73; + 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_SET_EXTENDED_PUBKEY= (byte) 0x74; + 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_TRANSACTION= (byte) 0x6F; + public final static byte INS_PARSE_TRANSACTION = (byte) 0x71; + 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_RESET_2FA_KEY = (byte) 0x78; + public final static byte INS_SIGN_TRANSACTION_HASH= (byte) 0x7A; + // secure channel + public final static byte INS_INIT_SECURE_CHANNEL = (byte) 0x81; + public final static byte INS_PROCESS_SECURE_CHANNEL = (byte) 0x82; + // secure import from SeedKeeper + 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_EXPORT_TRUSTED_PUBKEY = (byte) 0xAB; + public final static byte INS_EXPORT_AUTHENTIKEY= (byte) 0xAD; + // Personalization PKI support + 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_SIGN_PKI_CSR = (byte) 0x94; + public final static byte INS_EXPORT_PKI_PUBKEY = (byte) 0x98; + public final static byte INS_LOCK_PKI = (byte) 0x99; + public final static byte INS_CHALLENGE_RESPONSE_PKI= (byte) 0x9A; + // reset to factory settings + public final static byte INS_RESET_TO_FACTORY = (byte) 0xFF; + + /**************************************** + * Error codes * + ****************************************/ + + /** Entered PIN is not correct */ + public final static short SW_PIN_FAILED = (short)0x63C0;// includes number of tries remaining + ///** DEPRECATED - Entered PIN is not correct */ + //public final static short SW_AUTH_FAILED = (short) 0x9C02; + /** Required operation is not allowed in actual circumstances */ + public final static short SW_OPERATION_NOT_ALLOWED = (short) 0x9C03; + /** Required setup is not not done */ + public final static short SW_SETUP_NOT_DONE = (short) 0x9C04; + /** Required setup is already done */ + public final static short SW_SETUP_ALREADY_DONE = (short) 0x9C07; + /** Required feature is not (yet) supported */ + final static short SW_UNSUPPORTED_FEATURE = (short) 0x9C05; + /** Required operation was not authorized because of a lack of privileges */ + public final static short SW_UNAUTHORIZED = (short) 0x9C06; + /** Algorithm specified is not correct */ + public final static short SW_INCORRECT_ALG = (short) 0x9C09; + + /** There have been memory problems on the card */ + public final static short SW_NO_MEMORY_LEFT = (short) 0x9C01; + ///** DEPRECATED - Required object is missing */ + //public final static short SW_OBJECT_NOT_FOUND= (short) 0x9C07; + + /** Incorrect P1 parameter */ + public final static short SW_INCORRECT_P1 = (short) 0x9C10; + /** Incorrect P2 parameter */ + public final static short SW_INCORRECT_P2 = (short) 0x9C11; + /** Invalid input parameter to command */ + public final static short SW_INVALID_PARAMETER = (short) 0x9C0F; + + /** Eckeys initialized */ + public final static short SW_ECKEYS_INITIALIZED_KEY = (short) 0x9C1A; + + /** Verify operation detected an invalid signature */ + public final static short SW_SIGNATURE_INVALID = (short) 0x9C0B; + /** Operation has been blocked for security reason */ + public final static short SW_IDENTITY_BLOCKED = (short) 0x9C0C; + /** For debugging purposes */ + public final static short SW_INTERNAL_ERROR = (short) 0x9CFF; + /** Very low probability error */ + public final static short SW_BIP32_DERIVATION_ERROR = (short) 0x9C0E; + /** Incorrect initialization of method */ + public final static short SW_INCORRECT_INITIALIZATION = (short) 0x9C13; + /** Bip32 seed is not initialized*/ + public final static short SW_BIP32_UNINITIALIZED_SEED = (short) 0x9C14; + /** Bip32 seed is already initialized (must be reset before change)*/ + public final static short SW_BIP32_INITIALIZED_SEED = (short) 0x9C17; + //** DEPRECATED - Bip32 authentikey pubkey is not initialized*/ + //public final static short SW_BIP32_UNINITIALIZED_AUTHENTIKEY_PUBKEY= (short) 0x9C16; + /** Incorrect transaction hash */ + public final static short SW_INCORRECT_TXHASH = (short) 0x9C15; + + /** 2FA already initialized*/ + public final static short SW_2FA_INITIALIZED_KEY = (short) 0x9C18; + /** 2FA uninitialized*/ + public final static short SW_2FA_UNINITIALIZED_KEY = (short) 0x9C19; + + /** HMAC errors */ + static final short SW_HMAC_UNSUPPORTED_KEYSIZE = (short) 0x9c1E; + static final short SW_HMAC_UNSUPPORTED_MSGSIZE = (short) 0x9c1F; + + /** Secure channel */ + 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_WRONG_IV= (short) 0x9C22; + public final static short SW_SECURE_CHANNEL_WRONG_MAC= (short) 0x9C23; + + /** Secret data is too long for import **/ + public final static short SW_IMPORTED_DATA_TOO_LONG = (short) 0x9C32; + /** Wrong HMAC when importing Secret through Secure import **/ + public final static short SW_SECURE_IMPORT_WRONG_MAC = (short) 0x9C33; + /** Wrong Fingerprint when importing Secret through Secure import **/ + public final static short SW_SECURE_IMPORT_WRONG_FINGERPRINT = (short) 0x9C34; + /** No Trusted Pubkey when importing Secret through Secure import **/ + public final static short SW_SECURE_IMPORT_NO_TRUSTEDPUBKEY = (short) 0x9C35; + + /** PKI perso error */ + public final static short SW_PKI_ALREADY_LOCKED = (short) 0x9C40; + /** CARD HAS BEEN RESET TO FACTORY */ + public final static short SW_RESET_TO_FACTORY = (short) 0xFF00; + /** For instructions that have been deprecated*/ + public final static short SW_INS_DEPRECATED = (short) 0x9C26; + /** For debugging purposes 2 */ + public final static short SW_DEBUG_FLAG = (short) 0x9FFF; + +} diff --git a/src/main/java/com/sparrowwallet/sparrow/io/satochip/KeyPath.java b/src/main/java/com/sparrowwallet/sparrow/io/satochip/KeyPath.java index dfbdaddf..28a9cce0 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/satochip/KeyPath.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/satochip/KeyPath.java @@ -6,97 +6,97 @@ import java.util.StringTokenizer; * Keypath object to be used with the SatochipCommandSet */ public class KeyPath { - private byte[] data; + private byte[] data; - /** - * 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 "/". - * - * 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, - * which indicates an hardened key. - * - * An example of a valid path is "m/44'/0'/0'/0/0" - * - * - * @param keypath the keypath as a string - */ - public KeyPath(String keypath) { - StringTokenizer tokenizer = new StringTokenizer(keypath, "/"); + /** + * 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 "/". + * + * 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, + * which indicates an hardened key. + * + * An example of a valid path is "m/44'/0'/0'/0/0" + * + * + * @param keypath the keypath as a string + */ + public KeyPath(String keypath) { + StringTokenizer tokenizer = new StringTokenizer(keypath, "/"); - String sourceOrFirstElement = tokenizer.nextToken(); // m + String sourceOrFirstElement = tokenizer.nextToken(); // m - int componentCount = tokenizer.countTokens(); - if (componentCount > 10) { - throw new IllegalArgumentException("Too many components"); + int componentCount = tokenizer.countTokens(); + if (componentCount > 10) { + 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]; - - 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; + public KeyPath(byte[] data) { + this.data = data; } - if (num.startsWith("+") || num.startsWith("-")) { - throw new NumberFormatException("No sign allowed"); - } - return (sign | Long.parseLong(num)); - } + private long parseComponent(String num) { + long sign; - private void writeComponent(long component, int i) { - int off = (i*4); - data[off] = (byte)((component >> 24) & 0xff); - data[off + 1] = (byte)((component >> 16) & 0xff); - data[off + 2] = (byte)((component >> 8) & 0xff); - data[off + 3] = (byte)(component & 0xff); - } + if (num.endsWith("'")) { + sign = 0x80000000L; + num = num.substring(0, (num.length() - 1)); + } else { + sign = 0L; + } - /** - * 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); + if (num.startsWith("+") || num.startsWith("-")) { + throw new NumberFormatException("No sign allowed"); + } + return (sign | Long.parseLong(num)); } - 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('\''); + private void writeComponent(long component, int i) { + int off = (i*4); + data[off] = (byte)((component >> 24) & 0xff); + data[off + 1] = (byte)((component >> 16) & 0xff); + data[off + 2] = (byte)((component >> 8) & 0xff); + data[off + 3] = (byte)(component & 0xff); + } + + /** + * 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('\''); + } } - } } diff --git a/src/main/java/com/sparrowwallet/sparrow/io/satochip/SatoCardApi.java b/src/main/java/com/sparrowwallet/sparrow/io/satochip/SatoCardApi.java index 8641026e..e7e18553 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/satochip/SatoCardApi.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/satochip/SatoCardApi.java @@ -57,7 +57,7 @@ public class SatoCardApi extends CardApi { log.debug("SATOCHIP SatoCardApi initialize() START"); // TODO check device certificate SatoCardStatus cardStatus = this.getStatus(); - + APDUResponse rapdu; if (!cardStatus.isSetupDone()){ byte maxPinTries = 5; @@ -134,7 +134,7 @@ public class SatoCardApi extends CardApi { String derivationString = "m"; for(int i=0;i 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. + * the basePath (from masterseed to xpub) is only provided in Satochip.java:getKeystore(String pin, List 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. * currently, we try to get the path from this.basePath if available (or use a default value) but it's not reliable enough */ @Override @@ -174,7 +174,7 @@ public class SatoCardApi extends CardApi { ExtendedKey.Header xtype = Network.get().getXpubHeader(); String xpub = this.cardProtocol.cardBip32GetXpub(keyDerivationString, xtype); log.debug("SATOCHIP SatoCardApi getKeystore() xpub: " + xpub); - + ExtendedKey extendedKey = ExtendedKey.fromDescriptor(xpub); 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() 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 signingNodes = wallet.getSigningNodes(psbt); //log.debug("SATOCHIP SatoCardApi sign() signingNodes: " + signingNodes); for(PSBTInput psbtInput : psbt.getPsbtInputs()) { if(!psbtInput.isSigned()) { WalletNode signingNode = signingNodes.get(psbtInput); 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 { - /*// debug - Map mapPubKey = psbtInput.getDerivedPublicKeys(); - log.debug("SATOCHIP SatoCardApi sign() mapPubKey.size(): " + mapPubKey.size()); - log.debug("SATOCHIP SatoCardApi sign() mapPubKey: " + mapPubKey); - for (Map.Entry 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; List keystores = wallet.getKeystores(); 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); fullPath = basePath + extendedPath; log.debug("SATOCHIP SatoCardApi sign() fullPath: " + fullPath); - + ECKey keystorePubkey = keystore.getPubKey(signingNode); log.debug("SATOCHIP SatoCardApi sign() keystore.getPubKey(signingNode): " + Utils.bytesToHex(keystorePubkey.getPubKey())); break; } - } - - //psbtInput.printDebugInfo(); + } psbtInput.sign(new CardPSBTInputSigner(signingNode, fullPath)); - //psbtInput.printDebugInfo(); } finally { } }// endif else { log.debug("SATOCHIP SatoCardApi sign() psbtInput already signed!"); - //psbtInput.printDebugInfo(); } } // endfor } @@ -313,7 +267,7 @@ public class SatoCardApi extends CardApi { try { 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. SatoCardStatus cardStatus = this.getStatus(); if (cardStatus.needs2FA()){ @@ -452,7 +406,7 @@ public class SatoCardApi extends CardApi { try { log.debug("SATOCHIP SatoCardApi.CardPSBTInputSigner sign() fullPath:" + this.fullPath); 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. SatoCardStatus cardStatus = getStatus(); if (cardStatus.needs2FA()){ @@ -461,7 +415,7 @@ public class SatoCardApi extends CardApi { // verify PIN APDUResponse rapdu0 = cardProtocol.cardVerifyPIN(0, pin); - + // derive the correct key in satochip and recover pubkey APDUResponse rapdu = cardProtocol.cardBip32GetExtendedKey(fullPath); SatochipParser parser= new SatochipParser(); @@ -485,7 +439,7 @@ public class SatoCardApi extends CardApi { boolean isCorrect = pubkey.verify(hash, txSig); log.debug("SATOCHIP SatoCardApi.CardPSBTInputSigner sign() ECDSA verify with pubkey: " + isCorrect); - return txSig; + return txSig; } else { // 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)); SchnorrSignature schnorrSig = SchnorrSignature.decode(sigBytes); TransactionSignature txSig = new TransactionSignature(schnorrSig, sigHash); - + // verify sig with outputPubkey... boolean isCorrect2 = pubkey.verify(hash, txSig); log.debug("SATOCHIP SatoCardApi.CardPSBTInputSigner sign() SCHNORR verify with outputPubkey: " + isCorrect2); return txSig; //new TransactionSignature(schnorrSig, sigHash); } - + } catch(Exception e) { e.printStackTrace(); log.error("SATOCHIP SatoCardApi.CardPSBTInputSigner sign() Exception: " + e); diff --git a/src/main/java/com/sparrowwallet/sparrow/io/satochip/SatoCardStatus.java b/src/main/java/com/sparrowwallet/sparrow/io/satochip/SatoCardStatus.java index 82d708d6..7529653b 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/satochip/SatoCardStatus.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/satochip/SatoCardStatus.java @@ -6,7 +6,7 @@ package com.sparrowwallet.sparrow.io.satochip; * Parses the result of a GET STATUS command retrieving application status. */ public class SatoCardStatus { - + private boolean setup_done= false; private boolean is_seeded= false; private boolean needs_secure_channel= false; @@ -22,7 +22,7 @@ public class SatoCardStatus { private byte PIN1_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 @@ -30,18 +30,18 @@ public class SatoCardStatus { * @throws IllegalArgumentException if the TLV does not follow the expected format */ public SatoCardStatus(APDUResponse rapdu) { - + int sw= rapdu.getSw(); - + if (sw==0x9000){ - + byte[] data= rapdu.getData(); protocol_major_version= data[0]; protocol_minor_version= data[1]; applet_major_version= data[2]; applet_minor_version= data[3]; protocol_version= (protocol_major_version<<8) + protocol_minor_version; - + if (data.length >=8){ PIN0_remaining_tries= data[4]; PUK0_remaining_tries= data[5]; @@ -54,14 +54,14 @@ public class SatoCardStatus { } if (data.length >=10){ is_seeded= (data[9]==0X00)? false : true; - } + } if (data.length >=11){ setup_done= (data[10]==0X00)? false : true; } else { setup_done= true; } if (data.length >=12){ - needs_secure_channel= (data[11]==0X00)? false : true; + needs_secure_channel= (data[11]==0X00)? false : true; } else { needs_secure_channel= false; needs_2FA= false; //default value @@ -74,7 +74,7 @@ public class SatoCardStatus { //throws IllegalArgumentException("Wrong getStatus data!"); // should not happen } } - + // getters public boolean isSeeded() { return is_seeded; @@ -98,7 +98,7 @@ public class SatoCardStatus { public byte getPin0RemainingCounter(){ return PIN0_remaining_tries; } - + public byte[] getCardVersion(){ byte[] versionBytes= new byte[4]; @@ -120,5 +120,5 @@ public class SatoCardStatus { "applet_minor_version: " + applet_minor_version; return status_info; } - + } diff --git a/src/main/java/com/sparrowwallet/sparrow/io/satochip/SatoCardTransport.java b/src/main/java/com/sparrowwallet/sparrow/io/satochip/SatoCardTransport.java index d68af463..a208d666 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/satochip/SatoCardTransport.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/satochip/SatoCardTransport.java @@ -63,7 +63,7 @@ public class SatoCardTransport { // convert back to APDUResponse... (todo?) APDUResponse rapdu = new APDUResponse(resp.getBytes()); log.trace("SATOCHIP SatoCardTransport send rapdu:" + rapdu.toHexString()); - return rapdu; + return rapdu; } void disconnect() throws CardException { diff --git a/src/main/java/com/sparrowwallet/sparrow/io/satochip/SatochipCommandSet.java b/src/main/java/com/sparrowwallet/sparrow/io/satochip/SatochipCommandSet.java index c53cfd3b..3c10a2d9 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/satochip/SatochipCommandSet.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/satochip/SatochipCommandSet.java @@ -24,7 +24,7 @@ import javax.smartcardio.*; * pre/post processing. */ public class SatochipCommandSet { - + private static final Logger log = LoggerFactory.getLogger(SatochipCommandSet.class); private final SatoCardTransport cardTransport; @@ -52,7 +52,7 @@ public class SatochipCommandSet { this.secureChannel = new SecureChannelSession(); this.parser= new SatochipParser(); } - + /** * 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. @@ -65,11 +65,11 @@ public class SatochipCommandSet { } return this.status; } - + /**************************************** * AUTHENTIKEY * ****************************************/ - + public APDUResponse cardTransmit(APDUCommand plainApdu) { log.trace("SATOCHIP: SatochipCommandSet cardTransmit() START"); @@ -115,17 +115,17 @@ public class SatochipCommandSet { // check answer if (sw12==0x9000){ // ok! if (isEncrypted){ - // decrypt + // decrypt //log.info("SATOCHIP Rapdu encrypted:"+ rapdu.toHexString()); rapdu = secureChannel.decrypt_secure_channel(rapdu); //log.info("SATOCHIP Rapdu decrypted:"+ rapdu.toHexString()); } isApduTransmitted= true; // leave loop return rapdu; - } + } // PIN authentication is required else if (sw12==0x9C06){ - //cardVerifyPIN(); + //cardVerifyPIN(); log.error("SATOCHIP: SatochipCommandSet cardTransmit() sw12==0x9C06: PIN required!"); //TODO: throw? //TODO: verify PIN? @@ -133,7 +133,7 @@ public class SatochipCommandSet { // SecureChannel is not initialized else if (sw12==0x9C21){ log.error("SATOCHIP: SatochipCommandSet cardTransmit() sw12==0x9C21: secureChannel required!"); - secureChannel.resetSecureChannel(); + secureChannel.resetSecureChannel(); } else { // cannot resolve issue at this point @@ -145,12 +145,12 @@ public class SatochipCommandSet { log.warn("SATOCHIP: SatochipCommandSet cardTransmit() Exception: "+ e); return new APDUResponse(new byte[0], (byte)0x00, (byte)0x00); // return empty APDUResponse } - + } while(!isApduTransmitted); - + return new APDUResponse(new byte[0], (byte)0x00, (byte)0x00); // should not happen } - + public void cardDisconnect(){ secureChannel.resetSecureChannel(); status= null; @@ -161,37 +161,37 @@ public class SatochipCommandSet { log.error("SATOCHIP SatochipCommandSet cardDisconnect() Exception: " + e); } } - + public APDUResponse cardGetStatus() { APDUCommand plainApdu = new APDUCommand(0xB0, INS_GET_STATUS, 0x00, 0x00, new byte[0]); - + log.trace("SATOCHIP SatochipCommandSet cardGetStatus() capdu: "+ plainApdu.toHexString()); APDUResponse respApdu = this.cardTransmit(plainApdu); log.trace("SATOCHIP SatochipCommandSet cardGetStatus() rapdu: "+ respApdu.toHexString()); - + this.status= new SatoCardStatus(respApdu); log.debug("SATOCHIP SatochipCommandSet cardGetStatus(): "+ this.status.toString()); - + return respApdu; } - + public APDUResponse cardInitiateSecureChannel() throws CardException{ - + byte[] pubkey= secureChannel.getPublicKey(); - + APDUCommand plainApdu = new APDUCommand(0xB0, INS_INIT_SECURE_CHANNEL, 0x00, 0x00, pubkey); - + log.trace("SATOCHIP SatochipCommandSet cardInitiateSecureChannel capdu: "+ plainApdu.toHexString()); APDUResponse respApdu = this.cardTransport.send(plainApdu); log.trace("SATOCHIP SatochipCommandSet cardInitiateSecureChannel rapdu: "+ respApdu.toHexString()); - + return respApdu; - } - + } + /**************************************** * CARD MGMT * ****************************************/ - + public APDUResponse cardSetup(byte pin_tries0, byte[] pin0){ log.debug("SATOCHIP SatochipCommandSet cardSetup()"); // use random values for pin1, ublk0, ublk1 @@ -202,19 +202,19 @@ public class SatochipCommandSet { random.nextBytes(ublk0); random.nextBytes(ublk1); random.nextBytes(pin1); - + byte ublk_tries0=(byte)0x01; byte ublk_tries1=(byte)0x01; byte pin_tries1=(byte)0x01; - + return cardSetup(pin_tries0, ublk_tries0, pin0, ublk0, pin_tries1, ublk_tries1, pin1, ublk1); } - + public APDUResponse cardSetup( byte pin_tries0, byte ublk_tries0, byte[] pin0, byte[] ublk0, byte pin_tries1, byte ublk_tries1, byte[] pin1, byte[] ublk1){ log.debug("SATOCHIP SatochipCommandSet cardSetup()"); - + byte[] pin={0x4D, 0x75, 0x73, 0x63, 0x6C, 0x65, 0x30, 0x30}; //default pin byte cla= (byte) 0xB0; byte ins= INS_SETUP; @@ -269,25 +269,25 @@ public class SatochipCommandSet { log.trace("SATOCHIP SatochipCommandSet cardSetup capdu: "+ plainApdu.toHexString()); APDUResponse respApdu = this.cardTransmit(plainApdu); log.trace("SATOCHIP SatochipCommandSet cardSetup rapdu:"+ respApdu.toHexString()); - + if (respApdu.getSw() == 0x9000){ - //setPin0(pin0); // todo: cache value... + //setPin0(pin0); // todo: cache value... } else { log.error("SATOCHIP SatochipCommandSet cardSetup error:"+ respApdu.toHexString()); } - return respApdu; + return respApdu; } - - + + /**************************************** * PIN MGMT * ****************************************/ - + public APDUResponse cardVerifyPIN() { return this.cardVerifyPIN((byte)0, pinCached); } - + public APDUResponse cardVerifyPIN(int pinNbr, String pin) { log.debug("SATOCHIP SatochipCommandSet cardVerifyPIN()"); if (pin == null){ @@ -296,34 +296,34 @@ public class SatochipCommandSet { throw new RuntimeException("PIN required!"); } pin = this.pinCached; - } + } byte[] pinBytes = pin.getBytes(StandardCharsets.UTF_8); APDUCommand capdu = new APDUCommand(0xB0, INS_VERIFY_PIN, (byte)pinNbr, 0x00, pinBytes); log.trace("SATOCHIP SatochipCommandSet cardVerifyPIN() capdu:"+ capdu.toHexString()); APDUResponse rapdu = this.cardTransmit(capdu); log.trace("SATOCHIP SatochipCommandSet cardVerifyPIN() rapdu: "+ rapdu.toHexString()); - + // correct PIN: cache PIN value int sw = rapdu.getSw(); if (sw == 0x9000){ this.pinCached = pin; //set cached PIN value } // 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 int pinLeft= (sw & ~0xffc0); throw new RuntimeException("Wrong PIN, remaining tries: " + pinLeft); } - // wrong PIN (legacy before v0.11) - else if (sw == 0x9c02){ + // wrong PIN (legacy before v0.11) + else if (sw == 0x9c02){ this.pinCached = null; //reset cached PIN value SatoCardStatus cardStatus = this.getApplicationStatus(); int pinLeft= cardStatus.getPin0RemainingCounter(); throw new RuntimeException("Wrong PIN, remaining tries: " + pinLeft); } // blocked PIN - else if (sw == 0x9c0c){ + else if (sw == 0x9c0c){ throw new RuntimeException("Card is blocked!"); } return rapdu; @@ -348,19 +348,19 @@ public class SatochipCommandSet { log.trace("SATOCHIP SatochipCommandSet cardChangePIN() capdu: "+ capdu.toHexString()); APDUResponse rapdu = this.cardTransmit(capdu); log.trace("SATOCHIP SatochipCommandSet cardChangePIN() rapdu: "+ rapdu.toHexString()); - + // correct PIN: cache PIN value int sw = rapdu.getSw(); if (sw == 0x9000){ - this.pinCached = newPin; + this.pinCached = newPin; } // wrong PIN, get remaining tries available (since v0.11) - else if ((sw & 0xffc0) == 0x63c0){ + else if ((sw & 0xffc0) == 0x63c0){ int pinLeft= (sw & ~0xffc0); throw new RuntimeException("Wrong PIN, remaining tries: " + pinLeft); } - // wrong PIN (legacy before v0.11) - else if (sw == 0x9c02){ + // wrong PIN (legacy before v0.11) + else if (sw == 0x9c02){ SatoCardStatus cardStatus = this.getApplicationStatus(); int pinLeft= cardStatus.getPin0RemainingCounter(); throw new RuntimeException("Wrong PIN, remaining tries: " + pinLeft); @@ -375,7 +375,7 @@ public class SatochipCommandSet { /**************************************** * BIP32 * ****************************************/ - + public APDUResponse cardBip32ImportSeed(byte[] masterseed){ //TODO: check seed (length...) APDUCommand plainApdu = new APDUCommand(0xB0, INS_BIP32_IMPORT_SEED, masterseed.length, 0x00, masterseed); @@ -386,10 +386,10 @@ public class SatochipCommandSet { return respApdu; } - + public APDUResponse cardBip32GetExtendedKey(String stringPath){ log.debug("SATOCHIP SatochipCommandSet cardBip32GetExtendedKey() stringPath: " + stringPath); - KeyPath keyPath = new KeyPath(stringPath); + KeyPath keyPath = new KeyPath(stringPath); byte[] bytePath = keyPath.getData(); //log.trace("SATOCHIP SatochipCommandSet cardBip32GetExtendedKey() bytePath: " + Utils.bytesToHex(bytePath)); return cardBip32GetExtendedKey(bytePath); @@ -398,38 +398,38 @@ public class SatochipCommandSet { public APDUResponse cardBip32GetExtendedKey(byte[] bytePath){ log.debug("SATOCHIP SatochipCommandSet cardBip32GetExtendedKey() bytePath: " + Utils.bytesToHex(bytePath)); byte p1= (byte) (bytePath.length/4); - + APDUCommand plainApdu = new APDUCommand(0xB0, INS_BIP32_GET_EXTENDED_KEY, p1, 0x40, bytePath); log.trace("SATOCHIP SatochipCommandSet cardBip32GetExtendedKey() capdu: "+ plainApdu.toHexString()); APDUResponse respApdu = this.cardTransmit(plainApdu); log.trace("SATOCHIP SatochipCommandSet cardBip32GetExtendedKey() rapdu: "+ respApdu.toHexString()); // TODO: check SW code for particular status - + // TODO: parse apdu to extract data? return respApdu; - } - - /* + } + + /* * 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) * xtype (str): the type of transaction such as 'standard', 'p2wpkh-p2sh', 'p2wpkh', 'p2wsh-p2sh', 'p2wsh' - * is_mainnet (bool): is mainnet or testnet - * - * Return: + * is_mainnet (bool): is mainnet or testnet + * + * Return: * xpub (str): the corresponding xpub value */ public String cardBip32GetXpub(String stringPath, ExtendedKey.Header xtype){ // path is of the form 44'/0'/1' log.debug("SATOCHIP SatochipCommandSet cardBip32GetXpub() path: " + stringPath); - KeyPath keyPath = new KeyPath(stringPath); + KeyPath keyPath = new KeyPath(stringPath); byte[] bytePath = keyPath.getData(); int depth = bytePath.length/4; log.debug("SATOCHIP SatochipCommandSet cardBip32GetXpub() bytePath: " + Utils.bytesToHex(bytePath)); - + APDUResponse rapdu = this.cardBip32GetExtendedKey(bytePath); 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() Network.get().getXpubHeader(): " + Network.get().getXpubHeader()); - + ByteBuffer buffer = ByteBuffer.allocate(78); buffer.putInt(xtype.getHeader()); buffer.put((byte) depth); @@ -494,24 +494,24 @@ public class SatochipCommandSet { throw new RuntimeException("Wrong challenge-response length (should be 20)"); } APDUCommand plainApdu = new APDUCommand(0xB0, INS_SIGN_TRANSACTION_HASH, keynbr, 0x00, data); - + log.trace("SATOCHIP SatochipCommandSet cardSignTransactionHash() capdu: "+ plainApdu.toHexString()); APDUResponse respApdu = this.cardTransmit(plainApdu); log.trace("SATOCHIP SatochipCommandSet cardSignTransactionHash() rapdu: "+ respApdu.toHexString()); // TODO: check SW code for particular status - + return respApdu; } - - /** + + /** * 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. * - * ins: 0x7B - * p1: key number or 0xFF for the last derived Bip32 extended key - * p2: 0x00 + * If 2FA is enabled, a HMAC must be provided as an additional security layer. * + * ins: 0x7B + * p1: key number or 0xFF for the last derived Bip32 extended key + * p2: 0x00 * data: [hash(32b) | option: 2FA-flag(2b)|hmac(20b)] * return: [sig] - * + * */ public APDUResponse cardSignSchnorrHash(byte keynbr, byte[] txhash, byte[] chalresponse){ log.debug("SATOCHIP SatochipCommandSet cardSignSchnorrHash()"); @@ -535,23 +535,23 @@ public class SatochipCommandSet { throw new RuntimeException("Wrong challenge-response length (should be 20)"); } APDUCommand plainApdu = new APDUCommand(0xB0, 0x7B, keynbr, 0x00, data); - + log.trace("SATOCHIP SatochipCommandSet cardSignSchnorrHash() capdu: "+ plainApdu.toHexString()); APDUResponse respApdu = this.cardTransmit(plainApdu); log.trace("SATOCHIP SatochipCommandSet cardSignSchnorrHash() rapdu: "+ respApdu.toHexString()); // TODO: check SW code for particular status - + return respApdu; } - /** + /** * 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: * https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs - * - * ins: 0x7C - * p1: key number or 0xFF for the last derived Bip32 extended key - * p2: 0x00 + * + * ins: 0x7C + * p1: key number or 0xFF for the last derived Bip32 extended key + * p2: 0x00 * data: [hash(32b) | option: 2FA-flag(2b)|hmac(20b)] * return: [sig] */ @@ -567,14 +567,14 @@ public class SatochipCommandSet { data= new byte[33]; data[0]= (byte)32; System.arraycopy(tweak, 0, data, 1, tweak.length); - + APDUCommand plainApdu = new APDUCommand(0xB0, 0x7C, keynbr, 0x00, data); - + log.trace("SATOCHIP SatochipCommandSet cardTaprootTweakPrivkey() capdu: "+ plainApdu.toHexString()); APDUResponse respApdu = this.cardTransmit(plainApdu); log.trace("SATOCHIP SatochipCommandSet cardTaprootTweakPrivkey() rapdu: "+ respApdu.toHexString()); // TODO: check SW code for particular status - + return respApdu; } @@ -582,10 +582,10 @@ public class SatochipCommandSet { * 2FA commands * ****************************************/ //todo - - + + /**************************************** * PKI commands * - ****************************************/ + ****************************************/ // todo } diff --git a/src/main/java/com/sparrowwallet/sparrow/io/satochip/SatochipParser.java b/src/main/java/com/sparrowwallet/sparrow/io/satochip/SatochipParser.java index 862f1eee..3c80d6ba 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/satochip/SatochipParser.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/satochip/SatochipParser.java @@ -1,155 +1,155 @@ -package com.sparrowwallet.sparrow.io.satochip; - -import com.sparrowwallet.drongo.protocol.Sha256Hash; -import com.sparrowwallet.drongo.crypto.ECKey; -import com.sparrowwallet.drongo.crypto.ECDSASignature; -import com.sparrowwallet.drongo.Utils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.math.BigInteger; -import java.io.IOException; -import java.util.Arrays; -import java.util.Base64; - -public class SatochipParser{ - - private static final Logger log = LoggerFactory.getLogger(SatochipParser.class); - - private byte[] authentikey= null; - - public SatochipParser(){ - } - - /**************************************** - * PARSER * - ****************************************/ - - public byte[] parseInitiateSecureChannel(APDUResponse rapdu){ - - try{ - byte[] data= rapdu.getData(); - log.trace("SATOCHIP SatochipParser parseInitiateSecureChannel() data: " + Utils.bytesToHex(data)); - - // data= [coordxSize | coordx | sig1Size | sig1 | sig2Size | sig2] - int offset=0; - int coordxSize= 256*data[offset++] + data[offset++]; - - byte[] coordx= new byte[coordxSize]; - System.arraycopy(data, offset, coordx, 0, coordxSize); - offset+=coordxSize; - - // msg1 is [coordx_size | coordx] - byte[] msg1= new byte[2+coordxSize]; - System.arraycopy(data, 0, msg1, 0, msg1.length); - - int sig1Size= 256*data[offset++] + data[offset++]; - byte[] sig1= new byte[sig1Size]; - System.arraycopy(data, offset, sig1, 0, sig1Size); - offset+=sig1Size; - - // msg2 is [coordxSize | coordx | sig1Size | sig1] - byte[] msg2= new byte[2+coordxSize + 2 + sig1Size]; - System.arraycopy(data, 0, msg2, 0, msg2.length); - - int sig2Size= 256*data[offset++] + data[offset++]; - byte[] sig2= new byte[sig2Size]; - System.arraycopy(data, offset, sig2, 0, sig2Size); - offset+=sig2Size; - - byte[] pubkey= recoverPubkey(msg1, sig1, coordx, false); // false: uncompressed - log.trace("SATOCHIP SatochipParser parseInitiateSecureChannel() pubkey: " + Utils.bytesToHex(pubkey)); - - // todo: recover from sig2 - - return pubkey; - } catch(Exception e) { - log.error("SATOCHIP SatochipParser parseInitiateSecureChannel() exception: " + e); - throw new RuntimeException("SATOCHIP SatochipParser parseInitiateSecureChannel() exception: ", e); - } - - } - - public byte[][] parseBip32GetExtendedKey(APDUResponse rapdu){ - log.trace("SATOCHIP SatochipParser parseBip32GetExtendedKey()"); - try{ - byte[][] extendedkey = new byte[2][]; - extendedkey[0] = new byte[33]; // pubkey - extendedkey[1] = new byte[32]; // chaincode - - byte[] data= rapdu.getData(); - log.trace("SATOCHIP SatochipParser parseBip32GetExtendedKey data: " + Utils.bytesToHex(data)); - //data: [chaincode(32b) | coordx_size(2b) | coordx | sig_size(2b) | sig | sig_size(2b) | sig2] - - int offset=0; - byte[] chaincode= new byte[32]; - System.arraycopy(data, offset, chaincode, 0, chaincode.length); - offset+=32; - - int coordxSize= 256*(data[offset++] & 0x7f) + data[offset++]; // (data[32] & 0x80) is ignored (optimization flag) - byte[] coordx= new byte[coordxSize]; - System.arraycopy(data, offset, coordx, 0, coordxSize); - offset+=coordxSize; - - // msg1 is [chaincode | coordx_size | coordx] - byte[] msg1= new byte[32+2+coordxSize]; - System.arraycopy(data, 0, msg1, 0, msg1.length); - - int sig1Size= 256*data[offset++] + data[offset++]; - byte[] sig1= new byte[sig1Size]; - System.arraycopy(data, offset, sig1, 0, sig1Size); - offset+=sig1Size; - - // msg2 is [chaincode | coordxSize | coordx | sig1Size | sig1] - byte[] msg2= new byte[32 + 2+coordxSize + 2 + sig1Size]; - System.arraycopy(data, 0, msg2, 0, msg2.length); - - int sig2Size= 256*data[offset++] + data[offset++]; - byte[] sig2= new byte[sig2Size]; - System.arraycopy(data, offset, sig2, 0, sig2Size); - offset+=sig2Size; - - byte[] pubkey= recoverPubkey(msg1, sig1, coordx, true); // true: compressed (33 bytes) - log.trace("SATOCHIP SatochipParser parseBip32GetExtendedKey pubkey: " + Utils.bytesToHex(pubkey)); - - // todo: recover from si2 - System.arraycopy(pubkey, 0, extendedkey[0], 0, pubkey.length); - System.arraycopy(chaincode, 0, extendedkey[1], 0, chaincode.length); - return extendedkey; - - } catch(Exception e) { - log.error("SATOCHIP SatochipParser parseBip32GetExtendedKey() exception: " + e); - throw new RuntimeException("SATOCHIP SatochipParser parseBip32GetExtendedKey() exception: ", e); - } - - } - - /**************************************** - * recovery methods * - ****************************************/ - - public byte[] recoverPubkey(byte[] msg, byte[] dersig, byte[] coordx, Boolean compressed) { - log.trace("SATOCHIP SatochipParser recoverPubkey() coordx: " + Utils.bytesToHex(coordx)); - - // convert msg to hash - //byte[] hash = Sha256Hash.hash(msg); - ECDSASignature ecdsaSig = ECDSASignature.decodeFromDER(dersig); - - byte recId = -1; - ECKey k = null; - for(byte i = 0; i < 4; i++) { - k = ECKey.recoverFromSignature(i, ecdsaSig, Sha256Hash.of(msg), compressed); - if(k != null && Arrays.equals(k.getPubKeyXCoord(),coordx)) { - recId = i; - break; - } - } - if(recId == -1) { - 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())); - return k.getPubKey(); - } - +package com.sparrowwallet.sparrow.io.satochip; + +import com.sparrowwallet.drongo.protocol.Sha256Hash; +import com.sparrowwallet.drongo.crypto.ECKey; +import com.sparrowwallet.drongo.crypto.ECDSASignature; +import com.sparrowwallet.drongo.Utils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.math.BigInteger; +import java.io.IOException; +import java.util.Arrays; +import java.util.Base64; + +public class SatochipParser{ + + private static final Logger log = LoggerFactory.getLogger(SatochipParser.class); + + private byte[] authentikey= null; + + public SatochipParser(){ + } + + /**************************************** + * PARSER * + ****************************************/ + + public byte[] parseInitiateSecureChannel(APDUResponse rapdu){ + + try{ + byte[] data= rapdu.getData(); + log.trace("SATOCHIP SatochipParser parseInitiateSecureChannel() data: " + Utils.bytesToHex(data)); + + // data= [coordxSize | coordx | sig1Size | sig1 | sig2Size | sig2] + int offset=0; + int coordxSize= 256*data[offset++] + data[offset++]; + + byte[] coordx= new byte[coordxSize]; + System.arraycopy(data, offset, coordx, 0, coordxSize); + offset+=coordxSize; + + // msg1 is [coordx_size | coordx] + byte[] msg1= new byte[2+coordxSize]; + System.arraycopy(data, 0, msg1, 0, msg1.length); + + int sig1Size= 256*data[offset++] + data[offset++]; + byte[] sig1= new byte[sig1Size]; + System.arraycopy(data, offset, sig1, 0, sig1Size); + offset+=sig1Size; + + // msg2 is [coordxSize | coordx | sig1Size | sig1] + byte[] msg2= new byte[2+coordxSize + 2 + sig1Size]; + System.arraycopy(data, 0, msg2, 0, msg2.length); + + int sig2Size= 256*data[offset++] + data[offset++]; + byte[] sig2= new byte[sig2Size]; + System.arraycopy(data, offset, sig2, 0, sig2Size); + offset+=sig2Size; + + byte[] pubkey= recoverPubkey(msg1, sig1, coordx, false); // false: uncompressed + log.trace("SATOCHIP SatochipParser parseInitiateSecureChannel() pubkey: " + Utils.bytesToHex(pubkey)); + + // todo: recover from sig2 + + return pubkey; + } catch(Exception e) { + log.error("SATOCHIP SatochipParser parseInitiateSecureChannel() exception: " + e); + throw new RuntimeException("SATOCHIP SatochipParser parseInitiateSecureChannel() exception: ", e); + } + + } + + public byte[][] parseBip32GetExtendedKey(APDUResponse rapdu){ + log.trace("SATOCHIP SatochipParser parseBip32GetExtendedKey()"); + try{ + byte[][] extendedkey = new byte[2][]; + extendedkey[0] = new byte[33]; // pubkey + extendedkey[1] = new byte[32]; // chaincode + + byte[] data= rapdu.getData(); + log.trace("SATOCHIP SatochipParser parseBip32GetExtendedKey data: " + Utils.bytesToHex(data)); + //data: [chaincode(32b) | coordx_size(2b) | coordx | sig_size(2b) | sig | sig_size(2b) | sig2] + + int offset=0; + byte[] chaincode= new byte[32]; + System.arraycopy(data, offset, chaincode, 0, chaincode.length); + offset+=32; + + int coordxSize= 256*(data[offset++] & 0x7f) + data[offset++]; // (data[32] & 0x80) is ignored (optimization flag) + byte[] coordx= new byte[coordxSize]; + System.arraycopy(data, offset, coordx, 0, coordxSize); + offset+=coordxSize; + + // msg1 is [chaincode | coordx_size | coordx] + byte[] msg1= new byte[32+2+coordxSize]; + System.arraycopy(data, 0, msg1, 0, msg1.length); + + int sig1Size= 256*data[offset++] + data[offset++]; + byte[] sig1= new byte[sig1Size]; + System.arraycopy(data, offset, sig1, 0, sig1Size); + offset+=sig1Size; + + // msg2 is [chaincode | coordxSize | coordx | sig1Size | sig1] + byte[] msg2= new byte[32 + 2+coordxSize + 2 + sig1Size]; + System.arraycopy(data, 0, msg2, 0, msg2.length); + + int sig2Size= 256*data[offset++] + data[offset++]; + byte[] sig2= new byte[sig2Size]; + System.arraycopy(data, offset, sig2, 0, sig2Size); + offset+=sig2Size; + + byte[] pubkey= recoverPubkey(msg1, sig1, coordx, true); // true: compressed (33 bytes) + log.trace("SATOCHIP SatochipParser parseBip32GetExtendedKey pubkey: " + Utils.bytesToHex(pubkey)); + + // todo: recover from si2 + System.arraycopy(pubkey, 0, extendedkey[0], 0, pubkey.length); + System.arraycopy(chaincode, 0, extendedkey[1], 0, chaincode.length); + return extendedkey; + + } catch(Exception e) { + log.error("SATOCHIP SatochipParser parseBip32GetExtendedKey() exception: " + e); + throw new RuntimeException("SATOCHIP SatochipParser parseBip32GetExtendedKey() exception: ", e); + } + + } + + /**************************************** + * recovery methods * + ****************************************/ + + public byte[] recoverPubkey(byte[] msg, byte[] dersig, byte[] coordx, Boolean compressed) { + log.trace("SATOCHIP SatochipParser recoverPubkey() coordx: " + Utils.bytesToHex(coordx)); + + // convert msg to hash + //byte[] hash = Sha256Hash.hash(msg); + ECDSASignature ecdsaSig = ECDSASignature.decodeFromDER(dersig); + + byte recId = -1; + ECKey k = null; + for(byte i = 0; i < 4; i++) { + k = ECKey.recoverFromSignature(i, ecdsaSig, Sha256Hash.of(msg), compressed); + if(k != null && Arrays.equals(k.getPubKeyXCoord(),coordx)) { + recId = i; + break; + } + } + if(recId == -1) { + 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())); + return k.getPubKey(); + } + } \ No newline at end of file diff --git a/src/main/java/com/sparrowwallet/sparrow/io/satochip/SecureChannelSession.java b/src/main/java/com/sparrowwallet/sparrow/io/satochip/SecureChannelSession.java index a77396c3..713e7e87 100644 --- a/src/main/java/com/sparrowwallet/sparrow/io/satochip/SecureChannelSession.java +++ b/src/main/java/com/sparrowwallet/sparrow/io/satochip/SecureChannelSession.java @@ -27,14 +27,14 @@ import org.slf4j.LoggerFactory; * Handles a SecureChannel session with the card. */ public class SecureChannelSession { - + private static final Logger log = LoggerFactory.getLogger(SecureChannelSession.class); - + public static final int SC_SECRET_LENGTH = 16; - public static final int SC_BLOCK_SIZE = 16; - public static final int IV_SIZE = 16; + public static final int SC_BLOCK_SIZE = 16; + public static final int IV_SIZE = 16; public static final int MAC_SIZE= 20; - + // secure channel constants private final static byte INS_INIT_SECURE_CHANNEL = (byte) 0x81; 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 boolean initialized_secure_channel= false; - + // secure channel keys private byte[] secret; private byte[] iv; private int ivCounter; byte[] derived_key; byte[] mac_key; - + // for ECDH private SecretPoint secretPoint; private ECKey eckey; - + // for session encryption private SecureRandom random; private AESKeyCrypter aesCipher; - + /** * Constructs a SecureChannel session on the client. */ public SecureChannelSession() { random = new SecureRandom(); - + try { // generate keypair eckey = new ECKey(); @@ -75,8 +75,8 @@ public class SecureChannelSession { 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 * 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 log.trace("SATOCHIP SecureChannelSession initiateSecureChannel()"); try { - + byte[] privkeyData = this.eckey.getPrivKeyBytes(); secretPoint = new SecretPoint(privkeyData, pubkeyData); secret = secretPoint.ECDHSecretAsBytes(); @@ -109,17 +109,17 @@ public class SecureChannelSession { log.error("SATOCHIP SecureChannelSession initiateSecureChannel() exception:" + e); } } - + public APDUCommand encrypt_secure_channel(APDUCommand plainApdu){ log.trace("SATOCHIP: SecureChannelSession encrypt_secure_channel()"); try { - + byte[] plainBytes= plainApdu.serialize(); // set iv iv = new byte[SC_BLOCK_SIZE]; random.nextBytes(iv); - ByteBuffer bb = ByteBuffer.allocate(4); + ByteBuffer bb = ByteBuffer.allocate(4); bb.putInt(ivCounter); // big endian byte[] ivCounterBytes= bb.array(); System.arraycopy(ivCounterBytes, 0, iv, 12, 4); @@ -158,23 +158,23 @@ public class SecureChannelSession { data[offset++]= (byte)(mac.length%256); System.arraycopy(mac, 0, data, offset, mac.length); log.trace("SATOCHIP: SecureChannelSession encrypt_secure_channel() full_encrypted_data: " + Utils.bytesToHex(data)); - + // convert to C-APDU APDUCommand encryptedApdu= new APDUCommand(0xB0, INS_PROCESS_SECURE_CHANNEL, 0x00, 0x00, data); return encryptedApdu; - + } catch (Exception e) { e.printStackTrace(); log.error("SATOCHIP: Exception in encrypt_secure_channel: "+ e); throw new RuntimeException("SATOCHIP: Exception in encrypt_secure_channel:", e); } - + } - + public APDUResponse decrypt_secure_channel(APDUResponse encryptedApdu){ log.trace("SATOCHIP SecureChannelSession decrypt_secure_channel()"); try { - + byte[] encryptedBytes= encryptedApdu.getData(); if (encryptedBytes.length==0){ return encryptedApdu; // no decryption needed @@ -204,30 +204,30 @@ public class SecureChannelSession { APDUResponse plainResponse= new APDUResponse(decrypted, (byte)0x90, (byte)0x00); return plainResponse; - + } catch (Exception e) { e.printStackTrace(); log.error("SATOCHIP SecureChannelSession decrypt_secure_channel() Exception: " + e); throw new RuntimeException("Exception during secure channel decryption: ", e); } - + } - + public boolean initializedSecureChannel(){ return initialized_secure_channel; } - + public byte[] getPublicKey(){ log.trace("SATOCHIP SecureChannelSession getPublicKey() eckey.getPubKey(): " + Utils.bytesToHex(eckey.getPubKey(false))); return eckey.getPubKey(false); // false: uncompressed } - + public void resetSecureChannel(){ initialized_secure_channel= false; // todo: generate new eckey? return; } - + public static byte[] getHmacSha1Hash(byte[] key, byte[] data) { try{ //log.trace("SATOCHIP SecureChannelSession getHmacSha1Hash() START"); diff --git a/src/main/java/com/sparrowwallet/sparrow/keystoreimport/HwAirgappedController.java b/src/main/java/com/sparrowwallet/sparrow/keystoreimport/HwAirgappedController.java index f2a3ce92..dd749ce5 100644 --- a/src/main/java/com/sparrowwallet/sparrow/keystoreimport/HwAirgappedController.java +++ b/src/main/java/com/sparrowwallet/sparrow/keystoreimport/HwAirgappedController.java @@ -23,7 +23,6 @@ public class HwAirgappedController extends KeystoreImportDetailController { private Accordion importAccordion; public void initializeView() { - log.debug("SATOCHIP HwAirgappedController START"); List fileImporters = Collections.emptyList(); 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()); @@ -40,7 +39,6 @@ public class HwAirgappedController extends KeystoreImportDetailController { } } - log.debug("SATOCHIP HwAirgappedController initializeView() - BEFORE cardImporters"); List cardImporters = List.of(new Tapsigner(), new Satochip()); for(KeystoreCardImport importer : cardImporters) { 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())); } diff --git a/src/main/java/com/sparrowwallet/sparrow/keystoreimport/HwUsbDevicesController.java b/src/main/java/com/sparrowwallet/sparrow/keystoreimport/HwUsbDevicesController.java index 05db183a..c99f9a9b 100644 --- a/src/main/java/com/sparrowwallet/sparrow/keystoreimport/HwUsbDevicesController.java +++ b/src/main/java/com/sparrowwallet/sparrow/keystoreimport/HwUsbDevicesController.java @@ -12,9 +12,7 @@ public class HwUsbDevicesController extends KeystoreImportDetailController { private Accordion deviceAccordion; public void initializeView(List devices) { - //log.debug("SATOCHIP HwUsbDevicesController initializeView START"); for(Device device : devices) { - //log.debug("SATOCHIP HwUsbDevicesController initializeView device: " + device); DevicePane devicePane = new DevicePane(getMasterController().getWallet(), device, devices.size() == 1, getMasterController().getRequiredDerivation()); if(getMasterController().getRequiredModel() == null || getMasterController().getRequiredModel() == device.getModel()) { deviceAccordion.getPanes().add(devicePane);