xref: /illumos-gate/usr/src/test/os-tests/tests/uccid/atrparse.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2019, Joyent, Inc.
14  */
15 
16 /*
17  * Verify that we can parse various forms of ATR data and detect invalid data.
18  */
19 
20 #include <err.h>
21 #include <stdlib.h>
22 
23 #include <atr.h>
24 
25 typedef struct atr_vals {
26 } atr_vals_t;
27 
28 typedef struct atr_test {
29 	const char *ar_test;
30 	uint8_t ar_len;
31 	uint8_t ar_buf[64];
32 	atr_parsecode_t ar_retval;
33 	/* Everything after this is data from the ATR */
34 	atr_protocol_t ar_sup;
35 	atr_protocol_t ar_def;
36 	boolean_t ar_neg;
37 	uint8_t ar_fi;
38 	uint8_t ar_di;
39 	atr_convention_t ar_conv;
40 	uint8_t ar_guard;
41 	atr_clock_stop_t ar_stop;
42 	/* These will be checked based on sup prot */
43 	uint8_t ar_t0_wi;
44 	atr_t1_checksum_t  ar_t1_cksum;
45 	uint8_t ar_t1_bwi;
46 	uint8_t ar_t1_cwi;
47 	uint8_t ar_t1_ifsc;
48 } atr_test_t;
49 
50 atr_test_t atr_tests[] = {
51 	{ "zero-length data", 0, { 0 }, ATR_CODE_TOO_SHORT },
52 	{ "No T0", 1, { 0x3f }, ATR_CODE_TOO_SHORT },
53 	{ "Too much data", 34, { 0 }, ATR_CODE_TOO_LONG },
54 	{ "Overrun T0 (1)", 2, { 0x3b, 0x10 }, ATR_CODE_OVERRUN },
55 	{ "Overrun T0 (2)", 2, { 0x3b, 0x80 }, ATR_CODE_OVERRUN },
56 	{ "Overrun T0 (3)", 2, { 0x3b, 0x01 }, ATR_CODE_OVERRUN },
57 	{ "Overrun T0 (4)", 2, { 0x3b, 0x11 }, ATR_CODE_OVERRUN },
58 	{ "Overrun T0 (5)", 2, { 0x3b, 0xff }, ATR_CODE_OVERRUN },
59 	{ "Overrun TD1", 3, { 0x3b, 0x80, 0x10 }, ATR_CODE_OVERRUN },
60 	{ "Overrun TD2", 4, { 0x3b, 0x80, 0x80, 0x10 }, ATR_CODE_OVERRUN },
61 	{ "Overrun TD", 33, { 0x3b, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
62 	    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
63 	    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
64 	    0x80, 0x80, 0x80 }, ATR_CODE_OVERRUN },
65 	{ "T0 w/ T=15 and no cksum", 5, { 0x3b, 0x80, 0x80, 0x1f, 0x00 },
66 	    ATR_CODE_OVERRUN },
67 	{ "Bad TS (1)", 2, { 0x3a, 0x00 }, ATR_CODE_INVALID_TS },
68 	{ "Bad TS (2)", 2, { 0xff, 0x00 }, ATR_CODE_INVALID_TS },
69 	{ "T0 w/ T=15 and bad cksum", 6, { 0x3b, 0x80, 0x80, 0x1f, 0x00, 0x00 },
70 	    ATR_CODE_CHECKSUM_ERROR },
71 	{ "T0 w/ T=15 and bad cksum (make sure no TS)", 6,
72 	    { 0x3b, 0x80, 0x80, 0x1f, 0x00, 0x24 },
73 	    ATR_CODE_CHECKSUM_ERROR },
74 	{ "T=15 in TD1", 4, { 0x3b, 0x80, 0x0f, 0x8f }, ATR_CODE_INVALID_TD1 },
75 	{
76 		.ar_test = "Minimal T0 Direct",
77 		.ar_len = 2,
78 		.ar_buf = { 0x3b, 0x00 },
79 		.ar_sup = ATR_P_T0,
80 		.ar_def = ATR_P_T0,
81 		.ar_neg = B_TRUE,
82 		.ar_fi = 1,
83 		.ar_di = 1,
84 		.ar_conv = ATR_CONVENTION_DIRECT,
85 		.ar_guard = 0,
86 		.ar_stop = ATR_CLOCK_STOP_NONE,
87 		.ar_t0_wi = 10,
88 	}, {
89 		.ar_test = "Minimal T0 Inverse",
90 		.ar_len = 2,
91 		.ar_buf = { 0x3f, 0x00 },
92 		.ar_sup = ATR_P_T0,
93 		.ar_def = ATR_P_T0,
94 		.ar_neg = B_TRUE,
95 		.ar_fi = 1,
96 		.ar_di = 1,
97 		.ar_conv = ATR_CONVENTION_INVERSE,
98 		.ar_guard = 0,
99 		.ar_stop = ATR_CLOCK_STOP_NONE,
100 		.ar_t0_wi = 10,
101 	}, {
102 		.ar_test = "T0 Fi/Di (1)",
103 		.ar_len = 3,
104 		.ar_buf = { 0x3b, 0x10, 0x24 },
105 		.ar_sup = ATR_P_T0,
106 		.ar_def = ATR_P_T0,
107 		.ar_neg = B_TRUE,
108 		.ar_fi = 2,
109 		.ar_di = 4,
110 		.ar_conv = ATR_CONVENTION_DIRECT,
111 		.ar_guard = 0,
112 		.ar_stop = ATR_CLOCK_STOP_NONE,
113 		.ar_t0_wi = 10,
114 	}, {
115 		.ar_test = "T0 Fi/Di (2)",
116 		.ar_len = 3,
117 		.ar_buf = { 0x3b, 0x10, 0x93 },
118 		.ar_sup = ATR_P_T0,
119 		.ar_def = ATR_P_T0,
120 		.ar_neg = B_TRUE,
121 		.ar_fi = 9,
122 		.ar_di = 3,
123 		.ar_conv = ATR_CONVENTION_DIRECT,
124 		.ar_guard = 0,
125 		.ar_stop = ATR_CLOCK_STOP_NONE,
126 		.ar_t0_wi = 10,
127 	}, {
128 		.ar_test = "T0 Ignore deprecated TB1",
129 		.ar_len = 3,
130 		.ar_buf = { 0x3b, 0x20, 0x42 },
131 		.ar_sup = ATR_P_T0,
132 		.ar_def = ATR_P_T0,
133 		.ar_neg = B_TRUE,
134 		.ar_fi = 1,
135 		.ar_di = 1,
136 		.ar_conv = ATR_CONVENTION_DIRECT,
137 		.ar_guard = 0,
138 		.ar_stop = ATR_CLOCK_STOP_NONE,
139 		.ar_t0_wi = 10,
140 	}, {
141 		.ar_test = "T0 Ignore deprecated TB2",
142 		.ar_len = 4,
143 		.ar_buf = { 0x3b, 0x80, 0x20, 0x42 },
144 		.ar_sup = ATR_P_T0,
145 		.ar_def = ATR_P_T0,
146 		.ar_neg = B_TRUE,
147 		.ar_fi = 1,
148 		.ar_di = 1,
149 		.ar_conv = ATR_CONVENTION_DIRECT,
150 		.ar_guard = 0,
151 		.ar_stop = ATR_CLOCK_STOP_NONE,
152 		.ar_t0_wi = 10,
153 	}, {
154 		.ar_test = "T0 Ignore deprecated TB1/TB2",
155 		.ar_len = 5,
156 		.ar_buf = { 0x3b, 0xa0, 0x55, 0x20, 0x42 },
157 		.ar_sup = ATR_P_T0,
158 		.ar_def = ATR_P_T0,
159 		.ar_neg = B_TRUE,
160 		.ar_fi = 1,
161 		.ar_di = 1,
162 		.ar_conv = ATR_CONVENTION_DIRECT,
163 		.ar_guard = 0,
164 		.ar_stop = ATR_CLOCK_STOP_NONE,
165 		.ar_t0_wi = 10,
166 	}, {
167 		.ar_test = "T0 Encode TC1",
168 		.ar_len = 3,
169 		.ar_buf = { 0x3b, 0x40, 0x23 },
170 		.ar_sup = ATR_P_T0,
171 		.ar_def = ATR_P_T0,
172 		.ar_neg = B_TRUE,
173 		.ar_fi = 1,
174 		.ar_di = 1,
175 		.ar_conv = ATR_CONVENTION_DIRECT,
176 		.ar_guard = 0x23,
177 		.ar_stop = ATR_CLOCK_STOP_NONE,
178 		.ar_t0_wi = 10,
179 	}, {
180 		.ar_test = "T0 TA2 says neg",
181 		.ar_len = 4,
182 		.ar_buf = { 0x3b, 0x80, 0x10, 0x00 },
183 		.ar_sup = ATR_P_T0,
184 		.ar_def = ATR_P_T0,
185 		.ar_neg = B_TRUE,
186 		.ar_fi = 1,
187 		.ar_di = 1,
188 		.ar_conv = ATR_CONVENTION_DIRECT,
189 		.ar_guard = 0,
190 		.ar_stop = ATR_CLOCK_STOP_NONE,
191 		.ar_t0_wi = 10,
192 	}, {
193 		.ar_test = "T0 TA2 says not neg",
194 		.ar_len = 4,
195 		.ar_buf = { 0x3b, 0x80, 0x10, 0x80 },
196 		.ar_sup = ATR_P_T0,
197 		.ar_def = ATR_P_T0,
198 		.ar_neg = B_FALSE,
199 		.ar_fi = 1,
200 		.ar_di = 1,
201 		.ar_conv = ATR_CONVENTION_DIRECT,
202 		.ar_guard = 0,
203 		.ar_stop = ATR_CLOCK_STOP_NONE,
204 		.ar_t0_wi = 10,
205 	}, {
206 		.ar_test = "T0 TA2 says not neg, honor Fi/Di",
207 		.ar_len = 5,
208 		.ar_buf = { 0x3b, 0x90, 0x24, 0x10, 0x80 },
209 		.ar_sup = ATR_P_T0,
210 		.ar_def = ATR_P_T0,
211 		.ar_neg = B_FALSE,
212 		.ar_fi = 2,
213 		.ar_di = 4,
214 		.ar_conv = ATR_CONVENTION_DIRECT,
215 		.ar_guard = 0,
216 		.ar_stop = ATR_CLOCK_STOP_NONE,
217 		.ar_t0_wi = 10,
218 	}, {
219 		.ar_test = "T0 TA2 says not neg, don't honor Fi/Di",
220 		.ar_len = 5,
221 		.ar_buf = { 0x3b, 0x90, 0x24, 0x10, 0x90 },
222 		.ar_sup = ATR_P_T0,
223 		.ar_def = ATR_P_T0,
224 		.ar_neg = B_FALSE,
225 		.ar_fi = 1,
226 		.ar_di = 1,
227 		.ar_conv = ATR_CONVENTION_DIRECT,
228 		.ar_guard = 0,
229 		.ar_stop = ATR_CLOCK_STOP_NONE,
230 		.ar_t0_wi = 10,
231 	}, {
232 		.ar_test = "T0 TC2 set",
233 		.ar_len = 4,
234 		.ar_buf = { 0x3b, 0x80, 0x40, 0x35 },
235 		.ar_sup = ATR_P_T0,
236 		.ar_def = ATR_P_T0,
237 		.ar_neg = B_TRUE,
238 		.ar_fi = 1,
239 		.ar_di = 1,
240 		.ar_conv = ATR_CONVENTION_DIRECT,
241 		.ar_guard = 0,
242 		.ar_stop = ATR_CLOCK_STOP_NONE,
243 		.ar_t0_wi = 0x35,
244 	}, {
245 		.ar_test = "T0 T15 empty (requires checksum)",
246 		.ar_len = 5,
247 		.ar_buf = { 0x3b, 0x80, 0x80, 0x0f, 0x0f },
248 		.ar_sup = ATR_P_T0,
249 		.ar_def = ATR_P_T0,
250 		.ar_neg = B_TRUE,
251 		.ar_fi = 1,
252 		.ar_di = 1,
253 		.ar_conv = ATR_CONVENTION_DIRECT,
254 		.ar_guard = 0,
255 		.ar_stop = ATR_CLOCK_STOP_NONE,
256 		.ar_t0_wi = 10,
257 	}, {
258 		.ar_test = "T0 T15 Clock Stop (1)",
259 		.ar_len = 6,
260 		.ar_buf = { 0x3b, 0x80, 0x80, 0x1f, 0x07, 0x18 },
261 		.ar_sup = ATR_P_T0,
262 		.ar_def = ATR_P_T0,
263 		.ar_neg = B_TRUE,
264 		.ar_fi = 1,
265 		.ar_di = 1,
266 		.ar_conv = ATR_CONVENTION_DIRECT,
267 		.ar_guard = 0,
268 		.ar_stop = ATR_CLOCK_STOP_NONE,
269 		.ar_t0_wi = 10,
270 	}, {
271 		.ar_test = "T0 T15 Clock Stop (2)",
272 		.ar_len = 6,
273 		.ar_buf = { 0x3b, 0x80, 0x80, 0x1f, 0x47, 0x58 },
274 		.ar_sup = ATR_P_T0,
275 		.ar_def = ATR_P_T0,
276 		.ar_neg = B_TRUE,
277 		.ar_fi = 1,
278 		.ar_di = 1,
279 		.ar_conv = ATR_CONVENTION_DIRECT,
280 		.ar_guard = 0,
281 		.ar_stop = ATR_CLOCK_STOP_LOW,
282 		.ar_t0_wi = 10,
283 	}, {
284 		.ar_test = "T0 T15 Clock Stop (3)",
285 		.ar_len = 6,
286 		.ar_buf = { 0x3b, 0x80, 0x80, 0x1f, 0x87, 0x98 },
287 		.ar_sup = ATR_P_T0,
288 		.ar_def = ATR_P_T0,
289 		.ar_neg = B_TRUE,
290 		.ar_fi = 1,
291 		.ar_di = 1,
292 		.ar_conv = ATR_CONVENTION_DIRECT,
293 		.ar_guard = 0,
294 		.ar_stop = ATR_CLOCK_STOP_HI,
295 		.ar_t0_wi = 10,
296 	}, {
297 		.ar_test = "T0 T15 Clock Stop (4)",
298 		.ar_len = 6,
299 		.ar_buf = { 0x3b, 0x80, 0x80, 0x1f, 0xc7, 0xd8 },
300 		.ar_sup = ATR_P_T0,
301 		.ar_def = ATR_P_T0,
302 		.ar_neg = B_TRUE,
303 		.ar_fi = 1,
304 		.ar_di = 1,
305 		.ar_conv = ATR_CONVENTION_DIRECT,
306 		.ar_guard = 0,
307 		.ar_stop = ATR_CLOCK_STOP_BOTH,
308 		.ar_t0_wi = 10,
309 	}, {
310 		.ar_test = "T0 with random prots",
311 		.ar_len = 7,
312 		.ar_buf = { 0x3b, 0x80, 0x84, 0x85, 0x88, 0x0f, 0x06 },
313 		.ar_sup = ATR_P_T0,
314 		/*
315 		 * This comes from the fact that TD1 is T=4 and that isn't
316 		 * supported in the system.
317 		 */
318 		.ar_def = ATR_P_NONE,
319 		.ar_neg = B_TRUE,
320 		.ar_fi = 1,
321 		.ar_di = 1,
322 		.ar_conv = ATR_CONVENTION_DIRECT,
323 		.ar_guard = 0,
324 		.ar_stop = ATR_CLOCK_STOP_NONE,
325 		.ar_t0_wi = 10,
326 	}, {
327 		.ar_test = "Actual ATR (1, Yubikey4)",
328 		.ar_len = 18,
329 		.ar_buf = { 0x3b, 0xf8, 0x13, 0x00, 0x00, 0x81, 0x31, 0xfe,
330 		    0x15, 0x59, 0x75, 0x62, 0x69, 0x6b, 0x65, 0x79, 0x34,
331 		    0xd4 },
332 		.ar_sup = ATR_P_T1,
333 		.ar_def = ATR_P_T1,
334 		.ar_neg = B_TRUE,
335 		.ar_fi = 1,
336 		.ar_di = 3,
337 		.ar_conv = ATR_CONVENTION_DIRECT,
338 		.ar_guard = 0,
339 		.ar_stop = ATR_CLOCK_STOP_NONE,
340 		.ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
341 		.ar_t1_bwi = 1,
342 		.ar_t1_cwi = 5,
343 		.ar_t1_ifsc = 254
344 	}, {
345 		.ar_test = "Actual ATR (2)",
346 		.ar_len = 19,
347 		.ar_buf = { 0x3b, 0xf9, 0x18, 0x00, 0x00, 0x81, 0x31, 0xfe,
348 		    0x45, 0x4a, 0x32, 0x44, 0x30, 0x38, 0x31, 0x5f, 0x50, 0x56,
349 		    0xb6 },
350 		.ar_sup = ATR_P_T1,
351 		.ar_def = ATR_P_T1,
352 		.ar_neg = B_TRUE,
353 		.ar_fi = 1,
354 		.ar_di = 8,
355 		.ar_conv = ATR_CONVENTION_DIRECT,
356 		.ar_guard = 0,
357 		.ar_stop = ATR_CLOCK_STOP_NONE,
358 		.ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
359 		.ar_t1_bwi = 4,
360 		.ar_t1_cwi = 5,
361 		.ar_t1_ifsc = 254
362 	}, {
363 		.ar_test = "Actual ATR (3)",
364 		.ar_len = 22,
365 		.ar_buf = { 0x3b, 0xfc, 0x18, 0x00, 0x00, 0x81, 0x31, 0x80,
366 		    0x45, 0x90, 0x67, 0x46, 0x4a, 0x00, 0x64, 0x16, 0x6, 0xf2,
367 		    0x72, 0x7e, 0x00, 0xe0 },
368 		.ar_sup = ATR_P_T1,
369 		.ar_def = ATR_P_T1,
370 		.ar_neg = B_TRUE,
371 		.ar_fi = 1,
372 		.ar_di = 8,
373 		.ar_conv = ATR_CONVENTION_DIRECT,
374 		.ar_guard = 0,
375 		.ar_stop = ATR_CLOCK_STOP_NONE,
376 		.ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
377 		.ar_t1_bwi = 4,
378 		.ar_t1_cwi = 5,
379 		.ar_t1_ifsc = 128
380 	}, {
381 		.ar_test = "Minimal T=1",
382 		.ar_len = 4,
383 		.ar_buf = { 0x3b, 0x80, 0x01, 0x81 },
384 		.ar_sup = ATR_P_T1,
385 		.ar_def = ATR_P_T1,
386 		.ar_neg = B_TRUE,
387 		.ar_fi = 1,
388 		.ar_di = 1,
389 		.ar_conv = ATR_CONVENTION_DIRECT,
390 		.ar_guard = 0,
391 		.ar_stop = ATR_CLOCK_STOP_NONE,
392 		.ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
393 		.ar_t1_bwi = 4,
394 		.ar_t1_cwi = 13,
395 		.ar_t1_ifsc = 32
396 	}, {
397 		.ar_test = "T=1 Fi/Di",
398 		.ar_len = 5,
399 		.ar_buf = { 0x3b, 0x90, 0x34, 0x01, 0xa5 },
400 		.ar_sup = ATR_P_T1,
401 		.ar_def = ATR_P_T1,
402 		.ar_neg = B_TRUE,
403 		.ar_fi = 3,
404 		.ar_di = 4,
405 		.ar_conv = ATR_CONVENTION_DIRECT,
406 		.ar_guard = 0,
407 		.ar_stop = ATR_CLOCK_STOP_NONE,
408 		.ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
409 		.ar_t1_bwi = 4,
410 		.ar_t1_cwi = 13,
411 		.ar_t1_ifsc = 32
412 	}, {
413 		.ar_test = "T=1 TA2 says neg, T=1 def",
414 		.ar_len = 5,
415 		.ar_buf = { 0x3b, 0x80, 0x11, 0x11, 0x80 },
416 		.ar_sup = ATR_P_T1,
417 		.ar_def = ATR_P_T1,
418 		.ar_neg = B_TRUE,
419 		.ar_fi = 1,
420 		.ar_di = 1,
421 		.ar_conv = ATR_CONVENTION_DIRECT,
422 		.ar_guard = 0,
423 		.ar_stop = ATR_CLOCK_STOP_NONE,
424 		.ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
425 		.ar_t1_bwi = 4,
426 		.ar_t1_cwi = 13,
427 		.ar_t1_ifsc = 32
428 	}, {
429 		.ar_test = "T=0, T=1 TA2 says neg, T=0 def",
430 		.ar_len = 6,
431 		.ar_buf = { 0x3b, 0x80, 0x90, 0x10, 0x01, 0x01 },
432 		.ar_sup = ATR_P_T0 | ATR_P_T1,
433 		.ar_def = ATR_P_T0,
434 		.ar_neg = B_TRUE,
435 		.ar_fi = 1,
436 		.ar_di = 1,
437 		.ar_conv = ATR_CONVENTION_DIRECT,
438 		.ar_guard = 0,
439 		.ar_stop = ATR_CLOCK_STOP_NONE,
440 		.ar_t0_wi = 10,
441 		.ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
442 		.ar_t1_bwi = 4,
443 		.ar_t1_cwi = 13,
444 		.ar_t1_ifsc = 32
445 	}, {
446 		.ar_test = "T=0, T=1 TA2 says neg, T=1 def",
447 		.ar_len = 6,
448 		.ar_buf = { 0x3b, 0x80, 0x90, 0x11, 0x01, 0x00 },
449 		.ar_sup = ATR_P_T0 | ATR_P_T1,
450 		.ar_def = ATR_P_T1,
451 		.ar_neg = B_TRUE,
452 		.ar_fi = 1,
453 		.ar_di = 1,
454 		.ar_conv = ATR_CONVENTION_DIRECT,
455 		.ar_guard = 0,
456 		.ar_stop = ATR_CLOCK_STOP_NONE,
457 		.ar_t0_wi = 10,
458 		.ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
459 		.ar_t1_bwi = 4,
460 		.ar_t1_cwi = 13,
461 		.ar_t1_ifsc = 32
462 	}, {
463 		.ar_test = "T=0, T=1 TA2 says not neg, T=0 def",
464 		.ar_len = 6,
465 		.ar_buf = { 0x3b, 0x80, 0x90, 0x90, 0x01, 0x81 },
466 		.ar_sup = ATR_P_T0 | ATR_P_T1,
467 		.ar_def = ATR_P_T0,
468 		.ar_neg = B_FALSE,
469 		.ar_fi = 1,
470 		.ar_di = 1,
471 		.ar_conv = ATR_CONVENTION_DIRECT,
472 		.ar_guard = 0,
473 		.ar_stop = ATR_CLOCK_STOP_NONE,
474 		.ar_t0_wi = 10,
475 		.ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
476 		.ar_t1_bwi = 4,
477 		.ar_t1_cwi = 13,
478 		.ar_t1_ifsc = 32
479 	}, {
480 		.ar_test = "T=0, T=1 TA2 says not neg, T=1 def",
481 		.ar_len = 6,
482 		.ar_buf = { 0x3b, 0x80, 0x90, 0x81, 0x01, 0x90 },
483 		.ar_sup = ATR_P_T0 | ATR_P_T1,
484 		.ar_def = ATR_P_T1,
485 		.ar_neg = B_FALSE,
486 		.ar_fi = 1,
487 		.ar_di = 1,
488 		.ar_conv = ATR_CONVENTION_DIRECT,
489 		.ar_guard = 0,
490 		.ar_stop = ATR_CLOCK_STOP_NONE,
491 		.ar_t0_wi = 10,
492 		.ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
493 		.ar_t1_bwi = 4,
494 		.ar_t1_cwi = 13,
495 		.ar_t1_ifsc = 32
496 	}, {
497 		.ar_test = "T=1, BWI/CWI",
498 		.ar_len = 6,
499 		.ar_buf = { 0x3b, 0x80, 0x81, 0x21, 0x59, 0x79 },
500 		.ar_sup = ATR_P_T1,
501 		.ar_def = ATR_P_T1,
502 		.ar_neg = B_TRUE,
503 		.ar_fi = 1,
504 		.ar_di = 1,
505 		.ar_conv = ATR_CONVENTION_DIRECT,
506 		.ar_guard = 0,
507 		.ar_stop = ATR_CLOCK_STOP_NONE,
508 		.ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
509 		.ar_t1_bwi = 5,
510 		.ar_t1_cwi = 9,
511 		.ar_t1_ifsc = 32
512 	}, {
513 		.ar_test = "T=1, IFSC",
514 		.ar_len = 6,
515 		.ar_buf = { 0x3b, 0x80, 0x81, 0x11, 0x49, 0x59 },
516 		.ar_sup = ATR_P_T1,
517 		.ar_def = ATR_P_T1,
518 		.ar_neg = B_TRUE,
519 		.ar_fi = 1,
520 		.ar_di = 1,
521 		.ar_conv = ATR_CONVENTION_DIRECT,
522 		.ar_guard = 0,
523 		.ar_stop = ATR_CLOCK_STOP_NONE,
524 		.ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
525 		.ar_t1_bwi = 4,
526 		.ar_t1_cwi = 13,
527 		.ar_t1_ifsc = 73
528 	}, {
529 		.ar_test = "T=1, Checksum (LRC)",
530 		.ar_len = 6,
531 		.ar_buf = { 0x3b, 0x80, 0x81, 0x41, 0x00, 0x40 },
532 		.ar_sup = ATR_P_T1,
533 		.ar_def = ATR_P_T1,
534 		.ar_neg = B_TRUE,
535 		.ar_fi = 1,
536 		.ar_di = 1,
537 		.ar_conv = ATR_CONVENTION_DIRECT,
538 		.ar_guard = 0,
539 		.ar_stop = ATR_CLOCK_STOP_NONE,
540 		.ar_t1_cksum = ATR_T1_CHECKSUM_LRC,
541 		.ar_t1_bwi = 4,
542 		.ar_t1_cwi = 13,
543 		.ar_t1_ifsc = 32
544 	}, {
545 		.ar_test = "T=1, Checksum (CRC)",
546 		.ar_len = 6,
547 		.ar_buf = { 0x3b, 0x80, 0x81, 0x41, 0x01, 0x41 },
548 		.ar_sup = ATR_P_T1,
549 		.ar_def = ATR_P_T1,
550 		.ar_neg = B_TRUE,
551 		.ar_fi = 1,
552 		.ar_di = 1,
553 		.ar_conv = ATR_CONVENTION_DIRECT,
554 		.ar_guard = 0,
555 		.ar_stop = ATR_CLOCK_STOP_NONE,
556 		.ar_t1_cksum = ATR_T1_CHECKSUM_CRC,
557 		.ar_t1_bwi = 4,
558 		.ar_t1_cwi = 13,
559 		.ar_t1_ifsc = 32
560 	}
561 };
562 
563 static void
564 atr_parse_failed(atr_test_t *test, const char *fmt, ...)
565 {
566 	va_list ap;
567 
568 	va_start(ap, fmt);
569 	(void) fprintf(stderr, "Test \"%s\" failed: ", test->ar_test);
570 	(void) vfprintf(stderr, fmt, ap);
571 	(void) fprintf(stderr, "\n");
572 	va_end(ap);
573 }
574 
575 static uint_t
576 atr_parse_one(atr_data_t *data, atr_test_t *test)
577 {
578 	uint_t err = 0;
579 	atr_parsecode_t ret;
580 	atr_protocol_t sup, def;
581 	boolean_t neg;
582 	uint8_t fi, di, guard;
583 	atr_convention_t conv;
584 	atr_clock_stop_t stop;
585 
586 	ret = atr_parse(test->ar_buf, test->ar_len, data);
587 	if (ret != test->ar_retval) {
588 		atr_parse_failed(test, "found unexpected return "
589 		    "value: %u (%s), expected: %u", ret, atr_strerror(ret),
590 		    test->ar_retval);
591 		return (1);
592 	}
593 
594 	/* Don't test anything else if it's not OK */
595 	if (ret != ATR_CODE_OK)
596 		return (0);
597 
598 	sup = atr_supported_protocols(data);
599 	def = atr_default_protocol(data);
600 	neg = atr_params_negotiable(data);
601 	fi = atr_fi_index(data);
602 	di = atr_di_index(data);
603 	conv = atr_convention(data);
604 	guard = atr_extra_guardtime(data);
605 	stop = atr_clock_stop(data);
606 
607 	if (sup != test->ar_sup) {
608 		atr_parse_failed(test, "Found mismatched supported "
609 		    "protocols: %u, expected: %u", sup, test->ar_sup);
610 		err++;
611 	}
612 
613 	if (def != test->ar_def) {
614 		atr_parse_failed(test, "Found mismatched default "
615 		    "protocols: %u, expected: %u", def, test->ar_def);
616 		err++;
617 	}
618 
619 	if (neg != test->ar_neg) {
620 		atr_parse_failed(test, "Found mismatched negotiable bit: "
621 		    "%u, expected %u", neg, test->ar_neg);
622 		err++;
623 	}
624 
625 	if (fi != test->ar_fi) {
626 		atr_parse_failed(test, "Found mismatched fi index: "
627 		    "%u, expected: %u", fi, test->ar_fi);
628 		err++;
629 	}
630 
631 	if (di != test->ar_di) {
632 		atr_parse_failed(test, "Found mismatched di index: "
633 		    "%u, expected: %u", di, test->ar_di);
634 		err++;
635 	}
636 
637 	if (conv != test->ar_conv) {
638 		atr_parse_failed(test, "Found mismatched TS convention: "
639 		    "%u, expected: %u", conv, test->ar_conv);
640 		err++;
641 	}
642 
643 	if (guard != test->ar_guard) {
644 		atr_parse_failed(test, "Found mismatched extra guardtime: "
645 		    "%u, expected: %u", guard, test->ar_guard);
646 		err++;
647 	}
648 
649 	if (stop != test->ar_stop) {
650 		atr_parse_failed(test, "Found mismatched clock stop: "
651 		    "%u, expected: %u", stop, test->ar_stop);
652 		err++;
653 	}
654 
655 	if ((sup & ATR_P_T0) != 0) {
656 		uint8_t wi;
657 
658 		wi = atr_t0_wi(data);
659 		if (wi != test->ar_t0_wi) {
660 			atr_parse_failed(test, "Found mismatched T0 wi: "
661 			    "%u, expected: %u", wi, test->ar_t0_wi);
662 			err++;
663 		}
664 	}
665 
666 	if ((sup & ATR_P_T1) != 0) {
667 		atr_t1_checksum_t cksum;
668 		uint8_t bwi, cwi, ifsc;
669 
670 		cksum = atr_t1_checksum(data);
671 		bwi = atr_t1_bwi(data);
672 		cwi = atr_t1_cwi(data);
673 		ifsc = atr_t1_ifsc(data);
674 
675 		if (cksum != test->ar_t1_cksum) {
676 			atr_parse_failed(test, "Found mistmatched T1 checksum: "
677 			    "%u, expected: %u", cksum, test->ar_t1_cksum);
678 			err++;
679 		}
680 
681 		if (bwi != test->ar_t1_bwi) {
682 			atr_parse_failed(test, "Found mistmatched T1 bwi: "
683 			    "%u, expected: %u", bwi, test->ar_t1_bwi);
684 			err++;
685 		}
686 
687 		if (cwi != test->ar_t1_cwi) {
688 			atr_parse_failed(test, "Found mistmatched T1 cwi: "
689 			    "%u, expected: %u", cwi, test->ar_t1_cwi);
690 			err++;
691 		}
692 
693 		if (ifsc != test->ar_t1_ifsc) {
694 			atr_parse_failed(test, "Found mistmatched T1 ifsc: "
695 			    "%u, expected: %u", ifsc, test->ar_t1_ifsc);
696 			err++;
697 		}
698 	}
699 
700 	if (err > 0) {
701 		atr_data_dump(data, stderr);
702 		return (1);
703 	}
704 
705 	return (0);
706 }
707 
708 int
709 main(void)
710 {
711 	uint_t i;
712 	uint_t errs = 0;
713 	atr_data_t *data;
714 
715 	data = atr_data_alloc();
716 	if (data == NULL) {
717 		errx(EXIT_FAILURE, "failed to allocate atr_data_t");
718 	}
719 
720 	for (i = 0; i < sizeof (atr_tests) / sizeof (atr_test_t); i++) {
721 		atr_data_reset(data);
722 		errs += atr_parse_one(data, &atr_tests[i]);
723 	}
724 
725 	atr_data_free(data);
726 
727 	if (errs != 0) {
728 		warnx("%d test(s) failed", errs);
729 	}
730 	return (errs != 0 ? EXIT_FAILURE : EXIT_SUCCESS);
731 }
732