package com.licel.jcardsim.base;
import com.licel.jcardsim.samples.Sha1Applet;
import com.licel.jcardsim.utils.AIDUtil;
import javacard.framework.AID;
import javacard.framework.JCSystem;
import javacard.framework.SystemException;
import junit.framework.TestCase;
import javax.smartcardio.ResponseAPDU;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
public class TransientMemoryTest extends TestCase {
private static final byte CLA = (byte) 0x80;
private static final byte INS_DIGEST = 0;
private static final byte INS_LAST_DIGEST = 6;
public TransientMemoryTest(String name) {
super(name);
}
public void testMemoryManagementWorks() {
final Object dummy1 = new Object();
final short size = 1;
TransientMemory transientMemory = new TransientMemory();
byte[] corBytes = transientMemory.makeByteArray(size, JCSystem.CLEAR_ON_RESET);
short[] corShorts = transientMemory.makeShortArray(size, JCSystem.CLEAR_ON_RESET);
boolean[] corBooleans = transientMemory.makeBooleanArray(size, JCSystem.CLEAR_ON_RESET);
Object[] corObjects = transientMemory.makeObjectArray(size, JCSystem.CLEAR_ON_RESET);
corBytes[0] = 123;
corShorts[0] = 123;
corBooleans[0] = true;
corObjects[0] = dummy1;
byte[] codBytes = transientMemory.makeByteArray(size, JCSystem.CLEAR_ON_DESELECT);
short[] codShorts = transientMemory.makeShortArray(size, JCSystem.CLEAR_ON_DESELECT);
boolean[] codBooleans = transientMemory.makeBooleanArray(size, JCSystem.CLEAR_ON_DESELECT);
Object[] codObjects = transientMemory.makeObjectArray(size, JCSystem.CLEAR_ON_DESELECT);
codBytes[0] = 123;
codShorts[0] = 123;
codBooleans[0] = true;
codObjects[0] = dummy1;
transientMemory.clearOnDeselect();
assertTrue(codBytes[0] == 0 && corBytes[0] != 0);
assertTrue(codShorts[0] == 0 && corShorts[0] != 0);
assertTrue(codObjects[0] == null && corObjects[0] == dummy1);
assertTrue(!codBooleans[0] && corBooleans[0]);
codBytes[0] = 123;
codShorts[0] = 123;
codBooleans[0] = true;
codObjects[0] = dummy1;
transientMemory.clearOnReset();
assertTrue(codBytes[0] == 0 && corBytes[0] == 0);
assertTrue(codShorts[0] == 0 && corShorts[0] == 0);
assertTrue(codObjects[0] == null && corObjects[0] == null);
assertTrue(!codBooleans[0] && !corBooleans[0]);
assertEquals(JCSystem.CLEAR_ON_RESET, transientMemory.isTransient(corBytes));
assertEquals(JCSystem.CLEAR_ON_RESET, transientMemory.isTransient(corShorts));
assertEquals(JCSystem.CLEAR_ON_RESET, transientMemory.isTransient(corBooleans));
assertEquals(JCSystem.CLEAR_ON_RESET, transientMemory.isTransient(corObjects));
assertEquals(JCSystem.CLEAR_ON_DESELECT, transientMemory.isTransient(codBytes));
assertEquals(JCSystem.CLEAR_ON_DESELECT, transientMemory.isTransient(codShorts));
assertEquals(JCSystem.CLEAR_ON_DESELECT, transientMemory.isTransient(codBooleans));
assertEquals(JCSystem.CLEAR_ON_DESELECT, transientMemory.isTransient(codObjects));
transientMemory.forgetBuffers();
assertEquals(JCSystem.NOT_A_TRANSIENT_OBJECT, transientMemory.isTransient(corBytes));
assertEquals(JCSystem.NOT_A_TRANSIENT_OBJECT, transientMemory.isTransient(corShorts));
assertEquals(JCSystem.NOT_A_TRANSIENT_OBJECT, transientMemory.isTransient(corBooleans));
assertEquals(JCSystem.NOT_A_TRANSIENT_OBJECT, transientMemory.isTransient(corObjects));
assertEquals(JCSystem.NOT_A_TRANSIENT_OBJECT, transientMemory.isTransient(codBytes));
assertEquals(JCSystem.NOT_A_TRANSIENT_OBJECT, transientMemory.isTransient(codShorts));
assertEquals(JCSystem.NOT_A_TRANSIENT_OBJECT, transientMemory.isTransient(codBooleans));
assertEquals(JCSystem.NOT_A_TRANSIENT_OBJECT, transientMemory.isTransient(codObjects));
}
public void testInvalidEventThrows() {
final byte invalid = JCSystem.CLEAR_ON_DESELECT + JCSystem.CLEAR_ON_RESET;
TransientMemory transientMemory = new TransientMemory();
try {
transientMemory.makeByteArray(2, invalid);
fail("No exception");
}
catch (SystemException e) {
assertEquals(SystemException.ILLEGAL_VALUE, e.getReason());
}
try {
transientMemory.makeBooleanArray((short) 1, invalid);
fail("No exception");
}
catch (SystemException e) {
assertEquals(SystemException.ILLEGAL_VALUE, e.getReason());
}
try {
transientMemory.makeObjectArray((short) 1, invalid);
fail("No exception");
}
catch (SystemException e) {
assertEquals(SystemException.ILLEGAL_VALUE, e.getReason());
}
try {
transientMemory.makeBooleanArray((short) 1, invalid);
fail("No exception");
}
catch (SystemException e) {
assertEquals(SystemException.ILLEGAL_VALUE, e.getReason());
}
}
public void testCleanOnDeselectWorks() throws NoSuchAlgorithmException {
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
byte[] expectedOutput = sha1.digest(new byte[]{'A'});
AID aid = AIDUtil.create("0102030405");
Simulator instance = new Simulator();
instance.installApplet(aid, Sha1Applet.class);
instance.selectApplet(aid);
// calculate SHA1
byte[] apdu = new byte[]{CLA, INS_DIGEST, 0, 0, 1, 'A'};
byte[] result = instance.transmitCommand(apdu);
ResponseAPDU responseApdu = new ResponseAPDU(result);
assertEquals(0x9000, responseApdu.getSW());
assertEquals(Arrays.toString(expectedOutput), Arrays.toString(responseApdu.getData()));
// check last digest
apdu = new byte[]{CLA, INS_LAST_DIGEST, 0, 0};
result = instance.transmitCommand(apdu);
responseApdu = new ResponseAPDU(result);
assertEquals(0x9000, responseApdu.getSW());
assertEquals(Arrays.toString(expectedOutput), Arrays.toString(responseApdu.getData()));
// trigger clean on deselect
instance.selectApplet(aid);
// check last digest is all zero
apdu = new byte[]{CLA, INS_LAST_DIGEST, 0, 0};
result = instance.transmitCommand(apdu);
responseApdu = new ResponseAPDU(result);
assertEquals(0x9000, responseApdu.getSW());
assertEquals(Arrays.toString(new byte[20]), Arrays.toString(responseApdu.getData()));
}
}