From 4d9e27858a27b031e1e80ea10fdd1360a8ced3a0 Mon Sep 17 00:00:00 2001 From: Frank Morgner Date: Fri, 13 May 2016 08:30:56 +0200 Subject: [PATCH] added proper separation between JCardSim and VICC Aditionally use the wording emulator within this project to not be confused with JCardSim's Simulator classes. --- .../app/src/main/AndroidManifest.xml | 2 +- .../acardemulator/EmulatorService.java | 128 +++++++ .../acardemulator/MainActivity.java | 16 +- .../acardemulator/SettingsActivity.java | 4 +- .../acardemulator/SimulatorService.java | 356 ------------------ .../acardemulator/emulators/Emulator.java | 11 + .../acardemulator/emulators/JCEmulator.java | 125 ++++++ .../acardemulator/emulators/VICCEmulator.java | 134 +++++++ 8 files changed, 409 insertions(+), 367 deletions(-) create mode 100644 ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/EmulatorService.java delete mode 100644 ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/SimulatorService.java create mode 100644 ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/emulators/Emulator.java create mode 100644 ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/emulators/JCEmulator.java create mode 100644 ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/emulators/VICCEmulator.java diff --git a/ACardEmulator/app/src/main/AndroidManifest.xml b/ACardEmulator/app/src/main/AndroidManifest.xml index 13bca01..a78ad84 100644 --- a/ACardEmulator/app/src/main/AndroidManifest.xml +++ b/ACardEmulator/app/src/main/AndroidManifest.xml @@ -37,7 +37,7 @@ - diff --git a/ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/EmulatorService.java b/ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/EmulatorService.java new file mode 100644 index 0000000..d462bb0 --- /dev/null +++ b/ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/EmulatorService.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2015 Frank Morgner + * + * This file is part of ACardEmulator. + * + * ACardEmulator is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * ACardEmulator is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * ACardEmulator. If not, see . + */ + +package com.vsmartcard.acardemulator; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.nfc.cardemulation.HostApduService; +import android.os.Bundle; +import android.os.StrictMode; +import android.preference.PreferenceManager; +import android.support.v4.content.LocalBroadcastManager; +import android.util.Log; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; + +import com.vsmartcard.acardemulator.emulators.Emulator; +import com.vsmartcard.acardemulator.emulators.JCEmulator; +import com.vsmartcard.acardemulator.emulators.VICCEmulator; + +public class EmulatorService extends HostApduService { + + public static final String TAG = "com.vsmartcard.acardemulator.EmulatorService"; + public static final String EXTRA_CAPDU = "MSG_CAPDU"; + public static final String EXTRA_RAPDU = "MSG_RAPDU"; + public static final String EXTRA_ERROR = "MSG_ERROR"; + public static final String EXTRA_DESELECT = "MSG_DESELECT"; + public static final String EXTRA_INSTALL = "MSG_INSTALL"; + + private static Emulator emulator = null; + private static boolean do_destroy = false; + + @Override + public void onCreate () { + super.onCreate(); + // force reloading applets if requested + do_destroy(); + + if (emulator == null) { + Log.d("", "Begin transaction"); + SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(this); + String str_emulator = SP.getString("emulator", ""); + if (str_emulator.equals(getString(R.string.vicc))) { + emulator = new VICCEmulator( + SP.getString("hostname", VICCEmulator.DEFAULT_HOSTNAME), + Integer.parseInt(SP.getString("port", Integer.toString(VICCEmulator.DEFAULT_PORT)))); + } else { + emulator = new JCEmulator(this, + SP.getBoolean("activate_helloworld", false), + SP.getBoolean("activate_openpgp", false), + SP.getBoolean("activate_oath", false), + SP.getBoolean("activate_isoapplet", false), + SP.getBoolean("activate_gidsapplet", false)); + } + } + } + + public static void destroySimulator(Context context) { + do_destroy = true; + } + + private void do_destroy() { + if (do_destroy) { + emulator.destroy(); + emulator = null; + do_destroy = false; + Intent i = new Intent(TAG); + i.putExtra(EXTRA_DESELECT, "Uninstalled all applets."); + LocalBroadcastManager.getInstance(this).sendBroadcast(i); + } + } + + @Override + public void onDestroy() { + do_destroy(); + super.onDestroy(); + } + + @Override + public byte[] processCommandApdu(byte[] capdu, Bundle extras) { + byte[] rapdu = emulator.process(capdu); + + Intent i = new Intent(TAG); + i.putExtra(EXTRA_CAPDU, Util.byteArrayToHexString(capdu)); + if (rapdu != null) + i.putExtra(EXTRA_RAPDU, Util.byteArrayToHexString(rapdu)); + LocalBroadcastManager.getInstance(this).sendBroadcast(i); + + return rapdu; + } + + @Override + public void onDeactivated(int reason) { + Intent i = new Intent(TAG); + + Log.d("", "End transaction"); + switch (reason) { + case DEACTIVATION_LINK_LOSS: + i.putExtra(EXTRA_DESELECT, "link lost"); + emulator.deactivate(); + break; + case DEACTIVATION_DESELECTED: + emulator.deactivate(); + break; + } + + LocalBroadcastManager.getInstance(this).sendBroadcast(i); + } +} diff --git a/ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/MainActivity.java b/ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/MainActivity.java index 66289b0..e151d6e 100644 --- a/ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/MainActivity.java +++ b/ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/MainActivity.java @@ -48,12 +48,12 @@ public class MainActivity extends AppCompatActivity { private BroadcastReceiver bReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(SimulatorService.TAG)) { - String capdu = intent.getStringExtra(SimulatorService.EXTRA_CAPDU); - String rapdu = intent.getStringExtra(SimulatorService.EXTRA_RAPDU); - String error = intent.getStringExtra(SimulatorService.EXTRA_ERROR); - String deselect = intent.getStringExtra(SimulatorService.EXTRA_DESELECT); - String install = intent.getStringExtra(SimulatorService.EXTRA_INSTALL); + if (intent.getAction().equals(EmulatorService.TAG)) { + String capdu = intent.getStringExtra(EmulatorService.EXTRA_CAPDU); + String rapdu = intent.getStringExtra(EmulatorService.EXTRA_RAPDU); + String error = intent.getStringExtra(EmulatorService.EXTRA_ERROR); + String deselect = intent.getStringExtra(EmulatorService.EXTRA_DESELECT); + String install = intent.getStringExtra(EmulatorService.EXTRA_INSTALL); if (install != null) textViewVPCDStatus.append(getResources().getString(R.string.status_install) + ":" + install + "\n"); if (capdu != null) @@ -102,7 +102,7 @@ public class MainActivity extends AppCompatActivity { if (emulator != getString(R.string.vicc)) { Snackbar.make(view, "Scheduled re-installation of all applets...", Snackbar.LENGTH_LONG) .setAction("Action", null).show(); - SimulatorService.destroySimulator(getApplicationContext()); + EmulatorService.destroySimulator(getApplicationContext()); } } }); @@ -116,7 +116,7 @@ public class MainActivity extends AppCompatActivity { LocalBroadcastManager bManager = LocalBroadcastManager.getInstance(this); IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(SimulatorService.TAG); + intentFilter.addAction(EmulatorService.TAG); bManager.registerReceiver(bReceiver, intentFilter); } diff --git a/ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/SettingsActivity.java b/ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/SettingsActivity.java index c1420d5..a108105 100644 --- a/ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/SettingsActivity.java +++ b/ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/SettingsActivity.java @@ -21,7 +21,6 @@ package com.vsmartcard.acardemulator; import android.annotation.TargetApi; -import android.app.FragmentTransaction; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -41,6 +40,7 @@ import android.view.MenuItem; import com.google.zxing.integration.android.IntentIntegrator; import com.google.zxing.integration.android.IntentResult; +import com.vsmartcard.acardemulator.emulators.VICCEmulator; /** * A {@link PreferenceActivity} that presents a set of application settings. On @@ -230,7 +230,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity { h = uri.getHost(); int _p = uri.getPort(); if (_p < 0) { - _p = SimulatorService.DEFAULT_PORT; + _p = VICCEmulator.DEFAULT_PORT; } p = Integer.toString(_p); SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(this); diff --git a/ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/SimulatorService.java b/ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/SimulatorService.java deleted file mode 100644 index 9cbf2ad..0000000 --- a/ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/SimulatorService.java +++ /dev/null @@ -1,356 +0,0 @@ -/* - * Copyright (C) 2015 Frank Morgner - * - * This file is part of ACardEmulator. - * - * ACardEmulator is free software: you can redistribute it and/or - * modify it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at your - * option) any later version. - * - * ACardEmulator is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * ACardEmulator. If not, see . - */ - -package com.vsmartcard.acardemulator; - -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.nfc.cardemulation.HostApduService; -import android.os.Bundle; -import android.os.StrictMode; -import android.preference.PreferenceManager; -import android.support.v4.content.LocalBroadcastManager; -import android.util.Log; - -import com.licel.jcardsim.base.Simulator; -import com.licel.jcardsim.base.SimulatorRuntime; -import com.licel.jcardsim.samples.HelloWorldApplet; -import com.licel.jcardsim.utils.AIDUtil; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetAddress; -import java.net.Socket; - -import openpgpcard.OpenPGPApplet; -import pkgYkneoOath.YkneoOath; -import net.pwendland.javacard.pki.isoapplet.IsoApplet; -import com.mysmartlogon.gidsApplet.GidsApplet; - -public class SimulatorService extends HostApduService { - - public static final int DEFAULT_PORT = 35963; - private static final String DEFAULT_HOSTNAME = "10.0.2.2"; - - private static Socket socket; - private static InputStream inputStream; - private static OutputStream outputStream; - - public static final String TAG = "com.vsmartcard.acardemulator.SimulatorService"; - public static final String EXTRA_CAPDU = "MSG_CAPDU"; - public static final String EXTRA_RAPDU = "MSG_RAPDU"; - public static final String EXTRA_ERROR = "MSG_ERROR"; - public static final String EXTRA_DESELECT = "MSG_DESELECT"; - public static final String EXTRA_INSTALL = "MSG_INSTALL"; - - private static Simulator simulator = null; - private static boolean do_destroy = false; - - private boolean useVPCD() { - SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(this); - String emulator = SP.getString("emulator", ""); - if (emulator.equals(getString(R.string.vicc))) - return true; - - return false; - } - - private int port() { - SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(this); - return Integer.parseInt(SP.getString("port", Integer.toString(DEFAULT_PORT))); - } - - private String hostname() { - SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(this); - return SP.getString("hostname", DEFAULT_HOSTNAME); - } - - private void createSimulator() { - String aid, name, extra_install = "", extra_error = ""; - SharedPreferences SP = PreferenceManager.getDefaultSharedPreferences(this); - simulator = new Simulator(new SimulatorRuntime()); - - if (SP.getBoolean("activate_helloworld", false)) { - name = getResources().getString(R.string.applet_helloworld); - aid = getResources().getString(R.string.aid_helloworld); - try { - simulator.installApplet(AIDUtil.create(aid), HelloWorldApplet.class); - extra_install += "\n" + name + " (AID: " + aid + ")"; - } catch (Exception e) { - e.printStackTrace(); - extra_error += "\n" + "Could not install " + name + " (AID: " + aid + ")"; - } - } - - if (SP.getBoolean("activate_openpgp", false)) { - name = getResources().getString(R.string.applet_openpgp); - aid = getResources().getString(R.string.aid_openpgp); - try { - byte[] aid_bytes = Util.hexStringToByteArray(aid); - byte[] inst_params = new byte[aid.length() + 1]; - inst_params[0] = (byte) aid_bytes.length; - System.arraycopy(aid_bytes, 0, inst_params, 1, aid_bytes.length); - simulator.installApplet(AIDUtil.create(aid), OpenPGPApplet.class, inst_params, (short) 0, (byte) inst_params.length); - extra_install += "\n" + name + " (AID: " + aid + ")"; - } catch (Exception e) { - e.printStackTrace(); - extra_error += "\n" + "Could not install " + name + " (AID: " + aid + ")"; - } - } - - if (SP.getBoolean("activate_oath", false)) { - name = getResources().getString(R.string.applet_oath); - aid = getResources().getString(R.string.aid_oath); - try { - byte[] aid_bytes = Util.hexStringToByteArray(aid); - byte[] inst_params = new byte[aid.length() + 1]; - inst_params[0] = (byte) aid_bytes.length; - System.arraycopy(aid_bytes, 0, inst_params, 1, aid_bytes.length); - simulator.installApplet(AIDUtil.create(aid), YkneoOath.class, inst_params, (short) 0, (byte) inst_params.length); - extra_install += "\n" + name + " (AID: " + aid + ")"; - } catch (Exception e) { - e.printStackTrace(); - extra_error += "\n" + "Could not install " + name + " (AID: " + aid + ")"; - } - } - - if (SP.getBoolean("activate_isoapplet", false)) { - name = getResources().getString(R.string.applet_isoapplet); - aid = getResources().getString(R.string.aid_isoapplet); - try { - simulator.installApplet(AIDUtil.create(aid), IsoApplet.class); - extra_install += "\n" + name + " (AID: " + aid + ")"; - } catch (Exception e) { - e.printStackTrace(); - extra_error += "\n" + "Could not install " + name + " (AID: " + aid + ")"; - } - } - - if (SP.getBoolean("activate_gidsapplet", false)) { - name = getResources().getString(R.string.applet_gidsapplet); - aid = getResources().getString(R.string.aid_gidsapplet); - try { - simulator.installApplet(AIDUtil.create(aid), GidsApplet.class); - extra_install += "\n" + name + " (AID: " + aid + ")"; - } catch (Exception e) { - e.printStackTrace(); - extra_error += "\n" + "Could not install " + name + " (AID: " + aid + ")"; - } - } - - Intent i = new Intent(TAG); - if (!extra_error.isEmpty()) - i.putExtra(EXTRA_ERROR, extra_error); - if (!extra_install.isEmpty()) - i.putExtra(EXTRA_INSTALL, extra_install); - LocalBroadcastManager.getInstance(this).sendBroadcast(i); - } - - @Override - public void onCreate () { - super.onCreate(); - // force reloading applets if requested - do_destroy(); - - Log.d("", "Begin transaction"); - if (useVPCD()) { - if (socket == null) { - try { - if (android.os.Build.VERSION.SDK_INT > 9) { - StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); - StrictMode.setThreadPolicy(policy); - } - vpcdConnect(); - sendPowerOn(); - } catch (IOException e) { - e.printStackTrace(); - vpcdDisconnect(); - } - } - } else { - if (simulator == null) - createSimulator(); - } - } - - public static void destroySimulator(Context context) { - do_destroy = true; - } - - private void do_destroy() { - if (do_destroy) { - simulator = null; - do_destroy = false; - Intent i = new Intent(TAG); - i.putExtra(EXTRA_DESELECT, "Uninstalled all applets."); - LocalBroadcastManager.getInstance(this).sendBroadcast(i); - } - } - - @Override - public void onDestroy() { - do_destroy(); - super.onDestroy(); - } - - @Override - public byte[] processCommandApdu(byte[] capdu, Bundle extras) { - Intent i = new Intent(TAG); - String extra_error = ""; - byte[] rapdu = null; - - i.putExtra(EXTRA_CAPDU, Util.byteArrayToHexString(capdu)); - try { - if (useVPCD()) { - rapdu = transmit(capdu); - } else { - rapdu = simulator.transmitCommand(capdu); - } - if (rapdu != null) - i.putExtra(EXTRA_RAPDU, Util.byteArrayToHexString(rapdu)); - } catch (Exception e) { - e.printStackTrace(); - vpcdDisconnect(); - extra_error += "Internal error"; - } - - if (!extra_error.isEmpty()) - i.putExtra(EXTRA_ERROR, extra_error); - - LocalBroadcastManager.getInstance(this).sendBroadcast(i); - - return rapdu; - } - - @Override - public void onDeactivated(int reason) { - Intent i = new Intent(TAG); - - Log.d("", "End transaction"); - switch (reason) { - case DEACTIVATION_LINK_LOSS: - i.putExtra(EXTRA_DESELECT, "link lost"); - if (useVPCD()) - try { - sendPowerOff(); - } catch (IOException e) { - e.printStackTrace(); - vpcdDisconnect(); - } - break; - case DEACTIVATION_DESELECTED: - i.putExtra(EXTRA_DESELECT, "deactivated"); - if (useVPCD()) - try { - sendReset(); - } catch (IOException e) { - e.printStackTrace(); - vpcdDisconnect(); - } - break; - } - if (useVPCD()) { - //vpcdDisconnect(); - } - - LocalBroadcastManager.getInstance(this).sendBroadcast(i); - } - - private byte[] receiveFromVPCD() throws IOException { - /* convert length from network byte order. - Note that Java always uses network byte order internally. */ - int length1 = inputStream.read(); - int length2 = inputStream.read(); - if (length1 == -1 || length2 == -1) { - // EOF - return null; - } - int length = (length1 << 8) + length2; - - byte[] data = new byte[length]; - - int offset = 0; - while (length > 0) { - int read = inputStream.read(data, offset, length); - if (read == -1) { - // EOF - return null; - } - offset += read; - length -= read; - } - - return data; - } - - private byte[] transmit(byte[] capdu) throws IOException { - sendToVPCD(capdu); - return receiveFromVPCD(); - } - - private void sendPowerOff() throws IOException { - Log.d("", "Power Off"); - sendToVPCD(new byte[] {0x00}); - } - private void sendPowerOn() throws IOException { - Log.d("", "Power On"); - sendToVPCD(new byte[] {0x01}); - } - private void sendReset() throws IOException { - Log.d("", "Reset"); - sendToVPCD(new byte[] {0x02}); - } - - private void sendToVPCD(byte[] data) throws IOException { - /* convert length to network byte order. - Note that Java always uses network byte order internally. */ - byte[] length = new byte[2]; - length[0] = (byte) (data.length >> 8); - length[1] = (byte) (data.length & 0xff); - outputStream.write(length); - - outputStream.write(data, 0, data.length); - - outputStream.flush(); - } - - private void vpcdConnect() throws IOException { - String hostname = hostname(); - int port = port(); - Log.d("", "Connecting to " + hostname + ":" + Integer.toString(port)); - socket = new Socket(InetAddress.getByName(hostname), port); - outputStream = socket.getOutputStream(); - inputStream = socket.getInputStream(); - } - - private void vpcdDisconnect() { - Log.d("", "Disconnecting"); - if (socket != null) { - try { - socket.close(); - } catch (IOException e) { - // ignore - } finally { - socket = null; - } - } - } -} diff --git a/ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/emulators/Emulator.java b/ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/emulators/Emulator.java new file mode 100644 index 0000000..621d2de --- /dev/null +++ b/ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/emulators/Emulator.java @@ -0,0 +1,11 @@ +package com.vsmartcard.acardemulator.emulators; + +public interface Emulator { + + public void deactivate(); + + public void destroy(); + + public byte[] process(byte[] commandAPDU); + +} diff --git a/ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/emulators/JCEmulator.java b/ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/emulators/JCEmulator.java new file mode 100644 index 0000000..99470d4 --- /dev/null +++ b/ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/emulators/JCEmulator.java @@ -0,0 +1,125 @@ +package com.vsmartcard.acardemulator.emulators; + +import android.content.Context; +import android.content.Intent; +import android.support.v4.content.LocalBroadcastManager; + +import com.licel.jcardsim.base.Simulator; +import com.licel.jcardsim.base.SimulatorRuntime; +import com.licel.jcardsim.samples.HelloWorldApplet; +import com.licel.jcardsim.utils.AIDUtil; +import com.mysmartlogon.gidsApplet.GidsApplet; +import com.vsmartcard.acardemulator.EmulatorService; +import com.vsmartcard.acardemulator.R; +import com.vsmartcard.acardemulator.Util; + +import net.pwendland.javacard.pki.isoapplet.IsoApplet; + +import openpgpcard.OpenPGPApplet; +import pkgYkneoOath.YkneoOath; + +public class JCEmulator implements Emulator { + + private static Simulator simulator = null; + + public JCEmulator( + Context context, + boolean activate_helloworld, + boolean activate_openpgp, + boolean activate_oath, + boolean activate_isoapplet, + boolean activate_gidsapplet) { + String aid, name, extra_install = "", extra_error = ""; + simulator = new Simulator(new SimulatorRuntime()); + + if (activate_helloworld) { + name = context.getResources().getString(R.string.applet_helloworld); + aid = context.getResources().getString(R.string.aid_helloworld); + try { + simulator.installApplet(AIDUtil.create(aid), HelloWorldApplet.class); + extra_install += "\n" + name + " (AID: " + aid + ")"; + } catch (Exception e) { + e.printStackTrace(); + extra_error += "\n" + "Could not install " + name + " (AID: " + aid + ")"; + } + } + + if (activate_openpgp) { + name = context.getResources().getString(R.string.applet_openpgp); + aid = context.getResources().getString(R.string.aid_openpgp); + try { + byte[] aid_bytes = Util.hexStringToByteArray(aid); + byte[] inst_params = new byte[aid.length() + 1]; + inst_params[0] = (byte) aid_bytes.length; + System.arraycopy(aid_bytes, 0, inst_params, 1, aid_bytes.length); + simulator.installApplet(AIDUtil.create(aid), OpenPGPApplet.class, inst_params, (short) 0, (byte) inst_params.length); + extra_install += "\n" + name + " (AID: " + aid + ")"; + } catch (Exception e) { + e.printStackTrace(); + extra_error += "\n" + "Could not install " + name + " (AID: " + aid + ")"; + } + } + + if (activate_oath) { + name = context.getResources().getString(R.string.applet_oath); + aid = context.getResources().getString(R.string.aid_oath); + try { + byte[] aid_bytes = Util.hexStringToByteArray(aid); + byte[] inst_params = new byte[aid.length() + 1]; + inst_params[0] = (byte) aid_bytes.length; + System.arraycopy(aid_bytes, 0, inst_params, 1, aid_bytes.length); + simulator.installApplet(AIDUtil.create(aid), YkneoOath.class, inst_params, (short) 0, (byte) inst_params.length); + extra_install += "\n" + name + " (AID: " + aid + ")"; + } catch (Exception e) { + e.printStackTrace(); + extra_error += "\n" + "Could not install " + name + " (AID: " + aid + ")"; + } + } + + if (activate_isoapplet) { + name = context.getResources().getString(R.string.applet_isoapplet); + aid = context.getResources().getString(R.string.aid_isoapplet); + try { + simulator.installApplet(AIDUtil.create(aid), IsoApplet.class); + extra_install += "\n" + name + " (AID: " + aid + ")"; + } catch (Exception e) { + e.printStackTrace(); + extra_error += "\n" + "Could not install " + name + " (AID: " + aid + ")"; + } + } + + if (activate_gidsapplet) { + name = context.getResources().getString(R.string.applet_gidsapplet); + aid = context.getResources().getString(R.string.aid_gidsapplet); + try { + simulator.installApplet(AIDUtil.create(aid), GidsApplet.class); + extra_install += "\n" + name + " (AID: " + aid + ")"; + } catch (Exception e) { + e.printStackTrace(); + extra_error += "\n" + "Could not install " + name + " (AID: " + aid + ")"; + } + } + + Intent i = new Intent(EmulatorService.TAG); + if (!extra_error.isEmpty()) + i.putExtra(EmulatorService.EXTRA_ERROR, extra_error); + if (!extra_install.isEmpty()) + i.putExtra(EmulatorService.EXTRA_INSTALL, extra_install); + LocalBroadcastManager.getInstance(context).sendBroadcast(i); + } + + public void destroy() { + if (simulator != null) { + simulator.reset(); + simulator = null; + } + } + + public byte[] process(byte[] commandAPDU) { + return simulator.transmitCommand(commandAPDU); + } + + public void deactivate() { + simulator.reset(); + } +} \ No newline at end of file diff --git a/ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/emulators/VICCEmulator.java b/ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/emulators/VICCEmulator.java new file mode 100644 index 0000000..e015f68 --- /dev/null +++ b/ACardEmulator/app/src/main/java/com/vsmartcard/acardemulator/emulators/VICCEmulator.java @@ -0,0 +1,134 @@ +package com.vsmartcard.acardemulator.emulators; + +import android.os.StrictMode; +import android.util.Log; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; + +public class VICCEmulator implements Emulator { + public static final int DEFAULT_PORT = 35963; + public static final String DEFAULT_HOSTNAME = "10.0.2.2"; + + private static Socket socket; + private static InputStream inputStream; + private static OutputStream outputStream; + + public VICCEmulator(String hostname, int port) { + if (socket == null) { + try { + if (android.os.Build.VERSION.SDK_INT > 9) { + StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); + StrictMode.setThreadPolicy(policy); + } + vpcdConnect(hostname, port); + sendPowerOn(); + } catch (IOException e) { + e.printStackTrace(); + vpcdDisconnect(); + } + } + } + + @Override + public void deactivate() { + try { + sendPowerOff(); + } catch (IOException e) { + e.printStackTrace(); + vpcdDisconnect(); + } + } + + @Override + public void destroy() { + vpcdDisconnect(); + } + + @Override + public byte[] process(byte[] commandAPDU) { + try { + sendToVPCD(commandAPDU); + return receiveFromVPCD(); + } catch (IOException e) { + e.printStackTrace(); + } + return new byte[] {0x6D, 0x00}; + } + + private void sendPowerOff() throws IOException { + Log.d("", "Power Off"); + sendToVPCD(new byte[] {0x00}); + } + private void sendPowerOn() throws IOException { + Log.d("", "Power On"); + sendToVPCD(new byte[] {0x01}); + } + private void sendReset() throws IOException { + Log.d("", "Reset"); + sendToVPCD(new byte[] {0x02}); + } + + private byte[] receiveFromVPCD() throws IOException { + /* convert length from network byte order. + Note that Java always uses network byte order internally. */ + int length1 = inputStream.read(); + int length2 = inputStream.read(); + if (length1 == -1 || length2 == -1) { + // EOF + return null; + } + int length = (length1 << 8) + length2; + + byte[] data = new byte[length]; + + int offset = 0; + while (length > 0) { + int read = inputStream.read(data, offset, length); + if (read == -1) { + // EOF + return null; + } + offset += read; + length -= read; + } + + return data; + } + + private void sendToVPCD(byte[] data) throws IOException { + /* convert length to network byte order. + Note that Java always uses network byte order internally. */ + byte[] length = new byte[2]; + length[0] = (byte) (data.length >> 8); + length[1] = (byte) (data.length & 0xff); + outputStream.write(length); + + outputStream.write(data, 0, data.length); + + outputStream.flush(); + } + + private void vpcdConnect(String hostname, int port) throws IOException { + Log.d("", "Connecting to " + hostname + ":" + Integer.toString(port)); + socket = new Socket(InetAddress.getByName(hostname), port); + outputStream = socket.getOutputStream(); + inputStream = socket.getInputStream(); + } + + private void vpcdDisconnect() { + Log.d("", "Disconnecting"); + if (socket != null) { + try { + socket.close(); + } catch (IOException e) { + // ignore + } finally { + socket = null; + } + } + } +}