changeset 41:118a12e9483b

simtrace3-sniff-dec started
author Mychaela Falconia <falcon@freecalypso.org>
date Thu, 31 Aug 2023 08:46:23 +0000
parents 510bef2b2000
children 5804ff735f9e
files .hgignore sw/sniff-dec/Makefile sw/sniff-dec/atr.c sw/sniff-dec/command.c sw/sniff-dec/dispatch.c sw/sniff-dec/invtable.c sw/sniff-dec/main.c sw/sniff-dec/parity.c sw/sniff-dec/pps.c sw/sniff-dec/state.h
diffstat 10 files changed, 617 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Wed Aug 30 05:39:53 2023 +0000
+++ b/.hgignore	Thu Aug 31 08:46:23 2023 +0000
@@ -19,4 +19,5 @@
 ^boards/sim-fpc-pasv/pcb/gerbers\.
 ^boards/sim-fpc-pasv/src/elements\.pcb$
 
+^sw/sniff-dec/simtrace3-sniff-dec$
 ^sw/sniff-rx/simtrace3-sniff-rx$
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sw/sniff-dec/Makefile	Thu Aug 31 08:46:23 2023 +0000
@@ -0,0 +1,20 @@
+CC=	gcc
+CFLAGS=	-O2
+PROG=	simtrace3-sniff-dec
+OBJS=	atr.o command.o dispatch.o invtable.o main.o parity.o pps.o
+
+INSTALL_PREFIX=	/opt/freecalypso
+
+INSTBIN=${INSTALL_PREFIX}/bin
+
+all:	${PROG}
+
+${PROG}:	${OBJS} ${LIBS}
+	${CC} ${CFLAGS} -o $@ ${OBJS} ${LIBS}
+
+install:
+	mkdir -p ${INSTBIN}
+	install -c ${PROG} ${INSTBIN}
+
+clean:
+	rm -f *.o ${PROG}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sw/sniff-dec/atr.c	Thu Aug 31 08:46:23 2023 +0000
@@ -0,0 +1,175 @@
+/*
+ * Here we implement ATR decoding.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "state.h"
+
+extern char linebuf[];
+extern int lineno;
+extern unsigned rx_byte;
+extern int state;
+
+#define	MAX_ATR_BYTES	33
+
+#define	SUBST_TS	0
+#define	SUBST_T0	1
+#define	SUBST_TAn	2
+#define	SUBST_TBn	3
+#define	SUBST_TCn	4
+#define	SUBST_TDn	5
+#define	SUBST_HIST	6
+#define	SUBST_TCK	7
+
+static char atr_start_timestamp[18];
+static int atr_start_line;
+static int substate;
+static u_char atr_bytes[MAX_ATR_BYTES];
+static unsigned byte_count;
+static u_char latch_y, latch_k, have_tck;
+
+void
+atr_begin()
+{
+	strcpy(atr_start_timestamp, linebuf);
+	atr_start_line = lineno;
+	substate = SUBST_TS;
+	byte_count = 0;
+}
+
+static int
+advance_state()
+{
+	if (substate == SUBST_TAn) {
+		if (latch_y & 0x10)
+			return 0;
+		substate = SUBST_TBn;
+	}
+	if (substate == SUBST_TBn) {
+		if (latch_y & 0x20)
+			return 0;
+		substate = SUBST_TCn;
+	}
+	if (substate == SUBST_TCn) {
+		if (latch_y & 0x40)
+			return 0;
+		substate = SUBST_TDn;
+	}
+	if (substate == SUBST_TDn) {
+		if (latch_y & 0x80)
+			return 0;
+		substate = SUBST_HIST;
+	}
+	if (substate == SUBST_HIST) {
+		if (latch_k)
+			return 0;
+		substate = SUBST_TCK;
+	}
+	if (substate == SUBST_TCK) {
+		if (have_tck)
+			return 0;
+		return 1;
+	}
+	fprintf(stderr, "BUG in ATR decoder: bad state in advance_state()\n");
+	abort();
+}
+
+static void
+check_tck()
+{
+	unsigned n, xor;
+
+	xor = 0;
+	for (n = 1; n < byte_count; n++)
+		xor ^= atr_bytes[n];
+	printf(" TCK is %s\n", xor ? "bad!" : "correct");
+}
+
+static void
+atr_finish()
+{
+	unsigned n;
+
+	printf("%s line %d: ATR\n", atr_start_timestamp, atr_start_line);
+	for (n = 0; n < byte_count; n++)
+		printf(" %02X", atr_bytes[n]);
+	putchar('\n');
+	if (have_tck)
+		check_tck();
+	state = STATE_READY_FOR_CMD;
+}
+
+void
+atr_byte_in()
+{
+	atr_bytes[byte_count++] = rx_byte;
+	switch (substate) {
+	case SUBST_TS:
+		substate = SUBST_T0;
+		return;
+	case SUBST_T0:
+		latch_y = rx_byte & 0xF0;
+		latch_k = rx_byte & 0x0F;
+		have_tck = 0;
+		substate = SUBST_TAn;
+		if (advance_state()) {
+			atr_finish();
+			return;
+		}
+		return;
+	case SUBST_TAn:
+		substate = SUBST_TBn;
+		if (advance_state()) {
+			atr_finish();
+			return;
+		}
+		break;
+	case SUBST_TBn:
+		substate = SUBST_TCn;
+		if (advance_state()) {
+			atr_finish();
+			return;
+		}
+		break;
+	case SUBST_TCn:
+		substate = SUBST_TDn;
+		if (advance_state()) {
+			atr_finish();
+			return;
+		}
+		break;
+	case SUBST_TDn:
+		latch_y = rx_byte & 0xF0;
+		if (rx_byte & 0x0F)
+			have_tck = 1;
+		substate = SUBST_TAn;
+		if (advance_state()) {
+			atr_finish();
+			return;
+		}
+		break;
+	case SUBST_HIST:
+		latch_k--;
+		if (advance_state()) {
+			atr_finish();
+			return;
+		}
+		break;
+	case SUBST_TCK:
+		atr_finish();
+		return;
+	default:
+		fprintf(stderr,
+			"BUG in ATR decoder: bad state in atr_byte_in()\n");
+		abort();
+	}
+	if (byte_count < MAX_ATR_BYTES)
+		return;
+	printf("%s line %d: ERROR: ATR is too long\n", atr_start_timestamp,
+		atr_start_line);
+	state = STATE_ERROR;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sw/sniff-dec/command.c	Thu Aug 31 08:46:23 2023 +0000
@@ -0,0 +1,23 @@
+/*
+ * Here we implement decoding of command APDU exchanges.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "state.h"
+
+extern char linebuf[];
+extern int lineno;
+extern unsigned rx_byte;
+extern int state;
+
+void
+start_cmd_header()
+{
+	printf("input line %d: command header, end of implementation so far\n",
+		lineno);
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sw/sniff-dec/dispatch.c	Thu Aug 31 08:46:23 2023 +0000
@@ -0,0 +1,150 @@
+/*
+ * Dispatching received FPGA words based on the current state.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "state.h"
+
+extern char linebuf[];
+extern int lineno;
+extern unsigned fpga_word;
+
+extern u_char parity_table[256];
+extern u_char inverse_coding_table[256];
+
+int state;
+int inverse_conv;
+unsigned rx_byte;
+
+#define	PREFIX	"%s line %d: "
+
+static void
+handle_rst_change()
+{
+	if (fpga_word & 0x4000) {
+		printf(PREFIX "RST high, card session begin\n",
+			linebuf, lineno);
+		state = STATE_ATR_TS;
+		atr_begin();
+	} else {
+		printf(PREFIX "RST low, card session end\n", linebuf, lineno);
+		state = STATE_RSTLOW;
+	}
+}
+
+static int
+byte_prelim_checks()
+{
+	if (!(fpga_word & 0x4000)) {
+		printf(PREFIX "ERROR: char Rx with RST low\n", linebuf, lineno);
+		return 1;
+	}
+	if (fpga_word & 0x0200) {
+		printf(PREFIX "ERROR: start bit midpoint sampled high\n",
+			linebuf, lineno);
+		return 1;
+	}
+	if (fpga_word & 0x0400) {
+		printf(PREFIX "ISO 7816-3 section 7.3 error signal\n",
+			linebuf, lineno);
+		return 1;
+	}
+	return 0;
+}
+
+static int
+check_parity()
+{
+	unsigned expect_par;
+
+	expect_par = (parity_table[fpga_word & 0xFF] ^ inverse_conv) << 8;
+	if ((fpga_word & 0x100) != expect_par) {
+		printf(PREFIX "ERROR: bad character parity\n", linebuf, lineno);
+		state = STATE_ERROR;
+		return 1;
+	}
+	return 0;
+}
+
+static void
+extract_rx_byte()
+{
+	if (inverse_conv)
+		rx_byte = inverse_coding_table[fpga_word & 0xFF];
+	else
+		rx_byte = fpga_word & 0xFF;
+}
+
+void
+process_fpga_word()
+{
+	if (fpga_word & 0x8000) {
+		handle_rst_change();
+		return;
+	}
+	if (state == STATE_ERROR)
+		return;
+	if (byte_prelim_checks()) {
+		state = STATE_ERROR;
+		return;
+	}
+	switch (state) {
+	case STATE_UNDEF:
+		printf(PREFIX "ERROR: char Rx without preceding RST high\n",
+			linebuf, lineno);
+		state = STATE_ERROR;
+		return;
+	case STATE_RSTLOW:
+		printf(PREFIX "ERROR: char Rx after RST low\n",
+			linebuf, lineno);
+		state = STATE_ERROR;
+		return;
+	case STATE_ATR_TS:
+		if ((fpga_word & 0x1FF) == 0x13B) {
+			printf(PREFIX "TS sets direct coding convention\n",
+				linebuf, lineno);
+			inverse_conv = 0;
+			rx_byte = 0x3B;
+			atr_byte_in();
+			state = STATE_ATR_CONT;
+		} else if ((fpga_word & 0x1FF) == 0x103) {
+			printf(PREFIX "TS sets inverse coding convention\n",
+				linebuf, lineno);
+			inverse_conv = 1;
+			rx_byte = 0x3F;
+			atr_byte_in();
+			state = STATE_ATR_CONT;
+		} else {
+			printf(PREFIX "ERROR: invalid char after RST high\n",
+				linebuf, lineno);
+			state = STATE_ERROR;
+		}
+		return;
+	case STATE_ATR_CONT:
+		if (check_parity())
+			return;
+		extract_rx_byte();
+		atr_byte_in();
+		return;
+	case STATE_READY_FOR_CMD:
+		if (check_parity())
+			return;
+		extract_rx_byte();
+		if (rx_byte == 0xFF)
+			start_pps_msg();
+		else
+			start_cmd_header();
+		return;
+	case STATE_PPS_MSG:
+		if (check_parity())
+			return;
+		extract_rx_byte();
+		pps_byte_in();
+		return;
+	default:
+		fprintf(stderr, "BUG in top state machine: invalid state\n");
+		abort();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sw/sniff-dec/invtable.c	Thu Aug 31 08:46:23 2023 +0000
@@ -0,0 +1,27 @@
+/*
+ * We would like to be able to decode trace sessions with SIM cards using
+ * either direct or inverse coding convention.  When inverse coding convention
+ * is in use, we have to translate every logged byte in software.  Here we
+ * define the table for the inverse coding convention.
+ */
+
+unsigned char inverse_coding_table[256] = {
+
+0xFF,0x7F,0xBF,0x3F,0xDF,0x5F,0x9F,0x1F,0xEF,0x6F,0xAF,0x2F,0xCF,0x4F,0x8F,0x0F,
+0xF7,0x77,0xB7,0x37,0xD7,0x57,0x97,0x17,0xE7,0x67,0xA7,0x27,0xC7,0x47,0x87,0x07,
+0xFB,0x7B,0xBB,0x3B,0xDB,0x5B,0x9B,0x1B,0xEB,0x6B,0xAB,0x2B,0xCB,0x4B,0x8B,0x0B,
+0xF3,0x73,0xB3,0x33,0xD3,0x53,0x93,0x13,0xE3,0x63,0xA3,0x23,0xC3,0x43,0x83,0x03,
+0xFD,0x7D,0xBD,0x3D,0xDD,0x5D,0x9D,0x1D,0xED,0x6D,0xAD,0x2D,0xCD,0x4D,0x8D,0x0D,
+0xF5,0x75,0xB5,0x35,0xD5,0x55,0x95,0x15,0xE5,0x65,0xA5,0x25,0xC5,0x45,0x85,0x05,
+0xF9,0x79,0xB9,0x39,0xD9,0x59,0x99,0x19,0xE9,0x69,0xA9,0x29,0xC9,0x49,0x89,0x09,
+0xF1,0x71,0xB1,0x31,0xD1,0x51,0x91,0x11,0xE1,0x61,0xA1,0x21,0xC1,0x41,0x81,0x01,
+0xFE,0x7E,0xBE,0x3E,0xDE,0x5E,0x9E,0x1E,0xEE,0x6E,0xAE,0x2E,0xCE,0x4E,0x8E,0x0E,
+0xF6,0x76,0xB6,0x36,0xD6,0x56,0x96,0x16,0xE6,0x66,0xA6,0x26,0xC6,0x46,0x86,0x06,
+0xFA,0x7A,0xBA,0x3A,0xDA,0x5A,0x9A,0x1A,0xEA,0x6A,0xAA,0x2A,0xCA,0x4A,0x8A,0x0A,
+0xF2,0x72,0xB2,0x32,0xD2,0x52,0x92,0x12,0xE2,0x62,0xA2,0x22,0xC2,0x42,0x82,0x02,
+0xFC,0x7C,0xBC,0x3C,0xDC,0x5C,0x9C,0x1C,0xEC,0x6C,0xAC,0x2C,0xCC,0x4C,0x8C,0x0C,
+0xF4,0x74,0xB4,0x34,0xD4,0x54,0x94,0x14,0xE4,0x64,0xA4,0x24,0xC4,0x44,0x84,0x04,
+0xF8,0x78,0xB8,0x38,0xD8,0x58,0x98,0x18,0xE8,0x68,0xA8,0x28,0xC8,0x48,0x88,0x08,
+0xF0,0x70,0xB0,0x30,0xD0,0x50,0x90,0x10,0xE0,0x60,0xA0,0x20,0xC0,0x40,0x80,0x00
+
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sw/sniff-dec/main.c	Thu Aug 31 08:46:23 2023 +0000
@@ -0,0 +1,69 @@
+/*
+ * simtrace3-sniff-dec main module: reading the log file.
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+
+char *log_filename;
+char linebuf[128];
+int lineno;
+unsigned fpga_word;
+
+static void
+process_line()
+{
+	char *cp;
+
+	cp = index(linebuf, '\n');
+	if (!cp) {
+		fprintf(stderr, "%s line %d: too long or missing newline\n",
+			log_filename, lineno);
+		exit(1);
+	}
+	if (linebuf[0] != '[')
+		return;
+	cp = linebuf + 1;
+	while (isdigit(*cp) || *cp == ':' || *cp == '.')
+		cp++;
+	if (*cp++ != ']') {
+invalid:	fprintf(stderr, "%s line %d: failed to parse\n",
+			log_filename, lineno);
+		exit(1);
+	}
+	if (!isspace(*cp))
+		goto invalid;
+	*cp++ = '\0';
+	if (strlen(linebuf) > 17)
+		goto invalid;
+	while (isspace(*cp))
+		cp++;
+	if (!isxdigit(cp[0]) || !isxdigit(cp[1]) || !isxdigit(cp[2]) ||
+	    !isxdigit(cp[3]) || !isspace(cp[4]))
+		goto invalid;
+	fpga_word = strtoul(cp, 0, 16);
+	process_fpga_word();
+}
+
+main(argc, argv)
+	char **argv;
+{
+	FILE *inf;
+
+	if (argc != 2) {
+		fprintf(stderr, "usage: %s logfile\n", argv[0]);
+		exit(1);
+	}
+	log_filename = argv[1];
+	inf = fopen(log_filename, "r");
+	if (!inf) {
+		perror(log_filename);
+		exit(1);
+	}
+	for (lineno = 1; fgets(linebuf, sizeof linebuf, inf); lineno++)
+		process_line();
+	exit(0);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sw/sniff-dec/parity.c	Thu Aug 31 08:46:23 2023 +0000
@@ -0,0 +1,23 @@
+/*
+ * We need to check the parity of each received ISO 7816-3 character in
+ * software; this module provides the needed table.
+ */
+
+unsigned char parity_table[256] = {
+	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0,
+	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+	1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1,
+	0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sw/sniff-dec/pps.c	Thu Aug 31 08:46:23 2023 +0000
@@ -0,0 +1,116 @@
+/*
+ * Here we implement PPS request/response message decoding.
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include "state.h"
+
+extern char linebuf[];
+extern int lineno;
+extern unsigned rx_byte;
+extern int state;
+
+#define	SUBST_PPS0	0
+#define	SUBST_PPS1	1
+#define	SUBST_PPS2	2
+#define	SUBST_PPS3	3
+#define	SUBST_PCK	4
+
+static char pps_start_timestamp[18];
+static int pps_start_line;
+static int substate;
+static u_char pps_bytes[6];
+static unsigned byte_count;
+
+void
+start_pps_msg()
+{
+	strcpy(pps_start_timestamp, linebuf);
+	pps_start_line = lineno;
+	pps_bytes[0] = rx_byte;
+	byte_count = 1;
+	state = STATE_PPS_MSG;
+	substate = SUBST_PPS0;
+}
+
+static void
+advance_state()
+{
+	if (substate == SUBST_PPS1) {
+		if (pps_bytes[1] & 0x10)
+			return;
+		substate = SUBST_PPS2;
+	}
+	if (substate == SUBST_PPS2) {
+		if (pps_bytes[1] & 0x20)
+			return;
+		substate = SUBST_PPS3;
+	}
+	if (substate == SUBST_PPS3) {
+		if (pps_bytes[2] & 0x40)
+			return;
+		substate = SUBST_PCK;
+	}
+	if (substate == SUBST_PCK)
+		return;
+	fprintf(stderr, "BUG in PPS decoder: bad state in advance_state()\n");
+	abort();
+}
+
+static void
+check_pck()
+{
+	unsigned n, xor;
+
+	xor = 0;
+	for (n = 0; n < byte_count; n++)
+		xor ^= pps_bytes[n];
+	printf(" PCK is %s\n", xor ? "bad!" : "correct");
+}
+
+static void
+pps_finish()
+{
+	unsigned n;
+
+	printf("%s line %d: PPS", pps_start_timestamp, pps_start_line);
+	for (n = 0; n < byte_count; n++)
+		printf(" %02X", pps_bytes[n]);
+	putchar('\n');
+	check_pck();
+	state = STATE_READY_FOR_CMD;
+}
+
+void
+pps_byte_in()
+{
+	pps_bytes[byte_count++] = rx_byte;
+	switch (substate) {
+	case SUBST_PPS0:
+		substate = SUBST_PPS1;
+		advance_state();
+		return;
+	case SUBST_PPS1:
+		substate = SUBST_PPS2;
+		advance_state();
+		return;
+	case SUBST_PPS2:
+		substate = SUBST_PPS3;
+		advance_state();
+		return;
+	case SUBST_PPS3:
+		substate = SUBST_PCK;
+		return;
+	case SUBST_PCK:
+		pps_finish();
+		return;
+	default:
+		fprintf(stderr,
+			"BUG in PPS decoder: bad state in pps_byte_in()\n");
+		abort();
+	}
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sw/sniff-dec/state.h	Thu Aug 31 08:46:23 2023 +0000
@@ -0,0 +1,13 @@
+/*
+ * This header file defines our little state machine for SIMtrace session
+ * log analysis.
+ */
+
+#define	STATE_UNDEF		0
+#define	STATE_ERROR		1
+#define	STATE_RSTLOW		2
+#define	STATE_ATR_TS		3
+#define	STATE_ATR_CONT		4
+#define	STATE_READY_FOR_CMD	5
+#define	STATE_PPS_MSG		6
+#define	STATE_CMD_HDR		7