view simtool/select.c @ 99:97ba63d9361a

scripts/fcsim1-sst: turn off STK & OTA services In the initial unprogrammed state of the cards from Grcard, SST has services 25 through 29 set to allocated and activated. However, these cards appear to not actually support OTA, ENVELOPE commands do nothing (just return SW 9000), and they were never observed issuing any proactive SIM commands, even after a feature-generous TERMINAL PROFILE. Therefore, let's list these STK & OTA services as allocated, but not activated in our FCSIM1 SST.
author Mychaela Falconia <falcon@freecalypso.org>
date Wed, 05 May 2021 04:26:07 +0000
parents ddd767f6e15b
children
line wrap: on
line source

#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);
}