view simtool/select.c @ 74:8562d8508cf2

grcard2-set-{adm,super}-hex commands implemented It appears that GrcardSIM2 cards allow arbitrary 64-bit keys for ADM and SUPER ADM, not necessarily consisting of ASCII digits like the specs require for standard PIN and PUK, and pySim-prog.py in fact sets the ADM key to 4444444444444444 in hex by default, which is not an ASCII digit string. If the cards allow such keys, we need to support them too.
author Mychaela Falconia <falcon@freecalypso.org>
date Tue, 16 Feb 2021 04:10:36 +0000
parents 2071b28cd0c7
children b0982c0cf54d
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"

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