2 * Routines for packet dissection of generic ISO 7816 smart card messages
3 * Copyright 2012-2013 by Martin Kaiser <martin@kaiser.cx>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
12 /* This dissector supports the command and response apdu structure
13 * as defined in ISO 7816-4. Detailed dissection of the APDUs defined
14 * in the ISO 7816 specifications will be added in the future.
16 * The dissection of Answer To Reset (ATR) messages was made a separate
17 * protocol so that it can be shared easily.
23 #include <epan/packet.h>
24 #include <epan/expert.h>
25 #include <epan/decode_as.h>
27 void proto_register_iso7816(void);
28 void proto_reg_handoff_iso7816(void);
30 static int proto_iso7816
;
31 static int proto_iso7816_atr
;
33 static dissector_handle_t iso7816_handle
;
34 static dissector_handle_t iso7816_atr_handle
;
36 static wmem_tree_t
*transactions
;
38 static dissector_table_t iso7816_apdu_pld_table
;
40 static int ett_iso7816
;
41 static int ett_iso7816_class
;
42 static int ett_iso7816_param
;
43 static int ett_iso7816_p1
;
44 static int ett_iso7816_p2
;
45 static int ett_iso7816_atr
;
46 static int ett_iso7816_atr_ta
;
47 static int ett_iso7816_atr_td
;
49 static int hf_iso7816_atr_init_char
;
50 static int hf_iso7816_atr_t0
;
51 static int hf_iso7816_atr_ta
;
52 /* these two fields hold the converted values Fi and Di,
53 not the binary representations FI and DI */
54 static int hf_iso7816_atr_ta1_fi
;
55 static int hf_iso7816_atr_ta1_di
;
56 static int hf_iso7816_atr_tb
;
57 static int hf_iso7816_atr_tc
;
58 static int hf_iso7816_atr_td
;
59 static int hf_iso7816_atr_next_ta_present
;
60 static int hf_iso7816_atr_next_tb_present
;
61 static int hf_iso7816_atr_next_tc_present
;
62 static int hf_iso7816_atr_next_td_present
;
63 static int hf_iso7816_atr_k
;
64 static int hf_iso7816_atr_t
;
65 static int hf_iso7816_atr_hist_bytes
;
66 static int hf_iso7816_atr_tck
;
68 static int hf_iso7816_resp_in
;
69 static int hf_iso7816_resp_to
;
70 static int hf_iso7816_cla
;
71 static int hf_iso7816_cla_sm
;
72 static int hf_iso7816_cla_channel
;
73 static int hf_iso7816_ins
;
74 static int hf_iso7816_p1
;
75 static int hf_iso7816_p2
;
76 static int hf_iso7816_lc
;
77 static int hf_iso7816_le
;
78 static int hf_iso7816_body
;
79 static int hf_iso7816_sw1
;
80 static int hf_iso7816_sw2
;
81 static int hf_iso7816_sel_file_ctrl
;
82 static int hf_iso7816_sel_file_fci_req
;
83 static int hf_iso7816_sel_file_occ
;
84 static int hf_iso7816_read_rec_ef
;
85 static int hf_iso7816_read_rec_usage
;
86 static int hf_iso7816_get_resp
;
87 static int hf_iso7816_offset_first_byte
;
88 static int hf_iso7816_rfu
;
89 static int hf_iso7816_application_data
;
91 static expert_field ei_iso7816_atr_tck_not1
;
93 #define ADDR_INTF "Interface"
94 #define ADDR_CARD "Card"
96 typedef struct _iso7816_transaction_t
{
99 uint8_t cmd_ins
; /* instruction byte in the command apdu */
100 /* no need to add the channel number,
101 the response contains no channel number to compare this to
102 and the spec explicitly prohibits interleaving of command-response
103 pairs, regardless of logical channels */
104 dissector_handle_t handle
;
105 } iso7816_transaction_t
;
107 static const value_string iso7816_atr_init_char
[] = {
108 { 0x3B, "Direct convention (A==0, Z==1, MSB==m9)" },
109 { 0x3F, "Inverse convention (A==1, Z==0, MSB==m2)" },
113 static const value_string iso7816_cla_sm
[] = {
115 { 0x01, "Proprietary SM" },
116 { 0x02, "SM, command header not authenticated" },
117 { 0x03, "SM, command header authenticated" },
121 #define INS_ERASE_BIN 0x0E
122 #define INS_VRFY 0x20
123 #define INS_MANAGE_CHANNEL 0x70
124 #define INS_EXT_AUTH 0x82
125 #define INS_GET_CHALLENGE 0x84
126 #define INS_SELECT_FILE 0xA4
127 #define INS_READ_BIN 0xB0
128 #define INS_READ_REC 0xB2
129 #define INS_GET_RESP 0xC0
130 #define INS_ENVELOPE 0xC2
131 #define INS_GET_DATA 0xCA
132 #define INS_WRITE_BIN 0xD0
133 #define INS_WRITE_REC 0xD2
134 #define INS_UPDATE_BIN 0xD6
135 #define INS_PUT_DATA 0xDA
136 #define INS_UPDATE_REC 0xDC
137 #define INS_APPEND_REC 0xE2
138 /* for our transaction tracking, not defined in the specification */
139 #define INS_INVALID 0x00
141 static const value_string iso7816_ins
[] = {
142 /* instructions defined in ISO 7816-4 */
143 { INS_ERASE_BIN
, "Erase binary" },
144 { INS_VRFY
, "Verify" },
145 { INS_MANAGE_CHANNEL
, "Manage channel" },
146 { INS_EXT_AUTH
, "External authenticate" },
147 { INS_GET_CHALLENGE
, "Get challenge" },
148 { INS_SELECT_FILE
, "Select file" },
149 { INS_READ_BIN
, "Read binary" },
150 { INS_READ_REC
, "Read record" },
151 { INS_GET_RESP
, "Get response" },
152 { INS_ENVELOPE
, "Envelope" },
153 { INS_GET_DATA
, "Get data" },
154 { INS_WRITE_BIN
, "Write binary" },
155 { INS_WRITE_REC
, "Write record" },
156 { INS_UPDATE_BIN
, "Update binary" },
157 { INS_PUT_DATA
, "Put data" },
158 { INS_UPDATE_REC
, "Update record" },
159 { INS_APPEND_REC
, "Append record" },
162 static value_string_ext iso7816_ins_ext
= VALUE_STRING_EXT_INIT(iso7816_ins
);
164 static const value_string iso7816_sel_file_ctrl
[] = {
165 { 0x00, "Select MF, DF or EF" },
166 { 0x01, "Select child DF" },
167 { 0x02, "Select EF under current DF" },
168 { 0x03, "Select parent DF of the current DF" },
169 { 0x04, "Direct selection by DF name" },
170 { 0x08, "Selection by path from MF" },
171 { 0x09, "Selection by path from current DF" },
174 static value_string_ext ext_iso7816_sel_file_ctrl
=
175 VALUE_STRING_EXT_INIT(iso7816_sel_file_ctrl
);
177 static const value_string iso7816_sel_file_fci_req
[] = {
178 { 0x00, "Return FCI, optional template" },
179 { 0x01, "Return FCP template" },
180 { 0x02, "Return FMD template" },
183 static value_string_ext ext_iso7816_sel_file_fci_req
=
184 VALUE_STRING_EXT_INIT(iso7816_sel_file_fci_req
);
186 static const value_string iso7816_sel_file_occ
[] = {
187 { 0x00, "First or only occurrence" },
188 { 0x01, "Last occurrence" },
189 { 0x02, "Next occurrence" },
190 { 0x03, "Previous occurrence" },
193 static value_string_ext ext_iso7816_sel_file_occ
=
194 VALUE_STRING_EXT_INIT(iso7816_sel_file_occ
);
196 #define READ_REC_USAGE_SINGLE 0x04
197 #define READ_REC_USAGE_START 0x05
198 static const value_string iso7816_read_rec_usage
[] = {
199 { READ_REC_USAGE_SINGLE
, "Read record P1" },
200 { READ_REC_USAGE_START
, "Read all records from P1 up to the last" },
203 static value_string_ext ext_iso7816_read_rec_usage
=
204 VALUE_STRING_EXT_INIT(iso7816_read_rec_usage
);
206 static const range_string iso7816_sw1
[] = {
207 { 0x61, 0x61, "Normal processing" },
208 { 0x62, 0x63, "Warning processing" },
209 { 0x64, 0x65, "Execution error" },
210 { 0x67, 0x6F, "Checking error" },
211 { 0x90, 0x90, "Normal processing" },
215 static const range_string iso7816_class_rvals
[] = {
216 {0x00, 0x0F, "structure and coding according to ISO/IEC 7816" },
217 {0x10, 0x7F, "reserved for future use" },
218 {0x80, 0x9F, "structure according to ISO/IEC 7816, coding is proprietary" },
219 {0xA0, 0xAF, "structure and coding according to ISO/IEC 7816 unless specified otherwise by the application context" },
220 {0xB0, 0xCF, "structure according to ISO/IEC 7816" },
221 {0xD0, 0xFE, "proprietary structure and coding" },
222 {0xFF, 0xFF, "reserved for Protocol Type Selection" },
226 static const value_string unique_or_unused
[] = {
231 static const value_string unique_max_num_available_bytes
[] = {
232 { 0, "maximum number of available bytes" },
237 uint16_t FI_to_Fi(uint8_t FI
)
254 return 0; /* 0 means RFU (reserved for future use) here */
258 uint8_t DI_to_Di(uint8_t DI
)
267 return 0; /* 0 means RFU (reserved for future use) here */
270 /* dissect TA(ta_index) */
272 dissect_iso7816_atr_ta(tvbuff_t
*tvb
, int offset
, unsigned ta_index
,
273 packet_info
*pinfo _U_
, proto_tree
*tree
)
281 ta
= tvb_get_uint8(tvb
, offset
);
282 ta_it
= proto_tree_add_uint_format(tree
, hf_iso7816_atr_ta
,
284 "Interface character TA(%d): 0x%02x", ta_index
, ta
);
285 ta_tree
= proto_item_add_subtree(ta_it
, ett_iso7816_atr_ta
);
288 FI
= (tvb_get_uint8(tvb
, offset
) & 0xF0) >> 4;
291 proto_tree_add_uint_format(ta_tree
, hf_iso7816_atr_ta1_fi
,
293 "Clock rate conversion factor Fi: %d (FI 0x%x)",
297 DI
= tvb_get_uint8(tvb
, offset
) & 0x0F;
300 proto_tree_add_uint_format(ta_tree
, hf_iso7816_atr_ta1_di
,
302 "Baud rate adjustment factor Di: %d (DI 0x%x)",
309 dissect_iso7816_atr(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
313 unsigned i
=0; /* loop index for TA(i)...TD(i) */
314 proto_item
*proto_it
;
315 proto_tree
*proto_tr
;
316 uint8_t tb
, tc
, td
, k
=0;
319 /* we need at least the initial char TS and the format char T0 */
320 if (tvb_captured_length(tvb
) < 2)
321 return 0; /* no ATR sequence */
323 init_char
= tvb_get_uint8(tvb
, offset
);
324 if (init_char
!=0x3B && init_char
!=0x3F)
327 proto_it
= proto_tree_add_protocol_format(tree
, proto_iso7816_atr
,
328 tvb
, 0, -1, "ISO 7816 ATR");
329 proto_tr
= proto_item_add_subtree(proto_it
, ett_iso7816_atr
);
331 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, NULL
, "ATR");
333 /* ISO 7816-4, section 4 indicates that concatenations are big endian */
334 proto_tree_add_item(proto_tr
, hf_iso7816_atr_init_char
,
335 tvb
, offset
, 1, ENC_BIG_ENDIAN
);
342 /* for i==0, this is the T0 byte, otherwise it's the TD(i) byte
343 in each loop, we dissect T0/TD(i) and TA(i+1), TB(i+1), TC(i+1) */
344 td
= tvb_get_uint8(tvb
, offset
);
346 td_it
= proto_tree_add_item(proto_tr
, hf_iso7816_atr_t0
,
347 tvb
, offset
, 1, ENC_BIG_ENDIAN
);
350 td_it
= proto_tree_add_uint_format(proto_tr
, hf_iso7816_atr_td
,
352 "Interface character TD(%d): 0x%02x", i
, td
);
354 td_tree
= proto_item_add_subtree(td_it
, ett_iso7816_atr_td
);
356 proto_tree_add_boolean_format(td_tree
, hf_iso7816_atr_next_ta_present
,
357 tvb
, offset
, 1, td
&0x10,
358 "TA(%d) present: %s", i
+1, td
&0x10 ? "True" : "False");
359 proto_tree_add_boolean_format(td_tree
, hf_iso7816_atr_next_tb_present
,
360 tvb
, offset
, 1, td
&0x20,
361 "TB(%d) present: %s", i
+1, td
&0x20 ? "True" : "False");
362 proto_tree_add_boolean_format(td_tree
, hf_iso7816_atr_next_tc_present
,
363 tvb
, offset
, 1, td
&0x40,
364 "TC(%d) present: %s", i
+1, td
&0x40 ? "True" : "False");
365 proto_tree_add_boolean_format(td_tree
, hf_iso7816_atr_next_td_present
,
366 tvb
, offset
, 1, td
&0x80,
367 "TD(%d) present: %s", i
+1, td
&0x80 ? "True" : "False");
369 col_append_sep_fstr(pinfo
->cinfo
, COL_INFO
, NULL
,
370 "TA(%d)=%s TB(%d)=%s TC(%d)=%s TD(%d)=%s",
371 i
+1, td
&0x10 ? "True" : "False",
372 i
+1, td
&0x20 ? "True" : "False",
373 i
+1, td
&0x40 ? "True" : "False",
374 i
+1, td
&0x80 ? "True" : "False");
377 k
= td
&0x0F; /* number of historical bytes */
378 proto_tree_add_item(td_tree
, hf_iso7816_atr_k
,
379 tvb
, offset
, 1, ENC_BIG_ENDIAN
);
382 proto_tree_add_item(td_tree
, hf_iso7816_atr_t
,
383 tvb
, offset
, 1, ENC_BIG_ENDIAN
);
388 /* we read TA(i+1), see comment above */
389 dissect_iso7816_atr_ta(tvb
, offset
, i
+1, pinfo
, proto_tr
);
393 tb
= tvb_get_uint8(tvb
, offset
);
394 proto_tree_add_uint_format(proto_tr
, hf_iso7816_atr_tb
,
396 "Interface character TB(%d): 0x%02x", i
+1, tb
);
400 tc
= tvb_get_uint8(tvb
, offset
);
401 proto_tree_add_uint_format(proto_tr
, hf_iso7816_atr_tc
,
403 "Interface character TC(%d): 0x%02x", i
+1, tc
);
411 proto_tree_add_item(proto_tr
, hf_iso7816_atr_hist_bytes
,
412 tvb
, offset
, k
, ENC_NA
);
416 tck_len
= tvb_reported_length_remaining(tvb
, offset
);
417 /* tck is either absent or exactly one byte */
419 proto_tree_add_item(proto_tr
, hf_iso7816_atr_tck
,
420 tvb
, offset
, 1, ENC_BIG_ENDIAN
);
423 else if (tck_len
>1) {
424 proto_tree_add_expert(proto_tr
, pinfo
, &ei_iso7816_atr_tck_not1
,
425 tvb
, offset
, tck_len
);
428 proto_item_set_len(proto_it
, offset
);
432 /* Dissect the class byte. Return 1 if the APDU's structure and coding
433 adhere to ISO 7816. In this case, we can dissect the rest of the
434 APDU. Otherwise, return -1. We may then pass the APDU to other
437 dissect_iso7816_class(tvbuff_t
*tvb
, int offset
,
438 packet_info
*pinfo _U_
, proto_tree
*tree
)
440 proto_item
*class_item
;
441 proto_tree
*class_tree
;
444 class_item
= proto_tree_add_item(tree
, hf_iso7816_cla
,
445 tvb
, offset
, 1, ENC_BIG_ENDIAN
);
446 class_tree
= proto_item_add_subtree(class_item
, ett_iso7816_class
);
448 dev_class
= tvb_get_uint8(tvb
, offset
);
450 if (dev_class
>=0x10 && dev_class
<=0x7F) {
451 /* these values are RFU. */
455 if (dev_class
>=0xD0 && dev_class
<=0xFE) {
456 /* proprietary structure and coding */
460 if (dev_class
==0xFF) {
461 /* reserved for Protocol Type Selection */
465 /* If we made it this far, the structrue of the APDU is compliant
468 proto_tree_add_item(class_tree
, hf_iso7816_cla_sm
,
469 tvb
, offset
, 1, ENC_BIG_ENDIAN
);
471 proto_tree_add_item(class_tree
, hf_iso7816_cla_channel
,
472 tvb
, offset
, 1, ENC_BIG_ENDIAN
);
474 if (dev_class
>=0x80 && dev_class
<=0x9F) {
475 /* structure according to ISO 7816, coding is proprietary */
479 if (dev_class
>=0xB0 && dev_class
<=0xCF) {
480 /* structure according to ISO 7816 */
484 /* both structure and coding according to ISO 7816 */
488 /* dissect the parameters p1 and p2
489 return number of dissected bytes or -1 for error */
491 dissect_iso7816_params(uint8_t ins
, tvbuff_t
*tvb
, int offset
,
492 packet_info
*pinfo _U_
, proto_tree
*tree
)
494 int offset_start
, p1_offset
, p2_offset
;
495 proto_tree
*params_tree
;
497 proto_item
*p1_it
= NULL
, *p2_it
= NULL
;
498 proto_tree
*p1_tree
= NULL
, *p2_tree
= NULL
;
499 proto_item
*p1_p2_it
= NULL
;
501 uint32_t ef
, read_rec_usage
;
503 offset_start
= offset
;
505 params_tree
= proto_tree_add_subtree(tree
, tvb
, offset_start
, 2,
506 ett_iso7816_param
, NULL
, "Parameters");
508 p1
= tvb_get_uint8(tvb
,offset
);
509 p1_it
= proto_tree_add_item(params_tree
, hf_iso7816_p1
, tvb
,
510 offset
, 1, ENC_BIG_ENDIAN
);
513 p2
= tvb_get_uint8(tvb
,offset
);
514 p2_it
= proto_tree_add_item(params_tree
, hf_iso7816_p2
,
515 tvb
, offset
, 1, ENC_BIG_ENDIAN
);
523 proto_item_append_text(p1_it
,
524 " (reference of the algorithm on the card)");
526 proto_item_append_text(p2_it
, " (reference of the secret)");
528 case INS_SELECT_FILE
:
529 proto_item_append_text(p1_it
, " (selection control)");
530 p1_tree
= proto_item_add_subtree(p1_it
, ett_iso7816_p1
);
531 proto_tree_add_item(p1_tree
, hf_iso7816_sel_file_ctrl
,
532 tvb
, p1_offset
, 1, ENC_BIG_ENDIAN
);
533 proto_item_append_text(p2_it
, " (selection options)");
534 p2_tree
= proto_item_add_subtree(p2_it
, ett_iso7816_p2
);
535 proto_tree_add_item(p2_tree
, hf_iso7816_sel_file_fci_req
,
536 tvb
, p2_offset
, 1, ENC_BIG_ENDIAN
);
537 proto_tree_add_item(p2_tree
, hf_iso7816_sel_file_occ
,
538 tvb
, p2_offset
, 1, ENC_BIG_ENDIAN
);
542 /* XXX - b5-b1 of P1 == short ef identifier for the selected file */
543 /* XXX - P2 == offset for the read */
546 p1_p2_it
= proto_tree_add_uint(params_tree
, hf_iso7816_offset_first_byte
,
547 tvb
, offset_start
, offset
-offset_start
, P1P2
);
548 col_append_sep_fstr(pinfo
->cinfo
, COL_INFO
, NULL
,
553 proto_item_append_text(p1_it
, " (record number)");
554 proto_item_append_text(p2_it
, " (reference control)");
555 p2_tree
= proto_item_add_subtree(p2_it
, ett_iso7816_p2
);
556 proto_tree_add_item_ret_uint(p2_tree
, hf_iso7816_read_rec_ef
,
557 tvb
, p2_offset
, 1, ENC_BIG_ENDIAN
, &ef
);
558 col_append_sep_fstr(pinfo
->cinfo
, COL_INFO
, NULL
, "EF %d", ef
);
559 proto_tree_add_item_ret_uint(p2_tree
, hf_iso7816_read_rec_usage
,
560 tvb
, p2_offset
, 1, ENC_BIG_ENDIAN
, &read_rec_usage
);
561 if (read_rec_usage
== READ_REC_USAGE_SINGLE
) {
563 pinfo
->cinfo
, COL_INFO
, NULL
, "record %d", p1
);
567 p1_p2_it
= proto_tree_add_uint_format(params_tree
, hf_iso7816_get_resp
,
568 tvb
, offset_start
, offset
-offset_start
, P1P2
,
569 "Both should be 0x00, other values are RFU");
572 if (P1P2
<=0x003F || (0x0300<=P1P2
&& P1P2
<=0x3FFF)) {
573 p1_p2_it
= proto_tree_add_uint(params_tree
, hf_iso7816_rfu
,
574 tvb
, offset_start
, offset
-offset_start
, P1P2
);
576 else if (0x0100<=P1P2
&& P1P2
<=0x01FF) {
577 p1_p2_it
= proto_tree_add_uint(params_tree
, hf_iso7816_application_data
,
578 tvb
, offset_start
, offset
-offset_start
, P1P2
);
585 proto_item_set_generated(p1_p2_it
);
592 tvbuff_t
*tvb
, int offset
, packet_info
*pinfo _U_
, proto_tree
*tree
)
594 proto_tree_add_item(tree
, hf_iso7816_le
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
601 dissect_iso7816_cmd_apdu(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
603 iso7816_transaction_t
*iso7816_trans
= NULL
;
604 proto_item
*trans_ti
= NULL
;
612 if (PINFO_FD_VISITED(pinfo
)) {
613 iso7816_trans
= (iso7816_transaction_t
*)wmem_tree_lookup32(
614 transactions
, pinfo
->num
);
615 if (iso7816_trans
&& iso7816_trans
->cmd_frame
==pinfo
->num
&&
616 iso7816_trans
->resp_frame
!=0) {
617 trans_ti
= proto_tree_add_uint_format(tree
, hf_iso7816_resp_in
,
618 NULL
, 0, 0, iso7816_trans
->resp_frame
,
619 "Response in frame %d", iso7816_trans
->resp_frame
);
620 proto_item_set_generated(trans_ti
);
625 iso7816_trans
= wmem_new(wmem_file_scope(), iso7816_transaction_t
);
626 iso7816_trans
->cmd_frame
= pinfo
->num
;
627 iso7816_trans
->resp_frame
= 0;
628 iso7816_trans
->cmd_ins
= INS_INVALID
;
629 iso7816_trans
->handle
= NULL
;
631 wmem_tree_insert32(transactions
,
632 iso7816_trans
->cmd_frame
, (void *)iso7816_trans
);
636 ret
= dissect_iso7816_class(tvb
, offset
, pinfo
, tree
);
638 /* the class byte says that the remaining APDU is not
642 iso7816_trans
->handle
=
643 dissector_get_payload_handle(iso7816_apdu_pld_table
);
644 if (iso7816_trans
->handle
!= NULL
) {
645 ret
= call_dissector(iso7816_trans
->handle
, tvb
, pinfo
, tree
);
647 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, NULL
,
648 "Command APDU using proprietary format");
649 return 1; /* we only dissected the class byte */
658 ins
= tvb_get_uint8(tvb
, offset
);
659 proto_tree_add_item(tree
, hf_iso7816_ins
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
660 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, NULL
,
661 val_to_str_ext_const(ins
, &iso7816_ins_ext
, "Unknown instruction"));
663 /* if we just created a new transaction, we can now fill in the cmd id */
664 if (iso7816_trans
&& iso7816_trans
->cmd_ins
==INS_INVALID
)
665 iso7816_trans
->cmd_ins
= ins
;
667 ret
= dissect_iso7816_params(ins
, tvb
, offset
, pinfo
, tree
);
671 /* for now, we support only short length fields
672 based on infos from the ATR, we could support extended length fields too */
673 body_len
= tvb_reported_length_remaining(tvb
, offset
);
675 /* nothing to do for body_len==0 */
677 offset
+= dissect_iso7816_le(tvb
, offset
, pinfo
, tree
);
679 else if (body_len
>1) {
680 lc
= tvb_get_uint8(tvb
, offset
);
682 tree
, hf_iso7816_lc
, tvb
, offset
, 1, ENC_BIG_ENDIAN
);
685 proto_tree_add_item(tree
, hf_iso7816_body
, tvb
, offset
, lc
, ENC_NA
);
688 if (tvb_reported_length_remaining(tvb
, offset
)>0) {
689 offset
+= dissect_iso7816_le(tvb
, offset
, pinfo
, tree
);
697 dissect_iso7816_resp_apdu(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
)
699 iso7816_transaction_t
*iso7816_trans
;
700 proto_item
*trans_ti
= NULL
;
701 const char *cmd_ins_str
;
705 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, NULL
, "Response APDU");
708 /* receive the largest key that is less than or equal to our frame
710 iso7816_trans
= (iso7816_transaction_t
*)wmem_tree_lookup32_le(
711 transactions
, pinfo
->num
);
713 if (iso7816_trans
->resp_frame
==0) {
714 /* there's a pending request, this packet is the response */
715 iso7816_trans
->resp_frame
= pinfo
->num
;
718 if (iso7816_trans
->resp_frame
== pinfo
->num
) {
719 /* we found the request that corresponds to our response */
720 cmd_ins_str
= val_to_str_const(iso7816_trans
->cmd_ins
,
721 iso7816_ins
, "Unknown instruction");
722 trans_ti
= proto_tree_add_uint_format(tree
, hf_iso7816_resp_to
,
723 NULL
, 0, 0, iso7816_trans
->cmd_frame
,
724 "Response to frame %d (%s)",
725 iso7816_trans
->cmd_frame
, cmd_ins_str
);
726 proto_item_set_generated(trans_ti
);
728 col_append_sep_fstr(pinfo
->cinfo
, COL_INFO
, " ",
729 "(to %s)", cmd_ins_str
);
732 if (iso7816_trans
->handle
!= NULL
)
733 call_dissector(iso7816_trans
->handle
, tvb
, pinfo
, tree
);
737 /* - 2 bytes SW1, SW2 */
738 body_len
= tvb_reported_length_remaining(tvb
, offset
) - 2;
741 proto_tree_add_item(tree
, hf_iso7816_body
,
742 tvb
, offset
, body_len
, ENC_NA
);
746 if (tvb_reported_length_remaining(tvb
, offset
) >= 2) {
747 proto_tree_add_item(tree
, hf_iso7816_sw1
,
748 tvb
, offset
, 1, ENC_BIG_ENDIAN
);
750 proto_tree_add_item(tree
, hf_iso7816_sw2
,
751 tvb
, offset
, 1, ENC_BIG_ENDIAN
);
759 dissect_iso7816(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
763 proto_tree
*iso7816_tree
;
766 if (pinfo
->p2p_dir
!=P2P_DIR_SENT
&& pinfo
->p2p_dir
!=P2P_DIR_RECV
)
769 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "ISO 7816");
770 col_clear(pinfo
->cinfo
, COL_INFO
);
772 tree_ti
= proto_tree_add_protocol_format(tree
, proto_iso7816
,
773 tvb
, 0, tvb_reported_length(tvb
), "ISO 7816");
774 iso7816_tree
= proto_item_add_subtree(tree_ti
, ett_iso7816
);
776 /* per our definition, sent/received is from the perspective of the interface
777 i.e sent is from interface to card, received is from card to interface */
778 if (pinfo
->p2p_dir
==P2P_DIR_SENT
) {
779 set_address(&pinfo
->src
, AT_STRINGZ
,
780 (int)strlen(ADDR_INTF
)+1, ADDR_INTF
);
781 set_address(&pinfo
->dst
, AT_STRINGZ
,
782 (int)strlen(ADDR_CARD
)+1, ADDR_CARD
);
783 proto_item_append_text(tree_ti
, " Command APDU");
784 offset
= dissect_iso7816_cmd_apdu(tvb
, pinfo
, iso7816_tree
);
786 else if (pinfo
->p2p_dir
==P2P_DIR_RECV
) {
787 set_address(&pinfo
->src
, AT_STRINGZ
,
788 (int)strlen(ADDR_CARD
)+1, ADDR_CARD
);
789 set_address(&pinfo
->dst
, AT_STRINGZ
,
790 (int)strlen(ADDR_INTF
)+1, ADDR_INTF
);
792 if (iso7816_atr_handle
) {
793 offset
= call_dissector_only(iso7816_atr_handle
,
794 tvb
, pinfo
, iso7816_tree
, NULL
);
799 proto_item_append_text(tree_ti
, " Response APDU");
800 offset
= dissect_iso7816_resp_apdu(tvb
, pinfo
, iso7816_tree
);
808 proto_register_iso7816(void)
810 static hf_register_info hf
[] = {
811 { &hf_iso7816_atr_init_char
,
812 { "Initial character", "iso7816.atr.init_char",
813 FT_UINT8
, BASE_HEX
, VALS(iso7816_atr_init_char
), 0, NULL
, HFILL
}
815 { &hf_iso7816_atr_t0
,
816 { "Format character T0", "iso7816.atr.t0",
817 FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
819 { &hf_iso7816_atr_ta
,
820 { "Interface character TA(i)", "iso7816.atr.ta",
821 FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
823 { &hf_iso7816_atr_ta1_fi
,
824 { "Fi", "iso7816.atr.ta1.fi",
825 FT_UINT16
, BASE_DEC
, NULL
, 0, NULL
, HFILL
}
827 { &hf_iso7816_atr_ta1_di
,
828 { "Di", "iso7816.atr.ta1.di",
829 FT_UINT8
, BASE_DEC
, NULL
, 0, NULL
, HFILL
}
831 { &hf_iso7816_atr_tb
,
832 { "Interface character TB(i)", "iso7816.atr.tb",
833 FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
835 { &hf_iso7816_atr_tc
,
836 { "Interface character TC(i)", "iso7816.atr.tc",
837 FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
839 { &hf_iso7816_atr_td
,
840 { "Interface character TD(i)", "iso7816.atr.td",
841 FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
843 { &hf_iso7816_atr_next_ta_present
,
844 { "TA(i+1) present", "iso7816.atr.next_ta_present",
845 FT_BOOLEAN
, 8, NULL
, 0x10, NULL
, HFILL
}
847 { &hf_iso7816_atr_next_tb_present
,
848 { "TB(i+1) present", "iso7816.atr.next_tb_present",
849 FT_BOOLEAN
, 8, NULL
, 0x20, NULL
, HFILL
}
851 { &hf_iso7816_atr_next_tc_present
,
852 { "TC(i+1) present", "iso7816.atr.next_tc_present",
853 FT_BOOLEAN
, 8, NULL
, 0x40, NULL
, HFILL
}
855 { &hf_iso7816_atr_next_td_present
,
856 { "TD(i+1) present", "iso7816.atr.next_td_present",
857 FT_BOOLEAN
, 8, NULL
, 0x80, NULL
, HFILL
}
860 { "Number K of historical bytes", "iso7816.atr.k",
861 FT_UINT8
, BASE_DEC
, NULL
, 0x0F, NULL
, HFILL
}
864 { "Protocol reference T", "iso7816.atr.t",
865 FT_UINT8
, BASE_HEX
, NULL
, 0x0F, NULL
, HFILL
}
867 { &hf_iso7816_atr_hist_bytes
,
868 { "Historical bytes", "iso7816.atr.historical_bytes",
869 FT_BYTES
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}
871 { &hf_iso7816_atr_tck
,
872 { "Check character TCK", "iso7816.atr.tck",
873 FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
875 { &hf_iso7816_resp_in
,
876 { "Response In", "iso7816.resp_in",
877 FT_FRAMENUM
, BASE_NONE
, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE
), 0x0,
878 "The response to this command is in this frame", HFILL
}
880 { &hf_iso7816_resp_to
,
881 { "Response To", "iso7816.resp_to",
882 FT_FRAMENUM
, BASE_NONE
, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST
), 0x0,
883 "This is the response to the command in this frame", HFILL
}
886 { "Class", "iso7816.apdu.cla",
887 FT_UINT8
, BASE_HEX
|BASE_RANGE_STRING
, RVALS(iso7816_class_rvals
), 0, NULL
, HFILL
}
889 { &hf_iso7816_cla_sm
,
890 { "Secure Messaging", "iso7816.apdu.cla.sm",
891 FT_UINT8
, BASE_HEX
, VALS(iso7816_cla_sm
), 0x0C, NULL
, HFILL
}
893 { &hf_iso7816_cla_channel
,
894 { "Logical channel number", "iso7816.apdu.cla.channel",
895 FT_UINT8
, BASE_HEX
|BASE_SPECIAL_VALS
, VALS(unique_or_unused
), 0x03, NULL
, HFILL
}
898 { "Instruction", "iso7816.apdu.ins",
899 FT_UINT8
, BASE_HEX
| BASE_EXT_STRING
, &iso7816_ins_ext
, 0, NULL
, HFILL
}
902 { "Parameter 1", "iso7816.apdu.p1",
903 FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
906 { "Parameter 2", "iso7816.apdu.p2",
907 FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
910 { "Length field Lc", "iso7816.apdu.lc",
911 FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
914 { "Expected response length Le", "iso7816.apdu.le",
915 FT_UINT8
, BASE_HEX
|BASE_SPECIAL_VALS
, VALS(unique_max_num_available_bytes
), 0, NULL
, HFILL
}
918 { "APDU Body", "iso7816.apdu.body",
919 FT_BYTES
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}
922 { "Status Word SW1", "iso7816.apdu.sw1", FT_UINT8
,
923 BASE_RANGE_STRING
|BASE_HEX
, RVALS(iso7816_sw1
), 0, NULL
, HFILL
}
926 { "Status Word SW2", "iso7816.apdu.sw2",
927 FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
929 { &hf_iso7816_sel_file_ctrl
,
930 { "Selection control", "iso7816.apdu.select_file.ctrl",
931 FT_UINT8
, BASE_HEX
| BASE_EXT_STRING
,
932 &ext_iso7816_sel_file_ctrl
, 0, NULL
, HFILL
}
934 { &hf_iso7816_sel_file_fci_req
,
935 { "File control information request", "iso7816.apdu.select_file.fci_req",
936 FT_UINT8
, BASE_HEX
| BASE_EXT_STRING
,
937 &ext_iso7816_sel_file_fci_req
, 0x0C, NULL
, HFILL
}
939 { &hf_iso7816_sel_file_occ
,
940 { "Occurrence", "iso7816.apdu.select_file.occurrence",
941 FT_UINT8
, BASE_HEX
| BASE_EXT_STRING
,
942 &ext_iso7816_sel_file_occ
, 0x03, NULL
, HFILL
}
944 { &hf_iso7816_read_rec_ef
,
945 { "Short EF identifier", "iso7816.apdu.read_rec.ef",
946 FT_UINT8
, BASE_HEX
, NULL
, 0xF8, NULL
, HFILL
}
948 { &hf_iso7816_read_rec_usage
,
949 { "Usage", "iso7816.apdu.read_rec.usage",
950 FT_UINT8
, BASE_HEX
| BASE_EXT_STRING
,
951 &ext_iso7816_read_rec_usage
, 0x07, NULL
, HFILL
}
953 { &hf_iso7816_offset_first_byte
,
954 { "Offset of the first byte to read", "iso7816.offset_first_byte",
955 FT_UINT16
, BASE_DEC
, NULL
, 0, NULL
, HFILL
}
957 { &hf_iso7816_get_resp
,
958 { "GetResp", "iso7816.get_resp",
959 FT_UINT16
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
962 { "RFU", "iso7816.rfu",
963 FT_UINT16
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
965 { &hf_iso7816_application_data
,
966 { "Application data (proprietary coding)", "iso7816.application_data",
967 FT_UINT16
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}
970 static int *ett
[] = {
981 static ei_register_info ei
[] = {
982 { &ei_iso7816_atr_tck_not1
, { "iso7816.atr.tck.not1", PI_PROTOCOL
, PI_WARN
, "TCK byte must either be absent or exactly one byte", EXPFILL
}}
985 expert_module_t
* expert_iso7816
;
987 proto_iso7816
= proto_register_protocol("ISO/IEC 7816", "ISO 7816", "iso7816");
988 proto_register_field_array(proto_iso7816
, hf
, array_length(hf
));
989 proto_register_subtree_array(ett
, array_length(ett
));
990 expert_iso7816
= expert_register_protocol(proto_iso7816
);
991 expert_register_field_array(expert_iso7816
, ei
, array_length(ei
));
993 iso7816_handle
= register_dissector("iso7816", dissect_iso7816
, proto_iso7816
);
995 transactions
= wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
997 proto_iso7816_atr
= proto_register_protocol_in_name_only("ISO/IEC 7816-3", "ISO 7816-3", "iso7816.atr", proto_iso7816
, FT_PROTOCOL
);
998 iso7816_atr_handle
= register_dissector("iso7816.atr", dissect_iso7816_atr
, proto_iso7816_atr
);
1000 iso7816_apdu_pld_table
=
1001 register_decode_as_next_proto(proto_iso7816
,
1002 "iso7816.apdu_payload",
1003 "ISO7816 proprietary APDU dissector", NULL
);
1007 void proto_reg_handoff_iso7816(void)
1009 dissector_add_for_decode_as("usbccid.subdissector", iso7816_handle
);
1010 dissector_add_for_decode_as("iso14443.subdissector", iso7816_handle
);
1015 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1020 * indent-tabs-mode: nil
1023 * vi: set shiftwidth=4 tabstop=8 expandtab:
1024 * :indentSize=4:tabSize=8:noTabs=true: