diff --git a/android/build.gradle b/android/build.gradle index 3fcbd0c..5880608 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -41,5 +41,5 @@ dependencies { implementation 'com.facebook.react:react-native:+' implementation 'org.bouncycastle:bcprov-jdk15on:1.60' implementation 'org.apache.commons:commons-lang3:3.9' - implementation 'com.github.status-im.status-keycard-java:android:2.2.1' + implementation 'com.github.status-im.status-keycard-java:android:3.0.1' } diff --git a/android/src/main/java/im/status/ethereum/keycard/RNStatusKeycardModule.java b/android/src/main/java/im/status/ethereum/keycard/RNStatusKeycardModule.java index fcbdd3e..ef6ca9e 100644 --- a/android/src/main/java/im/status/ethereum/keycard/RNStatusKeycardModule.java +++ b/android/src/main/java/im/status/ethereum/keycard/RNStatusKeycardModule.java @@ -232,6 +232,20 @@ public class RNStatusKeycardModule extends ReactContextBaseJavaModule implements }).start(); } + @ReactMethod + public void signWithPath(final String pairing, final String pin, final String path, final String hash, final Promise promise) { + new Thread(new Runnable() { + public void run() { + try { + promise.resolve(smartCard.signWithPath(pairing, pin, path, hash)); + } catch (IOException | APDUException e) { + Log.d(TAG, e.getMessage()); + promise.reject(e); + } + } + }).start(); + } + @ReactMethod public void signPinless(final String hash, final Promise promise) { new Thread(new Runnable() { diff --git a/android/src/main/java/im/status/ethereum/keycard/SmartCard.java b/android/src/main/java/im/status/ethereum/keycard/SmartCard.java index 32ef34a..99db052 100644 --- a/android/src/main/java/im/status/ethereum/keycard/SmartCard.java +++ b/android/src/main/java/im/status/ethereum/keycard/SmartCard.java @@ -277,8 +277,10 @@ public class SmartCard extends BroadcastReceiver implements CardListener { KeyPath currentPath = new KeyPath(cmdSet.getStatus(KeycardCommandSet.GET_STATUS_P1_KEY_PATH).checkOK().getData()); Log.i(TAG, "Current key path: " + currentPath); - cmdSet.deriveKey(path).checkOK(); - Log.i(TAG, "Derived " + path); + if (!currentPath.toString().equals(path)) { + cmdSet.deriveKey(path).checkOK(); + Log.i(TAG, "Derived " + path); + } } public String exportKey(final String pairingBase64, final String pin) throws IOException, APDUException { @@ -312,13 +314,13 @@ public class SmartCard extends BroadcastReceiver implements CardListener { cmdSet.verifyPIN(pin).checkOK(); Log.i(TAG, "pin verified"); - byte[] tlvMaster = cmdSet.exportKey(MASTER_PATH, true, true).checkOK().getData(); + byte[] tlvMaster = cmdSet.exportKey(MASTER_PATH, false, true).checkOK().getData(); BIP32KeyPair masterPair = BIP32KeyPair.fromTLV(tlvMaster); - byte[] tlvRoot = cmdSet.exportKey(ROOT_PATH, true, true).checkOK().getData(); + byte[] tlvRoot = cmdSet.exportKey(ROOT_PATH, false, true).checkOK().getData(); BIP32KeyPair keyPair = BIP32KeyPair.fromTLV(tlvRoot); - byte[] tlv = cmdSet.exportKey(WALLET_PATH, true, true).checkOK().getData(); + byte[] tlv = cmdSet.exportKey(WALLET_PATH, false, true).checkOK().getData(); BIP32KeyPair walletKeyPair = BIP32KeyPair.fromTLV(tlv); byte[] tlv2 = cmdSet.exportKey(WHISPER_PATH, false, false).checkOK().getData(); @@ -362,31 +364,23 @@ public class SmartCard extends BroadcastReceiver implements CardListener { byte[] seed = Mnemonic.toBinarySeed(mnemonic, ""); BIP32KeyPair keyPair = BIP32KeyPair.fromBinarySeed(seed); - cmdSet.loadKey(keyPair); + cmdSet.loadKey(keyPair).checkOK(); log("keypair loaded to card"); - cmdSet.deriveKey(ROOT_PATH).checkOK(); + byte[] tlvMaster = cmdSet.exportKey(ROOT_PATH, false, true).checkOK().getData(); Log.i(TAG, "Derived " + ROOT_PATH); + BIP32KeyPair rootKeyPair = BIP32KeyPair.fromTLV(tlvMaster); - byte[] tlvRoot = cmdSet.exportCurrentKey(true).checkOK().getData(); - BIP32KeyPair rootKeyPair = BIP32KeyPair.fromTLV(tlvRoot); - - cmdSet.deriveKey(WALLET_PATH).checkOK(); + byte[] tlv = cmdSet.exportKey(WALLET_PATH, false, true).checkOK().getData(); Log.i(TAG, "Derived " + WALLET_PATH); - - byte[] tlv = cmdSet.exportCurrentKey(true).checkOK().getData(); BIP32KeyPair walletKeyPair = BIP32KeyPair.fromTLV(tlv); - cmdSet.deriveKey(WHISPER_PATH).checkOK(); + byte[] tlv2 = cmdSet.exportKey(WHISPER_PATH, false, false).checkOK().getData(); Log.i(TAG, "Derived " + WHISPER_PATH); - - byte[] tlv2 = cmdSet.exportCurrentKey(false).checkOK().getData(); BIP32KeyPair whisperKeyPair = BIP32KeyPair.fromTLV(tlv2); - cmdSet.deriveKey(ENCRYPTION_PATH).checkOK(); + byte[] tlv3 = cmdSet.exportKey(ENCRYPTION_PATH, false, false).checkOK().getData(); Log.i(TAG, "Derived " + ENCRYPTION_PATH); - - byte[] tlv3 = cmdSet.exportCurrentKey(false).checkOK().getData(); BIP32KeyPair encryptionKeyPair = BIP32KeyPair.fromTLV(tlv3); ApplicationInfo info = new ApplicationInfo(cmdSet.select().checkOK().getData()); @@ -557,6 +551,39 @@ public class SmartCard extends BroadcastReceiver implements CardListener { return sig; } + public String signWithPath(final String pairingBase64, final String pin, final String path, final String message) throws IOException, APDUException { + KeycardCommandSet cmdSet = new KeycardCommandSet(this.cardChannel); + cmdSet.select().checkOK(); + + Pairing pairing = new Pairing(pairingBase64); + cmdSet.setPairing(pairing); + + cmdSet.autoOpenSecureChannel(); + Log.i(TAG, "secure channel opened"); + + cmdSet.verifyPIN(pin).checkOK(); + Log.i(TAG, "pin verified"); + + byte[] hash = Hex.decode(message); + RecoverableSignature signature = new RecoverableSignature(hash, cmdSet.signWithPath(hash, path, false).checkOK().getData()); + + Log.i(TAG, "Signed hash: " + Hex.toHexString(hash)); + Log.i(TAG, "Recovery ID: " + signature.getRecId()); + Log.i(TAG, "R: " + Hex.toHexString(signature.getR())); + Log.i(TAG, "S: " + Hex.toHexString(signature.getS())); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + out.write(signature.getR()); + out.write(signature.getS()); + out.write(signature.getRecId()); + + String sig = Hex.toHexString(out.toByteArray()); + Log.i(TAG, "Signature: " + sig); + + return sig; + } + public String signPinless(final String message) throws IOException, APDUException { CashCommandSet cmdSet = new CashCommandSet(this.cardChannel); cmdSet.select().checkOK();