diff serial/exchange.c @ 43:be27d1c85861

serial: main function implemented
author Mychaela Falconia <falcon@freecalypso.org>
date Sat, 20 Mar 2021 21:49:59 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/serial/exchange.c	Sat Mar 20 21:49:59 2021 +0000
@@ -0,0 +1,183 @@
+/*
+ * 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]);
+}