bertrandmartel / javacard-gradle-plugin

:key: Gradle plugin for JavaCard development
MIT License
32 stars 14 forks source link

Use JCDK's simulator to run APDU tasks #10

Open sigalor opened 6 years ago

sigalor commented 6 years ago

Hi everyone!

First, thanks so much for this astonishing and incredibly useful repository! I recently started getting into JavaCard/SIM development, and without open source projects like this, I would definitely be completely lost.

Currently, I am trying out the examples from your javacard-tutorial repository. The CAP file generation and everything else before it runs perfectly fine, but, as I don't have a physical smart card and reader yet, I'd like to try it out on a simulator. The JavaCard Development Kit by Oracle comes with the cref utility; that should be the simulator, right? Sadly, it comes as a Windows EXE file only, so one will need to use wine to execute it on Mac/Linux (I tried it and it works).

Still, it seems like currently, the Gradle plugin cannot use it yet. When I try something like gradle sendHello in the other repository, I get an error message like:

> Task :jc101-1c:sendHello FAILED
gp [-d, -a, 00A404000A01020304050607080901, -a, 0040000000]
GlobalPlatformPro unknown-development
Running on Linux 4.15.0-38-generic amd64, Java 1.8.0_181 by Oracle Corporation
Exception in thread "main" jnasmartcardio.Smartcardio$EstablishContextException: jnasmartcardio.Smartcardio$JnaPCSCException: SCardEstablishContext got response 0x8010001d (SCARD_E_NO_SERVICE: The Smart card resource manager is not running.)
        at jnasmartcardio.Smartcardio$JnaTerminalFactorySpi.engineTerminals(Smartcardio.java:81)
        at javax.smartcardio.TerminalFactory.terminals(TerminalFactory.java:351)
        at pro.javacard.gp.GPTool.main(GPTool.java:344)
Caused by: jnasmartcardio.Smartcardio$JnaPCSCException: SCardEstablishContext got response 0x8010001d (SCARD_E_NO_SERVICE: The Smart card resource manager is not running.)
        at jnasmartcardio.Smartcardio.check(Smartcardio.java:960)
        at jnasmartcardio.Smartcardio.check(Smartcardio.java:951)
        at jnasmartcardio.Smartcardio.access$000(Smartcardio.java:34)
        at jnasmartcardio.Smartcardio$JnaTerminalFactorySpi.engineTerminals(Smartcardio.java:79)
        ... 2 more

And, well, the message The Smart card resource manager is not running. is a fairly clear indication of what's going on. So, does anyone know how I can use the simulator here?

Regarding my system setup: I'm using Ubuntu 16.04 with OpenJDK 1.8.0_181. My JavaCard Development Kit version is 3.0.5u3.

bertrandmartel commented 6 years ago

@sigalor I've never used the cref utility. Note that the sendHello task calls global platform pro with the following parameters (see logs) :

gp  -d -a 00A404000A01020304050607080901 -a 0040000000

so you can test with global platform pro if you need to tweak parameters

Note that I have just updated the lib to work with the most recent version of global platform pro. Use 1.5.6 and add global platform pro dependency in build.gradle :

apply plugin: 'javacard'

dependencies {
    compile 'com.github.martinpaljak:globalplatformpro:18.09.14'
}

javacard {
....
}
sigalor commented 6 years ago

@bertrandmartel Thanks a lot for your answer! So, I investigated further on this myself and found out the following. There are two methods of simulating a Java Card application.


First, the one I thought of initially using the cref utility. It seems like it emulates the actual EEPROM, i.e. one somehow needs to write actual bytecode onto that 64K file. For that, the maskgen utility is needed. Although there is no maskgen.bat in the current JCDK 3.0.5u3, it's still there as com.sun.javacard.jcasm.mask.Main in lib/tools.jar. I found this class name, because maskgen.bat still existed in JCDK 2.1.x, so I was able to extract it from there.

Yet, there is a problem: Maskgen needs a config file. I am really confused by this, as the documentation states that some "mapping to native functions" is supposed to happen. Additionally, I am not able to find the mentioned cref_mask.cfg example file in any version of the JCDK or anywhere on the internet. Thus, the error Error: Entry PACKAGE_TABLE and/or PACKAGE_CONTEXT_TABLE missing from the configuration file. output by Maskgen persists for now.

I think Maskgen is supposed to create a .c file, according to this sketchy forum question (shoutout to Google Translate!). The .c file is then passed to a smart card chip C compiler to eventually generate the binary code of the smart card chip. I have absolutely no idea where to find such a C compiler... Someone in that forum eventually wrote that some source code related to that needs to be licensed from Sun for millions of oceans (Taiwan dollars) (wtf?), so I decided to try to find another solution for now.


Then, I stumbled over a Q&A with a Java Card engineer from Oracle. She even wrote a book about Java Card. Although, because that article is from 2000, she is taking about JCDK 2.1.1 in it, I could still extract some useful information from it.

For this method, you need the Java Card Workstation Development Environment (JCWDE). Apparently, it has been dropped from JCDK 3.x releases, but version 2.2.2 still has it. The following JAR files are needed to execute it (they're all part of the lib directory of that older version): apduio.jar, api.jar and jcwde.jar. I just copied these files into the lib directory of the newer 3.0.5u3 version.

To make invocation easier, I then wrote a small BASH script I put into jcdk-3.0.5u3/bin/jcwde.sh:

#!/bin/bash
JCDK_ROOT=$(dirname "$0")/..
L=$JCDK_ROOT/lib/v2.2.2
CLASS_DIR=$1
shift
java -Djc.home=$JCDK_ROOT -classpath $L/jcwde.jar:$L/apduio.jar:$L/api.jar:$CLASS_DIR com.sun.javacard.jcwde.Main "$@"

Note that in the following, the environment variable JC_HOME references my jcdk-3.0.5u3 directory.

Next, as a temporary solution, I created a file called jcwde.app in a directory which also had the build.gradle usable with this repository in it. That file contains the className and aid (with 0x prefixed to every byte), taken straight from an applet definition in build.gradle, concatenated with a space character. An example would be:

com.sun.jcclassic.samples.wallet.Wallet 0xA0:0x00:0x00:0x00:0x62:0x03:0x01:0x0C:0x06:0x01

Next, one needs to run the JCWDE with a command like the following. If it succeeds it outputs jcwde is listening for T=1 Apdu's on TCP/IP port 9.025. and keeps running. Note that the project needs to have been compiled before for this, i.e. .class files need to exist for your Java source files.

$JC_HOME/bin/jcwde.sh ./build/classes/java/main -p 9025 jcwde.app

We're almost done. There's just one more file to create: the list of APDU bytes to send. I just called it inputdata and put it into the same directory as build.gradle. For the Wallet demo app provided by Oracle, it could contain the following text (note the semicolons at the end of every row of bytes!):

output on;
powerup;

// create wallet applet
// 0x80 0xB8 0x00 0x00 0x14 0x0a 0xa0 0x0 0x0 0x0 0x62 0x3 0x1 0xc 0x6 0x1 0x08 0x0 0x0 0x05 0x01 0x02 0x03 0x04 0x05 0x7F;

// select Wallet
0x00 0xA4 0x04 0x00 0x0a 0xa0 0x0 0x0 0x0 0x62 0x3 0x1 0xc 0x6 0x1 0x7F;
// 90 00 = SW_NO_ERROR

// verify user pin
0x80 0x20 0x00 0x00 0x05 0x01 0x02 0x03 0x04 0x05 0x7F;
// 90 00 = SW_NO_ERROR

// get wallet balance
0x80 0x50 0x00 0x00 0x00 0x02;
// 0x00 0x00 0x00 0x00 0x90 0x00 = Balance = 0 and SW_ON_ERROR

powerdown;

Note that the initial powerup; is absolutely crucial. If you omit it, ApduTool complains because Card is not initialized. I commented out the create wallet applet message here, as everything seems to work without it equally well. The final powerdown; is optional, but at least for me, the JDWDE terminates with java.lang.NullPointerException after all APDU messages have been sent if I omit it; when I keep it in, it terminates gracefully.

So finally, the ApduTool command, which should be rather trivial; it just sends all data from inputfile and outputs the responses (apdutool.sh is just a quick wrapper I wrote around com.sun.javacard.apdutool.Main in lib/tools.jar).

$JC_HOME/bin/apdutool.sh -nobanner -noatr -p 9025 inputdata

Coming to an end, I hope you can forgive me for this wall of text. My main motivation of writing this down was that I couldn't find any real condensed information on this, as everyone developing with the JCDK seems to use the Eclipse plugin provided by Oracle.

As you can see, this entire process really isn't elegant, especially because of the JAR files needed from an older JCDK release. That's why I'd really appreciate if these instructions would be added to the repository. One could be able to specify whether to use GlobalPlatform or this type of simulator in the applet section of build.gradle. Additionally, the output of ApduTool could be parsed, so script sections in build.gradle could also contain the expected output, which would greatly ease unit test-based development.

Sadly, my knowledge in Gradle development is practically non-existent, so I won't be able to make any meaningful source code contributions to this repository. Thus, I am looking forward to its further development!

bertrandmartel commented 6 years ago

nice digging ! why not using jcardsim like in javacard-tutorial repo. This javacard plugin makes it easy to use it in a test sourceSet like this.

My personal advice if you come from Eclipse & are new to gradle :

You can build a new project from one of these module to start. In the test folder keep TestSuite & JavaCardTest to begin and add your own tests class extending JavaCardTest. Update the TestSuite with your own classes :

@RunWith(Suite.class)
@SuiteClasses({PasswordEntryTest.class, PasswordManagerTest.class})  <=== change this
public class TestSuite

Then you can start to develop while testing your applet(s) sending your own APDU and checking for the result etc...

Other project examples you can check :

I developped all projects above without physical card with only the tests using Jcardsim and tested on physical cards afterwards

When you have access to physical smartcard, in IntelIJ check the gradle tasks :

gradle

This plugin includes default tasks like installJavacard, listJavacard which are wrapping global platform pro and you can create yours using the scripts fields (check https://github.com/bertrandmartel/javacard-gradle-plugin#usage)

Note that if you use some external Javacard library like org.globalplatform, you would need to mock all those calls for instance here