diff options
| author | Harald Welte <laforge@gnumonks.org> | 2016-03-14 15:35:50 +0100 | 
|---|---|---|
| committer | Harald Welte <laforge@gnumonks.org> | 2016-03-17 16:55:11 +0100 | 
| commit | 4acaa13433cc15e4f1d9ee850d78af47419768ab (patch) | |
| tree | e3438d0895439579e2d568e877aaf9118f5755ba | |
| parent | 2c34ab4a80c227be4bb685f75fc24fde86a0704e (diff) | |
sim: add class_tables / card profiles
The tables permit code to determine the APDU class of an APDU
based on it APDU/TPDU header (CLA/INS/P1/P2/P3).
| -rw-r--r-- | include/Makefile.am | 1 | ||||
| -rw-r--r-- | include/osmocom/sim/class_tables.h | 42 | ||||
| -rw-r--r-- | src/sim/Makefile.am | 5 | ||||
| -rw-r--r-- | src/sim/class_tables.c | 313 | 
4 files changed, 359 insertions, 2 deletions
diff --git a/include/Makefile.am b/include/Makefile.am index ac22ee64..0e5ed745 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -92,6 +92,7 @@ nobase_include_HEADERS = \                         osmocom/gsm/rxlev_stat.h \                         osmocom/gsm/sysinfo.h \                         osmocom/gsm/tlv.h \ +		       osmocom/sim/class_tables.h \  		       osmocom/sim/sim.h  if ENABLE_PLUGIN diff --git a/include/osmocom/sim/class_tables.h b/include/osmocom/sim/class_tables.h new file mode 100644 index 00000000..ad89d949 --- /dev/null +++ b/include/osmocom/sim/class_tables.h @@ -0,0 +1,42 @@ +#pragma once + +/* simtrace - tables determining APDU case for card emulation + * + * (C) 2016 by 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 version 2, or + *  any later version as published by the Free Software Foundation. + * + *  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 <stdint.h> + +struct osim_cla_ins_case { +	uint8_t cla; +	uint8_t cla_mask; +	int (*helper)(const struct osim_cla_ins_case *cic, const uint8_t *hdr); +	const uint8_t *ins_tbl; +}; + +struct osim_cla_ins_card_profile { +	const char	*name; +	const char	*description; +	const struct osim_cla_ins_case *cic_arr; +	unsigned int cic_arr_size; +}; + +extern const struct osim_cla_ins_card_profile osim_iso7816_cic_profile; +extern const struct osim_cla_ins_card_profile osim_uicc_cic_profile; +extern const struct osim_cla_ins_card_profile osim_uicc_sim_cic_profile; + +int osim_determine_apdu_case(const struct osim_cla_ins_card_profile *prof, +			     const uint8_t *hdr); diff --git a/src/sim/Makefile.am b/src/sim/Makefile.am index 4358cc35..bcdaf210 100644 --- a/src/sim/Makefile.am +++ b/src/sim/Makefile.am @@ -13,8 +13,9 @@ noinst_HEADERS = sim_int.h gsm_int.h  lib_LTLIBRARIES = libosmosim.la -libosmosim_la_SOURCES = core.c reader.c reader_pcsc.c \ -			card_fs_sim.c card_fs_usim.c card_fs_uicc.c card_fs_isim.c card_fs_tetra.c +libosmosim_la_SOURCES = core.c reader.c reader_pcsc.c class_tables.c \ +			card_fs_sim.c card_fs_usim.c card_fs_uicc.c \ +			card_fs_isim.c card_fs_tetra.c  libosmosim_la_LDFLAGS = -version-info $(LIBVERSION)  libosmosim_la_LIBADD = \  	$(top_builddir)/src/libosmocore.la \ diff --git a/src/sim/class_tables.c b/src/sim/class_tables.c new file mode 100644 index 00000000..c3e18d82 --- /dev/null +++ b/src/sim/class_tables.c @@ -0,0 +1,313 @@ +/* simtrace - tables determining APDU case for card emulation + * + * (C) 2016 by 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 version 2, or + *  any later version as published by the Free Software Foundation. + * + *  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 <stdint.h> +#include <osmocom/core/utils.h> +#include <osmocom/sim/class_tables.h> + +static const uint8_t iso7816_ins_tbl[] = { +	[0xB0]	= 2,	/* READ BIN */ +	[0xD0]	= 3,	/* WRITE BIN */ +	[0xD6]	= 3,	/* UPDATE BIN */ +	[0x0E]	= 3,	/* ERASE BIN */ +	[0xB2]	= 2,	/* READ REC */ +	[0xD2]	= 3,	/* WRITE REC */ +	[0xE2]	= 3,	/* APPEND REC */ +	[0xDC]	= 3,	/* UPDATE REC */ +	[0xCA]	= 2,	/* GET DATA */ +	[0xDA]	= 3,	/* PUT DATA */ +	[0xA4]	= 4,	/* SELECT FILE */ +	[0x20]	= 3,	/* VERIFY */ +	[0x88]	= 4,	/* INT AUTH */ +	[0x82]	= 3,	/* EXT AUTH */ +	[0x84]	= 2,	/* GET CHALLENGE */ +	[0x70]	= 2,	/* MANAGE CHANNEL */ +}; + +static const struct osim_cla_ins_case iso7816_4_ins_case[] = { +	{ +		.cla		= 0x00, +		.cla_mask	= 0xF0, +		.ins_tbl	= iso7816_ins_tbl, +	}, { +		.cla		= 0x80,	/* 0x80/0x90 */ +		.cla_mask	= 0xE0, +		.ins_tbl	= iso7816_ins_tbl, +	}, { +		.cla		= 0xB0, +		.cla_mask	= 0xF0, +		.ins_tbl	= iso7816_ins_tbl, +	}, { +		.cla		= 0xC0, +		.cla_mask	= 0xF0, +		.ins_tbl	= iso7816_ins_tbl, +	}, +}; + +const struct osim_cla_ins_card_profile osim_iso7816_cic_profile = { +	.name		= "ISO 7816-4", +	.description	= "ISO 7816-4", +	.cic_arr	= iso7816_4_ins_case, +	.cic_arr_size	= ARRAY_SIZE(iso7816_4_ins_case), +}; + +static const uint8_t gsm1111_ins_tbl[256] = { +	[0xA4]	= 4,	/* SELECT FILE */ +	[0xF2]	= 2,	/* STATUS */ +	[0xB0]	= 2,	/* READ BINARY */ +	[0xD6]	= 3,	/* UPDATE BINARY */ +	[0xB2]	= 2,	/* READ RECORD */ +	[0xDC]	= 3,	/* UPDATE RECORD */ +	[0xA2]	= 4,	/* SEEK */ +	[0x32]	= 4,	/* INCREASE */ +	[0x20]	= 3,	/* VERIFY CHV */ +	[0x24]	= 3,	/* CHANGE CHV */ +	[0x26]	= 3,	/* DISABLE CHV */ +	[0x28]	= 3,	/* ENABLE CHV */ +	[0x2C]	= 3,	/* UNBLOCK CHV */ +	[0x04]	= 1,	/* INVALIDATE */ +	[0x44]	= 1,	/* REHABILITATE */ +	[0x88]	= 4,	/* RUN GSM ALGO */ +	[0xFA]	= 1,	/* SLEEP */ +	[0xC0]	= 2,	/* GET RESPONSE */ +	[0x10]	= 3,	/* TERMINAL PROFILE */ +	[0xC2]	= 4,	/* ENVELOPE */ +	[0x12]	= 2,	/* FETCH */ +	[0x14]	= 3,	/* TERMINAL RESPONSE */ +}; + +/* According to Table 9 / Section 9.2 of TS 11.11 */ +static const struct osim_cla_ins_case gsm1111_ins_case[] = { +	{ +		.cla		= 0xA0, +		.cla_mask	= 0xFF, +		.ins_tbl	= gsm1111_ins_tbl, +	}, +}; + +const struct osim_cla_ins_card_profile osim_gsm1111_cic_profile = { +	.name		= "GSM SIM", +	.description	= "GSM/3GPP TS 11.11", +	.cic_arr	= gsm1111_ins_case, +	.cic_arr_size	= ARRAY_SIZE(gsm1111_ins_case), +}; + +/* ETSI TS 102 221, Table 10.5, CLA = 0x0x, 0x4x or 0x6x */ +static const uint8_t uicc_ins_tbl_046[256] = { +	[0xA4]	= 4,	/* SELET FILE */ +	[0xB0]	= 2,	/* READ BINARY */ +	[0xD6]	= 3,	/* UPDATE BINARY */ +	[0xB2]	= 2,	/* READ RECORD */ +	[0xDC]	= 3,	/* UPDATE RECORD */ +	[0xA2]	= 4,	/* SEEK */ +	[0x20]	= 3,	/* VERIFY PIN */ +	[0x24]	= 3,	/* CHANGE PIN */ +	[0x26]	= 3,	/* DISABLE PIN */ +	[0x28]	= 3,	/* ENABLE PIN */ +	[0x2C]	= 3,	/* UNBLOCK PIN */ +	[0x04]	= 1,	/* DEACTIVATE FILE */ +	[0x44]	= 1,	/* ACTIVATE FILE */ +	[0x88]	= 4,	/* AUTHENTICATE */ +	[0x89]	= 4,	/* AUTHENTICATE */ +	[0x84]	= 2,	/* GET CHALLENGE */ +	[0x70]	= 2,	/* MANAGE CHANNEL */ +	[0x73]	= 0x80,	/* MANAGE SECURE CHANNEL */ +	[0x75]	= 0x80,	/* TRANSACT DATA */ +	[0xC0]	= 2,	/* GET RESPONSE */ +}; + +static int uicc046_cla_ins_helper(const struct osim_cla_ins_case *cic, +				  const uint8_t *hdr) +{ +	uint8_t ins = hdr[1]; +	uint8_t p1 = hdr[2]; +	uint8_t p2 = hdr[3]; +	uint8_t p2_cmd; + +	switch (ins) { +	case 0x73:	/* MANAGE SECURE CHANNEL */ +		if (p1 == 0x00)		/* Retrieve UICC Endpoints */ +			return 2; +		switch (p1 & 0x07) { +		case 1:	/* Establish SA - Master SA */ +		case 2:	/* Establish SA - Conn. SA */ +		case 3:	/* Start secure channel SA */ +			p2_cmd = p2 >> 5; +			if (p2 == 0x80 || p2_cmd == 0) { +				/* command data */ +				return 3; +			} +			if (p2_cmd == 5 || p2_cmd == 1) { +				/* response data */ +				return 2; +			} +			return 0; +			break; +		case 4:	/* Terminate secure chan SA */ +			return 3; +			break; +		} +		break; +	case 0x75:	/* TRANSACT DATA */ +		if (p1 & 0x04) +			return 3; +		else +			return 2; +		break; +	} + +	return 0; +} + +/* ETSI TS 102 221, Table 10.5, CLA = 0x8x, 0xCx or 0xEx */ +static const uint8_t uicc_ins_tbl_8ce[256] = { +	[0xF2]		= 2,	/* STATUS */ +	[0x32]		= 4,	/* INCREASE */ +	[0xCB]		= 4,	/* RETRIEVE DATA */ +	[0xDB]		= 3,	/* SET DATA */ +	[0xAA]		= 3,	/* TERMINAL CAPABILITY */ +}; + +/* ETSI TS 102 221, Table 10.5, CLA = 0x80 */ +static const uint8_t uicc_ins_tbl_80[256] = { +	[0x10]		= 3,	/* TERMINAL PROFILE */ +	[0xC2]		= 4,	/* ENVELOPE */ +	[0x12]		= 2,	/* FETCH */ +	[0x14]		= 3,	/* TERMINAL RESPONSE */ +}; + +static const struct osim_cla_ins_case uicc_ins_case[] = { +	{ +		.cla		= 0x80, +		.cla_mask	= 0xFF, +		.ins_tbl	= uicc_ins_tbl_80, +	}, { +		.cla		= 0x00, +		.cla_mask	= 0xF0, +		.helper		= uicc046_cla_ins_helper, +		.ins_tbl	= uicc_ins_tbl_046, +	}, { +		.cla		= 0x40, +		.cla_mask	= 0xF0, +		.helper		= uicc046_cla_ins_helper, +		.ins_tbl	= uicc_ins_tbl_046, +	}, { +		.cla		= 0x60, +		.cla_mask	= 0xF0, +		.helper		= uicc046_cla_ins_helper, +		.ins_tbl	= uicc_ins_tbl_046, +	}, { +		.cla		= 0x80, +		.cla_mask	= 0xF0, +		.ins_tbl	= uicc_ins_tbl_8ce, +	}, { +		.cla		= 0xC0, +		.cla_mask	= 0xF0, +		.ins_tbl	= uicc_ins_tbl_8ce, +	}, { +		.cla		= 0xE0, +		.cla_mask	= 0xF0, +		.ins_tbl	= uicc_ins_tbl_8ce, +	}, +}; + +const struct osim_cla_ins_card_profile osim_uicc_cic_profile = { +	.name		= "UICC", +	.description	= "TS 102 221 / 3GPP TS 31.102", +	.cic_arr	= uicc_ins_case, +	.cic_arr_size	= ARRAY_SIZE(uicc_ins_case), +}; + + +static const struct osim_cla_ins_case uicc_sim_ins_case[] = { +	{ +		.cla		= 0xA0, +		.cla_mask	= 0xFF, +		.ins_tbl	= gsm1111_ins_tbl, +	}, { +		.cla		= 0x80, +		.cla_mask	= 0xFF, +		.ins_tbl	= uicc_ins_tbl_80, +	}, { +		.cla		= 0x00, +		.cla_mask	= 0xF0, +		.helper		= uicc046_cla_ins_helper, +		.ins_tbl	= uicc_ins_tbl_046, +	}, { +		.cla		= 0x40, +		.cla_mask	= 0xF0, +		.helper		= uicc046_cla_ins_helper, +		.ins_tbl	= uicc_ins_tbl_046, +	}, { +		.cla		= 0x60, +		.cla_mask	= 0xF0, +		.helper		= uicc046_cla_ins_helper, +		.ins_tbl	= uicc_ins_tbl_046, +	}, { +		.cla		= 0x80, +		.cla_mask	= 0xF0, +		.ins_tbl	= uicc_ins_tbl_8ce, +	}, { +		.cla		= 0xC0, +		.cla_mask	= 0xF0, +		.ins_tbl	= uicc_ins_tbl_8ce, +	}, { +		.cla		= 0xE0, +		.cla_mask	= 0xF0, +		.ins_tbl	= uicc_ins_tbl_8ce, +	}, +}; + +const struct osim_cla_ins_card_profile osim_uicc_sim_cic_profile = { +	.name		= "UICC+SIM", +	.description	= "TS 102 221 / 3GPP TS 31.102 + GSM TS 11.11", +	.cic_arr	= uicc_sim_ins_case, +	.cic_arr_size	= ARRAY_SIZE(uicc_sim_ins_case), +}; + +/* 3GPP TS 31.102 */ +const uint8_t usim_ins_case[256] = { +	[0x88]		= 4,	/* AUTHENTICATE */ +}; + +int osim_determine_apdu_case(const struct osim_cla_ins_card_profile *prof, +			     const uint8_t *hdr) +{ +	uint8_t cla = hdr[0]; +	uint8_t ins = hdr[1]; +	int i; +	int rc; + +	for (i = 0; i < prof->cic_arr_size; i++) { +		const struct osim_cla_ins_case *cic = &prof->cic_arr[i]; +		if ((cla & cic->cla_mask) != cic->cla) +			continue; +		rc = cic->ins_tbl[ins]; +		switch (rc) { +		case 0x80: +			return cic->helper(cic, hdr); +		case 0x00: +			/* continue with fruther cic, rather than abort +			 * now */ +			continue; +		default: +			return rc; +		} +	} +	return 0; +}  | 
