blob: e650f3758f84ff62a4bdab7a8e1196a2e08b3806 [file] [log] [blame]
Harald Weltef30e22b2019-11-14 08:36:19 +01001module CCID_Tests {
2
Harald Welte1d9986a2019-11-28 14:47:22 +01003/* TTCN-3 tests for USB CCID (Chip Card Interface Device)
4 *
5 * (C) 2018-2019 by Harald Welte <laforge@gnumonks.org>
6 */
7
Harald Weltef30e22b2019-11-14 08:36:19 +01008import from General_Types all;
9import from Osmocom_Types all;
10import from Misc_Helpers all;
11
12import from USB_PortType all;
Harald Welte1d9986a2019-11-28 14:47:22 +010013import from USB_Component all;
Harald Weltef30e22b2019-11-14 08:36:19 +010014
15import from CCID_Types all;
16import from CCID_Templates all;
17import from CCID_Emulation all;
18
Harald Welte1d9986a2019-11-28 14:47:22 +010019modulepar {
Harald Welte94528c72020-07-30 22:11:08 +020020 USB_Device_Match mp_usb_dev_match := { vid_pid := { vid := '1d50'H, pid := '6141'H } };
Harald Welte1d9986a2019-11-28 14:47:22 +010021 integer mp_use_slot_count := 8;
22 boolean mp_test_power_off := true;
23 boolean mp_quirk_resetpar_returns_slotsts := false;
24}
Harald Weltef30e22b2019-11-14 08:36:19 +010025/* global test component; manages CCID device */
26type component Test_CT {
27 var CCID_Emulation_CT vc_CCID;
28 port USB_PT USB;
29 var Slot_CT vc_SLOT[NR_SLOTS];
30};
31
Harald Welte1d9986a2019-11-28 14:47:22 +010032/* maximum number of slots we are supporting in the test suite */
33private const integer NR_SLOTS := 16;
Harald Weltef30e22b2019-11-14 08:36:19 +010034
35/***********************************************************************
36 * helper infrastructure
37 ***********************************************************************/
38
Harald Welte1d9986a2019-11-28 14:47:22 +010039const octetstring c_UICC_SELECT_MF := '00a40004023f00'O;
40const octetstring c_SIM_SELECT_MF := 'a0a40004023f00'O;
41
Eric Wild7f5cfd42019-11-22 17:45:40 +010042/* Table 7 of ISO7816-3 */
43type enumerated ISO7816_Fi {
44 ISO7816_FI_372_4MHz ('0000'B),
45 ISO7816_FI_372_5MHz ('0001'B),
46 ISO7816_FI_558_6MHz ('0010'B),
47 ISO7816_FI_744_8MHz ('0011'B),
48 ISO7816_FI_1116_12MHz ('0100'B),
49 ISO7816_FI_1488_16MHz ('0101'B),
50 ISO7816_FI_1860_20MHz ('0110'B),
51
52 ISO7816_FI_512_5MHz ('1001'B),
53 ISO7816_FI_768_7MHz ('1010'B),
54 ISO7816_FI_1024_10MHz ('1011'B),
55 ISO7816_FI_1536_15MHz ('1100'B),
56 ISO7816_FI_2048_20MHz ('1101'B)
57};
58
59/* Table 8 of ISO7816-3 */
60type enumerated ISO7816_Di {
61 ISO7816_DI_1 ('0001'B),
62 ISO7816_DI_2 ('0010'B),
63 ISO7816_DI_4 ('0011'B),
64 ISO7816_DI_8 ('0100'B),
65 ISO7816_DI_16 ('0101'B),
66 ISO7816_DI_32 ('0110'B),
67 ISO7816_DI_64 ('0111'B),
68
69 ISO7816_DI_12 ('1000'B),
70 ISO7816_DI_20 ('1001'B)
71}
72
73private template (value) CCID_ProtocolData ts_ProtoDataT0(ISO7816_Fi fi, ISO7816_Di di,
74 uint8_t guard_time := 0,
75 uint8_t wait_int := 0) := {
76 T0 := {
77 Findex := enum2int(fi),
78 Dindex := enum2int(di),
79
80 bRFU := '000000'B,
81 inv_convention := false,
82 bRFU2 := '0'B,
83
84 bGuardTimeT0 := guard_time,
85 bWaitingIntegerT0 := wait_int,
86 bClockStop := STOPPING_NOT_ALLOWED
87 }
88};
89
Harald Weltef30e22b2019-11-14 08:36:19 +010090type function void_fn() runs on Slot_CT;
91
Harald Weltef30e22b2019-11-14 08:36:19 +010092/* first function inside Slot_CT; wait for CCID_EVENT_UP + call testcase-specific function */
93private function f_handler_init(void_fn fn, integer slot_nr) runs on Slot_CT {
94 g_slot_nr := slot_nr;
95 CCID.receive(CCID_Emulation_Event:{up_down:=CCID_EVENT_UP});
96 g_Tguard.start;
97 activate(as_Tguard());
98
99 fn.apply();
100}
101
102/* start a single slot handler */
103private function f_start_handler(void_fn fn, integer slot_nr) runs on Test_CT
104{
105 var Slot_CT vc;
106
107 vc_SLOT[slot_nr] := Slot_CT.create("Slot" & int2str(slot_nr));
108 connect(vc_SLOT[slot_nr]:CCID, vc_CCID:SLOT[slot_nr]);
109 vc_SLOT[slot_nr].start(f_handler_init(fn, slot_nr));
110}
111
112private function f_wait_handlers_complete() runs on Test_CT {
113 var integer i;
114
115 for (i := 0; i < NR_SLOTS; i := i+1) {
116 if (vc_SLOT[i] != null) {
117 vc_SLOT[i].done;
118 }
119 }
120 setverdict(pass);
121}
122
123private function f_start_and_wait() runs on Test_CT {
124 /* start CCID_Emulation last, it will trigger all the per-slot components */
Harald Welte1d9986a2019-11-28 14:47:22 +0100125 var CCID_Emulation_Params cep := { usb_dev_match := mp_usb_dev_match };
126 vc_CCID.start(CCID_Emulation.main(cep));
Harald Weltef30e22b2019-11-14 08:36:19 +0100127 f_wait_handlers_complete();
128}
129
130private function f_init() runs on Test_CT {
131 var integer i;
132 vc_CCID := CCID_Emulation_CT.create("CCID");
133 map(vc_CCID:USB, system:USB);
134 for (i := 0; i < NR_SLOTS; i := i+1) {
135 vc_SLOT[i] := null;
136 }
137}
138
Harald Weltef30e22b2019-11-14 08:36:19 +0100139
Harald Weltef30e22b2019-11-14 08:36:19 +0100140
141/***********************************************************************
Harald Welte1d9986a2019-11-28 14:47:22 +0100142 * Test behavior regarding valid situations
Harald Weltef30e22b2019-11-14 08:36:19 +0100143 ***********************************************************************/
144
Harald Weltef30e22b2019-11-14 08:36:19 +0100145/* request 100 times the slot status */
146private function f_TC_getstatus() runs on Slot_CT
147{
148 var integer i;
149 for (i := 0; i < 100; i := i+1) {
150 CCID.send(ts_CCID_GetSlotStatus(g_slot_nr));
151 /* it would be fun to simply send more requests here, but the CCID
152 * spec doesn't permit more than one unresponded command [per slot] */
153 alt {
154 [] CCID.receive(tr_CCID_SlotStatus(g_slot_nr));
Harald Welte1d9986a2019-11-28 14:47:22 +0100155 [] as_ccid_any();
Harald Weltef30e22b2019-11-14 08:36:19 +0100156 }
157 }
158 setverdict(pass);
159}
160testcase TC_get_status() runs on Test_CT
161{
162 var integer i;
163
164 f_init();
165
Harald Welte1d9986a2019-11-28 14:47:22 +0100166 for (i := 0; i < mp_use_slot_count; i := i+1) {
Harald Weltef30e22b2019-11-14 08:36:19 +0100167 f_start_handler(refers(f_TC_getstatus), i);
168 }
169
170 f_start_and_wait();
171}
172
173
174private function f_TC_power_on() runs on Slot_CT
175{
Harald Welte1d9986a2019-11-28 14:47:22 +0100176 f_ccid_power_on();
Harald Weltef30e22b2019-11-14 08:36:19 +0100177}
178testcase TC_power_on() runs on Test_CT
179{
180 var integer i;
181
182 f_init();
183
Harald Welte1d9986a2019-11-28 14:47:22 +0100184 for (i := 0; i < mp_use_slot_count; i := i+1) {
Harald Weltef30e22b2019-11-14 08:36:19 +0100185 f_start_handler(refers(f_TC_power_on), i);
186 }
187
188 f_start_and_wait();
189}
190
Harald Welte1d9986a2019-11-28 14:47:22 +0100191private function f_TC_power_off() runs on Slot_CT
192{
193 f_ccid_power_on();
194 f_ccid_power_off();
195}
196testcase TC_power_off() runs on Test_CT
197{
198 var integer i;
199
200 f_init();
201
202 for (i := 0; i < mp_use_slot_count; i := i+1) {
203 f_start_handler(refers(f_TC_power_off), i);
204 }
205
206 f_start_and_wait();
207}
208
209
210/* repeat IccPowerOn on slot that's already active (next warm reset ATR) */
211private function f_TC_power_on_warm() runs on Slot_CT
212{
213 var integer i;
214
215 /* initial power on */
216 f_ccid_power_on();
217
218 /* additional power on */
219 for (i := 0; i < 20; i := i+1) {
220 f_ccid_power_on();
221 }
222}
223testcase TC_power_on_warm() runs on Test_CT
224{
225 var integer i;
226
227 f_init();
228
229 for (i := 0; i < mp_use_slot_count; i := i+1) {
230 f_start_handler(refers(f_TC_power_on_warm), i);
231 }
232
233 f_start_and_wait();
234}
235
236/* transfer 1000 APDUs by issuing SELECT MF */
Harald Weltef30e22b2019-11-14 08:36:19 +0100237private function f_TC_select_mf() runs on Slot_CT
238{
239 var integer i;
Harald Welte1d9986a2019-11-28 14:47:22 +0100240 f_ccid_power_on();
Eric Wild7f5cfd42019-11-22 17:45:40 +0100241 f_ccid_set_par(ts_ProtoDataT0(ISO7816_FI_512_5MHz, ISO7816_DI_32));
Harald Welte1d9986a2019-11-28 14:47:22 +0100242 for (i := 0; i < 1000; i := i+1) {
Harald Weltec6fde3f2020-07-30 22:09:55 +0200243 f_ccid_xfr(c_UICC_SELECT_MF, '??'O);
Harald Weltef30e22b2019-11-14 08:36:19 +0100244 }
245}
246testcase TC_select_mf() runs on Test_CT
247{
248 var integer i;
249
250 f_init();
251
Harald Welte1d9986a2019-11-28 14:47:22 +0100252 for (i := 0; i < mp_use_slot_count; i := i+1) {
Harald Weltef30e22b2019-11-14 08:36:19 +0100253 f_start_handler(refers(f_TC_select_mf), i);
254 }
255
256 f_start_and_wait();
257}
258
Harald Welte1d9986a2019-11-28 14:47:22 +0100259/* GetParametrs: verify contents */
260private function f_TC_get_params() runs on Slot_CT
261{
262 var CCID_PDU par;
263 f_ccid_power_on();
264 par := f_ccid_get_par();
265 log(par);
266}
267testcase TC_get_params() runs on Test_CT
268{
269 var integer i;
270
271 f_init();
272
273 for (i := 0; i < mp_use_slot_count; i := i+1) {
274 f_start_handler(refers(f_TC_get_params), i);
275 }
276 f_start_and_wait();
277}
278
279/* SetParameters: verify change */
280private function f_TC_set_params() runs on Slot_CT
281{
282 var CCID_PDU par;
283 f_ccid_power_on();
284
285 /* get current parameters */
286 par := f_ccid_get_par();
287
288 /* modify some of them */
289 var CCID_ProtocolData pd := par.u.Parameters.abProtocolData;
290 pd.T0.bGuardTimeT0 := 23;
291 pd.T0.bWaitingIntegerT0 := 42;
292 par := f_ccid_set_par(pd);
293
294 /* check if modifications were applied */
295 var template (present) CCID_ProtocolData tr_PD := {
296 T0 := {
297 Findex := ?,
298 Dindex := ?,
299 bRFU := ?,
300 inv_convention := ?,
301 bRFU2 := ?,
302 bGuardTimeT0 := 23,
303 bWaitingIntegerT0 := 42,
304 bClockStop := ?
305 }
306 };
307 if (match(par.u.Parameters.abProtocolData, tr_PD)) {
308 setverdict(pass);
309 } else {
310 setverdict(fail, "SetParameters didn't change GuardTime/WaitingInteger");
311 }
312}
313testcase TC_set_params() runs on Test_CT
314{
315 var integer i;
316
317 f_init();
318
319 for (i := 0; i < mp_use_slot_count; i := i+1) {
320 f_start_handler(refers(f_TC_set_params), i);
321 }
322 f_start_and_wait();
323}
324
325/* ResetParameters: verify change */
326private function f_TC_reset_params() runs on Slot_CT
327{
328 var CCID_PDU par;
329
330 f_TC_set_params();
331 par := f_ccid_reset_par();
332 if (mp_quirk_resetpar_returns_slotsts) {
333 par := f_ccid_get_par();
334 }
335 if (par.u.Parameters.abProtocolData.T0.bGuardTimeT0 == 23 or
336 par.u.Parameters.abProtocolData.T0.bWaitingIntegerT0 == 42) {
337 setverdict(fail, "ResetParameters didn't reset properly");
338 }
339}
340testcase TC_reset_params() runs on Test_CT
341{
342 var integer i;
343
344 f_init();
345
346 for (i := 0; i < mp_use_slot_count; i := i+1) {
347 f_start_handler(refers(f_TC_reset_params), i);
348 }
349 f_start_and_wait();
350}
351
352
353
354/* TODO */
355/* IccPowerOn: verify that CCID resets all parameters to default values */
356/* IccPowerOn: verify that bPowerSelect has no effect in active state */
357/* XfrBlock: length corner cases (Lc/Le max, ...) */
358/* IccClock: verify clock has stopped/restarted */
359/* Abort for command that already terminated */
360/* Abort for command that's still processing */
361
362
363/***********************************************************************
364 * Test behavior regarding invalid situations
365 ***********************************************************************/
366
367/* message for invalid slot number (more than we have) */
368private function f_TC_inval_slot() runs on Slot_CT {
369 CCID.send(ts_CCID_GetSlotStatus(g_slot_nr));
370 alt {
371 [] CCID.receive(tr_CCID_SlotStatus(hdr_in := tr_CCID_HeaderIN_FAIL(CCID_ERR_SLOT_NOT_EXIST))) {
372 setverdict(pass);
373 }
374 [] CCID.receive(tr_CCID_SlotStatus) {
375 setverdict(fail, "Unexpected SlotStatus");
376 mtc.stop;
377 }
378 [] as_ccid_any();
379 }
380}
381testcase TC_inval_slot() runs on Test_CT {
382 f_init();
383 f_start_handler(refers(f_TC_inval_slot), 15);
384 f_start_and_wait();
385}
386
387/* switch card off and then XfrBlock. Requires reader with IccPowerOff support */
388private function f_TC_xfer_off() runs on Slot_CT {
389 f_ccid_power_off();
390 CCID.send(ts_CCID_XfrBlock(g_slot_nr, c_SIM_SELECT_MF, 0));
391 alt {
392 [] CCID.receive(tr_CCID_DataBlock(slot:=g_slot_nr, hdr_in:=tr_CCID_HeaderIN_FAIL)) {
393 setverdict(pass);
394 }
395 [] CCID.receive(tr_CCID_DataBlock(slot:=g_slot_nr, hdr_in:=tr_CCID_HeaderIN_OK)) {
396 setverdict(fail, "Expected XfrBlock to fail");
397 mtc.stop;
398 }
399 [] as_ccid_any();
400 }
401}
402testcase TC_xfer_off() runs on Test_CT {
403 f_init();
404 f_start_handler(refers(f_TC_xfer_off), 0);
405 f_start_and_wait();
406}
407
408
409/* unsupported Mechanical */
410private function f_TC_unsupp_mechanical() runs on Slot_CT {
411 CCID.send(ts_CCID_Mechanical(g_slot_nr, CCID_MECH_FN_EJECT_CARD));
412 alt {
413 [] CCID.receive(tr_CCID_SlotStatus(hdr_in := tr_CCID_HeaderIN_FAIL(CCID_ERR_CMD_NOT_SUPPORTED))) {
414 setverdict(pass);
415 }
416 [] as_ccid_any();
417 }
418}
419testcase TC_unsupp_mechanical() runs on Test_CT {
420 f_init();
421 f_start_handler(refers(f_TC_unsupp_mechanical), 0);
422 f_start_and_wait();
423}
424
425/* unsupported Secure */
426private function f_TC_unsupp_secure() runs on Slot_CT {
427 CCID.send(ts_CCID_Secure(g_slot_nr, 0, 0, ''O));
428 alt {
429 [] CCID.receive(tr_CCID_DataBlock(hdr_in := tr_CCID_HeaderIN_FAIL(CCID_ERR_CMD_NOT_SUPPORTED))) {
430 setverdict(pass);
431 }
432 [] as_ccid_any();
433 }
434}
435testcase TC_unsupp_secure() runs on Test_CT {
436 f_init();
437 f_start_handler(refers(f_TC_unsupp_secure), 0);
438 f_start_and_wait();
439}
440
441
442/* TODO */
443/* truncated message */
444/* IccPowerOn with wrong voltage (> 0x04) */
445/* XfrBlock on empty slot */
446/* GetParameters on empty slot */
447/* SetParameters for bProtocolNum > 0x01 */
448/* SetParameters: invalid parameters */
449/* set unsupported frequency */
450/* set unsupported clock rate */
451/* XfrBlock: bWI in T=0? */
452/* XfrBlock: wLevelParameter not matching level? */
453/* Abort for command that was not even submitted yet*/
454/* dwMaxCCIDMessageLength */
Harald Weltef30e22b2019-11-14 08:36:19 +0100455
456
457control {
Harald Welte1d9986a2019-11-28 14:47:22 +0100458 /* valid transactions */
Harald Weltef30e22b2019-11-14 08:36:19 +0100459 execute( TC_get_status() );
460 execute( TC_power_on() );
Harald Welte1d9986a2019-11-28 14:47:22 +0100461 execute( TC_power_on_warm() );
462 if (mp_test_power_off) {
463 execute( TC_power_off() );
464 }
Harald Weltef30e22b2019-11-14 08:36:19 +0100465 execute( TC_select_mf() );
Harald Welte1d9986a2019-11-28 14:47:22 +0100466 execute( TC_get_params() );
467 execute( TC_set_params() );
468 execute( TC_reset_params() );
469
470 /* error handling */
471 execute( TC_inval_slot() );
472 if (mp_test_power_off) {
473 execute( TC_xfer_off() );
474 }
475 execute( TC_unsupp_mechanical() );
476 execute( TC_unsupp_secure() );
Harald Weltef30e22b2019-11-14 08:36:19 +0100477}
478
479
480
481
482}