diff --git a/src/main/java/com/licel/jcardsim/base/AppletContextManager.java b/src/main/java/com/licel/jcardsim/base/AppletContextManager.java new file mode 100644 index 00000000..bdc68ca4 --- /dev/null +++ b/src/main/java/com/licel/jcardsim/base/AppletContextManager.java @@ -0,0 +1,91 @@ +package com.licel.jcardsim.base; + +import java.util.Stack; +import javacard.framework.AID; + +public final class AppletContextManager { + /** the current context */ + private Stack contextStack = new Stack(); + + public AppletContextManager() { + } + + /** + * Clear the context. + */ + public void clear() { + contextStack.clear(); + } + + /** + * Enter a context + * @param packageAID packageAID + * @param appletAID appletAID + */ + public void enterContext(AID packageAID, AID appletAID) { + contextStack.push(new AppletContext(packageAID, appletAID)); + } + + /** + * Leave a context + */ + public void leaveContext() { + if (!contextStack.isEmpty()) { + contextStack.pop(); + } + } + + /** + * @return the AID of the active applet or null. + */ + public AID getActiveAID() { + return contextStack.empty() ? null : contextStack.peek().appletAID; + } + + /** + * @return the AID of the active applet or null. + */ + public AID getActivePackageAID() { + return contextStack.empty() ? null : contextStack.peek().packageAID; + } + + + /** + * @return the AID of the previous applet or null. + */ + public AID getPreviousContextAID() { + if (contextStack.size() <= 1) { + return null; + } + final AID currentPackage = contextStack.peek().packageAID; + for (int i = contextStack.size() - 1; i >= 0; --i) { + final AID p = contextStack.get(i).packageAID; + if (!p.equals(currentPackage)) { + return contextStack.get(i).appletAID; + } + } + return null; + } + + private static final class AppletContext { + public final AID packageAID; + public final AID appletAID; + + public AppletContext(AID packageAID, AID appletAID) { + if (packageAID == null) + throw new NullPointerException("packageAID"); + if (appletAID == null) + throw new NullPointerException("appletAID"); + this.packageAID = packageAID; + this.appletAID = appletAID; + } + + @Override + public String toString() { + return "AppletContext{" + + "packageAID=" + packageAID + + ", appletAID=" + appletAID + + '}'; + } + } +} diff --git a/src/main/java/com/licel/jcardsim/base/AppletFirewall.java b/src/main/java/com/licel/jcardsim/base/AppletFirewall.java new file mode 100644 index 00000000..db7427a9 --- /dev/null +++ b/src/main/java/com/licel/jcardsim/base/AppletFirewall.java @@ -0,0 +1,95 @@ +package com.licel.jcardsim.base; + +import com.licel.jcardsim.utils.AIDUtil; +import com.licel.jcardsim.utils.Supplier; +import javacard.framework.AID; +import javacard.framework.Shareable; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.*; + +public class AppletFirewall { + private final Shareable firewalledShareable; + private final AID packageAID; + private final AID appletAID; + private final Set whiteList; + private final Supplier appletContextManagerSupplier; + private final Shareable shareable; + + public AppletFirewall(Supplier appletContextManagerSupplier, Shareable shareable) { + if (shareable == null) { + throw new NullPointerException("shareable"); + } + + final Class shareableClass = shareable.getClass(); + final AppletContextManager appletContextManager = appletContextManagerSupplier.get(); + final Class[] interfaces = allInterfaces(shareableClass); + + this.appletContextManagerSupplier = appletContextManagerSupplier; + this.shareable = shareable; + this.packageAID = appletContextManager.getActivePackageAID(); + this.appletAID = appletContextManager.getActiveAID(); + this.whiteList = buildWhiteList(interfaces); + + this.firewalledShareable = (Shareable) Proxy.newProxyInstance(shareableClass.getClassLoader(), + interfaces, + new InvocationHandler() { + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return AppletFirewall.this.invoke(proxy, method, args); + } + }); + } + + protected static Class[] allInterfaces(final Class shareableClass) { + final List interfaces = new ArrayList(); + Class currentClass = shareableClass; + while (!currentClass.equals(Object.class)) { + Collections.addAll(interfaces, currentClass.getInterfaces()); + currentClass = currentClass.getSuperclass(); + } + return interfaces.toArray(new Class[interfaces.size()]); + } + + protected static Set buildWhiteList(final Class[] interfaces) { + final Set whiteList = new HashSet(); + for (Class anInterface : interfaces) { + if (Shareable.class.isAssignableFrom(anInterface)) { + Collections.addAll(whiteList, anInterface.getMethods()); + } + } + return whiteList; + } + + protected Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (proxy != firewalledShareable) { + throw new AssertionError("Wrong proxy"); + } + + final AppletContextManager contextManager = appletContextManagerSupplier.get(); + final AID callerPackage = contextManager.getActivePackageAID(); + + if (method.getDeclaringClass().equals(Object.class) && method.getName().equals("toString")) { + return "AppletFirewallProxy bound to " + AIDUtil.toString(packageAID) + " for " + shareable; + } + if (!packageAID.equals(callerPackage)) { + if (!whiteList.contains(method)) { + throw new SecurityException("Calling this method is not allowed"); + } + contextManager.enterContext(packageAID, appletAID); + try { + return method.invoke(shareable, args); + } + finally { + contextManager.leaveContext(); + } + } else { + return method.invoke(shareable, args); + } + } + + public Shareable getShareable() { + return firewalledShareable; + } +} \ No newline at end of file diff --git a/src/main/java/com/licel/jcardsim/base/Simulator.java b/src/main/java/com/licel/jcardsim/base/Simulator.java index 1af9d297..2ef18a5e 100644 --- a/src/main/java/com/licel/jcardsim/base/Simulator.java +++ b/src/main/java/com/licel/jcardsim/base/Simulator.java @@ -163,7 +163,7 @@ public AID loadApplet(AID aid, String appletClassName) throws SystemException { */ public AID loadApplet(AID aid, Class appletClass) throws SystemException { synchronized (runtime) { - runtime.loadApplet(aid, requireExtendsApplet(appletClass)); + runtime.wrapAppletIntoLoadFile(aid, requireExtendsApplet(appletClass)); } return aid; } diff --git a/src/main/java/com/licel/jcardsim/base/SimulatorRuntime.java b/src/main/java/com/licel/jcardsim/base/SimulatorRuntime.java index 07dab23e..6289f721 100644 --- a/src/main/java/com/licel/jcardsim/base/SimulatorRuntime.java +++ b/src/main/java/com/licel/jcardsim/base/SimulatorRuntime.java @@ -17,14 +17,16 @@ import com.licel.jcardsim.utils.AIDUtil; import com.licel.jcardsim.utils.BiConsumer; +import com.licel.jcardsim.utils.Supplier; import javacard.framework.*; import javacardx.apdu.ExtendedLength; +import org.bouncycastle.crypto.digests.SHA256Digest; +import java.io.UnsupportedEncodingException; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.*; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; /** @@ -39,8 +41,8 @@ public class SimulatorRuntime { protected final SortedMap applets = new TreeMap(AIDUtil.comparator()); /** storage for load files */ protected final SortedMap loadFiles = new TreeMap(AIDUtil.comparator()); - /** storage for automatically generated loadFile AIDs */ - protected final SortedMap generatedLoadFileAIDs = new TreeMap(AIDUtil.comparator()); + /** maps applet AIDs to LoadFile AIDs */ + protected final Map appletAIDToLoadFileAID = new HashMap(); /** method for resetting APDUs */ protected final Method apduPrivateResetMethod; /** outbound response byte array buffer */ @@ -52,10 +54,6 @@ public class SimulatorRuntime { /** extended APDU */ protected final APDU extendedAPDU; - /** current selected applet */ - protected AID currentAID; - /** previous selected applet */ - protected AID previousAID; /** outbound response byte array buffer size */ protected short responseBufferSize = 0; /** if the applet is currently being selected */ @@ -68,6 +66,8 @@ public class SimulatorRuntime { protected byte transactionDepth = 0; /** previousActiveObject */ protected Object previousActiveObject; + /** context managers. one per channel */ + private AppletContextManager[] contextManagers = new AppletContextManager[] {new AppletContextManager()}; public SimulatorRuntime() { this(new TransientMemory()); @@ -106,7 +106,7 @@ protected final void activateSimulatorRuntimeInstance() { * @return current applet context AID or null */ public AID getAID() { - return currentAID; + return getAppletContextManagerForActiveChannel().getActiveAID(); } /** @@ -144,7 +144,7 @@ public ApplicationInstance lookupApplet(AID lookupAid) { * @return previous selected applet context AID or null */ public AID getPreviousContextAID() { - return previousAID; + return getAppletContextManagerForActiveChannel().getPreviousContextAID(); } /** @@ -161,24 +161,6 @@ protected Applet getApplet(AID aid) { else return a.getApplet(); } - /** - * Load applet - * @param aid Applet AID - * @param appletClass Applet class - */ - public void loadApplet(AID aid, Class appletClass) { - if (generatedLoadFileAIDs.keySet().contains(aid)) { - throw new SystemException(SystemException.ILLEGAL_AID); - } - // generate a load file AID - byte[] generated = new byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, 0, 0}; - Util.setShort(generated, (short) 3, (short) generatedLoadFileAIDs.size()); - AID generatedAID = AIDUtil.create(generated); - - generatedLoadFileAIDs.put(aid, generatedAID); - loadLoadFile(new LoadFile(generatedAID, generatedAID, appletClass)); - } - /** * Load a LoadFile * @param loadFile LoadFile to load @@ -197,6 +179,7 @@ public void loadLoadFile(LoadFile loadFile) { */ protected void deleteApplet(AID aid) { activateSimulatorRuntimeInstance(); + ApplicationInstance applicationInstance = lookupApplet(aid); if (applicationInstance == null) { throw new SystemException(SystemException.ILLEGAL_AID); @@ -208,7 +191,8 @@ protected void deleteApplet(AID aid) { return; } - if (getApplet(currentAID) == applet) { + AID currentAppletAID = getAppletContextManagerForActiveChannel().getActiveAID(); + if (getApplet(currentAppletAID) == applet) { deselect(applicationInstance); } @@ -251,7 +235,11 @@ public byte[] transmitCommand(byte[] command) throws SystemException { AID newAid = findAppletForSelectApdu(command, apduCase); if (newAid != null) { deselect(lookupApplet(getAID())); - currentAID = newAid; + + AppletContextManager contextManager = getAppletContextManagerForActiveChannel(); + contextManager.clear(); + contextManager.enterContext(appletAIDToLoadFileAID.get(newAid), newAid); + applet = getApplet(getAID()); selecting = true; } @@ -381,8 +369,9 @@ public void reset() { Arrays.fill(responseBuffer, (byte) 0); transactionDepth = 0; responseBufferSize = 0; - currentAID = null; - previousAID = null; + for (AppletContextManager manager : contextManagers) { + manager.clear(); + } transientMemory.clearOnReset(); } @@ -399,12 +388,13 @@ public void resetRuntime() { } loadFiles.clear(); - generatedLoadFileAIDs.clear(); + appletAIDToLoadFileAID.clear(); Arrays.fill(responseBuffer, (byte) 0); transactionDepth = 0; responseBufferSize = 0; - currentAID = null; - previousAID = null; + for (AppletContextManager manager : contextManagers) { + manager.clear(); + } transientMemory.clearOnReset(); transientMemory.forgetBuffers(); } @@ -525,12 +515,34 @@ public short getAvailableTransientDeselectMemory() { * @return the shareable interface object or null */ public Shareable getSharedObject(AID serverAID, byte parameter) { - Applet serverApplet = getApplet(serverAID); - if (serverApplet != null) { - return serverApplet.getShareableInterfaceObject(getAID(), + final AID callerAID = getAID(); + final AID callerPackageAID = appletAIDToLoadFileAID.get(callerAID); + final AID serverPackageAID = appletAIDToLoadFileAID.get(serverAID); + + final Applet serverApplet = getApplet(serverAID); + if (serverApplet == null) { + return null; + } + + final AppletContextManager context = getAppletContextManagerForActiveChannel(); + try { + context.enterContext(callerPackageAID, callerAID); + context.enterContext(serverPackageAID, serverAID); + final Shareable shareable = serverApplet.getShareableInterfaceObject(callerAID, parameter); + if (shareable == null) { + return null; + } + return new AppletFirewall( + new Supplier() { + public AppletContextManager get() { + return getAppletContextManagerForActiveChannel(); + } + }, shareable).getShareable(); + } finally { + context.leaveContext(); + context.leaveContext(); } - return null; } /** @@ -582,11 +594,24 @@ protected static boolean isAppletSelectionApdu(byte[] apdu) { } public void installApplet(final AID appletAid, byte[] bArray, short bOffset, byte bLength) { - AID generatedAID = generatedLoadFileAIDs.get(appletAid); - if (generatedAID == null || !loadFiles.keySet().contains(generatedAID)) { + LoadFile loadFile = findLoadFile(appletAid); + if (loadFile == null) { throw new SystemException(SystemException.ILLEGAL_AID); } - installApplet(generatedAID, generatedAID, appletAid, bArray, bOffset, bLength); + installApplet(loadFile.getAid(), appletAid, appletAid, bArray, bOffset, bLength); + } + + private LoadFile findLoadFile(AID appletAid) { + if (appletAid == null) { + return null; + } + for (LoadFile loadFile : loadFiles.values()) { + Module m = loadFile.getModule(appletAid); + if (m != null) { + return loadFile; + } + } + return null; } public void installApplet(AID loadFileAID, AID moduleAID, final AID appletAID, @@ -644,6 +669,45 @@ public void accept(Applet applet, AID installAID) { if (callCount.get() != 1) { throw new SystemException(SystemException.ILLEGAL_AID); } + appletAIDToLoadFileAID.put(appletAID, loadFileAID); + } + + protected AppletContextManager getAppletContextManagerForActiveChannel() { + return contextManagers[0]; + } + + public void wrapAppletIntoLoadFile(AID aid, Class aClass) { + final String packageName = aClass.getPackage() != null ? aClass.getPackage().getName() : ""; + final byte[] loadFileAIDBytes = new byte[16]; + loadFileAIDBytes[0] = (byte) 0xF0; + loadFileAIDBytes[1] = (byte) 'J'; + loadFileAIDBytes[2] = (byte) 'C'; + loadFileAIDBytes[3] = (byte) 'S'; + + final SHA256Digest digest = new SHA256Digest(); + final byte[] buffer = new byte[digest.getDigestSize()]; + try { + byte[] bytes = packageName.getBytes("UTF-8"); + digest.update(bytes, 0, bytes.length); + } catch (UnsupportedEncodingException e) { + // ignore + } + digest.doFinal(buffer, 0); + + System.arraycopy(buffer, 0, loadFileAIDBytes, 4, loadFileAIDBytes.length - 4); + + final AID loadFileAID = AIDUtil.create(loadFileAIDBytes); + final LoadFile loadFile = loadFiles.get(loadFileAID); + final List modules = new ArrayList(); + + if (loadFile != null) { + for (Module m : loadFile.getModules()) { + modules.add(m); + } + } + + modules.add(new Module(aid, aClass)); + loadFiles.put(loadFileAID, new LoadFile(loadFileAID, modules.toArray(new Module[modules.size()]))); } /** Represents an Applet instance */ diff --git a/src/main/java/com/licel/jcardsim/crypto/AsymmetricCipherImpl.java b/src/main/java/com/licel/jcardsim/crypto/AsymmetricCipherImpl.java index 08b25741..840a2f96 100644 --- a/src/main/java/com/licel/jcardsim/crypto/AsymmetricCipherImpl.java +++ b/src/main/java/com/licel/jcardsim/crypto/AsymmetricCipherImpl.java @@ -15,6 +15,8 @@ */ package com.licel.jcardsim.crypto; +import com.licel.jcardsim.base.SimulatorSystem; +import com.licel.jcardsim.base.TransientMemory; import javacard.framework.JCSystem; import javacard.framework.Util; import javacard.security.CryptoException; @@ -39,9 +41,11 @@ public class AsymmetricCipherImpl extends Cipher { boolean isInitialized; byte[] buffer; short bufferPos; + private final boolean externalAccess; - public AsymmetricCipherImpl(byte algorithm) { + public AsymmetricCipherImpl(byte algorithm, boolean externalAccess) { this.algorithm = algorithm; + this.externalAccess = externalAccess; switch (algorithm) { case ALG_RSA_NOPAD: engine = new RSAEngine(); @@ -67,9 +71,12 @@ public void init(Key theKey, byte theMode) throws CryptoException { if (!(theKey instanceof KeyWithParameters)) { CryptoException.throwIt(CryptoException.ILLEGAL_VALUE); } + final byte memoryType = externalAccess ? + JCSystem.MEMORY_TYPE_TRANSIENT_RESET : JCSystem.MEMORY_TYPE_TRANSIENT_DESELECT; + KeyWithParameters key = (KeyWithParameters) theKey; engine.init(theMode == MODE_ENCRYPT, key.getParameters()); - buffer = JCSystem.makeTransientByteArray((short) engine.getInputBlockSize(), JCSystem.CLEAR_ON_DESELECT); + buffer = JCSystem.makeTransientByteArray((short) engine.getInputBlockSize(), memoryType); bufferPos = 0; isInitialized = true; } diff --git a/src/main/java/com/licel/jcardsim/crypto/AsymmetricSignatureImpl.java b/src/main/java/com/licel/jcardsim/crypto/AsymmetricSignatureImpl.java index 4547ceae..98a2ff87 100644 --- a/src/main/java/com/licel/jcardsim/crypto/AsymmetricSignatureImpl.java +++ b/src/main/java/com/licel/jcardsim/crypto/AsymmetricSignatureImpl.java @@ -52,7 +52,7 @@ public class AsymmetricSignatureImpl extends Signature implements SignatureMessa boolean isRecovery; byte[] preSig; - public AsymmetricSignatureImpl(byte algorithm) { + public AsymmetricSignatureImpl(byte algorithm, boolean externalAccess) { this.algorithm = algorithm; isRecovery = false; switch (algorithm) { diff --git a/src/main/java/com/licel/jcardsim/crypto/CRC16.java b/src/main/java/com/licel/jcardsim/crypto/CRC16.java index 890ec2f7..7cebfcea 100644 --- a/src/main/java/com/licel/jcardsim/crypto/CRC16.java +++ b/src/main/java/com/licel/jcardsim/crypto/CRC16.java @@ -31,8 +31,10 @@ public class CRC16 extends Checksum { static final byte LENGTH = 2; private byte crc16[]; - public CRC16() { - crc16 = JCSystem.makeTransientByteArray(LENGTH, JCSystem.CLEAR_ON_DESELECT); + public CRC16(boolean externalAccess) { + final byte memoryType = externalAccess ? + JCSystem.MEMORY_TYPE_TRANSIENT_RESET : JCSystem.MEMORY_TYPE_TRANSIENT_DESELECT; + crc16 = JCSystem.makeTransientByteArray(LENGTH, memoryType); } public byte getAlgorithm() { diff --git a/src/main/java/com/licel/jcardsim/crypto/CRC32.java b/src/main/java/com/licel/jcardsim/crypto/CRC32.java index 4374cac5..49cc435b 100644 --- a/src/main/java/com/licel/jcardsim/crypto/CRC32.java +++ b/src/main/java/com/licel/jcardsim/crypto/CRC32.java @@ -34,8 +34,10 @@ public class CRC32 extends Checksum { 4, -63, 29, -73 }; - public CRC32() { - crc32 = JCSystem.makeTransientByteArray(LENGTH, JCSystem.CLEAR_ON_DESELECT); + public CRC32(boolean externalAccess) { + final byte memoryType = externalAccess ? + JCSystem.MEMORY_TYPE_TRANSIENT_RESET : JCSystem.MEMORY_TYPE_TRANSIENT_DESELECT; + crc32 = JCSystem.makeTransientByteArray(LENGTH, memoryType); } public byte getAlgorithm() { diff --git a/src/main/java/com/licel/jcardsim/crypto/SymmetricCipherImpl.java b/src/main/java/com/licel/jcardsim/crypto/SymmetricCipherImpl.java index 32380aa3..c4f0b932 100644 --- a/src/main/java/com/licel/jcardsim/crypto/SymmetricCipherImpl.java +++ b/src/main/java/com/licel/jcardsim/crypto/SymmetricCipherImpl.java @@ -67,7 +67,7 @@ public void init(Key theKey, byte theMode, byte[] bArray, short bOff, short bLen break; } selectCipherEngine(theKey); - byte[] iv = JCSystem.makeTransientByteArray(bLen, JCSystem.CLEAR_ON_RESET); + byte[] iv = new byte[bLen]; Util.arrayCopyNonAtomic(bArray, bOff, iv, (short) 0, bLen); engine.init(theMode == MODE_ENCRYPT, new ParametersWithIV(((SymmetricKeyImpl) theKey).getParameters(), iv)); isInitialized = true; diff --git a/src/main/java/com/licel/jcardsim/crypto/SymmetricSignatureImpl.java b/src/main/java/com/licel/jcardsim/crypto/SymmetricSignatureImpl.java index 7562b4e8..bb215fa1 100644 --- a/src/main/java/com/licel/jcardsim/crypto/SymmetricSignatureImpl.java +++ b/src/main/java/com/licel/jcardsim/crypto/SymmetricSignatureImpl.java @@ -15,6 +15,9 @@ */ package com.licel.jcardsim.crypto; +import com.licel.jcardsim.base.SimulatorSystem; +import com.licel.jcardsim.base.TransientMemory; +import javacard.framework.JCSystem; import javacard.framework.Util; import javacard.security.CryptoException; import javacard.security.Key; @@ -45,13 +48,59 @@ * @see Signature */ public class SymmetricSignatureImpl extends Signature { - + Mac engine; byte algorithm; boolean isInitialized; + private final TransientMemory transientMemory; + private byte[] sig; - public SymmetricSignatureImpl(byte algorithm) { + public SymmetricSignatureImpl(byte algorithm, boolean externalAccess) { + final byte memoryType = externalAccess ? + JCSystem.MEMORY_TYPE_TRANSIENT_RESET : JCSystem.MEMORY_TYPE_TRANSIENT_DESELECT; this.algorithm = algorithm; + this.transientMemory = SimulatorSystem.instance().getTransientMemory(); + + int size; + switch (algorithm) { + case ALG_DES_MAC4_ISO9797_M2: + case ALG_DES_MAC4_PKCS5: + case ALG_DES_MAC4_NOPAD: + case ALG_DES_MAC4_ISO9797_M1: + size = 4; + break; + case ALG_DES_MAC8_ISO9797_M1: + case ALG_DES_MAC8_NOPAD: + case ALG_DES_MAC8_ISO9797_M2: + case ALG_DES_MAC8_ISO9797_1_M2_ALG3: + case ALG_DES_MAC8_PKCS5: + size = 8; + break; + case ALG_AES_MAC_128_NOPAD: + size = 16; + break; + case ALG_HMAC_SHA1: + size = new SHA1Digest().getDigestSize(); + break; + case ALG_HMAC_SHA_256: + size = new HMac(new SHA256Digest()).getMacSize(); + break; + case ALG_HMAC_SHA_384: + size = new HMac(new SHA384Digest()).getMacSize(); + break; + case ALG_HMAC_SHA_512: + size = new HMac(new SHA512Digest()).getMacSize(); + break; + case ALG_HMAC_MD5: + size = new HMac(new MD5Digest()).getMacSize(); + break; + case ALG_HMAC_RIPEMD160: + size = new HMac(new RIPEMD160Digest()).getMacSize(); + break; + default: + throw new CryptoException(CryptoException.NO_SUCH_ALGORITHM); + } + this.sig = transientMemory.makeByteArray(size, memoryType); } public void init(Key theKey, byte theMode) throws CryptoException { @@ -179,7 +228,6 @@ public boolean verify(byte[] inBuff, short inOffset, short inLength, byte[] sigB CryptoException.throwIt(CryptoException.ILLEGAL_USE); } engine.update(inBuff, inOffset, inLength); - byte[] sig = new byte[getLength()]; engine.doFinal(sig, (short) 0); engine.reset(); return Util.arrayCompare(sig, (short) 0, sigBuff, sigOffset, (short) sig.length) == 0; diff --git a/src/main/java/com/licel/jcardsim/utils/Supplier.java b/src/main/java/com/licel/jcardsim/utils/Supplier.java new file mode 100644 index 00000000..18a06427 --- /dev/null +++ b/src/main/java/com/licel/jcardsim/utils/Supplier.java @@ -0,0 +1,8 @@ +package com.licel.jcardsim.utils; + +/** + * Back-port of Java 8 java.util.function.Supplier. + */ +public interface Supplier { + T get(); +} diff --git a/src/test/java/com/licel/jcardsim/base/AppletFirewallWhiteListTest.java b/src/test/java/com/licel/jcardsim/base/AppletFirewallWhiteListTest.java new file mode 100644 index 00000000..6288ce3a --- /dev/null +++ b/src/test/java/com/licel/jcardsim/base/AppletFirewallWhiteListTest.java @@ -0,0 +1,168 @@ +package com.licel.jcardsim.base; + +import com.licel.jcardsim.utils.AIDUtil; +import com.licel.jcardsim.utils.ByteUtil; +import javacard.framework.*; +import junit.framework.TestCase; + +import java.lang.reflect.Method; +import java.util.Set; + +public class AppletFirewallWhiteListTest extends TestCase { + private static final StringBuffer LOG = new StringBuffer(); + private static final AID PACKAGE1 = AIDUtil.create("F0AA000000"); + private static final AID AID1 = AIDUtil.create("F0AA000001"); + private static final AID PACKAGE2 = AIDUtil.create("F0BB000000"); + private static final AID AID2 = AIDUtil.create("F0BB000001"); + + public void testWhiteListBuilding() throws Exception { + final Set whiteList = AppletFirewall.buildWhiteList( + AppletFirewall.allInterfaces(ConcreteDummyClass.class)); + for (Method m : whiteList) { + System.out.println(m); + } + assertTrue(whiteList.contains(ShareableA.class.getMethod("s1"))); + assertTrue(whiteList.contains(ShareableA.class.getMethod("s2"))); + assertTrue(whiteList.contains(ShareableA.class.getMethod("s3"))); + assertTrue(whiteList.contains(ShareableAExtended.class.getMethod("s4"))); + assertTrue(whiteList.contains(ShareableB.class.getMethod("s5"))); + assertEquals(5, whiteList.size()); + } + + public void testCalls() throws Throwable { + final SimulatorRuntime simulatorRuntime = new SimulatorRuntime(); + simulatorRuntime.loadLoadFile(new LoadFile(PACKAGE1, new Module(AID1, ServerApplet.class))); + simulatorRuntime.loadLoadFile(new LoadFile(PACKAGE2, new Module(AID2, ClientApplet.class))); + simulatorRuntime.installApplet(PACKAGE1, AID1, AID1, new byte[0], (short) 0, (byte) 0); + simulatorRuntime.installApplet(PACKAGE2, AID2, AID2, new byte[0], (short)0, (byte)0); + simulatorRuntime.transmitCommand(AIDUtil.select(AID2)); + + final ShareableAExtended obj = (ShareableAExtended) simulatorRuntime.getSharedObject(AID1, (byte) 0); + final AppletContextManager contextManager = simulatorRuntime.getAppletContextManagerForActiveChannel(); + assertNull(contextManager.getPreviousContextAID()); + + + // check all calls from applet with AID1 are allowed + LOG.setLength(0); + simulatorRuntime.transmitCommand(AIDUtil.select(AID1)); + obj.s1(); + ((AnotherInterface)obj).unsharedMethod(); + contextManager.leaveContext(); + assertEquals("s1 AID=F0AA000001 PREVIOUS CONTEXT=\n" + + "unsharedMethod AID=F0AA000001 PREVIOUS CONTEXT=\n", LOG.toString()); + + // check firewall works + LOG.setLength(0); + simulatorRuntime.transmitCommand(AIDUtil.select(AID2)); + obj.s1(); + try { + ((AnotherInterface)obj).unsharedMethod(); + fail("No exception"); + } catch (SecurityException se) { + // pass + } + obj.s2(); + assertEquals("s1 AID=F0AA000001 PREVIOUS CONTEXT=F0BB000001\n" + + "s2 AID=F0AA000001 PREVIOUS CONTEXT=F0BB000001\n", LOG.toString()); + } + + public void testGetPreviousContextAID() { + final AppletContextManager contextManager = new AppletContextManager(); + assertNull(contextManager.getPreviousContextAID()); + + contextManager.enterContext(AIDUtil.create("F0AA000010"), AIDUtil.create("F0AA000011")); + assertNull(contextManager.getPreviousContextAID()); + contextManager.enterContext(AIDUtil.create("F0AA000010"), AIDUtil.create("F0AA000012")); + assertNull(contextManager.getPreviousContextAID()); + contextManager.enterContext(AIDUtil.create("F0AA000010"), AIDUtil.create("F0AA000013")); + assertNull(contextManager.getPreviousContextAID()); + + contextManager.enterContext(AIDUtil.create("F0BB000010"), AIDUtil.create("F0BB000001")); + assertEquals("F0AA000013", AIDUtil.toString(contextManager.getPreviousContextAID())); + } + + public static class ServerApplet extends Applet { + public static void install(byte[] bArray, short bOffset, byte bLength) { + new ServerApplet().register(); + } + + @Override + public void process(APDU apdu) throws ISOException { + } + + @Override + public Shareable getShareableInterfaceObject(AID clientAID, byte parameter) { + return new ConcreteDummyClass(); + } + } + + public static class ClientApplet extends Applet { + public static void install(byte[] bArray, short bOffset, byte bLength) { + new ClientApplet().register(); + } + + @Override + public void process(APDU apdu) throws ISOException { + } + } + + interface ShareableA extends Shareable { + short s1(); + short s2(); + short s3(); + } + + interface ShareableAExtended extends ShareableA { + short s4(); + } + + interface ShareableB extends Shareable { + short s5(); + } + + interface AnotherInterface { + short unsharedMethod(); + } + + public static class AbstractDummyClass implements ShareableAExtended, AnotherInterface { + public short unsharedMethod() { + log("unsharedMethod"); + return (short) 0; + } + + public short s4() { + log("s4"); + return (short) 4; + } + + public short s1() { + log("s1"); + return (short) 1; + } + + public short s2() { + log("s2"); + return (short) 2; + } + + public short s3() { + log("s3"); + return (short) 3; + } + + protected void log(String method) { + AID previousContextAID = JCSystem.getPreviousContextAID(); + LOG.append(method) + .append(" AID=").append(AIDUtil.toString(JCSystem.getAID())) + .append(" PREVIOUS CONTEXT=").append(previousContextAID == null ? "" : AIDUtil.toString(previousContextAID)) + .append("\n"); + } + } + + public static class ConcreteDummyClass extends AbstractDummyClass implements ShareableB { + public short s5() { + log("s5"); + return (short) 5; + } + } +}