rename to keycard (#11)

* rename to keycard

* clean imports

* update to latest SDK

* use unified SDK
This commit is contained in:
Bitgamma 2018-12-11 16:30:01 +03:00 committed by Andrea Franz
parent 85177fe5d3
commit 98caa6106e
14 changed files with 158 additions and 301 deletions

View File

@ -22,9 +22,7 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
implementation 'com.madgag.spongycastle:core:1.58.0.0'
implementation 'com.madgag.spongycastle:prov:1.58.0.0'
implementation 'com.github.status-im:hardwallet-lite-android:c749e5a'
implementation 'com.github.status-im.status-keycard-java:android:2.0rc1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'

View File

@ -1,26 +0,0 @@
package im.status.applet_installer_test.appletinstaller;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("im.status.applet_installer_test.appletinstaller", appContext.getPackageName());
}
}

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="im.status.applet_installer_test.appletinstaller">
package="im.status.keycard.installer">
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc.hce"
@ -13,7 +13,7 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<activity android:name="im.status.keycard.installer.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@ -1,30 +0,0 @@
package im.status.applet_installer_test.appletinstaller;
public class HexUtils {
public static byte[] hexStringToByteArray(String s) {
int len = s.length();
byte[] data = new byte[len/2];
for(int i = 0; i < len; i+=2){
data[i/2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i+1), 16));
}
return data;
}
final protected static char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
public static String byteArrayToHexString(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
int v;
for(int j=0; j < bytes.length; j++) {
v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v>>>4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
}

View File

@ -1,103 +0,0 @@
package im.status.applet_installer_test.appletinstaller;
import android.content.res.AssetManager;
import java.io.IOException;
import java.io.InputStream;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import im.status.hardwallet_lite_android.globalplatform.Load;
import im.status.hardwallet_lite_android.io.APDUException;
import im.status.hardwallet_lite_android.io.APDUResponse;
import im.status.hardwallet_lite_android.io.CardChannel;
import im.status.hardwallet_lite_android.globalplatform.ApplicationID;
import im.status.hardwallet_lite_android.globalplatform.GlobalPlatformCommandSet;
import im.status.hardwallet_lite_android.wallet.WalletAppletCommandSet;
public class Installer {
private CardChannel plainChannel;
private AssetManager assets;
private String capPath;
static final byte[] PACKAGE_AID = HexUtils.hexStringToByteArray("53746174757357616C6C6574");
static final byte[] WALLET_AID = HexUtils.hexStringToByteArray("53746174757357616C6C6574417070");
static final byte[] NDEF_APPLET_AID = HexUtils.hexStringToByteArray("53746174757357616C6C65744E4643");
static final byte[] NDEF_INSTANCE_AID = HexUtils.hexStringToByteArray("D2760000850101");
private GlobalPlatformCommandSet cmdSet;
private boolean testSecrets;
public Installer(CardChannel channel, AssetManager assets, String capPath, boolean testSecrets) {
this.plainChannel = channel;
this.assets = assets;
this.capPath = capPath;
this.testSecrets = testSecrets;
}
public void start() throws IOException, APDUException, NoSuchAlgorithmException, InvalidKeySpecException {
Logger.i("installation started...");
long startTime = System.currentTimeMillis();
Logger.i("auto select sdaid...");
cmdSet = new GlobalPlatformCommandSet(this.plainChannel);
ApplicationID sdaid = new ApplicationID(cmdSet.select().checkOK().getData());
SecureRandom random = new SecureRandom();
byte hostChallenge[] = new byte[8];
random.nextBytes(hostChallenge);
Logger.i("initialize update...");
cmdSet.initializeUpdate(hostChallenge).checkOK();
Logger.i("external authenticate...");
cmdSet.externalAuthenticate(hostChallenge).checkOK();
Logger.i("delete NDEF instance AID...");
cmdSet.delete(NDEF_INSTANCE_AID).checkSW(APDUResponse.SW_OK, APDUResponse.SW_REFERENCED_DATA_NOT_FOUND);
Logger.i("delete wallet AID...");
cmdSet.delete(WALLET_AID).checkSW(APDUResponse.SW_OK, APDUResponse.SW_REFERENCED_DATA_NOT_FOUND);
Logger.i("delete package AID...");
cmdSet.delete(PACKAGE_AID).checkSW(APDUResponse.SW_OK, APDUResponse.SW_REFERENCED_DATA_NOT_FOUND);
Logger.i("install for load...");
cmdSet.installForLoad(PACKAGE_AID, sdaid.getAID()).checkSW(APDUResponse.SW_OK, APDUResponse.SW_REFERENCED_DATA_NOT_FOUND);
InputStream in = this.assets.open(this.capPath);
Load load = new Load(in);
byte[] block;
int steps = load.blocksCount();
while((block = load.nextDataBlock()) != null) {
int count = load.getCount() - 1;
Logger.i(String.format("load %d/%d...", count + 1, steps));
cmdSet.load(block, count, load.hasMore()).checkOK();
}
Logger.i("install for install ndef...");
byte[] params = HexUtils.hexStringToByteArray("0024d40f12616e64726f69642e636f6d3a706b67696d2e7374617475732e657468657265756d");
cmdSet.installForInstall(PACKAGE_AID, NDEF_APPLET_AID, NDEF_INSTANCE_AID, params).checkOK();
Logger.i("install for install wallet...");
cmdSet.installForInstall(PACKAGE_AID, WALLET_AID, WALLET_AID, new byte[0]).checkOK();
this.personalizeApplet();
long duration = System.currentTimeMillis() - startTime;
Logger.i(String.format("\n\ninstallation completed in %d seconds", duration / 1000));
}
private void personalizeApplet() throws NoSuchAlgorithmException, InvalidKeySpecException, APDUException, IOException {
Secrets secrets = testSecrets ? Secrets.testSecrets() : Secrets.generate();
WalletAppletCommandSet cmdSet = new WalletAppletCommandSet(this.plainChannel);
cmdSet.select().checkOK();
cmdSet.init(secrets.getPin(), secrets.getPuk(), secrets.getPairingToken()).checkOK();
Logger.i(String.format("PIN: %s\nPUK: %s\nPairing password: %s\nPairing token: %s", secrets.getPin(), secrets.getPuk(), secrets.getPairingPassword(), HexUtils.byteArrayToHexString(secrets.getPairingToken())));
}
}

View File

@ -1,90 +0,0 @@
package im.status.applet_installer_test.appletinstaller;
import android.support.annotation.NonNull;
import android.util.Base64;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import static android.util.Base64.NO_PADDING;
public class Secrets {
private String pin;
private String puk;
private String pairingPassword;
private byte[] pairingToken;
private static long PIN_BOUND = 999999L;
private static long PUK_BOUND = 999999999999L;
public Secrets(String pin, String puk, String pairingPassword, byte[] pairingToken) {
this.pin = pin;
this.puk = puk;
this.pairingPassword = pairingPassword;
this.pairingToken = pairingToken;
}
@NonNull
public static Secrets generate() throws NoSuchAlgorithmException, InvalidKeySpecException {
String pairingPassword = randomToken(12);
byte[] pairingToken = generatePairingKey(pairingPassword.toCharArray());
long pinNumber = randomLong(PIN_BOUND);
long pukNumber = randomLong(PUK_BOUND);
String pin = String.format("%06d", pinNumber);
String puk = String.format("%012d", pukNumber);
return new Secrets(pin, puk, pairingPassword, pairingToken);
}
public static Secrets testSecrets() throws NoSuchAlgorithmException, InvalidKeySpecException {
String pairingPassword = "WalletAppletTest";
byte[] pairingToken = generatePairingKey(pairingPassword.toCharArray());
return new Secrets("000000", "123456789012", pairingPassword, pairingToken);
}
public String getPin() {
return pin;
}
public String getPuk() {
return puk;
}
public String getPairingPassword() {
return pairingPassword;
}
public byte[] getPairingToken() {
return pairingToken;
}
public static byte[] randomBytes(int length) {
SecureRandom random = new SecureRandom();
byte data[] = new byte[length];
random.nextBytes(data);
return data;
}
public static String randomToken(int length) {
return Base64.encodeToString(randomBytes(length), NO_PADDING);
}
public static long randomLong(long bound) {
SecureRandom random = new SecureRandom();
return Math.abs(random.nextLong()) % bound;
}
public static byte[] generatePairingKey(char[] pairing) throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
String salt = "Status Hardware Wallet Lite";
PBEKeySpec spec = new PBEKeySpec(pairing, salt.getBytes(), 50000, 32*8);
SecretKey key = skf.generateSecret(spec);
return key.getEncoded();
}
}

View File

@ -1,12 +1,9 @@
package im.status.applet_installer_test.appletinstaller;
package im.status.keycard.installer;
import android.content.res.AssetManager;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.IsoDep;
import im.status.hardwallet_lite_android.io.APDUException;
import im.status.hardwallet_lite_android.io.CardChannel;
import im.status.hardwallet_lite_android.io.CardListener;
import im.status.keycard.io.APDUException;
import im.status.keycard.io.CardChannel;
import im.status.keycard.io.CardListener;
import java.io.IOException;
import java.util.Timer;

View File

@ -0,0 +1,73 @@
package im.status.keycard.installer;
import android.content.res.AssetManager;
import im.status.keycard.applet.KeycardCommandSet;
import im.status.keycard.globalplatform.GlobalPlatformCommandSet;
import im.status.keycard.globalplatform.LoadCallback;
import im.status.keycard.io.APDUException;
import im.status.keycard.io.CardChannel;
import org.bouncycastle.util.encoders.Hex;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
public class Installer {
private CardChannel plainChannel;
private AssetManager assets;
private String capPath;
private GlobalPlatformCommandSet cmdSet;
private boolean testSecrets;
public Installer(CardChannel channel, AssetManager assets, String capPath, boolean testSecrets) {
this.plainChannel = channel;
this.assets = assets;
this.capPath = capPath;
this.testSecrets = testSecrets;
}
public void start() throws IOException, APDUException, NoSuchAlgorithmException, InvalidKeySpecException {
Logger.i("installation started...");
long startTime = System.currentTimeMillis();
Logger.i("select ISD...");
cmdSet = new GlobalPlatformCommandSet(this.plainChannel);
cmdSet.select().checkOK();
Logger.i("opening secure channel...");
cmdSet.openSecureChannel();
Logger.i("deleting old version (if present)...");
cmdSet.deleteKeycardInstancesAndPackage();
Logger.i("loading package...");
cmdSet.loadKeycardPackage(this.assets.open(this.capPath), new LoadCallback() {
public void blockLoaded(int loadedBlock, int blockCount) {
Logger.i(String.format("load %d/%d...", loadedBlock, blockCount));
}
});
Logger.i("installing NDEF applet...");
cmdSet.installNDEFApplet(Hex.decode("0024d40f12616e64726f69642e636f6d3a706b67696d2e7374617475732e657468657265756d")).checkOK();
Logger.i("installing Keycard applet...");
cmdSet.installKeycardApplet().checkOK();
this.personalizeApplet();
long duration = System.currentTimeMillis() - startTime;
Logger.i(String.format("\n\ninstallation completed in %d seconds", duration / 1000));
}
private void personalizeApplet() throws NoSuchAlgorithmException, InvalidKeySpecException, APDUException, IOException {
Secrets secrets = testSecrets ? Secrets.testSecrets() : Secrets.generate();
KeycardCommandSet cmdSet = new KeycardCommandSet(this.plainChannel);
cmdSet.select().checkOK();
cmdSet.init(secrets.getPin(), secrets.getPuk(), secrets.getPairingPassword()).checkOK();
Logger.i(String.format("PIN: %s\nPUK: %s\nPairing password: %s", secrets.getPin(), secrets.getPuk(), secrets.getPairingPassword()));
}
}

View File

@ -1,4 +1,4 @@
package im.status.applet_installer_test.appletinstaller;
package im.status.keycard.installer;
import android.util.Log;

View File

@ -1,24 +1,17 @@
package im.status.applet_installer_test.appletinstaller;
package im.status.keycard.installer;
import android.content.res.AssetManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.nfc.NfcAdapter;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ScrollView;
import android.widget.TextView;
import im.status.hardwallet_lite_android.io.CardManager;
import java.security.Security;
import im.status.keycard.android.NFCCardManager;
public class MainActivity extends AppCompatActivity implements UILogger {
static {
Security.insertProviderAt(new org.spongycastle.jce.provider.BouncyCastleProvider(), 1);
}
private NfcAdapter nfcAdapter;
private TextView textView;
private ScrollView textViewScroll;
@ -27,7 +20,7 @@ public class MainActivity extends AppCompatActivity implements UILogger {
private Button buttonInstallTest;
private Button buttonPerfTest;
private ActionRunner actionRunner;
private CardManager cardManager;
private NFCCardManager cardManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -37,8 +30,8 @@ public class MainActivity extends AppCompatActivity implements UILogger {
Logger.setUILogger(this);
AssetManager assets = this.getAssets();
this.actionRunner = new ActionRunner(assets, "wallet.cap");
this.cardManager = new CardManager();
this.actionRunner = new ActionRunner(assets, "keycard.cap");
this.cardManager = new NFCCardManager();
this.cardManager.setCardListener(this.actionRunner);
cardManager.start();

View File

@ -1,19 +1,15 @@
package im.status.applet_installer_test.appletinstaller;
package im.status.keycard.installer;
import android.util.Log;
import im.status.hardwallet_lite_android.io.CardChannel;
import im.status.hardwallet_lite_android.wallet.WalletAppletCommandSet;
import org.spongycastle.jce.ECNamedCurveTable;
import org.spongycastle.jce.spec.ECParameterSpec;
import im.status.keycard.io.CardChannel;
import im.status.keycard.applet.KeycardCommandSet;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.util.Random;
import java.security.SecureRandom;
public class PerfTest {
private CardChannel cardChannel;
private WalletAppletCommandSet cmdSet;
private KeycardCommandSet cmdSet;
private long openSecureChannelTime = 0;
private long loadKeysTime = 0;
@ -28,11 +24,10 @@ public class PerfTest {
}
public void test() throws Exception {
cmdSet = new WalletAppletCommandSet(cardChannel);
cmdSet = new KeycardCommandSet(cardChannel);
cmdSet.select().checkOK();
String pairingPassword = "WalletAppletTest";
cmdSet.autoPair(cmdSet.pairingPasswordToSecret(pairingPassword));
cmdSet.autoPair(cmdSet.pairingPasswordToSecret(Secrets.testSecrets().getPairingPassword()));
openSecureChannelTime = System.currentTimeMillis();
cmdSet.autoOpenSecureChannel();
openSecureChannelTime = System.currentTimeMillis() - openSecureChannelTime;
@ -69,7 +64,7 @@ public class PerfTest {
long time = System.currentTimeMillis();
cmdSet.select().checkOK();
cmdSet.autoOpenSecureChannel();
cmdSet.getStatus(WalletAppletCommandSet.GET_STATUS_P1_APPLICATION).checkOK();
cmdSet.getStatus(KeycardCommandSet.GET_STATUS_P1_APPLICATION).checkOK();
getStatusTime = System.currentTimeMillis() - time;
}
@ -84,12 +79,10 @@ public class PerfTest {
}
private void loadKeys() throws Exception {
KeyPairGenerator g = keypairGenerator();
KeyPair keyPair = g.generateKeyPair();
byte[] chainCode = new byte[32];
new Random().nextBytes(chainCode);
byte[] seed = new byte[64];
new SecureRandom().nextBytes(seed);
cmdSet.loadKey(keyPair, false, chainCode).checkOK();
cmdSet.loadKey(seed).checkOK();
long time = System.currentTimeMillis();
cmdSet.deriveKey(BIP44_WALLET_PATH).checkOK();
@ -104,12 +97,4 @@ public class PerfTest {
cmdSet.sign("any32bytescanbeahashyouknowthat!".getBytes()).checkOK();
signTime = System.currentTimeMillis() - time;
}
private KeyPairGenerator keypairGenerator() throws Exception {
ECParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256k1");
KeyPairGenerator g = KeyPairGenerator.getInstance("ECDH");
g.initialize(ecSpec);
return g;
}
}

View File

@ -0,0 +1,60 @@
package im.status.keycard.installer;
import android.util.Base64;
import im.status.keycard.globalplatform.Crypto;
import java.security.SecureRandom;
import static android.util.Base64.NO_PADDING;
import static im.status.keycard.globalplatform.Crypto.randomLong;
public class Secrets {
private String pin;
private String puk;
private String pairingPassword;
public Secrets(String pin, String puk, String pairingPassword) {
this.pin = pin;
this.puk = puk;
this.pairingPassword = pairingPassword;
}
public static Secrets generate() {
String pairingPassword = randomToken(12);
long pinNumber = randomLong(Crypto.PIN_BOUND);
long pukNumber = randomLong(Crypto.PUK_BOUND);
String pin = String.format("%06d", pinNumber);
String puk = String.format("%012d", pukNumber);
return new Secrets(pin, puk, pairingPassword);
}
public static Secrets testSecrets() {
String pairingPassword = "KeycardTest";
return new Secrets("000000", "123456789012", pairingPassword);
}
public String getPin() {
return pin;
}
public String getPuk() {
return puk;
}
public String getPairingPassword() {
return pairingPassword;
}
public static byte[] randomBytes(int length) {
SecureRandom random = new SecureRandom();
byte data[] = new byte[length];
random.nextBytes(data);
return data;
}
public static String randomToken(int length) {
return Base64.encodeToString(randomBytes(length), NO_PADDING);
}
}

View File

@ -1,3 +1,3 @@
<resources>
<string name="app_name">AppletInstaller</string>
<string name="app_name">KeycardInstaller</string>
</resources>