comparison uicc/select.c @ 15:b70d35f5476f

fc-uicc-tool ported over
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 14 Mar 2021 07:41:09 +0000
parents
children 0e46bbb801e0
comparison
equal deleted inserted replaced
14:b7ee2e85686b 15:b70d35f5476f
1 #include <sys/types.h>
2 #include <ctype.h>
3 #include <string.h>
4 #include <strings.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include "simresp.h"
8
9 u_char std_aid_usim[7] = {0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x02};
10 u_char std_aid_isim[7] = {0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x04};
11
12 unsigned last_sel_file_record_len;
13
14 elem_select_op(file_id)
15 unsigned file_id;
16 {
17 u_char cmd[7];
18 int rc;
19
20 last_sel_file_record_len = 0;
21 /* SELECT command APDU */
22 cmd[0] = 0x00;
23 cmd[1] = 0xA4;
24 cmd[2] = 0x00;
25 cmd[3] = 0x04;
26 cmd[4] = 2;
27 cmd[5] = file_id >> 8;
28 cmd[6] = file_id;
29 return apdu_exchange(cmd, 7);
30 }
31
32 select_op(file_id)
33 unsigned file_id;
34 {
35 u_char cmd[7];
36 int rc;
37 unsigned expect_resp_len;
38
39 last_sel_file_record_len = 0;
40 /* SELECT command APDU */
41 cmd[0] = 0x00;
42 cmd[1] = 0xA4;
43 cmd[2] = 0x00;
44 cmd[3] = 0x04;
45 cmd[4] = 2;
46 cmd[5] = file_id >> 8;
47 cmd[6] = file_id;
48 rc = apdu_exchange(cmd, 7);
49 if (rc < 0)
50 return(rc);
51 if ((sim_resp_sw & 0xFF00) != 0x6100) {
52 fprintf(stderr,
53 "error or unexpected SW response to SELECT of 0x%04X: %04X\n",
54 file_id, sim_resp_sw);
55 return(-1);
56 }
57 expect_resp_len = sim_resp_sw & 0xFF;
58 /* GET RESPONSE follow-up */
59 cmd[1] = 0xC0;
60 cmd[2] = 0;
61 cmd[3] = 0;
62 cmd[4] = expect_resp_len;
63 rc = apdu_exchange(cmd, 5);
64 if (rc < 0)
65 return(rc);
66 if (sim_resp_sw != 0x9000) {
67 fprintf(stderr,
68 "bad SW resp to GET RESPONSE after SELECT: %04X\n",
69 sim_resp_sw);
70 return(-1);
71 }
72 if (sim_resp_data_len != expect_resp_len) {
73 fprintf(stderr,
74 "error: GET RESPONSE after SELECT returned %u bytes, expected %u\n",
75 sim_resp_data_len, expect_resp_len);
76 return(-1);
77 }
78 return(0);
79 }
80
81 select_aid_op(aid, aid_len)
82 u_char *aid;
83 unsigned aid_len;
84 {
85 u_char cmd[21];
86 int rc;
87 unsigned expect_resp_len;
88
89 last_sel_file_record_len = 0;
90 /* SELECT command APDU */
91 cmd[0] = 0x00;
92 cmd[1] = 0xA4;
93 cmd[2] = 0x04;
94 cmd[3] = 0x04;
95 cmd[4] = aid_len;
96 bcopy(aid, cmd + 5, aid_len);
97 rc = apdu_exchange(cmd, aid_len + 5);
98 if (rc < 0)
99 return(rc);
100 if ((sim_resp_sw & 0xFF00) != 0x6100) {
101 fprintf(stderr,
102 "error or unexpected SW response to SELECT by AID: %04X\n",
103 sim_resp_sw);
104 return(-1);
105 }
106 expect_resp_len = sim_resp_sw & 0xFF;
107 /* GET RESPONSE follow-up */
108 cmd[1] = 0xC0;
109 cmd[2] = 0;
110 cmd[3] = 0;
111 cmd[4] = expect_resp_len;
112 rc = apdu_exchange(cmd, 5);
113 if (rc < 0)
114 return(rc);
115 if (sim_resp_sw != 0x9000) {
116 fprintf(stderr,
117 "bad SW resp to GET RESPONSE after SELECT: %04X\n",
118 sim_resp_sw);
119 return(-1);
120 }
121 if (sim_resp_data_len != expect_resp_len) {
122 fprintf(stderr,
123 "error: GET RESPONSE after SELECT returned %u bytes, expected %u\n",
124 sim_resp_data_len, expect_resp_len);
125 return(-1);
126 }
127 return(0);
128 }
129
130 select_resp_header_check(ret_offset, ret_length)
131 unsigned *ret_offset, *ret_length;
132 {
133 unsigned offset, len;
134
135 if (sim_resp_data_len < 2) {
136 tooshort: fprintf(stderr, "error: SELECT response is too short\n");
137 return(-1);
138 }
139 if (sim_resp_data[0] != 0x62) {
140 fprintf(stderr, "error: SELECT response first byte != 0x62\n");
141 return(-1);
142 }
143 len = sim_resp_data[1];
144 if (len <= 0x7F) {
145 offset = 2;
146 return_check: if (offset + len > sim_resp_data_len)
147 goto tooshort;
148 if (ret_offset)
149 *ret_offset = offset;
150 if (ret_length)
151 *ret_length = len;
152 return(0);
153 }
154 if (len != 0x81) {
155 fprintf(stderr, "SELECT response: first length byte is bad\n");
156 return(-1);
157 }
158 if (sim_resp_data_len < 3)
159 goto tooshort;
160 len = sim_resp_data[2];
161 offset = 3;
162 goto return_check;
163 }
164
165 static void
166 check_for_record_struct(tlv)
167 u_char *tlv;
168 {
169 unsigned reclen;
170
171 if (tlv[1] != 5)
172 return;
173 if (tlv[2] & 0x80)
174 return;
175 if ((tlv[2] & 0x38) == 0x38)
176 return;
177 if ((tlv[2] & 0x03) != 0x02)
178 return;
179 reclen = (tlv[4] << 8) | tlv[5];
180 if (reclen < 1 || reclen > 255)
181 return;
182 last_sel_file_record_len = reclen;
183 }
184
185 parse_and_display_select_response(outf)
186 FILE *outf;
187 {
188 unsigned offset, totlen, reclen, n;
189 u_char *dp, *endp;
190 int rc;
191
192 rc = select_resp_header_check(&offset, &totlen);
193 if (rc < 0)
194 return(rc);
195 dp = sim_resp_data + offset;
196 endp = sim_resp_data + offset + totlen;
197 while (dp < endp) {
198 if (endp - dp < 2) {
199 trunc_error: fprintf(stderr,
200 "error: truncated TLV record in SELECT response\n");
201 return(-1);
202 }
203 if ((dp[0] & 0x1F) == 0x1F) {
204 fprintf(stderr,
205 "error: extended tag not supported in SELECT response\n");
206 return(-1);
207 }
208 if (dp[1] & 0x80) {
209 fprintf(stderr,
210 "error: extended length not supported in SELECT response\n");
211 return(-1);
212 }
213 reclen = dp[1] + 2;
214 if (endp - dp < reclen)
215 goto trunc_error;
216 if (dp[0] == 0x82)
217 check_for_record_struct(dp);
218 for (n = 0; n < reclen; n++) {
219 if (n)
220 putc(' ', outf);
221 fprintf(outf, "%02X", *dp++);
222 }
223 putc('\n', outf);
224 }
225 return(0);
226 }
227
228 cmd_select(argc, argv, outf)
229 char **argv;
230 FILE *outf;
231 {
232 int file_id, rc;
233
234 if (isxdigit(argv[1][0]) && isxdigit(argv[1][1]) &&
235 isxdigit(argv[1][2]) && isxdigit(argv[1][3]) && !argv[1][4])
236 file_id = strtoul(argv[1], 0, 16);
237 else
238 file_id = find_symbolic_file_name(argv[1]);
239 if (file_id < 0) {
240 fprintf(stderr,
241 "error: file ID argument is not a hex value or a recognized symbolic name\n");
242 return(-1);
243 }
244 rc = select_op(file_id);
245 if (rc < 0)
246 return(rc);
247 return parse_and_display_select_response(outf);
248 }
249
250 cmd_select_aid(argc, argv, outf)
251 char **argv;
252 FILE *outf;
253 {
254 u_char aid[16];
255 unsigned aid_len;
256 int rc;
257
258 rc = decode_hex_data_from_string(argv[1], aid, 1, 16);
259 if (rc < 0)
260 return(rc);
261 aid_len = rc;
262 rc = select_aid_op(aid, aid_len);
263 if (rc < 0)
264 return(rc);
265 return parse_and_display_select_response(outf);
266 }
267
268 cmd_select_usim(argc, argv, outf)
269 char **argv;
270 FILE *outf;
271 {
272 int rc;
273
274 rc = select_aid_op(std_aid_usim, 7);
275 if (rc < 0)
276 return(rc);
277 return parse_and_display_select_response(outf);
278 }
279
280 cmd_select_isim(argc, argv, outf)
281 char **argv;
282 FILE *outf;
283 {
284 int rc;
285
286 rc = select_aid_op(std_aid_isim, 7);
287 if (rc < 0)
288 return(rc);
289 return parse_and_display_select_response(outf);
290 }
291
292 u_char *
293 extract_select_resp_tag(sought_tag)
294 unsigned sought_tag;
295 {
296 unsigned offset, totlen, reclen;
297 u_char *dp, *endp;
298 int rc;
299
300 rc = select_resp_header_check(&offset, &totlen);
301 if (rc < 0)
302 return(0);
303 dp = sim_resp_data + offset;
304 endp = sim_resp_data + offset + totlen;
305 while (dp < endp) {
306 if (endp - dp < 2) {
307 trunc_error: fprintf(stderr,
308 "error: truncated TLV record in SELECT response\n");
309 return(0);
310 }
311 if ((dp[0] & 0x1F) == 0x1F) {
312 fprintf(stderr,
313 "error: extended tag not supported in SELECT response\n");
314 return(0);
315 }
316 if (dp[1] & 0x80) {
317 fprintf(stderr,
318 "error: extended length not supported in SELECT response\n");
319 return(0);
320 }
321 reclen = dp[1] + 2;
322 if (endp - dp < reclen)
323 goto trunc_error;
324 if (dp[0] == sought_tag)
325 return(dp);
326 dp += reclen;
327 }
328 fprintf(stderr, "error: tag 0x%02X not found in SELECT response\n",
329 sought_tag);
330 return(0);
331 }
332
333 select_resp_get_transparent(lenp)
334 unsigned *lenp;
335 {
336 u_char *tlv;
337
338 tlv = extract_select_resp_tag(0x82);
339 if (!tlv)
340 return(-1);
341 if (tlv[1] != 2) {
342 bad_file_desc: fprintf(stderr, "error: file type is not transparent EF\n");
343 return(-1);
344 }
345 if (tlv[2] & 0x80)
346 goto bad_file_desc;
347 if ((tlv[2] & 0x38) == 0x38)
348 goto bad_file_desc;
349 if ((tlv[2] & 0x07) != 0x01)
350 goto bad_file_desc;
351 tlv = extract_select_resp_tag(0x80);
352 if (!tlv)
353 return(-1);
354 if (tlv[1] != 2) {
355 fprintf(stderr,
356 "error: file size TLV element has wrong length\n");
357 return(-1);
358 }
359 if (lenp)
360 *lenp = (tlv[2] << 8) | tlv[3];
361 return(0);
362 }
363
364 select_resp_get_linear_fixed(rec_len_ret, rec_count_ret)
365 unsigned *rec_len_ret, *rec_count_ret;
366 {
367 u_char *tlv;
368 unsigned reclen;
369
370 tlv = extract_select_resp_tag(0x82);
371 if (!tlv)
372 return(-1);
373 if (tlv[1] != 5) {
374 bad_file_desc: fprintf(stderr, "error: file type is not linear fixed EF\n");
375 return(-1);
376 }
377 if (tlv[2] & 0x80)
378 goto bad_file_desc;
379 if ((tlv[2] & 0x38) == 0x38)
380 goto bad_file_desc;
381 if ((tlv[2] & 0x07) != 0x02)
382 goto bad_file_desc;
383 reclen = (tlv[4] << 8) | tlv[5];
384 if (reclen < 1 || reclen > 255) {
385 fprintf(stderr,
386 "error: SELECT response gives invalid record length\n");
387 return(-1);
388 }
389 if (rec_len_ret)
390 *rec_len_ret = reclen;
391 if (rec_count_ret)
392 *rec_count_ret = tlv[6];
393 return(0);
394 }