FreeCalypso > hg > fc-sim-tools
view uicc/select.c @ 99:97ba63d9361a
scripts/fcsim1-sst: turn off STK & OTA services
In the initial unprogrammed state of the cards from Grcard, SST has
services 25 through 29 set to allocated and activated. However,
these cards appear to not actually support OTA, ENVELOPE commands
do nothing (just return SW 9000), and they were never observed
issuing any proactive SIM commands, even after a feature-generous
TERMINAL PROFILE. Therefore, let's list these STK & OTA services
as allocated, but not activated in our FCSIM1 SST.
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Wed, 05 May 2021 04:26:07 +0000 |
parents | db131929ee96 |
children |
line wrap: on
line source
#include <sys/types.h> #include <ctype.h> #include <string.h> #include <strings.h> #include <stdio.h> #include <stdlib.h> #include "simresp.h" #include "efstruct.h" u_char std_aid_usim[7] = {0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x02}; u_char std_aid_isim[7] = {0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x04}; unsigned last_sel_file_record_len; elem_select_op(file_id) unsigned file_id; { u_char cmd[7]; int rc; last_sel_file_record_len = 0; /* SELECT command APDU */ cmd[0] = 0x00; cmd[1] = 0xA4; cmd[2] = 0x00; cmd[3] = 0x04; cmd[4] = 2; cmd[5] = file_id >> 8; cmd[6] = file_id; return apdu_exchange(cmd, 7); } select_op(file_id) unsigned file_id; { u_char cmd[7]; int rc; unsigned expect_resp_len; last_sel_file_record_len = 0; /* SELECT command APDU */ cmd[0] = 0x00; cmd[1] = 0xA4; cmd[2] = 0x00; cmd[3] = 0x04; cmd[4] = 2; cmd[5] = file_id >> 8; cmd[6] = file_id; rc = apdu_exchange(cmd, 7); if (rc < 0) return(rc); if ((sim_resp_sw & 0xFF00) != 0x6100) { fprintf(stderr, "error or unexpected SW response to SELECT of 0x%04X: %04X\n", file_id, sim_resp_sw); return(-1); } expect_resp_len = sim_resp_sw & 0xFF; /* GET RESPONSE follow-up */ cmd[1] = 0xC0; cmd[2] = 0; cmd[3] = 0; cmd[4] = expect_resp_len; rc = apdu_exchange(cmd, 5); if (rc < 0) return(rc); if (sim_resp_sw != 0x9000) { fprintf(stderr, "bad SW resp to GET RESPONSE after SELECT: %04X\n", sim_resp_sw); return(-1); } if (sim_resp_data_len != expect_resp_len) { fprintf(stderr, "error: GET RESPONSE after SELECT returned %u bytes, expected %u\n", sim_resp_data_len, expect_resp_len); return(-1); } return(0); } select_aid_op(aid, aid_len) u_char *aid; unsigned aid_len; { u_char cmd[21]; int rc; unsigned expect_resp_len; last_sel_file_record_len = 0; /* SELECT command APDU */ cmd[0] = 0x00; cmd[1] = 0xA4; cmd[2] = 0x04; cmd[3] = 0x04; cmd[4] = aid_len; bcopy(aid, cmd + 5, aid_len); rc = apdu_exchange(cmd, aid_len + 5); if (rc < 0) return(rc); if ((sim_resp_sw & 0xFF00) != 0x6100) { fprintf(stderr, "error or unexpected SW response to SELECT by AID: %04X\n", sim_resp_sw); return(-1); } expect_resp_len = sim_resp_sw & 0xFF; /* GET RESPONSE follow-up */ cmd[1] = 0xC0; cmd[2] = 0; cmd[3] = 0; cmd[4] = expect_resp_len; rc = apdu_exchange(cmd, 5); if (rc < 0) return(rc); if (sim_resp_sw != 0x9000) { fprintf(stderr, "bad SW resp to GET RESPONSE after SELECT: %04X\n", sim_resp_sw); return(-1); } if (sim_resp_data_len != expect_resp_len) { fprintf(stderr, "error: GET RESPONSE after SELECT returned %u bytes, expected %u\n", sim_resp_data_len, expect_resp_len); return(-1); } return(0); } select_resp_header_check(ret_offset, ret_length) unsigned *ret_offset, *ret_length; { unsigned offset, len; if (sim_resp_data_len < 2) { tooshort: fprintf(stderr, "error: SELECT response is too short\n"); return(-1); } if (sim_resp_data[0] != 0x62) { fprintf(stderr, "error: SELECT response first byte != 0x62\n"); return(-1); } len = sim_resp_data[1]; if (len <= 0x7F) { offset = 2; return_check: if (offset + len > sim_resp_data_len) goto tooshort; if (ret_offset) *ret_offset = offset; if (ret_length) *ret_length = len; return(0); } if (len != 0x81) { fprintf(stderr, "SELECT response: first length byte is bad\n"); return(-1); } if (sim_resp_data_len < 3) goto tooshort; len = sim_resp_data[2]; offset = 3; goto return_check; } static void check_for_record_struct(tlv) u_char *tlv; { unsigned reclen; if (tlv[1] != 5) return; if (tlv[2] & 0x80) return; if ((tlv[2] & 0x38) == 0x38) return; if ((tlv[2] & 0x03) != 0x02) return; reclen = (tlv[4] << 8) | tlv[5]; if (reclen < 1 || reclen > 255) return; last_sel_file_record_len = reclen; } parse_and_display_select_response(outf) FILE *outf; { unsigned offset, totlen, reclen, n; u_char *dp, *endp; int rc; rc = select_resp_header_check(&offset, &totlen); if (rc < 0) return(rc); dp = sim_resp_data + offset; endp = sim_resp_data + offset + totlen; while (dp < endp) { if (endp - dp < 2) { trunc_error: fprintf(stderr, "error: truncated TLV record in SELECT response\n"); return(-1); } if ((dp[0] & 0x1F) == 0x1F) { fprintf(stderr, "error: extended tag not supported in SELECT response\n"); return(-1); } if (dp[1] & 0x80) { fprintf(stderr, "error: extended length not supported in SELECT response\n"); return(-1); } reclen = dp[1] + 2; if (endp - dp < reclen) goto trunc_error; if (dp[0] == 0x82) check_for_record_struct(dp); for (n = 0; n < reclen; n++) { if (n) putc(' ', outf); fprintf(outf, "%02X", *dp++); } putc('\n', outf); } return(0); } cmd_select(argc, argv, outf) char **argv; FILE *outf; { int file_id, rc; if (isxdigit(argv[1][0]) && isxdigit(argv[1][1]) && isxdigit(argv[1][2]) && isxdigit(argv[1][3]) && !argv[1][4]) file_id = strtoul(argv[1], 0, 16); else file_id = find_symbolic_file_name(argv[1]); if (file_id < 0) { fprintf(stderr, "error: file ID argument is not a hex value or a recognized symbolic name\n"); return(-1); } rc = select_op(file_id); if (rc < 0) return(rc); return parse_and_display_select_response(outf); } cmd_select_aid(argc, argv, outf) char **argv; FILE *outf; { u_char aid[16]; unsigned aid_len; int rc; rc = decode_hex_data_from_string(argv[1], aid, 1, 16); if (rc < 0) return(rc); aid_len = rc; rc = select_aid_op(aid, aid_len); if (rc < 0) return(rc); return parse_and_display_select_response(outf); } cmd_select_usim(argc, argv, outf) char **argv; FILE *outf; { int rc; rc = select_aid_op(std_aid_usim, 7); if (rc < 0) return(rc); return parse_and_display_select_response(outf); } cmd_select_isim(argc, argv, outf) char **argv; FILE *outf; { int rc; rc = select_aid_op(std_aid_isim, 7); if (rc < 0) return(rc); return parse_and_display_select_response(outf); } u_char * extract_select_resp_tag(sought_tag) unsigned sought_tag; { unsigned offset, totlen, reclen; u_char *dp, *endp; int rc; rc = select_resp_header_check(&offset, &totlen); if (rc < 0) return(0); dp = sim_resp_data + offset; endp = sim_resp_data + offset + totlen; while (dp < endp) { if (endp - dp < 2) { trunc_error: fprintf(stderr, "error: truncated TLV record in SELECT response\n"); return(0); } if ((dp[0] & 0x1F) == 0x1F) { fprintf(stderr, "error: extended tag not supported in SELECT response\n"); return(0); } if (dp[1] & 0x80) { fprintf(stderr, "error: extended length not supported in SELECT response\n"); return(0); } reclen = dp[1] + 2; if (endp - dp < reclen) goto trunc_error; if (dp[0] == sought_tag) return(dp); dp += reclen; } fprintf(stderr, "error: tag 0x%02X not found in SELECT response\n", sought_tag); return(0); } select_resp_get_transparent(lenp) unsigned *lenp; { u_char *tlv; tlv = extract_select_resp_tag(0x82); if (!tlv) return(-1); if (tlv[1] != 2) { bad_file_desc: fprintf(stderr, "error: file type is not transparent EF\n"); return(-1); } if (tlv[2] & 0x80) goto bad_file_desc; if ((tlv[2] & 0x38) == 0x38) goto bad_file_desc; if ((tlv[2] & 0x07) != 0x01) goto bad_file_desc; tlv = extract_select_resp_tag(0x80); if (!tlv) return(-1); if (tlv[1] != 2) { fprintf(stderr, "error: file size TLV element has wrong length\n"); return(-1); } if (lenp) *lenp = (tlv[2] << 8) | tlv[3]; return(0); } select_resp_get_linear_fixed(rec_len_ret, rec_count_ret) unsigned *rec_len_ret, *rec_count_ret; { u_char *tlv; unsigned reclen; tlv = extract_select_resp_tag(0x82); if (!tlv) return(-1); if (tlv[1] != 5) { bad_file_desc: fprintf(stderr, "error: file type is not linear fixed EF\n"); return(-1); } if (tlv[2] & 0x80) goto bad_file_desc; if ((tlv[2] & 0x38) == 0x38) goto bad_file_desc; if ((tlv[2] & 0x07) != 0x02) goto bad_file_desc; reclen = (tlv[4] << 8) | tlv[5]; if (reclen < 1 || reclen > 255) { fprintf(stderr, "error: SELECT response gives invalid record length\n"); return(-1); } if (rec_len_ret) *rec_len_ret = reclen; if (rec_count_ret) *rec_count_ret = tlv[6]; return(0); } select_resp_get_ef_struct(efs) struct ef_struct *efs; { u_char *tlv; tlv = extract_select_resp_tag(0x82); if (!tlv) return(-1); if (tlv[1] < 2) { bad_file_desc: fprintf(stderr, "error: unable to figure out file structure\n"); return(-1); } if (tlv[2] & 0x80) goto bad_file_desc; if ((tlv[2] & 0x38) == 0x38) goto bad_file_desc; efs->structure = tlv[2] & 0x07; switch (efs->structure) { case 0x01: if (tlv[1] != 2) { fprintf(stderr, "error: file descriptor TLV element has wrong length\n"); return(-1); } tlv = extract_select_resp_tag(0x80); if (!tlv) return(-1); if (tlv[1] != 2) { fprintf(stderr, "error: file size TLV element has wrong length\n"); return(-1); } efs->total_size = (tlv[2] << 8) | tlv[3]; if (efs->total_size > 0x8000) { fprintf(stderr, "error: transparent file size exceeds UICC protocol limit\n"); return(-1); } return(0); case 0x02: case 0x06: if (tlv[1] != 5) { fprintf(stderr, "error: file descriptor TLV element has wrong length\n"); return(-1); } efs->record_len = (tlv[4] << 8) | tlv[5]; if (efs->record_len < 1 || efs->record_len > 255) { fprintf(stderr, "error: SELECT response gives invalid record length\n"); return(-1); } efs->record_count = tlv[6]; efs->total_size = efs->record_len * efs->record_count; return(0); default: goto bad_file_desc; } }