view simtool/select.c @ 93:6041c601304d

fcsim1-mkprov: revert OTA key addition It appears that GrcardSIM2 cards (which is what we got for FCSIM1) do not support OTA after all, contrary to what we were previously led to believe by some tech support emails from Grcard - apparently those support emails and OTA descriptions referred to some other card model(s).
author Mychaela Falconia <falcon@freecalypso.org>
date Wed, 21 Apr 2021 05:38:39 +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);
}