# HG changeset patch # User Mychaela Falconia # Date 1613104493 0 # Node ID 1b1468869ccf5ad3903221daa979f4e50a2a84cf # Parent d4dc861953828cf93a148c2095aa41b0269c5807 new trimmed fc-uicc-tool is here diff -r d4dc86195382 -r 1b1468869ccf .hgignore --- a/.hgignore Fri Feb 12 04:16:35 2021 +0000 +++ b/.hgignore Fri Feb 12 04:34:53 2021 +0000 @@ -3,3 +3,5 @@ \.[oa]$ ^simtool/fc-simtool$ + +^uicc/fc-uicc-tool$ diff -r d4dc86195382 -r 1b1468869ccf uicc/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/Makefile Fri Feb 12 04:34:53 2021 +0000 @@ -0,0 +1,19 @@ +CC= gcc +CFLAGS= -O2 -I/usr/include/PCSC -I../libcommon +PROG= fc-uicc-tool +OBJS= dispatch.o dumpdir.o hlread.o main.o readcmd.o readops.o script.o \ + select.o writecmd.o writeops.o +LIBS= ../libcommon/libcommon.a +INSTBIN=/opt/freecalypso/bin + +all: ${PROG} + +${PROG}: ${OBJS} ${LIBS} + ${CC} ${CFLAGS} -o $@ ${OBJS} ${LIBS} -lpcsclite + +install: + mkdir -p ${INSTBIN} + install -c ${PROG} ${INSTBIN} + +clean: + rm -f ${PROG} *.o diff -r d4dc86195382 -r 1b1468869ccf uicc/dispatch.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/dispatch.c Fri Feb 12 04:34:53 2021 +0000 @@ -0,0 +1,141 @@ +/* + * This module implements the command dispatch for fc-uicc-tool + */ + +#include +#include +#include +#include +#include + +extern int cmd_dir(); +extern int cmd_exec(); +extern int cmd_iccid(); +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_update_bin(); +extern int cmd_update_bin_imm(); +extern int cmd_update_rec(); + +extern int display_sim_resp_in_hex(); +extern int good_exit(); + +static struct cmdtab { + char *cmd; + int minargs; + int maxargs; + int (*func)(); +} cmdtab[] = { + {"dir", 0, 0, cmd_dir}, + {"exec", 1, 1, cmd_exec}, + {"exit", 0, 0, good_exit}, + {"iccid", 0, 0, cmd_iccid}, + {"quit", 0, 0, good_exit}, + {"readbin", 2, 2, cmd_readbin}, + {"readef", 1, 1, cmd_readef}, + {"readrec", 1, 2, cmd_readrec}, + {"select", 1, 1, cmd_select}, + {"select-aid", 1, 1, cmd_select_aid}, + {"select-isim", 0, 0, cmd_select_isim}, + {"select-usim", 0, 0, cmd_select_usim}, + {"sim-resp", 0, 0, display_sim_resp_in_hex}, + {"update-bin", 2, 2, cmd_update_bin}, + {"update-bin-imm", 2, 2, cmd_update_bin_imm}, + {"update-rec", 2, 2, cmd_update_rec}, + {0, 0, 0, 0} +}; + +simtool_dispatch_cmd(cmd, is_script) + char *cmd; +{ + char *argv[10]; + char *cp, **ap; + struct cmdtab *tp; + + for (cp = cmd; isspace(*cp); cp++) + ; + if (!*cp || *cp == '#') + return(0); + if (is_script) + printf("Script command: %s\n", cp); + argv[0] = cp; + while (*cp && !isspace(*cp)) + cp++; + if (*cp) + *cp++ = '\0'; + for (tp = cmdtab; tp->cmd; tp++) + if (!strcmp(tp->cmd, argv[0])) + break; + if (!tp->func) { + fprintf(stderr, "error: no such command\n"); + return(-1); + } + for (ap = argv + 1; ; ) { + while (isspace(*cp)) + cp++; + if (!*cp || *cp == '#') + break; + if (ap - argv - 1 >= tp->maxargs) { + fprintf(stderr, "error: too many arguments\n"); + return(-1); + } + if (*cp == '"') { + *ap++ = ++cp; + for (;;) { + if (!*cp) { +unterm_qstring: fprintf(stderr, + "error: unterminated quoted string\n"); + return(-1); + } + if (*cp == '"') + break; + if (*cp++ == '\\') { + if (!*cp) + goto unterm_qstring; + cp++; + } + } + *cp++ = '\0'; + } else { + *ap++ = cp; + while (*cp && !isspace(*cp)) + cp++; + if (*cp) + *cp++ = '\0'; + } + } + if (ap - argv - 1 < tp->minargs) { + fprintf(stderr, "error: too few arguments\n"); + return(-1); + } + *ap = 0; + return tp->func(ap - argv, argv); +} + +dispatch_ready_argv(argc, argv) + char **argv; +{ + struct cmdtab *tp; + + for (tp = cmdtab; tp->cmd; tp++) + if (!strcmp(tp->cmd, argv[0])) + break; + if (!tp->func) { + fprintf(stderr, "error: no such command\n"); + return(-1); + } + if (argc - 1 > tp->maxargs) { + fprintf(stderr, "error: too many arguments\n"); + return(-1); + } + if (argc - 1 < tp->minargs) { + fprintf(stderr, "error: too few arguments\n"); + return(-1); + } + return tp->func(argc, argv); +} diff -r d4dc86195382 -r 1b1468869ccf uicc/dumpdir.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/dumpdir.c Fri Feb 12 04:34:53 2021 +0000 @@ -0,0 +1,38 @@ +/* + * This module implements the dump of EF_DIR. + */ + +#include +#include +#include "file_id.h" + +cmd_dir() +{ + 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; + printf("Record #%u:\n", recno); + dump_efdir_record(); + } + return(0); +} diff -r d4dc86195382 -r 1b1468869ccf uicc/hlread.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/hlread.c Fri Feb 12 04:34:53 2021 +0000 @@ -0,0 +1,36 @@ +/* + * This module implements some high-level or user-friendly read commands. + */ + +#include +#include +#include "simresp.h" +#include "file_id.h" + +cmd_iccid() +{ + int rc; + unsigned len; + char buf[21]; + + 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); + buf[20] = '\0'; + printf("%s\n", buf); + return(0); +} diff -r d4dc86195382 -r 1b1468869ccf uicc/main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/main.c Fri Feb 12 04:34:53 2021 +0000 @@ -0,0 +1,35 @@ +#include +#include +#include +#include +#include +#include "cardif.h" + +main(argc, argv) + char **argv; +{ + char command[512]; + int rc; + + setup_pcsc_context(); + get_reader_name(); + printf("Card reader name: %s\n", reader_name_buf); + connect_to_card(); + retrieve_atr(); + if (argc >= 2) { + rc = dispatch_ready_argv(argc - 1, argv + 1); + if (rc) + error_exit(); + else + good_exit(); + } + for (;;) { + if (isatty(0)) { + fputs("uicc> ", stdout); + fflush(stdout); + } + if (!fgets(command, sizeof command, stdin)) + good_exit(); + simtool_dispatch_cmd(command, 0); + } +} diff -r d4dc86195382 -r 1b1468869ccf uicc/readcmd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/readcmd.c Fri Feb 12 04:34:53 2021 +0000 @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include "simresp.h" + +extern unsigned last_sel_file_record_len; + +cmd_readbin(argc, argv) + char **argv; +{ + 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(); + return(0); +} + +cmd_readrec(argc, argv) + char **argv; +{ + 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(); + return(0); +} + +cmd_readef(argc, argv) + char **argv; +{ + 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); + printf("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(); + return(0); +} diff -r d4dc86195382 -r 1b1468869ccf uicc/readops.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/readops.c Fri Feb 12 04:34:53 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 d4dc86195382 -r 1b1468869ccf uicc/script.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/script.c Fri Feb 12 04:34:53 2021 +0000 @@ -0,0 +1,37 @@ +/* + * This module implements the exec command, which is our scripting facility. + */ + +#include +#include +#include +#include + +cmd_exec(argc, argv) + char **argv; +{ + FILE *f; + char linebuf[512], *cp; + int lineno, retval = 0; + + f = fopen(argv[1], "r"); + if (!f) { + perror(argv[1]); + return(-1); + } + for (lineno = 1; fgets(linebuf, sizeof linebuf, f); lineno++) { + cp = index(linebuf, '\n'); + if (!cp) { + fprintf(stderr, "%s line %d: missing newline\n", + argv[1], lineno); + fclose(f); + return(-1); + } + *cp = '\0'; + retval = simtool_dispatch_cmd(linebuf, 1); + if (retval) + break; + } + fclose(f); + return(retval); +} diff -r d4dc86195382 -r 1b1468869ccf uicc/select.c --- /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 +#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; + +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); +} diff -r d4dc86195382 -r 1b1468869ccf uicc/writecmd.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/writecmd.c Fri Feb 12 04:34:53 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); + 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); + 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 d4dc86195382 -r 1b1468869ccf uicc/writeops.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uicc/writeops.c Fri Feb 12 04:34:53 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); +}