# HG changeset patch # User Mychaela Falconia # Date 1615707669 0 # Node ID b70d35f5476f021679f8cc2c6abc89374d08b440 # Parent b7ee2e85686b4fefb14a3cf0d9afad25b2292983 fc-uicc-tool ported over diff -r b7ee2e85686b -r b70d35f5476f .hgignore --- a/.hgignore Sun Mar 14 07:34:35 2021 +0000 +++ b/.hgignore Sun Mar 14 07:41:09 2021 +0000 @@ -10,3 +10,5 @@ ^pcsc/fc-pcsc-list$ ^simtool/fc-simtool$ + +^uicc/fc-uicc-tool$ diff -r b7ee2e85686b -r b70d35f5476f uicc/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/Makefile Sun Mar 14 07:41:09 2021 +0000 @@ -0,0 +1,20 @@ +CC= gcc +CFLAGS= -O2 +CPPFLAGS=-I../libcommon +PROG= fc-uicc-tool +OBJS= bfsearch.o cmdtab.o createfile.o dumpdir.o getresp.o hlread.o main.o \ + pins.o readcmd.o readops.o select.o writecmd.o writeops.o +LIBS= ../libcommon/libcommon.a ../libutil/libutil.a +INSTBIN=/opt/freecalypso/bin + +all: ${PROG} + +${PROG}: ${OBJS} ${LIBS} + ${CC} ${CFLAGS} -o $@ ${OBJS} ${LIBS} + +install: + mkdir -p ${INSTBIN} + install -c ${PROG} ${INSTBIN} + +clean: + rm -f ${PROG} *.o diff -r b7ee2e85686b -r b70d35f5476f uicc/bfsearch.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/bfsearch.c Sun Mar 14 07:41:09 2021 +0000 @@ -0,0 +1,136 @@ +/* + * This module implements a brute force search of file ID space, + * both MF and ADF trees. + */ + +#include +#include +#include "simresp.h" +#include "file_id.h" + +extern u_char *extract_select_resp_tag(); + +static +bfsearch_dir(path, pathlen, siblings, nsiblings, outf) + unsigned *path, pathlen, *siblings, nsiblings; + FILE *outf; +{ + unsigned bfs, n; + unsigned df_children[255], ndfc; + unsigned childpath[8]; + u_char *tlv_file_desc, *tlv_file_size; + int rc; + + for (n = 0; n < pathlen; n++) { + rc = elem_select_op(path[n]); + if (rc < 0) + return(rc); + if ((sim_resp_sw & 0xFF00) != 0x6100) { + fprintf(stderr, + "error selecting 0x%04X: SW resp 0x%04X\n", + path[n], sim_resp_sw); + return(-1); + } + } + ndfc = 0; + for (bfs = 0; bfs <= 0xFFFF; bfs++) { + if (bfs == FILEID_MF || bfs == FILEID_ADF) + continue; + for (n = 0; n < pathlen; n++) { + if (bfs == path[n]) + break; + } + if (n < pathlen) + continue; + for (n = 0; n < nsiblings; n++) { + if (bfs == siblings[n]) + break; + } + if (n < nsiblings) + continue; + rc = elem_select_op(bfs); + if (rc < 0) + return(rc); + if (sim_resp_sw == 0x6A82) + continue; + if ((sim_resp_sw & 0xFF00) != 0x6100) { + for (n = 0; n < pathlen; n++) + fprintf(outf, "%04X/", path[n]); + fprintf(outf, "%04X: SW response 0x%04X\n", bfs, + sim_resp_sw); + continue; + } + rc = get_response_op(); + if (rc < 0) + return(rc); + tlv_file_desc = extract_select_resp_tag(0x82); + if (!tlv_file_desc) + return(-1); + if (tlv_file_desc[1] < 1) { + fprintf(stderr, + "error: file desc TLV object is too short\n"); + return(-1); + } + for (n = 0; n < pathlen; n++) + fprintf(outf, "%04X/", path[n]); + fprintf(outf, "%04X: file desc 0x%02X", bfs, tlv_file_desc[2]); + if ((tlv_file_desc[2] & 0xBF) == 0x38) { + fprintf(outf, ", DF\n"); + if (ndfc < 255) + df_children[ndfc++] = bfs; + } else { + tlv_file_size = extract_select_resp_tag(0x80); + if (tlv_file_size && tlv_file_size[1] == 2) + fprintf(outf, ", total size %u", + (tlv_file_size[2] << 8) | + tlv_file_size[3]); + if (tlv_file_desc[1] == 5) + fprintf(outf, ", %u records of %u bytes", + tlv_file_desc[6], + (tlv_file_desc[4] << 8) | + tlv_file_desc[5]); + putc('\n', outf); + } + rc = elem_select_op(path[pathlen-1]); + if (rc < 0) + return(rc); + if ((sim_resp_sw & 0xFF00) != 0x6100) { + fprintf(stderr, + "reselecting starting file ID 0x%04X: SW resp 0x%04X\n", + path[pathlen-1], sim_resp_sw); + return(-1); + } + } + if (pathlen >= 8) + return(0); + for (n = 0; n < pathlen; n++) + childpath[n] = path[n]; + for (n = 0; n < ndfc; n++) { + childpath[pathlen] = df_children[n]; + rc = bfsearch_dir(childpath, pathlen + 1, df_children, ndfc, + outf); + if (rc < 0) + return(rc); + } + return(0); +} + +cmd_bfsearch_mf(argc, argv, outf) + char **argv; + FILE *outf; +{ + unsigned initpath; + + initpath = FILEID_MF; + return bfsearch_dir(&initpath, 1, &initpath, 1, outf); +} + +cmd_bfsearch_adf(argc, argv, outf) + char **argv; + FILE *outf; +{ + unsigned initpath; + + initpath = FILEID_ADF; + return bfsearch_dir(&initpath, 1, &initpath, 1, outf); +} diff -r b7ee2e85686b -r b70d35f5476f uicc/cmdtab.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/cmdtab.c Sun Mar 14 07:41:09 2021 +0000 @@ -0,0 +1,74 @@ +/* + * This module contains the command table for fc-uicc-tool. + */ + +#include "cmdtab.h" + +extern int cmd_apdu(); +extern int cmd_atr(); +extern int cmd_bfsearch_adf(); +extern int cmd_bfsearch_mf(); +extern int cmd_cd(); +extern int cmd_change_pin(); +extern int cmd_create_file(); +extern int cmd_delete_file(); +extern int cmd_dir(); +extern int cmd_disable_pin(); +extern int cmd_enable_pin(); +extern int cmd_exec(); +extern int cmd_exit(); +extern int cmd_get_response(); +extern int cmd_iccid(); +extern int cmd_pin_attempt_cnt(); +extern int cmd_puk_attempt_cnt(); +extern int cmd_readbin(); +extern int cmd_readef(); +extern int cmd_readrec(); +extern int cmd_select(); +extern int cmd_select_aid(); +extern int cmd_select_isim(); +extern int cmd_select_usim(); +extern int cmd_sim_resp(); +extern int cmd_unblock_pin(); +extern int cmd_update_bin(); +extern int cmd_update_bin_imm(); +extern int cmd_update_rec(); +extern int cmd_verify_hex(); +extern int cmd_verify_pin(); + +struct cmdtab cmdtab[] = { + {"apdu", 1, 1, 0, cmd_apdu}, + {"atr", 0, 0, 0, cmd_atr}, + {"bfsearch-adf", 0, 0, 1, cmd_bfsearch_adf}, + {"bfsearch-mf", 0, 0, 1, cmd_bfsearch_mf}, + {"cd", 1, 1, 0, cmd_cd}, + {"change-pin", 3, 3, 0, cmd_change_pin}, + {"create-file", 1, 1, 0, cmd_create_file}, + {"delete-file", 1, 1, 0, cmd_delete_file}, + {"dir", 0, 0, 1, cmd_dir}, + {"disable-pin", 2, 2, 0, cmd_disable_pin}, + {"enable-pin", 2, 2, 0, cmd_enable_pin}, + {"exec", 1, 1, 0, cmd_exec}, + {"exit", 0, 1, 0, cmd_exit}, + {"get-response", 1, 1, 1, cmd_get_response}, + {"iccid", 0, 0, 1, cmd_iccid}, + {"pin-attempt-cnt", 1, 1, 0, cmd_pin_attempt_cnt}, + {"puk-attempt-cnt", 1, 1, 0, cmd_puk_attempt_cnt}, + {"quit", 0, 1, 0, cmd_exit}, + {"readbin", 2, 2, 1, cmd_readbin}, + {"readef", 1, 1, 1, cmd_readef}, + {"readrec", 1, 2, 1, cmd_readrec}, + {"select", 1, 1, 1, cmd_select}, + {"select-aid", 1, 1, 1, cmd_select_aid}, + {"select-isim", 0, 0, 1, cmd_select_isim}, + {"select-usim", 0, 0, 1, cmd_select_usim}, + {"sim-resp", 0, 0, 1, cmd_sim_resp}, + {"unblock-pin", 3, 3, 0, cmd_unblock_pin}, + {"update-bin", 2, 2, 0, cmd_update_bin}, + {"update-bin-imm", 2, 2, 0, cmd_update_bin_imm}, + {"update-rec", 2, 2, 0, cmd_update_rec}, + {"verify-ext", 2, 2, 0, cmd_verify_pin}, + {"verify-hex", 2, 2, 0, cmd_verify_hex}, + {"verify-pin", 2, 2, 0, cmd_verify_pin}, + {0, 0, 0, 0, 0} +}; diff -r b7ee2e85686b -r b70d35f5476f uicc/createfile.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/createfile.c Sun Mar 14 07:41:09 2021 +0000 @@ -0,0 +1,81 @@ +/* + * This module implements commands that exercise ETSI TS 102 222 + * CREATE FILE and DELETE FILE operations. + */ + +#include +#include +#include +#include +#include +#include +#include "simresp.h" + +cmd_create_file(argc, argv) + char **argv; +{ + u_char apdu[260], inbuf[252], *dp; + unsigned len1, len2; + int rc; + + rc = read_hex_data_file(argv[1], inbuf, 252); + if (rc < 0) + return(rc); + len1 = rc; + dp = apdu + 5; + *dp++ = 0x62; + if (len1 < 0x80) { + *dp++ = len1; + len2 = len1 + 2; + } else { + *dp++ = 0x81; + *dp++ = len1; + len2 = len1 + 3; + } + bcopy(inbuf, dp, len1); + /* command header */ + apdu[0] = 0x00; + apdu[1] = 0xE0; + apdu[2] = 0; + apdu[3] = 0; + apdu[4] = len2; + rc = apdu_exchange(apdu, len2 + 5); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} + +cmd_delete_file(argc, argv) + char **argv; +{ + u_char apdu[7]; + unsigned file_id; + int rc; + + if (!isxdigit(argv[1][0]) || !isxdigit(argv[1][1]) || + !isxdigit(argv[1][2]) || !isxdigit(argv[1][3]) || argv[1][4]) { + fprintf(stderr, "error: 4-digit hex argument required\n"); + return(-1); + } + file_id = strtoul(argv[1], 0, 16); + /* form command APDU */ + apdu[0] = 0x00; + apdu[1] = 0xE4; + apdu[2] = 0; + apdu[3] = 0; + apdu[4] = 2; + apdu[5] = file_id >> 8; + apdu[6] = file_id; + rc = apdu_exchange(apdu, 7); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} diff -r b7ee2e85686b -r b70d35f5476f uicc/dumpdir.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/dumpdir.c Sun Mar 14 07:41:09 2021 +0000 @@ -0,0 +1,40 @@ +/* + * This module implements the dump of EF_DIR. + */ + +#include +#include +#include "file_id.h" + +cmd_dir(argc, argv, outf) + char **argv; + FILE *outf; +{ + int rc; + unsigned record_len, record_count; + unsigned recno; + + rc = select_op(FILEID_MF); + if (rc < 0) + return(rc); + rc = select_op(EF_DIR); + if (rc < 0) + return(rc); + rc = select_resp_get_linear_fixed(&record_len, &record_count); + if (rc < 0) + return(rc); + if (record_len < 5) { + fprintf(stderr, "error: EF_DIR record length is too short\n"); + return(-1); + } + for (recno = 1; recno <= record_count; recno++) { + rc = readrec_op(recno, 0x04, record_len); + if (rc < 0) + return(rc); + if (check_simresp_all_blank()) + continue; + fprintf(outf, "Record #%u:\n", recno); + dump_efdir_record(outf); + } + return(0); +} diff -r b7ee2e85686b -r b70d35f5476f uicc/getresp.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/getresp.c Sun Mar 14 07:41:09 2021 +0000 @@ -0,0 +1,69 @@ +/* + * This module implements an elementary GET RESPONSE command + */ + +#include +#include +#include +#include "simresp.h" + +cmd_get_response(argc, argv, outf) + char **argv; + FILE *outf; +{ + u_char cmd[5]; + int rc; + unsigned len; + + len = strtoul(argv[1], 0, 0); + if (len < 1 || len > 256) { + fprintf(stderr, "error: length argument is out of range\n"); + return(-1); + } + /* GET RESPONSE command APDU */ + cmd[0] = 0x00; + cmd[1] = 0xC0; + cmd[2] = 0; + cmd[3] = 0; + cmd[4] = len; + rc = apdu_exchange(cmd, 5); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW resp to GET RESPONSE: %04X\n", + sim_resp_sw); + return(-1); + } + display_sim_resp_in_hex(outf); + return(0); +} + +get_response_op() +{ + u_char cmd[5]; + int rc; + unsigned expect_resp_len; + + expect_resp_len = sim_resp_sw & 0xFF; + /* GET RESPONSE command APDU */ + cmd[0] = 0x00; + 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: %04X\n", + sim_resp_sw); + return(-1); + } + if (sim_resp_data_len != expect_resp_len) { + fprintf(stderr, + "error: GET RESPONSE returned %u bytes, expected %u\n", + sim_resp_data_len, expect_resp_len); + return(-1); + } + return(0); +} diff -r b7ee2e85686b -r b70d35f5476f uicc/hlread.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/hlread.c Sun Mar 14 07:41:09 2021 +0000 @@ -0,0 +1,40 @@ +/* + * This module implements some high-level or user-friendly read commands. + */ + +#include +#include +#include "simresp.h" +#include "file_id.h" + +cmd_iccid(argc, argv, outf) + char **argv; + FILE *outf; +{ + int rc; + unsigned len; + char buf[21], *cp; + + rc = select_op(FILEID_MF); + if (rc < 0) + return(rc); + rc = select_op(EF_ICCID); + if (rc < 0) + return(rc); + rc = select_resp_get_transparent(&len); + if (rc < 0) + return(rc); + if (len != 10) { + fprintf(stderr, "error: expected transparent EF of 10 bytes\n"); + return(-1); + } + rc = readbin_op(0, 10); + if (rc < 0) + return(rc); + decode_reversed_nibbles(sim_resp_data, 10, buf); + for (cp = buf + 20; (cp > buf + 1) && (cp[-1] == 'F'); cp--) + ; + *cp = '\0'; + fprintf(outf, "%s\n", buf); + return(0); +} diff -r b7ee2e85686b -r b70d35f5476f uicc/main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/main.c Sun Mar 14 07:41:09 2021 +0000 @@ -0,0 +1,38 @@ +#include +#include +#include + +extern char be_reader_name[]; +extern char be_atr_string[]; + +main(argc, argv) + char **argv; +{ + extern int optind; + char command[512]; + int rc; + + parse_global_options(argc, argv); + launch_backend(); + collect_backend_init_strings(); + if (argc > optind) { + rc = dispatch_ready_argv(argc - optind, argv + optind); + if (rc) + exit(1); + else + good_exit(); + } + if (be_reader_name[0]) + printf("Card reader name: %s\n", be_reader_name); + if (be_atr_string[0]) + printf("ATR: %s\n", be_atr_string); + for (;;) { + if (isatty(0)) { + fputs("uicc> ", stdout); + fflush(stdout); + } + if (!fgets(command, sizeof command, stdin)) + good_exit(); + simtool_dispatch_cmd(command, 0); + } +} diff -r b7ee2e85686b -r b70d35f5476f uicc/pins.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/pins.c Sun Mar 14 07:41:09 2021 +0000 @@ -0,0 +1,205 @@ +/* + * This module implements the standard set of commands for working + * with UICC PINs; because all of these commands take a user-specified + * P2 key ID, they should work with ADM PINs as well. + */ + +#include +#include +#include "simresp.h" + +cmd_verify_pin(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* VERIFY PIN command APDU */ + cmd[0] = 0x00; + cmd[1] = 0x20; + cmd[2] = 0x00; + cmd[3] = strtoul(argv[1], 0, 0); + cmd[4] = 8; + rc = encode_pin_entry(argv[2], cmd + 5); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 13); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} + +cmd_verify_hex(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* VERIFY PIN command APDU */ + cmd[0] = 0x00; + cmd[1] = 0x20; + cmd[2] = 0x00; + cmd[3] = strtoul(argv[1], 0, 0); + cmd[4] = 8; + rc = decode_hex_data_from_string(argv[2], cmd + 5, 8, 8); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 13); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} + +cmd_change_pin(argc, argv) + char **argv; +{ + u_char cmd[21]; + int rc; + + /* CHANGE PIN command APDU */ + cmd[0] = 0x00; + cmd[1] = 0x24; + cmd[2] = 0x00; + cmd[3] = strtoul(argv[1], 0, 0); + cmd[4] = 16; + rc = encode_pin_entry(argv[2], cmd + 5); + if (rc < 0) + return(rc); + rc = encode_pin_entry(argv[3], cmd + 13); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 21); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} + +cmd_disable_pin(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* DISABLE PIN command APDU */ + cmd[0] = 0x00; + cmd[1] = 0x26; + cmd[2] = 0x00; + cmd[3] = strtoul(argv[1], 0, 0); + cmd[4] = 8; + rc = encode_pin_entry(argv[2], cmd + 5); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 13); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} + +cmd_enable_pin(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* ENABLE PIN command APDU */ + cmd[0] = 0x00; + cmd[1] = 0x28; + cmd[2] = 0x00; + cmd[3] = strtoul(argv[1], 0, 0); + cmd[4] = 8; + rc = encode_pin_entry(argv[2], cmd + 5); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 13); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} + +cmd_unblock_pin(argc, argv) + char **argv; +{ + u_char cmd[21]; + int rc; + + /* UNBLOCK PIN command APDU */ + cmd[0] = 0x00; + cmd[1] = 0x2C; + cmd[2] = 0x00; + cmd[3] = strtoul(argv[1], 0, 0); + cmd[4] = 16; + rc = encode_pin_entry(argv[2], cmd + 5); + if (rc < 0) + return(rc); + rc = encode_pin_entry(argv[3], cmd + 13); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 21); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response: %04X\n", sim_resp_sw); + return(-1); + } + return(0); +} + +/* retrieving PIN attempt counters from the card */ + +cmd_pin_attempt_cnt(argc, argv) + char **argv; +{ + u_char cmd[5]; + int rc; + + /* VERIFY PIN command APDU */ + cmd[0] = 0x00; + cmd[1] = 0x20; + cmd[2] = 0x00; + cmd[3] = strtoul(argv[1], 0, 0); + cmd[4] = 0; + rc = apdu_exchange(cmd, 5); + if (rc < 0) + return(rc); + printf("%04X\n", sim_resp_sw); + return(0); +} + +cmd_puk_attempt_cnt(argc, argv) + char **argv; +{ + u_char cmd[5]; + int rc; + + /* UNBLOCK PIN command APDU */ + cmd[0] = 0x00; + cmd[1] = 0x2C; + cmd[2] = 0x00; + cmd[3] = strtoul(argv[1], 0, 0); + cmd[4] = 0; + rc = apdu_exchange(cmd, 5); + if (rc < 0) + return(rc); + printf("%04X\n", sim_resp_sw); + return(0); +} diff -r b7ee2e85686b -r b70d35f5476f uicc/readcmd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/readcmd.c Sun Mar 14 07:41:09 2021 +0000 @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include "simresp.h" + +extern unsigned last_sel_file_record_len; + +cmd_readbin(argc, argv, outf) + char **argv; + FILE *outf; +{ + unsigned offset, len; + int rc; + + offset = strtoul(argv[1], 0, 0); + if (offset > 0x7FFF) { + fprintf(stderr, "error: offset argument is out of range\n"); + return(-1); + } + len = strtoul(argv[2], 0, 0); + if (len < 1 || len > 256) { + fprintf(stderr, "error: length argument is out of range\n"); + return(-1); + } + rc = readbin_op(offset, len); + if (rc < 0) + return(rc); + display_sim_resp_in_hex(outf); + return(0); +} + +cmd_readrec(argc, argv, outf) + char **argv; + FILE *outf; +{ + unsigned recno, len; + int rc; + + recno = strtoul(argv[1], 0, 0); + if (recno < 1 || recno > 255) { + fprintf(stderr, + "error: record number argument is out of range\n"); + return(-1); + } + if (argv[2]) { + len = strtoul(argv[2], 0, 0); + if (len < 1 || len > 255) { + fprintf(stderr, + "error: length argument is out of range\n"); + return(-1); + } + } else { + if (!last_sel_file_record_len) { + fprintf(stderr, + "error: no current file record length is available\n"); + return(-1); + } + len = last_sel_file_record_len; + } + rc = readrec_op(recno, 0x04, len); + if (rc < 0) + return(rc); + display_sim_resp_in_hex(outf); + return(0); +} + +cmd_readef(argc, argv, outf) + char **argv; + FILE *outf; +{ + int file_id, rc; + unsigned file_len, readlen; + + 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); + rc = select_resp_get_transparent(&file_len); + if (rc < 0) + return(rc); + fprintf(outf, "Transparent EF of %u byte(s)\n", file_len); + if (!file_len) + return(0); + readlen = file_len; + if (readlen > 256) + readlen = 256; + rc = readbin_op(0, readlen); + if (rc < 0) + return(rc); + display_sim_resp_in_hex(outf); + return(0); +} diff -r b7ee2e85686b -r b70d35f5476f uicc/readops.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/readops.c Sun Mar 14 07:41:09 2021 +0000 @@ -0,0 +1,62 @@ +#include +#include +#include +#include "simresp.h" + +readbin_op(offset, len) + unsigned offset, len; +{ + u_char cmd[5]; + int rc; + + /* READ BINARY command APDU */ + cmd[0] = 0x00; + cmd[1] = 0xB0; + cmd[2] = offset >> 8; + cmd[3] = offset; + cmd[4] = len; + rc = apdu_exchange(cmd, 5); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response to READ BINARY: %04X\n", + sim_resp_sw); + return(-1); + } + if (sim_resp_data_len != len) { + fprintf(stderr, + "error: READ BINARY returned %u bytes, expected %u\n", + sim_resp_data_len, len); + return(-1); + } + return(0); +} + +readrec_op(recno, mode, len) + unsigned recno, mode, len; +{ + u_char cmd[5]; + int rc; + + /* READ RECORD command APDU */ + cmd[0] = 0x00; + cmd[1] = 0xB2; + cmd[2] = recno; + cmd[3] = mode; + cmd[4] = len; + rc = apdu_exchange(cmd, 5); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response to READ RECORD: %04X\n", + sim_resp_sw); + return(-1); + } + if (sim_resp_data_len != len) { + fprintf(stderr, + "error: READ RECORD returned %u bytes, expected %u\n", + sim_resp_data_len, len); + return(-1); + } + return(0); +} diff -r b7ee2e85686b -r b70d35f5476f uicc/select.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/select.c Sun Mar 14 07:41:09 2021 +0000 @@ -0,0 +1,394 @@ +#include +#include +#include +#include +#include +#include +#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; + +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); +} diff -r b7ee2e85686b -r b70d35f5476f uicc/writecmd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/writecmd.c Sun Mar 14 07:41:09 2021 +0000 @@ -0,0 +1,70 @@ +#include +#include +#include + +extern unsigned last_sel_file_record_len; + +cmd_update_bin(argc, argv) + char **argv; +{ + unsigned offset, len; + u_char data[255]; + int rc; + + offset = strtoul(argv[1], 0, 0); + if (offset > 0x7FFF) { + fprintf(stderr, "error: offset argument is out of range\n"); + return(-1); + } + rc = read_hex_data_file(argv[2], data, 255); + if (rc < 0) + return(rc); + len = rc; + return update_bin_op(offset, data, len); +} + +cmd_update_bin_imm(argc, argv) + char **argv; +{ + unsigned offset, len; + u_char data[255]; + int rc; + + offset = strtoul(argv[1], 0, 0); + if (offset > 0x7FFF) { + fprintf(stderr, "error: offset argument is out of range\n"); + return(-1); + } + rc = decode_hex_data_from_string(argv[2], data, 1, 255); + if (rc < 0) + return(rc); + len = rc; + return update_bin_op(offset, data, len); +} + +cmd_update_rec(argc, argv) + char **argv; +{ + unsigned recno; + u_char data[255]; + int rc; + + if (!last_sel_file_record_len) { + fprintf(stderr, "error: no record-based file selected\n"); + return(-1); + } + recno = strtoul(argv[1], 0, 0); + if (recno < 1 || recno > 255) { + fprintf(stderr, + "error: record number argument is out of range\n"); + return(-1); + } + rc = read_hex_data_file(argv[2], data, 255); + if (rc < 0) + return(rc); + if (rc != last_sel_file_record_len) { + fprintf(stderr, "error: hex data length != EF record length\n"); + return(-1); + } + return update_rec_op(recno, 0x04, data, last_sel_file_record_len); +} diff -r b7ee2e85686b -r b70d35f5476f uicc/writeops.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/writeops.c Sun Mar 14 07:41:09 2021 +0000 @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include +#include "simresp.h" + +update_bin_op(offset, data, len) + unsigned offset, len; + u_char *data; +{ + u_char cmd[260]; + int rc; + + /* UPDATE BINARY command APDU */ + cmd[0] = 0x00; + cmd[1] = 0xD6; + cmd[2] = offset >> 8; + cmd[3] = offset; + cmd[4] = len; + bcopy(data, cmd + 5, len); + rc = apdu_exchange(cmd, len + 5); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response to UPDATE BINARY: %04X\n", + sim_resp_sw); + return(-1); + } + return(0); +} + +update_rec_op(recno, mode, data, len) + unsigned recno, mode, len; + u_char *data; +{ + u_char cmd[260]; + int rc; + + /* UPDATE RECORD command APDU */ + cmd[0] = 0x00; + cmd[1] = 0xDC; + cmd[2] = recno; + cmd[3] = mode; + cmd[4] = len; + bcopy(data, cmd + 5, len); + rc = apdu_exchange(cmd, len + 5); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9000) { + fprintf(stderr, "bad SW response to UPDATE RECORD: %04X\n", + sim_resp_sw); + return(-1); + } + return(0); +}