add lots of unfinished code for TPDU parsing / APDU dispatching
This commit is contained in:
parent
aaefe914be
commit
fb3cb58dbf
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 "apdu.h"
|
||||
|
||||
|
||||
static int apdu_handle_cls(const struct apdu_class *ac, struct apdu_parser *ap,
|
||||
int mode, const struct apdu_hdr *hdr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ac->num_cmds; i++) {
|
||||
struct apdu_cmd *cmd = &ac->cmds[i];
|
||||
if (cmd->ins == hdr->ins)
|
||||
return cmd->cb(ap, mode, hdr);
|
||||
}
|
||||
|
||||
return RV_SW(0x6D00);
|
||||
}
|
||||
|
||||
|
||||
int apdu_handle_hdr(struct apdu_parser *ap, struct apdu_hdr *hdr)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* iterate over all registered classes and check for a CLA match */
|
||||
for (i = 0; i < ap->num_classes; i++) {
|
||||
const struct apdu_class *ac = &ap->classes[i];
|
||||
/* if CLA matches, iterate over INS array and call callback */
|
||||
if (hdr->cla & ac->mask == ac->compare)
|
||||
return apdu_handle_cls(ac, ap, AP_MODE_T0_HDR, hdr);
|
||||
}
|
||||
|
||||
return RV_SW(0x6E00);
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#ifndef _APDU_H
|
||||
#define _APDU_H
|
||||
|
||||
#define RC_SW 0
|
||||
#define RC_PROC_RX 1
|
||||
#define RC_PROC_TX 2
|
||||
|
||||
#define RV(class, value) (((class) << 24) | (value) & 0xff)
|
||||
#define RV_SW(x) RV(RC_SW, x)
|
||||
#define RV_PROC_RX(x) RV(RC_PROC_RX, x)
|
||||
#define RV_PROC_TX(x) RV(RC_PROC_TX, x)
|
||||
|
||||
/*! \brief In which mode do we call the APDU call-back? */
|
||||
enum apdu_cb_mode {
|
||||
AP_MODE_T0_HDR,
|
||||
};
|
||||
|
||||
/* dynamic in-memory structure listing classes currently available/visible for
|
||||
* the given APDU channel */
|
||||
struct apdu_parser {
|
||||
unsigned int num_classes;
|
||||
const struct apdu_class *classes;
|
||||
};
|
||||
|
||||
struct apdu_hdr {
|
||||
uint8_t cla;
|
||||
uint8_t ins;
|
||||
uint8_t p1;
|
||||
uint8_t p2;
|
||||
uint8_t p3;
|
||||
} __attribute((packed))__;
|
||||
|
||||
struct apdu_cmd {
|
||||
uint16_t _pad;
|
||||
uint8_t flags;
|
||||
uint8_t ins;
|
||||
int (*cb)(struct apdu_parser *ap, int mode, const struct apdu_hdr *hdr);
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct apdu_class {
|
||||
uint8_t mask;
|
||||
uint8_t compare;
|
||||
uint8_t num_cmds;
|
||||
const struct apdu_cmd *cmds;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* ISO 7816 command handler for GSM SIM commands
|
||||
*
|
||||
* 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 <osmocom/core/utils.h>
|
||||
|
||||
#include "apdu.h"
|
||||
|
||||
static int cmd_select_file(struct apdu_parser *ap, int mode,
|
||||
const struct apdu_hdr *hdr)
|
||||
{
|
||||
return RV_SW(0x9000);
|
||||
}
|
||||
|
||||
static const struct apdu_cmd gsm_cmds[] = {
|
||||
{ .ins = 0xA4, .cb = cmd_select_file },
|
||||
};
|
||||
|
||||
const struct apdu_class gsm_class = {
|
||||
.mask = 0xff,
|
||||
.compare = 0xa0,
|
||||
.cmds = gsm_cmds,
|
||||
.num_cmds = ARRAY_SIZE(gsm_cmds),
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
#define DPDU 23
|
|
@ -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;
|
||||
}
|
Reference in New Issue