/src/wireshark/epan/dissectors/packet-iso7816.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* packet-iso7816.c |
2 | | * Routines for packet dissection of generic ISO 7816 smart card messages |
3 | | * Copyright 2012-2013 by Martin Kaiser <martin@kaiser.cx> |
4 | | * |
5 | | * Wireshark - Network traffic analyzer |
6 | | * By Gerald Combs <gerald@wireshark.org> |
7 | | * Copyright 1998 Gerald Combs |
8 | | * |
9 | | * SPDX-License-Identifier: GPL-2.0-or-later |
10 | | */ |
11 | | |
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. |
15 | | * |
16 | | * The dissection of Answer To Reset (ATR) messages was made a separate |
17 | | * protocol so that it can be shared easily. |
18 | | */ |
19 | | |
20 | | |
21 | | #include "config.h" |
22 | | |
23 | | #include <epan/packet.h> |
24 | | #include <epan/expert.h> |
25 | | #include <epan/decode_as.h> |
26 | | |
27 | | void proto_register_iso7816(void); |
28 | | void proto_reg_handoff_iso7816(void); |
29 | | |
30 | | static int proto_iso7816; |
31 | | static int proto_iso7816_atr; |
32 | | |
33 | | static dissector_handle_t iso7816_handle; |
34 | | static dissector_handle_t iso7816_atr_handle; |
35 | | |
36 | | static wmem_tree_t *transactions; |
37 | | |
38 | | static dissector_table_t iso7816_apdu_pld_table; |
39 | | |
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; |
48 | | |
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; |
67 | | |
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; |
90 | | |
91 | | static expert_field ei_iso7816_atr_tck_not1; |
92 | | |
93 | 0 | #define ADDR_INTF "Interface" |
94 | 0 | #define ADDR_CARD "Card" |
95 | | |
96 | | typedef struct _iso7816_transaction_t { |
97 | | uint32_t cmd_frame; |
98 | | uint32_t resp_frame; |
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; |
106 | | |
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)" }, |
110 | | { 0, NULL } |
111 | | }; |
112 | | |
113 | | static const value_string iso7816_cla_sm[] = { |
114 | | { 0x00, "No SM" }, |
115 | | { 0x01, "Proprietary SM" }, |
116 | | { 0x02, "SM, command header not authenticated" }, |
117 | | { 0x03, "SM, command header authenticated" }, |
118 | | { 0, NULL } |
119 | | }; |
120 | | |
121 | | #define INS_ERASE_BIN 0x0E |
122 | | #define INS_VRFY 0x20 |
123 | | #define INS_MANAGE_CHANNEL 0x70 |
124 | 0 | #define INS_EXT_AUTH 0x82 |
125 | | #define INS_GET_CHALLENGE 0x84 |
126 | 0 | #define INS_SELECT_FILE 0xA4 |
127 | 0 | #define INS_READ_BIN 0xB0 |
128 | 0 | #define INS_READ_REC 0xB2 |
129 | 0 | #define INS_GET_RESP 0xC0 |
130 | | #define INS_ENVELOPE 0xC2 |
131 | 0 | #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 | 0 | #define INS_INVALID 0x00 |
140 | | |
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" }, |
160 | | { 0, NULL } |
161 | | }; |
162 | | static value_string_ext iso7816_ins_ext = VALUE_STRING_EXT_INIT(iso7816_ins); |
163 | | |
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" }, |
172 | | { 0, NULL } |
173 | | }; |
174 | | static value_string_ext ext_iso7816_sel_file_ctrl = |
175 | | VALUE_STRING_EXT_INIT(iso7816_sel_file_ctrl); |
176 | | |
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" }, |
181 | | { 0, NULL } |
182 | | }; |
183 | | static value_string_ext ext_iso7816_sel_file_fci_req = |
184 | | VALUE_STRING_EXT_INIT(iso7816_sel_file_fci_req); |
185 | | |
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" }, |
191 | | { 0, NULL } |
192 | | }; |
193 | | static value_string_ext ext_iso7816_sel_file_occ = |
194 | | VALUE_STRING_EXT_INIT(iso7816_sel_file_occ); |
195 | | |
196 | 0 | #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" }, |
201 | | { 0, NULL } |
202 | | }; |
203 | | static value_string_ext ext_iso7816_read_rec_usage = |
204 | | VALUE_STRING_EXT_INIT(iso7816_read_rec_usage); |
205 | | |
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" }, |
212 | | { 0,0, NULL } |
213 | | }; |
214 | | |
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" }, |
223 | | {0, 0, NULL} |
224 | | }; |
225 | | |
226 | | static const value_string unique_or_unused[] = { |
227 | | { 0, "or unused" }, |
228 | | { 0, NULL } |
229 | | }; |
230 | | |
231 | | static const value_string unique_max_num_available_bytes[] = { |
232 | | { 0, "maximum number of available bytes" }, |
233 | | { 0, NULL } |
234 | | }; |
235 | | |
236 | | static inline |
237 | | uint16_t FI_to_Fi(uint8_t FI) |
238 | 12 | { |
239 | 12 | if (FI<=1) |
240 | 2 | return 372; |
241 | 10 | else if (FI<=6) |
242 | 0 | return (FI-1) * 372; |
243 | 10 | else if (FI==9) |
244 | 1 | return 512; |
245 | 9 | else if (FI==10) |
246 | 0 | return 768; |
247 | 9 | else if (FI==11) |
248 | 0 | return 1024; |
249 | 9 | else if (FI==12) |
250 | 8 | return 1536; |
251 | 1 | else if (FI==13) |
252 | 1 | return 2048; |
253 | | |
254 | 0 | return 0; /* 0 means RFU (reserved for future use) here */ |
255 | 12 | } |
256 | | |
257 | | static inline |
258 | | uint8_t DI_to_Di(uint8_t DI) |
259 | 12 | { |
260 | 12 | if (DI>=1 && DI<=6) |
261 | 2 | return 1 << (DI-1); |
262 | 10 | else if (DI==8) |
263 | 1 | return 12; |
264 | 9 | else if (DI==9) |
265 | 0 | return 20; |
266 | | |
267 | 9 | return 0; /* 0 means RFU (reserved for future use) here */ |
268 | 12 | } |
269 | | |
270 | | /* dissect TA(ta_index) */ |
271 | | static void |
272 | | dissect_iso7816_atr_ta(tvbuff_t *tvb, int offset, unsigned ta_index, |
273 | | packet_info *pinfo _U_, proto_tree *tree) |
274 | 79 | { |
275 | 79 | uint8_t ta, FI, DI; |
276 | 79 | uint16_t Fi; |
277 | 79 | uint8_t Di; |
278 | 79 | proto_item *ta_it; |
279 | 79 | proto_tree *ta_tree; |
280 | | |
281 | 79 | ta = tvb_get_uint8(tvb, offset); |
282 | 79 | ta_it = proto_tree_add_uint_format(tree, hf_iso7816_atr_ta, |
283 | 79 | tvb, offset, 1, ta, |
284 | 79 | "Interface character TA(%d): 0x%02x", ta_index, ta); |
285 | 79 | ta_tree = proto_item_add_subtree(ta_it, ett_iso7816_atr_ta); |
286 | | |
287 | 79 | if (ta_index==1) { |
288 | 12 | FI = (tvb_get_uint8(tvb, offset) & 0xF0) >> 4; |
289 | 12 | Fi = FI_to_Fi(FI); |
290 | 12 | if (Fi>0) { |
291 | 12 | proto_tree_add_uint_format(ta_tree, hf_iso7816_atr_ta1_fi, |
292 | 12 | tvb, offset, 1, Fi, |
293 | 12 | "Clock rate conversion factor Fi: %d (FI 0x%x)", |
294 | 12 | Fi, FI); |
295 | 12 | } |
296 | | |
297 | 12 | DI = tvb_get_uint8(tvb, offset) & 0x0F; |
298 | 12 | Di = DI_to_Di(DI); |
299 | 12 | if (Di>0) { |
300 | 3 | proto_tree_add_uint_format(ta_tree, hf_iso7816_atr_ta1_di, |
301 | 3 | tvb, offset, 1, Di, |
302 | 3 | "Baud rate adjustment factor Di: %d (DI 0x%x)", |
303 | 3 | Di, DI); |
304 | 3 | } |
305 | 12 | } |
306 | 79 | } |
307 | | |
308 | | static int |
309 | | dissect_iso7816_atr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) |
310 | 17 | { |
311 | 17 | int offset=0; |
312 | 17 | uint8_t init_char; |
313 | 17 | unsigned i=0; /* loop index for TA(i)...TD(i) */ |
314 | 17 | proto_item *proto_it; |
315 | 17 | proto_tree *proto_tr; |
316 | 17 | uint8_t tb, tc, td, k=0; |
317 | 17 | int tck_len; |
318 | | |
319 | | /* we need at least the initial char TS and the format char T0 */ |
320 | 17 | if (tvb_captured_length(tvb) < 2) |
321 | 0 | return 0; /* no ATR sequence */ |
322 | | |
323 | 17 | init_char = tvb_get_uint8(tvb, offset); |
324 | 17 | if (init_char!=0x3B && init_char!=0x3F) |
325 | 4 | return 0; |
326 | | |
327 | 13 | proto_it = proto_tree_add_protocol_format(tree, proto_iso7816_atr, |
328 | 13 | tvb, 0, -1, "ISO 7816 ATR"); |
329 | 13 | proto_tr = proto_item_add_subtree(proto_it, ett_iso7816_atr); |
330 | | |
331 | 13 | col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "ATR"); |
332 | | |
333 | | /* ISO 7816-4, section 4 indicates that concatenations are big endian */ |
334 | 13 | proto_tree_add_item(proto_tr, hf_iso7816_atr_init_char, |
335 | 13 | tvb, offset, 1, ENC_BIG_ENDIAN); |
336 | 13 | offset++; |
337 | | |
338 | 107 | do { |
339 | 107 | proto_item *td_it; |
340 | 107 | proto_tree *td_tree; |
341 | | |
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 | 107 | td = tvb_get_uint8(tvb, offset); |
345 | 107 | if (i==0) { |
346 | 13 | td_it = proto_tree_add_item(proto_tr, hf_iso7816_atr_t0, |
347 | 13 | tvb, offset, 1, ENC_BIG_ENDIAN); |
348 | 13 | } |
349 | 94 | else { |
350 | 94 | td_it = proto_tree_add_uint_format(proto_tr, hf_iso7816_atr_td, |
351 | 94 | tvb, offset, 1, td, |
352 | 94 | "Interface character TD(%d): 0x%02x", i, td); |
353 | 94 | } |
354 | 107 | td_tree = proto_item_add_subtree(td_it, ett_iso7816_atr_td); |
355 | | |
356 | 107 | proto_tree_add_boolean_format(td_tree, hf_iso7816_atr_next_ta_present, |
357 | 107 | tvb, offset, 1, td&0x10, |
358 | 107 | "TA(%d) present: %s", i+1, td&0x10 ? "True" : "False"); |
359 | 107 | proto_tree_add_boolean_format(td_tree, hf_iso7816_atr_next_tb_present, |
360 | 107 | tvb, offset, 1, td&0x20, |
361 | 107 | "TB(%d) present: %s", i+1, td&0x20 ? "True" : "False"); |
362 | 107 | proto_tree_add_boolean_format(td_tree, hf_iso7816_atr_next_tc_present, |
363 | 107 | tvb, offset, 1, td&0x40, |
364 | 107 | "TC(%d) present: %s", i+1, td&0x40 ? "True" : "False"); |
365 | 107 | proto_tree_add_boolean_format(td_tree, hf_iso7816_atr_next_td_present, |
366 | 107 | tvb, offset, 1, td&0x80, |
367 | 107 | "TD(%d) present: %s", i+1, td&0x80 ? "True" : "False"); |
368 | | |
369 | 107 | col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, |
370 | 107 | "TA(%d)=%s TB(%d)=%s TC(%d)=%s TD(%d)=%s", |
371 | 107 | i+1, td&0x10 ? "True" : "False", |
372 | 107 | i+1, td&0x20 ? "True" : "False", |
373 | 107 | i+1, td&0x40 ? "True" : "False", |
374 | 107 | i+1, td&0x80 ? "True" : "False"); |
375 | | |
376 | 107 | if (i==0) { |
377 | 13 | k = td&0x0F; /* number of historical bytes */ |
378 | 13 | proto_tree_add_item(td_tree, hf_iso7816_atr_k, |
379 | 13 | tvb, offset, 1, ENC_BIG_ENDIAN); |
380 | 13 | } |
381 | 94 | else { |
382 | 94 | proto_tree_add_item(td_tree, hf_iso7816_atr_t, |
383 | 94 | tvb, offset, 1, ENC_BIG_ENDIAN); |
384 | 94 | } |
385 | 107 | offset++; |
386 | | |
387 | 107 | if (td&0x10) { |
388 | | /* we read TA(i+1), see comment above */ |
389 | 79 | dissect_iso7816_atr_ta(tvb, offset, i+1, pinfo, proto_tr); |
390 | 79 | offset++; |
391 | 79 | } |
392 | 107 | if (td&0x20) { |
393 | 36 | tb = tvb_get_uint8(tvb, offset); |
394 | 36 | proto_tree_add_uint_format(proto_tr, hf_iso7816_atr_tb, |
395 | 36 | tvb, offset, 1, tb, |
396 | 36 | "Interface character TB(%d): 0x%02x", i+1, tb); |
397 | 36 | offset++; |
398 | 36 | } |
399 | 107 | if (td&0x40) { |
400 | 43 | tc = tvb_get_uint8(tvb, offset); |
401 | 43 | proto_tree_add_uint_format(proto_tr, hf_iso7816_atr_tc, |
402 | 43 | tvb, offset, 1, tc, |
403 | 43 | "Interface character TC(%d): 0x%02x", i+1, tc); |
404 | 43 | offset++; |
405 | 43 | } |
406 | | |
407 | 107 | i++; |
408 | 107 | } while (td&0x80); |
409 | | |
410 | 13 | if (k>0) { |
411 | 4 | proto_tree_add_item(proto_tr, hf_iso7816_atr_hist_bytes, |
412 | 4 | tvb, offset, k, ENC_NA); |
413 | 4 | offset += k; |
414 | 4 | } |
415 | | |
416 | 13 | tck_len = tvb_reported_length_remaining(tvb, offset); |
417 | | /* tck is either absent or exactly one byte */ |
418 | 13 | if (tck_len==1) { |
419 | 0 | proto_tree_add_item(proto_tr, hf_iso7816_atr_tck, |
420 | 0 | tvb, offset, 1, ENC_BIG_ENDIAN); |
421 | 0 | offset++; |
422 | 0 | } |
423 | 13 | else if (tck_len>1) { |
424 | 13 | proto_tree_add_expert(proto_tr, pinfo, &ei_iso7816_atr_tck_not1, |
425 | 13 | tvb, offset, tck_len); |
426 | 13 | } |
427 | | |
428 | 13 | proto_item_set_len(proto_it, offset); |
429 | 13 | return offset; |
430 | 17 | } |
431 | | |
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 |
435 | | dissectors. */ |
436 | | static int |
437 | | dissect_iso7816_class(tvbuff_t *tvb, int offset, |
438 | | packet_info *pinfo _U_, proto_tree *tree) |
439 | 0 | { |
440 | 0 | proto_item *class_item; |
441 | 0 | proto_tree *class_tree; |
442 | 0 | uint8_t dev_class; |
443 | |
|
444 | 0 | class_item = proto_tree_add_item(tree, hf_iso7816_cla, |
445 | 0 | tvb, offset, 1, ENC_BIG_ENDIAN); |
446 | 0 | class_tree = proto_item_add_subtree(class_item, ett_iso7816_class); |
447 | |
|
448 | 0 | dev_class = tvb_get_uint8(tvb, offset); |
449 | |
|
450 | 0 | if (dev_class>=0x10 && dev_class<=0x7F) { |
451 | | /* these values are RFU. */ |
452 | 0 | return -1; |
453 | 0 | } |
454 | | |
455 | 0 | if (dev_class>=0xD0 && dev_class<=0xFE) { |
456 | | /* proprietary structure and coding */ |
457 | 0 | return -1; |
458 | 0 | } |
459 | | |
460 | 0 | if (dev_class==0xFF) { |
461 | | /* reserved for Protocol Type Selection */ |
462 | 0 | return -1; |
463 | 0 | } |
464 | | |
465 | | /* If we made it this far, the structrue of the APDU is compliant |
466 | | with ISO 7816. */ |
467 | | |
468 | 0 | proto_tree_add_item(class_tree, hf_iso7816_cla_sm, |
469 | 0 | tvb, offset, 1, ENC_BIG_ENDIAN); |
470 | |
|
471 | 0 | proto_tree_add_item(class_tree, hf_iso7816_cla_channel, |
472 | 0 | tvb, offset, 1, ENC_BIG_ENDIAN); |
473 | |
|
474 | 0 | if (dev_class>=0x80 && dev_class<=0x9F) { |
475 | | /* structure according to ISO 7816, coding is proprietary */ |
476 | 0 | return -1; |
477 | 0 | } |
478 | | |
479 | 0 | if (dev_class>=0xB0 && dev_class<=0xCF) { |
480 | | /* structure according to ISO 7816 */ |
481 | 0 | return -1; |
482 | 0 | } |
483 | | |
484 | | /* both structure and coding according to ISO 7816 */ |
485 | 0 | return 1; |
486 | 0 | } |
487 | | |
488 | | /* dissect the parameters p1 and p2 |
489 | | return number of dissected bytes or -1 for error */ |
490 | | static int |
491 | | dissect_iso7816_params(uint8_t ins, tvbuff_t *tvb, int offset, |
492 | | packet_info *pinfo _U_, proto_tree *tree) |
493 | 0 | { |
494 | 0 | int offset_start, p1_offset, p2_offset; |
495 | 0 | proto_tree *params_tree; |
496 | 0 | uint8_t p1, p2; |
497 | 0 | proto_item *p1_it = NULL, *p2_it = NULL; |
498 | 0 | proto_tree *p1_tree = NULL, *p2_tree = NULL; |
499 | 0 | proto_item *p1_p2_it = NULL; |
500 | 0 | uint16_t P1P2; |
501 | 0 | uint32_t ef, read_rec_usage; |
502 | |
|
503 | 0 | offset_start = offset; |
504 | |
|
505 | 0 | params_tree = proto_tree_add_subtree(tree, tvb, offset_start, 2, |
506 | 0 | ett_iso7816_param, NULL, "Parameters"); |
507 | |
|
508 | 0 | p1 = tvb_get_uint8(tvb,offset); |
509 | 0 | p1_it = proto_tree_add_item(params_tree, hf_iso7816_p1, tvb, |
510 | 0 | offset, 1, ENC_BIG_ENDIAN); |
511 | 0 | p1_offset = offset; |
512 | 0 | offset++; |
513 | 0 | p2 = tvb_get_uint8(tvb,offset); |
514 | 0 | p2_it = proto_tree_add_item(params_tree, hf_iso7816_p2, |
515 | 0 | tvb, offset, 1, ENC_BIG_ENDIAN); |
516 | 0 | p2_offset = offset; |
517 | 0 | offset++; |
518 | 0 | P1P2 = (p1<<8|p2); |
519 | |
|
520 | 0 | switch (ins) { |
521 | 0 | case INS_EXT_AUTH: |
522 | 0 | if (p1>0) { |
523 | 0 | proto_item_append_text(p1_it, |
524 | 0 | " (reference of the algorithm on the card)"); |
525 | 0 | } |
526 | 0 | proto_item_append_text(p2_it, " (reference of the secret)"); |
527 | 0 | break; |
528 | 0 | case INS_SELECT_FILE: |
529 | 0 | proto_item_append_text(p1_it, " (selection control)"); |
530 | 0 | p1_tree = proto_item_add_subtree(p1_it, ett_iso7816_p1); |
531 | 0 | proto_tree_add_item(p1_tree, hf_iso7816_sel_file_ctrl, |
532 | 0 | tvb, p1_offset, 1, ENC_BIG_ENDIAN); |
533 | 0 | proto_item_append_text(p2_it, " (selection options)"); |
534 | 0 | p2_tree = proto_item_add_subtree(p2_it, ett_iso7816_p2); |
535 | 0 | proto_tree_add_item(p2_tree, hf_iso7816_sel_file_fci_req, |
536 | 0 | tvb, p2_offset, 1, ENC_BIG_ENDIAN); |
537 | 0 | proto_tree_add_item(p2_tree, hf_iso7816_sel_file_occ, |
538 | 0 | tvb, p2_offset, 1, ENC_BIG_ENDIAN); |
539 | 0 | break; |
540 | 0 | case INS_READ_BIN: |
541 | 0 | if (p1&0x80) { |
542 | | /* XXX - b5-b1 of P1 == short ef identifier for the selected file */ |
543 | | /* XXX - P2 == offset for the read */ |
544 | 0 | } |
545 | 0 | else { |
546 | 0 | p1_p2_it = proto_tree_add_uint(params_tree, hf_iso7816_offset_first_byte, |
547 | 0 | tvb, offset_start, offset-offset_start, P1P2); |
548 | 0 | col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, |
549 | 0 | "offset %d", P1P2); |
550 | 0 | } |
551 | 0 | break; |
552 | 0 | case INS_READ_REC: |
553 | 0 | proto_item_append_text(p1_it, " (record number)"); |
554 | 0 | proto_item_append_text(p2_it, " (reference control)"); |
555 | 0 | p2_tree = proto_item_add_subtree(p2_it, ett_iso7816_p2); |
556 | 0 | proto_tree_add_item_ret_uint(p2_tree, hf_iso7816_read_rec_ef, |
557 | 0 | tvb, p2_offset, 1, ENC_BIG_ENDIAN, &ef); |
558 | 0 | col_append_sep_fstr(pinfo->cinfo, COL_INFO, NULL, "EF %d", ef); |
559 | 0 | proto_tree_add_item_ret_uint(p2_tree, hf_iso7816_read_rec_usage, |
560 | 0 | tvb, p2_offset, 1, ENC_BIG_ENDIAN, &read_rec_usage); |
561 | 0 | if (read_rec_usage == READ_REC_USAGE_SINGLE) { |
562 | 0 | col_append_sep_fstr( |
563 | 0 | pinfo->cinfo, COL_INFO, NULL, "record %d", p1); |
564 | 0 | } |
565 | 0 | break; |
566 | 0 | case INS_GET_RESP: |
567 | 0 | p1_p2_it = proto_tree_add_uint_format(params_tree, hf_iso7816_get_resp, |
568 | 0 | tvb, offset_start, offset-offset_start, P1P2, |
569 | 0 | "Both should be 0x00, other values are RFU"); |
570 | 0 | break; |
571 | 0 | case INS_GET_DATA: |
572 | 0 | if (P1P2<=0x003F || (0x0300<=P1P2 && P1P2<=0x3FFF)) { |
573 | 0 | p1_p2_it = proto_tree_add_uint(params_tree, hf_iso7816_rfu, |
574 | 0 | tvb, offset_start, offset-offset_start, P1P2); |
575 | 0 | } |
576 | 0 | else if (0x0100<=P1P2 && P1P2<=0x01FF) { |
577 | 0 | p1_p2_it = proto_tree_add_uint(params_tree, hf_iso7816_application_data, |
578 | 0 | tvb, offset_start, offset-offset_start, P1P2); |
579 | 0 | } |
580 | 0 | break; |
581 | 0 | default: |
582 | 0 | break; |
583 | 0 | } |
584 | | |
585 | 0 | proto_item_set_generated(p1_p2_it); |
586 | |
|
587 | 0 | return 2; |
588 | 0 | } |
589 | | |
590 | | static int |
591 | | dissect_iso7816_le( |
592 | | tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree) |
593 | 0 | { |
594 | 0 | proto_tree_add_item(tree, hf_iso7816_le, tvb, offset, 1, ENC_BIG_ENDIAN); |
595 | |
|
596 | 0 | return 1; |
597 | 0 | } |
598 | | |
599 | | |
600 | | static int |
601 | | dissect_iso7816_cmd_apdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) |
602 | 0 | { |
603 | 0 | iso7816_transaction_t *iso7816_trans = NULL; |
604 | 0 | proto_item *trans_ti = NULL; |
605 | 0 | int ret; |
606 | 0 | int offset = 0; |
607 | 0 | uint8_t ins; |
608 | 0 | int body_len; |
609 | 0 | uint8_t lc; |
610 | | |
611 | |
|
612 | 0 | if (PINFO_FD_VISITED(pinfo)) { |
613 | 0 | iso7816_trans = (iso7816_transaction_t *)wmem_tree_lookup32( |
614 | 0 | transactions, pinfo->num); |
615 | 0 | if (iso7816_trans && iso7816_trans->cmd_frame==pinfo->num && |
616 | 0 | iso7816_trans->resp_frame!=0) { |
617 | 0 | trans_ti = proto_tree_add_uint_format(tree, hf_iso7816_resp_in, |
618 | 0 | NULL, 0, 0, iso7816_trans->resp_frame, |
619 | 0 | "Response in frame %d", iso7816_trans->resp_frame); |
620 | 0 | proto_item_set_generated(trans_ti); |
621 | 0 | } |
622 | 0 | } |
623 | 0 | else { |
624 | 0 | if (transactions) { |
625 | 0 | iso7816_trans = wmem_new(wmem_file_scope(), iso7816_transaction_t); |
626 | 0 | iso7816_trans->cmd_frame = pinfo->num; |
627 | 0 | iso7816_trans->resp_frame = 0; |
628 | 0 | iso7816_trans->cmd_ins = INS_INVALID; |
629 | 0 | iso7816_trans->handle = NULL; |
630 | |
|
631 | 0 | wmem_tree_insert32(transactions, |
632 | 0 | iso7816_trans->cmd_frame, (void *)iso7816_trans); |
633 | 0 | } |
634 | 0 | } |
635 | |
|
636 | 0 | ret = dissect_iso7816_class(tvb, offset, pinfo, tree); |
637 | 0 | if (ret == -1) { |
638 | | /* the class byte says that the remaining APDU is not |
639 | | in ISO7816 format */ |
640 | |
|
641 | 0 | if (iso7816_trans) { |
642 | 0 | iso7816_trans->handle = |
643 | 0 | dissector_get_payload_handle(iso7816_apdu_pld_table); |
644 | 0 | if (iso7816_trans->handle != NULL) { |
645 | 0 | ret = call_dissector(iso7816_trans->handle, tvb, pinfo, tree); |
646 | 0 | if (ret == 0) { |
647 | 0 | col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, |
648 | 0 | "Command APDU using proprietary format"); |
649 | 0 | return 1; /* we only dissected the class byte */ |
650 | 0 | } |
651 | 0 | } |
652 | 0 | } |
653 | | |
654 | 0 | return ret; |
655 | 0 | } |
656 | 0 | offset += ret; |
657 | |
|
658 | 0 | ins = tvb_get_uint8(tvb, offset); |
659 | 0 | proto_tree_add_item(tree, hf_iso7816_ins, tvb, offset, 1, ENC_BIG_ENDIAN); |
660 | 0 | col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, |
661 | 0 | val_to_str_ext_const(ins, &iso7816_ins_ext, "Unknown instruction")); |
662 | 0 | offset++; |
663 | | /* if we just created a new transaction, we can now fill in the cmd id */ |
664 | 0 | if (iso7816_trans && iso7816_trans->cmd_ins==INS_INVALID) |
665 | 0 | iso7816_trans->cmd_ins = ins; |
666 | |
|
667 | 0 | ret = dissect_iso7816_params(ins, tvb, offset, pinfo, tree); |
668 | 0 | if (ret>0) |
669 | 0 | offset += ret; |
670 | | |
671 | | /* for now, we support only short length fields |
672 | | based on infos from the ATR, we could support extended length fields too */ |
673 | 0 | body_len = tvb_reported_length_remaining(tvb, offset); |
674 | | |
675 | | /* nothing to do for body_len==0 */ |
676 | 0 | if (body_len==1) { |
677 | 0 | offset += dissect_iso7816_le(tvb, offset, pinfo, tree); |
678 | 0 | } |
679 | 0 | else if (body_len>1) { |
680 | 0 | lc = tvb_get_uint8(tvb, offset); |
681 | 0 | proto_tree_add_item( |
682 | 0 | tree, hf_iso7816_lc, tvb, offset, 1, ENC_BIG_ENDIAN); |
683 | 0 | offset++; |
684 | 0 | if (lc>0) { |
685 | 0 | proto_tree_add_item(tree, hf_iso7816_body, tvb, offset, lc, ENC_NA); |
686 | 0 | offset += lc; |
687 | 0 | } |
688 | 0 | if (tvb_reported_length_remaining(tvb, offset)>0) { |
689 | 0 | offset += dissect_iso7816_le(tvb, offset, pinfo, tree); |
690 | 0 | } |
691 | 0 | } |
692 | |
|
693 | 0 | return offset; |
694 | 0 | } |
695 | | |
696 | | static int |
697 | | dissect_iso7816_resp_apdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) |
698 | 0 | { |
699 | 0 | iso7816_transaction_t *iso7816_trans; |
700 | 0 | proto_item *trans_ti = NULL; |
701 | 0 | const char *cmd_ins_str; |
702 | 0 | int offset = 0; |
703 | 0 | int body_len; |
704 | |
|
705 | 0 | col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "Response APDU"); |
706 | |
|
707 | 0 | if (transactions) { |
708 | | /* receive the largest key that is less than or equal to our frame |
709 | | number */ |
710 | 0 | iso7816_trans = (iso7816_transaction_t *)wmem_tree_lookup32_le( |
711 | 0 | transactions, pinfo->num); |
712 | 0 | if (iso7816_trans) { |
713 | 0 | if (iso7816_trans->resp_frame==0) { |
714 | | /* there's a pending request, this packet is the response */ |
715 | 0 | iso7816_trans->resp_frame = pinfo->num; |
716 | 0 | } |
717 | |
|
718 | 0 | if (iso7816_trans->resp_frame== pinfo->num) { |
719 | | /* we found the request that corresponds to our response */ |
720 | 0 | cmd_ins_str = val_to_str_const(iso7816_trans->cmd_ins, |
721 | 0 | iso7816_ins, "Unknown instruction"); |
722 | 0 | trans_ti = proto_tree_add_uint_format(tree, hf_iso7816_resp_to, |
723 | 0 | NULL, 0, 0, iso7816_trans->cmd_frame, |
724 | 0 | "Response to frame %d (%s)", |
725 | 0 | iso7816_trans->cmd_frame, cmd_ins_str); |
726 | 0 | proto_item_set_generated(trans_ti); |
727 | |
|
728 | 0 | col_append_sep_fstr(pinfo->cinfo, COL_INFO, " ", |
729 | 0 | "(to %s)", cmd_ins_str); |
730 | 0 | } |
731 | |
|
732 | 0 | if (iso7816_trans->handle != NULL) |
733 | 0 | call_dissector(iso7816_trans->handle, tvb, pinfo, tree); |
734 | 0 | } |
735 | 0 | } |
736 | | |
737 | | /* - 2 bytes SW1, SW2 */ |
738 | 0 | body_len = tvb_reported_length_remaining(tvb, offset) - 2; |
739 | |
|
740 | 0 | if (body_len>0) { |
741 | 0 | proto_tree_add_item(tree, hf_iso7816_body, |
742 | 0 | tvb, offset, body_len, ENC_NA); |
743 | 0 | offset += body_len; |
744 | 0 | } |
745 | |
|
746 | 0 | if (tvb_reported_length_remaining(tvb, offset) >= 2) { |
747 | 0 | proto_tree_add_item(tree, hf_iso7816_sw1, |
748 | 0 | tvb, offset, 1, ENC_BIG_ENDIAN); |
749 | 0 | offset++; |
750 | 0 | proto_tree_add_item(tree, hf_iso7816_sw2, |
751 | 0 | tvb, offset, 1, ENC_BIG_ENDIAN); |
752 | 0 | offset++; |
753 | 0 | } |
754 | |
|
755 | 0 | return offset; |
756 | 0 | } |
757 | | |
758 | | static int |
759 | | dissect_iso7816(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) |
760 | 0 | { |
761 | 0 | int offset = 0; |
762 | 0 | proto_item *tree_ti; |
763 | 0 | proto_tree *iso7816_tree; |
764 | 0 | bool is_atr = false; |
765 | |
|
766 | 0 | if (pinfo->p2p_dir!=P2P_DIR_SENT && pinfo->p2p_dir!=P2P_DIR_RECV) |
767 | 0 | return 0; |
768 | | |
769 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "ISO 7816"); |
770 | 0 | col_clear(pinfo->cinfo, COL_INFO); |
771 | |
|
772 | 0 | tree_ti = proto_tree_add_protocol_format(tree, proto_iso7816, |
773 | 0 | tvb, 0, tvb_reported_length(tvb), "ISO 7816"); |
774 | 0 | iso7816_tree = proto_item_add_subtree(tree_ti, ett_iso7816); |
775 | | |
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 | 0 | if (pinfo->p2p_dir==P2P_DIR_SENT) { |
779 | 0 | set_address(&pinfo->src, AT_STRINGZ, |
780 | 0 | (int)strlen(ADDR_INTF)+1, ADDR_INTF); |
781 | 0 | set_address(&pinfo->dst, AT_STRINGZ, |
782 | 0 | (int)strlen(ADDR_CARD)+1, ADDR_CARD); |
783 | 0 | proto_item_append_text(tree_ti, " Command APDU"); |
784 | 0 | offset = dissect_iso7816_cmd_apdu(tvb, pinfo, iso7816_tree); |
785 | 0 | } |
786 | 0 | else if (pinfo->p2p_dir==P2P_DIR_RECV) { |
787 | 0 | set_address(&pinfo->src, AT_STRINGZ, |
788 | 0 | (int)strlen(ADDR_CARD)+1, ADDR_CARD); |
789 | 0 | set_address(&pinfo->dst, AT_STRINGZ, |
790 | 0 | (int)strlen(ADDR_INTF)+1, ADDR_INTF); |
791 | |
|
792 | 0 | if (iso7816_atr_handle) { |
793 | 0 | offset = call_dissector_only(iso7816_atr_handle, |
794 | 0 | tvb, pinfo, iso7816_tree, NULL); |
795 | 0 | if (offset > 0) |
796 | 0 | is_atr = true; |
797 | 0 | } |
798 | 0 | if (!is_atr) { |
799 | 0 | proto_item_append_text(tree_ti, " Response APDU"); |
800 | 0 | offset = dissect_iso7816_resp_apdu(tvb, pinfo, iso7816_tree); |
801 | 0 | } |
802 | 0 | } |
803 | |
|
804 | 0 | return offset; |
805 | 0 | } |
806 | | |
807 | | void |
808 | | proto_register_iso7816(void) |
809 | 14 | { |
810 | 14 | static hf_register_info hf[] = { |
811 | 14 | { &hf_iso7816_atr_init_char, |
812 | 14 | { "Initial character", "iso7816.atr.init_char", |
813 | 14 | FT_UINT8, BASE_HEX, VALS(iso7816_atr_init_char), 0, NULL, HFILL } |
814 | 14 | }, |
815 | 14 | { &hf_iso7816_atr_t0, |
816 | 14 | { "Format character T0", "iso7816.atr.t0", |
817 | 14 | FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } |
818 | 14 | }, |
819 | 14 | { &hf_iso7816_atr_ta, |
820 | 14 | { "Interface character TA(i)", "iso7816.atr.ta", |
821 | 14 | FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } |
822 | 14 | }, |
823 | 14 | { &hf_iso7816_atr_ta1_fi, |
824 | 14 | { "Fi", "iso7816.atr.ta1.fi", |
825 | 14 | FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } |
826 | 14 | }, |
827 | 14 | { &hf_iso7816_atr_ta1_di, |
828 | 14 | { "Di", "iso7816.atr.ta1.di", |
829 | 14 | FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } |
830 | 14 | }, |
831 | 14 | { &hf_iso7816_atr_tb, |
832 | 14 | { "Interface character TB(i)", "iso7816.atr.tb", |
833 | 14 | FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } |
834 | 14 | }, |
835 | 14 | { &hf_iso7816_atr_tc, |
836 | 14 | { "Interface character TC(i)", "iso7816.atr.tc", |
837 | 14 | FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } |
838 | 14 | }, |
839 | 14 | { &hf_iso7816_atr_td, |
840 | 14 | { "Interface character TD(i)", "iso7816.atr.td", |
841 | 14 | FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } |
842 | 14 | }, |
843 | 14 | { &hf_iso7816_atr_next_ta_present, |
844 | 14 | { "TA(i+1) present", "iso7816.atr.next_ta_present", |
845 | 14 | FT_BOOLEAN, 8, NULL, 0x10, NULL, HFILL } |
846 | 14 | }, |
847 | 14 | { &hf_iso7816_atr_next_tb_present, |
848 | 14 | { "TB(i+1) present", "iso7816.atr.next_tb_present", |
849 | 14 | FT_BOOLEAN, 8, NULL, 0x20, NULL, HFILL } |
850 | 14 | }, |
851 | 14 | { &hf_iso7816_atr_next_tc_present, |
852 | 14 | { "TC(i+1) present", "iso7816.atr.next_tc_present", |
853 | 14 | FT_BOOLEAN, 8, NULL, 0x40, NULL, HFILL } |
854 | 14 | }, |
855 | 14 | { &hf_iso7816_atr_next_td_present, |
856 | 14 | { "TD(i+1) present", "iso7816.atr.next_td_present", |
857 | 14 | FT_BOOLEAN, 8, NULL, 0x80, NULL, HFILL } |
858 | 14 | }, |
859 | 14 | { &hf_iso7816_atr_k, |
860 | 14 | { "Number K of historical bytes", "iso7816.atr.k", |
861 | 14 | FT_UINT8, BASE_DEC, NULL, 0x0F, NULL, HFILL } |
862 | 14 | }, |
863 | 14 | { &hf_iso7816_atr_t, |
864 | 14 | { "Protocol reference T", "iso7816.atr.t", |
865 | 14 | FT_UINT8, BASE_HEX, NULL, 0x0F, NULL, HFILL } |
866 | 14 | }, |
867 | 14 | { &hf_iso7816_atr_hist_bytes, |
868 | 14 | { "Historical bytes", "iso7816.atr.historical_bytes", |
869 | 14 | FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } |
870 | 14 | }, |
871 | 14 | { &hf_iso7816_atr_tck, |
872 | 14 | { "Check character TCK", "iso7816.atr.tck", |
873 | 14 | FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } |
874 | 14 | }, |
875 | 14 | { &hf_iso7816_resp_in, |
876 | 14 | { "Response In", "iso7816.resp_in", |
877 | 14 | FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0, |
878 | 14 | "The response to this command is in this frame", HFILL } |
879 | 14 | }, |
880 | 14 | { &hf_iso7816_resp_to, |
881 | 14 | { "Response To", "iso7816.resp_to", |
882 | 14 | FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0, |
883 | 14 | "This is the response to the command in this frame", HFILL } |
884 | 14 | }, |
885 | 14 | { &hf_iso7816_cla, |
886 | 14 | { "Class", "iso7816.apdu.cla", |
887 | 14 | FT_UINT8, BASE_HEX|BASE_RANGE_STRING, RVALS(iso7816_class_rvals), 0, NULL , HFILL } |
888 | 14 | }, |
889 | 14 | { &hf_iso7816_cla_sm, |
890 | 14 | { "Secure Messaging", "iso7816.apdu.cla.sm", |
891 | 14 | FT_UINT8, BASE_HEX, VALS(iso7816_cla_sm), 0x0C, NULL , HFILL } |
892 | 14 | }, |
893 | 14 | { &hf_iso7816_cla_channel, |
894 | 14 | { "Logical channel number", "iso7816.apdu.cla.channel", |
895 | 14 | FT_UINT8, BASE_HEX|BASE_SPECIAL_VALS, VALS(unique_or_unused), 0x03, NULL , HFILL } |
896 | 14 | }, |
897 | 14 | { &hf_iso7816_ins, |
898 | 14 | { "Instruction", "iso7816.apdu.ins", |
899 | 14 | FT_UINT8, BASE_HEX | BASE_EXT_STRING, &iso7816_ins_ext, 0, NULL, HFILL } |
900 | 14 | }, |
901 | 14 | { &hf_iso7816_p1, |
902 | 14 | { "Parameter 1", "iso7816.apdu.p1", |
903 | 14 | FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } |
904 | 14 | }, |
905 | 14 | { &hf_iso7816_p2, |
906 | 14 | { "Parameter 2", "iso7816.apdu.p2", |
907 | 14 | FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } |
908 | 14 | }, |
909 | 14 | { &hf_iso7816_lc, |
910 | 14 | { "Length field Lc", "iso7816.apdu.lc", |
911 | 14 | FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } |
912 | 14 | }, |
913 | 14 | { &hf_iso7816_le, |
914 | 14 | { "Expected response length Le", "iso7816.apdu.le", |
915 | 14 | FT_UINT8, BASE_HEX|BASE_SPECIAL_VALS, VALS(unique_max_num_available_bytes), 0, NULL, HFILL } |
916 | 14 | }, |
917 | 14 | { &hf_iso7816_body, |
918 | 14 | { "APDU Body", "iso7816.apdu.body", |
919 | 14 | FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } |
920 | 14 | }, |
921 | 14 | { &hf_iso7816_sw1, |
922 | 14 | { "Status Word SW1", "iso7816.apdu.sw1", FT_UINT8, |
923 | 14 | BASE_RANGE_STRING|BASE_HEX, RVALS(iso7816_sw1), 0, NULL, HFILL } |
924 | 14 | }, |
925 | 14 | { &hf_iso7816_sw2, |
926 | 14 | { "Status Word SW2", "iso7816.apdu.sw2", |
927 | 14 | FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } |
928 | 14 | }, |
929 | 14 | { &hf_iso7816_sel_file_ctrl, |
930 | 14 | { "Selection control", "iso7816.apdu.select_file.ctrl", |
931 | 14 | FT_UINT8, BASE_HEX | BASE_EXT_STRING, |
932 | 14 | &ext_iso7816_sel_file_ctrl, 0, NULL, HFILL } |
933 | 14 | }, |
934 | 14 | { &hf_iso7816_sel_file_fci_req, |
935 | 14 | { "File control information request", "iso7816.apdu.select_file.fci_req", |
936 | 14 | FT_UINT8, BASE_HEX | BASE_EXT_STRING, |
937 | 14 | &ext_iso7816_sel_file_fci_req, 0x0C, NULL, HFILL } |
938 | 14 | }, |
939 | 14 | { &hf_iso7816_sel_file_occ, |
940 | 14 | { "Occurrence", "iso7816.apdu.select_file.occurrence", |
941 | 14 | FT_UINT8, BASE_HEX | BASE_EXT_STRING, |
942 | 14 | &ext_iso7816_sel_file_occ, 0x03, NULL, HFILL } |
943 | 14 | }, |
944 | 14 | { &hf_iso7816_read_rec_ef, |
945 | 14 | { "Short EF identifier", "iso7816.apdu.read_rec.ef", |
946 | 14 | FT_UINT8, BASE_HEX, NULL, 0xF8, NULL, HFILL } |
947 | 14 | }, |
948 | 14 | { &hf_iso7816_read_rec_usage, |
949 | 14 | { "Usage", "iso7816.apdu.read_rec.usage", |
950 | 14 | FT_UINT8, BASE_HEX | BASE_EXT_STRING, |
951 | 14 | &ext_iso7816_read_rec_usage, 0x07, NULL, HFILL } |
952 | 14 | }, |
953 | 14 | { &hf_iso7816_offset_first_byte, |
954 | 14 | { "Offset of the first byte to read", "iso7816.offset_first_byte", |
955 | 14 | FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } |
956 | 14 | }, |
957 | 14 | { &hf_iso7816_get_resp, |
958 | 14 | { "GetResp", "iso7816.get_resp", |
959 | 14 | FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } |
960 | 14 | }, |
961 | 14 | { &hf_iso7816_rfu, |
962 | 14 | { "RFU", "iso7816.rfu", |
963 | 14 | FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } |
964 | 14 | }, |
965 | 14 | { &hf_iso7816_application_data, |
966 | 14 | { "Application data (proprietary coding)", "iso7816.application_data", |
967 | 14 | FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } |
968 | 14 | }, |
969 | 14 | }; |
970 | 14 | static int *ett[] = { |
971 | 14 | &ett_iso7816, |
972 | 14 | &ett_iso7816_class, |
973 | 14 | &ett_iso7816_param, |
974 | 14 | &ett_iso7816_p1, |
975 | 14 | &ett_iso7816_p2, |
976 | 14 | &ett_iso7816_atr, |
977 | 14 | &ett_iso7816_atr_ta, |
978 | 14 | &ett_iso7816_atr_td |
979 | 14 | }; |
980 | | |
981 | 14 | static ei_register_info ei[] = { |
982 | 14 | { &ei_iso7816_atr_tck_not1, { "iso7816.atr.tck.not1", PI_PROTOCOL, PI_WARN, "TCK byte must either be absent or exactly one byte", EXPFILL }} |
983 | 14 | }; |
984 | | |
985 | 14 | expert_module_t* expert_iso7816; |
986 | | |
987 | 14 | proto_iso7816 = proto_register_protocol("ISO/IEC 7816", "ISO 7816", "iso7816"); |
988 | 14 | proto_register_field_array(proto_iso7816, hf, array_length(hf)); |
989 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
990 | 14 | expert_iso7816 = expert_register_protocol(proto_iso7816); |
991 | 14 | expert_register_field_array(expert_iso7816, ei, array_length(ei)); |
992 | | |
993 | 14 | iso7816_handle = register_dissector("iso7816", dissect_iso7816, proto_iso7816); |
994 | | |
995 | 14 | transactions = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope()); |
996 | | |
997 | 14 | proto_iso7816_atr = proto_register_protocol_in_name_only("ISO/IEC 7816-3", "ISO 7816-3", "iso7816.atr", proto_iso7816, FT_PROTOCOL); |
998 | 14 | iso7816_atr_handle = register_dissector("iso7816.atr", dissect_iso7816_atr, proto_iso7816_atr); |
999 | | |
1000 | 14 | iso7816_apdu_pld_table = |
1001 | 14 | register_decode_as_next_proto(proto_iso7816, |
1002 | 14 | "iso7816.apdu_payload", |
1003 | 14 | "ISO7816 proprietary APDU dissector", NULL); |
1004 | 14 | } |
1005 | | |
1006 | | |
1007 | | void proto_reg_handoff_iso7816(void) |
1008 | 14 | { |
1009 | 14 | dissector_add_for_decode_as("usbccid.subdissector", iso7816_handle); |
1010 | 14 | dissector_add_for_decode_as("iso14443.subdissector", iso7816_handle); |
1011 | 14 | } |
1012 | | |
1013 | | |
1014 | | /* |
1015 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
1016 | | * |
1017 | | * Local variables: |
1018 | | * c-basic-offset: 4 |
1019 | | * tab-width: 8 |
1020 | | * indent-tabs-mode: nil |
1021 | | * End: |
1022 | | * |
1023 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
1024 | | * :indentSize=4:tabSize=8:noTabs=true: |
1025 | | */ |