view uicc/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 db131929ee96
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 "efstruct.h"

u_char std_aid_usim[7] = {0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x02};
u_char std_aid_isim[7] = {0xA0, 0x00, 0x00, 0x00, 0x87, 0x10, 0x04};

unsigned last_sel_file_record_len;

elem_select_op(file_id)
	unsigned file_id;
{
	u_char cmd[7];
	int rc;

	last_sel_file_record_len = 0;
	/* SELECT command APDU */
	cmd[0] = 0x00;
	cmd[1] = 0xA4;
	cmd[2] = 0x00;
	cmd[3] = 0x04;
	cmd[4] = 2;
	cmd[5] = file_id >> 8;
	cmd[6] = file_id;
	return apdu_exchange(cmd, 7);
}

select_op(file_id)
	unsigned file_id;
{
	u_char cmd[7];
	int rc;
	unsigned expect_resp_len;

	last_sel_file_record_len = 0;
	/* SELECT command APDU */
	cmd[0] = 0x00;
	cmd[1] = 0xA4;
	cmd[2] = 0x00;
	cmd[3] = 0x04;
	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) != 0x6100) {
		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[2] = 0;
	cmd[3] = 0;
	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);
}

select_aid_op(aid, aid_len)
	u_char *aid;
	unsigned aid_len;
{
	u_char cmd[21];
	int rc;
	unsigned expect_resp_len;

	last_sel_file_record_len = 0;
	/* SELECT command APDU */
	cmd[0] = 0x00;
	cmd[1] = 0xA4;
	cmd[2] = 0x04;
	cmd[3] = 0x04;
	cmd[4] = aid_len;
	bcopy(aid, cmd + 5, aid_len);
	rc = apdu_exchange(cmd, aid_len + 5);
	if (rc < 0)
		return(rc);
	if ((sim_resp_sw & 0xFF00) != 0x6100) {
		fprintf(stderr,
		"error or unexpected SW response to SELECT by AID: %04X\n",
			sim_resp_sw);
		return(-1);
	}
	expect_resp_len = sim_resp_sw & 0xFF;
	/* GET RESPONSE follow-up */
	cmd[1] = 0xC0;
	cmd[2] = 0;
	cmd[3] = 0;
	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);
}

select_resp_header_check(ret_offset, ret_length)
	unsigned *ret_offset, *ret_length;
{
	unsigned offset, len;

	if (sim_resp_data_len < 2) {
tooshort:	fprintf(stderr, "error: SELECT response is too short\n");
		return(-1);
	}
	if (sim_resp_data[0] != 0x62) {
		fprintf(stderr, "error: SELECT response first byte != 0x62\n");
		return(-1);
	}
	len = sim_resp_data[1];
	if (len <= 0x7F) {
		offset = 2;
return_check:	if (offset + len > sim_resp_data_len)
			goto tooshort;
		if (ret_offset)
			*ret_offset = offset;
		if (ret_length)
			*ret_length = len;
		return(0);
	}
	if (len != 0x81) {
		fprintf(stderr, "SELECT response: first length byte is bad\n");
		return(-1);
	}
	if (sim_resp_data_len < 3)
		goto tooshort;
	len = sim_resp_data[2];
	offset = 3;
	goto return_check;
}

static void
check_for_record_struct(tlv)
	u_char *tlv;
{
	unsigned reclen;

	if (tlv[1] != 5)
		return;
	if (tlv[2] & 0x80)
		return;
	if ((tlv[2] & 0x38) == 0x38)
		return;
	if ((tlv[2] & 0x03) != 0x02)
		return;
	reclen = (tlv[4] << 8) | tlv[5];
	if (reclen < 1 || reclen > 255)
		return;
	last_sel_file_record_len = reclen;
}

parse_and_display_select_response(outf)
	FILE *outf;
{
	unsigned offset, totlen, reclen, n;
	u_char *dp, *endp;
	int rc;

	rc = select_resp_header_check(&offset, &totlen);
	if (rc < 0)
		return(rc);
	dp = sim_resp_data + offset;
	endp = sim_resp_data + offset + totlen;
	while (dp < endp) {
		if (endp - dp < 2) {
trunc_error:		fprintf(stderr,
			"error: truncated TLV record in SELECT response\n");
			return(-1);
		}
		if ((dp[0] & 0x1F) == 0x1F) {
			fprintf(stderr,
		"error: extended tag not supported in SELECT response\n");
			return(-1);
		}
		if (dp[1] & 0x80) {
			fprintf(stderr,
		"error: extended length not supported in SELECT response\n");
			return(-1);
		}
		reclen = dp[1] + 2;
		if (endp - dp < reclen)
			goto trunc_error;
		if (dp[0] == 0x82)
			check_for_record_struct(dp);
		for (n = 0; n < reclen; n++) {
			if (n)
				putc(' ', outf);
			fprintf(outf, "%02X", *dp++);
		}
		putc('\n', outf);
	}
	return(0);
}

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);
	return parse_and_display_select_response(outf);
}

cmd_select_aid(argc, argv, outf)
	char **argv;
	FILE *outf;
{
	u_char aid[16];
	unsigned aid_len;
	int rc;

	rc = decode_hex_data_from_string(argv[1], aid, 1, 16);
	if (rc < 0)
		return(rc);
	aid_len = rc;
	rc = select_aid_op(aid, aid_len);
	if (rc < 0)
		return(rc);
	return parse_and_display_select_response(outf);
}

cmd_select_usim(argc, argv, outf)
	char **argv;
	FILE *outf;
{
	int rc;

	rc = select_aid_op(std_aid_usim, 7);
	if (rc < 0)
		return(rc);
	return parse_and_display_select_response(outf);
}

cmd_select_isim(argc, argv, outf)
	char **argv;
	FILE *outf;
{
	int rc;

	rc = select_aid_op(std_aid_isim, 7);
	if (rc < 0)
		return(rc);
	return parse_and_display_select_response(outf);
}

u_char *
extract_select_resp_tag(sought_tag)
	unsigned sought_tag;
{
	unsigned offset, totlen, reclen;
	u_char *dp, *endp;
	int rc;

	rc = select_resp_header_check(&offset, &totlen);
	if (rc < 0)
		return(0);
	dp = sim_resp_data + offset;
	endp = sim_resp_data + offset + totlen;
	while (dp < endp) {
		if (endp - dp < 2) {
trunc_error:		fprintf(stderr,
			"error: truncated TLV record in SELECT response\n");
			return(0);
		}
		if ((dp[0] & 0x1F) == 0x1F) {
			fprintf(stderr,
		"error: extended tag not supported in SELECT response\n");
			return(0);
		}
		if (dp[1] & 0x80) {
			fprintf(stderr,
		"error: extended length not supported in SELECT response\n");
			return(0);
		}
		reclen = dp[1] + 2;
		if (endp - dp < reclen)
			goto trunc_error;
		if (dp[0] == sought_tag)
			return(dp);
		dp += reclen;
	}
	fprintf(stderr, "error: tag 0x%02X not found in SELECT response\n",
		sought_tag);
	return(0);
}

select_resp_get_transparent(lenp)
	unsigned *lenp;
{
	u_char *tlv;

	tlv = extract_select_resp_tag(0x82);
	if (!tlv)
		return(-1);
	if (tlv[1] != 2) {
bad_file_desc:	fprintf(stderr, "error: file type is not transparent EF\n");
		return(-1);
	}
	if (tlv[2] & 0x80)
		goto bad_file_desc;
	if ((tlv[2] & 0x38) == 0x38)
		goto bad_file_desc;
	if ((tlv[2] & 0x07) != 0x01)
		goto bad_file_desc;
	tlv = extract_select_resp_tag(0x80);
	if (!tlv)
		return(-1);
	if (tlv[1] != 2) {
		fprintf(stderr,
			"error: file size TLV element has wrong length\n");
		return(-1);
	}
	if (lenp)
		*lenp = (tlv[2] << 8) | tlv[3];
	return(0);
}

select_resp_get_linear_fixed(rec_len_ret, rec_count_ret)
	unsigned *rec_len_ret, *rec_count_ret;
{
	u_char *tlv;
	unsigned reclen;

	tlv = extract_select_resp_tag(0x82);
	if (!tlv)
		return(-1);
	if (tlv[1] != 5) {
bad_file_desc:	fprintf(stderr, "error: file type is not linear fixed EF\n");
		return(-1);
	}
	if (tlv[2] & 0x80)
		goto bad_file_desc;
	if ((tlv[2] & 0x38) == 0x38)
		goto bad_file_desc;
	if ((tlv[2] & 0x07) != 0x02)
		goto bad_file_desc;
	reclen = (tlv[4] << 8) | tlv[5];
	if (reclen < 1 || reclen > 255) {
		fprintf(stderr,
			"error: SELECT response gives invalid record length\n");
		return(-1);
	}
	if (rec_len_ret)
		*rec_len_ret = reclen;
	if (rec_count_ret)
		*rec_count_ret = tlv[6];
	return(0);
}

select_resp_get_ef_struct(efs)
	struct ef_struct *efs;
{
	u_char *tlv;

	tlv = extract_select_resp_tag(0x82);
	if (!tlv)
		return(-1);
	if (tlv[1] < 2) {
bad_file_desc:	fprintf(stderr, "error: unable to figure out file structure\n");
		return(-1);
	}
	if (tlv[2] & 0x80)
		goto bad_file_desc;
	if ((tlv[2] & 0x38) == 0x38)
		goto bad_file_desc;
	efs->structure = tlv[2] & 0x07;
	switch (efs->structure) {
	case 0x01:
		if (tlv[1] != 2) {
			fprintf(stderr,
		"error: file descriptor TLV element has wrong length\n");
			return(-1);
		}
		tlv = extract_select_resp_tag(0x80);
		if (!tlv)
			return(-1);
		if (tlv[1] != 2) {
			fprintf(stderr,
			"error: file size TLV element has wrong length\n");
			return(-1);
		}
		efs->total_size = (tlv[2] << 8) | tlv[3];
		if (efs->total_size > 0x8000) {
			fprintf(stderr,
		"error: transparent file size exceeds UICC protocol limit\n");
			return(-1);
		}
		return(0);
	case 0x02:
	case 0x06:
		if (tlv[1] != 5) {
			fprintf(stderr,
		"error: file descriptor TLV element has wrong length\n");
			return(-1);
		}
		efs->record_len = (tlv[4] << 8) | tlv[5];
		if (efs->record_len < 1 || efs->record_len > 255) {
			fprintf(stderr,
			"error: SELECT response gives invalid record length\n");
			return(-1);
		}
		efs->record_count = tlv[6];
		efs->total_size = efs->record_len * efs->record_count;
		return(0);
	default:
		goto bad_file_desc;
	}
}