FreeCalypso > hg > fc-pcsc-tools
diff uicc/select.c @ 22:1b1468869ccf
new trimmed fc-uicc-tool is here
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Fri, 12 Feb 2021 04:34:53 +0000 |
parents | |
children | 58406ead2497 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/select.c Fri Feb 12 04:34:53 2021 +0000 @@ -0,0 +1,369 @@ +#include <sys/types.h> +#include <ctype.h> +#include <string.h> +#include <strings.h> +#include <stdio.h> +#include <stdlib.h> +#include "simresp.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; + +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() +{ + 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) + putchar(' '); + printf("%02X", *dp++); + } + putchar('\n'); + } + return(0); +} + +cmd_select(argc, argv) + char **argv; +{ + 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(); +} + +cmd_select_aid(argc, argv) + char **argv; +{ + 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(); +} + +cmd_select_usim() +{ + int rc; + + rc = select_aid_op(std_aid_usim, 7); + if (rc < 0) + return(rc); + return parse_and_display_select_response(); +} + +cmd_select_isim() +{ + int rc; + + rc = select_aid_op(std_aid_isim, 7); + if (rc < 0) + return(rc); + return parse_and_display_select_response(); +} + +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); +}