aboutsummaryrefslogtreecommitdiffstats
path: root/src/tpdu_t0.c
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2012-08-21 22:52:37 +0200
committerHarald Welte <laforge@gnumonks.org>2012-08-21 22:52:37 +0200
commitfb3cb58dbf56cb781dda9706be3464a19ce9ad0e (patch)
treec42e2de222f76d7937cc461dc26a6d231de08457 /src/tpdu_t0.c
parentaaefe914be9eb31748b14811cfa30c6e2a8e8092 (diff)
add lots of unfinished code for TPDU parsing / APDU dispatching
Diffstat (limited to 'src/tpdu_t0.c')
-rw-r--r--src/tpdu_t0.c244
1 files changed, 244 insertions, 0 deletions
diff --git a/src/tpdu_t0.c b/src/tpdu_t0.c
new file mode 100644
index 0000000..ee9199f
--- /dev/null
+++ b/src/tpdu_t0.c
@@ -0,0 +1,244 @@
+/*
+ * ISO 7816 APDU handler routines (card side)
+ *
+ * Copyright (C) 2012 Harald Welte <laforge@gnumonks.org>
+ *
+ * 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 or
+ * (at your option) version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+
+#include "apdu.h"
+#include "logging.h"
+
+enum tpdu_rx_state {
+ T_STATE_POST_ATR,
+ T_STATE_WAIT_PPS,
+ T_STATE_WAIT_PCK,
+ T_STATE_WAIT_INS,
+ T_STATE_WAIT_P1,
+ T_STATE_WAIT_P2,
+ T_STATE_WAIT_P3,
+ T_STATE_POST_HDR,
+ T_STATE_WAIT_DATA,
+ T_STATE_WAIT_CLA,
+};
+
+static const struct value_string tpdu_rx_state_names[] = {
+ { T_STATE_POST_ATR, "POST_ATR" },
+ { T_STATE_WAIT_PPS, "WAIT_PPS" },
+ { T_STATE_WAIT_PCK, "WAIT_PCK" },
+ { T_STATE_WAIT_INS, "WAIT_INS" },
+ { T_STATE_WAIT_P1, "WAIT_P1" },
+ { T_STATE_WAIT_P2, "WAIT_P2" },
+ { T_STATE_WAIT_P3, "WAIT_P3" },
+ { T_STATE_POST_HDR, "POST_HDR" },
+ { T_STATE_WAIT_CLA, "WAIT_CLA" },
+ { 0, NULL }
+};
+
+enum tpdu_tx_state {
+ TX_S_IDLE,
+ TX_S_WAIT_PROC,
+ TX_S_WAIT_SW1,
+ TX_S_WAIT_SW2,
+ TX_S_WAIT_DATA,
+};
+
+static const struct value_string tpdu_tx_state_names[] = {
+ { TX_S_IDLE, "IDLE" },
+ { TX_S_WAIT_PROC, "WAIT_PROC" },
+ { TX_S_WAIT_SW1, "WAIT_SW1" },
+ { TX_S_WAIT_SW2, "WAIT_SW2" },
+ { TX_S_WAIT_DATA, "WAIT_DATA" },
+ { 0, NULL }
+};
+
+
+struct tpdu_state {
+ union {
+ struct apdu_hdr s;
+ uint8_t a[5];
+ } hdr;
+ struct {
+ uint8_t pps[4];
+ uint8_t pck;
+ uint8_t idx;
+ uint8_t xor_sum;
+ } pps;
+ uint16_t sw;
+ uint8_t proc;
+ enum tpdu_rx_state state;
+ enum tpdu_tx_state tx_state;
+
+ uint8_t tx_data_pending;
+ uint8_t rx_data_pending;
+ uint8_t buf[256];
+
+ struct apdu_parser *ap;
+};
+
+static struct tpdu_state _ts;
+
+static void set_state(struct tpdu_state *s, enum tpdu_rx_state new)
+{
+ DEBUGP(DPDU, "RX State change %s -> %s\n",
+ get_value_string(tpdu_rx_state_names, s->state),
+ get_value_string(tpdu_rx_state_names, new));
+
+ s->state = new;
+
+ if (s->state == T_STATE_POST_ATR) {
+ memset(&s->hdr, sizeof(s->hdr), 0);
+ memset(&s->pps, sizeof(s->hdr), 0);
+ }
+}
+
+static void set_tx_state(struct tpdu_state *s, enum tpdu_tx_state new)
+{
+ DEBUGP(DPDU, "TX State change %s -> %s\n",
+ get_value_string(tpdu_tx_state_names, s->tx_state),
+ get_value_string(tpdu_tx_state_names, new));
+
+ s->tx_state = new;
+
+ if (s->tx_state == TX_S_IDLE) {
+ s->tx_data_pending = 0;
+ memset(&s->hdr, sizeof(s->hdr), 0);
+ memset(&s->pps, sizeof(s->hdr), 0);
+ }
+}
+
+
+static void tpdu_rx_byte(struct tpdu_state *ts, uint8_t byte)
+{
+ int rc;
+
+ switch (ts->state) {
+ case T_STATE_POST_ATR:
+ if (byte == 0xFF)
+ set_state(ts, T_STATE_WAIT_PPS);
+ else {
+ ts->hdr.s.cla = byte;
+ set_state(ts, T_STATE_WAIT_INS);
+ }
+ break;
+ case T_STATE_WAIT_INS:
+ ts->hdr.s.ins = byte;
+ set_state(ts, T_STATE_WAIT_P1);
+ break;
+ case T_STATE_WAIT_P1:
+ ts->hdr.s.p1 = byte;
+ set_state(ts, T_STATE_WAIT_P2);
+ break;
+ case T_STATE_WAIT_P2:
+ ts->hdr.s.p2 = byte;
+ set_state(ts, T_STATE_WAIT_P3);
+ break;
+ case T_STATE_WAIT_P3:
+ ts->hdr.s.p3 = byte;
+ set_state(ts, T_STATE_POST_HDR);
+ /* FIXME: set-up automatic WTX timer */
+ /* FIXME: call APDU dispatcher */
+ rc = apdu_handle_hdr(ts->ap, &ts->hdr.s);
+ switch (rc >> 24) {
+ case RC_SW:
+ ts->sw = rc & 0xffff;
+ set_tx_state(ts, TX_S_WAIT_SW1);
+ set_state(ts, T_STATE_WAIT_CLA);
+ break;
+ case RC_PROC_RX:
+ /* procedure byte */
+ ts->proc = rc & 0xff;
+ set_tx_state(ts, TX_S_WAIT_PROC);
+ //ts->rx_data_len = (rc >> 8) & 0xff;
+ set_state(ts, T_STATE_WAIT_DATA);
+ break;
+ case RC_PROC_TX:
+ /* procedure byte */
+ set_tx_state(ts, TX_S_WAIT_PROC);
+ //ts->rx_data_len = (rc >> 8) & 0xff;
+ set_state(ts, T_STATE_WAIT_CLA);
+ break;
+ }
+ break;
+ case T_STATE_WAIT_PPS:
+ ts->pps.pps[ts->pps.idx++] = byte;
+ ts->pps.xor_sum ^= byte;
+ switch (ts->pps.idx) {
+ case 1:
+ if (!(ts->pps.pps[0] & 0x10))
+ set_state(ts, T_STATE_WAIT_PCK);
+ break;
+ case 2:
+ if (!(ts->pps.pps[0] & 0x20))
+ set_state(ts, T_STATE_WAIT_PCK);
+ break;
+ case 3:
+ if (!(ts->pps.pps[0] & 0x40))
+ set_state(ts, T_STATE_WAIT_PCK);
+ /* otherwise: stay in T_STATE_WAIT_PPS */
+ break;
+ default:
+ set_state(ts, T_STATE_WAIT_PCK);
+ }
+ break;
+ case T_STATE_WAIT_PCK:
+ if (byte != ts->pps.xor_sum) {
+ /* checksum mismatch ! */
+ while (1) {}
+ }
+ /* FIXME: check if protocol is supported */
+ //proto = ts->pps.pps[0] & 0x0f;
+ break;
+ }
+
+}
+
+/* pull one byte out of the TPDU transmit state machine, negative if thre is
+ * none */
+static int tpdu_tx_pull(struct tpdu_state *ts)
+{
+ int rc = -2;
+
+ switch (ts->tx_state) {
+ case TX_S_IDLE:
+ rc = -1;
+ break;
+ case TX_S_WAIT_PROC:
+ rc = ts->proc;
+ if (ts->tx_data_pending)
+ set_tx_state(ts, TX_S_WAIT_DATA);
+ else
+ set_tx_state(ts, TX_S_IDLE);
+ break;
+ case TX_S_WAIT_SW1:
+ rc = ts->sw >> 8;
+ set_tx_state(ts, TX_S_WAIT_SW2);
+ break;
+ case TX_S_WAIT_SW2:
+ rc = ts->sw & 0xff;
+ set_tx_state(ts, TX_S_IDLE);
+ break;
+ case TX_S_WAIT_DATA:
+ rc = ts->buf[ts->tx_data_pending--];
+ if (!ts->tx_data_pending)
+ set_tx_state(ts, TX_S_IDLE);
+ break;
+ }
+ return rc;
+}