view serial/exchange.c @ 47:b0cf75d0bb2d

doc/Serial-SIM-readers article written
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 21 Mar 2021 04:32:18 +0000
parents be27d1c85861
children
line wrap: on
line source

/*
 * This module implements the main function of our back end:
 * exchanging APDUs with the SIM.
 */

#include <sys/types.h>
#include <stdio.h>

static
collect_one_byte()
{
	u_char buf;
	int rc;

	rc = collect_bytes_from_sim(&buf, 1);
	if (rc < 0)
		return rc;
	else
		return buf;
}

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

	rc = collect_one_byte();
	if (rc < 0) {
		printf("back end error at SW2 Rx step\n");
		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;
	u_char *data;
{
	int rc;
	unsigned null_count, bytes_sent, ack, ack1;

	ack = ins & 0xFE;
	ack1 = ~ins & 0xFE;
	bytes_sent = 0;
	null_count = 0;
	for (;;) {
		rc = collect_one_byte();
		if (rc < 0) {
			printf("back end error at procedure byte step\n");
			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_bytes_to_sim(data + bytes_sent,
						datalen - bytes_sent);
			if (rc < 0) {
				printf("back end error at data output step\n");
				return;
			}
			bytes_sent = datalen;
			continue;
		}
		if ((rc & 0xFE) == ack1) {
			if (bytes_sent >= datalen)
				goto bad_xfer_req;
			rc = send_bytes_to_sim(data + bytes_sent, 1);
			if (rc < 0) {
				printf("back end error at data output step\n");
				return;
			}
			bytes_sent++;
			continue;
		}
		printf("ERROR: non-understood procedure byte %02X\n", rc);
		return;
	}
}

static void
exchange_data_in(ins, datalen)
	unsigned ins, datalen;
{
	int rc;
	unsigned null_count, bytes_rcvd, ack, ack1;
	u_char data[256];

	if (!datalen)
		datalen = 256;
	ack = ins & 0xFE;
	ack1 = ~ins & 0xFE;
	bytes_rcvd = 0;
	null_count = 0;
	for (;;) {
		rc = collect_one_byte();
		if (rc < 0) {
			printf("back end error at procedure byte step\n");
			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, data, bytes_rcvd);
			return;
		}
		if ((rc & 0xFE) == ack) {
			if (bytes_rcvd >= datalen) {
bad_xfer_req:			printf(
	"ERROR: SIM requests more xfer after we received all expected data\n");
				return;
			}
			rc = collect_bytes_from_sim(data + bytes_rcvd,
						    datalen - bytes_rcvd);
			if (rc < 0) {
				printf("back end error at data input step\n");
				return;
			}
			bytes_rcvd = datalen;
			continue;
		}
		if ((rc & 0xFE) == ack1) {
			if (bytes_rcvd >= datalen)
				goto bad_xfer_req;
			rc = collect_one_byte();
			if (rc < 0) {
				printf("back end error at data input step\n");
				return;
			}
			data[bytes_rcvd++] = rc;
			continue;
		}
		printf("ERROR: non-understood procedure byte %02X\n", rc);
		return;
	}
}

void
apdu_exchange(cmd_apdu, cmd_apdu_len)
	u_char *cmd_apdu;
	unsigned cmd_apdu_len;
{
	int rc;

	rc = send_bytes_to_sim(cmd_apdu, 5);
	if (rc < 0) {
		printf("back end error at the command sending step\n");
		return;
	}
	if (cmd_apdu_len > 5)
		exchange_data_out(cmd_apdu[1], cmd_apdu + 5, cmd_apdu_len - 5);
	else
		exchange_data_in(cmd_apdu[1], cmd_apdu[4]);
}