package com.licel.jcardsim.smartcardio;
import com.licel.jcardsim.utils.AutoResetEvent;
import javacard.framework.ISO7816;
import junit.framework.TestCase;
import org.bouncycastle.util.encoders.Hex;
import javax.smartcardio.*;
import java.security.NoSuchAlgorithmException;
import java.security.Security;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class CardTerminalSimulatorTest extends TestCase {
private static final ATR ETALON_ATR = new ATR(Hex.decode("3BFA1800008131FE454A434F5033315632333298"));
private static final String TEST_APPLET_AID = "010203040506070809";
public CardTerminalSimulatorTest(String name) {
super(name);
}
@Override
protected void setUp() throws Exception {
System.setProperty("com.licel.jcardsim.card.applet.0.AID", TEST_APPLET_AID);
System.setProperty("com.licel.jcardsim.card.applet.0.Class", "com.licel.jcardsim.samples.HelloWorldApplet");
}
@Override
protected void tearDown() throws Exception {
System.clearProperty("com.licel.jcardsim.card.applet.0.AID");
System.clearProperty("com.licel.jcardsim.card.applet.0.Class");
}
public void testCreateSingleTerminal() throws CardException, InterruptedException {
final AutoResetEvent autoResetEvent = new AutoResetEvent();
// get instance
final CardTerminals terminals = CardTerminalSimulator.terminals("my terminal");
final CardTerminal terminal = terminals.getTerminal("my terminal");
// create and insert card
CardSimulator cardSimulator = new CardSimulator();
cardSimulator.assignToTerminal(terminal);
assertSame(terminal, cardSimulator.getAssignedCardTerminal());
// connect to card
Card card = terminal.connect("T=1");
test(card);
// assign same card
new Thread() {
@Override
public void run() {
try {
if (terminals.waitForChange(0)) {
autoResetEvent.signal();
}
} catch (CardException e) {
throw new RuntimeException(e);
}
}
}.start();
cardSimulator.assignToTerminal(terminal);
assertTrue(autoResetEvent.await(1, TimeUnit.SECONDS));
assertSame(terminal, cardSimulator.getAssignedCardTerminal());
// assign different card
CardSimulator cardSimulator2 = new CardSimulator();
assertNull(cardSimulator2.getAssignedCardTerminal());
cardSimulator2.assignToTerminal(terminal);
assertSame(terminal, cardSimulator2.getAssignedCardTerminal());
assertNull(cardSimulator.getAssignedCardTerminal());
}
public void testCreateTerminals() throws CardException {
// get instance
CardTerminals terminals = CardTerminalSimulator.terminals("terminal #1", "terminal #2");
CardTerminal terminal = terminals.getTerminal("terminal #2");
// create and insert card
CardSimulator cardSimulator = new CardSimulator();
cardSimulator.assignToTerminal(terminal);
// connect to card
Card card = terminal.connect("T=1");
test(card);
}
public void testProvider() throws CardException, NoSuchAlgorithmException {
// register security provider
if (Security.getProvider("CardTerminalSimulator") == null) {
Security.addProvider(new CardTerminalSimulator.SecurityProvider());
}
// get instance
TerminalFactory tf = TerminalFactory.getInstance("CardTerminalSimulator", null);
CardTerminals terminals = tf.terminals();
CardTerminal terminal = terminals.getTerminal("jCardSim.Terminal");
// create and insert card
CardSimulator cardSimulator = new CardSimulator();
cardSimulator.assignToTerminal(terminal);
// connect to card
Card card = terminal.connect("T=1");
test(card);
}
public void testProviderCustomNames() throws CardException, NoSuchAlgorithmException {
// register security provider
if (Security.getProvider("CardTerminalSimulator") == null) {
Security.addProvider(new CardTerminalSimulator.SecurityProvider());
}
// get instance
TerminalFactory tf = TerminalFactory.getInstance("CardTerminalSimulator", new String[]{"x", "y"});
CardTerminals terminals = tf.terminals();
CardTerminal terminal = terminals.getTerminal("y");
// create and insert card
CardSimulator cardSimulator = new CardSimulator();
cardSimulator.assignToTerminal(terminal);
// connect to card
Card card = terminal.connect("T=1");
test(card);
assertEquals(2, terminals.list().size());
assertEquals("x", terminals.list().get(0).getName());
assertEquals("y", terminals.list().get(1).getName());
}
public void testWaitForInsert() throws CardException, InterruptedException {
final AutoResetEvent autoResetEvent = new AutoResetEvent();
final CardTerminals terminals = CardTerminalSimulator.terminals("my terminal");
final CardTerminal terminal = terminals.getTerminal("my terminal");
assertEquals(true, terminal.waitForCardAbsent(1));
assertEquals(false, terminal.waitForCardPresent(1));
final CardSimulator cardSimulator = new CardSimulator();
new Thread() {
@Override
public void run() {
cardSimulator.assignToTerminal(terminal);
autoResetEvent.signal();
}
}.start();
autoResetEvent.await(1, TimeUnit.MINUTES);
assertEquals(true, terminal.waitForCardPresent(1));
// connect to card
Card card = terminal.connect("T=1");
test(card);
}
public void testWaitForCardAbsent() throws CardException, InterruptedException {
final CardTerminals terminals = CardTerminalSimulator.terminals("my terminal");
final CardTerminal terminal = terminals.getTerminal("my terminal");
final CardSimulator cardSimulator = new CardSimulator();
cardSimulator.assignToTerminal(terminal);
assertEquals(true, terminal.waitForCardPresent(1));
assertEquals(false, terminal.waitForCardAbsent(1));
new Thread() {
@Override
public void run() {
cardSimulator.assignToTerminal(null);
}
}.start();
assertEquals(true, terminal.waitForCardAbsent(0));
}
public void testWaitForCardChange() throws CardException, InterruptedException {
final CardTerminals terminals = CardTerminalSimulator.terminals("my terminal");
final CardSimulator cardSimulator = new CardSimulator();
assertEquals(false, terminals.waitForChange(1));
CardTerminal terminal = terminals.getTerminal("my terminal");
cardSimulator.assignToTerminal(terminal);
assertEquals(true, terminals.waitForChange(1));
assertEquals(false, terminals.waitForChange(1));
cardSimulator.assignToTerminal(null);
assertEquals(true, terminals.waitForChange(1));
}
public void testList() throws CardException, InterruptedException {
final CardTerminals terminals = CardTerminalSimulator.terminals("1", "2", "3", "4");
CardTerminal terminal1 = terminals.getTerminal("1");
assertEquals(4, terminals.list().size());
assertEquals(4, terminals.list(CardTerminals.State.ALL).size());
CardSimulator cardSimulator = new CardSimulator();
cardSimulator.assignToTerminal(terminal1);
assertTrue(terminal1.isCardPresent());
assertEquals(3, terminals.list(CardTerminals.State.CARD_ABSENT).size());
assertEquals(3, terminals.list(CardTerminals.State.CARD_REMOVAL).size());
assertEquals(0, terminals.list(CardTerminals.State.CARD_PRESENT).size());
assertEquals(1, terminals.list(CardTerminals.State.CARD_INSERTION).size());
assertEquals(0, terminals.list(CardTerminals.State.CARD_PRESENT).size());
assertEquals(true, terminals.waitForChange(1));
assertEquals(1, terminals.list(CardTerminals.State.CARD_INSERTION).size());
assertEquals(0, terminals.list(CardTerminals.State.CARD_REMOVAL).size());
cardSimulator.assignToTerminal(null);
assertFalse(terminal1.isCardPresent());
assertEquals(true, terminals.waitForChange(1));
assertEquals(4, terminals.list(CardTerminals.State.ALL).size());
assertEquals(3, terminals.list(CardTerminals.State.CARD_ABSENT).size());
assertEquals(1, terminals.list(CardTerminals.State.CARD_REMOVAL).size());
assertEquals(0, terminals.list(CardTerminals.State.CARD_INSERTION).size());
assertEquals(0, terminals.list(CardTerminals.State.CARD_PRESENT).size());
assertEquals(4, terminals.list(CardTerminals.State.CARD_ABSENT).size());
}
public void testExclusive() throws CardException, InterruptedException {
final CardTerminal terminal = CardTerminalSimulator.terminal(new CardSimulator());
final AutoResetEvent autoResetEvent = new AutoResetEvent();
final AtomicBoolean gotException = new AtomicBoolean(false);
final CardSimulator cardSimulator = new CardSimulator();
cardSimulator.assignToTerminal(terminal);
final Card card = terminal.connect("T=1");
final CommandAPDU commandAPDU = new CommandAPDU(0, 0, 0, 0);
card.beginExclusive();
card.getBasicChannel().transmit(commandAPDU);
new Thread() {
@Override
public void run() {
try {
card.getBasicChannel().transmit(commandAPDU);
} catch (CardException e) {
gotException.set(true);
} finally {
autoResetEvent.signal();
}
autoResetEvent.signal();
}
}.start();
autoResetEvent.await(1, TimeUnit.MINUTES);
card.endExclusive();
}
private void test(Card jcsCard) throws CardException {
assertTrue(jcsCard != null);
// check card ATR
assertEquals(jcsCard.getATR(), ETALON_ATR);
// check card protocol
assertEquals(jcsCard.getProtocol(), "T=1");
// get basic channel
CardChannel jcsChannel = jcsCard.getBasicChannel();
assertTrue(jcsChannel != null);
// create applet data = aid len (byte), aid bytes, params length (byte), param
byte[] aidBytes = Hex.decode(TEST_APPLET_AID);
byte[] createData = new byte[1 + aidBytes.length + 1 + 2 + 3];
createData[0] = (byte) aidBytes.length;
System.arraycopy(aidBytes, 0, createData, 1, aidBytes.length);
createData[1 + aidBytes.length] = (byte) 5;
createData[2 + aidBytes.length] = 0; // aid
createData[3 + aidBytes.length] = 0; // control
createData[4 + aidBytes.length] = 2; // params
createData[5 + aidBytes.length] = 0xF; // params
createData[6 + aidBytes.length] = 0xF; // params
CommandAPDU createApplet = new CommandAPDU(0x80, 0xb8, 0, 0, createData);
ResponseAPDU response = jcsChannel.transmit(createApplet);
assertEquals(response.getSW(), 0x9000);
assertEquals(true, Arrays.equals(response.getData(), aidBytes));
// select applet
CommandAPDU selectApplet = new CommandAPDU(ISO7816.CLA_ISO7816, ISO7816.INS_SELECT, 4, 0, Hex.decode(TEST_APPLET_AID));
response = jcsChannel.transmit(selectApplet);
assertEquals(response.getSW(), 0x9000);
// test NOP
response = jcsChannel.transmit(new CommandAPDU(0x01, 0x02, 0x00, 0x00));
assertEquals(0x9000, response.getSW());
// test SW_INS_NOT_SUPPORTED
response = jcsChannel.transmit(new CommandAPDU(0x01, 0x05, 0x00, 0x00));
assertEquals(ISO7816.SW_INS_NOT_SUPPORTED, response.getSW());
// test hello world from card
response = jcsChannel.transmit(new CommandAPDU(0x01, 0x01, 0x00, 0x00));
assertEquals(0x9000, response.getSW());
assertEquals("Hello world !", new String(response.getData()));
// test echo
response = jcsChannel.transmit(new CommandAPDU(0x01, 0x01, 0x01, 0x00, ("Hello javacard world !").getBytes()));
assertEquals(0x9000, response.getSW());
assertEquals("Hello javacard world !", new String(response.getData()));
// test echo v2
response = jcsChannel.transmit(new CommandAPDU(0x01, 0x03, 0x00, 0x00, ("Hello javacard world !").getBytes()));
assertEquals(0x9000, response.getSW());
assertEquals("Hello javacard world !", new String(response.getData()));
// test echo install params
response = jcsChannel.transmit(new CommandAPDU(0x01, 0x04, 0x00, 0x00));
assertEquals(0x9000, response.getSW());
assertEquals(0xF, response.getData()[0]);
assertEquals(0xF, response.getData()[1]);
// test continued data
response = jcsChannel.transmit(new CommandAPDU(0x01, 0x06, 0x00, 0x00));
assertEquals(0x6107, response.getSW());
assertEquals("Hello ", new String(response.getData()));
// test https://github.com/licel/jcardsim/issues/13
byte[] listObjectsCmd = new byte[5];
listObjectsCmd[0] = (byte) 0xb0;
listObjectsCmd[1] = (byte) 0x58;
listObjectsCmd[2] = (byte) 0x00;
listObjectsCmd[3] = (byte) 0x00;
listObjectsCmd[4] = (byte) 0x0E;
response = jcsChannel.transmit(new CommandAPDU(listObjectsCmd));
assertEquals(0x9C12, response.getSW());
// application specific sw + data
response = jcsChannel.transmit(new CommandAPDU(0x01, 0x07, 0x00, 0x00));
assertEquals(0x9B00, response.getSW());
assertEquals("Hello world !", new String(response.getData()));
// sending maximum data
response = jcsChannel.transmit(new CommandAPDU(0x01, 0x08, 0x00, 0x00));
assertEquals(0x9000, response.getSW());
}
}