Smart Card Testing Framework
Testing smart card applications: unit testing applets, integration testing with simulators, and EMV conformance validation.
Smart Card Testing Framework
Rigorous testing of smart card applets and systems spans three distinct layers: functional APDUAPDUProtocolCommunication unit between card and reader.Click to view → testing, negative and fuzz testing, and formal compliance test suites required by certification schemes. A well-structured test framework automates all three layers and integrates with the development pipeline.
Use the APDU Builder to prototype commands before encoding them in automated test cases.
Testing Architecture
┌─────────────────────────────────────────────┐
│ Compliance Test Suites (EMVCo, GP, ICAO) │ ← Certification
├─────────────────────────────────────────────┤
│ Fuzz / Negative Testing │ ← Security
├─────────────────────────────────────────────┤
│ Functional APDU Test Suite │ ← Correctness
├─────────────────────────────────────────────┤
│ PC/SC Middleware / Simulator │ ← Infrastructure
└─────────────────────────────────────────────┘
Functional APDU Testing
The most portable approach is to define test vectors as structured data and execute
them via any PC/SC connection. A minimal Python framework using pyscard:
import pytest
from smartcard.System import readers
from smartcard.util import toBytes, toHexString
@pytest.fixture(scope="session")
def card_conn():
r = readers()[0]
conn = r.createConnection()
conn.connect()
yield conn
conn.disconnect()
class TestSelectApplet:
SELECT_AID = toBytes("00 A4 04 00 07 D2 76 00 00 85 01 01 00")
def test_select_returns_9000(self, card_conn):
data, sw1, sw2 = card_conn.transmit(self.SELECT_AID)
assert (sw1, sw2) == (0x90, 0x00), \
f"Expected 9000, got {sw1:02X}{sw2:02X}"
def test_fci_contains_aid(self, card_conn):
data, sw1, sw2 = card_conn.transmit(self.SELECT_AID)
assert 0xD2 in data # AID byte present in FCI
For more complex state machines, use a fixture that resets card state between tests via a card reset or session re-select.
Test Vector Tables
Organise APDU tests as parameter tables for systematic coverage:
| Command | CLA | INS | P1 | P2 | Lc | Data | Expected SW |
|---|---|---|---|---|---|---|---|
| SELECT (valid AIDAIDProtocolUnique identifier for card applications.Click to view →) | 00 | A4 | 04 | 00 | 07 | D2760000850101 | 9000 |
| SELECT (wrong AID) | 00 | A4 | 04 | 00 | 05 | FFFFFFFFFF | 6A82 |
| VERIFY PIN (correct) | 00 | 20 | 00 | 80 | 04 | 31323334 | 9000 |
| VERIFY PIN (wrong) | 00 | 20 | 00 | 80 | 04 | FFFFFFFF | 63C2 |
| GET DATA (empty) | 00 | CA | 00 | 6F | 00 | — | 9000 or 6A88 |
Encoding these in pytest.mark.parametrize allows running the full matrix with
a single command.
Negative and Boundary Testing
Security-critical commands must be tested for rejection of malformed input:
@pytest.mark.parametrize("lc,data,expected_sw", [
(0x01, [0xFF], 0x6700), # Wrong Lc for VERIFY
(0x10, [0xFF]*16, 0x6700), # Lc too long
(0x04, [0x31,0x32,0x33], 0x6700),# Lc/data length mismatch
])
def test_verify_rejects_malformed(card_conn, lc, data, expected_sw):
cmd = [0x00, 0x20, 0x00, 0x80, lc] + data
_, sw1, sw2 = card_conn.transmit(cmd)
assert (sw1 << 8 | sw2) == expected_sw
Fuzz Testing
For security evaluation, randomised APDU fuzzing discovers unexpected behaviour:
import random
def fuzz_apdu(card_conn, iterations=1000):
findings = []
for _ in range(iterations):
cla = random.choice([0x00, 0x80, 0x84, 0xC0])
ins = random.randint(0, 0xFF)
p1 = random.randint(0, 0xFF)
p2 = random.randint(0, 0xFF)
lc = random.randint(0, 0x10)
data = [random.randint(0, 0xFF) for _ in range(lc)]
cmd = [cla, ins, p1, p2, lc] + data
try:
_, sw1, sw2 = card_conn.transmit(cmd)
sw = (sw1 << 8 | sw2)
if sw == 0x9000: # Unexpected success
findings.append((cmd, sw))
elif sw not in (0x6700, 0x6A86, 0x6D00, 0x6E00, 0x6F00):
findings.append((cmd, sw)) # Unusual SW
except Exception as e:
findings.append((cmd, str(e)))
return findings
Document all findings; a 9000 response to a random INS byte may indicate an
undocumented command — a security risk worth investigating.
Compliance Test Suites
| Scheme | Suite | Tool |
|---|---|---|
| EMVEMVApplicationGlobal chip payment card standard.Click to view → | EMVCoEMVCoStandardBody managing EMV payment standards.Click to view → Level 1/2 Test Suite | Testhouse, UL Labs |
| GlobalPlatformGlobalPlatformSoftwareCard application management standard.Click to view → | GP Compliance Test Tool | GlobalPlatform member tools |
| ICAO ePassportePassportApplicationPassport with embedded contactless chip.Click to view → | ICAO Test Specifications (Doc 9303 Part 13) | Keesing RFI, Trustpoint |
| Common CriteriaCommon CriteriaSecurityInternational IT security evaluation standard.Click to view → | Sponsor's test cases in Security Target | Per-scheme tooling |
| FIPS 140FIPS 140ComplianceUS government cryptographic module security standard.Click to view → | CMVP ACVTS (algorithm validation) | NIST ACVTS |
EMVCo Level 1 compliance requires formal submission to an EMVCo-accredited test laboratory; Level 2 (kernel) compliance is tested by the card and terminal vendor using EMVCo's published test cases.
See the Smart Card Debugging Guide for APDU trace analysis, and the Dev Environment Setup Guide for reader and simulator configuration.
คำถามที่พบบ่อย
Our guides cover a range of experience levels. Getting Started guides introduce smart card fundamentals. Security guides address Common Criteria certification and key management. Programming guides target developers working with APDU commands, JavaCard applets, and GlobalPlatform card management.