module CCID_Tests { /* TTCN-3 tests for USB CCID (Chip Card Interface Device) * * (C) 2018-2019 by Harald Welte * (C) 2026 by Andreas Eversberg */ import from General_Types all; import from Osmocom_Types all; import from Misc_Helpers all; import from USB_PortType all; import from USB_Component all; import from CCID_Types all; import from CCID_Templates all; import from CCID_Emulation all; import from SIMTRACE_Types all; import from SIMTRACE_Templates all; import from SIMTRACE_Emulation all; modulepar { USB_Device_Match mp_usb_dev_match := { vid_pid := { vid := '1d50'H, pid := '6141'H } }; USB_Device_Match mp_cardem_usb_dev_match := { vid_pid := { vid := '1d50'H, pid := '60e3'H } }; integer mp_use_slot_count := 7; integer mp_simtrace_slot := 7; boolean mp_test_power_off := true; boolean mp_test_set_params := false; boolean mp_quirk_resetpar_returns_slotsts := false; } /* global test component; manages CCID device */ type component Test_CT { var CCID_Emulation_CT vc_CCID; port USB_PT USB; var CardemSlot_CT vc_SLOT[NR_SLOTS]; var ST_Emulation_CT vc_cardem_ref[NR_SLOTS]; private var boolean Cardem_In_Use := false; }; type component CardemSlot_CT extends Slot_CT { var ST_Emulation_CT vc_Cardem := null; port ST_USER_PT ST; port ST_USER_PT ST_IRQ; }; /* maximum number of slots we are supporting in the test suite */ private const integer NR_SLOTS := 16; /*********************************************************************** * helper infrastructure ***********************************************************************/ const octetstring c_UICC_SELECT_MF := '00a40004023f00'O; const octetstring c_SIM_SELECT_MF := 'a0a40004023f00'O; const octetstring c_UICC_MANAGE_CHANNEL := '0070800100'O; const octetstring c_SIM_SELECT_EF_ICCID := '00A4000C022FE2'O; const octetstring c_SIM_READ_BINARY := '00B000000A'O; const octetstring c_UICC_GET_RESPONSE_BASE := 'a0c00000'O; const octetstring c_UICC_TRUNCATED := '00a40004'O; /* Table 7 of ISO7816-3 */ type enumerated ISO7816_Fi { ISO7816_FI_372_4MHz ('0000'B), ISO7816_FI_372_5MHz ('0001'B), ISO7816_FI_558_6MHz ('0010'B), ISO7816_FI_744_8MHz ('0011'B), ISO7816_FI_1116_12MHz ('0100'B), ISO7816_FI_1488_16MHz ('0101'B), ISO7816_FI_1860_20MHz ('0110'B), ISO7816_FI_512_5MHz ('1001'B), ISO7816_FI_768_7MHz ('1010'B), ISO7816_FI_1024_10MHz ('1011'B), ISO7816_FI_1536_15MHz ('1100'B), ISO7816_FI_2048_20MHz ('1101'B) }; /* Table 8 of ISO7816-3 */ type enumerated ISO7816_Di { ISO7816_DI_1 ('0001'B), ISO7816_DI_2 ('0010'B), ISO7816_DI_4 ('0011'B), ISO7816_DI_8 ('0100'B), ISO7816_DI_16 ('0101'B), ISO7816_DI_32 ('0110'B), ISO7816_DI_64 ('0111'B), ISO7816_DI_12 ('1000'B), ISO7816_DI_20 ('1001'B) } private template (value) CCID_ProtocolData ts_ProtoDataT0(ISO7816_Fi fi, ISO7816_Di di, uint8_t guard_time := 0, uint8_t wait_int := 0) := { T0 := { Findex := enum2int(fi), Dindex := enum2int(di), bRFU := '000000'B, inv_convention := false, bRFU2 := '0'B, bGuardTimeT0 := guard_time, bWaitingIntegerT0 := wait_int, bClockStop := STOPPING_NOT_ALLOWED } }; type function void_fn() runs on CardemSlot_CT; /* first function inside CardemSlot_CT; wait for CCID_EVENT_UP + call testcase-specific function */ private function f_handler_init(void_fn fn, integer slot_nr, ST_Emulation_CT v_cardem_ref) runs on CardemSlot_CT { g_slot_nr := slot_nr; vc_Cardem := v_cardem_ref; g_Tguard.start; activate(as_Tguard()); CCID.receive(CCID_Emulation_Event:{up_down:=CCID_EVENT_UP}); fn.apply(); } /* start a single slot handler */ private function f_start_handler(void_fn fn, integer slot_nr, boolean use_cardem := false) runs on Test_CT { var ST_Emulation_CT vc_Cardem := null; vc_SLOT[slot_nr] := CardemSlot_CT.create("Slot" & int2str(slot_nr)); connect(vc_SLOT[slot_nr]:CCID, vc_CCID:SLOT[slot_nr]); if (use_cardem) { if (Cardem_In_Use) { setverdict(fail, "Only one CCID slot handler can use Cardem."); mtc.stop; } vc_Cardem := ST_Emulation_CT.create("Cardem"); map(vc_Cardem:USB, system:USB); connect(vc_Cardem:INOUT, vc_SLOT[slot_nr]:ST); connect(vc_Cardem:IRQ, vc_SLOT[slot_nr]:ST_IRQ); Cardem_In_Use := true; } vc_cardem_ref[slot_nr] := vc_Cardem; vc_SLOT[slot_nr].start(f_handler_init(fn, slot_nr, vc_Cardem)); } /* start a single Cardem slot handler */ private function f_wait_handlers_complete() runs on Test_CT { var integer i; for (i := 0; i < NR_SLOTS; i := i+1) { if (vc_SLOT[i] != null) { vc_SLOT[i].done; } } setverdict(pass); } private function f_start_and_wait() runs on Test_CT { var integer i; /* start CCID_Emulation and Cardem_Emulation last, it will trigger all the per-slot components */ var CCID_Emulation_Params ccidp := { usb_dev_match := mp_usb_dev_match }; var USB_IF_Params cardemp := { usb_dev_match := mp_cardem_usb_dev_match, usb_if_nr := 0 }; vc_CCID.start(CCID_Emulation.main(ccidp)); for (i := 0; i < NR_SLOTS; i := i+1) { if (vc_SLOT[i] != null) { if (vc_cardem_ref[i] != null) { vc_cardem_ref[i].start(SIMTRACE_Emulation.main(cardemp)); } } } f_wait_handlers_complete(); } private function f_init() runs on Test_CT { var integer i; vc_CCID := CCID_Emulation_CT.create("CCID"); map(vc_CCID:USB, system:USB); for (i := 0; i < NR_SLOTS; i := i+1) { vc_SLOT[i] := null; } } altstep as_cardem_any() runs on CardemSlot_CT { var SIMTRACE_PDU pdu; [] ST.receive(SIMTRACE_PDU:?) -> value pdu { setverdict(fail, "Received unexpected CCID ", pdu); self.stop; } [] ST.receive { setverdict(fail, "Received unexpected non-CCID"); self.stop; } } /* transceive a Cardem command (expect 'rx' on IN) */ function f_cardem_receive(template (present) SIMTRACE_PDU exp_rx) runs on CardemSlot_CT return SIMTRACE_PDU { var SIMTRACE_PDU pdu; alt { [] ST.receive(exp_rx) -> value pdu { return pdu; } [] as_cardem_any(); } return pdu; } function f_cardem_transmit(template (value) SIMTRACE_PDU tx) runs on CardemSlot_CT { ST.send(tx); } altstep as_cardem_manager() runs on CardemSlot_CT { var SIMTRACE_PDU pdu; [] ST.receive(tr_SIMTRACE_CEMU_CONFIG(tr_FeatureFlags(true))) { log("Got Config."); repeat; } [] ST_IRQ.receive(tr_SIMTRACE_MODEM_STATUS()) { log("Got Status."); repeat; } [] ST.receive(tr_SIMTRACE_CEMU_PTS(?, ?)) { log("Got PTS info."); repeat; } } function f_cardem_manager() runs on CardemSlot_CT { if (vc_Cardem == null) { setverdict(fail, "Please enable Cardem support first."); mtc.stop; } activate(as_cardem_manager()); f_cardem_transmit(ts_SIMTRACE_CEMU_CONFIG(ts_FeatureFlags(true))); f_cardem_transmit(ts_SIMTRACE_MODEM_SIM_SELECT(SIM_SELECT_REMOTE)); f_cardem_transmit(ts_SIMTRACE_CEMU_SET_ATR('3b00'O)); f_cardem_transmit(ts_SIMTRACE_CEMU_CARDINSERT(1)); f_cardem_transmit(ts_SIMTRACE_MODEM_RESET(MODEM_RESET_PULSE, 300)); f_cardem_transmit(ts_SIMTRACE_CEMU_SET_ATR('3b9f96801f878031e073fe211b674a4c753034054ba9'O)); f_sleep(0.2); } private function f_ccid_receive(template (present) CCID_PDU pdu_exp) runs on Slot_CT return CCID_PDU { var CCID_PDU pdu_rx; alt { [] CCID.receive(pdu_exp) -> value pdu_rx { } [] as_ccid_any(); } return pdu_rx; } /*********************************************************************** * Test behavior regarding valid situations ***********************************************************************/ /* request 100 times the slot status */ private function f_TC_getstatus() runs on Slot_CT { var integer i; for (i := 0; i < 100; i := i+1) { CCID.send(ts_CCID_GetSlotStatus(g_slot_nr)); /* it would be fun to simply send more requests here, but the CCID * spec doesn't permit more than one unresponded command [per slot] */ alt { [] CCID.receive(tr_CCID_SlotStatus(g_slot_nr)); [] as_ccid_any(); } } setverdict(pass); } testcase TC_get_status() runs on Test_CT { var integer i; f_init(); for (i := 0; i < mp_use_slot_count; i := i+1) { f_start_handler(refers(f_TC_getstatus), i); } f_start_and_wait(); } private function f_TC_power_on() runs on Slot_CT { f_ccid_power_on(); } testcase TC_power_on() runs on Test_CT { var integer i; f_init(); for (i := 0; i < mp_use_slot_count; i := i+1) { f_start_handler(refers(f_TC_power_on), i); } f_start_and_wait(); } private function f_TC_power_off() runs on Slot_CT { f_ccid_power_on(); f_ccid_power_off(); } testcase TC_power_off() runs on Test_CT { var integer i; f_init(); for (i := 0; i < mp_use_slot_count; i := i+1) { f_start_handler(refers(f_TC_power_off), i); } f_start_and_wait(); } /* repeat IccPowerOn on slot that's already active (next warm reset ATR) */ private function f_TC_power_on_warm() runs on Slot_CT { var integer i; /* initial power on */ f_ccid_power_on(); /* additional power on */ for (i := 0; i < 20; i := i+1) { f_ccid_power_on(); } } testcase TC_power_on_warm() runs on Test_CT { var integer i; f_init(); for (i := 0; i < mp_use_slot_count; i := i+1) { f_start_handler(refers(f_TC_power_on_warm), i); } f_start_and_wait(); } /* transfer 1000 APDUs by issuing SELECT MF */ private function f_TC_select_mf() runs on Slot_CT { var integer i; f_ccid_power_on(); f_ccid_set_par(ts_ProtoDataT0(ISO7816_FI_512_5MHz, ISO7816_DI_32)); for (i := 0; i < 1000; i := i+1) { f_ccid_xfr(c_UICC_SELECT_MF, '??'O); } } testcase TC_select_mf() runs on Test_CT { var integer i; f_init(); for (i := 0; i < mp_use_slot_count; i := i+1) { f_start_handler(refers(f_TC_select_mf), i); } f_start_and_wait(); } /* GetParametrs: verify contents */ private function f_TC_get_params() runs on Slot_CT { var CCID_PDU par; f_ccid_power_on(); par := f_ccid_get_par(); log(par); } testcase TC_get_params() runs on Test_CT { var integer i; f_init(); for (i := 0; i < mp_use_slot_count; i := i+1) { f_start_handler(refers(f_TC_get_params), i); } f_start_and_wait(); } /* SetParameters: verify change */ private function f_TC_set_params() runs on Slot_CT { var CCID_PDU par; f_ccid_power_on(); /* get current parameters */ par := f_ccid_get_par(); /* modify some of them */ var CCID_ProtocolData pd := par.u.Parameters.abProtocolData; pd.T0.bGuardTimeT0 := 23; pd.T0.bWaitingIntegerT0 := 42; par := f_ccid_set_par(pd); /* check if modifications were applied */ var template (present) CCID_ProtocolData tr_PD := { T0 := { Findex := ?, Dindex := ?, bRFU := ?, inv_convention := ?, bRFU2 := ?, bGuardTimeT0 := 23, bWaitingIntegerT0 := 42, bClockStop := ? } }; if (match(par.u.Parameters.abProtocolData, tr_PD)) { setverdict(pass); } else { setverdict(fail, "SetParameters didn't change GuardTime/WaitingInteger"); } } testcase TC_set_params() runs on Test_CT { var integer i; f_init(); for (i := 0; i < mp_use_slot_count; i := i+1) { f_start_handler(refers(f_TC_set_params), i); } f_start_and_wait(); } /* ResetParameters: verify change */ private function f_TC_reset_params() runs on Slot_CT { var CCID_PDU par; f_TC_set_params(); par := f_ccid_reset_par(); if (mp_quirk_resetpar_returns_slotsts) { par := f_ccid_get_par(); } if (par.u.Parameters.abProtocolData.T0.bGuardTimeT0 == 23 or par.u.Parameters.abProtocolData.T0.bWaitingIntegerT0 == 42) { setverdict(fail, "ResetParameters didn't reset properly"); } } testcase TC_reset_params() runs on Test_CT { var integer i; f_init(); for (i := 0; i < mp_use_slot_count; i := i+1) { f_start_handler(refers(f_TC_reset_params), i); } f_start_and_wait(); } /* SetParameters: verify acceptance of unchanged setting */ private function f_TC_set_params_nochange() runs on Slot_CT { var CCID_PDU set_par, get_par; f_ccid_power_on(); /* get current parameters */ set_par := f_ccid_get_par(); /* send unmodified */ get_par := f_ccid_set_par(set_par.u.Parameters.abProtocolData); /* check if they still match */ if (match(set_par.u.Parameters.abProtocolData, get_par.u.Parameters.abProtocolData)) { setverdict(pass); } else { setverdict(fail, "SetParameters changed parameters unexpected"); } } testcase TC_set_params_nochange() runs on Test_CT { var integer i; f_init(); for (i := 0; i < mp_use_slot_count; i := i+1) { f_start_handler(refers(f_TC_set_params_nochange), i); } f_start_and_wait(); } /* manage channel (Case 1) */ private function f_TC_successful_case_1() runs on Slot_CT { var octetstring res; f_ccid_power_on(); res := f_ccid_xfr(c_UICC_MANAGE_CHANNEL, '??'O); if (res != '6200'O) { setverdict(fail, "Unexpected SW1/SW2"); mtc.stop; } } testcase TC_successful_case_1() runs on Test_CT { var integer i; f_init(); for (i := 0; i < mp_use_slot_count; i := i+1) { f_start_handler(refers(f_TC_successful_case_1), i); } f_start_and_wait(); } /* read binary (Case 3 + 2) */ private function f_TC_successful_case_2_3() runs on Slot_CT { var octetstring res; f_ccid_power_on(); res := f_ccid_xfr(c_SIM_SELECT_EF_ICCID, '??'O); if (res != '9000'O) { setverdict(fail, "Unexpected SW1/SW2"); mtc.stop; } res := f_ccid_xfr(c_SIM_READ_BINARY, ?); if (substr(res, 10,2) != '9000'O) { setverdict(fail, "Unexpected SW1/SW2"); mtc.stop; } } testcase TC_successful_case_2_3() runs on Test_CT { var integer i; f_init(); for (i := 0; i < mp_use_slot_count; i := i+1) { f_start_handler(refers(f_TC_successful_case_2_3), i); } f_start_and_wait(); } /* GET RESPONSE (Case 4) */ private function f_TC_successful_case_4() runs on Slot_CT { var octetstring res; var integer len; f_ccid_power_on(); res := f_ccid_xfr(c_UICC_SELECT_MF, '??'O); if (res[0] != '61'O) { setverdict(fail, "Unexpected SW1"); mtc.stop; } len := oct2int(res[1]); res := f_ccid_xfr(c_UICC_GET_RESPONSE_BASE & int2oct(len, 1), ?) if (lengthof(res) != len + 2) { setverdict(fail, "Expected ", len + 2, " octets, but got ", res); mtc.stop; } } testcase TC_successful_case_4() runs on Test_CT { var integer i; f_init(); for (i := 0; i < mp_use_slot_count; i := i+1) { f_start_handler(refers(f_TC_successful_case_4), i); } f_start_and_wait(); } /* A general test to verify a working simtrace. */ private function f_TC_simtrace_test() runs on CardemSlot_CT { var octetstring req := c_UICC_MANAGE_CHANNEL; var octetstring res := '6200'O; var CCID_PDU ccid_pdu; f_cardem_manager(); f_ccid_power_on(CCID_PWRSEL_3V0); /* Send a request towards reader. */ CCID.send(ts_CCID_XfrBlock(g_slot_nr, req, 0)); /* Receive the request by SIM. */ f_cardem_receive(tr_SIMTRACE_CEMU_RX_DATA(?, req)); /* Transmit the response by SIM. */ f_cardem_transmit(ts_SIMTRACE_CEMU_TX_DATA(ts_CardEmu_DataFlags(pb_and_tx := true, final := true), res)); /* Receive the response from reader. */ ccid_pdu := f_ccid_receive(tr_CCID_DataBlock(g_slot_nr, ?, ?, ?)); if (ccid_pdu.u.DataBlock.abData != res) { setverdict(fail, "Unexpected SW1/SW2"); mtc.stop; } /* Stop simtrace emulation, to prevent race condition. */ vc_Cardem.stop; vc_Cardem.done; } testcase TC_simtrace_test() runs on Test_CT { f_init(); f_start_handler(refers(f_TC_simtrace_test), mp_simtrace_slot, true); f_start_and_wait(); } /* TODO */ /* IccPowerOn: verify that CCID resets all parameters to default values */ /* IccPowerOn: verify that bPowerSelect has no effect in active state */ /* XfrBlock: length corner cases (Lc/Le max, ...) */ /* IccClock: verify clock has stopped/restarted */ /* Abort for command that already terminated */ /* Abort for command that's still processing */ /*********************************************************************** * Test behavior regarding invalid situations ***********************************************************************/ /* message for invalid slot number (more than we have) */ private function f_TC_inval_slot() runs on Slot_CT { CCID.send(ts_CCID_GetSlotStatus(g_slot_nr)); alt { [] CCID.receive(tr_CCID_SlotStatus(hdr_in := tr_CCID_HeaderIN_FAIL(CCID_ERR_SLOT_NOT_EXIST))) { setverdict(pass); } [] CCID.receive(tr_CCID_SlotStatus) { setverdict(fail, "Unexpected SlotStatus"); mtc.stop; } [] as_ccid_any(); } } testcase TC_inval_slot() runs on Test_CT { f_init(); f_start_handler(refers(f_TC_inval_slot), 15); f_start_and_wait(); } /* switch card off and then XfrBlock. Requires reader with IccPowerOff support */ private function f_TC_xfer_off() runs on Slot_CT { f_ccid_power_off(); CCID.send(ts_CCID_XfrBlock(g_slot_nr, c_SIM_SELECT_MF, 0)); alt { [] CCID.receive(tr_CCID_DataBlock(slot:=g_slot_nr, hdr_in:=tr_CCID_HeaderIN_FAIL)) { setverdict(pass); } [] CCID.receive(tr_CCID_DataBlock(slot:=g_slot_nr, hdr_in:=tr_CCID_HeaderIN_OK)) { setverdict(fail, "Expected XfrBlock to fail"); mtc.stop; } [] as_ccid_any(); } } testcase TC_xfer_off() runs on Test_CT { f_init(); f_start_handler(refers(f_TC_xfer_off), 0); f_start_and_wait(); } /* unsupported Mechanical */ private function f_TC_unsupp_mechanical() runs on Slot_CT { CCID.send(ts_CCID_Mechanical(g_slot_nr, CCID_MECH_FN_EJECT_CARD)); alt { [] CCID.receive(tr_CCID_SlotStatus(hdr_in := tr_CCID_HeaderIN_FAIL(CCID_ERR_CMD_NOT_SUPPORTED))) { setverdict(pass); } [] as_ccid_any(); } } testcase TC_unsupp_mechanical() runs on Test_CT { f_init(); f_start_handler(refers(f_TC_unsupp_mechanical), 0); f_start_and_wait(); } /* unsupported Secure */ private function f_TC_unsupp_secure() runs on Slot_CT { CCID.send(ts_CCID_Secure(g_slot_nr, 0, 0, ''O)); alt { [] CCID.receive(tr_CCID_DataBlock(hdr_in := tr_CCID_HeaderIN_FAIL(CCID_ERR_CMD_NOT_SUPPORTED))) { setverdict(pass); } [] CCID.receive(tr_CCID_SlotStatus(hdr_in := tr_CCID_HeaderIN_FAIL(CCID_ERR_CMD_NOT_SUPPORTED))) { setverdict(pass); } [] as_ccid_any(); } } testcase TC_unsupp_secure() runs on Test_CT { f_init(); f_start_handler(refers(f_TC_unsupp_secure), 0); f_start_and_wait(); } /* truncated transfer */ private function f_TC_truncated() runs on Slot_CT { var integer i; var octetstring req; var CCID_PDU pdu; const record of octetstring c_status := { '6700'O, '6700'O, '6700'O, '6d00'O, '6881'O }; f_ccid_power_on(); for (i := 4; i >= 0; i := i-1) { log("Testing trunkted transfer block with ", i, " octet(s)."); req := substr(c_UICC_TRUNCATED, 0, i); pdu := f_ccid_xfr_pdu(req, ?); if (pdu.hdr_in.bStatus.bmCommandStatus == CCID_CMD_STATUS_FAILED) { log("Command failed as expected."); continue; } if (pdu.u.DataBlock.abData == c_status[4 - i]) { log("Got expected status result. (status ", c_status[4 - i], ")"); continue; } setverdict(fail, "Unexpected SW1/SW2. (expected ", c_status[4 - i], ", got ", pdu.u.DataBlock.abData, ")"); mtc.stop; } } testcase TC_truncated() runs on Test_CT { var integer i; f_init(); for (i := 0; i < mp_use_slot_count; i := i+1) { f_start_handler(refers(f_TC_truncated), i); } f_start_and_wait(); } /* The SIM responds with truncated response. */ private function f_TC_truncated_response() runs on CardemSlot_CT { var octetstring req := c_SIM_READ_BINARY; var octetstring res := '0102030405060708090a9000'O; var CCID_PDU ccid_pdu; f_cardem_manager(); f_ccid_power_on(CCID_PWRSEL_3V0); /* Send a request towards reader. */ CCID.send(ts_CCID_XfrBlock(g_slot_nr, req, 0)); /* Receive the request by SIM. */ f_cardem_receive(tr_SIMTRACE_CEMU_RX_DATA(?, req)); /* Transmit the response by SIM. */ f_cardem_transmit(ts_SIMTRACE_CEMU_TX_DATA(ts_CardEmu_DataFlags(pb_and_tx := true, final := true), req[1] & substr(res, 0, 5))); /* Receive the response from reader. */ ccid_pdu := f_ccid_receive(tr_CCID_DataBlock(g_slot_nr, ?, ?, ?)); if (ccid_pdu.hdr_in.bError != CCID_ERR_ICC_MUTE) { setverdict(fail, "Unexpected Response or error code"); mtc.stop; } /* Stop simtrace emulation, to prevent race condition. */ vc_Cardem.stop; vc_Cardem.done; } testcase TC_truncated_response() runs on Test_CT { f_init(); f_start_handler(refers(f_TC_truncated_response), mp_simtrace_slot, true); f_start_and_wait(); } /* The SIM sends a wrong procedure byte */ private function f_TC_wrong_procedure_byte() runs on CardemSlot_CT { var octetstring req := c_SIM_READ_BINARY; var octetstring res := '0102030405060708090a9000'O; var CCID_PDU ccid_pdu; f_cardem_manager(); f_ccid_power_on(CCID_PWRSEL_3V0); /* Send a request towards reader. */ CCID.send(ts_CCID_XfrBlock(g_slot_nr, req, 0)); /* Receive the request by SIM. */ f_cardem_receive(tr_SIMTRACE_CEMU_RX_DATA(?, req)); /* Transmit the response by SIM. */ f_cardem_transmit(ts_SIMTRACE_CEMU_TX_DATA(ts_CardEmu_DataFlags(pb_and_tx := true, final := true), '42'O & res)); /* Receive the response from reader. */ ccid_pdu := f_ccid_receive(tr_CCID_DataBlock(g_slot_nr, ?, ?, ?)); if (ccid_pdu.hdr_in.bError != CCID_ERR_PROCEDURE_BYTE_CONFLICT) { setverdict(fail, "Unexpected Response or error code"); mtc.stop; } /* Stop simtrace emulation, to prevent race condition. */ vc_Cardem.stop; vc_Cardem.done; } testcase TC_wrong_procedure_byte() runs on Test_CT { f_init(); f_start_handler(refers(f_TC_wrong_procedure_byte), mp_simtrace_slot, true); f_start_and_wait(); } /* Send a procedure byte where only a status is expected. */ private function f_TC_unexpected_procedure_byte() runs on CardemSlot_CT { var octetstring req := c_UICC_MANAGE_CHANNEL; var octetstring res := '62'O; var CCID_PDU ccid_pdu; f_cardem_manager(); f_ccid_power_on(CCID_PWRSEL_3V0); /* Send a request towards reader. */ CCID.send(ts_CCID_XfrBlock(g_slot_nr, req, 0)); /* Receive the request by SIM. */ f_cardem_receive(tr_SIMTRACE_CEMU_RX_DATA(?, req)); /* Transmit the response by SIM. */ f_cardem_transmit(ts_SIMTRACE_CEMU_TX_DATA(ts_CardEmu_DataFlags(pb_and_tx := true, final := true), req[1] & res)); /* Receive the response from reader. */ ccid_pdu := f_ccid_receive(tr_CCID_DataBlock(g_slot_nr, ?, ?, ?)); if (ccid_pdu.u.DataBlock.abData != req[1] & res[0]) { setverdict(fail, "Unexpected SW1/SW2"); mtc.stop; } /* Stop simtrace emulation, to prevent race condition. */ vc_Cardem.stop; vc_Cardem.done; } testcase TC_unexpected_procedure_byte() runs on Test_CT { f_init(); f_start_handler(refers(f_TC_unexpected_procedure_byte), mp_simtrace_slot, true); f_start_and_wait(); } /* The SIM does not send the expected procedure byte. */ private function f_TC_procedure_byte_timeout() runs on CardemSlot_CT { var octetstring req := c_SIM_READ_BINARY; var CCID_PDU ccid_pdu; f_cardem_manager(); f_ccid_power_on(CCID_PWRSEL_3V0); /* Send a request towards reader. */ CCID.send(ts_CCID_XfrBlock(g_slot_nr, req, 0)); /* Receive the request by SIM. */ f_cardem_receive(tr_SIMTRACE_CEMU_RX_DATA(?, req)); /* Transmit procedure byte from SIM. */ f_cardem_transmit(ts_SIMTRACE_CEMU_TX_DATA(ts_CardEmu_DataFlags(pb_and_tx := true, final := true), '60'O)); /* Eat the possible 'Time Extension status' */ timer T := 0.2; T.start; alt { [] CCID.receive(tr_CCID_DataBlock(g_slot_nr, ?, tr_CCID_HeaderIN(?, CCID_CMD_STATUS_TIME_EXT, ?), ?)) { log("Eating 'Time Extension status'."); repeat; } [] as_ccid_any(); [] T.timeout { } } /* Receive the response from reader. */ ccid_pdu := f_ccid_receive(tr_CCID_DataBlock(g_slot_nr, ?, ?, ?)); if (ccid_pdu.hdr_in.bError != CCID_ERR_ICC_MUTE) { setverdict(fail, "Unexpected Response or error code"); mtc.stop; } /* Stop simtrace emulation, to prevent race condition. */ vc_Cardem.stop; vc_Cardem.done; } testcase TC_procedure_byte_timeout() runs on Test_CT { f_init(); f_start_handler(refers(f_TC_procedure_byte_timeout), mp_simtrace_slot, true); f_start_and_wait(); } /* The SIM does not send the expected status words. */ private function f_TC_status_word_timeout() runs on CardemSlot_CT { var octetstring req := c_UICC_MANAGE_CHANNEL; var CCID_PDU ccid_pdu; f_cardem_manager(); f_ccid_power_on(CCID_PWRSEL_3V0); /* Send a request towards reader. */ CCID.send(ts_CCID_XfrBlock(g_slot_nr, req, 0)); /* Receive the request by SIM. */ f_cardem_receive(tr_SIMTRACE_CEMU_RX_DATA(?, req)); /* Receive the response from reader. */ ccid_pdu := f_ccid_receive(tr_CCID_DataBlock(g_slot_nr, ?, ?, ?)); if (ccid_pdu.hdr_in.bError != CCID_ERR_ICC_MUTE) { setverdict(fail, "Unexpected Response or error code"); mtc.stop; } /* Stop simtrace emulation, to prevent race condition. */ vc_Cardem.stop; vc_Cardem.done; } testcase TC_status_word_timeout() runs on Test_CT { f_init(); f_start_handler(refers(f_TC_status_word_timeout), mp_simtrace_slot, true); f_start_and_wait(); } /* TODO */ /* IccPowerOn with wrong voltage (> 0x04) */ /* XfrBlock on empty slot */ /* GetParameters on empty slot */ /* SetParameters for bProtocolNum > 0x01 */ /* SetParameters: invalid parameters */ /* set unsupported frequency */ /* set unsupported clock rate */ /* XfrBlock: bWI in T=0? */ /* XfrBlock: wLevelParameter not matching level? */ /* Abort for command that was not even submitted yet*/ /* dwMaxCCIDMessageLength */ control { /* valid transactions */ execute( TC_get_status() ); execute( TC_power_on() ); execute( TC_power_on_warm() ); if (mp_test_power_off) { execute( TC_power_off() ); } execute( TC_select_mf() ); execute( TC_get_params() ); if (mp_test_set_params) { execute( TC_set_params() ); execute( TC_reset_params() ); } execute( TC_set_params_nochange() ); execute( TC_successful_case_4() ); execute( TC_successful_case_1() ); execute( TC_successful_case_2_3() ); execute( TC_simtrace_test() ); /* error handling */ execute( TC_inval_slot() ); if (mp_test_power_off) { execute( TC_xfer_off() ); } execute( TC_unsupp_mechanical() ); execute( TC_unsupp_secure() ); execute( TC_truncated() ); execute( TC_truncated_response() ); execute( TC_wrong_procedure_byte() ); execute( TC_unexpected_procedure_byte() ); execute( TC_procedure_byte_timeout() ); execute( TC_status_word_timeout() ); } }