Protocollo di sicurezza per RFID Card

SOMMARIO

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.

PRINCIPALI MATERIALI UTILIZZATI

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)

GENERALITA'

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 DELLA CARD MIFARE CLASSIC

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

PROTOCOLLO DI CIFRATURA

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

PROGRAMMATORE DI RFID CARD

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

PROGRAMMAZIONE

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];           
    }
  }