Per innalzare il livello di sicurezza del protocollo di riconoscimento delle RFID Card (da clonazione a distanza, sniffing e intrusione informatica), si propone di estendere gli algoritmi di cifratura, descritti nel post Algoritmi di cifratura proprietari, alle RFID card nella versione MIFARE Classic.
![]() |
MFRC-522 RC522 RFID Modulo IC Card
Componente molto economico, ma ai test, risultato molto affidabile e dotato di una notevole libreria di applicazioni. E' possibile realizzare operazioni di lettura e scrittura di transponder a 13,56MHz; richiede un'alimentazione a 3.3V. |
![]() |
Arduino Nano V3.0 Scheda di Microcontrollore CH340 ATmega328P |
![]() |
RFID Chip con 13,56 MHz Transponder MIFARE Classic (1kbytes) |
![]() |
RFID PVC Card 13,56 MHz Transponder MIFARE Classic (1kbytes) |
La RFID Card nella versione MIFARE Classic dispone di una memoria non volatile scrivibile e leggibile di 1kByte. E' dotata di un identificativo univoco di 4 byte ed è quest'ultimo che, nelle applicazioni più semplici,
viene utilizzato per il riconoscimento e il controllo di accessi dal lettore RFID. Tuttavia, come è noto, la comunicazione fra Card e Lettore RFID è facilmente intercettabile, avvenendo con comunicazione radio a 13.56 MHz. E' sufficiente captare
a distanza la comunicazione (con attrezzatura reperibile online per poche decine di Euro) per poter clonare una Card, che fornisca lo stesso codice univoco di identificazione.
Una strategia, per irrobustire e rendere più sicuro il protocollo di comunicazione fra Card e Lettore RFID, è quella di utilizzare per l'identificazione l'intera dotazione di memoria della Card, con una cifratura di difficile duplicazione, con l'avvertenza di non rallentare eccessivamente la comunicazione e
l'obiettivo di rendere impossibile la clonazione, attraverso la captazione di una singola o poche sessioni di riconoscimento.
STRUTTURA CARD
Una Card MIFARE Classic è composta di 16 settori, ogni settore è composto da 4 blocchi di 16 byte (in totale 1.024 byte).
Il blocco 0 contiene la Card Id e non va alterato;
il quarto blocco di ogni settore contiene le chiavi di cifratura MIFARE e non si può utilizzare;
Quindi sono 47 i blocchi programmabili: 1,2,4,5,6,8,9,10,12,13,14,16,17,18,20,21,22,24,25,26,28,29,30,32,33,34,36,37,38,40,41,42,44,45,46,48,49,
50,52,53,54,56,57,58,60,61,62
La cifratura è realizzata a livello di blocco che, essendo composto da 16 byte, può applicare l'algoritmo descritto nel post Algoritmi di cifratura proprietari con unità di cifratura di 128 bit.
Si suggerisce che la stringa codificata in ogni blocco abbia la seguente struttura:
Byte | Descrizione |
---|---|
00-02 | numero progressivo di programmazione di lotto (16.777.216 valori differenti prima di ripetere la sequenza) |
03 | identificatore di blocco: permette di evitare che una card sia clonata ripetendo il contenuto di uno o più blocchi noti |
04-07 | Identificatore univoco di card (come riportato nei byte 00-03 del blocco 0): permette di evitare la clonazione su una card del contenuto di un'altra card |
08-0E | Identificatore univoco di dispositivo; contiene il codice del dispositivo da comandare |
0F | Livello della Card; 00 indica una card Utente; FF indica una card Passepartout; altre codifiche sono possibili con i codici rimanenti |
La soluzione più semplice ed economica per realizzare un Programmatore di RFID Card che abbia un'essenziale Interfaccia Utente è quella di usare un Arduino Nano (anche compatibile), che è dotato di una connessione USB. Lo schema proposto è il seguente:
![]() |
Fig.1 |
che può dare luogo alla semplice compatta realizzazione prototipale:
![]() |
Fig.2 |
Un essenziale codice, per realizzare le funzioni di programmazione RFID Card con lo schema di Fig.1, si può trovare qui di seguito.
Da notare che questo codice chiama due routine che nella libreria standard MFRC522 non esistono; occorre sostituire ai corrispondenti file di tale libreria i file
MFRC522.h e MFRC522.cpp opportunamente modificati.
Se non si vuole usare il Monitor seriale di Arduino IDE (non molto user friendly), si può usare il programma SuperUser_Programmer.exe (sviluppato in Visual Basic 6), che
semplifica la gestione dei comandi al dispositivo.
/* |
Mappa EEPROM |
00-02 Progressivo |
03-05 Lotto |
06-0D Passepartout Card Corrente |
0E-10 Counter |
*/ |
#include <EEPROM.h> // Libreria di gestione EEPROM |
#include <SPI.h> |
#include <MFRC522.h> |
#define LED_ON LOW |
#define LED_OFF HIGH |
constexpr uint8_t Led_pin = 5; // Pin del LED, attivo LOW |
constexpr uint8_t RST_PIN = 9; |
constexpr uint8_t SS_PIN = 10; |
MFRC522 mfrc522(SS_PIN, RST_PIN); |
MFRC522::MIFARE_Key key; |
/* |
Inizializzazione |
*/ |
byte blockAddr, trailerBlock; |
byte sectdata[4][16]; |
/* |
* PASSWORD GENERATRICE DI ESEMPIO |
* |
1CA97A224432B78088A91048A5C739C54BAB2C8400372600B8A37C0F825A5386F65F62368CDBF3A69492 |
DB949DE0D7D069C35640632C7811A6C03C7651A1D070414A8E117B18F5FF9C971568F491AF16D7C75258 |
4B8130DC5B37C37A540E9EC2945289FA2E424411FDFEA903918119483687A82D7C40A0C8B2D3ACC3DF6E |
7A1541885C9090E2FE1652CF87FF35A9CD87456D13944398DD26192672AF8D588D9AD03431173C581C11 |
4F3BA5B849211A7AC13009833B9AA277EC95A54C943303F20E77972C8F631E0E5565B05D853D7BCD2AF9 |
69D3C38E10906FCE03BD6A3E4EA2176A9A887582F1CC81901D907C8ACC36CEC8124EE2BB8146C83F2FB2 |
E0C2E621AF57E4EAE8DE641BA14027631FDB1C4E20C5E9FFC8DA14A73229122122B7D5F519E2DA9D9DC8 |
CCF394AFC8FFF34ADA97DC4E70958E11DBB1424AAA55B0738ABDC478E46212A00FC7617599265EF717B6 |
21CCBD2125F780945DDEA454AF2EA2D74A34262770C21A8A862EC61E0022D09D00ADFA815F33BB32866E |
6713DB85639E8EBB8B3E129D3FA312F7B90876F495DFC9CC0BB7ED630FCDFD128D67FFA78059D7AAC396 |
5A0A0C49D25C2C09D136779049E4C14555745CF53352B3C69ECF63898B6A75991CAEC19270ECBF7E1095 |
D2E1E745F7DFA42B53CE987B6B0881CE0A802C3018EDA109A8FACFCBEA5B9FDC8C18DB0E131E27DF124B |
2B2C5E101E5D162D968193F815A2A09B5D692D8C450D81D8 |
* |
*/ |
byte Key [][8] = { |
{203, 129, 150, 202, 104, 213, 42, 252}, |
{6, 240, 136, 110, 195, 193, 237, 109}, |
{218, 208, 198, 117, 74, 137, 219, 76}, |
{191, 35, 77, 220, 136, 155, 119, 134}, |
{183, 26, 29, 164, 170, 66, 214, 180}, |
{223, 11, 22, 186, 122, 42, 126, 133}, |
{126, 224, 172, 32, 242, 55, 61, 51}, |
{43, 231, 27, 38, 72, 142, 1, 149}, |
{52, 6, 93, 0, 117, 239, 26, 26}, |
{151, 113, 214, 179, 155, 13, 191, 107}, |
{58, 65, 180, 66, 136, 249, 149, 70}, |
{176, 20, 66, 20, 58, 248, 67, 150}, |
{157, 97, 233, 185, 77, 217, 246, 175}, |
{194, 101, 44, 253, 61, 206, 47, 210}, |
{199, 20, 201, 193, 206, 164, 24, 48}, |
{198, 134, 153, 159, 238, 115, 12, 83}, |
{26, 240, 158, 222, 163, 239, 29, 247}, |
{201, 83, 138, 247, 42, 56, 116, 21}, |
{210, 217, 66, 196, 186, 130, 160, 188}, |
{155, 122, 155, 152, 199, 61, 72, 107}, |
{7, 220, 21, 14, 155, 109, 135, 75}, |
{100, 245, 160, 203, 63, 16, 94, 76}, |
{73, 213, 140, 117, 4, 214, 145, 199}, |
{131, 65, 111, 158, 17, 6, 7, 32}, |
{72, 107, 212, 38, 94, 152, 79, 201}, |
{236, 147, 71, 143, 183, 114, 58, 130}, |
{101, 196, 214, 23, 35, 6, 85, 129}, |
{227, 97, 34, 16, 118, 197, 168, 124}, |
{219, 169, 131, 61, 114, 79, 39, 201}, |
{85, 150, 166, 90, 233, 238, 254, 26}, |
{112, 74, 236, 48, 17, 144, 71, 23}, |
{92, 122, 206, 207, 164, 179, 109, 39}, |
{183, 80, 89, 133, 74, 245, 39, 227}, |
{135, 43, 38, 36, 63, 45, 83, 15}, |
{131, 28, 200, 125, 101, 241, 71, 58}, |
{51, 89, 255, 81, 106, 83, 79, 131}, |
{194, 254, 197, 43, 26, 73, 223, 11}, |
{163, 76, 219, 137, 119, 121, 151, 16}, |
{96, 125, 131, 86, 83, 180, 152, 251}, |
{100, 85, 125, 109, 238, 104, 184, 227}, |
{182, 170, 106, 226, 32, 238, 245, 238}, |
{83, 32, 127, 45, 85, 121, 170, 157}, |
{189, 234, 171, 40, 174, 89, 122, 102}, |
{13, 47, 3, 83, 81, 239, 181, 12}, |
{197, 79, 183, 173, 25, 219, 88, 50}, |
{247, 45, 142, 72, 244, 191, 204, 100}, |
{171, 62, 153, 255, 245, 205, 17, 133}, |
{41, 155, 189, 246, 48, 223, 236, 42}, |
{239, 179, 233, 71, 155, 165, 116, 147}, |
{48, 160, 13, 155, 122, 158, 62, 51}, |
{140, 177, 253, 6, 41, 40, 151, 159}, |
{135, 30, 159, 192, 173, 42, 191, 187}, |
{195, 229, 201, 146, 221, 213, 224, 213}, |
{236, 204, 248, 34, 53, 218, 65, 3}, |
{47, 38, 136, 61, 35, 129, 123, 211}, |
{71, 22, 107, 217, 15, 1, 216, 185}, |
{186, 185, 241, 184, 158, 69, 115, 206}, |
{218, 109, 153, 244, 64, 224, 53, 129}, |
{37, 148, 168, 19, 167, 134, 170, 20}, |
{41, 172, 153, 72, 176, 134, 164, 152}, |
{126, 196, 216, 186, 192, 191, 164, 106}, |
{33, 99, 126, 224, 137, 161, 62, 98}, |
{4, 176, 112, 177, 169, 247, 203, 103}, |
{82, 73, 45, 227, 206, 165, 156, 37}, |
{168, 201, 113, 232, 82, 20, 174, 53}, |
{208, 181, 253, 161, 239, 174, 203, 112} |
}; |
char buf[20]; |
char ch[2]; |
char s[33]; |
byte a[8], b[8], dataBlock[16], ii, kk; |
byte CardN[8]; // codice card: diventa a in programmazione |
byte LockN[8]; // codice serratura: diventa b in programmazione |
void setup() { |
pinMode(Led_pin, OUTPUT); |
digitalWrite(Led_pin, LED_OFF); |
//Configurazione del microcontrollore |
Serial.begin(9600); // Inizializza la comunicazione seriale con il PC |
delay(1000); |
SPI.begin(); // Inizializza l'interfaccia SPI |
mfrc522.PCD_Init(); // Inizializza il lettore MFRC522 |
// usa FFFFFFFFFFFFh che sono le chiavi di cifratura di default della Card |
for (byte i = 0; i < 6; i++) { |
key.keyByte[i] = 0xff; |
} |
ch[0] = 0; |
Serial.print("Lotto:"); |
EEPROMPrint(a, 3, 3); |
for (byte i = 0; i < 3; i++) { |
LockN[i+4] = a[i]; |
} |
Serial.print(" Progressivo:"); |
EEPROMPrint(a, 3, 0); |
Serial.println(); |
Serial.print("Passepartout Card corrente:"); |
EEPROMPrint(a, 7, 6); |
Serial.println(); |
Serial.print("Contatore di programmazione:"); |
EEPROMPrint(a, 3, 14); |
Serial.println(); |
} |
/** |
* Main loop. |
*/ |
void loop() { |
// Acquisizione comando |
if (Serial.available()){ |
ch[0] = Serial.read(); |
delay(5); |
/* Comando L: CAMBIO DEL NUMERO DI LOTTO |
Struttura del comando: LXXXXXX: XXXXXX codice esadecimale del lotto |
Il comando azzera anche il contatore progressivo |
*/ |
if(ch[0] == 'L') { |
for (byte i=0; i<6; i++){ |
buf[i] = Serial.read(); |
delay(5); |
} |
ii= 0; |
for (byte i=0; i<6; i=i+2) { |
LockN[ii+4] = HextoDec(buf[i], buf[i+1]); |
EEPROM.write(ii+3, LockN[ii+4]); |
ii = ii + 1; |
} |
Serial.print("Lotto:"); |
EEPROMPrint(a, 3, 3); |
EEPROMReset(a, 3, 0); |
Serial.print(" Progressivo:"); |
EEPROMPrint(a, 3, 0); |
Serial.println(); |
ch[0] = 0; |
} |
/* Comando N: CAMBIO DEL NUMERO PROGRESSIVO DI LOTTO |
Struttura del comando: NXXXXXX: XXXXXX codice esadecimale del progressivo |
Il comando non altera il numero di lotto |
*/ |
else if(ch[0] == 'N') { |
for (byte i=0; i<6; i++){ |
buf[i] = Serial.read(); |
delay(5); |
} |
ii= 0; |
for (byte i=0; i<6; i=i+2) { |
a[ii] = HextoDec(buf[i], buf[i+1]); |
EEPROM.write(ii, a[ii]); |
ii = ii + 1; |
} |
Serial.print("Lotto:"); |
EEPROMPrint(a, 3, 3); |
Serial.print(" Progressivo:"); |
EEPROMPrint(a, 3, 0); |
Serial.println(); |
ch[0] = 0; |
} |
/* Comando A: AZZERAMENTO DEL NUMERO PROGRESSIVO DI LOTTO |
Struttura del comando: A |
Il comando non altera il numero di lotto |
*/ |
else if(ch[0] == 'A') { |
for (byte i=0; i<3; i++){ |
a[i]= 0; |
} |
EEPROMWrite(a, 3, 0); |
Serial.print("Lotto:"); |
EEPROMPrint(a, 3, 3); |
Serial.print(" Progressivo:"); |
EEPROMPrint(a, 3, 0); |
Serial.println(); |
ch[0] = 0; |
} |
/* Comando B: AZZERAMENTO DEL CONTATORE |
Struttura del comando: B |
Il comando non altera il numero di lotto |
*/ |
else if(ch[0] == 'B') { |
for (byte i=0; i<3; i++){ |
a[i]= 0; |
} |
EEPROMWrite(a, 3, 14); |
Serial.print("Contatore:"); |
EEPROMPrint(a, 3, 14); |
Serial.println(); |
ch[0] = 0; |
} |
/* Comando I: INFO SU NUMERO DI LOTTO E PROGRESSIVO |
Struttura del comando: I |
*/ |
else if(ch[0] == 'I') { |
Serial.print("Lotto:"); |
EEPROMPrint(a, 3, 3); |
Serial.print(" Progressivo:"); |
EEPROMPrint(a, 3, 0); |
Serial.println(); |
Serial.print("Passepartout Card corrente:"); |
EEPROMPrint(a, 7, 6); |
Serial.println(); |
Serial.print("Contatore di programmazione:"); |
EEPROMPrint(a, 3, 14); |
Serial.println(); |
ch[0] = 0; |
} |
/* Comando V: VERIFICA IL CONTENUTO DECIFRATO DELLA CARD |
Struttura del comando: V |
Il comando attende di leggere la card da verificare |
*/ |
else if (ch[0] == 'V') { |
Serial.println("Verifica card: in attesa di una card da verificare..."); |
} |
/* Comando ?: RESTITUISCE IL CODICE UNIVOCO DI CARD E IL TIPO |
Struttura del comando: ? |
Il comando attende di leggere la card da identificare |
*/ |
else if (ch[0] == '?') { |
Serial.println("Tipo card: in attesa di una card da leggere..."); |
} |
/* Comando E: Cancellazione card |
Struttura del comando: E |
Il comando attende di leggere la card da cancellare |
*/ |
else if (ch[0] == 'E') { |
Serial.println("Cancellazione Card: in attesa della card da cancellare..."); |
} |
/* Comando R: RESTITUISCE IL CONTENUTO COMPLETO DELLA CARD NON DECIFRATO |
Struttura del comando: R |
Il comando attende di leggere la card da leggere |
*/ |
else if (ch[0] == 'R') { |
Serial.println("Legge card: in attesa di una card da leggere..."); |
} |
/* Comando A: Annulla il comando in corso |
Struttura del comando: A |
*/ |
else if (ch[0] == 'C') { |
Serial.println("Comando annullato"); |
ch[0] = 0; |
} |
/* Comando P: COMANDO DI PROGRAMMAZIONE CARD PASSEPARTOUT |
Struttura del comando: P |
Il comando attende di leggere la card passepartout da programmare |
La card sarà programmata con il suo codice univoco, il numero di lotto corrente ed il progressivo corrente |
Alla fine del comando il progressivo sarà incrementato |
*/ |
else if (ch[0] == 'P') { |
Serial.print("Lotto:"); |
EEPROMPrint(a, 3, 3); |
Serial.print(" Progressivo:"); |
EEPROMPrint(a, 3, 0); |
Serial.println(); |
Serial.println("Legge card: in attesa di una card da programmare..."); |
} |
/* Comando K: INSERIMENTO DI PASSEPARTOUT CARD CORRENTE |
Struttura del comando: K |
Il comando attende di leggere la passepartout card corrente |
*/ |
else if (ch[0] == 'K') { |
Serial.println("Passepartout card: in attesa della card da acquisire..."); |
} |
/* Comando U: COMANDO DI PROGRAMMAZIONE USER CARD |
Struttura del comando: U |
Il comando attende di leggere la user card da programmare |
La card sarà programmata con il suo codice univoco, il numero di serratura fornito dalla Passepartout Card |
*/ |
else if (ch[0] == 'U') { |
EEPROMRead(LockN, 8, 6); |
if(LockN[7] == 0) { |
Serial.print("Passepartout Card:"); |
VectorPrint(LockN, 0, 7); |
Serial.println(); |
Serial.println("User Card: in attesa di una card da programmare..."); |
} |
else { |
Serial.println("Inserire prima una Passepartout Card valida"); |
ch[0] = 0; |
} |
} |
/* Comando Q: COMANDO DI PROGRAMMAZIONE QUESTION CARD |
Struttura del comando: Q |
Il comando attende di leggere la question card da programmare |
La card sarà programmata con il suo codice univoco, il numero di serratura fornito dalla Passepartout Card |
*/ |
else if (ch[0] == 'Q') { |
EEPROMRead(LockN, 8, 6); |
if(LockN[7] == 0) { |
Serial.print("Passepartout Card:"); |
VectorPrint(LockN, 0, 7); |
Serial.println(); |
Serial.println("Question Card: in attesa di una card da programmare..."); |
} |
else { |
Serial.println("Inserire prima una Passepartout Card valida"); |
ch[0] = 0; |
} |
} |
} |
// Look for new cards |
if ( ! mfrc522.PICC_IsNewCardPresent()) return; |
// Select one of the cards |
if ( ! mfrc522.PICC_ReadCardSerial()) return; |
else digitalWrite(Led_pin, LED_ON); // Led acceso |
for ( uint8_t i = 0; i < 4; i++) { |
CardN[4+i] = mfrc522.uid.uidByte[i]; // Salva il codice chiave in CardN |
} |
if(ch[0] == 0) ch[0] = '?'; |
if (ch[0] == 'V') { // Verifica contenuto card |
boolean flg, blk_err = false ; |
for (byte sector = 0; sector < 16 ; sector++) { |
Serial.print("Settore "); |
Serial.println(sector); |
mfrc522.PICC_DumpMifareClassicSectorToSave(&(mfrc522.uid), &key, sector, sectdata); |
for ( uint8_t j=0; j<3; j++) { |
for ( uint8_t i = 0; i < 8; i++) { |
a[i] = sectdata[j][i]; |
} |
for ( uint8_t i = 0; i < 8; i++) { |
b[i] = sectdata[j][i+8]; |
} |
blockAddr = sector*4+j; |
if(blockAddr > 0) DeKrypto(a,b, 31); |
if(blockAddr == 1) CopyVector(b, LockN, 8); |
if(blockAddr<10) Serial.print(0); |
Serial.print(blockAddr); |
Serial.print(" "); |
VectorPrint(a, 0, 8); |
VectorPrint(b, 0, 8); |
Serial.print(" Blocco "); |
if(blockAddr > 0) { |
flg = CompareVector(a, CardN, 4, 4, 4); |
if(flg && a[3] == blockAddr) Serial.print("Ok"); |
else { |
Serial.print("Er"); |
blk_err = true; |
} |
flg = CompareVector(b, LockN, 0, 0, 8); |
if(flg) Serial.print(" Ok"); |
else { |
Serial.print("Er"); |
blk_err = true; |
} |
} |
else { |
Serial.print("Ok Ok"); |
} |
Serial.println(); |
} |
} |
Serial.print("Card "); |
VectorPrint(mfrc522.uid.uidByte, 0, 4); |
if(blk_err) Serial.println(" Codice non valido"); |
else { |
if(LockN[7] == 0) Serial.println(" User"); |
else if(LockN[7] == 170) Serial.println(" Question"); |
else if(LockN[7] == 255) Serial.println(" Passepartout"); |
else Serial.println(" Card non valida"); |
} |
ch[0] = 0; |
// Halt PICC |
mfrc522.PICC_HaltA(); |
// Stop encryption on PCD |
mfrc522.PCD_StopCrypto1(); |
} |
else if (ch[0] == '?') { // Verifica tipo card |
mfrc522.PICC_DumpMifareClassicSectorToSave(&(mfrc522.uid), &key, 1, sectdata); |
for ( uint8_t i = 0; i < 8; i++) { |
a[i] = sectdata[1][i]; |
} |
for ( uint8_t i = 0; i < 8; i++) { |
b[i] = sectdata[1][i+8]; |
} |
DeKrypto(a,b, 31); |
if(a[3] != 5) Serial.print ("Codice non valido"); |
else { |
if(b[7] == 0) { |
Serial.print("User di"); |
VectorPrint(b, 0, 7); |
} |
else if(b[7] == 170) { |
Serial.print("Question di"); |
VectorPrint(b, 0, 7); |
} |
else if(b[7] == 255) { |
if(CompareVector(a, b, 4, 0, 4)){ |
Serial.print("Passepartout Card:"); |
VectorPrint(a, 4, 4); |
Serial.print(" Lotto:"); |
VectorPrint(b, 4, 3); |
Serial.print(" Progressivo:"); |
VectorPrint(a, 0, 3); |
} |
else { |
Serial.print ("Codice non valido"); |
} |
} |
else { |
Serial.print("Card non valida"); |
} |
} |
Serial.println(); |
ch[0] = 0; |
// Halt PICC |
mfrc522.PICC_HaltA(); |
// Stop encryption on PCD |
mfrc522.PCD_StopCrypto1(); |
} |
else if(ch[0] == 'R') { // Legge il contenuto della card |
ch[0] = 0; |
Serial.println(F("Contenuto cifrato")); |
// Dump debug info about the card; PICC_HaltA() is automatically called |
mfrc522.PICC_DumpToSerial(&(mfrc522.uid)); |
Serial.println(F("Fine scanning")); |
} |
else if (ch[0] == 'K') { // Acquisizione Passpartout Card corrente |
mfrc522.PICC_DumpMifareClassicSectorToSave(&(mfrc522.uid), &key, 1, sectdata); |
for ( uint8_t i = 0; i < 8; i++) { |
a[i] = sectdata[1][i]; |
} |
for ( uint8_t i = 0; i < 8; i++) { |
b[i] = sectdata[1][i+8]; |
} |
DeKrypto(a,b, 31); |
if(a[3] != 5) Serial.print ("Codice non valido"); |
else { |
if(b[7] == 0) { |
Serial.print("User Card"); |
} |
if(b[7] == 170) { |
Serial.print("Question Card"); |
} |
else if(b[7] == 255) { |
if(CompareVector(a, b, 4, 0, 4)){ |
Serial.print("Passepartout Card:"); |
VectorPrint(b, 0, 7); |
b[7] = 0; // Predispone alla memorizzaione di User Card |
EEPROMWrite(b, 8, 6); // Salva il codice serratura |
} |
else { |
Serial.print (" Codice non valido"); |
} |
} |
else { |
Serial.print(" Card non valida"); |
} |
} |
Serial.println(); |
ch[0] = 0; |
// Halt PICC |
mfrc522.PICC_HaltA(); |
// Stop encryption on PCD |
mfrc522.PCD_StopCrypto1(); |
} |
else if(ch[0] == 'P'){ // Programma la Passepartout Card |
boolean valid_flag = true; |
EEPROMRead(CardN, 3, 0); // inizializza CardN al progressivo di lotto |
Serial.print(F("Card UID:")); |
VectorPrint(mfrc522.uid.uidByte, 0, mfrc522.uid.size); |
EEPROMRead(a, 3, 3); // legge il numero di Lotto |
a[3] = 255; |
for ( uint8_t i = 0; i < 4; i++) { |
LockN[i] = mfrc522.uid.uidByte[i]; |
LockN[i+4] = a[i]; |
CardN[i+4] = LockN[i]; // Salva il codice chiave in CardN |
} |
Serial.println(); |
Serial.print(F("PICC type: ")); |
MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); |
Serial.println(mfrc522.PICC_GetTypeName(piccType)); |
// Check for compatibility |
if ( piccType != MFRC522::PICC_TYPE_MIFARE_MINI |
&& piccType != MFRC522::PICC_TYPE_MIFARE_1K |
&& piccType != MFRC522::PICC_TYPE_MIFARE_4K) { |
Serial.println(F("This sample only works with MIFARE Classic cards.")); |
return; |
} |
MFRC522::StatusCode status; |
for (byte sector = 0; sector < 16 ; sector++) { |
trailerBlock = sector * 4 + 3; |
status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); |
for (int bk = 0; bk<3; bk++) { |
blockAddr = sector * 4 + bk; |
if(blockAddr > 0) { |
for ( uint8_t i = 0; i < 8; i++) { |
a[i] = CardN[i]; // Salva il codice chiave in a |
} |
for ( uint8_t i = 0; i < 8; i++) { |
b[i] = LockN[i]; // Salva il codice serratura in b |
} |
a[3] = blockAddr; |
Serial.print("Blocco: "); |
Serial.print(blockAddr); |
Krypto(a,b,32); |
for ( uint8_t i = 0; i < 8; i++) { |
dataBlock[i] = a[i]; // salva kra in dataBlock |
} |
for ( uint8_t i = 0; i < 8; i++) { |
dataBlock[i+8] = b[i]; // salva krb in dataBlock |
} |
status = (MFRC522::StatusCode) mfrc522.MIFARE_Write(blockAddr, dataBlock, 16); |
if(status != 0) { |
valid_flag = false; |
Serial.println(" Err"); |
} |
else { |
Serial.println(" Ok"); |
} |
} |
} |
} |
ch[0] = 0; |
if(valid_flag) { |
EEPROMIncrement(3, 0); |
EEPROMWrite(LockN, 7, 6); // Salva il nuovo codice serratura come corrente |
Serial.println(F("Programmazione completata")); |
} |
else { |
Serial.println(F("Errore")); |
// Halt PICC |
mfrc522.PICC_HaltA(); |
} |
// Stop encryption on PCD |
mfrc522.PCD_StopCrypto1(); |
} |
else if(ch[0] == 'U'){ // Programma la User Card |
boolean valid_flag = true; |
EEPROMRead(CardN, 3, 14); // inizializza CardN con il Contatore di programmatore corrente |
EEPROMRead(LockN, 8, 6); // inizializza LockN con il codice di Passepartout Card corrente |
Serial.print(F("Card UID:")); |
VectorPrint(mfrc522.uid.uidByte, 0, mfrc522.uid.size); |
for ( uint8_t i = 0; i < 4; i++) { |
CardN[i+4] = mfrc522.uid.uidByte[i];; // Salva il codice chiave in CardN |
} |
Serial.println(); |
Serial.print(F("PICC type: ")); |
MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); |
Serial.println(mfrc522.PICC_GetTypeName(piccType)); |
// Check for compatibility |
if ( piccType != MFRC522::PICC_TYPE_MIFARE_MINI |
&& piccType != MFRC522::PICC_TYPE_MIFARE_1K |
&& piccType != MFRC522::PICC_TYPE_MIFARE_4K) { |
Serial.println(F("This sample only works with MIFARE Classic cards.")); |
return; |
} |
MFRC522::StatusCode status; |
for (byte sector = 0; sector < 16 ; sector++) { |
trailerBlock = sector * 4 + 3; |
status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); |
for (int bk = 0; bk<3; bk++) { |
blockAddr = sector * 4 + bk; |
if(blockAddr > 0) { |
for ( uint8_t i = 0; i < 8; i++) { |
a[i] = CardN[i]; // Salva il codice chiave in a |
} |
for ( uint8_t i = 0; i < 8; i++) { |
b[i] = LockN[i]; // Salva il codice serratura in b |
} |
a[3] = blockAddr; |
Krypto(a,b,32); |
for ( uint8_t i = 0; i < 8; i++) { |
dataBlock[i] = a[i]; // salva kra in dataBlock |
} |
for ( uint8_t i = 0; i < 8; i++) { |
dataBlock[i+8] = b[i]; // salva krb in dataBlock |
} |
status = (MFRC522::StatusCode) mfrc522.MIFARE_Write(blockAddr, dataBlock, 16); |
if(status != 0) { |
valid_flag = false; |
} |
} |
} |
} |
ch[0] = 0; |
if(valid_flag) { |
Serial.println(F("Programmazione completata")); |
EEPROMIncrement(3, 14); // Incrementa il Contatore di programmazione |
} |
else { |
Serial.println(F("Errore")); |
// Halt PICC |
mfrc522.PICC_HaltA(); |
} |
// Stop encryption on PCD |
mfrc522.PCD_StopCrypto1(); |
} |
else if(ch[0] == 'Q'){ // Programma la Question Card |
boolean valid_flag = true; |
EEPROMRead(CardN, 3, 14); // inizializza CardN con il Contatore di programmatore corrente |
EEPROMRead(LockN, 8, 6); // inizializza LockN con il codice di Passepartout Card corrente |
Serial.print(F("Card UID:")); |
VectorPrint(mfrc522.uid.uidByte, 0, mfrc522.uid.size); |
for ( uint8_t i = 0; i < 4; i++) { |
CardN[i+4] = mfrc522.uid.uidByte[i];; // Salva il codice chiave in CardN |
} |
Serial.println(); |
Serial.print(F("PICC type: ")); |
MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); |
Serial.println(mfrc522.PICC_GetTypeName(piccType)); |
// Check for compatibility |
if ( piccType != MFRC522::PICC_TYPE_MIFARE_MINI |
&& piccType != MFRC522::PICC_TYPE_MIFARE_1K |
&& piccType != MFRC522::PICC_TYPE_MIFARE_4K) { |
Serial.println(F("This sample only works with MIFARE Classic cards.")); |
return; |
} |
MFRC522::StatusCode status; |
for (byte sector = 0; sector < 16 ; sector++) { |
trailerBlock = sector * 4 + 3; |
status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); |
for (int bk = 0; bk<3; bk++) { |
blockAddr = sector * 4 + bk; |
if(blockAddr > 0) { |
for ( uint8_t i = 0; i < 8; i++) { |
a[i] = CardN[i]; // Salva il codice chiave in a |
} |
for ( uint8_t i = 0; i < 7; i++) { |
b[i] = LockN[i]; // Salva il codice serratura in b |
} |
b[7] = 170; // inserisce il codice AA di question card |
a[3] = blockAddr; |
Krypto(a,b,32); |
for ( uint8_t i = 0; i < 8; i++) { |
dataBlock[i] = a[i]; // salva kra in dataBlock |
} |
for ( uint8_t i = 0; i < 8; i++) { |
dataBlock[i+8] = b[i]; // salva krb in dataBlock |
} |
status = (MFRC522::StatusCode) mfrc522.MIFARE_Write(blockAddr, dataBlock, 16); |
if(status != 0) { |
valid_flag = false; |
} |
} |
} |
} |
ch[0] = 0; |
if(valid_flag) { |
Serial.println(F("Programmazione completata")); |
EEPROMIncrement(3, 14); // Incrementa il Contatore di programmazione |
} |
else { |
Serial.println(F("Errore")); |
// Halt PICC |
mfrc522.PICC_HaltA(); |
} |
// Stop encryption on PCD |
mfrc522.PCD_StopCrypto1(); |
} |
else if(ch[0] == 'E'){ // Cancella la Card |
boolean valid_flag = true; |
Serial.print(F("Card UID:")); |
VectorPrint(mfrc522.uid.uidByte, 0, mfrc522.uid.size); |
for ( uint8_t i = 0; i < 4; i++) { |
CardN[i+4] = mfrc522.uid.uidByte[i];; // Salva il codice chiave in CardN |
} |
Serial.println(); |
Serial.print(F("PICC type: ")); |
MFRC522::PICC_Type piccType = mfrc522.PICC_GetType(mfrc522.uid.sak); |
Serial.println(mfrc522.PICC_GetTypeName(piccType)); |
// Check for compatibility |
if ( piccType != MFRC522::PICC_TYPE_MIFARE_MINI |
&& piccType != MFRC522::PICC_TYPE_MIFARE_1K |
&& piccType != MFRC522::PICC_TYPE_MIFARE_4K) { |
Serial.println(F("This sample only works with MIFARE Classic cards.")); |
return; |
} |
MFRC522::StatusCode status; |
for (byte sector = 0; sector < 16 ; sector++) { |
trailerBlock = sector * 4 + 3; |
status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid)); |
for (int bk = 0; bk<3; bk++) { |
blockAddr = sector * 4 + bk; |
if(blockAddr > 0) { |
for ( uint8_t i = 0; i < 8; i++) { |
a[i] = 0; // azzera a |
b[i] = 0; // azzera b |
} |
for ( uint8_t i = 0; i < 8; i++) { |
dataBlock[i] = a[i]; // salva kra in dataBlock |
} |
for ( uint8_t i = 0; i < 8; i++) { |
dataBlock[i+8] = b[i]; // salva krb in dataBlock |
} |
status = (MFRC522::StatusCode) mfrc522.MIFARE_Write(blockAddr, dataBlock, 16); |
if(status != 0) { |
valid_flag = false; |
} |
} |
} |
} |
ch[0] = 0; |
if(valid_flag) { |
Serial.println(F("Cancellazione completata")); |
} |
else { |
Serial.println(F("Errore")); |
// Halt PICC |
mfrc522.PICC_HaltA(); |
} |
// Stop encryption on PCD |
mfrc522.PCD_StopCrypto1(); |
} |
delay(500); |
digitalWrite(Led_pin, LED_OFF); // Led spento |
} |
/* |
Visualizza vettore in formato esadecimale |
*/ |
void VectorPrint(byte *x, byte ix, byte dv) { |
Serial.print(" <"); |
for (byte i = 0; i < dv; i++) { |
Serial.print(x[ix+i] < 0x10 ? " 0" : " "); |
Serial.print(x[ix+i], HEX); |
} |
Serial.print(" >"); |
} |
/////////////////////////////////////// Gestione EEPROM ////////////////////////////// |
void EEPROMRead(byte *x, int dv, int adr){ |
for ( int i = 0; i < dv; i++) { // Legge il vettore dalla EEPROM |
x[i] = EEPROM.read(adr+i); // Salva il byte nel vettore |
} |
} |
void EEPROMWrite(byte *x, int dv, int adr){ |
for ( int i = 0; i < dv; i++) { // Scrive il vettore nella EEPROM |
EEPROM.write(adr+i, x[i]); |
} |
} |
void EEPROMReset(byte *x, int dv, int adr){ |
for ( int i = 0; i < dv; i++) { // Reset della porzione di EEPROM |
x[i] = 0; |
EEPROM.write(adr+i, 0); |
} |
} |
void EEPROMPrint(byte *x, int dv, int adr){ |
EEPROMRead(x, dv, adr); |
VectorPrint(x, 0, dv); |
} |
void EEPROMIncrement(int dv, int adr){ |
byte rep = 1; |
EEPROMRead(a, dv, adr); |
for (int i = dv-1; i >= 0; i=i-1) { |
if(rep == 1 && a[i] == 255){ |
a[i] = 0; |
} |
else { |
a[i] = a[i] + rep; |
rep = 0; |
} |
} |
EEPROMWrite(a, dv, adr); |
} |
/*-----( Funzioni di Conversione )-----*/ |
byte HextoDec(char x, char y) { |
byte h, l, r; |
h = 0; |
l = 0; |
if(x>64) h = x - 55; |
else h = x - 48; |
if(y>64) l = y - 55; |
else l = y - 48; |
r = h*16 + l; |
return r; |
} |
void DectoHex(byte z, char ch[2]) { |
byte nb; |
nb = z / 16; |
if (nb>9){nb = nb + 55;} |
else {nb = nb + 48;} |
ch[0] = char(nb); |
nb = z % 16; |
if (nb>9){nb = nb + 55;} |
else {nb = nb + 48;} |
ch[1] = char(nb); |
} |
/*-----( Funzioni di Cifratura )-----*/ |
byte Krypto(byte a[8], byte b[8], byte nit) { |
byte i, j, rck, rtb; |
byte rs[8]; |
Sum(a, Key[0], a); |
Sum(b, Key[1], b); |
for (i = 1; i |
Xor(a, b, rs); |
rck = b[7] & 1; |
if(rck > 0){ |
Swap(rs, rs); |
} |
rck = b[7] & 63; |
rtb = b[7] & 64; |
if(rtb > 0) LeftRot(rs, rck, rs); |
else RightRot(rs, rck, rs); |
Sum(rs, Key[2*i], a); |
Xor(b, a, rs); |
rck = a[7] & 1; |
if(rck > 0){ |
Swap(rs, rs); |
} |
rck = a[7] & 63; |
rtb = a[7] & 64; |
if(rtb > 0) LeftRot(rs, rck, rs); |
else RightRot(rs, rck, rs); |
Sum(rs, Key[2*i+1], b); |
} |
return ZeroNB(a, b); |
} |
byte DeKrypto(byte a[8], byte b[8], byte nit) { |
byte i, j, k, rck, rtb; |
byte rs[8]; |
for (k = 0; k |
i = nit - k; |
Subt (b, Key[2*i+1], rs); |
rck = a[7] & 63; |
rtb = a[7] & 64; |
if(rtb > 0) { |
RightRot(rs, rck, rs); |
} |
else { |
LeftRot(rs, rck, rs); |
} |
rck = a[7] & 1; |
if(rck > 0){ |
Swap(rs, rs); |
} |
Xor(rs, a, b); |
Subt (a, Key[2*i], rs); |
rck = b[7] & 63; |
rtb = b[7] & 64; |
if(rtb > 0){ |
RightRot(rs, rck, rs); |
} |
else { |
LeftRot(rs, rck, rs); |
} |
rck = b[7] & 1; |
if(rck > 0){ |
Swap(rs, rs); |
} |
Xor(rs, b, a); |
} |
Subt(b, Key[1], b); |
Subt(a, Key[0], a); |
return ZeroNB(a, b); |
} |
void Sum(byte x[8], byte y[8], byte z[8]) { |
byte rp = 0, i, j; |
int rs; |
for (j = 0; j<8; j++) { |
i = 7 - j; |
rs = x[i] + y[i] + rp; |
if(rs > 255) { |
rs = rs - 256; |
rp = 1; |
} |
else rp = 0; |
z[i] = byte(rs); |
} |
} |
void Subt(byte x[8], byte y[8], byte z[8]) { |
byte rp = 0, i, j; |
int rs; |
for (j = 0; j<8; j++) { |
i = 7 - j; |
rs = x[i] - y[i] - rp; |
if(rs < 0) { |
rs = rs + 256; |
rp = 1; |
} |
else rp = 0; |
z[i] = byte(rs); |
} |
} |
void LeftRot(byte x[8], byte rotn, byte z[8]) { |
byte rp; |
int rs; |
byte i; |
byte j; |
byte k; |
for (k = 0; k |
rp = 0; |
for (j = 0; j<8; j++) { |
i = 7 - j; |
rs = x[i] << 1; |
rs = rs | int(rp); |
if(rs > 255) { |
rs = rs - 256; |
rp = 1; |
} |
else rp = 0; |
z[i] = byte(rs); |
} |
z[7] = z[7] | rp; |
} |
} |
void RightRot(byte x[8], byte rotn, byte z[8]) { |
byte rp; |
byte rs; |
byte j; |
byte k; |
for (k = 0; k |
rp = 0; |
for (j = 0; j<8; j++) { |
rs = rp; |
if((x[j] & 1) > 0) rp = 128; |
else rp = 0; |
z[j] = (x[j] >> 1) | rs; |
} |
z[0] = z[0] | rp; |
} |
} |
void Xor(byte x[8], byte y[8], byte z[8]) { |
byte j; |
for (j = 0; j<8; j++) { |
z[j] = x[j]^y[j]; |
} |
} |
byte ZeroNB(byte x[8], byte y[8]) { |
byte i; |
byte j; |
byte msk; |
byte z; |
z = 0; |
for (i = 0; i<8; i++) { |
msk = 1; |
for (j = 0; j<8; j++) { |
if((x[i]&msk) == 0) z = z + 1; |
if((y[i]&msk) == 0) z = z + 1; |
msk = msk << 1; |
} |
} |
return z; |
} |
void Swap(byte x[8], byte z[8]) { |
byte j; |
for (j = 0; j<8; j++) { |
z[j] = (x[j] << 4) | (x[j] >> 4); |
} |
} |
boolean CompareVector(byte *x, byte *z, byte ix, byte iz, byte dv) { |
byte j; |
boolean flg = true; |
for (j = 0; j |
if(x[j+ix] != z[j+iz]) flg = false; |
} |
return flg; |
} |
void CopyVector(byte *x, byte *z, byte dv) { |
byte j; |
for (j = 0; j |
z[j] = x[j]; |
} |
} |