view target-utils/simagent/exchange.c @ 783:c136a1a2474b

simagent: initial implementation of APDU exchange
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 13 Mar 2021 22:06:08 +0000
parents
children 839bf41e7be0
line wrap: on
line source

/*
 * This module implements our main function: exchange of command
 * and response APDUs.
 */

#include "types.h"

extern int sim_if_state;

static void
finish_sw(sw1, data, datalen)
	unsigned sw1, datalen;
	u8 *data;
{
	unsigned sw2, n;
	int rc;

	rc = rx_sim_byte_hl();
	if (rc < 0)
		return;
	sw2 = rc;
	for (n = 0; n < datalen; n++)
		printf("%02X", data[n]);
	printf("%02X%02X\n", sw1, sw2);
}

static void
exchange_data_out(ins, data, datalen)
	unsigned ins, datalen;
	u8 *data;
{
	int rc;
	unsigned null_count, bytes_sent, ack, ack1;

	ack = ins & 0xFE;
	ack1 = ~ins & 0xFE;
	bytes_sent = 0;
	null_count = 0;
	for (;;) {
		rc = rx_sim_byte_hl();
		if (rc < 0)
			return;
		if (rc == 0x60) {
			null_count++;
			if (null_count >= 32) {
				printf(
		"ERROR: too many stalling NULL bytes received from SIM\n");
				return;
			}
			continue;
		}
		if ((rc & 0xF0) == 0x60 || (rc & 0xF0) == 0x90) {
			finish_sw(rc, 0, 0);
			return;
		}
		if ((rc & 0xFE) == ack) {
			if (bytes_sent >= datalen) {
bad_xfer_req:			printf(
		"ERROR: SIM requests more xfer after we sent everything\n");
				return;
			}
			rc = send_to_sim(data + bytes_sent,
					 datalen - bytes_sent);
			if (rc < 0)
				return;
			bytes_sent = datalen;
			continue;
		}
		if ((rc & 0xFE) == ack1) {
			if (bytes_sent >= datalen)
				goto bad_xfer_req;
			rc = send_to_sim(data + bytes_sent, 1);
			if (rc < 0)
				return;
			bytes_sent++;
			continue;
		}
		printf("ERROR: non-understood procedure byte %02X\n", rc);
		return;
	}
}

static void
exchange_data_in(ins, datalen)
	unsigned ins, datalen;
{
	printf("ERROR: data in mode not implemented yet\n");
}

void
cmd_exchange(argstr)
	char *argstr;
{
	u8 cmd[260];
	int rc;

	if (sim_if_state != 2) {
		printf("ERROR: SIM interface is not up\n");
		return;
	}
	rc = decode_hex_string_arg(argstr, cmd, 260);
	if (rc < 0)
		return;
	if (rc < 5) {
		printf("ERROR: command APDU is shorter than 5 bytes\n");
		return;
	}
	rc = flush_rx_fifo();
	if (rc < 0)
		return;
	rc = send_to_sim(cmd, 5);
	if (rc < 0)
		return;
	if (rc > 5)
		exchange_data_out(cmd[1], cmd + 5, rc - 5);
	else
		exchange_data_in(cmd[1], cmd[4]);
}