emv-card-simulator

Introduction: JavaCard implementation of an EMV card for payment terminal testing
More: Author   ReportBugs   
Tags:

Build and test

JavaCard implementation of an EMV card for payment terminal functional and security testing / fuzzing.

If you need a payment terminal simulator for testing, try emvpt project.

Building

Cloning project

git clone --recurse-submodules https://github.com/mrautio/emv-card-simulator.git

Docker build

If you don't want to install Java8/Gradle(>6), you may use Docker:

docker build -t emvcard-builder -f Dockerfile .

Gradle build

If you have all developer tools existing, or enter to nix-shell, then you can just use Gradle:

gradle build

Private test cards

Card setup APDU files in a git ignored private/cards directory are tested against emvpt with contact and contactless transactions, emvpt settings are in private/config/settings.yaml.

gradle testPrivateCards

Library

lib packages the simulator applets with a host API (emvcardsimulator.api) for embedding the simulator to other applications.

gradle -p lib build
EmvCard card = new EmvCard(EmvCard.PROTOCOL_CONTACTLESS);
card.personalize(ApduScript.bundled("card_setup_ppse_apdus.yaml"));
card.personalize(ApduScript.bundled("card_setup_app_apdus.yaml"));
byte[] response = card.transmit(commandApdu);

A script may describe the card in a card_information entry, it is not sent to the card and the other tools skip it:

- card_information:
    title: 'Test card'
- req: '00 A4 04 00 07 AF FF FF FF FF 12 34'
  res: '90 00'

Applets are installed when a script selects them. The APDU scripts of src/main/rust/config are bundled, and the ones of src/test/java/config with a test/ prefix, e.g. test/card_setup_app_visa_contactless_apdus.yaml.

Fault injection

Each applet has a table of eight faults for terminal fuzzing and fault testing, see src/main/rust/config/setup_fuzzing_apdus.yaml.

SET FAULT    80 11 <entry 00-07> 00 Lc <kind> <INS> <interface> <trigger mode> <n> <kind specific data>   (Lc 00 clears the entry)
RESET        80 07 00 00 00              clear the faults and the fallback READ RECORD of the applet
SEED         80 07 00 01 02 <seed>       card wide seed of the fault random numbers, 0000 uses the card's random generator

A fault applies to a command when its trigger fires. Triggers are evaluated once per command, setup commands and GET RESPONSE are not counted, GET RESPONSE keeps the faults of the command whose response it returns.

Field Values
INS INS of the matching commands, 00 any command
Interface 00 both, 01 contact, 02 contactless
Trigger mode 00 always, 01 randomly 1 in n, 02 once on the nth matching command, 03 every nth matching command
Kind Kind specific data
01 tag value tag (1-3 bytes) || offset || length || strategy || argument. Strategies: 00 random, 01 XOR argument (00 flips one random bit), 02 fill with argument, 03 boundary values 00 01 7F 80 FE FF in turn, 04 increment by the number of firings. The value is extended to offset + length bytes (at most 255) and the tag length is encoded for the mutated value.
02 tag encoding tag (1-3 bytes) || mode || argument 1 || argument 2. Modes: 01 length + signed argument 1, 02 long form length of argument 1 (1-4) bytes, 03 indefinite length 80 (argument 1 not 00 appends 00 00), 04 only the first tag byte announcing a subsequent byte, 05 constructed bit set, 06 argument 1 padding bytes of argument 2 before the tag, 07 omitted, 08 duplicated
03 status word SW1 SW2 || 00 command is not processed and has no response data, 01 command is processed and its response data is sent
04 response chain mode || argument. Modes: 01 GET RESPONSE repeats the same part and 61xx forever, 02 61xx announces argument bytes, 03 6Cxx gives argument as the exact length, 04 last argument bytes of the response are not sent, 05 response is sent in parts of argument bytes with 61xx, also with T=1 and contactless
05 delay number of busy-wait units (2 bytes) before the command is processed, the duration of a unit depends on the card. A real card may send waiting time extensions while busy.

Random values come from the seeded generator when a seed is set, its sequence restarts when the seed is set, when the faults are cleared and at card reset. A tag's random value is the same in all of its serializations of a command, e.g. in the response and the CDA hash. For example, the second GENERATE AC after the fault is set returns 6985 without processing:

80 11 00 00 08 03 AE 00 02 02 69 85 00

Update dependencies

Run the GitHub Actions Workflow.

Deploying to a SmartCard

If you have a SmartCard reader and a Global Platform compliant SmartCard, then you can deploy the application to an actual SmartCard. Common installation issue is to use incorrect JavaCard SDK version, set correct with jc_version.

# Deploy payment selection app to a JavaCard 2 SmartCard 
gradle deployPse -Pjc_version=2.2.2
# Deploy contactless payment selection app (PPSE) to a JavaCard 2 SmartCard
gradle deployPpse -Pjc_version=2.2.2
# Deploy the payment app to a JavaCard 2 SmartCard 
gradle deployPaymentApp -Pjc_version=2.2.2
# Show what is installed on the card without changing it
gradle cardInfo
# Check that the selected gp.jar works on the JDK that runs it, no card needed
gradle deployCheck

The deployment uses the GlobalPlatformPro library through src/deploy/java, which makes the PC/SC connection itself instead of running the gp command line tool. The tool connects with protocol T=*.

The newest known gp.jar release is used by default and runs on JDK >= 17. When jc_version targets a JavaCard SDK that is used with Java 8 (2.2.1, 2.2.2, 3.0.1 or 3.0.4), the deployment falls back to gp.jar 20.07.04 which may be incompatible with Apple Silicon.

# JavaCard 3.0.5, deployed with the newest gp.jar on a JDK 17 or newer
gradle deployPaymentApp
# JavaCard 2.2.2, deployed with gp.jar 20.07.04 on Java 8
gradle deployPaymentApp -Pjc_version=2.2.2
# pick the release and the JDK that runs it yourself
gradle deployPaymentApp -Pgp_version=20.07.04 -Pdeploy_java_version=8
# any other release, with the SHA-512 of its gp.jar
gradle deployPaymentApp -Pgp_version=26.04.20 -Pgp_checksum=<sha512>

gradle deployCheck reports which release and JDK are used and whether they work together.

MacOS

Deploying from MacOS works with the tasks above, but not with the gp command line tool, because the CCID driver may accept a T=1 connection to a card whose ATR announces T=0 only.

Apps
About Me
GitHub: Trinea
Facebook: Dev Tools
AI Daily Digest