FreeCalypso > hg > fc-sim-tools
diff simtool/select.c @ 10:ddd767f6e15b
fc-simtool ported over
author | Mychaela Falconia <falcon@freecalypso.org> |
---|---|
date | Sun, 14 Mar 2021 07:11:25 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simtool/select.c Sun Mar 14 07:11:25 2021 +0000 @@ -0,0 +1,263 @@ +#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" + +elem_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 == 0x9404) + return(0); + if ((sim_resp_sw & 0xFF00) == 0x9F00) + return(1); + fprintf(stderr, + "error or unexpected SW response to SELECT of 0x%04X: %04X\n", + file_id, sim_resp_sw); + return(-1); +} + +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(outf, name, byte) + FILE *outf; + char *name; + unsigned byte; +{ + fprintf(outf, "Status of %s: %s, %u attempts left\n", name, + byte & 0x80 ? "initialized" : "not initialized", + byte & 0x0F); +} + +void +show_access_conditions(outf, oper_name, cond_code) + FILE *outf; + 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"}; + + fprintf(outf, "Access condition for %s: %s\n", oper_name, + cond_names[cond_code]); +} + +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); + 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: + fprintf(outf, "File type: MF\n"); + goto mf_or_df; + case 0x02: + fprintf(outf, "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); + } + fprintf(outf, "File characteristics: %02X\n", + sim_resp_data[13]); + fprintf(outf, "Number of DF children: %u\n", sim_resp_data[14]); + fprintf(outf, "Number of EF children: %u\n", sim_resp_data[15]); + fprintf(outf, "Number of secret codes: %u\n", + sim_resp_data[16]); + show_secret_code_status(outf, "PIN1", sim_resp_data[18]); + show_secret_code_status(outf, "PUK1", sim_resp_data[19]); + show_secret_code_status(outf, "PIN2", sim_resp_data[20]); + show_secret_code_status(outf, "PUK2", sim_resp_data[21]); + break; + case 0x04: + fprintf(outf, "File type: EF\n"); + curfile_total_size = (sim_resp_data[2] << 8) | sim_resp_data[3]; + fprintf(outf, "File size: %u\n", curfile_total_size); + curfile_structure = sim_resp_data[13]; + switch (curfile_structure) { + case 0x00: + fprintf(outf, "Structure: transparent\n"); + break; + case 0x01: + fprintf(outf, "Structure: linear fixed\n"); + goto ef_record_based; + case 0x03: + fprintf(outf, "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); + } + fprintf(outf, "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; + fprintf(outf, "Number of records: %u\n", + curfile_record_count); + } else + curfile_record_count = 0; + break; + default: + fprintf(outf, "Structure: %02X (unknown)\n", + curfile_structure); + } + fprintf(outf, "File status: %02X\n", sim_resp_data[11]); + show_access_conditions(outf, "UPDATE", sim_resp_data[8] & 0xF); + show_access_conditions(outf, "READ & SEEK", + sim_resp_data[8] >> 4); + show_access_conditions(outf, "INCREASE", sim_resp_data[9] >> 4); + show_access_conditions(outf, "INVALIDATE", + sim_resp_data[10] & 0xF); + show_access_conditions(outf, "REHABILITATE", + sim_resp_data[10] >> 4); + break; + default: + fprintf(outf, "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; + if (curfile_record_count > 255) { + fprintf(stderr, + "error: EF record count exceeds protocol limit\n"); + return(-1); + } + break; + default: + fprintf(stderr, "error: unknown EF structure code %02X\n", + curfile_structure); + return(-1); + } + return(0); +}