aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAndreas Eversberg <jolly@eversberg.eu>2020-05-01 19:01:39 +0200
committerAndreas Eversberg <jolly@eversberg.eu>2020-05-24 16:46:39 +0200
commit8fea1ef7bf377a314a5c9e963a708ff0f057194a (patch)
treec379f1bb8075ab05c03675abea0b6eec3c61ca76 /src
parentcc49a3c67454ea168c8a50a03e860c48d17aca41 (diff)
Added SIM card emulator/sniffer for C-NetzHEADmaster
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am3
-rw-r--r--src/libdebug/debug.c4
-rw-r--r--src/libdebug/debug.h4
-rw-r--r--src/sim/Makefile.am18
-rw-r--r--src/sim/eeprom.h33
-rw-r--r--src/sim/image.c97
-rw-r--r--src/sim/main.c489
-rw-r--r--src/sim/sim.c1438
-rw-r--r--src/sim/sim.h148
-rw-r--r--src/sim/sim.ino287
-rw-r--r--src/sim/sniffer.c798
-rw-r--r--src/sim/sniffer.h26
12 files changed, 3344 insertions, 1 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 94c3f6c..40271e5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -52,7 +52,8 @@ SUBDIRS += \
eurosignal \
tv \
radio \
- zeitansage
+ zeitansage \
+ sim
if HAVE_SDR
if HAVE_FUSE
diff --git a/src/libdebug/debug.c b/src/libdebug/debug.c
index 5d2db98..89e0c16 100644
--- a/src/libdebug/debug.c
+++ b/src/libdebug/debug.c
@@ -71,6 +71,10 @@ struct debug_cat {
{ "device", "\033[0;33m" },
{ "datenklo", "\033[1;34m" },
{ "zeit", "\033[1;34m" },
+ { "sim layer 1", "\033[0;31m" },
+ { "sim layer 2", "\033[0;33m" },
+ { "sim ICL layer", "\033[0;36m" },
+ { "sim layer 7", "\033[0;37m" },
{ NULL, NULL }
};
diff --git a/src/libdebug/debug.h b/src/libdebug/debug.h
index 1f74946..33b188f 100644
--- a/src/libdebug/debug.h
+++ b/src/libdebug/debug.h
@@ -34,6 +34,10 @@
#define DDEVICE 27
#define DDATENKLO 28
#define DZEIT 29
+#define DSIM1 30
+#define DSIM2 31
+#define DSIMI 32
+#define DSIM7 33
void get_win_size(int *w, int *h);
diff --git a/src/sim/Makefile.am b/src/sim/Makefile.am
new file mode 100644
index 0000000..7d8d402
--- /dev/null
+++ b/src/sim/Makefile.am
@@ -0,0 +1,18 @@
+AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
+
+bin_PROGRAMS = \
+ cnetz_sim
+
+cnetz_sim_SOURCES = \
+ sim.c \
+ sniffer.c \
+ image.c \
+ main.c
+
+cnetz_sim_LDADD = \
+ $(COMMON_LA) \
+ $(top_builddir)/src/libdebug/libdebug.a \
+ $(top_builddir)/src/liboptions/liboptions.a \
+ $(top_builddir)/src/libserial/libserial.a \
+ -lm
+
diff --git a/src/sim/eeprom.h b/src/sim/eeprom.h
new file mode 100644
index 0000000..b4c8637
--- /dev/null
+++ b/src/sim/eeprom.h
@@ -0,0 +1,33 @@
+
+enum eeprom_locations {
+ EEPROM_MAGIC = 0x00,
+ EEPROM_FUTLN_H = 0x02,
+ EEPROM_FUTLN_M = 0x0a,
+ EEPROM_FUTLN_L = 0x12,
+ EEPROM_SICH_H = 0x1a,
+ EEPROM_SICH_L = 0x22,
+ EEPROM_SONDER_H = 0x2a,
+ EEPROM_SONDER_L = 0x32,
+ EEPROM_WARTUNG_H = 0x3a,
+ EEPROM_WARTUNG_L = 0x42,
+ EEPROM_GEBZ_H = 0x4a,
+ EEPROM_GEBZ_M = 0x4b,
+ EEPROM_GEBZ_L = 0x4c,
+ EEPROM_FLAGS = 0x4d,
+ EEPROM_PIN_DATA = 0x50,
+ EEPROM_AUTH_DATA = 0x58,
+ EEPROM_RUFN = 0x60,
+};
+
+#define EEPROM_VERSION 1 /* version eeprom layout */
+
+#define EEPROM_FLAG_PIN_LEN 0 /* pin length */
+#define EEPROM_FLAG_PIN_TRY 4 /* pin retires left */
+#define EEPROM_FLAG_GEBZ 6 /* metering locked */
+#define EEPROM_FLAG_APP 7 /* application locked */
+
+uint8_t eeprom_read(enum eeprom_locations loc);
+void eeprom_write(enum eeprom_locations loc, uint8_t value);
+uint8_t *eeprom_memory(void);
+size_t eeprom_length();
+
diff --git a/src/sim/image.c b/src/sim/image.c
new file mode 100644
index 0000000..92060c4
--- /dev/null
+++ b/src/sim/image.c
@@ -0,0 +1,97 @@
+#ifndef ARDUINO
+
+#include <stdio.h>
+#include <string.h>
+#include "../libmobile/image.h"
+
+const char *image[] = {
+ "@w",
+ " ()",
+ " // _______________________________________________",
+ " // / \\",
+ " @WC-NETZ SIM@w // | |",
+ " __________//_ | @WJ o l l y ' s@w |",
+ " / o o /| | |",
+ " /__________ / | | @Y _ __ _ @w |",
+ " //_________// / | @bVCC@Y (_)__(_) @bGND@w |",
+ " /@B_@g()@B_/ /_@r()@B_@w/ / | @bRES@Y (_)__(_) @w |",
+ " /@B_@W1@B_/_@W2@B_/_@W3@B_@w/ / | @bCLK@Y (_)__(_) @bI/O@w |",
+ " /@B_@W4@B_/_@W5@B_/_@W6@B_@w/ / | |",
+ " /@B_@W7@B_/_@W8@B_/_@W9@B_@w/ / | |",
+ " /@B_@W*@B_/_@W0@B_/_@W#@B_@w/ / | @y/|_____@w |",
+ " /___________/ / | @y/ @w @WT e l e K a r t e@w |",
+ " | _ _ | / | @y\\ _____@w |",
+ " |____________|/ | @y\\| @w |",
+ " \\_______________________________________________/",
+
+ "",
+ NULL
+};
+
+void print_image(void)
+{
+ int i, j;
+
+ for (i = 0; image[i]; i++) {
+ for (j = 0; j < (int)strlen(image[i]); j++) {
+ if (image[i][j] == '@') {
+ j++;
+ switch(image[i][j]) {
+ case 'k': /* black */
+ printf("\033[0;30m");
+ break;
+ case 'r': /* red */
+ printf("\033[0;31m");
+ break;
+ case 'g': /* green */
+ printf("\033[0;32m");
+ break;
+ case 'y': /* yellow */
+ printf("\033[0;33m");
+ break;
+ case 'b': /* blue */
+ printf("\033[0;34m");
+ break;
+ case 'm': /* magenta */
+ printf("\033[0;35m");
+ break;
+ case 'c': /* cyan */
+ printf("\033[0;36m");
+ break;
+ case 'w': /* white */
+ printf("\033[0;37m");
+ break;
+ case 'K': /* bright black */
+ printf("\033[1;30m");
+ break;
+ case 'R': /* bright red */
+ printf("\033[1;31m");
+ break;
+ case 'G': /* bright green */
+ printf("\033[1;32m");
+ break;
+ case 'Y': /* bright yellow */
+ printf("\033[1;33m");
+ break;
+ case 'B': /* bright blue */
+ printf("\033[1;34m");
+ break;
+ case 'M': /* bright magenta */
+ printf("\033[1;35m");
+ break;
+ case 'C': /* bright cyan */
+ printf("\033[1;36m");
+ break;
+ case 'W': /* bright white */
+ printf("\033[1;37m");
+ break;
+ }
+ } else
+ printf("%c", image[i][j]);
+ }
+ printf("\n");
+ }
+ printf("\033[0;39m");
+}
+
+#endif /* ARDUINO */
diff --git a/src/sim/main.c b/src/sim/main.c
new file mode 100644
index 0000000..fdc352a
--- /dev/null
+++ b/src/sim/main.c
@@ -0,0 +1,489 @@
+/* main function
+ *
+ * (C) 2020 by Andreas Eversberg <jolly@eversberg.eu>
+ * All Rights Reserved
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/>.
+ */
+
+#ifndef ARDUINO
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <math.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include "../libdebug/debug.h"
+#include "../liboptions/options.h"
+#include "../libserial/serial.h"
+#include "../libmobile/image.h"
+#include "sim.h"
+#include "sniffer.h"
+#include "eeprom.h"
+
+int num_kanal = 1;
+sim_sniffer_t sim_sniffer;
+sim_sim_t sim_sim;
+static int quit = 0;
+static const char *serialdev = "/dev/ttyUSB0";
+static int baudrate = 9600;
+
+static const char *eeprom_name = NULL;
+static const char *futln = NULL;
+static const char *sicherung = NULL;
+static const char *karten = NULL;
+static const char *sonder = NULL;
+static const char *wartung = NULL;
+static const char *pin = NULL;
+#define MAX_DIR_COUNT 64
+static int dir_count = 0;
+static int dir_location[MAX_DIR_COUNT];
+static const char *dir_number[MAX_DIR_COUNT];
+static const char *dir_name[MAX_DIR_COUNT];
+static const char *auth = NULL;
+
+#define TIMEOUT 0.2
+
+void print_help(const char *arg0)
+{
+ printf("Usage: %s [options] <command>\n", arg0);
+ /* - - */
+ printf("General options:\n");
+ printf(" -h --help\n");
+ printf(" This help\n");
+ printf(" -v --verbose <level> | <level>,<category>[,<category>[,...]] | list\n");
+ printf(" Use 'list' to get a list of all levels and categories\n");
+ printf(" Verbose level: digit of debug level (default = '%d')\n", debuglevel);
+ printf(" Verbose level+category: level digit followed by one or more categories\n");
+ printf(" -> If no category is specified, all categories are selected\n");
+ printf(" -s --serial-device <device>\n");
+ printf(" Serial device (default = '%s')\n", serialdev);
+ printf(" -b --baud-rate <baud>\n");
+ printf(" Serial baud rate (default = %d)\n", baudrate);
+ printf("\nSIM card simulator options:\n");
+ printf(" -E --eeprom <name>\n");
+ printf(" Stores and reads EEPROM data to/from file. The file is stored at\n");
+ printf(" \"~/osmocom/analog/sim_<name>.eeprom\". If the file dos not exit yet,\n");
+ printf(" the default values are used. Values are always overwritten with card\n");
+ printf(" data, if defined.\n");
+ printf(" -F --futln <phone number>\n");
+ printf(" Give 7 digits subsriber ID (default = '%s')\n", FUTLN_DEFAULT);
+ printf(" --sicherung <security code>\n");
+ printf(" Card's security code for simple authentication (default = '%s')\n", SICHERUNG_DEFAULT);
+ printf(" --kartenkennung <card ID>\n");
+ printf(" Card's ID. Not relevant! (default = '%s')\n", KARTEN_DEFAULT);
+ printf(" --sonder <special code>\n");
+ printf(" Special codes are used for service cards (default = '%s')\n", SONDER_DEFAULT);
+ printf(" --wartung <maitenance code>\n");
+ printf(" May define features of service cards (default = '%s')\n", WARTUNG_DEFAULT);
+ printf(" -P --pin <pin> | 0000\n");
+ printf(" Give 4 .. 8 digits of pin. Use '0000' to disable. (default = '%s')\n", PIN_DEFAULT);
+ printf(" This will also reset the PIN error counter and unlocks the card.\n");
+ printf(" -D --directory <location> <number> <name> [--directory ...]\n");
+ printf(" Give storage location '01' .. '%02d'. To erase give \"\" as number\n", directory_size() - 1);
+ printf(" and name. This option can be given multiple times for more entries.\n");
+ printf(" -A --authenticate 0x...\n");
+ printf(" Give 64 Bit value for authentication response. (default = all bits 1)\n");
+ printf("\nCommands are:\n");
+ printf(" sniff - To passively sniff ATR and message\n");
+ printf(" sim - To simulate a SIM card\n");
+}
+
+#define OPT_SICHERUNG 256
+#define OPT_KARTEN 257
+#define OPT_SONDER 258
+#define OPT_WARTUNG 259
+
+void add_options(void)
+{
+ option_add('h', "help", 0);
+ option_add('v', "debug", 1);
+ option_add('s', "serial-device", 1);
+ option_add('b', "baud-rate", 1);
+ option_add('E', "eeprom", 1);
+ option_add('F', "futln", 1);
+ option_add(OPT_SICHERUNG, "sicherung", 1);
+ option_add(OPT_KARTEN, "kartenkennung", 1);
+ option_add(OPT_SONDER, "sonder", 1);
+ option_add(OPT_WARTUNG, "wartung", 1);
+ option_add('P', "pin", 1);
+ option_add('D', "directory", 3);
+ option_add('A', "auth", 1);
+};
+
+int handle_options(int short_option, int argi, char **argv)
+{
+ int rc;
+
+ switch (short_option) {
+ case 'h':
+ print_help(argv[0]);
+ return 0;
+ case 'v':
+ if (!strcasecmp(argv[argi], "list")) {
+ debug_list_cat();
+ return 0;
+ }
+ rc = parse_debug_opt(argv[argi]);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to parse debug option, please use -h for help.\n");
+ return rc;
+ }
+ break;
+ case 's':
+ serialdev = strdup(argv[argi]);
+ break;
+ case 'b':
+ baudrate = atoi(argv[argi]);
+ break;
+ case 'E':
+ eeprom_name = strdup(argv[argi]);
+ break;
+ case 'F':
+ futln = strdup(argv[argi]);
+ break;
+ case OPT_SICHERUNG:
+ sicherung = strdup(argv[argi]);
+ break;
+ case OPT_KARTEN:
+ karten = strdup(argv[argi]);
+ break;
+ case OPT_SONDER:
+ sonder = strdup(argv[argi]);
+ break;
+ case OPT_WARTUNG:
+ wartung = strdup(argv[argi]);
+ break;
+ case 'P':
+ pin = strdup(argv[argi]);
+ break;
+ case 'D':
+ if (dir_count == MAX_DIR_COUNT)
+ break;
+ dir_location[dir_count] = atoi(argv[argi + 0]);
+ dir_number[dir_count] = strdup(argv[argi + 1]);
+ dir_name[dir_count] = strdup(argv[argi + 2]);
+ dir_count++;
+ break;
+ case 'A':
+ auth = strdup(argv[argi]);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 1;
+}
+
+/* EERPOM emulation */
+
+static uint8_t eeprom[2048];
+
+uint8_t eeprom_read(enum eeprom_locations loc)
+{
+ if (loc >= sizeof(eeprom))
+ abort();
+
+ return eeprom[loc];
+}
+
+void eeprom_write(enum eeprom_locations loc, uint8_t value)
+{
+ if (loc >= sizeof(eeprom))
+ abort();
+
+ eeprom[loc] = value;
+}
+
+uint8_t *eeprom_memory(void)
+{
+ return eeprom;
+}
+
+size_t eeprom_length(void)
+{
+ return sizeof(eeprom);
+}
+
+/* main loop for interfacing serial with sim / sniffer */
+
+int main_loop(serial_t *serial, int sniffer)
+{
+ int rc, cts, last_cts = 0;
+ uint8_t byte;
+ int skip_bytes = 0;
+ int work = 0;
+
+ struct timeval tv;
+ double now, timer = 0;
+
+ quit = 0;
+
+ while (!quit) {
+ gettimeofday(&tv, NULL);
+ now = (double)tv.tv_usec * 0.000001 + tv.tv_sec;
+
+ /* only check CTS when no work was done
+ * this is because USB query may take some time
+ * and we don't want to block transfer
+ */
+ if (!work) {
+ cts = serial_cts(serial);
+ /* initally AND when CTS becomes 1 (pulled to low by reset line) */
+ if (last_cts != cts) {
+ if (sniffer == 1)
+ sniffer_reset(&sim_sniffer);
+ else
+ sim_reset(&sim_sim, cts);
+ timer = 0;
+ }
+ last_cts = cts;
+ }
+ work = 0;
+
+ if (sniffer == 0) {
+ rc = sim_tx(&sim_sim);
+ if (rc >= 0) {
+ byte = rc;
+ serial_write(serial, &byte, 1);
+ work = 1;
+ skip_bytes++;
+ }
+ }
+
+ rc = serial_read(serial, &byte, 1);
+ if (rc > 0)
+ work = 1;
+ /* ignore while reset is low */
+ if (cts)
+ continue;
+ if (rc == 1) {
+ timer = now;
+ /* count length, to remove echo from transmission */
+ if (!skip_bytes) {
+ if (sniffer == 1)
+ sniffer_rx(&sim_sniffer, byte);
+ else
+ sim_rx(&sim_sim, byte);
+ } else {
+ /* done eliminating TX data, so we reset timer */
+ if (--skip_bytes == 0)
+ timer = 0;
+ }
+ } else {
+ rc = -1;
+ if (timer && now - timer > 12.0 * 5.0 / (double)baudrate) {
+ if (sniffer == 1)
+ sniffer_timeout(&sim_sniffer);
+ else
+ sim_timeout(&sim_sim);
+ timer = 0;
+ skip_bytes = 0;
+ }
+ }
+
+ if (!work) {
+ /* sleep some time if nothing was received */
+ usleep(100);
+ }
+ }
+
+ return quit;
+}
+
+void sighandler(int sigset)
+{
+ if (sigset == SIGHUP)
+ return;
+ if (sigset == SIGPIPE)
+ return;
+
+ printf("Signal received: %d\n", sigset);
+
+ quit = -1;
+}
+
+int main(int argc, char *argv[])
+{
+ const char *home;
+ char eeprom_file[128];
+ FILE *fp;
+ serial_t *serial = NULL;
+ uint8_t ebdt_data[9];
+ int rc, argi;
+ int sniffer = 0;
+ int i;
+
+ debuglevel = DEBUG_INFO;
+
+ add_options();
+ rc = options_config_file("~/.osmocom/analog/simsim.conf", handle_options);
+ if (rc < 0)
+ return 0;
+
+ rc = sim_init_eeprom();
+ if (rc < 0)
+ return rc;
+
+ /* parse command line */
+ argi = options_command_line(argc, argv, handle_options);
+ if (argi <= 0)
+ return argi;
+
+ /* read from eeprom file, if defined and exists */
+ if (eeprom_name) {
+ /* open config file */
+ home = getenv("HOME");
+ if (home == NULL)
+ return 1;
+ sprintf(eeprom_file, "%s/.osmocom/analog/sim_%s.eeprom", home, eeprom_name);
+
+ fp = fopen(eeprom_file, "r");
+ if (fp) {
+ rc = fread(eeprom_memory(), eeprom_length(), 1, fp);
+ fclose(fp);
+ } else
+ PDEBUG(DOPTIONS, DEBUG_INFO, "EEPROM file '%s' does not exist yet.\n", eeprom_file);
+
+ }
+
+ /* check version */
+ if (eeprom_read(EEPROM_MAGIC + 0) != 'C' || eeprom_read(EEPROM_MAGIC + 1) != '0' + EEPROM_VERSION) {
+ PDEBUG(DOPTIONS, DEBUG_ERROR, "EEPROM file '%s' is not compatible with this version of program, please remove it!\n", eeprom_file);
+ return 1;
+ }
+
+ /* apply config to eeprom, if defined */
+ ebdt_data[0] = eeprom_read(EEPROM_FUTLN_H);
+ ebdt_data[1] = eeprom_read(EEPROM_FUTLN_M);
+ ebdt_data[2] = eeprom_read(EEPROM_FUTLN_L);
+ ebdt_data[3] = eeprom_read(EEPROM_SICH_H);
+ ebdt_data[4] = eeprom_read(EEPROM_SICH_L);
+ ebdt_data[5] = eeprom_read(EEPROM_SONDER_H);
+ ebdt_data[6] = eeprom_read(EEPROM_SONDER_L);
+ ebdt_data[7] = eeprom_read(EEPROM_WARTUNG_H);
+ ebdt_data[8] = eeprom_read(EEPROM_WARTUNG_L);
+ rc = encode_ebdt(ebdt_data, futln, sicherung, karten, sonder, wartung);
+ if (rc < 0)
+ return 0;
+ eeprom_write(EEPROM_FUTLN_H, ebdt_data[0]);
+ eeprom_write(EEPROM_FUTLN_M, ebdt_data[1]);
+ eeprom_write(EEPROM_FUTLN_L, ebdt_data[2]);
+ eeprom_write(EEPROM_SICH_H, ebdt_data[3]);
+ eeprom_write(EEPROM_SICH_L, ebdt_data[4]);
+ eeprom_write(EEPROM_SONDER_H, ebdt_data[5]);
+ eeprom_write(EEPROM_SONDER_L, ebdt_data[6]);
+ eeprom_write(EEPROM_WARTUNG_H, ebdt_data[7]);
+ eeprom_write(EEPROM_WARTUNG_L, ebdt_data[8]);
+ if (pin) {
+ if (strlen(pin) < 4 || strlen(pin) > 8) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "Given PIN '%s' has invalid length. (Must be 4 .. 8)\n", pin);
+ return 0;
+ }
+ eeprom_write(EEPROM_FLAGS, (strlen(pin) << EEPROM_FLAG_PIN_LEN) | (MAX_PIN_TRY << EEPROM_FLAG_PIN_TRY));
+ for (i = 0; i < (int)strlen(pin); i++)
+ eeprom_write(EEPROM_PIN_DATA + i, pin[i]);
+ }
+ for (i = 0; i < dir_count; i++) {
+ uint8_t data[24];
+ rc = encode_directory(data, dir_number[i], dir_name[i]);
+ if (rc < 0)
+ return 0;
+ rc = save_directory(dir_location[i], data);
+ if (rc < 0)
+ return 0;
+ }
+ if (auth) {
+ uint64_t value = strtoull(auth, NULL, 0);
+ for (i = 0; i < 8; i++)
+ eeprom_write(EEPROM_AUTH_DATA, value >> (8 * (7 - i)));
+ }
+
+ if (argi >= argc) {
+ fprintf(stderr, "Expecting command, use '-h' for help!\n");
+ return 0;
+ } else if (!strcmp(argv[argi], "sniff")) {
+ sniffer = 1;
+ } else if (!strcmp(argv[argi], "sim")) {
+ sniffer = 0;
+ } else {
+ fprintf(stderr, "Unknown command '%s', use '-h' for help!\n", argv[argi]);
+ return -EINVAL;
+ }
+
+ /* open serial device */
+ serial = serial_open(serialdev, baudrate, 8, 'e', 2, 'd', 'd', 0, 1.0, 0.0);
+ if (!serial) {
+ printf("Serial failed: %s\n", serial_errnostr);
+ goto error;
+ }
+
+ if (sniffer == 1)
+ printf("SIM analyzer ready, please start the phone!\n");
+ else {
+ char temp[5][16];
+ print_image();
+ decode_ebdt(ebdt_data, temp[0], temp[1], temp[2], temp[3], temp[4]);
+ printf("FUTLN=%s, Sicherungscode=%s, Kartekennung=%s, Sonderheitenschluessel=%s, Wartungsschluessel=%s\n", temp[0], temp[1], temp[2], temp[3], temp[4]);
+ printf("Telephone directory has %d entries.\n", directory_size() - 1);
+ for (i = 0; i < directory_size() - 1; i++) {
+ uint8_t data[24];
+ char number[32], name[32];
+ load_directory(i + 1, data);
+ decode_directory(data, number, name);
+ if (number[0])
+ printf(" -> %02d %16s %s\n", i + 1, number, name);
+ }
+ printf("SIM emulator ready, please start the phone!\n");
+ }
+
+ /* catch signals */
+ signal(SIGINT, sighandler);
+ signal(SIGHUP, sighandler);
+ signal(SIGTERM, sighandler);
+ signal(SIGPIPE, sighandler);
+
+ /* run main loop until terminated by user */
+ main_loop(serial, sniffer);
+
+ /* reset signals */
+ signal(SIGINT, SIG_DFL);
+ signal(SIGHUP, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGPIPE, SIG_DFL);
+
+ /* write to eeprom file, if defined */
+ if (eeprom_name) {
+ fp = fopen(eeprom_file, "w");
+ if (fp) {
+ fwrite(eeprom_memory(), eeprom_length(), 1, fp);
+ fclose(fp);
+ PDEBUG(DOPTIONS, DEBUG_INFO, "EEPROM file '%s' written.\n", eeprom_file);
+ } else
+ PDEBUG(DOPTIONS, DEBUG_INFO, "EEPROM file '%s' cannot be written. (errno = %d)\n", eeprom_file, errno);
+ }
+
+error:
+ if (serial)
+ serial_close(serial);
+
+ return 0;
+}
+
+#endif /* ARDUINO */
diff --git a/src/sim/sim.c b/src/sim/sim.c
new file mode 100644
index 0000000..fba9417
--- /dev/null
+++ b/src/sim/sim.c
@@ -0,0 +1,1438 @@
+/* SIM card emulator
+ *
+ * (C) 2020 by Andreas Eversberg <jolly@eversberg.eu>
+ * All Rights Reserved
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#ifndef ARDUINO
+#include "../libdebug/debug.h"
+#endif
+#include "sim.h"
+#include "eeprom.h"
+
+#ifdef ARDUINO
+#define PDEBUG(cat, level, fmt, arg...) while(0)
+#define EINVAL 22
+static uint32_t my_strtoul(const char *nptr, char **endptr, int base)
+{
+ uint32_t number = 0;
+
+ while (*nptr >= '0' && *nptr <= '9')
+ number = number * 10 + (*nptr++ - '0');
+
+ return number;
+}
+#else
+#define my_strtoul strtoul
+#endif
+
+static void my_ultostr(char *nptr, uint32_t value, int zeros)
+{
+ int digits = 0;
+ uint32_t temp;
+
+ /* count digits */
+ temp = value;
+ while (temp) {
+ temp /= 10;
+ digits++;
+ }
+
+ /* minium digits to fill up with '0' */
+ if (digits < zeros)
+ digits = zeros;
+
+ /* go to end and terminate */
+ nptr += digits;
+ *nptr-- = '\0';
+
+ /* apply digits backwards */
+ while (digits--) {
+ *nptr-- = (value % 10) + '0';
+ value /= 10;
+ }
+}
+
+static void tx_sdu(sim_sim_t *sim, uint8_t ccrc, uint8_t *data, int length);
+static void tx_pdu(sim_sim_t *sim, uint8_t *data, int length);
+static void tx_block(sim_sim_t *sim, enum l2_cmd cmd, uint8_t *data, int length);
+
+/* read flags from eeprom */
+static void read_flags(sim_sim_t *sim)
+{
+ uint8_t flags;
+
+ flags = eeprom_read(EEPROM_FLAGS);
+ sim->pin_len = (flags >> EEPROM_FLAG_PIN_LEN) & 0xf;
+ sim->pin_try = (flags >> EEPROM_FLAG_PIN_TRY) & 0x3;
+ if ((flags >> EEPROM_FLAG_GEBZ) & 0x1)
+ sim->gebz_locked = 1;
+ if ((flags >> EEPROM_FLAG_APP) & 0x1)
+ sim->app_locked = 1;
+}
+
+/* write flags to eeprom */
+static void write_flags(sim_sim_t *sim)
+{
+ uint8_t flags = 0;
+
+ flags |= sim->pin_len << EEPROM_FLAG_PIN_LEN;
+ flags |= sim->pin_try << EEPROM_FLAG_PIN_TRY;
+ if (sim->gebz_locked)
+ flags |= (1 << EEPROM_FLAG_GEBZ);
+ if (sim->app_locked)
+ flags |= (1 << EEPROM_FLAG_APP);
+ eeprom_write(EEPROM_FLAGS, flags);
+}
+
+/* encode EBDT from strings */
+int encode_ebdt(uint8_t *data, const char *futln, const char *sicherung, const char *karten, const char *sonder, const char *wartung)
+{
+ uint32_t temp;
+ int i;
+
+ if (futln) {
+ temp = strlen(futln);
+ if (temp < 7 || temp > 8) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "Given FUTLN '%s' invalid length. (Must be 7 or 8 Digits)\n", futln);
+ return -EINVAL;
+ }
+ if (futln[0] < '0' || futln[0] > '7') {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "Given FUTLN '%s' has invalid first digit. (Must be '0' .. '7')\n", futln);
+ return -EINVAL;
+ }
+ data[0] = (futln[0] - '0') << 5;
+ futln++;
+ if (temp == 8) {
+ /* 8 digits */
+ temp = (futln[0] - '0') * 10 + (futln[1] - '0');
+ if (futln[0] < '0' || futln[0] > '9' || futln[1] < '0' || futln[1] > '9' || temp > 31) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "Given FUTLN '%s' has invalid second and third digit. (Must be '00' .. '31')\n", futln);
+ return -EINVAL;
+ }
+ data[0] |= temp;
+ futln += 2;
+ } else {
+ /* 7 digits */
+ if (futln[0] < '0' || futln[0] > '9') {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "Given FUTLN '%s' has invalid second digit. (Must be '0' .. '9')\n", futln);
+ return -EINVAL;
+ }
+ data[0] |= (futln[0] - '0');
+ futln++;
+ }
+ for (i = 0; i < 5; i++) {
+ if (futln[i] < '0' || futln[i] > '9')
+ break;
+ }
+ temp = my_strtoul(futln, NULL, 0);
+ if (i < 5 || temp > 65535) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "Given FUTLN '%s' has invalid last digits. (Must be '00000' .. '65535')\n", futln);
+ return -EINVAL;
+ }
+ data[1] = temp >> 8;
+ data[2] = temp;
+ }
+
+ if (sicherung) {
+ temp = my_strtoul(sicherung, NULL, 0);
+ if (temp > 65535) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "Given security code '%s' has invalid digits. (Must be '0' .. '65535')\n", sicherung);
+ return -EINVAL;
+ }
+ data[3] = temp >> 8;
+ data[4] = temp;
+ }
+
+ if (karten) {
+ temp = my_strtoul(karten, NULL, 0);
+ if (temp > 7) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "Given card number '%s' has invalid digit. (Must be '0' .. '7')\n", karten);
+ return -EINVAL;
+ }
+ data[5] = (data[5] & 0x1f) | ((karten[0] - '0') << 5);
+ }
+
+ if (sonder) {
+ temp = my_strtoul(sonder, NULL, 0);
+ if (temp > 8191) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "Given spacial code '%s' has invalid digits. (Must be '0' .. '8191')\n", sonder);
+ return -EINVAL;
+ }
+ data[5] = (data[5] & 0xe0) | (temp >> 8);
+ data[6] = temp;
+ }
+
+ if (wartung) {
+ temp = my_strtoul(wartung, NULL, 0);
+ if (temp > 65535) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "Given maintenance code '%s' has invalid digits. (Must be '0' .. '65535')\n", wartung);
+ return -EINVAL;
+ }
+ data[7] = temp >> 8;
+ data[8] = temp;
+ }
+
+ return 0;
+}
+
+/* convert EBDT to string */
+void decode_ebdt(uint8_t *data, char *futln, char *sicherung, char *karten, char *sonder, char *wartung)
+{
+ if (futln) {
+ /* second value becomes two digits automatically, if > 9 */
+ my_ultostr(futln++, data[0] >> 5, 1);
+ my_ultostr(futln++, data[0] & 0x1f, 1);
+ if (*futln)
+ futln++;
+ my_ultostr(futln, (data[1] << 8) | data[2], 5);
+ }
+
+ if (sicherung)
+ my_ultostr(sicherung, (data[3] << 8) | data[4], 1);
+
+ if (karten)
+ my_ultostr(karten, data[5] >> 5, 1);
+
+ if (sonder)
+ my_ultostr(sonder, ((data[5] & 0x1f) << 8) | data[6], 1);
+
+ if (wartung)
+ my_ultostr(wartung, (data[7] << 8) | data[8], 1);
+}
+
+/* get size of phone directory (including allocation map) */
+int directory_size(void)
+{
+ /* get size from space in eeprom */
+ int size = (eeprom_length() - EEPROM_RUFN) / 24;
+
+ /* may have 184 entries (23*8) plus allocation map (entry 0) */
+ if (size > 184 + 1)
+ size = 184 + 1;
+
+ return size;
+}
+
+/* store one phone number in the directory; also set allocation mask) */
+int save_directory(int location, uint8_t *data)
+{
+ int size, i, pos;
+ uint8_t mask;
+
+ size = directory_size();
+ if (location < 1 || location >= size) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "Given location for phone number '%d' is out of range. (Must be '01' .. '%02d')\n", location, size - 1);
+ return -EINVAL;
+ }
+
+ /* store entry */
+ for (i = 0; i < 24; i++)
+ eeprom_write(EEPROM_RUFN + 24 * location + i, data[i]);
+ /* set bit mask */
+ pos = EEPROM_RUFN + 1 + ((location - 1) >> 3);
+ mask = eeprom_read(pos);
+ if ((data[7] & 0xf) == 0xf)
+ mask |= (0x80 >> ((location - 1) & 7));
+ else
+ mask &= ~(0x80 >> ((location - 1) & 7));
+ eeprom_write(pos, mask);
+
+ return 0;
+}
+
+/* load one phone number from the directory; location 0 is the allocation mask) */
+void load_directory(int location, uint8_t *data)
+{
+ int i;
+
+ for (i = 0; i < 24; i++)
+ data[i] = eeprom_read(EEPROM_RUFN + 24 * location + i);
+ /* set directory size, on allocation map */
+ if (location == 0)
+ data[0] = directory_size() - 1;
+}
+
+/* encode number an name into directory data */
+int encode_directory(uint8_t *data, const char *number, const char *name)
+{
+ int len, pos, i;
+
+ len = strlen(number);
+ if (len > 16) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "Given phone number '%s' has too many digits. (Must be <= 16)\n", number);
+ return -EINVAL;
+ }
+
+ memset(data, 0xff, 8);
+ memset(data + 8, ' ', 16);
+ for (i = 0; i < len; i++) {
+ if (number[i] < '0' || number[i] > '9') {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "Given phone number '%s' has illegal digits. (Must be '0' .. '9')\n", number);
+ return -EINVAL;
+ }
+ pos = 16 - len + i;
+ if ((pos & 1) == 0)
+ data[pos >> 1] += ((number[i] - '0') << 4) - 0xf0;
+ else
+ data[pos >> 1] += number[i] - '0' - 0xf;
+ }
+ len = strlen(name);
+ if (len > 16)
+ len = 16;
+ for (i = 0; i < len; i++) {
+ pos = 8 + i;
+ data[pos] = name[i];
+ }
+
+ return 0;
+}
+
+void decode_directory(uint8_t *data, char *number, char *name)
+{
+ int i, j;
+ char digit;
+
+ if (number) {
+ j = 0;
+ for (i = 0; i < 16; i++) {
+ if ((i & 1) == 0)
+ digit = (data[i >> 1] >> 4) + '0';
+ else
+ digit = (data[i >> 1] & 0xf) + '0';
+ if (digit <= '9')
+ number[j++] = digit;
+ }
+ number[j] = '\0';
+ }
+
+ if (name) {
+ memcpy(name, data + 8, 16);
+ name[16] = '\0';
+ /* remove spaces in the end of the string */
+ for (i = 16 - 1; i >= 0; i--) {
+ if (name[i] != ' ')
+ break;
+ name[i] = '\0';
+ }
+ }
+}
+
+/* get APRC of NETZ-C application */
+static uint8_t get_aprc(sim_sim_t *sim)
+{
+ uint8_t aprc = 0x00;
+
+ if (sim->pin_required)
+ aprc |= APRC_PIN_REQ;
+ if (sim->app_locked)
+ aprc |= APRC_APP_LOCKED;
+ if (sim->gebz_locked)
+ aprc |= APRC_GEBZ_LOCK;
+ if (sim->gebz_full)
+ aprc |= APRC_GEBZ_FULL;
+
+ return aprc;
+}
+
+/* validate PIN and change states */
+static int validate_pin(sim_sim_t *sim, uint8_t *data, int length)
+{
+ uint8_t valid = 0, program_mode = 0;
+ int i;
+
+ if (!sim->pin_required)
+ return 0;
+
+ /* no PIN mode */
+ if (length == 4 && data[0] == '0' && data[1] == '0' && data[2] == '0' && data[3] >= '0' && data[3] <= '0' + MAX_CARDS) {
+ valid = 1;
+ if (data[3] > '0')
+ sim->card = data[3] - '1';
+ PDEBUG(DSIM1, DEBUG_INFO, "System PIN '000%c' entered. Selecting card #%d.\n", data[3], sim->card + 1);
+ }
+
+ /* programming mode */
+ if (length == 4 && data[0] == '9' && data[1] == '9' && data[2] == '9' && data[3] >= '0' && data[3] <= '0' + MAX_CARDS) {
+ program_mode = 1;
+ valid = 1;
+ if (data[3] > '0')
+ sim->card = data[3] - '1';
+ PDEBUG(DSIM1, DEBUG_INFO, "Configuration PIN '999%c' entered. Selecting card #%d in configuration mode.\n", data[3], sim->card + 1);
+ }
+
+ /* if not 'program mode' and PIN matches EEPROM */
+ if (!valid && length == sim->pin_len) {
+ for (i = 0; i < length; i++) {
+ if (data[i] != eeprom_read(EEPROM_PIN_DATA + i))
+ break;
+ }
+ if (i == length) {
+ valid = 1;
+ PDEBUG(DSIM1, DEBUG_INFO, "Correct PIN was entered. Selecting card #%d.\n", sim->card + 1);
+ }
+ }
+
+ if (valid) {
+ /* prevent permanent write when not needed */
+ if (sim->pin_try != MAX_PIN_TRY) {
+ sim->pin_try = MAX_PIN_TRY;
+ write_flags(sim);
+ }
+ sim->pin_required = 0;
+ if (program_mode)
+ sim->program_mode = 1;
+ return 0;
+ } else {
+ PDEBUG(DSIM1, DEBUG_INFO, "Wrong PIN was entered.\n");
+#ifndef ARDUINO
+ /* decrement error counter */
+ if (sim->pin_try) {
+ sim->pin_try--;
+ write_flags(sim);
+ }
+#endif
+ return -EINVAL;
+ }
+}
+
+/* message buffer handling */
+
+/* get space for return message */
+uint8_t *alloc_msg(sim_sim_t *sim, int size)
+{
+ /* we add 4, because we push 4 bytes (ICL and L2 header later) */
+ if (size + 4 > (int)sizeof(sim->block_tx_data))
+ PDEBUG(DSIM1, DEBUG_NOTICE, "TX buffer overflow: size+4=%d > buffer size (%d)\n", size + 4, (int)sizeof(sim->block_tx_data));
+ return sim->block_tx_data;
+}
+
+/* push space in front of a message */
+uint8_t *push_msg(uint8_t *data, int length, int offset)
+{
+ int i;
+
+ for (i = length - 1; i >= 0; --i)
+ data[i + offset] = data[i];
+
+ return data;
+}
+
+/* Layer 7 */
+
+static void return_error(sim_sim_t *sim)
+{
+ uint8_t *data;
+
+ data = alloc_msg(sim, 0);
+ tx_sdu(sim, CCRC_ERROR, data, 0);
+}
+
+static void return_pin_not_ok(sim_sim_t *sim)
+{
+ uint8_t *data;
+
+ data = alloc_msg(sim, 0);
+ tx_sdu(sim, CCRC_PIN_NOK, data, 0);
+}
+
+/* command: open application */
+static void sl_appl(sim_sim_t *sim, uint8_t *data, int length)
+{
+ uint8_t app;
+
+ if (length < 11) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "SL-APPL too short\n");
+ return_error(sim);
+ return;
+ }
+
+ /* application number */
+ app = (data[6] - '0') * 100;
+ app += (data[7] - '0') * 10;
+ app += data[8] - '0';
+
+ PDEBUG(DSIM7, DEBUG_INFO, " SL-APPL app %d\n", app);
+
+ /* if PIN is required */
+ if (sim->pin_required) {
+ return_pin_not_ok(sim);
+ return;
+ }
+
+ /* check application */
+ if (app != APP_NETZ_C && app != APP_RUFN_GEBZ) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "SL-APPL invalid app %d\n", sim->app);
+ return_error(sim);
+ return;
+ }
+
+ /* respond */
+ sim->app = app;
+ data = alloc_msg(sim, 0);
+ tx_sdu(sim, 0, data, 0);
+}
+
+/* command: close application */
+static void cl_appl(sim_sim_t *sim)
+{
+ uint8_t *data;
+
+ PDEBUG(DSIM7, DEBUG_INFO, " CL-APPL\n");
+
+ /* remove app */
+ sim->app = 0;
+
+ /* respond */
+ data = alloc_msg(sim, 0);
+ tx_sdu(sim, 0, data, 0);
+}
+
+/* command: show application */
+static void sh_appl(sim_sim_t *sim)
+{
+ uint8_t *data;
+
+ PDEBUG(DSIM7, DEBUG_INFO, " SH-APPL\n");
+
+ /* respond */
+ data = alloc_msg(sim, 33);
+ switch (sim->sh_appl_count) {
+ case 0: // first application is shown
+ /* L */
+ data[0] = 11;
+ /* APP-IDN */
+ data[1] = '8'; data[2] = '9';
+ data[3] = '4'; data[4] = '9';
+ data[5] = '0'; data[6] = '1';
+ data[7] = '0'; data[8] = '0'; data[9] = '3';
+ data[10] = '0'; data[11] = '1';
+ /* APP-TXT */
+ memcpy(data + 12, "Netz C ", 20);
+ /* APP-STS */
+ data[32] = get_aprc(sim);
+ tx_sdu(sim, 0, data, 33);
+ sim->sh_appl_count++;
+ break;
+ default: // no more application
+ tx_sdu(sim, 0, data, 0);
+ sim->sh_appl_count = 0;
+ }
+}
+
+/* command: show state of chip card */
+static void chk_kon(sim_sim_t *sim)
+{
+ uint8_t *data;
+
+ PDEBUG(DSIM7, DEBUG_INFO, " CHK-KON\n");
+
+ /* respond */
+ data = alloc_msg(sim, 0);
+ tx_sdu(sim, 0, data, 0);
+}
+
+/* command: read subscriber data */
+static void rd_ebdt(sim_sim_t *sim)
+{
+ uint8_t *data;
+
+ PDEBUG(DSIM7, DEBUG_INFO, " RD-EBDT\n");
+
+ /* respond */
+ data = alloc_msg(sim, 9);
+ if (sim->program_mode) {
+ /* SERVICE MODE */
+ data[0] = 0;
+ data[1] = 0;
+ data[2] = sim->card + 1;
+ data[3] = 12345 >> 8;
+ data[4] = 12345 & 0xff;
+ data[5] = 3 << 5;
+ data[6] = 0;
+ data[7] = 0x0ff;
+ data[8] = 0x0ff;
+ } else {
+ data[0] = eeprom_read(EEPROM_FUTLN_H + sim->card);
+ data[1] = eeprom_read(EEPROM_FUTLN_M + sim->card);
+ data[2] = eeprom_read(EEPROM_FUTLN_L + sim->card);
+ data[3] = eeprom_read(EEPROM_SICH_H + sim->card);
+ data[4] = eeprom_read(EEPROM_SICH_L + sim->card);
+ data[5] = eeprom_read(EEPROM_SONDER_H + sim->card);
+ data[6] = eeprom_read(EEPROM_SONDER_L + sim->card);
+ data[7] = eeprom_read(EEPROM_WARTUNG_H + sim->card);
+ data[8] = eeprom_read(EEPROM_WARTUNG_L + sim->card);
+ }
+ tx_sdu(sim, 0, data, 9);
+}
+
+/* command: read phone directory */
+static void rd_rufn(sim_sim_t *sim, uint8_t *data, int length)
+{
+ uint8_t rufn = data[0];
+ int size;
+
+ if (length < 1) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "RD_RUFN too short\n");
+ return_error(sim);
+ return;
+ }
+
+ PDEBUG(DSIM7, DEBUG_INFO, " RD-RUFN (loc=%d)\n", rufn);
+
+ /* SERVICE MODE */
+ if (sim->program_mode) {
+ char number[16];
+
+ /* respond */
+ data = alloc_msg(sim, 24);
+ switch (rufn) {
+ case 0: /* send bitmap for service mode */
+ memset(data, 0xff, 24);
+ data[0] = 5; /* 5 entries */
+ data[1] = 0x07; /* upper 5 bits = 0 */
+ break;
+ case 1: /* FUTLN */
+ data[0] = eeprom_read(EEPROM_FUTLN_H + sim->card);
+ data[1] = eeprom_read(EEPROM_FUTLN_M + sim->card);
+ data[2] = eeprom_read(EEPROM_FUTLN_L + sim->card);
+ decode_ebdt(data, number, NULL, NULL, NULL, NULL);
+ encode_directory(data, number, "FUTLN");
+ PDEBUG(DSIM7, DEBUG_INFO, "service mode: FUTLN = %s\n", number);
+ break;
+ case 2: /* security code */
+ data[3] = eeprom_read(EEPROM_SICH_H + sim->card);
+ data[4] = eeprom_read(EEPROM_SICH_L + sim->card);
+ decode_ebdt(data, NULL, number, NULL, NULL, NULL);
+ encode_directory(data, number, "Sicherungscode");
+ PDEBUG(DSIM7, DEBUG_INFO, "service mode: security = %s\n", number);
+ break;
+ case 3: /* card ID */
+ data[5] = eeprom_read(EEPROM_SONDER_H + sim->card);
+ decode_ebdt(data, NULL, NULL, number, NULL, NULL);
+ encode_directory(data, number, "Kartenkennung");
+ PDEBUG(DSIM7, DEBUG_INFO, "service mode: card = %s\n", number);
+ break;
+ case 4: /* special key */
+ data[5] = eeprom_read(EEPROM_SONDER_H + sim->card);
+ data[6] = eeprom_read(EEPROM_SONDER_L + sim->card);
+ decode_ebdt(data, NULL, NULL, NULL, number, NULL);
+ encode_directory(data, number, "Sonderheitsschl.");
+ PDEBUG(DSIM7, DEBUG_INFO, "service mode: special = %s\n", number);
+ break;
+ case 5: /* maintenance key */
+ data[7] = eeprom_read(EEPROM_WARTUNG_H + sim->card);
+ data[8] = eeprom_read(EEPROM_WARTUNG_L + sim->card);
+ decode_ebdt(data, NULL, NULL, NULL, NULL, number);
+ encode_directory(data, number, "Wartungsschl.");
+ PDEBUG(DSIM7, DEBUG_INFO, "service mode: maintenance = %s\n", number);
+ break;
+ }
+ tx_sdu(sim, 0, data, 24);
+ return;
+ }
+
+ size = directory_size();
+ /* first entry (0) is used as allocation map */
+ PDEBUG(DSIM7, DEBUG_INFO, " %d numbers can be stored in EEPROM\n", size - 1);
+ if (rufn >= size) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "RD_RUFN entry #%d out of range\n", rufn);
+ return_error(sim);
+ return;
+ }
+
+ /* respond */
+ data = alloc_msg(sim, 24);
+ load_directory(rufn, data);
+ tx_sdu(sim, 0, data, 24);
+}
+
+/* command: write phone directory */
+static void wt_rufn(sim_sim_t *sim, uint8_t *data, int length)
+{
+ uint8_t rufn = data[0];
+
+ if (length < 25) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "WT_RUFN too short\n");
+ return_error(sim);
+ return;
+ }
+
+ PDEBUG(DSIM7, DEBUG_INFO, " WT-RUFN (loc=%d)\n", rufn);
+
+ /* SERVICE MODE */
+ if (sim->program_mode) {
+ int rc;
+ char number[17];
+
+ decode_directory(data + 1, number, NULL);
+ /* if number is cleared, we ignore that */
+ if (number[0] == '\0')
+ goto respond;
+ switch (rufn) {
+ case 1: /* FUTLN */
+ PDEBUG(DSIM7, DEBUG_INFO, "service mode: FUTLN = %s\n", number);
+ rc = encode_ebdt(data, number, NULL, NULL, NULL, NULL);
+ if (rc < 0)
+ break;
+ eeprom_write(EEPROM_FUTLN_H + sim->card, data[0]);
+ eeprom_write(EEPROM_FUTLN_M + sim->card, data[1]);
+ eeprom_write(EEPROM_FUTLN_L + sim->card, data[2]);
+ break;
+ case 2: /* security code */
+ PDEBUG(DSIM7, DEBUG_INFO, "service mode: security = %s\n", number);
+ rc = encode_ebdt(data, NULL, number, NULL, NULL, NULL);
+ if (rc < 0)
+ break;
+ eeprom_write(EEPROM_SICH_H + sim->card, data[3]);
+ eeprom_write(EEPROM_SICH_L + sim->card, data[4]);
+ break;
+ case 3: /* card ID */
+ PDEBUG(DSIM7, DEBUG_INFO, "service mode: card = %s\n", number);
+ data[5] = eeprom_read(EEPROM_SONDER_H + sim->card);
+ rc = encode_ebdt(data, NULL, NULL, number, NULL, NULL);
+ if (rc < 0)
+ break;
+ eeprom_write(EEPROM_SONDER_H + sim->card, data[5]);
+ break;
+ case 4: /* special key */
+ PDEBUG(DSIM7, DEBUG_INFO, "service mode: special = %s\n", number);
+ data[5] = eeprom_read(EEPROM_SONDER_H + sim->card);
+ rc = encode_ebdt(data, NULL, NULL, NULL, number, NULL);
+ if (rc < 0)
+ break;
+ eeprom_write(EEPROM_SONDER_H + sim->card, data[5]);
+ eeprom_write(EEPROM_SONDER_L + sim->card, data[6]);
+ break;
+ case 5: /* maintenance key */
+ PDEBUG(DSIM7, DEBUG_INFO, "service mode: maintenance = %s\n", number);
+ rc = encode_ebdt(data, NULL, NULL, NULL, NULL, number);
+ if (rc < 0)
+ break;
+ eeprom_write(EEPROM_WARTUNG_H + sim->card, data[7]);
+ eeprom_write(EEPROM_WARTUNG_L + sim->card, data[8]);
+ break;
+ }
+ /* respond */
+ goto respond;
+ }
+
+ if (rufn >= directory_size() || rufn < 1) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "WT_RUFN entry #%d out of range\n", rufn);
+ return_error(sim);
+ return;
+ }
+
+ save_directory(data[0], data + 1);
+
+ /* respond */
+respond:
+ data = alloc_msg(sim, 0);
+ tx_sdu(sim, 0, data, 0);
+}
+
+/* command: check PIN (enter PIN and unlock) */
+static void chk_pin(sim_sim_t *sim, uint8_t *data, int length)
+{
+ int rc;
+
+ PDEBUG(DSIM7, DEBUG_INFO, " CHK-PIN\n");
+
+ if (length < 4 || length > 8) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "SET-PIN wrong length: %d\n", length);
+ return_error(sim);
+ return;
+ }
+
+ /* validate PIN */
+ rc = validate_pin(sim, data, length);
+ if (rc) {
+ return_pin_not_ok(sim);
+ return;
+ }
+
+ /* respond */
+ data = alloc_msg(sim, 0);
+ tx_sdu(sim, 0, data, 0);
+}
+
+/* command: set PIN */
+static void set_pin(sim_sim_t *sim, uint8_t *data, int length)
+{
+ uint8_t len_old, len_new;
+ uint8_t *pin_old, *pin_new;
+ int i;
+ int rc;
+
+ PDEBUG(DSIM7, DEBUG_INFO, " SET-PIN\n");
+
+ if (length < 1) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "SET-PIN too short\n");
+ return_error(sim);
+ return;
+ }
+
+ len_old = data[0];
+ pin_old = data + 1;
+ len_new = length - len_old - 1;
+ pin_new = data + 1 + len_old;
+ if (len_new < 4 || len_new > 8) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "New PIN wrong length %d!\n", len_new);
+ return_error(sim);
+ return;
+ }
+
+ /* validate PIN */
+ rc = validate_pin(sim, pin_old, length);
+ if (rc) {
+ return_pin_not_ok(sim);
+ return;
+ }
+
+ /* write PIN */
+ sim->pin_len = len_new;
+ write_flags(sim);
+ for (i = 0; i < len_new; i++)
+ eeprom_write(EEPROM_PIN_DATA + i, pin_new[i]);
+
+ /* respond */
+ data = alloc_msg(sim, 0);
+ tx_sdu(sim, 0, data, 0);
+}
+
+/* command: increment metering counter */
+static void eh_gebz(sim_sim_t *sim, uint8_t *data, int length)
+{
+ uint32_t gebz;
+
+ PDEBUG(DSIM7, DEBUG_INFO, " EH-GEBZ\n");
+
+ if (length < 1) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "EH-GEBZ wrong length: %d\n", length);
+ return_error(sim);
+ return;
+ }
+
+ /* increment counter */
+ gebz = eeprom_read(EEPROM_GEBZ_H) << 16;
+ gebz |= eeprom_read(EEPROM_GEBZ_M) << 8;
+ gebz |= eeprom_read(EEPROM_GEBZ_L);
+ gebz += data[0];
+ eeprom_write(EEPROM_GEBZ_H, gebz >> 16);
+ eeprom_write(EEPROM_GEBZ_M, gebz >> 8);
+ eeprom_write(EEPROM_GEBZ_L, gebz);
+
+ /* respond */
+ data = alloc_msg(sim, 0);
+ tx_sdu(sim, 0, data, 0);
+}
+
+/* command: clear metering counter */
+static void cl_gebz(sim_sim_t *sim)
+{
+ uint8_t *data;
+
+ PDEBUG(DSIM7, DEBUG_INFO, " CL-GEBZ\n");
+
+ /* clear counter */
+ eeprom_write(EEPROM_GEBZ_H, 0);
+ eeprom_write(EEPROM_GEBZ_M, 0);
+ eeprom_write(EEPROM_GEBZ_L, 0);
+
+ /* respond */
+ data = alloc_msg(sim, 0);
+ tx_sdu(sim, 0, data, 0);
+}
+
+/* command: read metering counter */
+static void rd_gebz(sim_sim_t *sim)
+{
+ uint8_t *data;
+
+ PDEBUG(DSIM7, DEBUG_INFO, " RD-GEBZ\n");
+
+ /* respond */
+ data = alloc_msg(sim, 3);
+ data[0] = eeprom_read(EEPROM_GEBZ_H);
+ data[1] = eeprom_read(EEPROM_GEBZ_M);
+ data[2] = eeprom_read(EEPROM_GEBZ_L);
+ tx_sdu(sim, 0, data, 3);
+}
+
+/* command: lock metering counter and directory */
+static void sp_gzrv(sim_sim_t *sim)
+{
+ uint8_t *data;
+
+ PDEBUG(DSIM7, DEBUG_INFO, " SP-GZRV\n");
+
+ sim->gebz_locked = 1;
+ write_flags(sim);
+
+ /* respond */
+ data = alloc_msg(sim, 0);
+ tx_sdu(sim, 0, data, 0);
+}
+
+/* command: unlock metering counter and directory */
+static void fr_gzrv(sim_sim_t *sim)
+{
+ uint8_t *data;
+
+ PDEBUG(DSIM7, DEBUG_INFO, " FR-GZRV\n");
+
+ sim->gebz_locked = 0;
+ write_flags(sim);
+
+ /* respond */
+ data = alloc_msg(sim, 0);
+ tx_sdu(sim, 0, data, 0);
+}
+
+/* command: authenticate */
+static void aut_1(sim_sim_t *sim)
+{
+ uint8_t *data;
+ int i;
+
+ PDEBUG(DSIM7, DEBUG_INFO, " RD-EBDT\n");
+
+ /* respond */
+ data = alloc_msg(sim, 1);
+ for (i = 0; i < 8; i++)
+ data[i] = eeprom_read(EEPROM_AUTH_DATA + i);
+ tx_sdu(sim, 0, data, 8);
+}
+
+/* command: UNKNOWN */
+static void rd_f4(sim_sim_t *sim)
+{
+ uint8_t *data;
+
+ PDEBUG(DSIM7, DEBUG_INFO, " RD-F4\n");
+
+ /* respond */
+ data = alloc_msg(sim, 2);
+ data[0] = 0x00;
+ data[1] = 0x13;
+ tx_sdu(sim, 0, data, 2);
+}
+
+/* command: UNKNOWN */
+static void rd_f5(sim_sim_t *sim)
+{
+ uint8_t *data;
+
+ PDEBUG(DSIM7, DEBUG_INFO, " RD-F5\n");
+
+ /* respond */
+ data = alloc_msg(sim, 0);
+ tx_sdu(sim, 0, data, 0);
+}
+
+/* command: UNKNOWN */
+static void rd_04(sim_sim_t *sim)
+{
+ uint8_t *data;
+
+ PDEBUG(DSIM7, DEBUG_INFO, " RD-04\n");
+
+ /* respond */
+ data = alloc_msg(sim, 25);
+ data[0] = 0x63;
+ memset(data + 1, 0x00, 24);
+ tx_sdu(sim, 0, data, 25);
+}
+
+/* parse layer 7 header */
+static void rx_sdu(sim_sim_t *sim, uint8_t *data, int length)
+{
+ uint8_t cla, ins, dlng;
+
+ if (length < 3) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "SDU too short\n");
+ return;
+ }
+
+ /* skip all responses, because we don't send commands */
+ if (*data & CCRC_IDENT) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "Skipping SDU with response\n");
+ return;
+ }
+
+ /* read application layer header */
+ cla = *data++ & 0x7f;
+ ins = *data++;
+ dlng = *data++;
+ length -= 3;
+
+ /* check length */
+ if (dlng != length) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "Skipping SDU with invalid length\n");
+ return;
+ }
+
+ /* select command */
+ switch (cla) {
+ case CLA_CNTR:
+ switch (ins) {
+ case SL_APPL: sl_appl(sim, data, length); return;
+ case CL_APPL: cl_appl(sim); return;
+ case SH_APPL: sh_appl(sim); return;
+ }
+ break;
+ case CLA_STAT:
+ switch (ins) {
+ case CHK_KON: chk_kon(sim); return;
+ }
+ break;
+ case CLA_WRTE:
+ switch (ins) {
+ case WT_RUFN: wt_rufn(sim, data, length); return;
+ }
+ break;
+ case CLA_READ:
+ switch (ins) {
+ case RD_EBDT: rd_ebdt(sim); return;
+ case RD_RUFN: rd_rufn(sim, data, length); return;
+ case RD_GEBZ: rd_gebz(sim); return;
+ case 0xf4: rd_f4(sim); return;
+ case 0xf5: rd_f5(sim); return;
+ case 0x04: rd_04(sim); return;
+ }
+ break;
+ case CLA_EXEC:
+ switch (ins) {
+ case CHK_PIN: chk_pin(sim, data, length); return;
+ case SET_PIN: set_pin(sim, data, length); return;
+ }
+ if (sim->app == APP_NETZ_C) switch (ins) {
+ case EH_GEBZ: eh_gebz(sim, data, length); return;
+ case CL_GEBZ: cl_gebz(sim); return;
+ }
+ if (sim->app == APP_RUFN_GEBZ) switch (ins) {
+ case SP_GZRV: sp_gzrv(sim); return;
+ case FR_GZRV: fr_gzrv(sim); return;
+ }
+ break;
+ case CLA_AUTO:
+ switch (ins) {
+ case AUT_1: aut_1(sim); return;
+ }
+ break;
+ }
+
+ /* unsupported message */
+ PDEBUG(DSIM7, DEBUG_NOTICE, "CLA 0x%02x INS 0x%02x uknown\n", cla, ins);
+ data = alloc_msg(sim, 0);
+ tx_sdu(sim, CCRC_ERROR, data, 0);
+}
+
+/* create layer 7 message for ICL layer */
+static void tx_sdu(sim_sim_t *sim, uint8_t ccrc, uint8_t *data, int length)
+{
+ /* header */
+ data = push_msg(data, length, 3);
+ data[0] = CCRC_IDENT | ccrc | CCRC_APRC_VALID;
+ data[1] = 0;
+ if (sim->pin_try == 0)
+ data[0] |= CCRC_AFBZ_NULL;
+ data[1] = get_aprc(sim);
+ data[2] = length;
+ length += 3;
+
+ /* forward to ICL layer */
+ tx_pdu(sim, data, length);
+}
+
+/* ICL layer */
+
+/* parse ICL header */
+static void rx_pdu(sim_sim_t *sim, uint8_t *data, int length)
+{
+ uint8_t ext = 1;
+
+ if (length < 1) {
+too_short:
+ PDEBUG(DSIMI, DEBUG_NOTICE, "PDU too short\n");
+ return;
+ }
+
+ /* read ICB1 */
+ sim->icl_online = (*data & ICB1_ONLINE) != 0;
+ sim->icl_master = (*data & ICB1_MASTER) != 0;
+ sim->icl_error = (*data & ICB1_ERROR) != 0;
+ sim->icl_chaining = (*data & ICB1_CHAINING) != 0;
+
+ /* skip all ICBx (should only one exist) */
+ while (ext) {
+ if (length < 1)
+ goto too_short;
+ ext = (*data++ & ICB_EXT) != 0;
+ length--;
+ }
+
+ rx_sdu(sim, data, length);
+}
+
+/* create ICL layer message for layer 2 */
+static void tx_pdu(sim_sim_t *sim, uint8_t *data, int length)
+{
+ /* header */
+ data = push_msg(data, length, 1);
+ data[0] = 0;
+ if (sim->icl_online)
+ data[0] |= ICB1_ONLINE;
+ if (!sim->icl_master)
+ data[0] |= ICB1_MASTER;
+ if (sim->icl_error)
+ data[0] |= ICB1_ERROR | ICB1_CONFIRM;
+ if (sim->icl_chaining)
+ data[0] |= ICB1_CHAINING | ICB1_CONFIRM;
+ length++;
+
+ tx_block(sim, L2_I, data, length);
+}
+
+/* Layer 2 */
+
+/* process received L2 message */
+static void rx_block(sim_sim_t *sim)
+{
+ uint8_t ns, nr;
+ uint8_t *data;
+
+ /* NOTE: This procedure is simplified, it does not comply with the specs. */
+
+ PDEBUG(DSIM2, DEBUG_INFO, "RX message\n");
+ sim->addr_src = sim->block_address >> 4;
+ sim->addr_dst = sim->block_address & 0xf;
+ if (sim->block_checksum != 0) {
+ PDEBUG(DSIM2, DEBUG_NOTICE, "Checksum error!\n");
+ goto reject;
+ }
+ if ((sim->block_control & 0x11) == 0x00) {
+ ns = (sim->block_control >> 1) & 7;
+ nr = sim->block_control >> 5;
+ PDEBUG(DSIM2, DEBUG_INFO, " control I: N(S)=%d N(R)=%d\n", ns, nr);
+ if (ns == sim->vr && nr == sim->vs) {
+ /* receive data */
+ sim->vr = (sim->vr + 1) & 0x7;
+ rx_pdu(sim, sim->block_rx_data, sim->block_rx_length);
+ return;
+ } else {
+ PDEBUG(DSIM2, DEBUG_NOTICE, "Seqeuence error!\n");
+reject:
+ /* reject (or send resync after 3 times) */
+ data = alloc_msg(sim, 0);
+ if (1) { // if (sim->reject_count < 3) {
+ tx_block(sim, L2_REJ, data, 0);
+ sim->reject_count++;
+ } else {
+ tx_block(sim, L2_RES, data, 0);
+ }
+ return;
+ }
+ return;
+ }
+ if ((sim->block_control & 0x1f) == 0x09) {
+ nr = sim->block_control >> 5;
+ PDEBUG(DSIM2, DEBUG_INFO, " control REJ: N(R)=%d\n", nr);
+ /* repeat last message */
+ if (sim->block_tx_length) {
+ tx_block(sim, L2_I, sim->block_tx_data, sim->block_tx_length);
+ return;
+ }
+ /* no block sent yet, sending resync */
+ data = alloc_msg(sim, 0);
+ tx_block(sim, L2_RES, data, 0);
+ return;
+ }
+ if (sim->block_control == 0xef) {
+ PDEBUG(DSIM2, DEBUG_INFO, " control RES\n");
+ sim->vr = sim->vs = 0;
+ sim->reject_count = 0;
+ if (sim->resync_sent == 0) {
+ /* resync */
+ data = alloc_msg(sim, 0);
+ tx_block(sim, L2_RES, data, 0);
+ return;
+ }
+ return;
+ }
+}
+
+/* receive data from layer 1 and create layer 2 message */
+static int rx_char(sim_sim_t *sim, uint8_t c)
+{
+ sim->block_checksum ^= c;
+
+ switch (sim->block_state) {
+ case BLOCK_STATE_ADDRESS:
+ sim->block_address = c;
+ sim->block_state = BLOCK_STATE_CONTROL;
+ sim->block_checksum = c;
+ return 0;
+ case BLOCK_STATE_CONTROL:
+ sim->block_control = c;
+ sim->block_state = BLOCK_STATE_LENGTH;
+ return 0;
+ case BLOCK_STATE_LENGTH:
+ if (c > sizeof(sim->block_rx_data)) {
+ c = sizeof(sim->block_rx_data);
+ PDEBUG(DSIM1, DEBUG_NOTICE, "RX buffer overflow: length=%d > buffer size (%d)\n", c, (int)sizeof(sim->block_rx_data));
+ }
+ sim->block_rx_length = c;
+ sim->block_count = 0;
+ sim->block_state = BLOCK_STATE_DATA;
+ return 0;
+ case BLOCK_STATE_DATA:
+ if (sim->block_count < sim->block_rx_length) {
+ sim->block_rx_data[sim->block_count++] = c;
+ return 0;
+ }
+ sim->l1_state = L1_STATE_IDLE;
+ rx_block(sim);
+ }
+
+ return -1;
+}
+
+/* create layer 2 message for layer 1 */
+static void tx_block(sim_sim_t *sim, enum l2_cmd cmd, uint8_t __attribute__((unused)) *data, int length)
+{
+ PDEBUG(DSIM2, DEBUG_INFO, "TX resonse\n");
+
+ /* header */
+ sim->block_address = (sim->addr_dst << 4) | sim->addr_src;
+ switch (cmd) {
+ case L2_I:
+ PDEBUG(DSIM2, DEBUG_INFO, " control I: N(S)=%d N(R)=%d\n", sim->vs, sim->vr);
+ sim->block_control = (sim->vr << 5) | (sim->vs << 1);
+ sim->vs = (sim->vs + 1) & 0x7;
+ sim->resync_sent = 0;
+ break;
+ case L2_REJ:
+ PDEBUG(DSIM2, DEBUG_INFO, " control REJ: N(R)=%d\n", sim->vr);
+ sim->block_control = (sim->vr << 5) | 0x09;
+ sim->resync_sent = 0;
+ break;
+ case L2_RES:
+ PDEBUG(DSIM2, DEBUG_INFO, " control RES\n");
+ sim->block_control = 0xef;
+ sim->resync_sent = 1;
+ break;
+ }
+ sim->block_tx_length = length;
+
+ sim->l1_state = L1_STATE_SEND;
+ sim->block_state = BLOCK_STATE_ADDRESS;
+}
+
+/* transmit character of current message to layer 1 */
+static uint8_t tx_char(sim_sim_t *sim)
+{
+ uint8_t c = -1;
+
+ switch (sim->block_state) {
+ case BLOCK_STATE_ADDRESS:
+ c = sim->block_address;
+ sim->block_state = BLOCK_STATE_CONTROL;
+ sim->block_checksum = 0;
+ break;
+ case BLOCK_STATE_CONTROL:
+ c = sim->block_control;
+ sim->block_state = BLOCK_STATE_LENGTH;
+ break;
+ case BLOCK_STATE_LENGTH:
+ c = sim->block_tx_length;
+ sim->block_count = 0;
+ sim->block_state = BLOCK_STATE_DATA;
+ break;
+ case BLOCK_STATE_DATA:
+ if (sim->block_count < sim->block_tx_length) {
+ c = sim->block_tx_data[sim->block_count++];
+ break;
+ }
+ c = sim->block_checksum;
+ sim->l1_state = L1_STATE_IDLE;
+ break;
+ }
+
+ sim->block_checksum ^= c;
+
+ return c;
+}
+
+/* ATR */
+
+static uint8_t atr[] = {
+ 0x3b, 0x88, /* TS, T0 */
+ 0x8e,
+ 0xfe,
+ 0x53, 0x2a, 0x03, 0x1e,
+ 0x04,
+ 0x92, 0x80, 0x00, 0x41, 0x32, 0x36, 0x01, 0x11,
+ 0xe4, /* TCK */
+};
+
+static uint8_t tx_atr(sim_sim_t *sim)
+{
+ uint8_t c;
+
+ c = atr[sim->atr_count++];
+ if (sim->atr_count == sizeof(atr))
+ sim->l1_state = L1_STATE_IDLE;
+
+ return c;
+}
+
+/* Layer 1 */
+
+int sim_init_eeprom(void)
+{
+ uint8_t ebdt_data[9];
+ int i, rc;
+
+ /* init EEPROM with all bits '1' */
+ for (i = 0; i < (int)eeprom_length(); i++)
+ eeprom_write(i, 0xff);
+
+ /* set default values in eeprom */
+ rc = encode_ebdt(ebdt_data, FUTLN_DEFAULT, SICHERUNG_DEFAULT, KARTEN_DEFAULT, SONDER_DEFAULT, WARTUNG_DEFAULT);
+ if (rc < 0)
+ return rc;
+ for (i = 0; i < MAX_CARDS; i++) {
+ eeprom_write(EEPROM_FUTLN_H + i, ebdt_data[0]);
+ eeprom_write(EEPROM_FUTLN_M + i, ebdt_data[1]);
+ eeprom_write(EEPROM_FUTLN_L + i, ebdt_data[2] + i);
+ eeprom_write(EEPROM_SICH_H + i, ebdt_data[3]);
+ eeprom_write(EEPROM_SICH_L + i, ebdt_data[4]);
+ eeprom_write(EEPROM_SONDER_H + i, ebdt_data[5]);
+ eeprom_write(EEPROM_SONDER_L + i, ebdt_data[6]);
+ eeprom_write(EEPROM_WARTUNG_H + i, ebdt_data[7]);
+ eeprom_write(EEPROM_WARTUNG_L + i, ebdt_data[8]);
+ }
+ eeprom_write(EEPROM_GEBZ_H, 0);
+ eeprom_write(EEPROM_GEBZ_M, 0);
+ eeprom_write(EEPROM_GEBZ_L, 0);
+ eeprom_write(EEPROM_FLAGS, (strlen(PIN_DEFAULT) << EEPROM_FLAG_PIN_LEN) | (MAX_PIN_TRY << EEPROM_FLAG_PIN_TRY));
+ for (i = 0; i < (int)strlen(PIN_DEFAULT); i++)
+ eeprom_write(EEPROM_PIN_DATA + i, PIN_DEFAULT[i]);
+
+ eeprom_write(EEPROM_MAGIC + 0, 'C');
+ eeprom_write(EEPROM_MAGIC + 1, '0' + EEPROM_VERSION);
+
+ return 0;
+}
+
+void sim_reset(sim_sim_t *sim, int reset)
+{
+ int i;
+ char pin[8];
+
+ PDEBUG(DSIM1, DEBUG_INFO, "Reset singnal %s\n", (reset) ? "on (low)" : "off (high)");
+ memset(sim, 0, sizeof(*sim));
+
+ if (reset)
+ return;
+
+ /* read flags from EEPROM data */
+ read_flags(sim);
+
+ /* check PIN and set flags */
+ for (i = 0; i < sim->pin_len; i++)
+ pin[i] = eeprom_read(EEPROM_PIN_DATA + i);
+
+ sim->pin_required = 1;
+ /* 'system' PIN = 0000, 0001, 0002, ... */
+ if (sim->pin_len == 4 && pin[0] == '0' && pin[1] == '0' && pin[2] == '0' && pin[3] >= '0' && pin[3] <= '0' + MAX_CARDS) {
+ sim->pin_required = 0;
+ if (pin[3] > '0')
+ sim->card = pin[3] - '1';
+ PDEBUG(DSIM1, DEBUG_INFO, "Card has disabled PIN (system PIN '000%c') Selecting card #%d.\n", pin[3], sim->card + 1);
+ }
+
+ PDEBUG(DSIM1, DEBUG_INFO, "Sending ATR\n");
+ sim->l1_state = L1_STATE_ATR;
+}
+
+int sim_rx(sim_sim_t *sim, uint8_t c)
+{
+ int rc = -1;
+
+ PDEBUG(DSIM1, DEBUG_DEBUG, "Serial RX '0x%02x'\n", c);
+
+ switch (sim->l1_state) {
+ case L1_STATE_IDLE:
+ sim->l1_state = L1_STATE_RECEIVE;
+ sim->block_state = BLOCK_STATE_ADDRESS;
+ /* fall through */
+ case L1_STATE_RECEIVE:
+ rc = rx_char(sim, c);
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+int sim_tx(sim_sim_t *sim)
+{
+ int c = -1;
+
+ switch (sim->l1_state) {
+ case L1_STATE_ATR:
+ c = tx_atr(sim);
+ break;
+ case L1_STATE_SEND:
+ c = tx_char(sim);
+ break;
+ default:
+ break;
+ }
+
+ if (c >= 0)
+ PDEBUG(DSIM1, DEBUG_DEBUG, "Serial TX '0x%02x'\n", c);
+
+ return c;
+}
+
+void sim_timeout(sim_sim_t *sim)
+{
+ switch (sim->l1_state) {
+ case L1_STATE_ATR:
+ PDEBUG(DSIM1, DEBUG_NOTICE, "Timeout while transmitting ATR!\n");
+ sim->l1_state = L1_STATE_RESET;
+ break;
+ case L1_STATE_RECEIVE:
+ PDEBUG(DSIM1, DEBUG_NOTICE, "Timeout while receiving message!\n");
+ sim->block_state = BLOCK_STATE_ADDRESS;
+ break;
+ case L1_STATE_SEND:
+ PDEBUG(DSIM1, DEBUG_NOTICE, "Timeout while sending message!\n");
+ sim->l1_state = L1_STATE_IDLE;
+ break;
+ default:
+ break;
+ }
+}
diff --git a/src/sim/sim.h b/src/sim/sim.h
new file mode 100644
index 0000000..855cb0b
--- /dev/null
+++ b/src/sim/sim.h
@@ -0,0 +1,148 @@
+
+#define FUTLN_DEFAULT "2222001"
+#define SICHERUNG_DEFAULT "3103"
+#define KARTEN_DEFAULT "3"
+#define SONDER_DEFAULT "0"
+#define WARTUNG_DEFAULT "65535"
+#define PIN_DEFAULT "0000"
+#define AUTH_DEFAULT "0xffffffffffffffff"
+
+enum l1_state {
+ L1_STATE_RESET = 0, /* reset is held */
+ L1_STATE_ATR, /* answer to reset is sent */
+ L1_STATE_IDLE, /* waiting for message or reset */
+ L1_STATE_SEND, /* sending reply */
+ L1_STATE_RECEIVE, /* receiving message */
+};
+
+enum block_state {
+ BLOCK_STATE_ADDRESS = 0,
+ BLOCK_STATE_CONTROL,
+ BLOCK_STATE_LENGTH,
+ BLOCK_STATE_DATA,
+};
+
+#define MAX_PIN_TRY 3
+#define MAX_CARDS 8 /* must also be defined at eeprom.h */
+
+typedef struct sim_sim {
+ int card;
+ enum l1_state l1_state;
+
+ /* ATR states */
+ int atr_count;
+
+ /* layer 2 states */
+ enum block_state block_state;
+ uint8_t block_address;
+ uint8_t block_control;
+ uint8_t block_checksum;
+ uint8_t block_count;
+ uint8_t block_rx_data[64];
+ uint8_t block_rx_length;
+ uint8_t block_tx_data[64];
+ uint8_t block_tx_length;
+ uint8_t vs, vr;
+ int reject_count;
+ int resync_sent;
+
+ /* ICL layer states */
+ int icl_online;
+ int icl_master;
+ int icl_chaining;
+ int icl_error;
+
+ /* layer 7 states */
+ int addr_src;
+ int addr_dst;
+ int sh_appl_count; /* counts applications for SH_APPL */
+
+ /* CNETZ states */
+ int pin_required; /* pin required an not yet validated */
+ int program_mode; /* program mode active (special PIN entered) */
+ int pin_len; /* length of pin (4 .. 8) */
+ int pin_try; /* number of tries left (0 == card locked) */
+ int app; /* currently selected APP number */
+ int app_locked; /* application locked */
+ int gebz_locked; /* metering counter and phonebook locked */
+ int gebz_full; /* metering counter full (does this really happen?) */
+} sim_sim_t;
+
+/* layer 2 */
+enum l2_cmd {
+ L2_I,
+ L2_REJ,
+ L2_RES,
+};
+
+/* ICL */
+#define ICB1_ONLINE 0x01
+#define ICB1_CONFIRM 0x02
+#define ICB1_MASTER 0x04
+#define ICB1_WT_EXT 0x08
+#define ICB1_ABORT 0x10
+#define ICB1_ERROR 0x20
+#define ICB1_CHAINING 0x40
+#define ICB2_BUFFER 0x0f
+#define ICB2_DYNAMIC 0x10
+#define ICB2_ISO_L2 0x20
+#define ICB2_PRIVATE 0x40
+#define ICB_EXT 0x80
+
+/* command */
+#define CLA_CNTR 0x02
+#define SL_APPL 0xf1
+#define CL_APPL 0xf2
+#define SH_APPL 0xf3
+
+#define CLA_STAT 0x03
+#define CHK_KON 0xf1
+
+#define CLA_WRTE 0x04
+#define WT_RUFN 0x01
+
+#define CLA_READ 0x05
+#define RD_EBDT 0x01
+#define RD_RUFN 0x02
+#define RD_GEBZ 0x03
+
+#define CLA_EXEC 0x06
+#define CHK_PIN 0xf1
+#define SET_PIN 0xf2
+#define EH_GEBZ 0x01
+#define CL_GEBZ 0x02
+#define SP_GZRV 0x01
+#define FR_GZRV 0x02
+
+#define CLA_AUTO 0x07
+#define AUT_1 0x01
+
+/* response */
+#define CCRC_PIN_NOK 0x01
+#define CCRC_AFBZ_NULL 0x02
+#define CCRC_APRC_VALID 0x04
+#define CCRC_ERROR 0x40
+#define CCRC_IDENT 0x80
+
+#define APRC_PIN_REQ 0x02
+#define APRC_APP_LOCKED 0x04
+#define APRC_GEBZ_LOCK 0x10
+#define APRC_GEBZ_FULL 0x20
+
+/* apps */
+#define APP_NETZ_C 3
+#define APP_RUFN_GEBZ 4
+
+int encode_ebdt(uint8_t *data, const char *futln, const char *sicherung, const char *karten, const char *sonder, const char *wartung);
+void decode_ebdt(uint8_t *data, char *futln, char *sicherung, char *karten, char *sonder, char *wartung);
+int directory_size(void);
+int save_directory(int location, uint8_t *data);
+void load_directory(int location, uint8_t *data);
+int encode_directory(uint8_t *data, const char *number, const char *name);
+void decode_directory(uint8_t *data, char *number, char *name);
+
+int sim_init_eeprom(void);
+void sim_reset(sim_sim_t *sim, int reset);
+int sim_rx(sim_sim_t *sim, uint8_t c);
+int sim_tx(sim_sim_t *sim);
+void sim_timeout(sim_sim_t *sim);
diff --git a/src/sim/sim.ino b/src/sim/sim.ino
new file mode 100644
index 0000000..fb12269
--- /dev/null
+++ b/src/sim/sim.ino
@@ -0,0 +1,287 @@
+/* SIM card for ATMEL
+ *
+ * (C) 2020 by Andreas Eversberg <jolly@eversberg.eu>
+ * All Rights Reserved
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/>.
+ */
+
+extern "C"
+{
+ #include "sim.h"
+ #include "eeprom.h"
+}
+
+/* settings for ATTINY85 */
+#if defined(__AVR_ATtiny85__)
+#define SERIAL_DATA 4
+#define SERIAL_DELAY 124
+#define SERIAL_TIMEOUT 1200 /* > two bytes */
+#else
+/* settings for Arduino UNO with 16 MHz */
+#define STATUS_LED LED_BUILTIN
+#define RESET_PIN 6
+#define SERIAL_DATA 7
+#define SERIAL_DELAY 410
+#define SERIAL_TIMEOUT 2500 /* > two bytes */
+#endif
+/* to set fused for ATTINY85:
+ * avrdude -c usbasp-clone -p t85 -U lfuse:w:0xc0:m -U hfuse:w:0xdf:m -U efuse:w:0xff:m
+ */
+
+/* timing test TX (010101010011) */
+//#define TEST_TX
+/* timing test RX (000000000001) */
+//#define TEST_RX
+/* timing test timeout (pause + 000000000001) */
+//#define TEST_TO
+
+sim_sim_t sim;
+
+#include <avr/eeprom.h>
+#include <util/delay.h>
+
+uint8_t eeprom_read(enum eeprom_locations loc)
+{
+ return eeprom_read_byte((uint8_t *)loc);
+}
+
+void eeprom_write(enum eeprom_locations loc, uint8_t value)
+{
+ eeprom_write_byte((uint8_t *)loc, value);
+}
+
+size_t eeprom_length(void)
+{
+ return 512;
+}
+
+#ifdef RESET_PIN
+volatile uint8_t *reset_in;
+uint8_t reset_bit;
+
+/* init reset pin */
+void reset_init(uint8_t pin)
+{
+ uint8_t port;
+ volatile uint8_t *mode, *out;
+
+ reset_bit = digitalPinToBitMask(pin);
+ port = digitalPinToPort(pin);
+
+ mode = portModeRegister(port);
+ out = portOutputRegister(port);
+ reset_in = portInputRegister(port);
+
+ *mode &= ~reset_bit; /* intput */
+ *out |= reset_bit; /* pullup */
+}
+#endif
+
+volatile uint8_t *serial_mode, *serial_out, *serial_in;
+uint8_t serial_bit;
+uint16_t serial_delay;
+
+/* init serial pin */
+void serial_init(uint8_t pin, uint16_t delay)
+{
+ uint8_t port;
+
+ serial_delay = delay;
+ serial_bit = digitalPinToBitMask(pin);
+ port = digitalPinToPort(pin);
+
+ serial_mode = portModeRegister(port);
+ serial_out = portOutputRegister(port);
+ serial_in = portInputRegister(port);
+
+ *serial_mode &= ~serial_bit; /* input */
+ *serial_out |= serial_bit; /* pullup */
+}
+
+/* wait some time so the stop bits haven been elapsed before transmitting a block */
+void serial_start_tx(void)
+{
+ /* wait some time, so previous stop bits have been elapsed */
+ _delay_loop_2(serial_delay * 3); /* 2..3 bits of time */
+}
+
+/* transmit a byte */
+void serial_tx(uint8_t b)
+{
+ uint8_t i, c = 0;
+
+ /* start bit */
+ *serial_mode |= serial_bit; /* output */
+ *serial_out &= ~serial_bit; /* low */
+ _delay_loop_2(serial_delay);
+ /* 8 data bits */
+ for (i = 8; i > 0; --i) {
+ if (b & 1)
+ *serial_out |= serial_bit; /* high */
+ else
+ *serial_out &= ~serial_bit; /* low */
+ _delay_loop_2(serial_delay);
+ c ^= b;
+ b>>= 1;
+ }
+ /* even parity */
+ if (c & 1)
+ *serial_out |= serial_bit; /* high */
+ else
+ *serial_out &= ~serial_bit; /* low */
+ _delay_loop_2(serial_delay);
+ /* 2 stop bits */
+ *serial_out |= serial_bit; /* high */
+ _delay_loop_2(serial_delay);
+ _delay_loop_2(serial_delay);
+ *serial_mode &= ~serial_bit; /* input */
+}
+
+/* receive a byte */
+uint8_t serial_rx(void)
+{
+ uint8_t i, b = 0;
+
+ /* center read */
+ _delay_loop_2(serial_delay >> 1);
+ /* 8 data bits */
+ for (i = 8; i > 0; --i) {
+ _delay_loop_2(serial_delay);
+ b >>= 1;
+ if ((*serial_in & serial_bit))
+ b |= 0x80;
+ }
+ /* parity */
+ _delay_loop_2(serial_delay);
+ /* move into (first) stop bit */
+ _delay_loop_2(serial_delay);
+
+ return b;
+}
+
+void setup() {
+ uint8_t byte, ver;
+
+#ifdef STATUS_LED
+ pinMode(STATUS_LED, OUTPUT);
+#endif
+
+ /* intial eeprom init */
+ byte = eeprom_read(EEPROM_MAGIC + 0);
+ ver = eeprom_read(EEPROM_MAGIC + 1);
+ if (byte != 'C' || ver != '0' + EEPROM_VERSION)
+ sim_init_eeprom();
+
+#ifdef RESET_PIN
+ reset_init(RESET_PIN);
+#endif
+ serial_init(SERIAL_DATA, SERIAL_DELAY);
+#ifdef TEST_TX
+ while (true)
+ serial_tx(0x55);
+#endif
+#ifdef TEST_RX
+ *serial_mode |= serial_bit; /* output */
+ while (true) {
+ /* show low for start bit up to end of first stop bit */
+ *serial_out &= ~serial_bit; /* low */
+ serial_rx();
+ _delay_loop_2(serial_delay >> 1);
+ *serial_out |= serial_bit; /* high */
+ _delay_loop_2(serial_delay);
+ }
+#endif
+#ifdef TEST_TO
+ uint16_t to;
+ int rx;
+ rx_again:
+ rx = 1;
+ /* wait until start bit is received or timeout */
+ for (to = 0; to <= SERIAL_TIMEOUT;) {
+ if (!(*serial_in & serial_bit)) {
+ serial_tx(0x33);
+ goto rx_again;
+ }
+#ifdef RESET_PIN
+ if (!(*reset_in & reset_bit)) {
+ serial_tx(0xf0);
+ goto rx_again;
+ }
+#endif
+ if (rx)
+ to++;
+ }
+ serial_tx(0x55);
+ goto rx_again;
+#endif
+}
+
+void loop() {
+#if !defined(TEST_TX) && !defined(TEST_RX) && !defined (TEST_TO)
+ uint16_t to;
+ int c, rx;
+
+reset_again:
+#ifdef RESET_PIN
+ /* wait until reset is released */
+ while(!(*reset_in & reset_bit));
+#endif
+ sim_reset(&sim, 0);
+
+tx_again:
+#ifdef STATUS_LED
+ digitalWrite(STATUS_LED, LOW);
+#endif
+ /* send buffer until no more data to be transmitted */
+ serial_start_tx();
+ while ((c = sim_tx(&sim)) >= 0) {
+#ifdef RESET_PIN
+ /* perform reset, when low */
+ if (!(*reset_in & reset_bit))
+ goto reset_again;
+#endif
+ /* perform transmission of a byte */
+ serial_tx(c);
+ }
+ /* wait until start bit is received or timeout */
+ rx = 0;
+ for (to = 0; to <= SERIAL_TIMEOUT;) {
+ /* perform RX, when low (start bit) */
+ if (!(*serial_in & serial_bit)) {
+ c = serial_rx();
+ /* if block was completly received, go to tx_again */
+ if (sim_rx(&sim, c) < 0)
+ goto tx_again;
+ /* start counting timeout condition */
+ rx = 1;
+ to = 0;
+#ifdef STATUS_LED
+ digitalWrite(STATUS_LED, HIGH);
+#endif
+ }
+#ifdef RESET_PIN
+ /* perform reset, when low */
+ if (!(*reset_in & reset_bit))
+ goto reset_again;
+#endif
+ /* only if we have an ongoing reception, we count for the timeout condition */
+ if (rx)
+ to++;
+ }
+ /* perform timeout */
+ sim_timeout(&sim);
+ goto tx_again;
+#endif
+}
diff --git a/src/sim/sniffer.c b/src/sim/sniffer.c
new file mode 100644
index 0000000..56bc4a6
--- /dev/null
+++ b/src/sim/sniffer.c
@@ -0,0 +1,798 @@
+/* SIM card sniffer
+ *
+ * (C) 2020 by Andreas Eversberg <jolly@eversberg.eu>
+ * All Rights Reserved
+ *
+ * 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/>.
+ */
+
+#ifndef ARDUINO
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include "../libdebug/debug.h"
+#include "sim.h"
+#include "sniffer.h"
+
+/* Layer 7 */
+
+static void rx_icl_sdu(uint8_t *data, int length)
+{
+ uint8_t I, cla_ccrc, ins_aprc;
+ uint16_t dlng;
+ int i;
+
+ if (length < 3) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, "Message too short\n");
+ return;
+ }
+
+ I = *data >> 7;
+ cla_ccrc = (*data++ & 0x7f);
+ ins_aprc = *data++;
+ dlng = *data++;
+ length -= 3;
+
+ PDEBUG(DSIM7, DEBUG_INFO, "Layer 7:\n");
+ if (I == 0) {
+ PDEBUG(DSIM7, DEBUG_INFO, " I = Command\n");
+ PDEBUG(DSIM7, DEBUG_INFO, " CLA = 0x%02x\n", cla_ccrc);
+ switch (cla_ccrc) {
+ case CLA_CNTR:
+ PDEBUG(DSIM7, DEBUG_INFO, " -> CNTR (Control Class)\n");
+ break;
+ case CLA_STAT:
+ PDEBUG(DSIM7, DEBUG_INFO, " -> STAT (Status Class)\n");
+ break;
+ case CLA_WRTE:
+ PDEBUG(DSIM7, DEBUG_INFO, " -> WRTE (Write Class)\n");
+ break;
+ case CLA_READ:
+ PDEBUG(DSIM7, DEBUG_INFO, " -> READ (Read Class)\n");
+ break;
+ case CLA_EXEC:
+ PDEBUG(DSIM7, DEBUG_INFO, " -> EXEC (Execute Class)\n");
+ break;
+ case CLA_AUTO:
+ PDEBUG(DSIM7, DEBUG_INFO, " -> AUTO (Authentication Class)\n");
+ break;
+ default:
+ PDEBUG(DSIM7, DEBUG_INFO, " -> unknown class\n");
+ break;
+ }
+ PDEBUG(DSIM7, DEBUG_INFO, " INS = 0x%02x\n", ins_aprc);
+ switch (cla_ccrc) {
+ case CLA_CNTR:
+ switch (ins_aprc) {
+ case SL_APPL:
+ PDEBUG(DSIM7, DEBUG_INFO, " -> SL-APPL (Select Application)\n");
+ break;
+ case CL_APPL:
+ PDEBUG(DSIM7, DEBUG_INFO, " -> CL-APPL (Close Application)\n");
+ break;
+ case SH_APPL:
+ PDEBUG(DSIM7, DEBUG_INFO, " -> SH-APPL (Show Application)\n");
+ break;
+ }
+ break;
+ case CLA_STAT:
+ switch (ins_aprc) {
+ case CHK_KON:
+ PDEBUG(DSIM7, DEBUG_INFO, " -> CHK-KCON (Consistency Check)\n");
+ break;
+ }
+ break;
+ case CLA_WRTE:
+ switch (ins_aprc) {
+ case WT_RUFN:
+ PDEBUG(DSIM7, DEBUG_INFO, " -> WR-RUFN (Write Rufnummernsatz)\n");
+ break;
+ }
+ break;
+ case CLA_READ:
+ switch (ins_aprc) {
+ case RD_EBDT:
+ PDEBUG(DSIM7, DEBUG_INFO, " -> RD-EBDT (Read Einbuchdaten)\n");
+ break;
+ case RD_RUFN:
+ PDEBUG(DSIM7, DEBUG_INFO, " -> RD-RUFN (Read Rufnummernsatz)\n");
+ break;
+ case RD_GEBZ:
+ PDEBUG(DSIM7, DEBUG_INFO, " -> RD-GEBZ (Read Gebuehrenzaehler)\n");
+ break;
+ }
+ break;
+ case CLA_EXEC:
+ switch (ins_aprc) {
+ case CHK_PIN:
+ PDEBUG(DSIM7, DEBUG_INFO, " -> CHK-PIN (Check PIN)\n");
+ break;
+ case SET_PIN:
+ PDEBUG(DSIM7, DEBUG_INFO, " -> SET-PIN (Set PIN)\n");
+ break;
+ case EH_GEBZ:
+ PDEBUG(DSIM7, DEBUG_INFO, " -> EH-GEBZ (Increment Gebuehrenzaehler)\n");
+ break;
+ case CL_GEBZ:
+ PDEBUG(DSIM7, DEBUG_INFO, " -> CL-GEBZ (Clear Gebuehrenzaehler)\n");
+ break;
+ }
+ break;
+ case CLA_AUTO:
+ switch (ins_aprc) {
+ case AUT_1:
+ PDEBUG(DSIM7, DEBUG_INFO, " -> AUTO-1 (Autorization)\n");
+ break;
+ }
+ break;
+ }
+ } else {
+ PDEBUG(DSIM7, DEBUG_INFO, " I = Response\n");
+ PDEBUG(DSIM7, DEBUG_INFO, " CCRC = 0x%02x\n", cla_ccrc);
+ if (cla_ccrc & CCRC_PIN_NOK)
+ PDEBUG(DSIM7, DEBUG_INFO, " -> PIN-NOT-OK\n");
+ if (cla_ccrc & CCRC_AFBZ_NULL)
+ PDEBUG(DSIM7, DEBUG_INFO, " -> AFBZ = NULL\n");
+ if (cla_ccrc & CCRC_APRC_VALID)
+ PDEBUG(DSIM7, DEBUG_INFO, " -> APRC valid\n");
+ if (cla_ccrc & 0x08)
+ PDEBUG(DSIM7, DEBUG_INFO, " -> reserved\n");
+ if (cla_ccrc & 0x10)
+ PDEBUG(DSIM7, DEBUG_INFO, " -> reserved\n");
+ if (cla_ccrc & 0x20)
+ PDEBUG(DSIM7, DEBUG_INFO, " -> reserved\n");
+ if (cla_ccrc & CCRC_ERROR)
+ PDEBUG(DSIM7, DEBUG_INFO, " -> GENERAL ERROR\n");
+ PDEBUG(DSIM7, DEBUG_INFO, " APRC = 0x%02x\n", ins_aprc);
+ if (ins_aprc & APRC_PIN_REQ)
+ PDEBUG(DSIM7, DEBUG_INFO, " -> Bit 2 = 1:PIN-Check required\n");
+ else
+ PDEBUG(DSIM7, DEBUG_INFO, " -> Bit 2 = 0:PIN-Check not required\n");
+ if (ins_aprc & APRC_APP_LOCKED)
+ PDEBUG(DSIM7, DEBUG_INFO, " -> Bit 3 = 1:Application locked\n");
+ else
+ PDEBUG(DSIM7, DEBUG_INFO, " -> Bit 3 = 0:Application unlocked\n");
+ if (ins_aprc & APRC_GEBZ_LOCK)
+ PDEBUG(DSIM7, DEBUG_INFO, " -> Bit 5 = 1:GEBZ/RUFN locked\n");
+ else
+ PDEBUG(DSIM7, DEBUG_INFO, " -> Bit 5 = 0:GEBZ/RUFN unlocked\n");
+ if (ins_aprc & APRC_GEBZ_FULL)
+ PDEBUG(DSIM7, DEBUG_INFO, " -> Bit 6 = 1:GEBZ full\n");
+ else
+ PDEBUG(DSIM7, DEBUG_INFO, " -> Bit 6 = 0:GEBZ not full\n");
+ }
+ if (dlng == 255) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, " Unsupported length 255!\n");
+ return;
+ }
+ PDEBUG(DSIM7, DEBUG_INFO, " DLNG = %d\n", dlng);
+ if (dlng != length) {
+ PDEBUG(DSIM7, DEBUG_NOTICE, " DLNG does not match message body!\n");
+ return;
+ }
+
+ for (i = 0; i < length; i++) {
+ PDEBUG(DSIM7, DEBUG_INFO, " DATA(%d) = 0x%02x '%c' %d\n", i, data[i], (data[i] >= 32 && data[i] <= 126) ? data[i] : '.', data[i]);
+ }
+}
+
+/* ICL layer */
+
+static void rx_icl_pdu(uint8_t *data, int length)
+{
+ int icb_count, ext = 1;
+
+ if (ext) {
+ if (length < 1) {
+ PDEBUG(DSIMI, DEBUG_NOTICE, "Message too short\n");
+ return;
+ }
+
+ PDEBUG(DSIMI, DEBUG_INFO, "Interface control layer ICB1:\n");
+ if (*data & ICB1_ONLINE)
+ PDEBUG(DSIMI, DEBUG_INFO, " ON-LINE-BIT: 1 = On-line data\n");
+ else
+ PDEBUG(DSIMI, DEBUG_INFO, " ON-LINE-BIT: 0 = Off-line data\n");
+ if (*data & ICB1_CONFIRM)
+ PDEBUG(DSIMI, DEBUG_INFO, " CONFIRM-BIT: 1 = Confirmation\n");
+ else
+ PDEBUG(DSIMI, DEBUG_INFO, " CONFIRM-BIT: 0 = No meaning\n");
+ if (*data & ICB1_MASTER)
+ PDEBUG(DSIMI, DEBUG_INFO, " MASTER/SLAVE-BIT: 1 = Sender is master\n");
+ else
+ PDEBUG(DSIMI, DEBUG_INFO, " MASTER/SLAVE-BIT: 0 = Sender is slave\n");
+ if (*data & ICB1_WT_EXT)
+ PDEBUG(DSIMI, DEBUG_INFO, " WT-EXTENSION-BIT: 1 = Request for WT-Extension\n");
+ else
+ PDEBUG(DSIMI, DEBUG_INFO, " WT-EXTENSION-BIT: 0 = No request for WT-Extension\n");
+ if (*data & ICB1_ABORT)
+ PDEBUG(DSIMI, DEBUG_INFO, " ABORT/TERMINATE-BIT: 1 = Abort/Terminate request\n");
+ else
+ PDEBUG(DSIMI, DEBUG_INFO, " ABORT/TERMINATE-BIT: 0 = No meaning\n");
+ if (*data & ICB1_ERROR)
+ PDEBUG(DSIMI, DEBUG_INFO, " ERROR-BIT: 1 = Error\n");
+ else
+ PDEBUG(DSIMI, DEBUG_INFO, " ERROR-BIT: 0 = No meaning\n");
+ if (*data & ICB1_CHAINING)
+ PDEBUG(DSIMI, DEBUG_INFO, " CHAINING-BIT: 1 = More ICL data follows\n");
+ else
+ PDEBUG(DSIMI, DEBUG_INFO, " CHAINING-BIT: 0 = No more ICL data follows\n");
+ if (*data & ICB_EXT)
+ PDEBUG(DSIMI, DEBUG_INFO, " ICB-EXTENSION-BIT: 1 = ICB2 follows\n");
+ else {
+ PDEBUG(DSIMI, DEBUG_INFO, " ICB-EXTENSION-BIT: 0 = no ICB follows\n");
+ ext = 0;
+ }
+ data++;
+ length--;
+ }
+
+ if (ext) {
+ if (length < 1) {
+ PDEBUG(DSIMI, DEBUG_NOTICE, "Message too short\n");
+ return;
+ }
+
+ PDEBUG(DSIMI, DEBUG_INFO, "Interface control layer ICB2:\n");
+ if (*data & ICB2_DYNAMIC)
+ PDEBUG(DSIMI, DEBUG_INFO, " DYN-BUFFER-SIZE-BIT: 1 = Buffer size %d\n", (*data & ICB2_BUFFER) * 8);
+ else
+ PDEBUG(DSIMI, DEBUG_INFO, " DYN-BUFFER-SIZE-BIT: 0 = No meaning\n");
+ if (*data & ICB2_ISO_L2)
+ PDEBUG(DSIMI, DEBUG_INFO, " ISO-7816-BLOCK-BIT: 1 = Compatible\n");
+ else
+ PDEBUG(DSIMI, DEBUG_INFO, " ISO-7816-BLOCK-BIT: 0 = Incompatible\n");
+ if (*data & ICB2_PRIVATE)
+ PDEBUG(DSIMI, DEBUG_INFO, " PRIVATE-USE-BIT: 1 = Private use layer 7 protocol\n");
+ else
+ PDEBUG(DSIMI, DEBUG_INFO, " PRIVATE-USE-BIT: 0 = No meaning\n");
+ if (*data & ICB_EXT)
+ PDEBUG(DSIMI, DEBUG_INFO, " ICB-EXTENSION-BIT: 1 = ICB3 follows\n");
+ else {
+ PDEBUG(DSIMI, DEBUG_INFO, " ICB-EXTENSION-BIT: 0 = no ICB follows\n");
+ ext = 0;
+ }
+ data++;
+ length--;
+ }
+
+ icb_count = 2;
+ while (ext) {
+ if (length < 1) {
+ PDEBUG(DSIMI, DEBUG_NOTICE, "Message too short\n");
+ return;
+ }
+
+ PDEBUG(DSIMI, DEBUG_INFO, "Interface control layer ICB%d:\n", ++icb_count);
+ PDEBUG(DSIMI, DEBUG_INFO, " Value: 0x%02x\n", *data);
+ if (!(*data & 0x80))
+ ext = 0;
+ data++;
+ length--;
+ }
+
+ rx_icl_sdu(data, length);
+}
+
+/* Layer 2 */
+
+static uint8_t flip(uint8_t c)
+{
+ c = ((c&0x55) << 1) | ((c&0xaa) >> 1); /* 67452301 */
+ c = ((c&0x33) << 2) | ((c&0xcc) >> 2); /* 45670123 */
+ c = (c << 4) | (c >> 4); /* 01234567 */
+
+ return c;
+}
+
+void sniffer_reset(sim_sniffer_t *sim)
+{
+ PDEBUG(DSIM1, DEBUG_INFO, "Resetting sniffer\n");
+ memset(sim, 0, sizeof(*sim));
+}
+
+static void decode_ta1(sim_sniffer_t __attribute__((unused)) *sim, uint8_t c, int count)
+{
+ int fi = -1, di = -1;
+ double fmax = 0.0;
+
+ switch (c >> 4) {
+ case 0:
+ fi = 372; fmax = 4.0;
+ break;
+ case 1:
+ fi = 372; fmax = 5.0;
+ break;
+ case 2:
+ fi = 558; fmax = 6.0;
+ break;
+ case 3:
+ fi = 744; fmax = 8.0;
+ break;
+ case 4:
+ fi = 1116; fmax = 12.0;
+ break;
+ case 5:
+ fi = 1488; fmax = 16.0;
+ break;
+ case 6:
+ fi = 1860; fmax = 20.0;
+ break;
+ case 9:
+ fi = 512; fmax = 5.0;
+ break;
+ case 10:
+ fi = 768; fmax = 7.5;
+ break;
+ case 11:
+ fi = 1014; fmax = 10.0;
+ break;
+ case 12:
+ fi = 1536; fmax = 15.0;
+ break;
+ case 13:
+ fi = 2048; fmax = 20.0;
+ break;
+ }
+
+ switch (c & 0xf) {
+ case 1:
+ di = 1;
+ break;
+ case 2:
+ di = 2;
+ break;
+ case 3:
+ di = 4;
+ break;
+ case 4:
+ di = 8;
+ break;
+ case 5:
+ di = 16;
+ break;
+ case 6:
+ di = 32;
+ break;
+ case 7:
+ di = 64;
+ break;
+ case 8:
+ di = 12;
+ break;
+ case 9:
+ di = 20;
+ break;
+ }
+
+ if (fi > 0)
+ PDEBUG(DSIM2, DEBUG_INFO, " TA%d Fi = %d, f(max.) = %.1f MHz\n", count, fi, fmax);
+ else
+ PDEBUG(DSIM2, DEBUG_INFO, " TA%d Fi = RFU\n", count);
+ if (di > 0)
+ PDEBUG(DSIM2, DEBUG_INFO, " TA%d Di = %d\n", count, di);
+ else
+ PDEBUG(DSIM2, DEBUG_INFO, " TA%d Di = RFU\n", count);
+}
+
+static void decode_ta2(sim_sniffer_t __attribute__((unused)) *sim, uint8_t c, int count)
+{
+ PDEBUG(DSIM2, DEBUG_INFO, " TA%d T = %d\n", count, c & 0xf);
+ if (!(c & 0x10))
+ PDEBUG(DSIM2, DEBUG_INFO, " TA%d : Fi and Di by TA1 shall apply.\n", count);
+ else
+ PDEBUG(DSIM2, DEBUG_INFO, " TA%d : Implicit values (and not Di / Di by TA1) sall apply.\n", count);
+ if (!(c & 0x80))
+ PDEBUG(DSIM2, DEBUG_INFO, " TA%d : Capable to change negotiable/specific mode.\n", count);
+ else
+ PDEBUG(DSIM2, DEBUG_INFO, " TA%d : Unable to change negotiable/specific mode.\n", count);
+}
+
+static void decode_tai(sim_sniffer_t *sim, uint8_t c, int count)
+{
+ if ((sim->atr_td & 0xf) != 14) {
+ PDEBUG(DSIM2, DEBUG_INFO, " TA%d Value = 0x%02x\n", count, c);
+ return;
+ }
+
+ if (count == 3) {
+ switch (c & 0xf) {
+ case 0:
+ PDEBUG(DSIM2, DEBUG_INFO, " TA%d fsmin = Default\n", count);
+ break;
+ case 1:
+ case 2:
+ case 3:
+ PDEBUG(DSIM2, DEBUG_INFO, " TA%d fsmin = %d MHz\n", count, c & 0xf);
+ break;
+ default:
+ PDEBUG(DSIM2, DEBUG_INFO, " TA%d fsmin = reserved\n", count);
+ break;
+ }
+
+ switch (c >> 4) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ PDEBUG(DSIM2, DEBUG_INFO, " TA%d fsmax = reserved\n", count);
+ break;
+ case 5:
+ PDEBUG(DSIM2, DEBUG_INFO, " TA%d fsmax = 5 MHz (Default)\n", count);
+ break;
+ default:
+ PDEBUG(DSIM2, DEBUG_INFO, " TA%d fsmax = %d MHz\n", count, c >> 4);
+ break;
+ }
+ } else {
+ PDEBUG(DSIM2, DEBUG_INFO, " TA%d Block Waiting Time = %d\n", count, c);
+ }
+}
+
+static void decode_tb1(sim_sniffer_t __attribute__((unused)) *sim, uint8_t c, int count)
+{
+ if ((c & 0x1f) == 0)
+ PDEBUG(DSIM2, DEBUG_INFO, " TB%d PI1=0: VPP not connected\n", count);
+ else if ((c & 0x1f) == 5)
+ PDEBUG(DSIM2, DEBUG_INFO, " TB%d PI1=5: VPP is 5 Volts (default)\n", count);
+ else if ((c & 0x1f) >= 6 && (c & 0x1f) <= 25)
+ PDEBUG(DSIM2, DEBUG_INFO, " TB%d PI1=%d: VPP is %d Volts\n", count, c & 0x1f, (c & 0x1f) - 1);
+ else
+ PDEBUG(DSIM2, DEBUG_INFO, " TB%d PI1=%d: not defined\n", count, c & 0x1f);
+ PDEBUG(DSIM2, DEBUG_INFO, " TB%d II = %d\n", count, (c >> 5) & 0x3);
+}
+
+static void decode_tb2(sim_sniffer_t __attribute__((unused)) *sim, uint8_t c, int count)
+{
+ PDEBUG(DSIM2, DEBUG_INFO, " TB%d Value = 0x%02x\n", count, c);
+}
+
+static void decode_tbi(sim_sniffer_t *sim, uint8_t c, int count)
+{
+ if ((sim->atr_td & 0xf) != 14) {
+ PDEBUG(DSIM2, DEBUG_INFO, " TB%d Value = 0x%02x\n", count, c);
+ return;
+ }
+
+ if (count == 3) {
+ PDEBUG(DSIM2, DEBUG_INFO, " TB%d Maximum block size = %d\n", count, c);
+ } else {
+ if (!(c & 0x01))
+ PDEBUG(DSIM2, DEBUG_INFO, " TB%d XOR Checksum\n", count);
+ else
+ PDEBUG(DSIM2, DEBUG_INFO, " TB%d CRC Checksum\n", count);
+ if (!(c & 0x02))
+ PDEBUG(DSIM2, DEBUG_INFO, " TB%d 12-etu frame\n", count);
+ else
+ PDEBUG(DSIM2, DEBUG_INFO, " TB%d 11-etu frame\n", count);
+ if (!(c & 0x04))
+ PDEBUG(DSIM2, DEBUG_INFO, " TB%d No Chaining in ICL-Layer-Protocol\n", count);
+ else
+ PDEBUG(DSIM2, DEBUG_INFO, " TB%d Chaining in ICL-Layer-Protocol\n", count);
+ if (!(c & 0x08))
+ PDEBUG(DSIM2, DEBUG_INFO, " TB%d Incompatible to ISO 7816 (Character Protocol)\n", count);
+ else
+ PDEBUG(DSIM2, DEBUG_INFO, " TB%d Compatible to ISO 7816 (Character Protocol)\n", count);
+ if (!(c & 0x10))
+ PDEBUG(DSIM2, DEBUG_INFO, " TB%d No private in ICL-Layer-Protocol\n", count);
+ else
+ PDEBUG(DSIM2, DEBUG_INFO, " TB%d Private in ICL-Layer-Protocol\n", count);
+ if (!(c & 0x20))
+ PDEBUG(DSIM2, DEBUG_INFO, " TB%d No ICB-Extension in ICL-Layer-Protocol\n", count);
+ else
+ PDEBUG(DSIM2, DEBUG_INFO, " TB%d ICB-Extension in ICL-Layer-Protocol\n", count);
+ }
+}
+
+static void decode_tc1(sim_sniffer_t __attribute__((unused)) *sim, uint8_t c, int count)
+{
+ PDEBUG(DSIM2, DEBUG_INFO, " TC%d N = %d\n", count, c);
+}
+
+static void decode_tc2(sim_sniffer_t __attribute__((unused)) *sim, uint8_t c, int count)
+{
+ PDEBUG(DSIM2, DEBUG_INFO, " TC%d Value = 0x%02x\n", count, c);
+}
+
+static void decode_tci(sim_sniffer_t *sim, uint8_t c, int count)
+{
+ if ((sim->atr_td & 0xf) != 14) {
+ PDEBUG(DSIM2, DEBUG_INFO, " TC%d Value = 0x%02x\n", count, c);
+ return;
+ }
+
+ PDEBUG(DSIM2, DEBUG_INFO, " TC%d Character Waiting Time = %d\n", count, c);
+}
+
+static void decode_td(sim_sniffer_t __attribute__((unused)) *sim, uint8_t c, int count)
+{
+ switch (c & 0xf) {
+ case 0:
+ PDEBUG(DSIM2, DEBUG_INFO, " TD%d T=1: Half-duplex transmission of characters (ISO 7816).\n", count);
+ break;
+ case 1:
+ PDEBUG(DSIM2, DEBUG_INFO, " TD%d T=1: Half-duplex transmission of blocks (ISO 7816).\n", count);
+ break;
+ case 2:
+ case 3:
+ PDEBUG(DSIM2, DEBUG_INFO, " TD%d T=%d: Reserved for future full-duplex operations.\n", count, c & 0xf);
+ break;
+ case 4:
+ PDEBUG(DSIM2, DEBUG_INFO, " TD%d T=4: Reserved for an enhanced half-duplex transmission of characters.\n", count);
+ break;
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ PDEBUG(DSIM2, DEBUG_INFO, " TD%d T=%d: Reserved for future use by ISO/IEC JTC 1/SC 17.\n", count, c & 0xf);
+ break;
+ case 14:
+ PDEBUG(DSIM2, DEBUG_INFO, " TD%d T=14: Refers to transmission protocols not standardized by ISO/IEC JTC 1/SC 17.\n", count);
+ break;
+ case 15:
+ PDEBUG(DSIM2, DEBUG_INFO, " TD%d T=15: Does not refer to a transmission protocol, but only qualifies global interface bytes.\n", count);
+ break;
+ }
+}
+
+static void decode_if(sim_sniffer_t *sim, int count)
+{
+ switch (count) {
+ case 1:
+ if (sim->atr_if_mask & 0x10)
+ decode_ta1(sim, sim->atr_ta, count);
+ if (sim->atr_if_mask & 0x20)
+ decode_tb1(sim, sim->atr_tb, count);
+ if (sim->atr_if_mask & 0x40)
+ decode_tc1(sim, sim->atr_tc, count);
+ if (sim->atr_if_mask & 0x80)
+ decode_td(sim, sim->atr_td, count);
+ break;
+ case 2:
+ if (sim->atr_if_mask & 0x10)
+ decode_ta2(sim, sim->atr_ta, count);
+ if (sim->atr_if_mask & 0x20)
+ decode_tb2(sim, sim->atr_tb, count);
+ if (sim->atr_if_mask & 0x40)
+ decode_tc2(sim, sim->atr_tc, count);
+ if (sim->atr_if_mask & 0x80)
+ decode_td(sim, sim->atr_td, count);
+ break;
+ default:
+ if (sim->atr_if_mask & 0x10)
+ decode_tai(sim, sim->atr_ta, count);
+ if (sim->atr_if_mask & 0x20)
+ decode_tbi(sim, sim->atr_tb, count);
+ if (sim->atr_if_mask & 0x40)
+ decode_tci(sim, sim->atr_tc, count);
+ if (sim->atr_if_mask & 0x80)
+ decode_td(sim, sim->atr_td, count);
+ }
+
+ if ((sim->atr_td >> 4))
+ PDEBUG(DSIM2, DEBUG_INFO, "----------------------------------------\n");
+}
+
+static void decode_hist(sim_sniffer_t __attribute__((unused)) *sim, uint8_t c, int count)
+{
+ PDEBUG(DSIM2, DEBUG_INFO, " History byte #%d: 0x%02x\n", count, c);
+}
+
+static void rx_atr(sim_sniffer_t *sim, uint8_t c)
+{
+ /* TS */
+ if (sim->atr_count == 0) {
+ PDEBUG(DSIM1, DEBUG_INFO, "----------------------------------------\n");
+ switch (c) {
+ case 0x3f:
+ PDEBUG(DSIM2, DEBUG_INFO, "Reading ATR inverse bit order:\n");
+ sim->inverse_order = 1;
+ break;
+ case 0x3b:
+ PDEBUG(DSIM2, DEBUG_INFO, "Reading ATR normal bit order:\n");
+ sim->inverse_order = 0;
+ break;
+ default:
+ sniffer_reset(sim);
+ return;
+ }
+ sim->atr_tck = c;
+ sim->atr_count++;
+ return;
+ }
+
+ if (sim->inverse_order)
+ c = flip (c);
+
+ sim->atr_tck ^= c;
+
+ if (sim->atr_count == 1) {
+ sim->atr_t0 = c;
+ sim->atr_if_mask = c;
+ sim->atr_count++;
+ return;
+ }
+
+ /* get TA, if included, or skip by inc. atr_count */
+ if (sim->atr_count == 2) {
+ if (sim->atr_if_mask & 0x10) {
+ sim->atr_ta = c;
+ sim->atr_count++;
+ return;
+ } else
+ sim->atr_count++;
+ }
+
+ /* get TB, if included, or skip by inc. atr_count */
+ if (sim->atr_count == 3) {
+ if (sim->atr_if_mask & 0x20) {
+ sim->atr_tb = c;
+ sim->atr_count++;
+ return;
+ } else
+ sim->atr_count++;
+ }
+
+ /* get TC, if included, or skip by inc. atr_count */
+ if (sim->atr_count == 4) {
+ if (sim->atr_if_mask & 0x40) {
+ sim->atr_tc = c;
+ sim->atr_count++;
+ return;
+ } else
+ sim->atr_count++;
+ }
+
+ /* get TD, if included, or skip by inc. atr_count */
+ if (sim->atr_count == 5) {
+ if (sim->atr_if_mask & 0x80) {
+ sim->atr_td = c;
+ /* decode content */
+ decode_if(sim, sim->atr_if_count + 1);
+ /* get new mask byte and start over */
+ sim->atr_count = 2;
+ sim->atr_if_mask = sim->atr_td;
+ sim->atr_if_count++;
+ return;
+ } else
+ sim->atr_count++;
+ }
+
+ /* decode content */
+ if (sim->atr_count == 6)
+ decode_if(sim, sim->atr_if_count + 1);
+
+ /* process historical character */
+ if (sim->atr_count < 6 + (sim->atr_t0 & 0xf)) {
+ decode_hist(sim, c, sim->atr_count - 6 + 1);
+ sim->atr_count++;
+ return;
+ }
+
+ if (sim->atr_tck == 0)
+ PDEBUG(DSIM2, DEBUG_INFO, " Checksum 0x%02x ok.\n", c);
+ else
+ PDEBUG(DSIM2, DEBUG_NOTICE, " Checksum 0x%02x error!\n", c);
+
+
+ sim->l1_state = L1_STATE_RECEIVE;
+ sim->block_state = BLOCK_STATE_ADDRESS;
+ PDEBUG(DSIM2, DEBUG_INFO, "ATR done!\n");
+}
+
+static void rx_char(sim_sniffer_t *sim, uint8_t c)
+{
+ if (sim->inverse_order)
+ c = flip(c);
+
+ sim->block_checksum ^= c;
+
+ switch (sim->block_state) {
+ case BLOCK_STATE_ADDRESS:
+ if ((c >> 4) != 1 && (c & 0xf) != 1) {
+ /* start over if we do not get a valid message start */
+ sniffer_reset(sim);
+ sniffer_rx(sim, c);
+ return;
+ }
+ PDEBUG(DSIM1, DEBUG_INFO, "----------------------------------------\n");
+ sim->block_address = c;
+ sim->block_state = BLOCK_STATE_CONTROL;
+ sim->block_checksum = c;
+ return;
+ case BLOCK_STATE_CONTROL:
+ sim->block_control = c;
+ sim->block_state = BLOCK_STATE_LENGTH;
+ return;
+ case BLOCK_STATE_LENGTH:
+ sim->block_length = c;
+ sim->block_count = 0;
+ sim->block_state = BLOCK_STATE_DATA;
+ return;
+ case BLOCK_STATE_DATA:
+ if (sim->block_count < sim->block_length) {
+ sim->block_data[sim->block_count++] = c;
+ return;
+ }
+ PDEBUG(DSIM2, DEBUG_INFO, "Layer 2:\n");
+ PDEBUG(DSIM2, DEBUG_INFO, " source %d -> to %d\n", sim->block_address >> 4, sim->block_address & 0xf);
+ if ((sim->block_control & 0x11) == 0x00)
+ PDEBUG(DSIM2, DEBUG_INFO, " control I: N(S)=%d N(R)=%d\n", (sim->block_control >> 1) & 7, sim->block_control >> 5);
+ else if ((sim->block_control & 0x1f) == 0x09)
+ PDEBUG(DSIM2, DEBUG_INFO, " control REJ: N(R)=%d\n", sim->block_control >> 5);
+ else if (sim->block_control == 0xef)
+ PDEBUG(DSIM2, DEBUG_INFO, " control RES");
+ else
+ PDEBUG(DSIM2, DEBUG_INFO, " control unknown 0x%02x\n", sim->block_control);
+ PDEBUG(DSIM2, DEBUG_INFO, " length %d\n", sim->block_length);
+ if (sim->block_checksum == 0)
+ rx_icl_pdu(sim->block_data, sim->block_length);
+ else
+ PDEBUG(DSIM2, DEBUG_NOTICE, "Received message with checksum error!\n");
+ sim->block_state = BLOCK_STATE_ADDRESS;
+ }
+}
+
+void sniffer_rx(sim_sniffer_t *sim, uint8_t c)
+{
+
+ PDEBUG(DSIM1, DEBUG_DEBUG, "Serial RX '0x%02x'\n", c);
+
+ switch (sim->l1_state) {
+ case L1_STATE_RESET:
+ if (c != 0x3f && c != 0x3b) {
+ PDEBUG(DSIM1, DEBUG_INFO, "Received garbage '0x%02x' while waiting for ATR\n", c);
+ break;
+ }
+ sim->l1_state = L1_STATE_ATR;
+ sim->atr_count = 0;
+ /* fall through */
+ case L1_STATE_ATR:
+ rx_atr(sim, c);
+ break;
+ case L1_STATE_RECEIVE:
+ rx_char(sim, c);
+ break;
+ default:
+ break;
+ }
+}
+
+void sniffer_timeout(sim_sniffer_t *sim)
+{
+ switch (sim->l1_state) {
+ case L1_STATE_RESET:
+ case L1_STATE_ATR:
+ if (sim->l1_state == L1_STATE_ATR && sim->atr_count)
+ PDEBUG(DSIM1, DEBUG_NOTICE, "Timeout while receiving ATR!\n");
+ sim->l1_state = L1_STATE_ATR;
+ sim->atr_count = 0;
+ break;
+ case L1_STATE_RECEIVE:
+ if (sim->block_state != BLOCK_STATE_ADDRESS)
+ PDEBUG(DSIM1, DEBUG_NOTICE, "Timeout while receiving message!\n");
+ sim->block_state = BLOCK_STATE_ADDRESS;
+ break;
+ default:
+ break;
+ }
+}
+
+#endif /* ARDUINO */
diff --git a/src/sim/sniffer.h b/src/sim/sniffer.h
new file mode 100644
index 0000000..be948d6
--- /dev/null
+++ b/src/sim/sniffer.h
@@ -0,0 +1,26 @@
+
+typedef struct sim_sniffer {
+ enum l1_state l1_state;
+ int inverse_order;
+ int atr_count;
+ int atr_if_count;
+ uint8_t atr_if_mask;
+ uint8_t atr_t0;
+ uint8_t atr_ta;
+ uint8_t atr_tb;
+ uint8_t atr_tc;
+ uint8_t atr_td;
+ uint8_t atr_tck;
+ enum block_state block_state;
+ uint8_t block_address;
+ uint8_t block_control;
+ uint8_t block_length;
+ uint8_t block_count;
+ uint8_t block_checksum;
+ uint8_t block_data[256];
+} sim_sniffer_t;
+
+void sniffer_reset(sim_sniffer_t *sim);
+void sniffer_rx(sim_sniffer_t *sim, uint8_t c);
+void sniffer_timeout(sim_sniffer_t *sim);
+