commit 417f792f5259aa51eb411679c35b4ad182138b59 from: Sven M. Hallberg date: Thu Mar 24 19:52:37 2022 UTC add 'simcard' example commit - ba66a49b73addf69451f8497dedffdd670d6b06b commit + 417f792f5259aa51eb411679c35b4ad182138b59 blob - 83fd94c11f6beaf00dc41c3d95c2886c3d8df0d3 blob + 4a1b7780aef447b311cdd9df18413185c21fc2f2 --- Makefile +++ Makefile @@ -1,11 +1,11 @@ TESTS = test_nfa test_regex test_pcre test_hammer +APPS = simcard OBJS = nfa.o .PHONY: all clean benchmark -all: ${OBJS} ${TESTS} +all: ${OBJS} ${TESTS} ${APPS} clean: - rm -f ${OBJS} - rm -f ${TESTS} + rm -f ${OBJS} ${TESTS} ${APPS} benchmark: ${TESTS} ./test_nfa -b ./test_regex -b @@ -22,3 +22,6 @@ test_pcre: test_pcre.c test_hammer: test_hammer.c $(CC) $(CFLAGS) -I/usr/local/include $(LDFLAGS) -o $@ $< \ -lhammer + +simcard: $(OBJS) simcard.c + $(CC) $(CFLAGS) -o $@ $(OBJS) $< blob - /dev/null blob + 1f779781ebc3e50890a5b96ebddc4adedcfa5684 (mode 644) --- /dev/null +++ select_mf.hex @@ -0,0 +1 @@ +00 A4 00 0C blob - /dev/null blob + 98c532c584d5181ca916047dcb186b565aaa6450 (mode 644) --- /dev/null +++ simcard.c @@ -0,0 +1,289 @@ +/* simulate a SIM card (application layer) + * pesco 2022 + * + * ref: ETSI TS 102 221 + * aux: ISO 7816-4 + */ + +#include +#include /* uint8_t */ +#include +#include +#include "nfa.h" + +enum gedanken { + SW1 = INNER, SW2 /* which status byte(s) to generate? */ +}; + +#define ACTION (OUTPUT + UCHAR_MAX + 1) +enum action { + STDCHAN = ACTION, /* class byte using standard channels (0-3) */ + EXTCHAN, /* class byte using extended channels (4-19) */ + LENBYTE, /* (any) length byte received */ + LEN256, /* length byte 0, meaning 256, received */ + LCMD, /* command length received */ + LEXP, /* expected response length received */ + + SELECT_MF, /* select the MF */ + SELECT_BYFID, /* select DF/EF/MF by file ID */ + SELECT_CHILDDF, /* select child DF of current DF */ + SELECT_PARENT, /* select parent DF of current DF */ + SELECT_BYNAME, /* select by DF name (i.e. AID) */ + SELECT_ABSPATH, /* select by path from MF */ + SELECT_RELPATH, /* select by path from current DF */ + + TODO /* unimplemented feature, abort match */ +}; + +/* status words; SW1 in high byte, SW2 in low byte. */ +enum status { + SW_NONE = 0, /* command not (yet) completed */ + SW_OK = 0x9000, /* normal ending of command */ + SW_ERROR = 0x6400 /* execution error, mem unchanged */ +}; + +struct mind { + const uint8_t *input; + int channel; + int lc, le; + int len; /* last seen length byte */ + uint16_t status; /* result to output for current command */ + uint8_t output; /* character choice to generate */ +}; + +/* global NFAs created by init() */ +NFA n_todo; +NFA n_len; +NFA n_cla_intr, n_cla_prop; +NFA n_lc, n_le; +NFA n_sw; +NFA n_fcp; +NFA n_select, n_sel_p2non, n_sel_p2fcp; +NFA n_app; + +NFA +octet(uint8_t x) +{ + /* NB: if we have uint8_t, CHAR_BIT must be 8 */ + return chr(x); +} + +NFA +header(bool prop, uint8_t ins) +{ + return seq(prop ? n_cla_prop : n_cla_intr, octet(ins)); +} + +NFA +status(uint16_t sw) +{ + uint8_t sw1 = sw >> 8; + uint8_t sw2 = sw & 0xFF; + + return seq(think(SW1), outchr(sw1), think(SW2), outchr(sw2)); +} + +/* helper: general pattern of the SELECT command */ +NFA +n_sel(uint8_t p1, NFA cdata, enum action a) +{ + NFA act = output(a); + + /* if command data is not empty, attach the Lc byte */ + if (cdata.size > 0) + cdata = seq(n_lc, cdata); + + NFA cont_non = seq(n_sel_p2non, cdata, act, n_sw); + NFA cont_fcp = seq(n_sel_p2fcp, cdata, act, n_le, n_fcp, n_sw); + + return seq(octet(p1), choice(cont_non, cont_fcp)); +} + +/* helper: special case (for SELECT_MF): no cdata, no rdata allowed */ +NFA +n_sel_nn(uint8_t p1, enum action a) +{ + return seq(octet(p1), n_sel_p2non, output(a), n_sw); +} + +void +init_select(void) +{ + /* response data (FCP template) */ + n_fcp = n_todo; + + /* command data (file ID, path, or DF name) */ + NFA fileid = n_todo; + NFA path = n_todo; + NFA name = n_todo; + NFA empty = epsilon(); + + /* possible values for P2 */ + n_sel_p2fcp = octet(0x04); /* return FCP template */ + n_sel_p2non = octet(0x0C); /* no data returned */ + + /* command forms (switching on P1) */ + NFA sel_mf = n_sel_nn(0x00, SELECT_MF); + NFA sel_byfid = n_sel(0x00, fileid, SELECT_BYFID); + NFA sel_childdf = n_sel(0x01, fileid, SELECT_CHILDDF); + NFA sel_parent = n_sel(0x03, empty, SELECT_PARENT); + NFA sel_byname = n_sel(0x04, name, SELECT_BYNAME); + NFA sel_abspath = n_sel(0x08, path, SELECT_ABSPATH); + NFA sel_relpath = n_sel(0x09, path, SELECT_RELPATH); + + n_select = choice(sel_mf, sel_parent, sel_childdf, + sel_byfid, sel_byname, sel_abspath, sel_relpath); +} + +void +init_cla(void) +{ + NFA cla_intr_stdchan = seq(range(0x00, 0x03), output(STDCHAN)); + NFA cla_intr_extchan = seq(range(0x40, 0x4F), output(EXTCHAN)); + NFA cla_prop_stdchan = seq(range(0x80, 0x83), output(STDCHAN)); + NFA cla_prop_extchan = seq(range(0xC0, 0xCF), output(EXTCHAN)); + + n_cla_intr = choice(cla_intr_stdchan, cla_intr_extchan); + n_cla_prop = choice(cla_prop_stdchan, cla_prop_extchan); +} + +void +init(void) +{ + n_todo = output(TODO); + n_len = choice(seq(octet(0), output(LEN256)), + seq(range(1, 255), output(LENBYTE))); + + n_lc = seq(n_len, output(LCMD)); + n_le = seq(n_len, output(LEXP)); + + n_sw = choice(status(SW_OK), status(SW_ERROR)); + + init_cla(); + init_select(); + + n_app = seq(header(0, 0xA4), n_select); +} + +int +sub(int x, size_t state, int pos, void *env_) +{ + struct mind *env = env_; + + switch(x) { + case SW1: + env->output = env->status >> 8; + return 1; + case SW2: + env->output = env->status & 0xFF; + return 1; + default: + fprintf(stderr, "pos %d: unhandled thought %d [state %zu]\n", + pos, x, state); + return 0; + } +} + +int +out(struct range r, size_t state, int pos, void *env_) +{ + struct mind *env = env_; + const uint8_t *p = env->input + pos - 1; + int x = 0; + + /* handle proper output characters */ + if (r.base < ACTION) { + /* + * do we have a specific output character in mind? + * if so, insist on it. + * otherwise, pass simple characters. + */ + if (env->output != 0) { + x = OUTPUT + env->output; + if (x >= r.base && x < r.base + r.n) + env->output = 0; /* done with it */ + } else if (r.n == 1) + x = r.base; + + if (x != 0) { + assert(x >= OUTPUT); + assert(x <= OUTPUT + UCHAR_MAX); + putchar(x - OUTPUT); + return x; + } + + /* reject unrecognized ranges. */ + return -1; + } + + /* handle semantic actions */ + switch (r.base) { + case STDCHAN: + env->channel = *p & 0x03; + break; + case EXTCHAN: + env->channel = 4 + (*p & 0x0F); + break; + case LENBYTE: + env->len = *p; + break; + case LEN256: + env->len = 256; + break; + case LCMD: + env->lc = env->len; + break; + case LEXP: + env->le = env->len; + break; + + case SELECT_MF: + fprintf(stderr, "action: SELECT_MF\n"); + env->status = SW_OK; + break; + + case TODO: + fprintf(stderr, "pos %d: TODO [state %zu]\n", pos, state); + return -1; + default: + fprintf(stderr, "pos %d: unhandled action %d [state %zu]\n", + pos, r.base - ACTION, state); + return -1; + } + assert(r.n == 1); + return r.base; +} + +int +main(int argc, char *argv[]) +{ + char buf[1024]; + struct mind env; + struct prep *pr; + size_t n; + int m; + + init(); + + //nfaprint(n_app); // XXX debug + + n = fread(buf, 1, sizeof buf, stdin); + if (ferror(stdin)) { + perror("stdin"); + return 2; + } + assert(feof(stdin)); // XXX + + env.input = buf; + pr = nfaprep(n_app, sub, out, &env); + + m = nfarun(pr, buf, n); + if (m == -1) { + fprintf(stderr, "Protocol error.\n"); + return 1; + } + if (m < n) + fprintf(stderr, "Trailing garbage (after %d bytes).\n", m); + + return 0; +}