Besser ist es, wenn der Zugang durch ein Passwort (oder eine PIN), die nur der Eigner der Karte kennt, geschützt ist. Dieses Passwort darf nicht auf der Karte vorliegen (zumindest nicht in einem ungeschützten Speicherbereich). Deswegen gehen wir folgendermaßen vor:
Der "Zugangsadministrator" startet das Programm mfrc_write_name_hash_1.py (s. u.) und lässt den Karten-Nutzer seinen Namen und sein gewünschtes Passwort am Terminal eingeben. Das Programm schreibt den Namen in Block 4 der Karte. Aus dem Passwort berechnet es nun mit einem Hash-Algorithmus einen so genannten Hash-Wert, welcher nun in Block 5 der Karte gespeichert wird. Außerdem fügt der Zugangsadministrator (der das Passwort NICHT erfährt) diesen Hash-Wert zusammen mit dem Namen in die Datenliste des Zugangs-Programms ein.
Wichtig ist: Das Passwort kann nicht aus dem Hash-Wert rekonstruiert werden. Zudem führen selbst Passwörter, die sich nur in einem einzigen Zeichen unterscheiden, zu sehr unterschiedlichen Hash-Werten. Umgekehrt kann aber mit demselben Algorithmus aus dem Passwort der Hash-Wert neu berechnet werden. Das nutzt das für die Zugangskontrolle eingesetzte Programm mfrc_access_control_2.py aus: Sobald der Kunde seine RFID-Karte vor das RFID-Modul hält, liest es den Namen und den Hash-Wert aus. Nun fragt es nach dem Passwort. Dieses muss der Kunde nun am Terminal eingeben. (Wer möchte, kann statt des Terminals ein anderes Gerät zur Eingabe benutzen; so könnte z. B. über das TM1638-Anzeige-Taster-Modul numerische Passwörter eingegeben werden. Damit würde die Einheit TTGO - RFID-Modul - TM1638 unabhängig von einem PC als Zugangskontrolle arbeiten können. Der Einfachheit halber verfolgen wir diesen Weg an dieser Stelle aber nicht weiter.) Unser Programm für die Zugangskontrolle berechnet nun aus diesem Passwort mit demselben Hash-Algorithmus den Hashwert und vergleicht ihn mit dem Wert, der in der Datenliste vorliegt. Stimmen beide Werte überein, so wird ein Zugang gewährt (Grüne LED leuchtet auf, vgl. Abb. 1.) oder nicht (Rote LED leuchtet auf.).
Hier der Source-Code von mfrc_write_name_hash_1.py:
Code: Alles auswählen
# mfrc_write_name_hash_1.py für TTGO
# liest die UID des Tags (RFID-Karte/Chip) und gibt sie auf dem Display und dem Terminal aus
# speichert einen eingegebenen Benutzer-Namen (maximal 16 Zeichen, keine deutschen Sonderzeichen) auf der RFID-Karte ab (Block 4).
# verwandelt ein eingegebenes Passwort in einen 16-Byte-Hashwert und speichert diesen auf der RFID-Karte ab (Block 5).
# Quelle: https://github.com/Tasm-Devil/micropython-mfrc522-esp32; ergänzt und modifiziert für TTGO-T-Display
# Initialisierungen...
from sha256 import make_short_hash256
from machine import Pin, SPI, SoftSPI
import st7789
import vga2_16x16 as font1 # mit Umlauten
import vga2_8x16 as font2
from os import uname
release = uname().release[0:4]
id = 2 # z. B. für V 1.12 und V 1.14
if release == '1.18':
id = 1
spi = SPI(id, baudrate=20000000, polarity=1, sck=Pin(18), mosi=Pin(19)) # 1. Parameter = 1 für Firmware v1.18
display = st7789.ST7789(spi, 135, 240, reset=Pin(23, Pin.OUT), cs=Pin(5, Pin.OUT), dc=Pin(16, Pin.OUT), backlight=Pin(4, Pin.OUT), rotation=3)
display.init()
# from time import sleep_ms
from mfrc522 import MFRC522
sck = Pin(17, Pin.OUT)
mosi = Pin(15, Pin.OUT)
miso = Pin(12, Pin.OUT)
softspi = SoftSPI(baudrate=100000, polarity=0, phase=0, sck=sck, mosi=mosi, miso=miso)
sda = Pin(25, Pin.OUT) # (SDA = ChipSelect)
# Funktionen...
def umlaute_ersetzen(zk):
zk = zk.replace(b'Ä', b'\x8e')
zk = zk.replace(b'ä', b'\x84')
zk = zk.replace(b'Ö', b'\x99')
zk = zk.replace(b'ö', b'\x94')
zk = zk.replace(b'Ü', b'\x9a')
zk = zk.replace(b'ü', b'\x81')
zk = zk.replace(b'ß', b'\xe1')
return zk
def komplettiere(b):
while len(b) < 16:
b = b + b' '
return b
def do_write(data):
rdr = MFRC522(softspi, sda)
block = 4 # mit Block 4 starten
try:
while True:
(stat, tag_type) = rdr.request(rdr.REQIDL)
if stat == rdr.OK:
(stat, raw_uid) = rdr.anticoll()
if stat == rdr.OK:
# print("New card detected")
# print(" - tag type: 0x%02x" % tag_type)
uid = '0x%02x%02x%02x%02x' % (raw_uid[0], raw_uid[1], raw_uid[2], raw_uid[3])
# print(' - uid: ', uid)
display.text(font1, uid, 5, 50)
# print('BlockNr: ', block)
display.text(font1, 'Block: ' + str(block), 5, 90)
new_data = data[block-4]
# print('block: ', block, ' new_data: ', new_data)
if rdr.select_tag(raw_uid) == rdr.OK:
key = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF]
if rdr.auth(rdr.AUTHENT1A, block, key, raw_uid) == rdr.OK:
stat = rdr.write(block, new_data)
rdr.stop_crypto1()
if stat == rdr.OK:
print('Daten von Block', str(block), 'auf Karte geschrieben')
else:
print('Daten konnten nicht auf Karte geschrieben werden')
else:
print('Authentifikationsfehler')
block += 1
if block > 5:
break
else:
print('Tag nicht vorhanden')
print('Karte!')
print('Datenübertragung abgeschlossen')
except KeyboardInterrupt:
print('Tschüss')
display.text(font1, umlaute_ersetzen(b'Tschüss! '), 5, 90)
# Hauptprogramm...
display.fill(0)
display.text(font1, 'RFID-RC522', 5, 10)
display.text(font1, 'Karte!', 5, 50)
display.text(font2, 'Exit mit Strg-C', 5, 90)
# Namen
name = input('Name (max. 16 Zeichen, keine deutschen Sonderzeichen): ')
name = bytes(name, 'UTF-8') # rdr.write erwartet Bytes-Objekt
# Zeichenkette soll genau 16 Zeichen besitzen...
if len(name) > 16:
print('Text wird gekürzt!')
name = name[:16]
else:
name = komplettiere(name)
# Passwort/Hash
passwort = input('Passwort (max. 16 Zeichen, keine deutschen Sonderzeichen): ') # später: Hash-Wert
hash_str = make_short_hash256(passwort)
print('Hash: ', hash_str)
hash_str = bytes(hash_str, 'UTF-8') # rdr.write erwartet Bytes-Objekt
data = [name, hash_str]
do_write(data)
Code: Alles auswählen
# mfrc_access_control_2.py für TTGO
# liest Block 4 (Name) und Block 5 (Hash-Wert) von der Karte*)
# zeigt Namen auf dem Display an
# verlangt Passwort-Eingabe (über die Tastatur) und bildet davon den Hash-Wert
# vergleicht die beiden Hashwerte und gewährt Zugang (Grüne LED leuchtet für 3 Sekunden), ansonsten wird er verweigert (Rote LED leuchtet für 3 Sekunden).
# *) zum Schreiben dieser Karte das Programm mfrc_write_name_hash.py benutzen (generiert automatisch aus dem eingegeben Passwort einen Hashwert)
# Initialisierungen...
from sha256 import make_short_hash256
from time import sleep_ms
from machine import Pin, SPI, SoftSPI
from mfrc522 import MFRC522
# Display initialisieren
import st7789
import vga2_16x16 as font1
import vga1_8x16 as font2
from os import uname
release = uname().release[0:4]
id = 2 # z. B. für V 1.12 und V 1.14
if release == '1.18':
id = 1
spi = SPI(id, baudrate=20000000, polarity=1, sck=Pin(18), mosi=Pin(19)) # 1. Parameter = 1 für Firmware v1.18
display = st7789.ST7789(spi, 135, 240, reset=Pin(23, Pin.OUT), cs=Pin(5, Pin.OUT), dc=Pin(16, Pin.OUT), backlight=Pin(4, Pin.OUT), rotation=3)
display.init()
display.fill(0)
# RFID initialisieren
sck = Pin(17, Pin.OUT)
mosi = Pin(15, Pin.OUT)
miso = Pin(12, Pin.OUT)
softspi = SoftSPI(baudrate=100000, polarity=0, phase=0, sck=sck, mosi=mosi, miso=miso)
sda = Pin(25, Pin.OUT)
# LEDs initialisieren
gruene_LED = Pin(27, Pin.OUT)
rote_LED = Pin(26, Pin.OUT)
# Daten
datenliste = [['Anne ', 'f3a000752ccc201a'], # Passwort???
['Bert ', 'cd89a4f160666ba1'], # Passwwort: qwert123
['Georg ', '958d51602bbfbd18'] # Passwort: 123456
] # Die Zeichenketten müssen jeweils aus 16 Zeichen bestehen
# Funktionen...
def umlaute_ersetzen(zk):
zk = zk.replace(b'Ä', b'\x8e')
zk = zk.replace(b'ä', b'\x84')
zk = zk.replace(b'Ö', b'\x99')
zk = zk.replace(b'ö', b'\x94')
zk = zk.replace(b'Ü', b'\x9a')
zk = zk.replace(b'ü', b'\x81')
zk = zk.replace(b'ß', b'\xe1')
return zk
def access(name, hashwert, datenliste):
if [name, hashwert] in datenliste: # falls name mit hashwert in datenliste auftaucht
print('Zugang!')
gruene_LED.value(1) # gruene LED einschalten
else: # sonst ...
print('Kein Zugang')
rote_LED.value(1) # rote LED einschalten
print('Karte entfernen!')
sleep_ms(3000)
gruene_LED.value(0) # LEDs ausschalten
rote_LED.value(0)
def make_to_string(l):
bs = bytes(l)
return str(bs, 'UTF-8')
def do_read(block):
weiter = True
try:
while weiter:
rdr = MFRC522(softspi, sda)
uid = ""
(stat, tag_type) = rdr.request(rdr.REQIDL) # liefert stat (stat = 2 > Err; stat = 0 > OK)
if stat == rdr.OK: # Karte/Chip vorhanden
(stat, raw_uid) = rdr.anticoll()
# UID lesen und anzeigen:
if stat == rdr.OK:
uid = ('0x%02x%02x%02x%02x' % (raw_uid[0], raw_uid[1], raw_uid[2], raw_uid[3])) # setzt 4 Bytes zu einem "HEX-String" zusammen
# print('raw_uid: ', raw_uid) # dezimal
# print('raw_uid: ', [hex(n) for n in raw_uid]) # hexadezimal
# display.text(font1, uid, 10, 50)
sleep_ms(100)
# Block lesen und anzeigen
if rdr.select_tag(raw_uid) == rdr.OK: # Karte selektieren
key = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] # Schlüssel für Authentifizierung
if rdr.auth(rdr.AUTHENT1A, block, key, raw_uid) == rdr.OK: # wenn Authentifizierung (KeyA) erfolgreich ist...
newdata = rdr.read(block) # Block mit der der Blocknummer "block" lesen und in newdata speichern
# print('Block['+str(block) + ']: ', newdata) # Ausgabe dezimal
# print('Block['+str(hex(block)) + ']: ', [hex(n) for n in newdata]) # Ausgabe hexadezimal
# print('Block['+str(block) + ']: ', make_to_string(newdata)) # Ausgabe als Zeichenkette
return make_to_string(newdata)
rdr.stop_crypto1()
weiter = False
else:
print('Authentifizierungsfehler') # falls Authentifizierung nicht erfolgreich
else:
print('Tag nicht vorhanden')
print()
else:
print('Karte!')
except KeyboardInterrupt:
print()
print("Tschüss")
display.text(font1, umlaute_ersetzen(b'Tschüss! '), 5, 90)
# Hauptprogramm...
print('Warte auf Karte')
print('Exit mit Strg-C')
print()
display.text(font1, 'RFID-RC522', 30, 10)
display.text(font1, 'Karte!', 5, 50)
display.text(font2, 'Exit mit Strg-C', 5, 90)
try:
name = do_read(4)
print('Name: ', name)
display.text(font2, 'Hallo ' + name, 5, 50)
hashwert = do_read(5)
passwort = input('Passwort: ')
access(name, make_short_hash256(passwort), datenliste)
except KeyboardInterrupt:
print()
print("Tschüss")
display.text(font1, umlaute_ersetzen(b'Tschüss! '), 5, 90)
Beide Programme befinden sich auch im Anhang, zusammen mit dem für die Hash-Funktion benötigten Modul sha256.py.
.