view serial/collect_atr.c @ 53:fbedb67d234f

serial: fix parity for inverse coding convention Important note: it is my (Mother Mychaela's) understanding that SIM cards with inverse coding convention are extremely rare, and I have never seen such a card. Therefore, our support for the inverse coding convention will likely remain forever untested.
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 21 Mar 2021 20:46:09 +0000
parents 1d96f3b4f155
children
line wrap: on
line source

/*
 * This module contains the code for collecting ATR bytes from the SIM,
 * as well as subsequent byte Rx.
 */

#include <sys/types.h>
#include <sys/time.h>
#include <sys/errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

extern int target_fd;

#define	MAX_ATR_BYTES	33

extern unsigned char inverse_coding_table[256];

u_char atr_buf[MAX_ATR_BYTES];
unsigned atr_length;
int inverse_coding;

void
invert_bytes(buf, nbytes)
	u_char *buf;
	unsigned nbytes;
{
	unsigned n;

	for (n = 0; n < nbytes; n++)
		buf[n] = inverse_coding_table[buf[n]];
}

collect_bytes_from_sim(buf, expect_len)
	u_char *buf;
	unsigned expect_len;
{
	fd_set fds;
	struct timeval tv;
	unsigned rcvd;
	int cc;

	for (rcvd = 0; rcvd < expect_len; ) {
		FD_ZERO(&fds);
		FD_SET(target_fd, &fds);
		tv.tv_sec = 2;
		tv.tv_usec = 0;
		cc = select(target_fd+1, &fds, NULL, NULL, &tv);
		if (cc < 0) {
			if (errno == EINTR)
				continue;
			perror("select");
			return(-1);
		}
		if (cc < 1) {
			fprintf(stderr,
			"error: timeout waiting for byte(s) from SIM\n");
			return(-1);
		}
		cc = read(target_fd, buf + rcvd, expect_len - rcvd);
		if (cc <= 0) {
			perror("read after successful select");
			return(-1);
		}
		rcvd += cc;
	}
	if (inverse_coding)
		invert_bytes(buf, rcvd);
	return(0);
}

check_atr_tck()
{
	unsigned b, p;

	b = 0;
	for (p = 1; p < atr_length; p++)
		b ^= atr_buf[p];
	if (b) {
		fprintf(stderr, "error: ATR checksum is bad\n");
		return(-1);
	}
	return(0);
}

collect_atr()
{
	int rc;
	unsigned count, y, nhist, have_tck;

	rc = collect_bytes_from_sim(atr_buf, 2);
	if (rc < 0)
		return(rc);
	if (atr_buf[0] == 0x3B) {
		/* direct convention */
	} else if (atr_buf[0] == 0x03) {
		/* inverse convention */
		inverse_coding = 1;
		atr_buf[0] = 0x3F;
		atr_buf[1] = inverse_coding_table[atr_buf[1]];
	} else {
		fprintf(stderr,
		"error: received TS=0x%02X, matches neither convention\n",
			atr_buf[0]);
		return(-1);
	}
	atr_length = 2;
	nhist = atr_buf[1] & 0x0F;
	y = atr_buf[1] & 0xF0;
	have_tck = 0;
	while (y) {
		count = 0;
		if (y & 0x10)
			count++;
		if (y & 0x20)
			count++;
		if (y & 0x40)
			count++;
		if (y & 0x80)
			count++;
		if (atr_length + count > MAX_ATR_BYTES) {
atr_too_long:		fprintf(stderr, "error: ATR exceeds 33 byte limit\n");
			return(-1);
		}
		rc = collect_bytes_from_sim(atr_buf + atr_length, count);
		if (rc < 0)
			return(rc);
		atr_length += count;
		if (y & 0x80) {
			y = atr_buf[atr_length-1] & 0xF0;
			if (atr_buf[atr_length-1] & 0x0F)
				have_tck = 1;
		} else
			y = 0;
	}
	count = nhist + have_tck;
	if (count) {
		if (atr_length + count > MAX_ATR_BYTES)
			goto atr_too_long;
		rc = collect_bytes_from_sim(atr_buf + atr_length, count);
		if (rc < 0)
			return(rc);
		atr_length += count;
	}
	if (have_tck)
		return check_atr_tck();
	else
		return 0;
}

void
print_atr(head)
	char *head;
{
	unsigned n;

	fputs(head, stdout);
	for (n = 0; n < atr_length; n++) {
		printf(" %02X", atr_buf[n]);
	}
	putchar('\n');
}