Added SIM card emulator/sniffer for C-Netz

This commit is contained in:
Andreas Eversberg
2020-05-01 19:01:39 +02:00
parent cc49a3c674
commit 8fea1ef7bf
22 changed files with 10411 additions and 2 deletions

View File

@@ -52,7 +52,8 @@ SUBDIRS += \
eurosignal \
tv \
radio \
zeitansage
zeitansage \
sim
if HAVE_SDR
if HAVE_FUSE

View File

@@ -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 }
};

View File

@@ -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);

18
src/sim/Makefile.am Normal file
View File

@@ -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

33
src/sim/eeprom.h Normal file
View File

@@ -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();

97
src/sim/image.c Normal file
View File

@@ -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 */

489
src/sim/main.c Normal file
View File

@@ -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 */

1438
src/sim/sim.c Normal file

File diff suppressed because it is too large Load Diff

148
src/sim/sim.h Normal file
View File

@@ -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);

287
src/sim/sim.ino Normal file
View File

@@ -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
}

798
src/sim/sniffer.c Normal file
View File

@@ -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 */

26
src/sim/sniffer.h Normal file
View File

@@ -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);