commit - ba66a49b73addf69451f8497dedffdd670d6b06b
commit + 417f792f5259aa51eb411679c35b4ad182138b59
blob - 83fd94c11f6beaf00dc41c3d95c2886c3d8df0d3
blob + 4a1b7780aef447b311cdd9df18413185c21fc2f2
--- Makefile
+++ Makefile
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
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
+00 A4 00 0C
blob - /dev/null
blob + 98c532c584d5181ca916047dcb186b565aaa6450 (mode 644)
--- /dev/null
+++ simcard.c
+/* simulate a SIM card (application layer)
+ * pesco 2022
+ *
+ * ref: ETSI TS 102 221
+ * aux: ISO 7816-4
+ */
+
+#include <stdio.h>
+#include <stdint.h> /* uint8_t */
+#include <stdbool.h>
+#include <assert.h>
+#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;
+}