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

1
.gitignore vendored
View File

@ -69,6 +69,7 @@ src/tv/osmotv
src/radio/osmoradio
src/datenklo/datenklo
src/zeitansage/zeitansage
src/sim/cnetz_sim
extra/cnetz_memory_card_generator
src/test/test_filter
src/test/test_sendevolumenregler

View File

@ -100,6 +100,7 @@ AC_OUTPUT(
src/radio/Makefile
src/datenklo/Makefile
src/zeitansage/Makefile
src/sim/Makefile
src/test/Makefile
src/Makefile
extra/Makefile

View File

@ -112,6 +112,7 @@ Additional features:
<ul>
<li><a href="datenklo.html">Das Datenklo</a></li>
<li><a href="tv.html">Osmo TV</a></li>
<li><a href="sim.html">C-Netz Sim Card</a></li>
</ul>
</center>

472
docs/sim.html Normal file
View File

@ -0,0 +1,472 @@
<html>
<head>
<link href="style.css" rel="stylesheet" type="text/css" />
<title>osmocom-analog</title>
</head>
<body>
<center><table><tr><td>
<h2><center>C-Netz SIM Emulator</center></h2>
<center><img src="sim.jpg"/></center>
<p>
Why emulating a SIM card?
Maybe you got an a C-Netz phone from the attic, friend or Ebay?
But the SIM card is missing. Without SIM card you cannot use your C-Netz phone at all.
Then you buy a high price on Ebay to get a used SIM card.
You find out that the SIM card requires a PIN that you don't know.
Even if you find a SIM card with no PIN enabled, it may not work with newer phones.
The emulator can help you in this case.
</p>
<p>
Also the emulator can be used to emulate service cards or special cards that enable cell monitoring.
With this emulator you can modify all subscriber data without restrictions.
</p>
<ul>
<li><a href="#emu">Emulating SIM Card</a>
<li><a href="#sniff">Sniffing SIM Card</a>
<li><a href="#byo">Build Your Own SIM Card</a>
<li><a href="#usage">Using the SIM Card</a>
<li><a href="#service">Service Cards</a>
</ul>
<p class="toppic">
<a name="emu"></a>
Emulating SIM Card
</p>
<center><img src="sim-rs232.jpg"/></center>
<p>
The easiest way to emulate a C-Netz SIM card is to use your Linux PC with a serial interface connected to the card reader of the phone.
Connection can be made directly via wires or via ISO card PCB.
You may also use an old ISO card an drill away the chip inside. Then solder thin wires close to the pads and connect them to a serial interface.
</p>
<p>
In order to connect the card to your Linux PC, you need a serial-to-USB interface.
Don't use the 9 pin SUB-D type of interface, because they have level shifters and will brick your phone.
Use TTL level interface as shown in the image above.
</p>
<p>
Because the SIM cards uses TTL level, we can connect a CP2102 controller directly to the card reader of a phone.
Don't worry about 5 Volts level from the card reader. The CP2102 can handle it.
In order to connect TX and RX together on one pad of the SIM card, we need to add a diode between the I/O pad and the TX output.
The cathode must point towards the TX output, so that TX can only pull the I/O line low (0).
Use a diode with a low forward voltage drop, like a Schottky diode. I use a simple 1n4148 Silicon diode and it works too.
</p>
<p>
<font color="red">Important: Some serial interfaces have wrong signal labels.
TX and RX might be reversed, so that TX is actually an input and RX an ouput.
You will find out when you connect an Milliamp meter between signal and ground.
The output will have several Milliamps, but the input doesn't.
</font>
</p>
<p>
<font color="red">Important: Be sure to run your phone on battery and not via gounded power supply.
If the output of the power supply is grounded, the ground of your power line is also connected to the phone's card reader.
Voltage spikes on the power line's ground between your PC and your phone may kill the card reader or the USB port.
Use an isolating transformer!
</font>
</p>
<p>
<font color="red">Important: The serial interface must support 8e2. (8 data bits, even parity, two stop bits)
I suggest to use the CP2102. If you know other serial interfaces that work, let me know.
</font>
</p>
<table><tr>
<td><img src="sim-contacts.jpg"/></td>
<td><p>
Connect Ground to GND.
<br>
Connect CTS input to RESET.
<br>
Connect RX input to I/O.
<br>
Connect TX output via diode to I/O.
<br>
(Place cathode towards TX output)
</p></td>
</tr></table>
<p>
To run the emulator, use the "sim" keyword at the end of the command line.
Use the '-s' option to give the correct serial interface:
</p>
<pre>
# src/sim/cnetz_sim -s /dev/ttyUSB0 sim
...
FUTLN=23100001, Sicherungscode=3103, Kartekennung=3, Sonderheitenschluessel=0, Wartungsschluessel=65535
Telephone directory has 80 entries.
SIM emulator ready, please start the phone!
sim.c:1352 info : Reset singnal on (low)
sim.c:1352 info : Reset singnal off (high)
sim.c:1371 info : Card has disabled PIN (system PIN '0000') Selecting card #1.
sim.c:1374 info : Sending ATR
sim.c:1125 info : RX message
sim.c:1135 info : control I: N(S)=0 N(R)=0
sim.c: 473 info : SL-APPL app 3
sim.c:1222 info : TX resonse
sim.c:1228 info : control I: N(S)=0 N(R)=1
sim.c:1125 info : RX message
sim.c:1135 info : control I: N(S)=1 N(R)=1
sim.c: 558 info : RD-EBDT
sim.c:1222 info : TX resonse
sim.c:1228 info : control I: N(S)=1 N(R)=2
sim.c:1125 info : RX message
sim.c:1135 info : control I: N(S)=2 N(R)=2
sim.c: 473 info : SL-APPL app 4
sim.c:1222 info : TX resonse
sim.c:1228 info : control I: N(S)=2 N(R)=3
sim.c:1125 info : RX message
sim.c:1135 info : control I: N(S)=3 N(R)=3
sim.c: 473 info : SL-APPL app 3
sim.c:1222 info : TX resonse
sim.c:1228 info : control I: N(S)=3 N(R)=4
sim.c:1125 info : RX message
sim.c:1135 info : control I: N(S)=4 N(R)=4
sim.c: 558 info : RD-EBDT
sim.c:1222 info : TX resonse
sim.c:1228 info : control I: N(S)=4 N(R)=5
sim.c:1125 info : RX message
sim.c:1135 info : control I: N(S)=5 N(R)=5
sim.c: 599 info : RD-RUFN (loc=0)
sim.c: 655 info : 80 numbers can be stored in EEPROM
sim.c:1222 info : TX resonse
sim.c:1228 info : control I: N(S)=5 N(R)=6
sim.c:1352 info : Reset singnal on (low)
</pre>
<p>
Use '-h' command line option to get a list of all options.
</p>
<p class="toppic">
<a name="sniff"></a>
Sniffing SIM Card
</p>
<p>
To run the sniffer, use the "sniff" keyword at the end of the command line.
You only need to connect I/O line to the RX line of your serial interface. (And ground of course!)
Use the '-s' option to give the correct serial interface:
</p>
<pre>
# src/sim/cnetz_sim -s /dev/ttyUSB0 sniff
sniffer.c: 602 info : ----------------------------------------
sniffer.c: 609 info : Reading ATR normal bit order:
sniffer.c: 547 info : TD1 T=14: Refers to transmission protocols not standardized by ISO/IEC JTC 1/SC 17.
sniffer.c: 590 info : ----------------------------------------
sniffer.c: 547 info : TD2 T=14: Refers to transmission protocols not standardized by ISO/IEC JTC 1/SC 17.
sniffer.c: 590 info : ----------------------------------------
sniffer.c: 418 info : TA3 fsmin = 3 MHz
sniffer.c: 433 info : TA3 fsmax = 5 MHz (Default)
sniffer.c: 470 info : TB3 Maximum block size = 42
sniffer.c: 516 info : TC3 Character Waiting Time = 3
sniffer.c: 547 info : TD3 T=14: Refers to transmission protocols not standardized by ISO/IEC JTC 1/SC 17.
sniffer.c: 590 info : ----------------------------------------
sniffer.c: 440 info : TA4 Block Waiting Time = 4
sniffer.c: 590 info : ----------------------------------------
sniffer.c: 595 info : History byte #1: 0x92
sniffer.c: 595 info : History byte #2: 0x80
sniffer.c: 595 info : History byte #3: 0x00
sniffer.c: 595 info : History byte #4: 0x41
sniffer.c: 595 info : History byte #5: 0x32
sniffer.c: 595 info : History byte #6: 0x36
sniffer.c: 595 info : History byte #7: 0x01
sniffer.c: 595 info : History byte #8: 0x11
sniffer.c: 690 info : Checksum 0xe4 ok.
sniffer.c: 697 info : ATR done!
sniffer.c: 715 info : ----------------------------------------
sniffer.c: 734 info : Layer 2:
sniffer.c: 735 info : source 3 -&gt; to 1
sniffer.c: 737 info : control I: N(S)=0 N(R)=0
sniffer.c: 744 info : length 15
sniffer.c: 203 info : Interface control layer ICB1:
sniffer.c: 207 info : ON-LINE-BIT: 0 = Off-line data
sniffer.c: 211 info : CONFIRM-BIT: 0 = No meaning
sniffer.c: 213 info : MASTER/SLAVE-BIT: 1 = Sender is master
sniffer.c: 219 info : WT-EXTENSION-BIT: 0 = No request for WT-Extension
sniffer.c: 223 info : ABORT/TERMINATE-BIT: 0 = No meaning
sniffer.c: 227 info : ERROR-BIT: 0 = No meaning
sniffer.c: 231 info : CHAINING-BIT: 0 = No more ICL data follows
sniffer.c: 235 info : ICB-EXTENSION-BIT: 0 = no ICB follows
sniffer.c: 48 info : Layer 7:
sniffer.c: 50 info : I = Command
sniffer.c: 51 info : CLA = 0x02
sniffer.c: 54 info : -&gt; CNTR (Control Class)
sniffer.c: 75 info : INS = 0xf1
sniffer.c: 80 info : -&gt; SL-APPL (Select Application)
sniffer.c: 180 info : DLNG = 11
sniffer.c: 187 info : DATA(0) = 0x38 '8' 56
sniffer.c: 187 info : DATA(1) = 0x39 '9' 57
sniffer.c: 187 info : DATA(2) = 0x34 '4' 52
sniffer.c: 187 info : DATA(3) = 0x39 '9' 57
sniffer.c: 187 info : DATA(4) = 0x30 '0' 48
sniffer.c: 187 info : DATA(5) = 0x31 '1' 49
sniffer.c: 187 info : DATA(6) = 0x30 '0' 48
sniffer.c: 187 info : DATA(7) = 0x30 '0' 48
sniffer.c: 187 info : DATA(8) = 0x33 '3' 51
sniffer.c: 187 info : DATA(9) = 0x30 '0' 48
sniffer.c: 187 info : DATA(10) = 0x31 '1' 49
sniffer.c: 715 info : ----------------------------------------
sniffer.c: 734 info : Layer 2:
sniffer.c: 735 info : source 1 -&gt; to 3
sniffer.c: 737 info : control I: N(S)=0 N(R)=1
sniffer.c: 744 info : length 4
sniffer.c: 203 info : Interface control layer ICB1:
sniffer.c: 207 info : ON-LINE-BIT: 0 = Off-line data
sniffer.c: 211 info : CONFIRM-BIT: 0 = No meaning
sniffer.c: 215 info : MASTER/SLAVE-BIT: 0 = Sender is slave
sniffer.c: 219 info : WT-EXTENSION-BIT: 0 = No request for WT-Extension
sniffer.c: 223 info : ABORT/TERMINATE-BIT: 0 = No meaning
sniffer.c: 227 info : ERROR-BIT: 0 = No meaning
sniffer.c: 231 info : CHAINING-BIT: 0 = No more ICL data follows
sniffer.c: 235 info : ICB-EXTENSION-BIT: 0 = no ICB follows
sniffer.c: 48 info : Layer 7:
sniffer.c: 142 info : I = Response
sniffer.c: 143 info : CCRC = 0x05
sniffer.c: 145 info : -&gt; PIN-NOT-OK
sniffer.c: 149 info : -&gt; APRC valid
sniffer.c: 158 info : APRC = 0x02
sniffer.c: 160 info : -&gt; Bit 2 = 1:PIN-Check required
sniffer.c: 166 info : -&gt; Bit 3 = 0:Application unlocked
sniffer.c: 170 info : -&gt; Bit 5 = 0:GEBZ/RUFN unlocked
sniffer.c: 174 info : -&gt; Bit 6 = 0:GEBZ not full
sniffer.c: 180 info : DLNG = 0
sniffer.c: 302 info : Resetting sniffer
</pre>
<p>
When the phone is switched on, the SIM card is powered up and outputs the ATR sequence (Answer To Reset).
</p>
<p>
The first message is a command message that is transmitted from the phone towards the SIM card.
The layer 2 header indicates the direction and the length of 15 bytes.
The ICR layer has no meaning with the C-Netz.
Except for the MASTER/SLAVE-BIT, no other bit is used.
The layer 7 (application) header indicates the command and the message type and length, followed by 11 bytes of data.
This command tells the SIM card to select C-Netz application.
</p>
<p>
The second message is a response message that is transmitted from the SIM card towards the phone.
The layer 2 header indicates the direction and the length of 4 bytes.
The layer 7 header indicates the response and status bits and length, followed by 0 bytes of data.
The response tells the SIM card that a PIN is required to complete the command.
The user is prompted to enter the pin.
</p>
<p>
To read more about the protocol, and the meaning of messages, refer to <a href="http://download.eversberg.eu/mobilfunk/C-Netz-Dokus/FTZ%20171%20TR%2060%20-%20Anhang%201%20Berechtigungskarte%20als%20Prozessorkarte.pdf">FTZ 171 TR 60 - Anhang 1 Berechtigungskarte als Prozessorkarte.pdf</a>
</p>
<p class="toppic">
<a name="byo"></a>
Build Your Own SIM Card
</p>
<center><img src="sim_layout.png"/></center>
<p>
You find the PCB drawings inside the "layout" directory of the git repository.
Be sure to print it without scaling!
Check if the printed size matches an ISO card.
Also there is the source files for the 'Eagle' layout program, if you like to change it.
</p>
<p>
You may use an "Arduino UNO" or "ATTINY85" to emulate a SIM card without a PC.
In case of the Arduino, you still need wires to connect it to the card reader of the phone.
If you use an ATTINY85, you can put the micro controller directly on a PCB card, as shown on top of this page.
</p>
<p>
To compile and run with Arduino, you need to open "src/sim/sim.ino" with Arduino software and select the "Arduino UNO" board.
The RESET input is at pin 6 and the I/O line at pin 7.
Connect these two lines together with ground line to the card reader or ISO card PCB.
You don't need a diode this time, since pin 7 is automatically switched between input and output.
The serial protocol is emulated in software.
The status LED (pin 13) will flash whenever a message is received from the card reader.
</p>
<p>
To compile and run with ATTINY85, you need to open "src/sim/sim.ino" with Arduino software and select the "ATiny25/45/85" board and the "ATiny85" chip.
Refer to the internet on how to compile and flash the ATTINY85 without boot-loader.
It is beyond the scope of this documentation.
This time you need 5 wires to connect (VCC and Clock also).
</p>
<p>
<font color="red">Important: After flashing you need to wait 10 seconds before removing power.
During that time the EEPROM is initialized.
If you would read out the EEPROM, you will notice the letter 'C' at address 0.
Then you would know that the init process was finished with success.
</font>
</p>
<p>
If you use the DIP version of the ATTINY85, you cannot put it on the card itself.
The PCB in the picture on top of this page shows the DIP socket next to the actual card area.
Be sure to put the chip on the back side of the SIM card.
This works only if the phone does not completely enclose the card.
</p>
<p>
If you use the SOIC version of the ATTINY85, you need to make it flat, so it fits into your phone.
You may use the full size SIM or just the mini SIM.
I prefer the mini SIM and use an adapter card for larger phones.
</p>
<center><img src="sim-attiny85.jpg"/></center>
<p>
The original ATTINY85 (1) is shown upside down.
Bend the legs straight and shorten them, so they still fit into a programmer's socket. (2)
The use P400 sand paper to sand off the bottom of the case until you reach copper plate. (3)
Make a hole into the PBC and solder the chip upside down into that hole.
Pin 1 is marked on the PCB.
</p>
<p>
<font color="red">Important: You need to change clock source to pin 1.
</font>
</p>
<p>
Change lower fuse of 0xc0.
Note that you will not be able to do any further programming unless you apply clock signal to pin 1.
Use a crystal oscillator connected to pin 1 when you like to update the firmware in the future.
You may also use other type of clock signal.
Try something between 1 and 8 MHz.
I recommend to use the USBasp or a clone of that. It is cheap and easy and works with USB.
To set the fuses using "avrdude" in conjunction with "usbasp" flash tool, use:<br>
<br>
avrdude -c usbasp-clone -p t85 -U lfuse:w:0xc0:m -U hfuse:w:0xdf:m -U efuse:w:0xff:m<br>
<br>
If you run it again, you might notice that there is no response without a clock applied.
Apply a clock to pin 1 and see if you get a response again.
</p>
<p class="toppic">
<a name="usage"></a>
Using the SIM Card
</p>
<p>
After powering up the phone with SIM adapter/emulator attached, the phone should show the default subscriber number (FUTLN) on the display.
(Not all phones do. Read the manual to get the key code on how to show the subscriber number.)
There is no PIN enabled by default, so the SIM card is ready after inserting or poweing up the phone.
Now you can make calls, add telephone numbers or change PIN.
</p>
<p>
The SIM card can emulate 8 different cards.
They share the same telephone directory, but have different subscriber data.
Subscriber data can be changed to anything you like.
This way it is possible to even emulate service cards ("Wartungskarten"), to put phones into service mode or special cell monitor mode.
</p>
<p>
If the PIN is disabled (default), the first card with first subscriber data is emulates.
To select different card with dfferent subscriber data, change the PIN to 0001 .. 0008.
Refer to the phone's manual on how to change the PIN.
E.g. if you store the PIN 0000 or 0001, the first card with the first subscriber data is emulated.
E.g. if you store the PIN 0005, the fifth card with the fifth subscriber data is emulated.
In all cases, there is no PIN required when you turn on the phone.
</p>
<p>
<table class="sim">
<tr><th>PIN</th><th>FUTLN =<br>Subscriber</th><th>Sicherungs-<br>code</th><th>Karten-<br>kennung</th><th>Sonderheiten-<br>schl&uuml;ssel</th><th>Wartungs-<br>schl&uuml;ssel</th></tr>
<tr><td>0000 or 0001</td><td>2222001</td><td>3103</td><td>3</td><td>0</td><td>65535</td></tr>
<tr><td>0002</td><td>2222002</td><td>3103</td><td>3</td><td>0</td><td>65535</td></tr>
<tr><td>0003</td><td>2222003</td><td>3103</td><td>3</td><td>0</td><td>65535</td></tr>
<tr><td>0004</td><td>2222004</td><td>3103</td><td>3</td><td>0</td><td>65535</td></tr>
<tr><td>0005</td><td>2222005</td><td>3103</td><td>3</td><td>0</td><td>65535</td></tr>
<tr><td>0006</td><td>2222006</td><td>3103</td><td>3</td><td>0</td><td>65535</td></tr>
<tr><td>0007</td><td>2222007</td><td>3103</td><td>3</td><td>0</td><td>65535</td></tr>
<tr><td>0008</td><td>2222008</td><td>3103</td><td>3</td><td>0</td><td>65535</td></tr>
</table>
</p>
<p>
You may want to use a PIN to select the card whenever you turn on the phone.
Use the phone to enable a PIN that does not start with "000".
When you restart your phone, you may enter that PIN, to select the first card.
Alternatively you may enter the PIN 0000 or 0001, to select the first card, no matter what the PIN was.
Or you may enter the PIN 0002 .. 0008, to select second to eight card.
</p>
<p>
You may also alter each of the 8 different subscriber data store on the SIM.
In order to do that, you need to set a PIN, so the phone will ask for a PIN whenever it is turned on.
Choose any PIN you like, but not a PIN stat starts with 000.
Turn on the phone and you will be asked for a PIN.
Enter the PIN 9991 to alter the first subscriber data.
Enter the PIN 9992 .. 9998 to alter second to eigtht subscriber data.
The subscriber data is shown in the telephone directory and can be altered by changing the numbers in that directory.
</p>
<p>
The default subscriber data and where to change them in the telephone directory:
<br><br>
<table class="sim">
<tr><th>Entry</th><th>Name</th><th>Number</th></tr>
<tr><td>01</td><td>FUTLN</td><td>2222001 *</td></tr>
<tr><td>02</td><td>Sicherungscode</td><td>3103</td></tr>
<tr><td>03</td><td>Kartenkennung</td><td>3</td></tr>
<tr><td>04</td><td>Sonderheitsschl.</td><td>0</td></tr>
<tr><td>05</td><td>Wartungsschl.</td><td>65535</td></tr>
</table>
<br>
(*) When PIN 9991 was entered.
</p>
<p class="toppic">
<a name="service"></a>
Service Cards
</p>
<p>
To program one of the following service cards, change the subscriber data to the indicated values.
</p>
<p>
<table class="sim">
<tr><th>Type</th><th>FUTLN =<br>Subscriber</th><th>Sicherungs-<br>code</th><th>Karten-<br>kennung</th><th>Sonderheiten-<br>schl&uuml;ssel</th><th>Wartungs-<br>schl&uuml;ssel</th></tr>
<tr><td>Siemens C5<br>service mode</td><td>-</td><td>-</td><td>-</td><td>900</td><td>1000</td></tr>
<tr><td>Phillips Miniporty<br>service mode</td><td>-</td><td>-</td><td>-</td><td>900</td><td>1000</td></tr>
<tr><td>Phillips Miniporty<br>cell monitor</td><td>-</td><td>-</td><td>-</td><td>900</td><td>1728 or<br>2729</td></tr>
<tr><td>Phillips Porty<br>service mode</td><td>0</td><td>0</td><td>0</td><td>2304</td><td>-</td></tr>
<tr><td>Phillips Porty<br>cell monitor</td><td>-</td><td>-</td><td>-</td><td>898</td><td>-</td></tr>
</table>
</p>
<hr><center>[<a href="index.html">Back to main page</a>]</center><hr>
</td></tr></table></center>
</body>
</html>

BIN
docs/sim.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

View File

@ -91,6 +91,15 @@ table.imts {
border-collapse: collapse;
}
table.sim {
width: 816;
text-align: left;
table-layout: fixed;
border-spacing: 0px;
border: none;
border-collapse: collapse;
}
li {
font-size: 20;
color: #000000;

View File

@ -27,7 +27,7 @@ The following test signals are supported:
</p>
<p>
<font color="red">Importaint: SDR is required! It must be capable of about 15 Mega samples per second.</font>
<font color="red">Important: SDR is required! It must be capable of about 15 Mega samples per second.</font>
<br><br>
If you use LimeSDR, you MUST use USB 3.0 to have enough bandwidth!
</p>

1786
layout/telekarte.brd Executable file

File diff suppressed because it is too large Load Diff

BIN
layout/telekarte.pdf Executable file

Binary file not shown.

4796
layout/telekarte.sch Executable file

File diff suppressed because it is too large Load Diff

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