lzaoral / JCProfilerNext

Profiler for JavaCard code and a rewrite of OpenCryptoProject/JCProfiler with easier usage and much more!
GNU General Public License v3.0
8 stars 3 forks source link

JCProfilerNext

link:https://github.com/lzaoral/JCProfilerNext/actions/workflows/ci.yml[image:https://github.com/lzaoral/JCProfilerNext/actions/workflows/ci.yml/badge.svg[Java CI with Gradle]]

JCProfilerNext is a complete rewrite of link:https://github.com/OpenCryptoProject/JCProfiler[OpenCryptoProject/JCProfiler] that provides a completely automated preprocessing, compilation, installation and profiling of JavaCard code on JavaCard smart cards or in the jCardSim simulator.

Prerequisites

.Compatibility Matrix [cols="^1,^2"] |=== | JDK | Supported JavaCard versions

| 8 | 2.2.1+

| 9-11 | 3.0.1+

| 12-19 | 3.1+ |===

Compilation

. Clone this repository. . Run ./gradlew build on Unix-like systems or gradlew.bat build on Windows. . Execute .. directly using Gradle: ./gradlew run --args='--help'. .. directly using built JAR: java -jar build/libs/JCProfilerNext-1.0-SNAPSHOT.jar --help. .. directly from distribution archives in build/distributions.

Execution modes

The tool can be executed in four different modes (select with --mode):

Time Example

Consider the `example` method in the following class.  The outer loop is clearly
not constant since its iteration count depends on the input value.  On the other
hand, the inner loop is constant.

[source,java]
----
package example;

import javacard.framework.*;

public class Example extends Applet {
    public static final byte INS_EXAMPLE = (byte) 0xEE;

    Example() { }

    public static void install(byte[] bArray, short bOffset, byte bLength) {
        new Example().register();
    }

    @Override
    public void process(APDU apdu) throws ISOException {
        if (selectingApplet())
            return;

        switch (apdu.getBuffer()[ISO7816.OFFSET_INS]) {
            case INS_EXAMPLE:
                example(apdu);
                break;
            default:
                ISOException.throwIt(ISO7816.SW_INS_NOT_SUPPORTED);
        }
    }

    private void example(APDU apdu) {
        short count = Util.getShort(apdu.getBuffer(), ISO7816.OFFSET_CDATA);
        for (short i = 0; i < count; i++) {
            short tmp = 0;
            for (short k = 0; k < 50; k++) {
                tmp++;
            }
        }
    }
}
----

Run as:
[source,console]
----
$ ./gradlew run --args="--work-dir example --jckit jc222_kit --executable example --ins 0xEE --data-regex 00[0-9A-F]{2} --repeat-count 100"
----
JCProfilerNext will process all sources in the `example` directory and will
also use this directory to store all intermediate results:

* `applet` - contains the built applet with corresponding CAP, JAR and JCA files
* `measurements.csv` - contains the profiled method signature, card ATR, elapsed time, input APDUs and corresponding raw measurements
* `measurements.html` - contains a visualisation of the measurements
* `sources_instr` - contains instrumented sources
* `sources_original` - contains original unchanged sources
* `sources_perf` - contains sources with inserted measurements as inline comments

If you open the corresponding link:https://lzaoral.github.io/JCProfilerNext/example-time.html[`measurements.html`]
produced by JCProfilerNext, you will see that some parts of the code are indeed
not constant, namely if you inspect the corresponding histograms of traps
after the `for` loops.

Memory Example

Consider the following source code:

[source,java]

package example;

import javacard.framework.*;

public class Example extends Applet {

private Example() {
    // Transient Deselect
    byte[] transientDeselect = JCSystem.makeTransientByteArray((short) 256, JCSystem.CLEAR_ON_DESELECT);

    // Transient Reset
    byte[] transientReset = JCSystem.makeTransientByteArray((short) 256, JCSystem.CLEAR_ON_RESET);

    // Persistent
    byte[] persistent = new byte[256];
}

public static void install(byte[] bArray, short bOffset, byte bLength) {
    new Example().register();
}

@Override
public void process(APDU apdu) {}

}

Run as: [source,console]

$ ./gradlew run --args="--work-dir example --jckit jc304_kit --mode memory"

JCProfilerNext will produce the same directory structure as in the time mode above. The --executable option is omitted because we want to measure memory usage in the entry point class constructor. Note that the allocation of transient deselect memory may also affect the amount of free transient memory and vice versa. See link:https://lzaoral.github.io/JCProfilerNext/example-memory.html[`measurements.html`] for visualisation of the measurements.

Stats Example


Consider the following source code:

[source,java]
----
import javacard.security.KeyPair;

class Example {
    public Example() {
        KeyPair kp = new KeyPair(KeyPair.ALG_RSA, (short) 2048);
        kp.genKeyPair();
    }
}
----

Run as:
[source,console]
----
$ ./gradlew run --args="--work-dir example --jckit jc222_kit --mode stats"
----
JCProfilerNext will process all sources in the `example` directory and produce
the `APIstatistics.csv` file. Note that if not all imports can be resolved,
the results may not be precise, and the tool will issue an appropriate warning.

[source,csv]
----
# package/outer type,type,member,frequency
javacard.security,KeyPair,,2
javacard.security,KeyPair,ALG_RSA,1
javacard.security,KeyPair,genKeyPair(),1
javacard.security,KeyPair,"KeyPair(byte,short)",1
----

Limitations
-----------

* Cards that require special communication procedures (e.g. `SecureChannel`) are not supported.
* It is not possible to use a different JDK to compile the JavaCard applets and run this project.
* Connection to wireless card terminals may occasionally fail.