blob: f6816be89eceb1d2e89de53711b7740793db93b0 [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 {
20 USB_Device_Match mp_usb_dev_match := { vid_pid := { vid := '1df0'H, pid := '6141'H } };
21 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
32/* per-slot test component; manages one slot */
33type component Slot_CT {
34 var uint8_t g_slot_nr;
35 port CCID_SLOT_PT CCID;
Harald Welteea8db4c2019-11-25 22:12:42 +010036 timer g_Tguard := 120.0;
Harald Weltef30e22b2019-11-14 08:36:19 +010037};
38
Harald Welte1d9986a2019-11-28 14:47:22 +010039/* maximum number of slots we are supporting in the test suite */
40private const integer NR_SLOTS := 16;
Harald Weltef30e22b2019-11-14 08:36:19 +010041
42/***********************************************************************
43 * helper infrastructure
44 ***********************************************************************/
45
Harald Welte1d9986a2019-11-28 14:47:22 +010046const octetstring c_UICC_SELECT_MF := '00a40004023f00'O;
47const octetstring c_SIM_SELECT_MF := 'a0a40004023f00'O;
48
Harald Weltef30e22b2019-11-14 08:36:19 +010049type function void_fn() runs on Slot_CT;
50
51/* altstep running on the per-slot test component */
52private altstep as_Tguard() runs on Slot_CT {
53 [] g_Tguard.timeout {
54 Misc_Helpers.f_shutdown(__BFILE__, __LINE__, fail, "Tguard timeout");
55 }
56}
57
Harald Welte1d9986a2019-11-28 14:47:22 +010058private altstep as_ccid_any() runs on Slot_CT {
59 var CCID_PDU pdu;
60 [] CCID.receive(CCID_PDU:?) -> value pdu {
61 setverdict(fail, "Received unexpected CCID ", pdu);
62 self.stop;
63 }
64 [] CCID.receive {
65 setverdict(fail, "Received unexpected non-CCID");
66 self.stop;
67 }
68}
69
Harald Weltef30e22b2019-11-14 08:36:19 +010070/* first function inside Slot_CT; wait for CCID_EVENT_UP + call testcase-specific function */
71private function f_handler_init(void_fn fn, integer slot_nr) runs on Slot_CT {
72 g_slot_nr := slot_nr;
73 CCID.receive(CCID_Emulation_Event:{up_down:=CCID_EVENT_UP});
74 g_Tguard.start;
75 activate(as_Tguard());
76
77 fn.apply();
78}
79
80/* start a single slot handler */
81private function f_start_handler(void_fn fn, integer slot_nr) runs on Test_CT
82{
83 var Slot_CT vc;
84
85 vc_SLOT[slot_nr] := Slot_CT.create("Slot" & int2str(slot_nr));
86 connect(vc_SLOT[slot_nr]:CCID, vc_CCID:SLOT[slot_nr]);
87 vc_SLOT[slot_nr].start(f_handler_init(fn, slot_nr));
88}
89
90private function f_wait_handlers_complete() runs on Test_CT {
91 var integer i;
92
93 for (i := 0; i < NR_SLOTS; i := i+1) {
94 if (vc_SLOT[i] != null) {
95 vc_SLOT[i].done;
96 }
97 }
98 setverdict(pass);
99}
100
101private function f_start_and_wait() runs on Test_CT {
102 /* start CCID_Emulation last, it will trigger all the per-slot components */
Harald Welte1d9986a2019-11-28 14:47:22 +0100103 var CCID_Emulation_Params cep := { usb_dev_match := mp_usb_dev_match };
104 vc_CCID.start(CCID_Emulation.main(cep));
Harald Weltef30e22b2019-11-14 08:36:19 +0100105 f_wait_handlers_complete();
106}
107
108private function f_init() runs on Test_CT {
109 var integer i;
110 vc_CCID := CCID_Emulation_CT.create("CCID");
111 map(vc_CCID:USB, system:USB);
112 for (i := 0; i < NR_SLOTS; i := i+1) {
113 vc_SLOT[i] := null;
114 }
115}
116
Harald Weltef30e22b2019-11-14 08:36:19 +0100117
Harald Welte1d9986a2019-11-28 14:47:22 +0100118/* transceive a CCID command (send 'tx' on OUT; expect 'rx' on IN) */
119private function f_ccid_xceive(template (value) CCID_PDU tx, template (present) CCID_PDU exp_rx)
120runs on Slot_CT return CCID_PDU {
Harald Weltef30e22b2019-11-14 08:36:19 +0100121 var CCID_PDU pdu;
122
Harald Welte1d9986a2019-11-28 14:47:22 +0100123 tx.hdr.bSlot := g_slot_nr;
124 exp_rx.hdr.bSlot := g_slot_nr;
125
126 CCID.send(tx);
Harald Weltef30e22b2019-11-14 08:36:19 +0100127 alt {
Harald Welte1d9986a2019-11-28 14:47:22 +0100128 [] CCID.receive(exp_rx) -> value pdu {
129 return pdu;
Harald Weltef30e22b2019-11-14 08:36:19 +0100130 }
Harald Welte1d9986a2019-11-28 14:47:22 +0100131 [] as_ccid_any();
Harald Weltef30e22b2019-11-14 08:36:19 +0100132 }
Harald Welte1d9986a2019-11-28 14:47:22 +0100133 return pdu;
134}
135
136private template (present) CCID_Header_IN tr_inact :=
137 tr_CCID_HeaderIN_OK(icc_status := (CCID_ICC_STATUS_PRES_INACT, CCID_ICC_STATUS_NO_ICC));
138
139private template (present) CCID_Header_IN tr_act :=
140 tr_CCID_HeaderIN_OK(icc_status := CCID_ICC_STATUS_PRES_ACT);
141
142/* Send IccPowerOn on OUT; expect DataBlock in retunr */
143private function f_ccid_power_on(CCID_PowerSelect psel := CCID_PWRSEL_AUTO,
144 template (present) CCID_Header_IN hdr_in := tr_act)
145runs on Slot_CT return CCID_PDU {
146 var CCID_PDU pdu;
147
148 pdu := f_ccid_xceive(ts_CCID_IccPowerOn(g_slot_nr, psel),
149 tr_CCID_DataBlock(g_slot_nr, hdr_in := hdr_in) );
150 return pdu;
151}
152
153/* Send IccPowerOn on OUT; expect SlotStatus in return */
154private function f_ccid_power_off(template (present) CCID_Header_IN hdr_in := tr_inact)
155runs on Slot_CT return CCID_PDU {
156 var CCID_PDU pdu;
157
158 pdu := f_ccid_xceive(ts_CCID_IccPowerOff(g_slot_nr),
159 tr_CCID_SlotStatus(slot := g_slot_nr, hdr_in := hdr_in) );
160 return pdu;
161}
162
163/* Send IccClockCommand on OUT; expect SlotStatus in return */
164private function f_ccid_clock_cmd(CCID_ClockCommand cmd,
165 template (present) CCID_Header_IN hdr_in := tr_CCID_HeaderIN_OK)
166runs on Slot_CT return CCID_PDU {
167 var CCID_PDU pdu;
168
169 pdu := f_ccid_xceive(ts_CCID_ClockCommand(g_slot_nr, cmd),
170 tr_CCID_SlotStatus(slot := g_slot_nr, hdr_in := hdr_in));
171 return pdu;
172}
173
174/* Send XfrBlock on OUT; expect DataBlock in return */
175private function f_ccid_xfr(octetstring tx, template octetstring rx) runs on Slot_CT return octetstring {
176 var CCID_PDU pdu;
177
178 pdu := f_ccid_xceive(ts_CCID_XfrBlock(g_slot_nr, tx, 0),
179 tr_CCID_DataBlock(g_slot_nr, ?, ?, rx) );
180 return pdu.u.DataBlock.abData;
181}
182
183/* Send SetParameters on OUT; expect Parameters on IN */
184private function f_ccid_set_par(template (value) CCID_ProtocolData par,
185 template (present) CCID_Header_IN hdr_in := tr_CCID_HeaderIN_OK)
186runs on Slot_CT return CCID_PDU {
187 var CCID_PDU pdu;
188
189 pdu := f_ccid_xceive(ts_CCID_SetParameters(g_slot_nr, par),
190 tr_CCID_Parameters(g_slot_nr, hdr_in := hdr_in));
191 return pdu;
192}
193
194/* Send GetParameters on OUT; expect Parameters on IN */
195private function f_ccid_get_par(template (present) CCID_Header_IN hdr_in := tr_CCID_HeaderIN_OK)
196runs on Slot_CT return CCID_PDU {
197 var CCID_PDU pdu;
198
199 pdu := f_ccid_xceive(ts_CCID_GetParameters(g_slot_nr),
200 tr_CCID_Parameters(g_slot_nr, hdr_in := hdr_in));
201 return pdu;
202}
203
204/* Send ResetParameters on OUT; expect Parameters on IN */
205private function f_ccid_reset_par(template (present) CCID_Header_IN hdr_in := tr_CCID_HeaderIN_OK)
206runs on Slot_CT return CCID_PDU {
207 var CCID_PDU pdu;
208
209 /* [at least] Omnikey seems to have failed to follow the CCID spec here :/ */
210 if (mp_quirk_resetpar_returns_slotsts) {
211 pdu := f_ccid_xceive(ts_CCID_ResetParameters(g_slot_nr),
212 tr_CCID_SlotStatus(g_slot_nr, hdr_in := hdr_in));
213 } else {
214 pdu := f_ccid_xceive(ts_CCID_ResetParameters(g_slot_nr),
215 tr_CCID_Parameters(g_slot_nr, hdr_in := hdr_in));
216 }
217 return pdu;
218}
219
220/* Send Escape on OUT; expect Escape on IN */
221private function f_ccid_escape(template (value) octetstring data,
222 template (present) CCID_Header_IN hdr_in := tr_CCID_HeaderIN_OK)
223runs on Slot_CT return CCID_PDU {
224 var CCID_PDU pdu;
225
226 pdu := f_ccid_xceive(ts_CCID_Escape(g_slot_nr, data),
227 tr_CCID_EscapeIN(g_slot_nr, hdr_in := hdr_in));
228 return pdu;
Harald Weltef30e22b2019-11-14 08:36:19 +0100229}
230
231
232/***********************************************************************
Harald Welte1d9986a2019-11-28 14:47:22 +0100233 * Test behavior regarding valid situations
Harald Weltef30e22b2019-11-14 08:36:19 +0100234 ***********************************************************************/
235
Harald Weltef30e22b2019-11-14 08:36:19 +0100236/* request 100 times the slot status */
237private function f_TC_getstatus() runs on Slot_CT
238{
239 var integer i;
240 for (i := 0; i < 100; i := i+1) {
241 CCID.send(ts_CCID_GetSlotStatus(g_slot_nr));
242 /* it would be fun to simply send more requests here, but the CCID
243 * spec doesn't permit more than one unresponded command [per slot] */
244 alt {
245 [] CCID.receive(tr_CCID_SlotStatus(g_slot_nr));
Harald Welte1d9986a2019-11-28 14:47:22 +0100246 [] as_ccid_any();
Harald Weltef30e22b2019-11-14 08:36:19 +0100247 }
248 }
249 setverdict(pass);
250}
251testcase TC_get_status() runs on Test_CT
252{
253 var integer i;
254
255 f_init();
256
Harald Welte1d9986a2019-11-28 14:47:22 +0100257 for (i := 0; i < mp_use_slot_count; i := i+1) {
Harald Weltef30e22b2019-11-14 08:36:19 +0100258 f_start_handler(refers(f_TC_getstatus), i);
259 }
260
261 f_start_and_wait();
262}
263
264
265private function f_TC_power_on() runs on Slot_CT
266{
Harald Welte1d9986a2019-11-28 14:47:22 +0100267 f_ccid_power_on();
Harald Weltef30e22b2019-11-14 08:36:19 +0100268}
269testcase TC_power_on() runs on Test_CT
270{
271 var integer i;
272
273 f_init();
274
Harald Welte1d9986a2019-11-28 14:47:22 +0100275 for (i := 0; i < mp_use_slot_count; i := i+1) {
Harald Weltef30e22b2019-11-14 08:36:19 +0100276 f_start_handler(refers(f_TC_power_on), i);
277 }
278
279 f_start_and_wait();
280}
281
Harald Welte1d9986a2019-11-28 14:47:22 +0100282private function f_TC_power_off() runs on Slot_CT
283{
284 f_ccid_power_on();
285 f_ccid_power_off();
286}
287testcase TC_power_off() runs on Test_CT
288{
289 var integer i;
290
291 f_init();
292
293 for (i := 0; i < mp_use_slot_count; i := i+1) {
294 f_start_handler(refers(f_TC_power_off), i);
295 }
296
297 f_start_and_wait();
298}
299
300
301/* repeat IccPowerOn on slot that's already active (next warm reset ATR) */
302private function f_TC_power_on_warm() runs on Slot_CT
303{
304 var integer i;
305
306 /* initial power on */
307 f_ccid_power_on();
308
309 /* additional power on */
310 for (i := 0; i < 20; i := i+1) {
311 f_ccid_power_on();
312 }
313}
314testcase TC_power_on_warm() runs on Test_CT
315{
316 var integer i;
317
318 f_init();
319
320 for (i := 0; i < mp_use_slot_count; i := i+1) {
321 f_start_handler(refers(f_TC_power_on_warm), i);
322 }
323
324 f_start_and_wait();
325}
326
327/* transfer 1000 APDUs by issuing SELECT MF */
Harald Weltef30e22b2019-11-14 08:36:19 +0100328private function f_TC_select_mf() runs on Slot_CT
329{
330 var integer i;
Harald Welte1d9986a2019-11-28 14:47:22 +0100331 f_ccid_power_on();
332 for (i := 0; i < 1000; i := i+1) {
333 f_ccid_xfr(c_UICC_SELECT_MF, ?);
Harald Weltef30e22b2019-11-14 08:36:19 +0100334 }
335}
336testcase TC_select_mf() runs on Test_CT
337{
338 var integer i;
339
340 f_init();
341
Harald Welte1d9986a2019-11-28 14:47:22 +0100342 for (i := 0; i < mp_use_slot_count; i := i+1) {
Harald Weltef30e22b2019-11-14 08:36:19 +0100343 f_start_handler(refers(f_TC_select_mf), i);
344 }
345
346 f_start_and_wait();
347}
348
Harald Welte1d9986a2019-11-28 14:47:22 +0100349/* GetParametrs: verify contents */
350private function f_TC_get_params() runs on Slot_CT
351{
352 var CCID_PDU par;
353 f_ccid_power_on();
354 par := f_ccid_get_par();
355 log(par);
356}
357testcase TC_get_params() runs on Test_CT
358{
359 var integer i;
360
361 f_init();
362
363 for (i := 0; i < mp_use_slot_count; i := i+1) {
364 f_start_handler(refers(f_TC_get_params), i);
365 }
366 f_start_and_wait();
367}
368
369/* SetParameters: verify change */
370private function f_TC_set_params() runs on Slot_CT
371{
372 var CCID_PDU par;
373 f_ccid_power_on();
374
375 /* get current parameters */
376 par := f_ccid_get_par();
377
378 /* modify some of them */
379 var CCID_ProtocolData pd := par.u.Parameters.abProtocolData;
380 pd.T0.bGuardTimeT0 := 23;
381 pd.T0.bWaitingIntegerT0 := 42;
382 par := f_ccid_set_par(pd);
383
384 /* check if modifications were applied */
385 var template (present) CCID_ProtocolData tr_PD := {
386 T0 := {
387 Findex := ?,
388 Dindex := ?,
389 bRFU := ?,
390 inv_convention := ?,
391 bRFU2 := ?,
392 bGuardTimeT0 := 23,
393 bWaitingIntegerT0 := 42,
394 bClockStop := ?
395 }
396 };
397 if (match(par.u.Parameters.abProtocolData, tr_PD)) {
398 setverdict(pass);
399 } else {
400 setverdict(fail, "SetParameters didn't change GuardTime/WaitingInteger");
401 }
402}
403testcase TC_set_params() runs on Test_CT
404{
405 var integer i;
406
407 f_init();
408
409 for (i := 0; i < mp_use_slot_count; i := i+1) {
410 f_start_handler(refers(f_TC_set_params), i);
411 }
412 f_start_and_wait();
413}
414
415/* ResetParameters: verify change */
416private function f_TC_reset_params() runs on Slot_CT
417{
418 var CCID_PDU par;
419
420 f_TC_set_params();
421 par := f_ccid_reset_par();
422 if (mp_quirk_resetpar_returns_slotsts) {
423 par := f_ccid_get_par();
424 }
425 if (par.u.Parameters.abProtocolData.T0.bGuardTimeT0 == 23 or
426 par.u.Parameters.abProtocolData.T0.bWaitingIntegerT0 == 42) {
427 setverdict(fail, "ResetParameters didn't reset properly");
428 }
429}
430testcase TC_reset_params() runs on Test_CT
431{
432 var integer i;
433
434 f_init();
435
436 for (i := 0; i < mp_use_slot_count; i := i+1) {
437 f_start_handler(refers(f_TC_reset_params), i);
438 }
439 f_start_and_wait();
440}
441
442
443
444/* TODO */
445/* IccPowerOn: verify that CCID resets all parameters to default values */
446/* IccPowerOn: verify that bPowerSelect has no effect in active state */
447/* XfrBlock: length corner cases (Lc/Le max, ...) */
448/* IccClock: verify clock has stopped/restarted */
449/* Abort for command that already terminated */
450/* Abort for command that's still processing */
451
452
453/***********************************************************************
454 * Test behavior regarding invalid situations
455 ***********************************************************************/
456
457/* message for invalid slot number (more than we have) */
458private function f_TC_inval_slot() runs on Slot_CT {
459 CCID.send(ts_CCID_GetSlotStatus(g_slot_nr));
460 alt {
461 [] CCID.receive(tr_CCID_SlotStatus(hdr_in := tr_CCID_HeaderIN_FAIL(CCID_ERR_SLOT_NOT_EXIST))) {
462 setverdict(pass);
463 }
464 [] CCID.receive(tr_CCID_SlotStatus) {
465 setverdict(fail, "Unexpected SlotStatus");
466 mtc.stop;
467 }
468 [] as_ccid_any();
469 }
470}
471testcase TC_inval_slot() runs on Test_CT {
472 f_init();
473 f_start_handler(refers(f_TC_inval_slot), 15);
474 f_start_and_wait();
475}
476
477/* switch card off and then XfrBlock. Requires reader with IccPowerOff support */
478private function f_TC_xfer_off() runs on Slot_CT {
479 f_ccid_power_off();
480 CCID.send(ts_CCID_XfrBlock(g_slot_nr, c_SIM_SELECT_MF, 0));
481 alt {
482 [] CCID.receive(tr_CCID_DataBlock(slot:=g_slot_nr, hdr_in:=tr_CCID_HeaderIN_FAIL)) {
483 setverdict(pass);
484 }
485 [] CCID.receive(tr_CCID_DataBlock(slot:=g_slot_nr, hdr_in:=tr_CCID_HeaderIN_OK)) {
486 setverdict(fail, "Expected XfrBlock to fail");
487 mtc.stop;
488 }
489 [] as_ccid_any();
490 }
491}
492testcase TC_xfer_off() runs on Test_CT {
493 f_init();
494 f_start_handler(refers(f_TC_xfer_off), 0);
495 f_start_and_wait();
496}
497
498
499/* unsupported Mechanical */
500private function f_TC_unsupp_mechanical() runs on Slot_CT {
501 CCID.send(ts_CCID_Mechanical(g_slot_nr, CCID_MECH_FN_EJECT_CARD));
502 alt {
503 [] CCID.receive(tr_CCID_SlotStatus(hdr_in := tr_CCID_HeaderIN_FAIL(CCID_ERR_CMD_NOT_SUPPORTED))) {
504 setverdict(pass);
505 }
506 [] as_ccid_any();
507 }
508}
509testcase TC_unsupp_mechanical() runs on Test_CT {
510 f_init();
511 f_start_handler(refers(f_TC_unsupp_mechanical), 0);
512 f_start_and_wait();
513}
514
515/* unsupported Secure */
516private function f_TC_unsupp_secure() runs on Slot_CT {
517 CCID.send(ts_CCID_Secure(g_slot_nr, 0, 0, ''O));
518 alt {
519 [] CCID.receive(tr_CCID_DataBlock(hdr_in := tr_CCID_HeaderIN_FAIL(CCID_ERR_CMD_NOT_SUPPORTED))) {
520 setverdict(pass);
521 }
522 [] as_ccid_any();
523 }
524}
525testcase TC_unsupp_secure() runs on Test_CT {
526 f_init();
527 f_start_handler(refers(f_TC_unsupp_secure), 0);
528 f_start_and_wait();
529}
530
531
532/* TODO */
533/* truncated message */
534/* IccPowerOn with wrong voltage (> 0x04) */
535/* XfrBlock on empty slot */
536/* GetParameters on empty slot */
537/* SetParameters for bProtocolNum > 0x01 */
538/* SetParameters: invalid parameters */
539/* set unsupported frequency */
540/* set unsupported clock rate */
541/* XfrBlock: bWI in T=0? */
542/* XfrBlock: wLevelParameter not matching level? */
543/* Abort for command that was not even submitted yet*/
544/* dwMaxCCIDMessageLength */
Harald Weltef30e22b2019-11-14 08:36:19 +0100545
546
547control {
Harald Welte1d9986a2019-11-28 14:47:22 +0100548 /* valid transactions */
Harald Weltef30e22b2019-11-14 08:36:19 +0100549 execute( TC_get_status() );
550 execute( TC_power_on() );
Harald Welte1d9986a2019-11-28 14:47:22 +0100551 execute( TC_power_on_warm() );
552 if (mp_test_power_off) {
553 execute( TC_power_off() );
554 }
Harald Weltef30e22b2019-11-14 08:36:19 +0100555 execute( TC_select_mf() );
Harald Welte1d9986a2019-11-28 14:47:22 +0100556 execute( TC_get_params() );
557 execute( TC_set_params() );
558 execute( TC_reset_params() );
559
560 /* error handling */
561 execute( TC_inval_slot() );
562 if (mp_test_power_off) {
563 execute( TC_xfer_off() );
564 }
565 execute( TC_unsupp_mechanical() );
566 execute( TC_unsupp_secure() );
Harald Weltef30e22b2019-11-14 08:36:19 +0100567}
568
569
570
571
572}