mirror of
https://gitlab.com/fabinfra/fabhardware/fabreader.git
synced 2025-03-12 14:41:44 +01:00
Merge branch 'master' of gitlab.com:fabinfra/fabhardware/fabreader into master
This commit is contained in:
commit
3d50346a2e
@ -0,0 +1,11 @@
|
||||
# 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.
|
||||
|
||||
## Besonderheiten der Hardware
|
||||
Die Hardware besteht Hauptsächlich aus standard "China"-Module. Diese werden mittel einer eigenen Adapterplatine (Siehe Eagle-Dateien) "verdrahtet". Damit das in ein Gehäuse mit brauchbare Abmßen passt werden die Platinen als Sandwich angeordnet, d.h. die Platinen liegen teilweise übereinander. Das macht den Zusammenbau an der eine oder andere Stelle etwas herausfordernd. Daher gibt es die detaillierte Bauanleitung.
|
||||
|
||||
## Das RC522 Modul
|
||||
Damit das RC522 Modul überhaupt sinnvoll zum Einsatz kommen kann müssen die Induktionen L1 und L2 auf der Platinen gegen solche mit größerer Bauform ausgetauscht werden. Bei den verbauten Induktionen handelt es sich um zu klein dimensionierte Bauteilen. Geeignet sind folgende Induktionen: FERROCORE CW1008-2200. Nur dann werden Mifare Ultralight Typ C und DESFire karten zuverlässig gelesen.
|
@ -0,0 +1,77 @@
|
||||
# Software
|
||||
|
||||
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.
|
||||
|
||||
## Bibliotheken
|
||||
Abgesehen von der 3DES-Bibliothek sind alle verwendete Bibliotheken sind über den Bibliothekenmanager von Arduino zu finden.
|
||||
Die DES Bibliothek befindet sich [in github](https://github.com/Octoate/ArduinoDES)
|
||||
|
||||
## MQTT_Reader06
|
||||
Aktuelle Version der Software für das Lesegerät.
|
||||
|
||||
|
||||
|
||||
|
||||
## Ultralight C
|
||||
Sofware zum Beschreiben von Mifare Ultralight C Karten. Als Hardware reicht ein Arduino Uno und ein RC522 Modul. Die Daten werden über den Serialmonitor eingegeben.
|
||||
Das Programm eignet sich zum Schreiben von Daten, Ändern des Schlüssels und Festlegen des verschlüsselten Datenbereiches auf der Karte.
|
||||
|
||||
### Struktur von Ultralight C Karten
|
||||
Mifare Ultralight C karten sind, abgesehen von der Möglichkeit der Verschlüsselung relativ einfach strukturierte RFID-Karten.
|
||||
Die Karten umfassen 144 Bytes beschreibbare Datenfelder die in je 4 Bytes pro Seite (Page) organisiert sind. Der Zugriff erfolgt immer über eine Seite (Page). Die ersten 4 Seiten (0-3) sind mit UID, sogenannte Lock-Bits sowie mit einem OTP (one time programmable) Speicher belegt, dann folgen Datenfelder. Die letzten 8 Seiten sind ebenfalls mit Lock-bits, einen 16-bit Aufwärtszähler, die Zugriffskonfiguration und mit dem Zugriffsschlüssel belegt. Siehe hierzu das NPX Dokument [https://www.nxp.com/docs/en/data-sheet/MF0ICU2.pdf](https://www.nxp.com/docs/en/data-sheet/MF0ICU2.pdf).
|
||||
|
||||
Das erste Byte auf den Seiten der Zugriffskonfiguration legt fest wie die verschlüssleung der Karte sich auswirkt.
|
||||
1. Es kann ab einer selbst festzulegende Seite an aufwärts der Zugriff auf die Daten eingeschränkt werden. d.h. ohne vorherige Authentifizierung ist kein / nur einen eingeschränkten Zugriff möglich.
|
||||
2. Es kann festgelegt werden, ob die gesperrte Seiten gar nicht sichtbar sind, oder nur ein Lesezugriff erlauben.
|
||||
|
||||
Wichtig dabei ist, dass
|
||||
1. die Seiten in der der Schlüssel steht nicht ausgelesen werden können
|
||||
2. das überschreiben des Schlüssels nur verhindert werden kann, wenn die Seiten wo der Schlüssel steht (Seite 44 bis 47) auch durch die Zugriffkonfiguration gegen schreiben gesperrt sind.
|
||||
|
||||
### Befehle
|
||||
#### auth
|
||||
Der "auth" Befehl führt den 3DES Authorisierungsprozess mit der Karte durch. Dies ist erforderlich, wenn die Karte über einen Schlüssel gesichert ist, also die Zugriffskonfiguration bestimmte Seiten schützt (zum Beispiel den Schlüsselbereich, Seite 44-47).
|
||||
+ Beispiel: "auth 49454D4B41455242214E4143554F5946" (hierbei handelt es sich um den Standard-Schlüssel für Mifare Ultralight C Karten)
|
||||
|
||||
#### newKey
|
||||
Hiermit kann ein neuer Schlüssel definiert werden. Das Programm erwartet, dass man sich vorher mit dem aktuellen Schlüssel authorisiert hat. Der Schlüssel muss 16 Bytes (also 32 Hex-Zeichen) lang sein. Ist er kürzer, meckert das Programm, ist er länger, werden die letzten zeichen ignoriert.
|
||||
+ Beispiel: "newKey 49454D4B41455242214E4143554F5946"
|
||||
|
||||
#### dump
|
||||
lesbare Datenfelder der Karte werden ausgegeben, inklusive der Information ob diese gesperrt sind und ob und wie diese durch eine Authentifizierung geschützt sind. Gesperrte Seiten können nciht mehr beschrieben werden. Sollte noch keine Authentifizierung stattgefunden haben und es Seiten geben, die gegene lesen gesperrt sind, werden diese nicht angezeigt. Es sieht so aus, als hätte die Karte weniger Datenfelder. Nach einer erfolgreichen Authentifizierung sollten alle Datenfelder sichtbar sein.
|
||||
+ Beispiel: "dump"
|
||||
|
||||
#### wchar
|
||||
beschreiben der Karte mit einem Text ab einer definierten Seite. Die Buchstaben des Textes werden 1:1 auf die Karte geschrieben. Die Zahl nach dem Befehl gibt die Startseite (dezimal) an.
|
||||
+ Beispiel: "wchar 10 hello world"
|
||||
|
||||
#### whex
|
||||
beschreiben der Karte mit Hex-Werte ab einer definierten Seite. Die zeichen des Textes werden ejtzt als Hex-Zeichen interpretiert (00-FF -> 0x00 - 0xFF). Es werden immer zwei Zeichen für ein Byte benötigt. Es düfen nur Zahlen von 0-9 und Buchstaben von A-F bzw. a-f verwendet werden.
|
||||
+ Beispiel: "whex 10 0123456789ABC"
|
||||
|
||||
#### protect
|
||||
legt fest ab welcher Seite (Page) die Zugriffkonfiguration sich auswirkt. Eine Zahl 15 schränkt den Zugriff ab seite 15 aufwärts ein. Wird hier die Zahl 48 eingegeben, ist die Karte nicht geschützt. ACHTUNG: Zahlen kleiner 2 können zu unerwartetes Verhalten führe, da vom Programm auf den unteren Seiten zugegriffen wird um das Vorhandensein einer Karte zu prüfen.
|
||||
+ Beispiel: "protect 25" (ab Seite 25 aufwärts geschützt)
|
||||
|
||||
#### setpbit
|
||||
Konfirurationsbit (protection Bit) mit dem festgelegt wird ob die geschützte Seiten nur Lesezugriff haben (1) oder weder Lese- noch Schreibzugriff haben (0). Mögliche werte sind 1 oder 0
|
||||
+ Beispiel: "setpbit 1" (nur Lesezugriff).
|
||||
|
||||
Mit diesen Befehle sollte es möglich sein, eine Mifare Ultralight C Karte entsprechend zu konfigurieren um diese für den MQTT-Reader nutzen zu können.
|
||||
|
||||
### Möglicher Ablauf zum Personalisieren einer Karte
|
||||
|
||||
+ auth 49454D4B41455242214E4143554F5946 (authentifizieren)
|
||||
+ dump (anzeigen was schon auf der Karte ist, hier werden dann auch die Lock- und Konfigurationsbits ausgelesen)
|
||||
+ newKey 00112233445566778899AABBCCDDEEFF (neuer Schlüssel schreiben)
|
||||
+ protect 30 (ab Seite 30 Zugriff einschränken)
|
||||
+ setpbit 0 (weder Lese- noch Schreibzugriff)
|
||||
+ wchar 4 Name der Werkstatt (damit dennoch gelesen werden kann zu welcher Werkstatt die Karte gehört)
|
||||
+ dump (überprüfen ob alles auch richtig geschrieben ist)
|
||||
|
||||
ACHTUNG
|
||||
nach manche Vorgänge meldet sich die Karte selber ab. D.h. hin uns wieder muss man sich entweder neu authentifizieren oder den dump-Befehl ausführen, damit die Lock- und Konfigurationsbits gelesen werden.
|
||||
|
||||
|
||||
|
||||
|
739
src/Ultralight_C.ino
Normal file
739
src/Ultralight_C.ino
Normal file
@ -0,0 +1,739 @@
|
||||
/*
|
||||
* --------------------------------------------------------------------------------------------------------------------
|
||||
* Example sketch/program showing how to read and modify data from a Mifare Ultralight C PICC
|
||||
* --------------------------------------------------------------------------------------------------------------------
|
||||
*
|
||||
* This code enables basic communication with a Mifare Ultralight C PICC.
|
||||
* It allows reading and writing of data including basic operations for password protection.
|
||||
*
|
||||
* This sketch imlements NO measures for page blocking and using OTP and the upwarts counter
|
||||
*
|
||||
* This sketch should run stable on a Arduinno Uno with RC522 module.
|
||||
* But the code itself is very sensitive for modifications due to my bad programming
|
||||
* or stability issues with the used libraries. If you change / optimize the code, be
|
||||
* prepared for some weird complications
|
||||
*
|
||||
* have fun!
|
||||
*
|
||||
* @license Released into the public domain.
|
||||
*
|
||||
* use
|
||||
* - auth 49454D4B41455242214E4143554F5946 for authentication (with standard key)
|
||||
* - newKey 49454D4B41455242214E4143554F5946 for writing a new key (in this case the standard key) to the PICC.
|
||||
* - dunp to list the content of the PICC (data might not visible due to read-protection)
|
||||
* - wchar 10 hello world to write "hello world" starting at page 10
|
||||
* - whex 10 0123456789ABC to write HEX values starting at page 10
|
||||
* - protect 25 to protect the page from 25 upward
|
||||
* - setpbit 1 to set the protection to write-protected
|
||||
*
|
||||
*
|
||||
* Typical pin layout used:
|
||||
* -----------------------------------------------------------------------------------------
|
||||
* MFRC522 Arduino Arduino Arduino Arduino Arduino
|
||||
* Reader/PCD Uno/101 Mega Nano v3 Leonardo/Micro Pro Micro
|
||||
* Signal Pin Pin Pin Pin Pin Pin
|
||||
* -----------------------------------------------------------------------------------------
|
||||
* RST/Reset RST 9 5 D9 RESET/ICSP-5 RST
|
||||
* SPI SS SDA(SS) 10 53 D10 10 10
|
||||
* SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16
|
||||
* SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14
|
||||
* SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15
|
||||
*/
|
||||
|
||||
/*
|
||||
* IMPORTANT!!!
|
||||
* in order to be able to read Mifare Ultralight C PICC the typical RC522-module needs to be modified.
|
||||
* L1 and L2 must be exchanged for better inductors (e.g. FERROCORE CW1008-2200). Otherwise this code will not work!
|
||||
*/
|
||||
|
||||
/*
|
||||
* The Mifare Ultralight C encrypted authentification process can be found here: https://www.nxp.com/docs/en/data-sheet/MF0ICU2.pdf
|
||||
*/
|
||||
|
||||
#include <SPI.h>
|
||||
#include <MFRC522.h>
|
||||
#include <DES.h>
|
||||
|
||||
#define RST_PIN 9 // Configurable, see typical pin layout above
|
||||
#define SS_PIN 10 // Configurable, see typical pin layout above
|
||||
|
||||
DES des;
|
||||
|
||||
MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance
|
||||
MFRC522::StatusCode status;
|
||||
|
||||
|
||||
//3DES-Key (This is the standard key according to the NPX specification)
|
||||
byte C_key[] = { 0x49, 0x45, 0x4D, 0x4B, 0x41, 0x45, 0x52, 0x42, 0x21, 0x4E, 0x41, 0x43, 0x55, 0x4F, 0x59, 0x46, 0x49, 0x45, 0x4D, 0x4B, 0x41, 0x45, 0x52, 0x42};
|
||||
|
||||
uint64_t IV=0;
|
||||
boolean isPresent=0;
|
||||
boolean isEncrypted=0;
|
||||
boolean isUlC=0;
|
||||
char Scmd[28];
|
||||
char Sdata[64];
|
||||
|
||||
// byte page=0;
|
||||
|
||||
byte buffer[24];
|
||||
boolean isLocked[255]; // which Pages are locked to read only
|
||||
byte startProtect=255; // starting from which page, the data is protectet by the key
|
||||
boolean access; // how is the data protected: 0=write protection (data visible) 1=read/write protection (data = 0x00)
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200); // Initialize serial communications with the PC
|
||||
while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4)
|
||||
des.init(C_key,IV);
|
||||
SPI.begin(); // Init SPI bus
|
||||
mfrc522.PCD_Init(); // Init MFRC522
|
||||
mfrc522.PCD_DumpVersionToSerial(); // Show details of PCD - MFRC522 Card Reader details
|
||||
Serial.println(F("Read/Write Mifare Ultralight C"));
|
||||
}
|
||||
|
||||
|
||||
void loop() {
|
||||
int i=0;
|
||||
boolean IsCommand=1;
|
||||
char x;
|
||||
|
||||
if(Serial.available() > 0){
|
||||
// Serial.println(freeRam());
|
||||
while(i<40){
|
||||
while(!Serial.available());
|
||||
x=Serial.read();
|
||||
if(IsCommand)
|
||||
Scmd[i]=x;
|
||||
else
|
||||
Sdata[i]=x;
|
||||
if(x == '\n'){ // newline
|
||||
if(IsCommand){
|
||||
Scmd[i]=0x00; // proper Ending
|
||||
i=41; // exit
|
||||
}
|
||||
else{
|
||||
Sdata[i]=0x00;
|
||||
i=41;
|
||||
}
|
||||
}
|
||||
else if(x == ' '){
|
||||
if(IsCommand==1){
|
||||
IsCommand=0;
|
||||
Scmd[i]=0x00;
|
||||
i=0;
|
||||
}
|
||||
else
|
||||
i++;
|
||||
}
|
||||
else
|
||||
i++;
|
||||
}
|
||||
if(!isPresent)
|
||||
Serial.println(F("No PICC available!"));
|
||||
else{
|
||||
if(!strcmp(Scmd,"dump")){ // dump data
|
||||
DumpDataUltralight();
|
||||
}
|
||||
else if(!strcmp(Scmd,"auth")){
|
||||
RequestAuthUltralightC();
|
||||
}
|
||||
else if(!strcmp(Scmd,"newKey")){
|
||||
newKey();
|
||||
}
|
||||
else if (!strcmp(Scmd,"wchar")){
|
||||
writeData(0);
|
||||
}
|
||||
else if (!strcmp(Scmd,"whex")){
|
||||
writeData(1);
|
||||
}
|
||||
else if (!strcmp(Scmd,"protect")){
|
||||
protect(1);
|
||||
}
|
||||
else if (!strcmp(Scmd,"setpbit")){
|
||||
protect(0);
|
||||
}
|
||||
}
|
||||
Serial.flush();
|
||||
Scmd[0]=0x00;
|
||||
Sdata[0]=0x00;
|
||||
}
|
||||
// Look for new cards
|
||||
if(!isPresent){
|
||||
if (!mfrc522.PICC_IsNewCardPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Select one of the cards
|
||||
if (!mfrc522.PICC_ReadCardSerial()) {
|
||||
return;
|
||||
}
|
||||
else{
|
||||
isPresent=1;
|
||||
if(mfrc522.uid.sak == 0x00){
|
||||
if(checkIfUltralight()){
|
||||
Serial.println (F("Mifare Ultralight C PICC found!"));
|
||||
getPageInfo();
|
||||
isUlC=1;
|
||||
}
|
||||
else{
|
||||
Serial.println (F("Other Mifare Ultralight compatible PICC found"));
|
||||
isUlC=0;
|
||||
}
|
||||
Serial.print(F("UID: "));
|
||||
dumpInfo(mfrc522.uid.uidByte, mfrc522.uid.size);
|
||||
}
|
||||
else{
|
||||
Serial.println (F("Other Card found, not compatible!"));
|
||||
mfrc522.PICC_HaltA();
|
||||
isPresent=0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(isPresent){ // test read - it it fails, the PICC is most likely gone
|
||||
byte byteCount = sizeof(buffer);
|
||||
status = mfrc522.MIFARE_Read(0, buffer, &byteCount);
|
||||
if(status!=mfrc522.STATUS_OK){
|
||||
isPresent=0;
|
||||
isEncrypted=0;
|
||||
mfrc522.PCD_StopCrypto1();
|
||||
Serial.println("Card gone...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* This small routine starts a request for encryption.
|
||||
* for details see https://www.nxp.com/docs/en/data-sheet/MF0ICU2.pdf
|
||||
*/
|
||||
void RequestAuthUltralightC(void){
|
||||
int i;
|
||||
byte AuthBuffer[24] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //
|
||||
byte AuthLength=24;
|
||||
byte RndA[8]={0,0,0,0,0,0,0,0};
|
||||
byte RndB[8]={0,0,0,0,0,0,0,0}; // decrypted RndB
|
||||
byte rRndB[8]={0,0,0,0,0,0,0,0}; // rotated RndB
|
||||
byte encRndA[8]={0,0,0,0,0,0,0,0}; // encrypted RndA' from the PICC
|
||||
byte dRndA[8]={0,0,0,0,0,0,0,0}; // decrypted RndA
|
||||
byte message[24]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // Message to transfer
|
||||
byte iv_ar[8]; // this is the starting IV for decryption
|
||||
byte byteCount=sizeof(buffer);
|
||||
|
||||
if(isEncrypted){
|
||||
Serial.println(F("PICC already authenticated"));
|
||||
return;
|
||||
}
|
||||
// sparse and set the key
|
||||
if(!setAuthKey()){
|
||||
Serial.println(F("key could not be set"));
|
||||
return;
|
||||
}
|
||||
// set IV to 0x00
|
||||
for(i=0 ; i<8 ; i++){
|
||||
iv_ar[i] = 0x00;
|
||||
}
|
||||
memcpy(&IV,iv_ar,8); // set IV
|
||||
des.set_IV(IV);
|
||||
|
||||
// Build command buffer
|
||||
AuthBuffer[0] = 0x1A; // CMD_3DES_AUTH -> Ultralight C 3DES Authentication.
|
||||
AuthBuffer[1] = 0x00; //
|
||||
|
||||
// Calculate CRC_A
|
||||
status = mfrc522.PCD_CalculateCRC(AuthBuffer, 2, &AuthBuffer[2]);
|
||||
if (status != mfrc522.STATUS_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
AuthLength=sizeof(AuthBuffer);
|
||||
|
||||
// Transmit the buffer and receive the response, validate CRC_A.
|
||||
status = mfrc522.PCD_TransceiveData(AuthBuffer, 4, AuthBuffer, &AuthLength, NULL, 0, true);
|
||||
if (status != mfrc522.STATUS_OK) {
|
||||
Serial.println("Ultralight C Auth failed");
|
||||
Serial.println(mfrc522.GetStatusCodeName(status));
|
||||
Serial.print(F("Reply: "));
|
||||
dumpInfo(AuthBuffer,AuthLength);
|
||||
return;
|
||||
}
|
||||
memcpy(message,AuthBuffer+1,8); // copy the enc(RndB) from the message
|
||||
memcpy(iv_ar,message,8); // use enc(RndB) as new IV for the next encryption.
|
||||
|
||||
des.set_size(8);
|
||||
des.tdesCbcDecipher(message,RndB); // decrypt enc(RndB) -> now we have RndB
|
||||
|
||||
randomSeed(AuthBuffer[1]); // create RndA
|
||||
int number;
|
||||
for(int i=0; i<8; i++){
|
||||
number=random(255);
|
||||
RndA[i]=number&0xFF; //
|
||||
}
|
||||
|
||||
memcpy(rRndB, RndB ,8); // copy RndB
|
||||
rol(rRndB,8); // create RndB'
|
||||
memcpy(message,RndA,8); // copy RndA in the first part of message
|
||||
memcpy(message+8, rRndB ,8); // adding RndB'
|
||||
|
||||
AuthBuffer[0] = 0xAF; // set the PCD-command
|
||||
|
||||
memcpy(&IV,iv_ar,8); // set IV = to enk(RndB)
|
||||
des.set_IV(IV);
|
||||
des.set_size(16);
|
||||
des.tdesCbcEncipher(message, &AuthBuffer[1]);
|
||||
|
||||
status = mfrc522.PCD_CalculateCRC(AuthBuffer, 17, &AuthBuffer[17]);
|
||||
if (status != mfrc522.STATUS_OK) {
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&IV,&AuthBuffer[9],8); // set IV to decrypt reply from PICC enc(RndA') -> RndA
|
||||
des.set_IV(IV);
|
||||
|
||||
status = mfrc522.PCD_TransceiveData(AuthBuffer, 19, AuthBuffer, &AuthLength, NULL, 0, true);
|
||||
if (status != mfrc522.STATUS_OK) {
|
||||
Serial.print(F("Auth failed failed: "));
|
||||
Serial.println(mfrc522.GetStatusCodeName(status));
|
||||
Serial.println(F("Reply: "));
|
||||
dumpInfo(AuthBuffer,AuthLength);
|
||||
return;
|
||||
}
|
||||
else{
|
||||
if(AuthBuffer[0]==0x00){ // reply from PICC should start with 0x00
|
||||
memcpy(encRndA, &AuthBuffer[1],8); // copy enc(RndA')
|
||||
des.set_size(8);
|
||||
des.tdesCbcDecipher(encRndA,dRndA); // decrypt now we have decrypted RndA'
|
||||
rol(RndA,8); // rotate orgiginal RndA to RndA'
|
||||
for(i=0; i<8; i++){ // compare it
|
||||
if(RndA[i] != dRndA[i]){
|
||||
i=9;
|
||||
}
|
||||
}
|
||||
if(i==8){
|
||||
Serial.println(F("Keys are correct :-)"));
|
||||
isEncrypted=1;
|
||||
}
|
||||
else
|
||||
Serial.println(F("Keys do not match"));
|
||||
}
|
||||
else{
|
||||
Serial.println(F("Wrong answer!!!"));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
byte DumpDataUltralight(void){
|
||||
byte page=0;
|
||||
|
||||
for(byte i=0; i<255; i++) // initialize lock bit table
|
||||
isLocked[i]=0;
|
||||
byte Count = sizeof(buffer);
|
||||
|
||||
Serial.println(F("Page lock auth 0 1 2 3"));
|
||||
getPageInfo(); // get information about acess
|
||||
status = mfrc522.MIFARE_Read(page, buffer, &Count);
|
||||
while((status==mfrc522.STATUS_OK)){ // This loop stops at an NAK-answer
|
||||
Serial.print(page);
|
||||
if(page<10)
|
||||
Serial.print(F(" "));
|
||||
Serial.print(F(" "));
|
||||
if(isLocked[page])
|
||||
Serial.print(F(" x "));
|
||||
else
|
||||
Serial.print(F(" "));
|
||||
if(page>=startProtect){
|
||||
if(access)
|
||||
Serial.print(F(" w "));
|
||||
else
|
||||
Serial.print(F("r/w "));
|
||||
}
|
||||
else
|
||||
Serial.print(F(" "));
|
||||
dumpInfo(buffer,4);
|
||||
page++;
|
||||
status = mfrc522.MIFARE_Read(page, buffer, &Count);
|
||||
}
|
||||
Count = sizeof(buffer);
|
||||
status=mfrc522.PICC_WakeupA(buffer, &Count); // needed to wake up the card after receiving a NAK-answer
|
||||
return page;
|
||||
}
|
||||
|
||||
|
||||
boolean checkIfUltralight(void){
|
||||
byte Count=sizeof(buffer);
|
||||
|
||||
if(mfrc522.MIFARE_Read(43, buffer, &Count)==mfrc522.STATUS_OK){
|
||||
if(mfrc522.MIFARE_Read(44, buffer, &Count)==mfrc522.STATUS_OK){
|
||||
return 0;
|
||||
}
|
||||
else{
|
||||
Count = sizeof(buffer);
|
||||
status=mfrc522.PICC_WakeupA(buffer, &Count); // needed to wake up the card after receiving a NAK-answer
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
Count = sizeof(buffer);
|
||||
status=mfrc522.PICC_WakeupA(buffer, &Count); // needed to wake up the card after receiving a NAK-answer
|
||||
Serial.println(mfrc522.GetStatusCodeName(status));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
boolean getPageInfo(){
|
||||
int i;
|
||||
uint32_t mask=0;
|
||||
byte bsize = sizeof(buffer);
|
||||
status = mfrc522.MIFARE_Read(2, buffer, &bsize); // read the first lock bits
|
||||
if(status==mfrc522.STATUS_OK){
|
||||
mask=buffer[3];
|
||||
mask=mask<<8;
|
||||
mask=mask+buffer[2];
|
||||
isLocked[0]=1;
|
||||
isLocked[1]=1;
|
||||
isLocked[2]=1;
|
||||
|
||||
if(mask&0x0001){
|
||||
isLocked[3]=1;
|
||||
}
|
||||
mask=mask>>1;
|
||||
|
||||
if(mask&0x0001){
|
||||
for(i=0;i<6;i++)
|
||||
isLocked[4+i]=1;
|
||||
}
|
||||
mask=mask>>1;
|
||||
|
||||
if(mask&0x0001){
|
||||
for(i=0;i<6;i++)
|
||||
isLocked[10+i]=1;
|
||||
}
|
||||
mask=mask>>1;
|
||||
|
||||
for(i=0;i<13;i++){
|
||||
if(mask&0x0001)
|
||||
isLocked[3+i]=1;
|
||||
mask=mask>>1;
|
||||
}
|
||||
}
|
||||
else{
|
||||
bsize = sizeof(buffer);
|
||||
status=mfrc522.PICC_WakeupA(buffer, &bsize); // needed to wake up the card after receiving a NAK-answer
|
||||
return 0;
|
||||
}
|
||||
status = mfrc522.MIFARE_Read(40, buffer, &bsize);
|
||||
if(status==mfrc522.STATUS_OK){
|
||||
mask=buffer[1];
|
||||
mask=mask<<8;
|
||||
mask=mask+buffer[0];
|
||||
int j=16;
|
||||
for(i=0; i<8; i++){
|
||||
if((i%4)==0){
|
||||
if(mask&0x0001)
|
||||
setBooleanBits(isLocked+j, 12);
|
||||
}
|
||||
else{
|
||||
if(mask&0x0001)
|
||||
setBooleanBits(isLocked+j, 4);
|
||||
j=j+4;
|
||||
}
|
||||
mask=mask>>1;
|
||||
}
|
||||
|
||||
|
||||
for(i=0;i<3;i++){
|
||||
if(mask&0x0001)
|
||||
isLocked[41+i]=1;
|
||||
mask=mask>>1;
|
||||
}
|
||||
|
||||
if(mask&0x0001)
|
||||
setBooleanBits(isLocked+44, 4);
|
||||
mask=mask>>1;
|
||||
|
||||
for(i=0;i<3;i++){
|
||||
if(mask&0x0001)
|
||||
isLocked[41+i]=1;
|
||||
mask=mask>>1;
|
||||
}
|
||||
|
||||
if(mask&0x0001)
|
||||
setBooleanBits(isLocked+44, 4);
|
||||
mask=mask>>1;
|
||||
|
||||
}
|
||||
else{
|
||||
bsize = sizeof(buffer);
|
||||
status=mfrc522.PICC_WakeupA(buffer, &bsize); // needed to wake up the card after receiving a NAK-answer
|
||||
return 0;
|
||||
}
|
||||
status = mfrc522.MIFARE_Read(42, buffer, &bsize);
|
||||
if(status==mfrc522.STATUS_OK){
|
||||
startProtect=buffer[0];
|
||||
}
|
||||
else{
|
||||
bsize = sizeof(buffer);
|
||||
status=mfrc522.PICC_WakeupA(buffer, &bsize); // needed to wake up the card after receiving a NAK-answer
|
||||
return 0;
|
||||
}
|
||||
status = mfrc522.MIFARE_Read(43, buffer, &bsize);
|
||||
if(status==mfrc522.STATUS_OK){
|
||||
access=buffer[0]&0x01;
|
||||
}
|
||||
else{
|
||||
bsize = sizeof(buffer);
|
||||
status=mfrc522.PICC_WakeupA(buffer, &bsize); // needed to wake up the card after receiving a NAK-answer
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void setBooleanBits(boolean *ar, int len){
|
||||
for(int i=0; i<len; i++)
|
||||
ar[i]=1;
|
||||
}
|
||||
|
||||
|
||||
void protect(bool mode){
|
||||
byte value=0;
|
||||
byte page;
|
||||
if(mode) // set start page of protection
|
||||
page=42;
|
||||
else // set write or read/write protection
|
||||
page=43;
|
||||
|
||||
if(isLocked[page]){
|
||||
Serial.println(F("PICC is write protected"));
|
||||
return;
|
||||
}
|
||||
if((startProtect<page)&&(!isEncrypted)){
|
||||
Serial.print(F("page "));
|
||||
Serial.print(page);
|
||||
Serial.println(F(" is password protected, autehticate first"));
|
||||
return;
|
||||
}
|
||||
|
||||
value = atoi(Sdata);
|
||||
|
||||
if(mode){
|
||||
if(value>48)
|
||||
value=48;
|
||||
}
|
||||
else{
|
||||
if(value>1)
|
||||
value=1;
|
||||
}
|
||||
|
||||
for(byte i=0;i<4;i++)
|
||||
buffer[i]=0;
|
||||
|
||||
buffer[0]=value;
|
||||
|
||||
status = mfrc522.MIFARE_Ultralight_Write(page, buffer, 4);
|
||||
if(status==mfrc522.STATUS_OK){
|
||||
if(mode){
|
||||
Serial.print(F("password protection starts now at page: "));
|
||||
Serial.println(value);
|
||||
}
|
||||
else{
|
||||
Serial.print(F("protection bit is set to: "));
|
||||
if(value)
|
||||
Serial.println(F("write protected"));
|
||||
else
|
||||
Serial.println(F("read/write protected"));
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(mode)
|
||||
Serial.print(F("ERROR: password protection not set: "));
|
||||
else
|
||||
Serial.print(F("ERROR: protection bit not set: "));
|
||||
Serial.println(mfrc522.GetStatusCodeName(status));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boolean setAuthKey(void){
|
||||
int i;
|
||||
for(i=0;i<32;i++){
|
||||
if(Sdata[i]==0x00){
|
||||
Serial.println(F("Key too short! (needs to be 32 digits)"));
|
||||
return 0;
|
||||
}
|
||||
else{
|
||||
if((i%2==0)){
|
||||
C_key[i/2]=char2byte(&Sdata[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
for(i=0;i<8;i++){
|
||||
C_key[i+16]=C_key[i];
|
||||
}
|
||||
des.change_key(C_key);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
boolean newKey(void){
|
||||
byte i,j,pos;
|
||||
byte pos_ar[4]={7,3,15,11};
|
||||
byte Buffer[4];
|
||||
byte page=0;
|
||||
|
||||
if(!isEncrypted){
|
||||
Serial.println(F("please authenticate fist"));
|
||||
return 0;
|
||||
}
|
||||
if(setAuthKey()){
|
||||
for(j=0;j<4;j++){
|
||||
page=0x2C+j;
|
||||
pos=pos_ar[j];
|
||||
for(i=0;i<4;i++){
|
||||
Buffer[i]=C_key[pos-i];
|
||||
}
|
||||
status = mfrc522.MIFARE_Ultralight_Write(page, Buffer, 4);
|
||||
}
|
||||
if(status==mfrc522.STATUS_OK){
|
||||
Serial.println(F("new key is written to the card!"));
|
||||
}
|
||||
else{
|
||||
Serial.print(F("new key could not be written: "));
|
||||
Serial.println(mfrc522.GetStatusCodeName(status));
|
||||
// restart the PICC
|
||||
mfrc522.PICC_IsNewCardPresent(); // is required, as the PICC remains silent after a NAK
|
||||
mfrc522.PICC_ReadCardSerial();
|
||||
}
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void writeData(boolean mode){
|
||||
byte page=0;
|
||||
byte i=0;
|
||||
byte Buffer[4]={0,0,0,0};
|
||||
|
||||
if(isUlC)
|
||||
Serial.println(F("Ultralight C"));
|
||||
else
|
||||
Serial.println(F("not a Ultralight C PICC!"));
|
||||
|
||||
while((Sdata[i]!=' ')&&(i<3)){
|
||||
page = page * 10;
|
||||
page = page + Sdata[i]-48;
|
||||
i++;
|
||||
}
|
||||
i++; // to overcome the ' '
|
||||
|
||||
if(page<4){
|
||||
Serial.println(F("no user memory here"));
|
||||
return;
|
||||
}
|
||||
if(isUlC&&(page>39)){
|
||||
Serial.println(F("no user memory here"));
|
||||
return;
|
||||
}
|
||||
byte j=0;
|
||||
boolean done=0;
|
||||
for(; page<40; page++){
|
||||
for(j=0;j<4;j++){
|
||||
if(Sdata[i]==0x00){
|
||||
while(j<4){
|
||||
Buffer[j]=0;
|
||||
j++;
|
||||
}
|
||||
done=1;
|
||||
}
|
||||
else{
|
||||
if(mode){
|
||||
Buffer[j]=char2byte(Sdata+i);
|
||||
i=i+2;
|
||||
}
|
||||
else{
|
||||
Buffer[j]=Sdata[i];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
status = mfrc522.MIFARE_Ultralight_Write(page, Buffer, 4);
|
||||
Serial.print(F("writing page "));
|
||||
Serial.print(page);
|
||||
Serial.println(mfrc522.GetStatusCodeName(status));
|
||||
if(status != mfrc522.STATUS_OK){
|
||||
return;
|
||||
}
|
||||
if(done)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Needed to create RndB' out of RndB
|
||||
void rol(byte *data, int len){
|
||||
byte first = data[0];
|
||||
for (int i = 0; i < len-1; i++) {
|
||||
data[i] = data[i+1];
|
||||
}
|
||||
data[len-1] = first;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dumpInfo(byte *ar, int len){
|
||||
for(int i=0 ; i<len ; i++){
|
||||
if(ar[i]<0x10)
|
||||
Serial.print(F("0"));
|
||||
Serial.print(ar[i], HEX);
|
||||
Serial.print(F(" "));
|
||||
}
|
||||
Serial.println("");
|
||||
}
|
||||
|
||||
|
||||
|
||||
byte char2byte(char *s){
|
||||
byte x = 0;
|
||||
for(int i=0; i <2 ; i++) {
|
||||
char c = *s;
|
||||
if (c >= '0' && c <= '9') {
|
||||
x *= 16;
|
||||
x += c - '0';
|
||||
}
|
||||
else if (c >= 'A' && c <= 'F') {
|
||||
x *= 16;
|
||||
x += (c - 'A') + 10;
|
||||
}
|
||||
else if (c >= 'a' && c <= 'f') {
|
||||
x *= 16;
|
||||
x += (c - 'a') + 10;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* If the hex key is: "00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F"
|
||||
* then you have to write the sequence "07 06 05 04 03 02 01 00 0F 0E 0D 0C 0B 0A 09 08" in 4 pages,
|
||||
* from page 0x2C (44) to page 0x2F (47).
|
||||
*
|
||||
* if you want to change where (which pages) the authentication is required, here is how to do that:
|
||||
* 0x2A defines the page address from which the authentication is required. E.g. if 0x2A = 0x30 no authentication is needed all as memory goes up to 0x2F.
|
||||
* 0x2B defines if authentication is required for read/write (0x2B=0) or only for write access (0x2B=1)
|
||||
|
||||
On example of Key1 = 0001020304050607h and Key2 = 08090A0B0C0D0E0Fh,
|
||||
the command sequence needed for key programming with WRITE command is:
|
||||
•A2 2C 07 06 05 04 CRC
|
||||
•A2 2D 03 02 01 00 CRC
|
||||
•A2 2E 0F 0E 0D 0C CRC
|
||||
•A2 2F 0B 0A 09 08 CRC
|
||||
|
||||
*/
|
Loading…
x
Reference in New Issue
Block a user