From 50b1aff6f6f2a3a127f6121c5dedd8c2fa5a1c4d Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sun, 14 Nov 2010 23:04:16 +0100 Subject: Initial version of a SAM7 USART based ISO7816-3 T=0 sniffer --- firmware/include/openpcd.h | 3 + firmware/src/openpcd.h | 7 +- firmware/src/simtrace.h | 18 ++ firmware/src/simtrace/iso7816_uart.c | 399 ++++++++++++++++++++++++++++++ firmware/src/simtrace/main_simtrace.c | 98 ++++++++ firmware/src/simtrace/usb_strings_app.txt | 6 + 6 files changed, 529 insertions(+), 2 deletions(-) create mode 100644 firmware/src/simtrace.h create mode 100644 firmware/src/simtrace/iso7816_uart.c create mode 100644 firmware/src/simtrace/main_simtrace.c create mode 100644 firmware/src/simtrace/usb_strings_app.txt (limited to 'firmware') diff --git a/firmware/include/openpcd.h b/firmware/include/openpcd.h index e5a0ac7..ea32764 100644 --- a/firmware/include/openpcd.h +++ b/firmware/include/openpcd.h @@ -34,6 +34,8 @@ enum openpcd_cmd_class { OPENPCD_CMD_CLS_ADC = 0x5, OPENPCD_CMD_CLS_LIBRFID = 0x6, OPENPCD_CMD_CLS_PRESENCE = 0x7, + /* SIM SCAN */ + OPENPCD_CMD_CLS_SIM = 0x8, /* PICC (transponder) side */ OPENPCD_CMD_CLS_PICC = 0xe, @@ -102,6 +104,7 @@ enum openpcd_cmd_class { #define OPENPCD_VENDOR_ID 0x16c0 +#define SIMTRACE_PRODUCT_ID 0x0762 #define OPENPCD_PRODUCT_ID 0x076b #define OPENPICC_PRODUCT_ID 0x076c #define OPENPCD_OUT_EP 0x01 diff --git a/firmware/src/openpcd.h b/firmware/src/openpcd.h index a65cd0e..3bc890e 100644 --- a/firmware/src/openpcd.h +++ b/firmware/src/openpcd.h @@ -10,11 +10,13 @@ #define USB_VENDOR_ID OPENPCD_VENDOR_ID -#ifdef OLIMEX +#if defined(OLIMEX) || defined(SIMTRACE) #define OPENPCD_PIO_LED2 AT91C_PIO_PA17 #define OPENPCD_PIO_LED1 AT91C_PIO_PA18 #define OPENPCD_PIO_UDP_CNX AT91C_PIO_PA24 -#define OPENPCD_PIO_UDP_PUP AT91C_PIO_PA16 +#define OPENPCD_PIO_UDP_PUPv4 AT91C_PIO_PA16 +#define PIO_BOOTLDR AT91C_PIO_PA19 /* B1 used as bootloader switch */ +#define USB_PRODUCT_ID SIMTRACE_PRODUCT_ID #else #if defined(PCD) #define OPENPCD_PIO_UDP_CNX AT91C_PIO_PA15 @@ -166,6 +168,7 @@ #define OPENPCD_IRQ_PRIO_SPI AT91C_AIC_PRIOR_HIGHEST #define OPENPCD_IRQ_PRIO_SSC (AT91C_AIC_PRIOR_HIGHEST-1) #define OPENPCD_IRQ_PRIO_SYS (AT91C_AIC_PRIOR_HIGHEST-2) +#define OPENPCD_IRQ_PRIO_USART (AT91C_AIC_PRIOR_HIGHEST-3) #define OPENPCD_IRQ_PRIO_TC_FDT (AT91C_AIC_PRIOR_LOWEST+3) #define OPENPCD_IRQ_PRIO_UDP (AT91C_AIC_PRIOR_LOWEST+2) #define OPENPCD_IRQ_PRIO_PIT (AT91C_AIC_PRIOR_LOWEST+1) diff --git a/firmware/src/simtrace.h b/firmware/src/simtrace.h new file mode 100644 index 0000000..5a27c31 --- /dev/null +++ b/firmware/src/simtrace.h @@ -0,0 +1,18 @@ + +#define USB_VENDOR_ID OPENPCD_VENDOR_ID + +#ifdef SIMTRACE +#define OPENPCD_PIO_LED2 AT91C_PIO_PA17 +#define OPENPCD_PIO_LED1 AT91C_PIO_PA18 +#define OPENPCD_PIO_UDP_CNX AT91C_PIO_PA24 +#define OPENPCD_PIO_UDP_PUP AT91C_PIO_PA16 +#define USB_PRODUCT_ID SIMTRACE_PRODUCT_ID +#else +#error "unknown PCB" +#endif + +#define SIMTRACE_PIO_CLK AT91C_PA2_SCK0 +#define SIMTRACE_PIO_IO AT91C_PA6_TXD0 +#define SIMTRACE_PIO_nRST AT91C_PIO_PA7 + + diff --git a/firmware/src/simtrace/iso7816_uart.c b/firmware/src/simtrace/iso7816_uart.c new file mode 100644 index 0000000..9022830 --- /dev/null +++ b/firmware/src/simtrace/iso7816_uart.c @@ -0,0 +1,399 @@ +/* Driver for AT91SAM7 USART0 in ISO7816-3 mode + * (C) 2010 by Harald Welte + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../simtrace.h" +#include "../openpcd.h" + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + +static const AT91PS_USART usart = AT91C_BASE_US0; + +enum iso7816_3_state { + ISO7816_S_RESET, /* in Reset */ + ISO7816_S_WAIT_ATR, /* waiting for ATR to start */ + ISO7816_S_IN_ATR, + ISO7816_S_WAIT_READER, /* waiting for data from reader */ + ISO7816_S_WAIT_CARD, /* waiting for data from card */ +}; + +enum atr_state { + ATR_S_WAIT_TS, + ATR_S_WAIT_T0, + ATR_S_WAIT_TA, + ATR_S_WAIT_TB, + ATR_S_WAIT_TC, + ATR_S_WAIT_TD, + ATR_S_WAIT_HIST, + ATR_S_WAIT_TCK, + ATR_S_DONE, +}; + +struct iso7816_3_handle { + enum iso7816_3_state state; + + u_int8_t fi; + u_int8_t di; + + u_int8_t atr_idx; + u_int8_t atr_hist_len; + u_int8_t atr_last_td; + enum atr_state atr_state; + u_int8_t atr[64]; + + u_int16_t apdu_len; + u_int16_t apdu_idx; +}; + +struct iso7816_3_handle isoh; + + +/* Table 6 from ISO 7816-3 */ +static const u_int16_t fi_table[] = { + 0, 372, 558, 744, 1116, 1488, 1860, 0, + 0, 512, 768, 1024, 1536, 2048, 0, 0 +}; + +/* Table 7 from ISO 7816-3 */ +static const u_int8_t di_table[] = { + 0, 1, 2, 4, 8, 16, 0, 0, + 0, 0, 2, 4, 8, 16, 32, 64, +}; + +static int compute_fidi_ratio(u_int8_t fi, u_int8_t di) +{ + u_int16_t f, d; + int ret; + + if (fi >= ARRAY_SIZE(fi_table) || + di >= ARRAY_SIZE(di_table)) + return -EINVAL; + + f = fi_table[fi]; + if (f == 0) + return -EINVAL; + + d = di_table[di]; + if (d == 0) + return -EINVAL; + + if (di < 8) + ret = f / d; + else + ret = f * d; + + return ret; +} + +static void set_atr_state(struct iso7816_3_handle *ih, enum atr_state new_atrs) +{ + if (new_atrs == ATR_S_WAIT_TS) { + ih->atr_idx = 0; + ih->atr_hist_len = 0; + ih->atr_last_td = 0; + memset(ih->atr, 0, sizeof(ih->atr)); + } else if (ih->atr_state == new_atrs) + return; + + //DEBUGPCR("ATR state %u -> %u", ih->atr_state, new_atrs); + ih->atr_state = new_atrs; +} + +static void set_state(struct iso7816_3_handle *ih, enum iso7816_3_state new_state) +{ + if (new_state == ISO7816_S_WAIT_ATR) { + int rc; + /* Initial Fi / Di ratio */ + ih->fi = 1; + ih->di = 1; + rc = compute_fidi_ratio(ih->fi, ih->di); + DEBUGPCRF("computed Fi(%u) Di(%u) ratio: %d", ih->fi, ih->di, rc); + usart->US_FIDI = rc & 0x3ff; + set_atr_state(ih, ATR_S_WAIT_TS); + } else if (new_state == ISO7816_S_WAIT_READER) { + /* CLA INS P1 P2 LEN */ + ih->apdu_len = 5; + ih->apdu_idx = 0; + } else if (new_state == ISO7816_S_WAIT_CARD) { + /* 8.2.2 procedure bytes sent by the card */ + /* FIXME: NULL byte and similar oddities */ + ih->apdu_len += 2; + } + + if (ih->state == new_state) + return; + + DEBUGPCR("7816 state %u -> %u", ih->state, new_state); + ih->state = new_state; +} + +/* determine the next ATR state based on received interface byte */ +static enum atr_state next_intb_state(struct iso7816_3_handle *ih, u_int8_t ch) +{ + switch (ih->atr_state) { + case ATR_S_WAIT_TD: + case ATR_S_WAIT_T0: + ih->atr_last_td = ch; + goto from_td; + case ATR_S_WAIT_TC: + goto from_tc; + case ATR_S_WAIT_TB: + goto from_tb; + case ATR_S_WAIT_TA: + if ((ih->atr_last_td & 0x0f) == 0) { + /* This must be TA1 */ + ih->fi = ch >> 4; + ih->di = ch & 0xf; + DEBUGPCR("found Fi=%u Di=%u", ih->fi, ih->di); + } + goto from_ta; + } + + DEBUGPCR("something wrong, old_state != TA"); + return ATR_S_WAIT_TCK; + +from_td: + if (ih->atr_last_td & 0x10) + return ATR_S_WAIT_TA; +from_ta: + if (ih->atr_last_td & 0x20) + return ATR_S_WAIT_TB; +from_tb: + if (ih->atr_last_td & 0x40) + return ATR_S_WAIT_TC; +from_tc: + if (ih->atr_last_td & 0x80) + return ATR_S_WAIT_TD; + + return ATR_S_WAIT_HIST; +} + +/* process an incomng ATR byte */ +static enum iso7816_3_state +process_byte_atr(struct iso7816_3_handle *ih, u_int8_t byte) +{ + int rc; + + /* add byte to ATR buffer */ + ih->atr[ih->atr_idx] = byte; + ih->atr_idx++; + + switch (ih->atr_state) { + case ATR_S_WAIT_TS: + /* FIXME: if we don't have the RST line we might get this */ + if (byte == 0) { + ih->atr_idx--; + break; + } + /* FIXME: check inverted logic */ + set_atr_state(ih, ATR_S_WAIT_T0); + break; + case ATR_S_WAIT_T0: + ih->atr_hist_len = byte & 0xf; + set_atr_state(ih, next_intb_state(ih, byte & 0xf0)); + break; + case ATR_S_WAIT_TA: + case ATR_S_WAIT_TB: + case ATR_S_WAIT_TC: + case ATR_S_WAIT_TD: + set_atr_state(ih, next_intb_state(ih, byte)); + break; + case ATR_S_WAIT_HIST: + ih->atr_hist_len--; + if (ih->atr_hist_len == 0) + set_atr_state(ih, ATR_S_WAIT_TCK); + break; + case ATR_S_WAIT_TCK: + /* FIXME: process TCK */ + set_atr_state(ih, ATR_S_DONE); + /* FIXME: update Fi/Di */ + rc = compute_fidi_ratio(ih->fi, ih->di); + if (rc > 0 && rc < 0x400) { + DEBUGPCR("computed FiDi ratio %d", rc); + /* update baud rate generator in UART */ + usart->US_CR |= AT91C_US_RXDIS| AT91C_US_RSTRX; + usart->US_FIDI = rc & 0x3ff; + usart->US_CR |= AT91C_US_RXEN; + } else + DEBUGPCRF("computed FiDi ratio %d unsupported", rc); + return ISO7816_S_WAIT_READER; + } + + return ISO7816_S_IN_ATR; +} + +/* process an incomng byte from the reader */ +static enum iso7816_3_state +process_byte_reader(struct iso7816_3_handle *ih, u_int8_t byte) +{ + /* add response length to total number of expected bytes */ + if (ih->apdu_idx == 4) + ih->apdu_len += byte; + + ih->apdu_idx++; + + /* once we have received all bytes, transition to card response */ + if (ih->apdu_idx == ih->apdu_len) + return ISO7816_S_WAIT_CARD; + + return ISO7816_S_WAIT_READER; +} + +/* process an incomng byte from the card */ +static enum iso7816_3_state +process_byte_card(struct iso7816_3_handle *ih, u_int8_t byte) +{ + ih->apdu_idx++; + + /* once we have received all bytes, apdu is finished */ + if (ih->apdu_idx == ih->apdu_len) + return ISO7816_S_WAIT_READER; + + return ISO7816_S_WAIT_CARD; +} + + +void process_byte(struct iso7816_3_handle *ih, u_int8_t byte) +{ + int new_state = -1; + + switch (ih->state) { + case ISO7816_S_RESET: + break; + case ISO7816_S_WAIT_ATR: + case ISO7816_S_IN_ATR: + new_state = process_byte_atr(ih, byte); + break; + case ISO7816_S_WAIT_READER: + new_state = process_byte_reader(ih, byte); + break; + case ISO7816_S_WAIT_CARD: + //new_state = process_byte_card(ih, byte); + break; + } + + if (new_state != -1) + set_state(ih, new_state); +} + +static int __ramfunc usart_irq(void) +{ + u_int32_t csr = usart->US_CSR; + u_int8_t octet; + + //DEBUGP("USART IRQ, CSR=0x%08x\n", csr); + + if (csr & AT91C_US_RXRDY) { + /* at least one character received */ + octet = usart->US_RHR & 0xff; + DEBUGP("%02x ", octet); + process_byte(&isoh, octet); + } + + if (csr & AT91C_US_TXRDY) { + /* nothing to transmit anymore */ + } + + if (csr & (AT91C_US_PARE|AT91C_US_FRAME|AT91C_US_OVRE)) { + /* some error has occurrerd */ + } +} + +void iso_uart_dump(void) +{ + u_int32_t csr = usart->US_CSR; + + DEBUGPCR("USART CSR=0x%08x", csr); +} + +void iso_uart_rst(unsigned int state) +{ + DEBUGPCR("USART nRST set state=%u", state); + switch (state) { + case 0: + AT91F_PIO_ClearOutput(AT91C_BASE_PIOA, SIMTRACE_PIO_nRST); + AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, SIMTRACE_PIO_nRST); + break; + case 1: + AT91F_PIO_SetOutput(AT91C_BASE_PIOA, SIMTRACE_PIO_nRST); + AT91F_PIO_CfgOutput(AT91C_BASE_PIOA, SIMTRACE_PIO_nRST); + break; + default: + AT91F_PIO_CfgInput(AT91C_BASE_PIOA, SIMTRACE_PIO_nRST); + break; + } +} + +void iso_uart_rx_mode(void) +{ + DEBUGPCR("USART Entering Rx Mode"); + /* Enable receive interrupts */ + usart->US_IER = AT91C_US_RXRDY | AT91C_US_OVRE | AT91C_US_FRAME | + AT91C_US_PARE | AT91C_US_NACK | AT91C_US_ITERATION; + + /* Enable the receiver */ + usart->US_CR = AT91C_US_RXEN; + + set_state(&isoh, ISO7816_S_WAIT_ATR); +} + +void iso_uart_clk_master(unsigned int master) +{ + DEBUGPCR("USART Clock Master %u", master); + if (master) { + usart->US_MR = AT91C_US_USMODE_ISO7816_0 | AT91C_US_CLKS_CLOCK | + AT91C_US_CHRL_8_BITS | AT91C_US_NBSTOP_1_BIT | + AT91C_US_CKLO; + usart->US_BRGR = (0x0000 << 16) | 16; + } else { + usart->US_MR = AT91C_US_USMODE_ISO7816_0 | AT91C_US_CLKS_EXT | + AT91C_US_CHRL_8_BITS | AT91C_US_NBSTOP_1_BIT | + AT91C_US_CKLO; + usart->US_BRGR = (0x0000 << 16) | 0x0001; + } +} + +void iso_uart_init(void) +{ + DEBUGPCR("USART Initializing"); + + AT91F_US0_CfgPMC(); + + /* configure all 3 signals as input */ + AT91F_PIO_CfgPeriph(AT91C_BASE_PIOA, SIMTRACE_PIO_IO, SIMTRACE_PIO_CLK); + + AT91F_PIO_CfgInput(AT91C_BASE_PIOA, SIMTRACE_PIO_nRST); + + AT91F_AIC_ConfigureIt(AT91C_BASE_AIC, AT91C_ID_US0, + OPENPCD_IRQ_PRIO_USART, + AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL, &usart_irq); + + AT91F_AIC_EnableIt(AT91C_BASE_AIC, AT91C_ID_US0); + + usart->US_CR = AT91C_US_RXDIS | AT91C_US_TXDIS | (AT91C_US_RSTRX | AT91C_US_RSTTX); + /* FIXME: wait for some time */ + usart->US_CR = AT91C_US_RXDIS | AT91C_US_TXDIS; + + usart->US_MR = AT91C_US_USMODE_ISO7816_0 | AT91C_US_CLKS_EXT | + AT91C_US_CHRL_8_BITS | AT91C_US_NBSTOP_1_BIT | + AT91C_US_CKLO; + + /* Disable all interrupts */ + usart->US_IDR = 0xff; + /* Clock Divider = 1, i.e. no division of SCLK */ + usart->US_BRGR = (0x0000 << 16) | 0x0001; + /* Disable Receiver Time-out */ + usart->US_RTOR = 0; + /* Disable Transmitter Timeguard */ + usart->US_TTGR = 0; +} diff --git a/firmware/src/simtrace/main_simtrace.c b/firmware/src/simtrace/main_simtrace.c new file mode 100644 index 0000000..d5a22dd --- /dev/null +++ b/firmware/src/simtrace/main_simtrace.c @@ -0,0 +1,98 @@ +/* OpenPICC Main Program + * (C) 2006 by Harald Welte + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include "../openpcd.h" +#include +#include +#include +#include + +//#include + +void _init_func(void) +{ + /* low-level hardware initialization */ + pio_irq_init(); + iso_uart_init(); + + /* high-level protocol */ + //opicc_usbapi_init(); + led_switch(1, 0); + led_switch(2, 1); +} + +static void help(void) +{ + DEBUGPCR("r: iso uart Rx mode\r\n" + "c: toggle clock master/slave\r\n" + "l: set nRST to low (active)\r\n" + "h: set nRST to high (inactive)\r\n" + "o: set nRST to input\r\n"); +} + +int _main_dbgu(char key) +{ + static int i = 0; + DEBUGPCRF("main_dbgu"); + + switch (key) { + case 'r': + iso_uart_rx_mode(); + break; + case 'c': + iso_uart_clk_master(i++ & 1); + break; + case 'l': + iso_uart_rst(0); + break; + case 'h': + iso_uart_rst(1); + break; + case 'o': + iso_uart_rst(2); + break; + case 'd': + iso_uart_dump(); + break; + case '?': + help(); + break; + } + + return -EINVAL; +} + +void _main_func(void) +{ + /* first we try to get rid of pending to-be-sent stuff */ + usb_out_process(); + + /* next we deal with incoming reqyests from USB EP1 (OUT) */ + usb_in_process(); + + udp_unthrottle(); +} diff --git a/firmware/src/simtrace/usb_strings_app.txt b/firmware/src/simtrace/usb_strings_app.txt new file mode 100644 index 0000000..c1d3c95 --- /dev/null +++ b/firmware/src/simtrace/usb_strings_app.txt @@ -0,0 +1,6 @@ +SimTrace DFU Interface - Application Partition +SimTrace DFU Interface - Bootloader Partition +hmw-consulting.de +SimTrace SIM Sniffer - Runtime Mode +SimTrace Runtime Configuration +SimTrace Runtime Interface -- cgit v1.2.3