forked from licel/jcardsim
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- implement applet firewall with java.reflect proxy. Fixes licel#60 - allow externalAccess. Fixes licel#45
- Loading branch information
1 parent
ade8ee9
commit 52c1cf3
Showing
12 changed files
with
538 additions
and
53 deletions.
There are no files selected for viewing
91 changes: 91 additions & 0 deletions
91
src/main/java/com/licel/jcardsim/base/AppletContextManager.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<AppletContext> contextStack = new Stack<AppletContext>(); | ||
|
||
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 <code>null</code>. | ||
*/ | ||
public AID getActiveAID() { | ||
return contextStack.empty() ? null : contextStack.peek().appletAID; | ||
} | ||
|
||
/** | ||
* @return the AID of the active applet or <code>null</code>. | ||
*/ | ||
public AID getActivePackageAID() { | ||
return contextStack.empty() ? null : contextStack.peek().packageAID; | ||
} | ||
|
||
|
||
/** | ||
* @return the AID of the previous applet or <code>null</code>. | ||
*/ | ||
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 + | ||
'}'; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Method> whiteList; | ||
private final Supplier<AppletContextManager> appletContextManagerSupplier; | ||
private final Shareable shareable; | ||
|
||
public AppletFirewall(Supplier<AppletContextManager> 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<Class> interfaces = new ArrayList<Class>(); | ||
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<Method> buildWhiteList(final Class[] interfaces) { | ||
final Set<Method> whiteList = new HashSet<Method>(); | ||
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; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.