# MQTT-RFID Reader

## Allgemein

Das Projekt beschreibt ein RFID Lesegerät welches über MQTT in einer IT-Infrastruktur für offene Werkstätten eingebunden werden kann, z. B. für die Freischaltung von Maschinen.

Ansatz:
- Mit einer RFID-Karte sich eine Person an einem Lesegerät in einem MQTT-Netzwerk identifizieren. In Abhängigkeit der damit verbundene Anwendungen können hiermit Maschinen freigeschaltet, Türen / Fächer aufgeschlossen oder Programme angesteuert werden. Dabei stellt das Lesegerät nur die Kommunikation mit der RFID-Karte und mit dem Anwender (Über ein Display) zur Verfügung. Aktuatoren und Netzschalter sind nicht vorgesehen.


Funktionsumfang:
- Lesen von UID und Identifikation von RFID-Karten verschiedener Bauart (Mifare Classic, DESFire, Ultralight C)
- Beschreiben von Mifare Classic mit einem Zeitstempel
- Kommunikation der Daten über MQTT (JSON Objekt) an Broker
- Anzeige von Statusmeldungen (intern) und Nachrichtigen (über MQTT empfangen)
- Konfiguration (Netzwerkeinstellung, Funktionalität) über HTML in Access Point Modus
- Anbindung über Wifi oder Ethernet
- Einbindung einer Taste zur Interaktion mit dem Anwender (z. B. zur Besätigung o. ä.)
- Buzzer

Aufbau:
- RC522 standard Modul
- '0.96 OLED Display
- ENC28J60 LAN Modul
- ESP-12 Modul (ESP8266)
- Interface-Platine
- 3D Druck Gehäuse
- ESP-01 Programmierer (nur zum Programmieren) 

## Funktion

Des Konzept lässt sich in mehrere Teilbereiche unterteilen:

### 1. Kommunikation mit RFID-Karten
Das Ziel ist, ein System zur Verfügung zu stellen, welches möglichst wenig anfällig für misbrauch ist, aber dennoch preiswert bleibt. Hierzu gitb es folgende Konzepte:
 1. Verwendung von Mifare Classic Karten - Die Karten sind klonbar und bieten daher die geringste Sicherheit 
 2. Verwendung von Mifare Classic Karten mit Zeitstempel der in einer Datenbank abgelegt wird. Sicherer, da eine geklonte Karte nur einmal verwendet werden kann. Dannach weichen der Zeitstempel auf dem Original und dem Klon von einander ab, und das Vorhandensein zweier Karten kann erkannt werden. Das ganze setzt eine stabile Kommunikation zwischen Lesegerät und Datenbank voraus.
3. Verwendung von DESFire Karten. Karten sind teuer und die Empfindlichkeit des Lesegerätes verringert sich. Die Kommunikation mit den DESFire Karten erfolgt OTA (Over The Air), d.h. der Server kommuniziert direkt mit der Karte, der Reader schiebt nur die Anfragen und Antworten hin nund her, der Reader versteht diese (teilweise verschlüsselte) Kommunikation nicht.
4. Verwendung von Mifare Ultralight C Karten. Die Karten sind günstiger, auch hier leidet die Empfindlichkeit. Da auch normale Mifare Ultralight Karten geklont werden können, muss die Ultralight "C" Karte mit dem Werkstatt eigenen Schlüssel gesperrt sein. Dieser Schlüssel muss dem Reader vom Server vorher zugeschickt werden (wird also nicht auf der Reader eingespeichert).

Alle 4 Optionen werden vom Lesegerät unterstützt. Die Option kann über die Konfigurationsseite des Lesegerätes eingestellt werden.

Das Szenario, dass die Karte unerlaubt weitergegeben wird, läßt sich nicht alleine über die Wahl der RFID Technologie lösen, daher bleibt ein gewisses Restrisiko von Missbrauch.

Zur Zeit wird die Karte einmal gelesen und dann vom Lesegerät abgeschaltet. Eine Option, dass die Anwesenheit der Karte kontinuierlich überprüft wird, könnte bei Bedarf hinzugefügt werden.

### 2. Kommunikation über MQTT
Das Lesegerät kann über die Konfigurationsseite entweder über LAN oder über WLAN in einem Netzwerk eingebunden werden. Die IP-Adresse des Brokers muss, zusammen mit der Geräte-ID ebenfalls manuell über die Konfigurationsseite eingegeben werden. Über standardisierte Adressen (Topics), in der die ID des Lesegerätes enthalten ist, werden Nachrichten (Payload) verschickt. Das Gerät abonniert ebenfalls ein standardisiertes Topic um Nachrichten empfangen zu können.
Die Kommunikation von Nachrichten erfolgt als JSON Objekt. Nur die UID kann auch als Klartext übermittelt werden (über die Konfigurationsseite einstellbar).
Nach erfolgreichem Lesen der RFID Karte wird die UID und, wenn eingestellt der alte und neue Zeitstempel an den Broker übermittelt. 
Das Lesegerät kann eine Reihe von standard-Nachrichten anzeigen (die intern auf das Display passend hinterlegt wurden) oder kann auch Klartext-Nachrichten empfangen und anzeigen. Hierzu enthält die empfangene MQTT-Nachricht immer ein "identifizierer" der Nachricht und eine Klartextnachricht. Je nachdem ob der Identifizierer im Lesegerät belegt ist, wird die passend formatierte Nachricht angezeigt oder die unformatierte Klartextnachricht.
Jede Nachricht hat ebenfalls einen Zusatztext, mit dem zusätzliche Varibale Informationen wie Dauer, Uhrzeit, User etc. zu einer Nachricht gezeigt werden können.

JSON object = {"Cmd": "message", "MssgID": 4 , "ClrTxt":"Hello World" , "AddnTxt":"3:00"}
 
### 3. Konfiguration des Gerätes
Das Gerät verfügt über einen Eingang zum Anschluss eines Tasters. Wird diese Taste während des Bootvorgangs betätigt, startet das Lesegerät im Web-AP Modus. Nach Anmeldung in das Netzwerk (mit den im Quellcode festgelegten Daten) findet man auf der Home-Webseite des Gerätes (192.168.4.1/) eine leere Konfigurationsseite. Hier können SSID und Passwort des Werkstatt-Netzwerks, Broker-IP, Geräte-ID, LAN/WLAN und RFID Optionen definiert werden. Die Optionen werden gespeichert und werden beim nächsten Bootvorgang verwendet.

### 4. MQTT-Befehle und Parameter
Nachrichten werden immer als JSON paket übermittelt. Dabei ist der Befehl "Cmd" immer das erste Element. Folgende Befehle sind für "Cmd" definiert:
- "message" - Nachricht auf dem Display zeigen
- "sendPICC" - Daten an DESFire Karte im APDU format schicken
- "haltPICC" - DESFire Karte wird abgemeldet
- "Key" - Zugriffschlüssel für den Zugriff auf Mifare C Karten
- "ConfirmUser" - Reader fordert den Nutzer (durch Fiepen) auf, seine Anwesenheit bei der Maschine zu besätigen (TODO)


#### "message"
Nachricht auf dem Display. 
Beispiel: {"Cmd": "message", "MssgID": 4 , "ClrTxt":"Hello World" , "AddnTxt":"3:00"}

| Befehl  | Parameter | Format  |           Bedeutung              | 
| ------- | --------- | ------- | -------------------------------- | 
|  "Cmd"  | "message" |  String | Nachricht auf dem Display anzeigen |
| "MssgID" | zahl  |  int | vordefinierte Nachricht anzeigen. Die Nachricht wird im QUellcode definiert. Hier wird eine, zum verendeten Display sinnvollen Zeilenumbruch definiert. |
| "ClrTxt"| Freitext | String | Nachricht in Klartext. Wird ignoriert wenn die Nachricht im Reader vordefiniert ist. Ansonsten wird die Nachricht "irgendwie" auf dem Display angezeigt. |
|"AddnTxt"| Freitext | String | Zusatztext, der auf dem Display angezeigt werden soll. Hier können zusätzlich zu fest definierte Nachrichten Ergänzungen wie Uhrzeit, Dauer, Nutzungskosten oder User angezeigt werden. ACHTUNG: das € Zeichen wird auf einige Displays nicht dargestellt. |

Vordefinierte Nachrichten sind:

| Nr.| Anzeigetext | Zeilenumbruch bei | englischen Text |
| -- | ----------- | ------------- | --------------- |
| 0  | "verfügbar"                           | 0 | equipment available      |
| 1  | "reserviert"                          | 0 | equipment reserved       |
| 2  | "reserviert ab"                       | 13 | reserved starting from  |
| 3  | "in Benutzung"                        | 0 | equipment in use         |
| 4  | "freigegeben"                         | 0 | access granted           |
| 5  | "Anmeld. fehlgeschlagen"              | 8 | login error              |
| 6  | "Nutzungsgebühr "                     | 15 | usage fee               |
| 7  | "Nutzung nicht erlaubt"               | 13 | operation not allowed   |
| 8  | "Nutzung auf eigenem Risiko"          | 11 | operation at own risk   |
| 9  | "gesperrt durch"                      | 14 | equipment locked by     |
| 10 | "freizugeben durch"                   | 11 | to be released by       |
| 11 | "außerhalb der Nutzungzeit"           | 14 | outside ooperating hours |
| 12 | "Nutzungsdauer"                       | 13 | duration of use         |
| 13 | "abgemeldet"                          | 0 | logged out               |
| 14 | "abschalten in"                       | 13 | equipment will lock down |
| 15 | "Karte gesperrt"                      | 0 | card locked              |
| 16 | "Karte unbekannt"                     | 0 | card unknown             |
| 17 | "Berechtigung endet"                  | 12 | training to expire      |
| 18 | "Ruhemodus"                           | 0 | sleep mode               |
| 19 | "Aufsicht bestätigen"                 | 8 | confirm presence         |


#### "sendPICC"
APDU  Paket an DESFire Karte schicken. 
Beispiel: {"Cmd": "sendPICC", "data": "9060000000"} - getVersion Befehl 0x60

| Befehl  | Parameter | Format  |           Bedeutung              | 
| ------- | ---------- | ------- | -------------------------------- | 
|  "Cmd"  | "sendPICC" |  String | Daten als APDU kommando an DESFire Karte schicken |
| "data" | APDU Paket  |  String | Komplettes APDU Paket als lesbarer Hex-Zeichen. für A bis F können Groß- oder Kleinbuchstaben verwendet werden. |

Der Reader antwortet mit einem APDU-Paket z.B. 
{"Cmd":"readPICC","data":"04010112001A0591AF"}. 
Dabei sind die letzten zwei Bytes die Statusbytes (0x91AF = es gibt noch mehr Daten -> DESFire Statusmeldungen)

#### "haltPICC"
Die kommunikation mit der DESFire Karte wird beendet. Dieser Befehl ist nur bei DESFire Karten notwendig, da Mifare Classic und Mifare Ultralight C Karten automatisch abgemeldet werden. 
Beispiel: {"Cmd": "haltPICC"} 

| Befehl  | Parameter | Format  |           Bedeutung              | 
| ------- | ---------- | ------- | -------------------------------- | 
|  "Cmd"  | "haltPICC" |  String | Kommunkation zwischen Reader und DESFire Karte wird beendet |

#### "Key"
Da der der Schlüssel der WErkstatt spezifisch für die Kodierung von Mifare Ultralight C Karten festgelegt wurde möglichst nicht in der Quellcode auftauchen sollte, muss dieser über den Server bereitgestellt werden. Dieser Befehl übermittelt den Schlüssel. Über einen im Server festgelegten Prozess muss sichergestellt werden, dass dieser Schlüssel nur dann verschickt wird, wenn auch sicher ein authorisiertes Lesegerät vorhanden ist. Dieser Befehl ist nur für Mifare Ultralight C Karten.

Beispiel: {"Cmd": "Key", "data": 49454D4B41455242214E4143554F5946} (Mifare Ultralight C standard Schlüssel)

| Befehl  | Parameter | Format  |           Bedeutung              | 
| ------- | ---------- | ------- | -------------------------------- | 
|  "Cmd"  | "Key" |  String | Schlüssel wird übermittelt |
| "data" | 32 Hex Zeichen | String | Zugriffsschlüssel für die Mifare Ultralight c Karte. Im Reader werden die ersten 8 Bytes (erst 16 Zeichen) hinten angehängt um einen 24 Byte schlüssel zu erzeugen|


#### "ConfirmUser" (ToDo)
Fordert den Nutzer der Maschine durch lautes Fiepen auf die Anwesenheitstaste betätigen.
Beispiel: {"Cmd": "ConfirmUser"} 

| Befehl  | Parameter | Format  |           Bedeutung              | 
| ------- | ---------- | ------- | -------------------------------- | 
|  "Cmd"  | "ConfirmUser" |  String | Reader fängt an zu fiepen |

Die Angezeigte Nachricht, dass der Nutzer seine Aufsicht bestätigen soll (Nachricht 19), muss separat an den Reader verschickt werden.

Der Reader antwortet mit einem APDU-Paket (ToDo) 
{"Cmd":"UserConfirmed"}.

## Sicherheit des Systems
Das System kann den Zutritt verwalten, Maschinen freischalten die eine Sicherheitseinweisung/Schulung erfordern oder es kann dazu genutzt werden Nutzungszeiten oder Bezahlvorgänge indirekt zu erfassen. 
Zur Vermeidung von Diebstahl, gefährliche Nutzung von Maschinen oder das Abrechnen von Leistung auf Kosten anderer sind von Hardwareseite zwei Szenarien zu betrachten:
- Es wird eine RFID-Karte dupliziert
- Es wird ein Lesegerät manipuliert 	

### 1. RFID Karten
#### UID
Die einfachste Art und Weise der Identifizierung über RFID ist die Verwendung der UID (Unique Idenification - Seriennummer) der Karte. Gleichzeitig ist das auch die unsicherste Variante! 
Es existieren eine vielzahl von "Magic Cards" die über verschiedene Lesegeräte neu konfiguriert werden können. Auf diese Weise kann die UID neu beschrieben werden. Da die UID beim ersten Lesevorgang der RFID-Karte bereits bekannt gegeben wird (auch wenn die Karte verschlüsselt ist), ist es ein leichtes die UID einer bestehende Karte zu ermitteln und diese auf einer "Magic Card" zu übertragen.
Als erste Schritt zur Erhöhung der Sicherheit kann man den Typ der gelesene Karte identifizieren (über den sogenannten SAK Wert). Auch hier gibt es mittlerweile "Magic Cards" bei denen sogar der SAK-wert geändert werden kann, und die Karte sich somit als einen beliebigen Typ identifiziert.
#### Karten Typ abfragen
Als zweiter Schritt sollte man daher Typ-spezifische Anfragen starten um sicher zu gehen, dass es sich auch tatsächlich um den vorgetäuschten Typ handelt. Identtifiziert sich die Karte als DESFire (SAK=0x20), kann man ein DESFire-spezifischen Befehl schicken. Nur so, läßt sich sicherstellen, dass es sich auch tatsächlich um eine Karte diesen Typs handelt. Diese Vorgehensweise reicht nur für den Typ Mifare Plus aus, für alle andere jedoch nicht, da es mittlerweile zu jede andere "sichere" Karte auch "Magic Cards" gibt.

#### Verschlüsselung
Als dritter Schritt bleibt somit nur die Freigabe übver eines geheimen Schlüssels oder die verschlüsselte Kommunikation mit der Karte übrig. Hier eigenen sich nur Mifare Classic, Mifare Ultralight C, Mifare Desfire und Mifare Plus, andere (Standard-) Karten sind unverschlüsselt und können daher kopiert werden. Bei der Verschlüsselung gilt es zwischen den System Crypto1 (Mifare Classic) und DES/AES (Ultralight C, DESFire, Mifare Plus) zu unterscheiden. Ersteres ist gehackt und die Daten können mit etwas Aufand dupliziert werden (inkl. Schlüssel). DES / AES ist in zusammenhang mit RFID noch nicht gehackt, kann also als sicher angesehen werden.
#### Zeitstempel
Eine alternative Methode ist die Verwendung eines Zeitstempels auf der Karte, der mit jedem Lesevorgang aktualisiert wurd und mit einem gespeicherten Zeitstempel abgeglichen wird. Dieser Zeitstempel kann sogar unverschlüsselt gespeichert werden, da nach dem Klonen einer Karte, nur einer der beiden Klone weiterverwendet werden kann. Die andere Karte ist nach der ersten Aktualisierung des Zeitstempels ungültig.

### 2. ESP8266 / ESP32
Im Gegensatz zu übliche Microcontroller werden beim ESP8266 Daten auf einem extenen Flash gespeichert. Somit werden auch die Daten auf den externen Bauteil gespeichert die für die Kommunikation, und somit für die Sicherheit des Systems relevant sind. Der ESP8266 bietet keinen Verschlüsselung dieser Daten, sie können daher aus dem Flash ausgelesen werden. Hierzu muss das Lesegerät zerlegt werden und der Flash separat ausgelesen werden. Da Netzwerkkennung im Lesegerät gespeichert sind, sollte der Diebstahl / Ausfall eines Lesegerätes somit kontinuierlich geprüft werden, und die Aktivierung des Lesegerätes über das System erfolgen. 
Der ESP32 verfügt im Gegensätz zum ESP8266 über eine Verschlüsselung der Daten auf dem Flash, allerdings gibt es auch hier bereits eine Methode diese Verschlüsselung zu umgehen. 
Der einzige Weg hier ein "Angriff" auf das System zu vermeiden ist, dass ein Lesegerät sich beim Server authentifizieren. Diese Authentifizierung darf nicht im Flash gespeichert sein (da dann ja wieder auslesbar). Daher ist angedacht, die Authentifizierung über eine separate Master-RFID Karte erfolgen zu lassen. Nur wenn diese Karte am Lesegerät gehalten wird wird das Gerät bei Server authentifiziert. Sollte das Mifare Ultralight C system verwendet werden, sollte auch erst dann der Schlüssel übertragen werden. 

### 3. TLS-verschlüsselung (ToDo)
Damit Daten in der Kommunikation nicht abgehört werden können ist geplant, dass die Kommunikation mit dem Server TLS-verschlüsselt wird. Diese TLS-verschlüsselung sollte idealerweise ebenfalls nicht im Flash gespeichert sein. Hier gibt es das Bestreben, die relevante Daten für die Verschlüsselung (Keys) ebenfalls per RFID-Karte auf die Lesegeräte zu übertragen. Dieser Vorgang kann dann gleichzeitig auch als Authentification des Lesegerätes dienen. 