add deriveKey and exportKey

This commit is contained in:
Dmitry Novotochinov 2018-11-26 20:08:00 +03:00
parent 2354cffa7b
commit 17312ef467
No known key found for this signature in database
GPG Key ID: 43D1DAF5AD39C927
2 changed files with 175 additions and 109 deletions

View File

@ -21,121 +21,143 @@ import java.security.spec.InvalidKeySpecException;
import im.status.hardwallet_lite_android.io.APDUException;
public class RNStatusKeycardModule extends ReactContextBaseJavaModule implements LifecycleEventListener {
private static final String TAG = "StatusKeycard";
private SmartCard smartCard;
private final ReactApplicationContext reactContext;
private static final String TAG = "StatusKeycard";
private SmartCard smartCard;
private final ReactApplicationContext reactContext;
public RNStatusKeycardModule(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
reactContext.addLifecycleEventListener(this);
}
@Override
public String getName() {
return "RNStatusKeycard";
}
@Override
public void onHostResume() {
if (this.smartCard == null) {
this.smartCard = new SmartCard(getCurrentActivity(), reactContext);
public RNStatusKeycardModule(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
reactContext.addLifecycleEventListener(this);
}
}
@Override
public void onHostPause() {
}
@Override
public void onHostDestroy() {
}
@ReactMethod
public void nfcIsSupported(final Promise promise) {
promise.resolve(smartCard.isNfcSupported());
}
@ReactMethod
public void nfcIsEnabled(final Promise promise) {
promise.resolve(smartCard.isNfcEnabled());
}
@ReactMethod
public void openNfcSettings(final Promise promise) {
Activity currentActivity = getCurrentActivity();
currentActivity.startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
promise.resolve(true);
}
@ReactMethod
public void start(final Promise promise) {
if (smartCard.start()) {
promise.resolve(true);
} else {
promise.reject("Error", "Not supported on this device");
}
}
@ReactMethod
public void init(final Promise promise) {
try {
SmartCardSecrets s = smartCard.init();
WritableMap params = Arguments.createMap();
params.putString("pin", s.getPin());
params.putString("puk", s.getPuk());
params.putString("password", s.getPairingPassword());
promise.resolve(params);
} catch (IOException | APDUException | NoSuchAlgorithmException | InvalidKeySpecException e) {
Log.d(TAG, e.getMessage());
promise.reject(e.getClass().toString(), e.getMessage());
@Override
public String getName() {
return "RNStatusKeycard";
}
}
@ReactMethod
public void pair(final String password, final Promise promise) {
try {
String pairing = smartCard.pair(password);
Log.d(TAG, "pairing done");
promise.resolve(pairing);
} catch (IOException | APDUException e) {
Log.d(TAG, e.getMessage());
promise.reject(e.getClass().toString(), e.getMessage());
@Override
public void onHostResume() {
if (this.smartCard == null) {
this.smartCard = new SmartCard(getCurrentActivity(), reactContext);
}
}
}
@ReactMethod
public void generateMnemonic(final String password, final Promise promise) {
try {
promise.resolve(smartCard.generateMnemonic(password));
} catch (IOException | APDUException e) {
Log.d(TAG, e.getMessage());
promise.reject(e.getClass().toString(), e.getMessage());
@Override
public void onHostPause() {
}
}
@ReactMethod
public void saveMnemonic(final String mnemonic, final String password, final String pin, final Promise promise) {
try {
smartCard.saveMnemonic(mnemonic, password, pin);
promise.resolve(true);
} catch (IOException | APDUException e) {
Log.d(TAG, e.getMessage());
promise.reject(e.getClass().toString(), e.getMessage());
@Override
public void onHostDestroy() {
}
}
@ReactMethod
public void getApplicationInfo(final Promise promise) {
try {
promise.resolve(smartCard.getApplicationInfo());
} catch (IOException | APDUException e) {
Log.d(TAG, e.getMessage());
promise.reject(e.getClass().toString(), e.getMessage());
}
}
@ReactMethod
public void nfcIsSupported(final Promise promise) {
promise.resolve(smartCard.isNfcSupported());
}
@ReactMethod
public void nfcIsEnabled(final Promise promise) {
promise.resolve(smartCard.isNfcEnabled());
}
@ReactMethod
public void openNfcSettings(final Promise promise) {
Activity currentActivity = getCurrentActivity();
currentActivity.startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
promise.resolve(true);
}
@ReactMethod
public void start(final Promise promise) {
if (smartCard.start()) {
promise.resolve(true);
} else {
promise.reject("Error", "Not supported on this device");
}
}
@ReactMethod
public void init(final Promise promise) {
try {
SmartCardSecrets s = smartCard.init();
WritableMap params = Arguments.createMap();
params.putString("pin", s.getPin());
params.putString("puk", s.getPuk());
params.putString("password", s.getPairingPassword());
promise.resolve(params);
} catch (IOException | APDUException | NoSuchAlgorithmException | InvalidKeySpecException e) {
Log.d(TAG, e.getMessage());
promise.reject(e);
}
}
@ReactMethod
public void pair(final String password, final Promise promise) {
try {
String pairing = smartCard.pair(password);
Log.d(TAG, "pairing done");
promise.resolve(pairing);
} catch (IOException | APDUException e) {
Log.d(TAG, e.getMessage());
promise.reject(e);
}
}
@ReactMethod
public void generateMnemonic(final String pairing, final Promise promise) {
try {
promise.resolve(smartCard.generateMnemonic(pairing));
} catch (IOException | APDUException e) {
Log.d(TAG, e.getMessage());
promise.reject(e);
}
}
@ReactMethod
public void saveMnemonic(final String mnemonic, final String pairing, final String pin, final Promise promise) {
try {
smartCard.saveMnemonic(mnemonic, pairing, pin);
promise.resolve(true);
} catch (IOException | APDUException e) {
Log.d(TAG, e.getMessage());
promise.reject(e);
}
}
@ReactMethod
public void getApplicationInfo(final Promise promise) {
try {
promise.resolve(smartCard.getApplicationInfo());
} catch (IOException | APDUException e) {
Log.d(TAG, e.getMessage());
promise.reject(e);
}
}
@ReactMethod
public void deriveKey(final String path, final String pairing, final String pin, final Promise promise) {
try {
smartCard.deriveKey(path, pairing, pin);
promise.resolve(path);
} catch (IOException | APDUException e) {
Log.d(TAG, e.getMessage());
promise.reject(e);
}
}
@ReactMethod
public void exportKey(final String pairing, final String pin, final Promise promise) {
try {
promise.resolve(smartCard.exportKey(pairing, pin));
} catch (IOException | APDUException e) {
Log.d(TAG, e.getMessage());
promise.reject(e);
}
}
}

View File

@ -9,6 +9,7 @@ import android.content.pm.PackageManager;
import android.nfc.NfcAdapter;
import android.support.annotation.Nullable;
import android.util.Log;
import com.facebook.react.bridge.*;
import com.facebook.react.modules.core.DeviceEventManagerModule;
@ -27,6 +28,7 @@ import im.status.hardwallet_lite_android.wallet.RecoverableSignature;
import im.status.hardwallet_lite_android.wallet.ApplicationInfo;
import im.status.hardwallet_lite_android.wallet.ApplicationStatus;
import im.status.hardwallet_lite_android.wallet.KeyPath;
import org.spongycastle.util.encoders.Hex;
public class SmartCard extends BroadcastReceiver implements CardListener {
@ -151,11 +153,12 @@ public class SmartCard extends BroadcastReceiver implements CardListener {
cmdSet.autoUnpair();
}
public String generateMnemonic(String password) throws IOException, APDUException {
public String generateMnemonic(String pairingBase64) throws IOException, APDUException {
WalletAppletCommandSet cmdSet = new WalletAppletCommandSet(this.cardChannel);
cmdSet.select().checkOK();
cmdSet.autoPair(password);
Pairing pairing = new Pairing(pairingBase64);
cmdSet.setPairing(pairing);
cmdSet.autoOpenSecureChannel();
Log.i(TAG, "secure channel opened");
@ -166,11 +169,12 @@ public class SmartCard extends BroadcastReceiver implements CardListener {
return mnemonic.toMnemonicPhrase();
}
public void saveMnemonic(String mnemonic, String password, String pin) throws IOException, APDUException {
public void saveMnemonic(String mnemonic, String pairingBase64, String pin) throws IOException, APDUException {
WalletAppletCommandSet cmdSet = new WalletAppletCommandSet(this.cardChannel);
cmdSet.select().checkOK();
cmdSet.autoPair(password);
Pairing pairing = new Pairing(pairingBase64);
cmdSet.setPairing(pairing);
cmdSet.autoOpenSecureChannel();
Log.i(TAG, "secure channel opened");
@ -214,4 +218,44 @@ public class SmartCard extends BroadcastReceiver implements CardListener {
return cardInfo;
}
public void deriveKey(final String path, final String pairingBase64, final String pin) throws IOException, APDUException {
WalletAppletCommandSet cmdSet = new WalletAppletCommandSet(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");
KeyPath currentPath = new KeyPath(cmdSet.getStatus(WalletAppletCommandSet.GET_STATUS_P1_KEY_PATH).checkOK().getData());
Log.i(TAG, "Current key path: " + currentPath);
if (!currentPath.toString().equals("m/44'/0'/0'/0/0")) {
cmdSet.deriveKey(path).checkOK();
Log.i(TAG, "Derived m/44'/0'/0'/0/0");
}
}
public String exportKey(final String pairingBase64,final String pin) throws IOException, APDUException {
WalletAppletCommandSet cmdSet = new WalletAppletCommandSet(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[] key = cmdSet.exportKey(WalletAppletCommandSet.EXPORT_KEY_P1_ANY, true).checkOK().getData();
return Hex.toHexString(key);
}
}