FreeCalypso > hg > fc-pcsc-tools
changeset 1:2071b28cd0c7
simtool: first refactored version
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Thu, 11 Feb 2021 23:04:28 +0000 |
parents | f7145c77b7fb |
children | 5b69dfc789f2 |
files | .hgignore simtool/Makefile simtool/a38.c simtool/chv.c simtool/curfile.c simtool/curfile.h simtool/dispatch.c simtool/dumpdir.c simtool/grcard1.c simtool/grcard2.c simtool/hlread.c simtool/main.c simtool/pbcommon.c simtool/pbdump.c simtool/pberase.c simtool/pbupdate.c simtool/readcmd.c simtool/readops.c simtool/saverestore.c simtool/script.c simtool/select.c simtool/sysmo.c simtool/telsum.c simtool/writecmd.c simtool/writeops.c |
diffstat | 25 files changed, 2846 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/.hgignore Thu Feb 11 22:28:45 2021 +0000 +++ b/.hgignore Thu Feb 11 23:04:28 2021 +0000 @@ -1,3 +1,5 @@ syntax: regexp \.[oa]$ + +^simtool/fc-simtool$
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/Makefile Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,20 @@ +CC= gcc +CFLAGS= -O2 -I/usr/include/PCSC -I../libcommon +PROG= fc-simtool +OBJS= a38.o chv.o curfile.o dispatch.o dumpdir.o grcard1.o grcard2.o hlread.o\ + main.o pbcommon.o pbdump.o pberase.o pbupdate.o readcmd.o readops.o \ + saverestore.o script.o select.o sysmo.o telsum.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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/a38.c Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,60 @@ +/* + * This module implements the a38 command for exercising + * the SIM's RUN GSM ALGORITHM operation. + */ + +#include <sys/types.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include "simresp.h" + +cmd_a38(argc, argv) + char **argv; +{ + u_char cmd[21]; + int rc; + + /* RUN GSM ALGORITHM command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0x88; + cmd[2] = 0; + cmd[3] = 0; + cmd[4] = 16; + rc = decode_hex_data_from_string(argv[1], cmd + 5, 16, 16); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 21); + if (rc < 0) + return(rc); + if (sim_resp_sw != 0x9F0C) { + fprintf(stderr, + "error or unexpected SW response to RUN GSM ALGO: %04X\n", + sim_resp_sw); + return(-1); + } + /* GET RESPONSE follow-up */ + cmd[1] = 0xC0; + cmd[4] = 12; + 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 != 12) { + fprintf(stderr, + "error: GET RESPONSE returned %u bytes, expected 12\n", + sim_resp_data_len); + return(-1); + } + printf("SRES: %02X %02X %02X %02X\n", sim_resp_data[0], + sim_resp_data[1], sim_resp_data[2], sim_resp_data[3]); + printf("Kc: %02X %02X %02X %02X %02X %02X %02X %02X\n", + sim_resp_data[4], sim_resp_data[5], sim_resp_data[6], + sim_resp_data[7], sim_resp_data[8], sim_resp_data[9], + sim_resp_data[10], sim_resp_data[11]); + return(0); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/chv.c Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,221 @@ +/* + * This module implements the standard set of CHV commands + * for GSM 11.11 SIMs. + */ + +#include <sys/types.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include "simresp.h" + +encode_pin_entry(arg, dest) + char *arg; + u_char *dest; +{ + unsigned n; + + n = 0; + while (*arg) { + if (!isdigit(*arg)) { + fprintf(stderr, + "error: PIN argument contains a non-digit character\n"); + return(-1); + } + if (n >= 8) { + fprintf(stderr, "error: PIN argument is too long\n"); + return(-1); + } + *dest++ = *arg++; + n++; + } + for (; n < 8; n++) + *dest++ = 0xFF; + return(0); +} + +cmd_verify_chv(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* VERIFY CHV command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0x20; + cmd[2] = 0x00; + switch (argv[0][10]) { + case '1': + cmd[3] = 0x01; + break; + case '2': + cmd[3] = 0x02; + break; + default: + fprintf(stderr, "BUG in verify-chvN command\n"); + return(-1); + } + cmd[4] = 8; + rc = encode_pin_entry(argv[1], 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_ext(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* VERIFY CHV command APDU */ + cmd[0] = 0xA0; + 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_change_chv(argc, argv) + char **argv; +{ + u_char cmd[21]; + int rc; + + /* CHANGE CHV command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0x24; + cmd[2] = 0x00; + switch (argv[0][10]) { + case '1': + cmd[3] = 0x01; + break; + case '2': + cmd[3] = 0x02; + break; + default: + fprintf(stderr, "BUG in change-chvN command\n"); + return(-1); + } + cmd[4] = 16; + rc = encode_pin_entry(argv[1], cmd + 5); + if (rc < 0) + return(rc); + rc = encode_pin_entry(argv[2], 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_chv(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* DISABLE CHV command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0x26; + cmd[2] = 0x00; + cmd[3] = 0x01; + cmd[4] = 8; + rc = encode_pin_entry(argv[1], 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_chv(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* ENABLE CHV command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0x28; + cmd[2] = 0x00; + cmd[3] = 0x01; + cmd[4] = 8; + rc = encode_pin_entry(argv[1], 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_chv(argc, argv) + char **argv; +{ + u_char cmd[21]; + int rc; + + /* UNBLOCK CHV command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0x2C; + cmd[2] = 0x00; + switch (argv[0][11]) { + case '1': + cmd[3] = 0x00; + break; + case '2': + cmd[3] = 0x02; + break; + default: + fprintf(stderr, "BUG in unblock-chvN command\n"); + return(-1); + } + cmd[4] = 16; + rc = encode_pin_entry(argv[1], cmd + 5); + if (rc < 0) + return(rc); + rc = encode_pin_entry(argv[2], 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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/curfile.c Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,4 @@ +/* these global vars hold info about the currently selected EF */ + +unsigned curfile_total_size, curfile_structure; +unsigned curfile_record_len, curfile_record_count;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/curfile.h Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,4 @@ +/* extern definitions for global vars defined in curfile.c */ + +extern unsigned curfile_total_size, curfile_structure; +extern unsigned curfile_record_len, curfile_record_count;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/dispatch.c Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,208 @@ +/* + * This module implements the command dispatch for fc-simtool + */ + +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <stdlib.h> + +extern int cmd_a38(); +extern int cmd_change_chv(); +extern int cmd_disable_chv(); +extern int cmd_enable_chv(); +extern int cmd_exec(); +extern int cmd_fix_sysmo_msisdn(); +extern int cmd_grcard1_set_adm(); +extern int cmd_grcard1_set_ki(); +extern int cmd_grcard1_set_pin(); +extern int cmd_grcard2_set_adm(); +extern int cmd_grcard2_set_pin(); +extern int cmd_grcard2_set_puk(); +extern int cmd_grcard2_set_super(); +extern int cmd_iccid(); +extern int cmd_imsi(); +extern int cmd_pb_dump(); +extern int cmd_pb_dump_rec(); +extern int cmd_pb_erase(); +extern int cmd_pb_erase_one(); +extern int cmd_pb_erase_range(); +extern int cmd_pb_update(); +extern int cmd_pb_update_imm(); +extern int cmd_pb_update_imm_hex(); +extern int cmd_readbin(); +extern int cmd_readef(); +extern int cmd_readrec(); +extern int cmd_restore_file(); +extern int cmd_savebin(); +extern int cmd_save_sms_bin(); +extern int cmd_select(); +extern int cmd_spn(); +extern int cmd_telecom_sum(); +extern int cmd_uicc_dir(); +extern int cmd_unblock_chv(); +extern int cmd_update_bin(); +extern int cmd_update_bin_imm(); +extern int cmd_update_rec(); +extern int cmd_verify_chv(); +extern int cmd_verify_ext(); + +extern int display_sim_resp_in_hex(); +extern int good_exit(); + +static struct cmdtab { + char *cmd; + int minargs; + int maxargs; + int (*func)(); +} cmdtab[] = { + {"a38", 1, 1, cmd_a38}, + {"change-chv1", 2, 2, cmd_change_chv}, + {"change-chv2", 2, 2, cmd_change_chv}, + {"change-pin1", 2, 2, cmd_change_chv}, + {"change-pin2", 2, 2, cmd_change_chv}, + {"disable-chv", 1, 1, cmd_disable_chv}, + {"disable-pin", 1, 1, cmd_disable_chv}, + {"enable-chv", 1, 1, cmd_enable_chv}, + {"enable-pin", 1, 1, cmd_enable_chv}, + {"exec", 1, 1, cmd_exec}, + {"exit", 0, 0, good_exit}, + {"fix-sysmo-msisdn", 0, 0, cmd_fix_sysmo_msisdn}, + {"grcard1-set-adm1", 2, 2, cmd_grcard1_set_adm}, + {"grcard1-set-adm2", 2, 2, cmd_grcard1_set_adm}, + {"grcard1-set-ki", 1, 1, cmd_grcard1_set_ki}, + {"grcard1-set-pin1", 2, 2, cmd_grcard1_set_pin}, + {"grcard1-set-pin2", 2, 2, cmd_grcard1_set_pin}, + {"grcard2-set-adm", 1, 1, cmd_grcard2_set_adm}, + {"grcard2-set-pin1", 1, 1, cmd_grcard2_set_pin}, + {"grcard2-set-pin2", 1, 1, cmd_grcard2_set_pin}, + {"grcard2-set-puk1", 1, 1, cmd_grcard2_set_puk}, + {"grcard2-set-puk2", 1, 1, cmd_grcard2_set_puk}, + {"grcard2-set-super", 1, 1, cmd_grcard2_set_super}, + {"iccid", 0, 0, cmd_iccid}, + {"imsi", 0, 0, cmd_imsi}, + {"pb-dump", 1, 2, cmd_pb_dump}, + {"pb-dump-rec", 2, 3, cmd_pb_dump_rec}, + {"pb-erase", 1, 1, cmd_pb_erase}, + {"pb-erase-one", 2, 2, cmd_pb_erase_one}, + {"pb-erase-range", 3, 3, cmd_pb_erase_range}, + {"pb-update", 2, 2, cmd_pb_update}, + {"pb-update-imm", 3, 4, cmd_pb_update_imm}, + {"pb-update-imm-hex", 4, 4, cmd_pb_update_imm_hex}, + {"quit", 0, 0, good_exit}, + {"readbin", 2, 2, cmd_readbin}, + {"readef", 1, 1, cmd_readef}, + {"readrec", 1, 2, cmd_readrec}, + {"restore-file", 2, 2, cmd_restore_file}, + {"savebin", 2, 2, cmd_savebin}, + {"save-sms-bin", 1, 1, cmd_save_sms_bin}, + {"select", 1, 1, cmd_select}, + {"sim-resp", 0, 0, display_sim_resp_in_hex}, + {"spn", 0, 0, cmd_spn}, + {"telecom-sum", 0, 0, cmd_telecom_sum}, + {"uicc-dir", 0, 0, cmd_uicc_dir}, + {"unblock-chv1", 2, 2, cmd_unblock_chv}, + {"unblock-chv2", 2, 2, cmd_unblock_chv}, + {"unblock-pin1", 2, 2, cmd_unblock_chv}, + {"unblock-pin2", 2, 2, cmd_unblock_chv}, + {"update-bin", 2, 2, cmd_update_bin}, + {"update-bin-imm", 2, 2, cmd_update_bin_imm}, + {"update-rec", 2, 2, cmd_update_rec}, + {"verify-chv1", 1, 1, cmd_verify_chv}, + {"verify-chv2", 1, 1, cmd_verify_chv}, + {"verify-ext", 2, 2, cmd_verify_ext}, + {"verify-pin1", 1, 1, cmd_verify_chv}, + {"verify-pin2", 1, 1, cmd_verify_chv}, + {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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/dumpdir.c Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,152 @@ +/* + * This module implements the dump of EF_DIR. + */ + +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include "simresp.h" +#include "curfile.h" +#include "file_id.h" + +static +check_all_blank() +{ + u_char *dp, *endp; + + dp = sim_resp_data; + endp = sim_resp_data + sim_resp_data_len; + while (dp < endp) + if (*dp++ != 0xFF) + return(0); + return(1); +} + +static void +dump_aid(tlv) + u_char *tlv; +{ + unsigned reclen, n; + + reclen = tlv[1]; + printf(" AID:"); + for (n = 0; n < reclen; n++) + printf(" %02X", tlv[n+2]); + putchar('\n'); +} + +static void +dump_label(tlv) + u_char *tlv; +{ + int rc; + unsigned textlen; + + printf(" Label: "); + rc = validate_alpha_field(tlv + 2, tlv[1], &textlen); + if (rc < 0) { + printf("malformed\n"); + return; + } + print_alpha_field(tlv + 2, textlen, stdout); + putchar('\n'); +} + +static void +dump_unknown_tlv(tlv) + u_char *tlv; +{ + unsigned reclen, n; + + reclen = tlv[1] + 2; + printf(" TLV:"); + for (n = 0; n < reclen; n++) + printf(" %02X", tlv[n]); + putchar('\n'); +} + +static void +dump_record(recno) + unsigned recno; +{ + unsigned totlen, reclen; + u_char *dp, *endp; + + printf("Record #%u:\n", recno); + if (sim_resp_data[0] != 0x61) { + printf(" bad: first byte != 0x61\n"); + return; + } + totlen = sim_resp_data[1]; + if (totlen < 3 || totlen > 0x7F) { + printf(" bad: global length byte 0x%02X is invalid\n", totlen); + return; + } + if (totlen + 2 > sim_resp_data_len) { + printf(" bad: TLV global length exceeds EF record length\n"); + return; + } + dp = sim_resp_data + 2; + endp = sim_resp_data + 2 + totlen; + while (dp < endp) { + if (endp - dp < 2) { +trunc_error: printf(" bad: truncated TLV record\n"); + return; + } + if ((dp[0] & 0x1F) == 0x1F) { + printf(" bad: extended tag not supported\n"); + return; + } + if (dp[1] & 0x80) { + printf(" bad: extended length not supported\n"); + return; + } + reclen = dp[1] + 2; + if (endp - dp < reclen) + goto trunc_error; + switch (dp[0]) { + case 0x4F: + dump_aid(dp); + break; + case 0x50: + dump_label(dp); + break; + default: + dump_unknown_tlv(dp); + } + dp += reclen; + } +} + +cmd_uicc_dir() +{ + int rc; + unsigned recno; + + rc = select_op(FILEID_MF); + if (rc < 0) + return(rc); + rc = select_op(EF_DIR); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x01) { + fprintf(stderr, "error: EF_DIR is not linear fixed\n"); + return(-1); + } + if (curfile_record_len < 5) { + fprintf(stderr, "error: EF_DIR record length is too short\n"); + return(-1); + } + for (recno = 1; recno <= curfile_record_count; recno++) { + rc = readrec_op(recno, 0x04, curfile_record_len); + if (rc < 0) + return(rc); + if (check_all_blank()) + continue; + dump_record(recno); + } + return(0); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/grcard1.c Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,115 @@ +/* + * This module implements a few special commands for those very few + * incredibly lucky people on Earth who have no-longer-available + * sysmoSIM-GR1 cards, or any other branded variant of the same card + * from Grcard. + */ + +#include <sys/types.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include "simresp.h" + +cmd_grcard1_set_pin(argc, argv) + char **argv; +{ + u_char cmd[21]; + int rc; + + /* Grcard1 proprietary command APDU */ + cmd[0] = 0x80; + cmd[1] = 0xD4; + cmd[2] = 0x00; + switch (argv[0][15]) { + case '1': + cmd[3] = 0x01; + break; + case '2': + cmd[3] = 0x02; + break; + default: + fprintf(stderr, "BUG in grcard1-set-pinN command\n"); + return(-1); + } + cmd[4] = 16; + rc = encode_pin_entry(argv[1], cmd + 5); + if (rc < 0) + return(rc); + rc = encode_pin_entry(argv[2], 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_grcard1_set_adm(argc, argv) + char **argv; +{ + u_char cmd[23]; + int rc; + + /* Grcard1 proprietary command APDU */ + cmd[0] = 0x80; + cmd[1] = 0xD4; + cmd[2] = 0x01; + switch (argv[0][15]) { + case '1': + cmd[3] = 0x04; + break; + case '2': + cmd[3] = 0x05; + break; + default: + fprintf(stderr, "BUG in grcard1-set-admN command\n"); + return(-1); + } + cmd[4] = 18; + cmd[5] = 0x03; + cmd[6] = 0x00; + rc = encode_pin_entry(argv[1], cmd + 7); + if (rc < 0) + return(rc); + rc = encode_pin_entry(argv[2], cmd + 15); + if (rc < 0) + return(rc); + rc = apdu_exchange(cmd, 23); + 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_grcard1_set_ki(argc, argv) + char **argv; +{ + u_char cmd[21]; + int rc; + + /* Grcard1 proprietary command APDU */ + cmd[0] = 0x80; + cmd[1] = 0xD4; + cmd[2] = 0x02; + cmd[3] = 0x00; + cmd[4] = 16; + rc = decode_hex_data_from_string(argv[1], cmd + 5, 16, 16); + 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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/grcard2.c Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,138 @@ +/* + * I, Mother Mychaela, am hoping to get some SIM cards from Grcard + * that follow the protocol which the Osmocom community has nicknamed + * GrcardSIM2: + * + * https://osmocom.org/projects/cellular-infrastructure/wiki/GrcardSIM2 + * + * I haven't got these cards yet and may not get them for a long time, + * hence the following code has been written blindly, untested. + * If anyone in the community happens to have a sysmoSIM-GR2 card + * that was once (aeons ago) sold by Sysmocom, please test this code! + */ + +#include <sys/types.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include "simresp.h" + +cmd_grcard2_set_pin(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* Grcard2 proprietary command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0xD4; + cmd[2] = 0x3A; + switch (argv[0][15]) { + case '1': + cmd[3] = 0x01; + break; + case '2': + cmd[3] = 0x02; + break; + default: + fprintf(stderr, "BUG in grcard2-set-pinN command\n"); + return(-1); + } + cmd[4] = 8; + rc = encode_pin_entry(argv[1], 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_grcard2_set_puk(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* Grcard2 proprietary command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0xD4; + cmd[2] = 0x3B; + switch (argv[0][15]) { + case '1': + cmd[3] = 0x00; + break; + case '2': + cmd[3] = 0x02; + break; + default: + fprintf(stderr, "BUG in grcard2-set-pukN command\n"); + return(-1); + } + cmd[4] = 8; + rc = encode_pin_entry(argv[1], 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_grcard2_set_adm(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* Grcard2 proprietary command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0xD4; + cmd[2] = 0x3A; + cmd[3] = 0x05; + cmd[4] = 8; + rc = encode_pin_entry(argv[1], 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_grcard2_set_super(argc, argv) + char **argv; +{ + u_char cmd[13]; + int rc; + + /* Grcard2 proprietary command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0xD4; + cmd[2] = 0x3A; + cmd[3] = 0x0B; + cmd[4] = 8; + rc = encode_pin_entry(argv[1], 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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/hlread.c Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,125 @@ +/* + * This module implements some high-level or user-friendly read commands. + */ + +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include "simresp.h" +#include "curfile.h" +#include "file_id.h" + +encode_hex_digit(d) + unsigned d; +{ + if (d <= 9) + return(d + '0'); + else + return(d - 10 + 'A'); +} + +decode_reversed_nibbles(bytes, nbytes, dest) + u_char *bytes; + unsigned nbytes; + char *dest; +{ + u_char *sp; + char *dp; + unsigned n, c; + + sp = bytes; + dp = dest; + for (n = 0; n < nbytes; n++) { + c = *sp & 0xF; + *dp++ = encode_hex_digit(c); + c = *sp >> 4; + *dp++ = encode_hex_digit(c); + sp++; + } +} + +cmd_iccid() +{ + int rc; + char buf[21]; + + rc = select_op(FILEID_MF); + if (rc < 0) + return(rc); + rc = select_op(EF_ICCID); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x00 || curfile_total_size != 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); +} + +cmd_imsi() +{ + int rc; + char buf[17]; + + rc = select_op(DF_GSM); + if (rc < 0) + return(rc); + rc = select_op(EF_IMSI); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x00 || curfile_total_size != 9) { + fprintf(stderr, "error: expected transparent EF of 9 bytes\n"); + return(-1); + } + rc = readbin_op(0, 9); + if (rc < 0) + return(rc); + decode_reversed_nibbles(sim_resp_data + 1, 8, buf); + buf[16] = '\0'; + printf("%s parity=%c len=%u\n", buf + 1, buf[0], sim_resp_data[0]); + return(0); +} + +cmd_spn() +{ + int rc; + unsigned textlen; + + rc = select_op(DF_GSM); + if (rc < 0) + return(rc); + rc = select_op(EF_SPN); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x00 || curfile_total_size != 17) { + fprintf(stderr, "error: expected transparent EF of 17 bytes\n"); + return(-1); + } + rc = readbin_op(0, 17); + if (rc < 0) + return(rc); + printf("Display condition: %02X\n", sim_resp_data[0]); + printf("SPN: "); + rc = validate_alpha_field(sim_resp_data + 1, 16, &textlen); + if (rc >= 0) + print_alpha_field(sim_resp_data, textlen, stdout); + else + printf("malformed alpha field"); + putchar('\n'); + return(0); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/main.c Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,35 @@ +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <pcsclite.h> +#include <winscard.h> +#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("simtool> ", stdout); + fflush(stdout); + } + if (!fgets(command, sizeof command, stdin)) + good_exit(); + simtool_dispatch_cmd(command, 0); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/pbcommon.c Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,70 @@ +/* + * This module implements the common functions for all phonebook commands. + */ + +#include <sys/types.h> +#include <string.h> +#include <strings.h> +#include <stdio.h> +#include <stdlib.h> +#include "simresp.h" +#include "curfile.h" +#include "file_id.h" + +static struct map { + char *user_name; + char *canon_name; + int file_id; +} phonebook_map[] = { + {"adn", "EF_ADN", EF_ADN}, + {"ADN", "EF_ADN", EF_ADN}, + {"EF_ADN", "EF_ADN", EF_ADN}, + {"fdn", "EF_FDN", EF_FDN}, + {"FDN", "EF_FDN", EF_FDN}, + {"EF_FDN", "EF_FDN", EF_FDN}, + {"sdn", "EF_SDN", EF_SDN}, + {"SDN", "EF_SDN", EF_SDN}, + {"EF_SDN", "EF_SDN", EF_SDN}, + {"msisdn", "EF_MSISDN", EF_MSISDN}, + {"MSISDN", "EF_MSISDN", EF_MSISDN}, + {"EF_MSISDN", "EF_MSISDN", EF_MSISDN}, + /* table search terminator */ + {0, 0, -1} +}; + +phonebook_op_common(reqname) + char *reqname; +{ + struct map *tp; + int rc; + + for (tp = phonebook_map; tp->user_name; tp++) + if (!strcmp(tp->user_name, reqname)) + break; + if (!tp->canon_name) { + fprintf(stderr, "error: phone book name \"%s\" not known\n", + reqname); + return(-1); + } + rc = select_op(DF_TELECOM); + if (rc < 0) + return(rc); + rc = select_op(tp->file_id); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x01) { + fprintf(stderr, "error: %s is not linear fixed\n", + tp->canon_name); + return(-1); + } + if (curfile_record_len < 14) { + fprintf(stderr, + "error: %s has record length of %u bytes, less than minimum 14\n", + tp->canon_name, curfile_record_len); + return(-1); + } + return(0); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/pbdump.c Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,181 @@ +/* + * This module implements the pb-dump command. + */ + +#include <sys/types.h> +#include <string.h> +#include <strings.h> +#include <stdio.h> +#include <stdlib.h> +#include "simresp.h" +#include "curfile.h" + +static char gsm_address_digits[16] = + {'0','1','2','3','4','5','6','7','8','9','*','#','a','b','c','?'}; + +static +check_all_blank() +{ + u_char *dp, *endp; + + dp = sim_resp_data; + endp = sim_resp_data + sim_resp_data_len; + while (dp < endp) + if (*dp++ != 0xFF) + return(0); + return(1); +} + +static +decode_number(data, nbytes, out) + u_char *data; + unsigned nbytes; + char *out; +{ + u_char *dp, *endp; + int c; + + dp = data; + endp = data + nbytes; + while (dp < endp) { + c = *dp & 0xF; + if (c == 0xF) + return(-1); + *out++ = gsm_address_digits[c]; + c = *dp >> 4; + if (c == 0xF) { + if (dp + 1 == endp) + break; + else + return(-1); + } + *out++ = gsm_address_digits[c]; + dp++; + } + *out = '\0'; + return(0); +} + +static +check_blank_area(dp, endp) + u_char *dp, *endp; +{ + while (dp < endp) + if (*dp++ != 0xFF) + return(-1); + return(0); +} + +static void +dump_record(recno, outf) + unsigned recno; + FILE *outf; +{ + int rc; + unsigned textlen; + u_char *fixp; + char digits[21]; + + fprintf(outf, "#%u: ", recno); + if (curfile_record_len > 14) { + rc = validate_alpha_field(sim_resp_data, + curfile_record_len - 14, + &textlen); + if (rc < 0) { +malformed: fprintf(outf, "malformed record\n"); + return; + } + } else + textlen = 0; + fixp = sim_resp_data + sim_resp_data_len - 14; + if (fixp[0] < 2 || fixp[0] > 11) + goto malformed; + rc = decode_number(fixp + 2, fixp[0] - 1, digits); + if (rc < 0) + goto malformed; + rc = check_blank_area(fixp + 1 + fixp[0], fixp + 12); + if (rc < 0) + goto malformed; + /* all checks passed */ + fprintf(outf, "%s,0x%02X ", digits, fixp[1]); + if (fixp[12] != 0xFF) + fprintf(outf, "CCP=%u ", fixp[12]); + if (fixp[13] != 0xFF) + fprintf(outf, "EXT=%u ", fixp[13]); + print_alpha_field(sim_resp_data, textlen, outf); + putc('\n', outf); +} + +cmd_pb_dump(argc, argv) + char **argv; +{ + int rc; + FILE *outf; + unsigned recno; + + rc = phonebook_op_common(argv[1]); + if (rc < 0) + return(rc); + if (argv[2]) { + outf = fopen(argv[2], "w"); + if (!outf) { + perror(argv[2]); + return(-1); + } + } else + outf = stdout; + for (recno = 1; recno <= curfile_record_count; recno++) { + rc = readrec_op(recno, 0x04, curfile_record_len); + if (rc < 0) { + if (argv[2]) + fclose(outf); + return(rc); + } + if (check_all_blank()) + continue; + dump_record(recno, outf); + } + if (argv[2]) + fclose(outf); + return(0); +} + +cmd_pb_dump_rec(argc, argv) + char **argv; +{ + int rc; + unsigned recno, startrec, endrec; + + rc = phonebook_op_common(argv[1]); + if (rc < 0) + return(rc); + startrec = strtoul(argv[2], 0, 0); + if (startrec < 1 || startrec > curfile_record_count) { + fprintf(stderr, + "error: specified starting record number is invalid\n"); + return(-1); + } + if (argv[3]) { + endrec = strtoul(argv[3], 0, 0); + if (endrec < 1 || endrec > curfile_record_count) { + fprintf(stderr, + "error: specified final record number is invalid\n"); + return(-1); + } + if (startrec > endrec) { + fprintf(stderr, + "error: reverse record range specified\n"); + return(-1); + } + } else + endrec = startrec; + for (recno = startrec; recno <= endrec; recno++) { + rc = readrec_op(recno, 0x04, curfile_record_len); + if (rc < 0) + return(rc); + if (check_all_blank()) + continue; + dump_record(recno, stdout); + } + return(0); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/pberase.c Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,84 @@ +/* + * This module implements the pb-erase command. + */ + +#include <sys/types.h> +#include <string.h> +#include <strings.h> +#include <stdio.h> +#include <stdlib.h> +#include "simresp.h" +#include "curfile.h" + +cmd_pb_erase(argc, argv) + char **argv; +{ + int rc; + unsigned recno; + u_char record[255]; + + rc = phonebook_op_common(argv[1]); + if (rc < 0) + return(rc); + memset(record, 0xFF, curfile_record_len); + for (recno = 1; recno <= curfile_record_count; recno++) { + rc = update_rec_op(recno, 0x04, record, curfile_record_len); + if (rc < 0) + return(rc); + } + return(0); +} + +cmd_pb_erase_one(argc, argv) + char **argv; +{ + int rc; + unsigned recno; + u_char record[255]; + + rc = phonebook_op_common(argv[1]); + if (rc < 0) + return(rc); + recno = strtoul(argv[2], 0, 0); + if (recno < 1 || recno > curfile_record_count) { + fprintf(stderr, "error: specified record number is invalid\n"); + return(-1); + } + memset(record, 0xFF, curfile_record_len); + return update_rec_op(recno, 0x04, record, curfile_record_len); +} + +cmd_pb_erase_range(argc, argv) + char **argv; +{ + int rc; + unsigned recno, startrec, endrec; + u_char record[255]; + + rc = phonebook_op_common(argv[1]); + if (rc < 0) + return(rc); + startrec = strtoul(argv[2], 0, 0); + if (startrec < 1 || startrec > curfile_record_count) { + fprintf(stderr, + "error: specified starting record number is invalid\n"); + return(-1); + } + endrec = strtoul(argv[3], 0, 0); + if (endrec < 1 || endrec > curfile_record_count) { + fprintf(stderr, + "error: specified final record number is invalid\n"); + return(-1); + } + if (startrec > endrec) { + fprintf(stderr, "error: reverse record range specified\n"); + return(-1); + } + memset(record, 0xFF, curfile_record_len); + for (recno = startrec; recno <= endrec; recno++) { + rc = update_rec_op(recno, 0x04, record, curfile_record_len); + if (rc < 0) + return(rc); + } + return(0); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/pbupdate.c Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,503 @@ +/* + * This module implements the pb-update command. + */ + +#include <sys/types.h> +#include <ctype.h> +#include <string.h> +#include <strings.h> +#include <stdio.h> +#include <stdlib.h> +#include "simresp.h" +#include "curfile.h" + +static u_char gsm7_encode_table[256] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x00 */ + 0xFF, 0xFF, '\n', 0xFF, 0xFF, '\r', 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x10 */ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + ' ', '!', '"', '#', 0x02, '%', '&', 0x27, /* 0x20 */ + '(', ')', '*', '+', ',', '-', '.', '/', + '0', '1', '2', '3', '4', '5', '6', '7', /* 0x30 */ + '8', '9', ':', ';', '<', '=', '>', '?', + 0x00, 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 0x40 */ + 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', + 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', /* 0x50 */ + 'X', 'Y', 'Z', 0xBC, 0xAF, 0xBE, 0x94, 0x11, + 0xFF, 'a', 'b', 'c', 'd', 'e', 'f', 'g', /* 0x60 */ + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', + 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', /* 0x70 */ + 'x', 'y', 'z', 0xA8, 0xC0, 0xA9, 0xBD, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x80 */ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0x90 */ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x40, 0xFF, 0x01, 0x24, 0x03, 0xFF, 0x5F, /* 0xA0 */ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 0xB0 */ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x60, + 0xFF, 0xFF, 0xFF, 0xFF, 0x5B, 0x0E, 0x1C, 0x09, /* 0xC0 */ + 0xFF, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x5D, 0xFF, 0xFF, 0xFF, 0xFF, 0x5C, 0xFF, /* 0xD0 */ + 0x0B, 0xFF, 0xFF, 0xFF, 0x5E, 0xFF, 0xFF, 0x1E, + 0x7F, 0xFF, 0xFF, 0xFF, 0x7B, 0x0F, 0x1D, 0xFF, /* 0xE0 */ + 0x04, 0x05, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, + 0xFF, 0x7D, 0x08, 0xFF, 0xFF, 0xFF, 0x7C, 0xFF, /* 0xF0 */ + 0x0C, 0x06, 0xFF, 0xFF, 0x7E, 0xFF, 0xFF, 0xFF +}; + +static +digit_char_to_gsm(ch) +{ + switch (ch) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return (ch - '0'); + case '*': + return 0xA; + case '#': + return 0xB; + case 'a': + case 'b': + case 'c': + return (ch - 'a' + 0xC); + case 'A': + case 'B': + case 'C': + return (ch - 'A' + 0xC); + } + return (-1); +} + +static void +pack_digit_bytes(digits, dest, num_digit_bytes) + u_char *digits, *dest; + unsigned num_digit_bytes; +{ + u_char *sp, *dp; + unsigned n; + + sp = digits; + dp = dest; + for (n = 0; n < num_digit_bytes; n++) { + *dp++ = sp[0] | (sp[1] << 4); + sp += 2; + } +} + +static char * +decode_qstring_alpha(cp, record, filename_for_errs, lineno_for_errs) + char *cp, *filename_for_errs; + u_char *record; +{ + unsigned maxlen, acclen, nadd; + int c; + + maxlen = curfile_record_len - 14; + for (acclen = 0; ; ) { + if (*cp == '\0') { +unterm_qstring: fprintf(stderr, + "%s line %d: unterminated quoted string\n", + filename_for_errs, lineno_for_errs); + return(0); + } + if (*cp == '"') + break; + c = *cp++; + if (c == '\\') { + if (*cp == '\0') + goto unterm_qstring; + c = *cp++; + switch (c) { + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case '"': + case '\\': + break; + default: + fprintf(stderr, + "%s line %d: non-understood backslash escape\n", + filename_for_errs, lineno_for_errs); + return(0); + } + } + c = gsm7_encode_table[c]; + if (c == 0xFF) { + fprintf(stderr, + "%s line %d: character in quoted string cannot be encoded in GSM7\n", + filename_for_errs, lineno_for_errs); + return(0); + } + if (c & 0x80) + nadd = 2; + else + nadd = 1; + if (acclen + nadd > maxlen) { + fprintf(stderr, + "%s line %d: alpha tag string is longer than SIM limit\n", + filename_for_errs, lineno_for_errs); + return(0); + } + if (c & 0x80) + record[acclen++] = 0x1B; + record[acclen++] = c & 0x7F; + } + return(cp + 1); +} + +static char * +decode_hex_alpha(cp, record, filename_for_errs, lineno_for_errs) + char *cp, *filename_for_errs; + u_char *record; +{ + unsigned maxlen, acclen; + + maxlen = curfile_record_len - 14; + for (acclen = 0; ; ) { + if (!isxdigit(cp[0]) || !isxdigit(cp[1])) + break; + if (acclen >= maxlen) { + fprintf(stderr, + "%s line %d: alpha tag string is longer than SIM limit\n", + filename_for_errs, lineno_for_errs); + return(0); + } + record[acclen++] = (decode_hex_digit(cp[0]) << 4) | + decode_hex_digit(cp[1]); + cp += 2; + } + return(cp); +} + +static +process_record(line, filename_for_errs, lineno_for_errs) + char *line, *filename_for_errs; +{ + unsigned recno; + u_char record[255], *fixp; + u_char digits[20]; + unsigned ndigits, num_digit_bytes; + char *cp; + int c; + + recno = strtoul(line+1, 0, 10); + if (recno < 1 || recno > curfile_record_count) { + fprintf(stderr, "%s line %d: record number is out of range\n", + filename_for_errs, lineno_for_errs); + return(-1); + } + cp = line + 1; + while (isdigit(*cp)) + cp++; + if (*cp++ != ':') { +inv_syntax: fprintf(stderr, "%s line %d: invalid syntax\n", + filename_for_errs, lineno_for_errs); + return(-1); + } + while (isspace(*cp)) + cp++; + memset(record, 0xFF, curfile_record_len); + fixp = record + curfile_record_len - 14; + if (digit_char_to_gsm(*cp) < 0) + goto inv_syntax; + for (ndigits = 0; ; ndigits++) { + c = digit_char_to_gsm(*cp); + if (c < 0) + break; + cp++; + if (ndigits >= 20) { + fprintf(stderr, "%s line %d: too many number digits\n", + filename_for_errs, lineno_for_errs); + return(-1); + } + digits[ndigits] = c; + } + if (ndigits & 1) + digits[ndigits++] = 0xF; + num_digit_bytes = ndigits >> 1; + fixp[0] = num_digit_bytes + 1; + pack_digit_bytes(digits, fixp + 2, num_digit_bytes); + if (*cp++ != ',') + goto inv_syntax; + if (cp[0] != '0' || cp[1] != 'x' && cp[1] != 'X' || !isxdigit(cp[2]) || + !isxdigit(cp[3]) || !isspace(cp[4])) + goto inv_syntax; + fixp[1] = strtoul(cp, 0, 16); + cp += 5; + while (isspace(*cp)) + cp++; + if (!strncasecmp(cp, "CCP=", 4)) { + cp += 4; + fixp[12] = strtoul(cp, 0, 0); + while (*cp && !isspace(*cp)) + cp++; + while (isspace(*cp)) + cp++; + } + if (!strncasecmp(cp, "EXT=", 4)) { + cp += 4; + fixp[13] = strtoul(cp, 0, 0); + while (*cp && !isspace(*cp)) + cp++; + while (isspace(*cp)) + cp++; + } + if (*cp == '"') { + cp++; + cp = decode_qstring_alpha(cp, record, filename_for_errs, + lineno_for_errs); + if (!cp) + return(-1); + } else if (!strncasecmp(cp, "HEX", 3)) { + cp += 3; + while (isspace(*cp)) + cp++; + cp = decode_hex_alpha(cp, record, filename_for_errs, + lineno_for_errs); + if (!cp) + return(-1); + } else + goto inv_syntax; + while (isspace(*cp)) + cp++; + if (*cp) + goto inv_syntax; + return update_rec_op(recno, 0x04, record, curfile_record_len); +} + +cmd_pb_update(argc, argv) + char **argv; +{ + int rc; + FILE *inf; + int lineno; + char linebuf[1024]; + + rc = phonebook_op_common(argv[1]); + if (rc < 0) + return(rc); + inf = fopen(argv[2], "r"); + if (!inf) { + perror(argv[2]); + return(-1); + } + for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++) { + if (!index(linebuf, '\n')) { + fprintf(stderr, + "%s line %d: too long or missing newline\n", + argv[2], lineno); + fclose(inf); + return(-1); + } + if (linebuf[0] != '#' || !isdigit(linebuf[1])) + continue; + rc = process_record(linebuf, argv[2], lineno); + if (rc < 0) { + fclose(inf); + return(rc); + } + } + fclose(inf); + return(0); +} + +static +decode_number_arg(arg, fixp) + char *arg; + u_char *fixp; +{ + u_char digits[20]; + unsigned ndigits, num_digit_bytes; + char *cp, *endp; + int c; + + cp = arg; + if (*cp == '+') { + fixp[1] = 0x91; + cp++; + } else + fixp[1] = 0x81; + if (digit_char_to_gsm(*cp) < 0) { +inv_arg: fprintf(stderr, "error: invalid phone number argument\n"); + return(-1); + } + for (ndigits = 0; ; ndigits++) { + c = digit_char_to_gsm(*cp); + if (c < 0) + break; + cp++; + if (ndigits >= 20) { + fprintf(stderr, "error: too many number digits\n"); + return(-1); + } + digits[ndigits] = c; + } + if (ndigits & 1) + digits[ndigits++] = 0xF; + num_digit_bytes = ndigits >> 1; + fixp[0] = num_digit_bytes + 1; + pack_digit_bytes(digits, fixp + 2, num_digit_bytes); + if (*cp == ',') { + cp++; + if (!isdigit(*cp)) + goto inv_arg; + fixp[1] = strtoul(cp, &endp, 0); + if (*endp) + goto inv_arg; + } else if (*cp) + goto inv_arg; + return(0); +} + +static +decode_alphatag_arg(arg, record) + char *arg; + u_char *record; +{ + unsigned maxlen, acclen, nadd; + char *cp; + int c; + + maxlen = curfile_record_len - 14; + cp = arg; + for (acclen = 0; *cp; ) { + c = *cp++; + if (c == '\\') { + if (*cp == '\0') { + fprintf(stderr, + "error: dangling backslash escape\n"); + return(-1); + } + c = *cp++; + switch (c) { + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case '"': + case '\\': + break; + default: + fprintf(stderr, + "error: non-understood backslash escape\n"); + return(-1); + } + } + c = gsm7_encode_table[c]; + if (c == 0xFF) { + fprintf(stderr, + "error: character in alpha tag string cannot be encoded in GSM7\n"); + return(-1); + } + if (c & 0x80) + nadd = 2; + else + nadd = 1; + if (acclen + nadd > maxlen) { + fprintf(stderr, + "error: alpha tag string is longer than SIM limit\n"); + return(-1); + } + if (c & 0x80) + record[acclen++] = 0x1B; + record[acclen++] = c & 0x7F; + } + return(0); +} + +cmd_pb_update_imm(argc, argv) + char **argv; +{ + int rc; + unsigned recno; + u_char record[255], *fixp; + + rc = phonebook_op_common(argv[1]); + if (rc < 0) + return(rc); + recno = strtoul(argv[2], 0, 0); + if (recno < 1 || recno > curfile_record_count) { + fprintf(stderr, "error: specified record number is invalid\n"); + return(-1); + } + memset(record, 0xFF, curfile_record_len); + fixp = record + curfile_record_len - 14; + rc = decode_number_arg(argv[3], fixp); + if (rc < 0) + return(rc); + if (argv[4]) { + rc = decode_alphatag_arg(argv[4], record); + if (rc < 0) + return(rc); + } + return update_rec_op(recno, 0x04, record, curfile_record_len); +} + +static +decode_alphatag_arg_hex(arg, record) + char *arg; + u_char *record; +{ + unsigned maxlen, acclen; + + maxlen = curfile_record_len - 14; + for (acclen = 0; ; acclen++) { + while (isspace(*arg)) + arg++; + if (!*arg) + break; + if (!isxdigit(arg[0]) || !isxdigit(arg[1])) { + fprintf(stderr, "error: invalid hex string input\n"); + return(-1); + } + if (acclen >= maxlen) { + fprintf(stderr, + "error: alpha tag string is longer than SIM limit\n"); + return(-1); + } + record[acclen] = (decode_hex_digit(arg[0]) << 4) | + decode_hex_digit(arg[1]); + arg += 2; + } + return(0); +} + +cmd_pb_update_imm_hex(argc, argv) + char **argv; +{ + int rc; + unsigned recno; + u_char record[255], *fixp; + + rc = phonebook_op_common(argv[1]); + if (rc < 0) + return(rc); + recno = strtoul(argv[2], 0, 0); + if (recno < 1 || recno > curfile_record_count) { + fprintf(stderr, "error: specified record number is invalid\n"); + return(-1); + } + memset(record, 0xFF, curfile_record_len); + fixp = record + curfile_record_len - 14; + rc = decode_number_arg(argv[3], fixp); + if (rc < 0) + return(rc); + rc = decode_alphatag_arg_hex(argv[4], record); + if (rc < 0) + return(rc); + return update_rec_op(recno, 0x04, record, curfile_record_len); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/readcmd.c Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,108 @@ +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include "simresp.h" +#include "curfile.h" + +cmd_readbin(argc, argv) + char **argv; +{ + unsigned offset, len; + int rc; + + offset = strtoul(argv[1], 0, 0); + if (offset > 0xFFFF) { + 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 (!curfile_record_len) { + fprintf(stderr, + "error: no current file record length is available\n"); + return(-1); + } + len = curfile_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 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 = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x00) { + fprintf(stderr, + "error: readef command is only for transparent EFs\n"); + return(-1); + } + printf("Transparent EF of %u byte(s)\n", curfile_total_size); + printf("File status: %02X\n", sim_resp_data[11]); + show_access_conditions("UPDATE", sim_resp_data[8] & 0xF); + show_access_conditions("READ & SEEK", sim_resp_data[8] >> 4); + show_access_conditions("INCREASE", sim_resp_data[9] >> 4); + show_access_conditions("INVALIDATE", sim_resp_data[10] & 0xF); + show_access_conditions("REHABILITATE", sim_resp_data[10] >> 4); + if (!curfile_total_size) + return(0); + readlen = curfile_total_size; + if (readlen > 256) + readlen = 256; + rc = readbin_op(0, readlen); + if (rc < 0) + return(rc); + display_sim_resp_in_hex(); + return(0); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/readops.c Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,62 @@ +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include "simresp.h" + +readbin_op(offset, len) + unsigned offset, len; +{ + u_char cmd[5]; + int rc; + + /* READ BINARY command APDU */ + cmd[0] = 0xA0; + 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] = 0xA0; + 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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/saverestore.c Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,227 @@ +/* + * This module implements commands for saving SIM file content in UNIX host + * files and restoring these backups back to the SIM. + */ + +#include <sys/types.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include "simresp.h" +#include "curfile.h" +#include "file_id.h" + +static +savebin_transparent(outf) + FILE *outf; +{ + unsigned off, cc; + int rc; + + for (off = 0; off < curfile_total_size; off += cc) { + cc = curfile_total_size - off; + if (cc > 256) + cc = 256; + rc = readbin_op(off, cc); + if (rc < 0) + return(rc); + fwrite(sim_resp_data, 1, cc, outf); + } + return(0); +} + +static +savebin_records(outf) + FILE *outf; +{ + unsigned recno; + int rc; + + for (recno = 1; recno <= curfile_record_count; recno++) { + rc = readrec_op(recno, 0x04, curfile_record_len); + if (rc < 0) + return(rc); + fwrite(sim_resp_data, curfile_record_len, 1, outf); + } + return(0); +} + +cmd_savebin(argc, argv) + char **argv; +{ + int file_id, rc; + FILE *of; + + 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 = parse_ef_select_response(); + if (rc < 0) + return(rc); + of = fopen(argv[2], "w"); + if (!of) { + perror(argv[2]); + return(-1); + } + switch (curfile_structure) { + case 0x00: + /* transparent */ + rc = savebin_transparent(of); + break; + case 0x01: + case 0x03: + /* record-based */ + rc = savebin_records(of); + break; + } + fclose(of); + return(rc); +} + +cmd_save_sms_bin(argc, argv) + char **argv; +{ + int rc; + FILE *of; + + rc = select_op(DF_TELECOM); + if (rc < 0) + return(rc); + rc = select_op(EF_SMS); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x01 || curfile_record_len != 176) { + fprintf(stderr, + "error: EF_SMS is not linear fixed with 176-byte records\n"); + return(-1); + } + of = fopen(argv[1], "w"); + if (!of) { + perror(argv[1]); + return(-1); + } + rc = savebin_records(of); + fclose(of); + return(rc); +} + +static +restore_transparent(data) + u_char *data; +{ + unsigned off, cc; + int rc; + + for (off = 0; off < curfile_total_size; off += cc) { + cc = curfile_total_size - off; + if (cc > 255) + cc = 255; + rc = update_bin_op(off, data + off, cc); + if (rc < 0) + return(rc); + } + return(0); +} + +static +restore_records(data) + u_char *data; +{ + unsigned recno; + u_char *dp; + int rc; + + dp = data; + for (recno = 1; recno <= curfile_record_count; recno++) { + rc = update_rec_op(recno, 0x04, dp, curfile_record_len); + if (rc < 0) + return(rc); + dp += curfile_record_len; + } + return(0); +} + +cmd_restore_file(argc, argv) + char **argv; +{ + int file_id, rc, fd; + struct stat st; + u_char *databuf; + + 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 = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (!curfile_total_size) { + printf("SIM indicates file of zero length, nothing to do\n"); + return(0); + } + fd = open(argv[2], O_RDONLY); + if (fd < 0) { + perror(argv[2]); + return(-1); + } + fstat(fd, &st); + if ((st.st_mode & S_IFMT) != S_IFREG) { + fprintf(stderr, "error: %s is not a regular file\n", argv[2]); + close(fd); + return(-1); + } + if (st.st_size != curfile_total_size) { + fprintf(stderr, + "error: length of %s does not match SIM EF length of %u bytes\n", + argv[2], curfile_total_size); + close(fd); + return(-1); + } + databuf = malloc(curfile_total_size); + if (!databuf) { + perror("malloc for file data"); + close(fd); + return(-1); + } + read(fd, databuf, curfile_total_size); + close(fd); + switch (curfile_structure) { + case 0x00: + /* transparent */ + rc = restore_transparent(databuf); + break; + case 0x01: + /* record-based */ + rc = restore_records(databuf); + break; + case 0x03: + fprintf(stderr, "error: cyclic files are not supported\n"); + rc = -1; + } + free(databuf); + return(rc); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/script.c Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,37 @@ +/* + * This module implements the exec command, which is our scripting facility. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> + +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); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/select.c Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,222 @@ +#include <sys/types.h> +#include <ctype.h> +#include <string.h> +#include <strings.h> +#include <stdio.h> +#include <stdlib.h> +#include "simresp.h" +#include "curfile.h" + +select_op(file_id) + unsigned file_id; +{ + u_char cmd[7]; + int rc; + unsigned expect_resp_len; + + /* SELECT command APDU */ + cmd[0] = 0xA0; + cmd[1] = 0xA4; + cmd[2] = 0; + cmd[3] = 0; + 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) != 0x9F00) { + 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[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); +} + +static void +show_secret_code_status(name, byte) + char *name; + unsigned byte; +{ + printf("Status of %s: %s, %u attempts left\n", name, + byte & 0x80 ? "initialized" : "not initialized", + byte & 0x0F); +} + +void +show_access_conditions(oper_name, cond_code) + char *oper_name; + unsigned cond_code; +{ + static char *cond_names[16] = + {"ALW", "CHV1", "CHV2", "RFU3", + "ADM4", "ADM5", "ADM6", "ADM7", + "ADM8", "ADM9", "ADM10", "ADM11", + "ADM12", "ADM13", "ADM14", "NEV"}; + + printf("Access condition for %s: %s\n", oper_name, + cond_names[cond_code]); +} + +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); + if (sim_resp_data_len < 14) { + fprintf(stderr, + "error: response length of %u bytes is too short for any file type\n", + sim_resp_data_len); + return(-1); + } + switch (sim_resp_data[6]) { + case 0x01: + printf("File type: MF\n"); + goto mf_or_df; + case 0x02: + printf("File type: DF\n"); + mf_or_df: + if (sim_resp_data_len < 22) { + fprintf(stderr, + "error: response length of %u bytes is too short for MF/DF\n", + sim_resp_data_len); + return(-1); + } + printf("File characteristics: %02X\n", sim_resp_data[13]); + printf("Number of DF children: %u\n", sim_resp_data[14]); + printf("Number of EF children: %u\n", sim_resp_data[15]); + printf("Number of secret codes: %u\n", sim_resp_data[16]); + show_secret_code_status("PIN1", sim_resp_data[18]); + show_secret_code_status("PUK1", sim_resp_data[19]); + show_secret_code_status("PIN2", sim_resp_data[20]); + show_secret_code_status("PUK2", sim_resp_data[21]); + break; + case 0x04: + printf("File type: EF\n"); + curfile_total_size = (sim_resp_data[2] << 8) | sim_resp_data[3]; + printf("File size: %u\n", curfile_total_size); + curfile_structure = sim_resp_data[13]; + switch (curfile_structure) { + case 0x00: + printf("Structure: transparent\n"); + break; + case 0x01: + printf("Structure: linear fixed\n"); + goto ef_record_based; + case 0x03: + printf("Structure: cyclic\n"); + ef_record_based: + if (sim_resp_data_len < 15) { + fprintf(stderr, + "error: response length of %u bytes is too short for record-based EF\n", + sim_resp_data_len); + return(-1); + } + printf("Record length: %u\n", sim_resp_data[14]); + curfile_record_len = sim_resp_data[14]; + if (curfile_record_len && + curfile_total_size % curfile_record_len == 0) { + curfile_record_count = + curfile_total_size / curfile_record_len; + printf("Number of records: %u\n", + curfile_record_count); + } else + curfile_record_count = 0; + break; + default: + printf("Structure: %02X (unknown)\n", + curfile_structure); + } + printf("File status: %02X\n", sim_resp_data[11]); + show_access_conditions("UPDATE", sim_resp_data[8] & 0xF); + show_access_conditions("READ & SEEK", sim_resp_data[8] >> 4); + show_access_conditions("INCREASE", sim_resp_data[9] >> 4); + show_access_conditions("INVALIDATE", sim_resp_data[10] & 0xF); + show_access_conditions("REHABILITATE", sim_resp_data[10] >> 4); + break; + default: + printf("File type: %02X (unknown)\n", sim_resp_data[6]); + } + return(0); +} + +parse_ef_select_response() +{ + if (sim_resp_data_len < 14) { + fprintf(stderr, + "error: SELECT response length of %u bytes is too short\n", + sim_resp_data_len); + return(-1); + } + if (sim_resp_data[6] != 0x04) { + fprintf(stderr, "error: selected file is not an EF\n"); + return(-1); + } + curfile_total_size = (sim_resp_data[2] << 8) | sim_resp_data[3]; + curfile_structure = sim_resp_data[13]; + switch (curfile_structure) { + case 0x00: + /* transparent */ + break; + case 0x01: + case 0x03: + /* record-based */ + if (sim_resp_data_len < 15) { + fprintf(stderr, +"error: SELECT response length of %u bytes is too short for record-based EF\n", + sim_resp_data_len); + return(-1); + } + curfile_record_len = sim_resp_data[14]; + if (!curfile_record_len) { + fprintf(stderr, + "error: SELECT response indicates record length of 0\n"); + return(-1); + } + if (curfile_total_size % curfile_record_len) { + fprintf(stderr, + "error: returned file size is not divisible by record length\n"); + return(-1); + } + curfile_record_count = curfile_total_size / curfile_record_len; + break; + default: + fprintf(stderr, "error: unknown EF structure code %02X\n", + curfile_structure); + return(-1); + } + return(0); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/sysmo.c Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,64 @@ +/* + * This module implements special commands for programmable and + * semi-programmable (made-up term for the version without ADM keys) + * SIM cards made by Sysmocom. + */ + +#include <sys/types.h> +#include <string.h> +#include <strings.h> +#include <stdio.h> +#include <stdlib.h> +#include "simresp.h" +#include "curfile.h" +#include "file_id.h" + +cmd_fix_sysmo_msisdn() +{ + int rc; + unsigned n; + u_char newrec[34]; + + rc = select_op(DF_TELECOM); + if (rc < 0) + return(rc); + rc = select_op(EF_MSISDN); + if (rc < 0) + return(rc); + rc = parse_ef_select_response(); + if (rc < 0) + return(rc); + if (curfile_structure != 0x01) { + fprintf(stderr, "error: EF_MSISDN is not linear fixed\n"); + return(-1); + } + if (curfile_record_len != 34) { + fprintf(stderr, + "error: expected EF_MSISDN record length of 34 bytes, got %u\n", + curfile_record_len); + return(-1); + } + rc = readrec_op(1, 0x04, 34); + if (rc < 0) + return(rc); + for (n = 0; n < 18; n++) { + if (sim_resp_data[n] != 0xFF) { + fprintf(stderr, + "error: non-FF data in the first 18 bytes of alpha tag area\n"); + return(-1); + } + } + if (sim_resp_data[18] == 0xFF && sim_resp_data[19] == 0xFF) { + printf( + "last 2 bytes of alpha tag area are clear - already fixed?\n"); + return(0); + } + if (sim_resp_data[18] != 0x07 || sim_resp_data[19] != 0x91) { + fprintf(stderr, + "error: bytes 18 & 19 don't match expected bogus programming\n"); + return(-1); + } + memset(newrec, 0xFF, 34); + memcpy(newrec + 20, sim_resp_data + 18, 8); + return update_rec_op(1, 0x04, newrec, 34); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/telsum.c Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,83 @@ +/* + * This module implements the telecom-sum (summary info) command. + */ + +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include "simresp.h" +#include "curfile.h" +#include "file_id.h" + +static +do_phonebook_file(file_id, book_name) + unsigned file_id; + char *book_name; +{ + int rc; + + rc = select_op(file_id); + if (rc < 0) { + printf("%s not present\n", book_name); + return(rc); + } + rc = parse_ef_select_response(); + if (rc < 0) { + fprintf(stderr, "error occurred on SELECT of EF_%s\n", + book_name); + return(rc); + } + if (curfile_structure != 0x01) { + fprintf(stderr, "error: EF_%s is not linear fixed\n", + book_name); + return(-1); + } + if (curfile_record_len < 14) { + fprintf(stderr, + "error: EF_%s has record length of %u bytes, less than minimum 14\n", + book_name, curfile_record_len); + return(-1); + } + printf("%s has %u entries, %u bytes of alpha tag\n", book_name, + curfile_record_count, curfile_record_len - 14); + return(0); +} + +static +do_sms_store() +{ + int rc; + + rc = select_op(EF_SMS); + if (rc < 0) { + printf("EF_SMS not present\n"); + return(rc); + } + rc = parse_ef_select_response(); + if (rc < 0) { + fprintf(stderr, "error occurred on SELECT of EF_SMS\n"); + return(rc); + } + if (curfile_structure != 0x01 || curfile_record_len != 176) { + fprintf(stderr, + "error: EF_SMS is not linear fixed with 176-byte records\n"); + return(-1); + } + printf("SMS store has %u entries\n", curfile_record_count); + return(0); +} + +cmd_telecom_sum() +{ + int rc; + + rc = select_op(DF_TELECOM); + if (rc < 0) + return(rc); + do_phonebook_file(EF_ADN, "ADN"); + do_phonebook_file(EF_FDN, "FDN"); + do_phonebook_file(EF_SDN, "SDN"); + do_phonebook_file(EF_MSISDN, "MSISDN"); + do_sms_store(); + return(0); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/writecmd.c Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,65 @@ +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include "curfile.h" + +cmd_update_bin(argc, argv) + char **argv; +{ + unsigned offset, len; + u_char data[255]; + int rc; + + offset = strtoul(argv[1], 0, 0); + if (offset > 0xFFFF) { + 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 > 0xFFFF) { + 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; + + 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 != curfile_record_len) { + fprintf(stderr, "error: hex data length != EF record length\n"); + return(-1); + } + return update_rec_op(recno, 0x04, data, curfile_record_len); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/writeops.c Thu Feb 11 23:04:28 2021 +0000 @@ -0,0 +1,56 @@ +#include <sys/types.h> +#include <string.h> +#include <strings.h> +#include <stdio.h> +#include <stdlib.h> +#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] = 0xA0; + 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] = 0xA0; + 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); +}