Zugangsbeschränkung  zu Geräten per Contactless Card mit der NodeMCU und dem RC522 Modul Teil 4 – Down the Rabbit Hole - AZ-Delivery

En el Blog de hoy, como prometí, vamos a "down the Rabbit Hole", y vamos a tratar con los intereses de la Proximity Integrated MiFare. Contactless Cards Fila. Examinaremos las características y las funciones.

Muchos Lector De Tarjetas- Proyectos que utilizan el conjunto de chips MF522 como base para Arduino por desgracia, los que he visto personalmente no aprovechan lo que la tarjeta Mifare Classic nos ofrece en términos de seguridad ni en términos de posibilidades.

Estos proyectos se limitan a leer libremente el identificador Unique (UUID) de la tarjeta para cada lector de tarjetas y teléfonos móviles y a comprobar que no existe una lista de uuid's permitidos. Si se incluye en la lista, la tarjeta se considerará válida.

También en nuestros dividir anterior hagamos uso de este principio. Esto es lo que queremos cambiar con nuestra entrada de blog de hoy y poner datos seguros en nuestra tarjeta MiFare Classic.

El tipo de tarjeta "MiFare Classic" utilizado en este proyecto (tenga en cuenta esta advertencia) nos permite almacenar la información que nosotros mismos hemos proporcionado en el mapa y protegerla contra la extracción o modificación no autorizada.

Para ello, el fabricante ya dispone de 16 sectores (0-15) de 4 * 16 Bytes cada uno de los cuales pueden describirse libremente, con excepción del sector 0 , 3 * 16 bytes. 16 Bytes de cada sector se denominan "caravanas" y se utilizan para archivar las dos claves de sector y definir el Matriz de acceso.

Los 16 Bytes de un sector de caravanas se dividen como sigue::
6 Bytes - primer código de sector
Segundo código de sector de 6 Bytes
Definición De Acceso De 4 Bytes

Ahora queremos guardar en nuestro mapa el nombre de pila y apellido encriptado. Para ello, definimos un sector (1-15) a través de la constante ... que queremos usar para ese fin. He marcado el bloque de Sector 1 en el código.

El nombre debe aparecer en el segundo campo de 16 Bytes y el apellido en el tercer campo de 16 Bytes. El primer campo de 16 Bytes está reservado para futuras ampliaciones.

Por otra parte, necesitamos una definición clave que depositemos en la estructura MiFareClassicKeyTable.

Por favor, cambie este bloque (2x6 Bytes) con su propio material clave y guárdelo en un lugar seguro, porque de lo contrario, cualquiera que conozca este proyecto con el material clave indicado en el código lee su mapa y su propio material válido. Puede crear mapas para su control del dispositivo.

Pequeño Spoiler: Además, necesitaremos de nuevo este material clave para una continuación posterior de la serie. Por favor, recuerden que describen el mapa y cambian las opciones de seguridad. Esto puede hacer que el mapa no pueda utilizarse para otros usos.

Subimos el siguiente código a nuestro ESP después de cambiar el material clave:

 

# include <SPI.h>
# include <MFRC522.h>
# include <ESP8266WiFi.h>
//#include <WiFiClient.h> 
# include <ESP8266WebServer.h>
# include <ESP8266mDNS.h>
# include <EEPROM.h>
# include <FS.h>           // Include the SPIFFS library

# define RST_PINA     5     // SPI Reset Pin (D1 salida))
# define RELAIS_PIN  16    // Relés (D0 salida) [Low activado] - LED interno cerca de USB Port
# define SS_PIN      15    // SPI Slave Select Pin

# define RGBLED_R    2     // Rojo (D4 Salida) 
# define RGBLED_G    0     // Verde (D3 salida) - LED interno en el módulo ESP
# define RGBLED_B    4     // Azul (D2 Salida)

# define WiFiPwdLen   25   // Longitud Máxima De Contraseña WiFi
# define STANameLen 20     // Longitud Máxima WiFi
# define ESPHostNameLen 20 // Número Máximo De Caracteres ESPHostName

# define KEYA true         // PICC Flag Definition
# define KEYB false        // PICC Flag Definition

# define LED_BUILTIN 16
# define PIN_WIRE_SDA 4 
# define PIN_WIRE_SCL 5 

# define USED_Sector 1  // Sector de tarjetas utilizado para los datos de autenticación y configuración EVR (válido: 1 - 15))

ADC_MODE(ADC_TOUT);   // Entrada analógica A0 configurada en el exterior. ADC_TOUT( for external voltage), ADC_VCC (for system voltage). 
MFRC522 mfrc522(SS_PIN, RST_PINA);   // Crear instancia MFRC522
MFRC522::MIFARE_Key clave;
ESP8266WebServer servidor(80);        / Crear instancia / servidor Web

struct WiFiEEPromData   {     char ESPHostName[ESPHostNameLen];      char APSTAName[STANameLen]; // STATION Name to Connect, if definded        char WiFiPwd[WiFiPwdLen]; // WiFiPAssword, if definded         char Configvalida[3]; //If Config is Vaild, Tag " TK " is required"   };    struct MiFareClassicKeyTable   {    bytes Key_A[6] = {0x22,0x44,0xFA,0xAB,0x90,0x11};   // Cambie la clave de la tarjeta PICC.     bytes Key_B[6] = {0xFE,0xE1,0xAA,0x3D,0xDF,0x37};   // Cambie la clave de la tarjeta PICC.     char Configvalida[3]; //If Config is Vaild, Tag " TK " is required"   };    MiFareClassicKeyTable MiFareClassicKey;
WiFiEEPromData MyWiFiConfig;

// Variables utilizadas globalmente
bool Resultado  = false;
bool LearnNewCard = false;
bool EraseCard = false; 
bool ExpirationDateActive = false;
Cadena Surname;
Cadena Givenname;
Cadena ExpirationDate;
Cadena temp;
sin firma largo Sessionida;
sin firma largo PCD_ServiceCall_Handler = 0;
sin firma largo PCD_WatchDog_Handler = 0;
uint8_t Buffer de datos[18]  = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };


void setup()     {    pinMode(RST_PINA,Salida);   write digital(RST_PINA,HIGH);   pinMode(RELAIS_PIN,Salida);   pinMode(RGBLED_R,Salida);   pinMode(RGBLED_G,Salida);   pinMode(RGBLED_B,Salida);   write digital(RELAIS_PIN,HIGH);    // Relés inactivos   SetRGBLed(255,0,255,false);       //LED color Lila inicializar   Serial.begin(115200);               / Iniciar la comunicación serie con el ordenador personal con 115200 Baud   Serial.println("");   temp = "ATSN:"+ Cadena(ESP.getChipId());   Serial.println(temp);   // Serial.print ("ADC Value:"); Serial.println(analogRead(A0)));   SPI.begin();                      // Iniciando comunicación SPI   PCDHardReset();   Sessionida = Milis();    Resultado  = startWiFiClient();     Servidor initalizhtp();   SetRGBLed(0,0,255,false);       // LED Color Azul inicialización completa   // ESP.wdtEnable (WDTO_4S); / / inicio Watchdog   }


// ******************* Start Helper/ Optimization Functions ********************************

void SetRGBLed(bytes RedValue,bytes GreenValue,bytes BlueValue,boolean SlowFade)  // Radion para el control de la rgb Led
{   write digital(RGBLED_R,LOW);       write digital(RGBLED_G,LOW);   write digital(RGBLED_B,LOW);    if (RedValue == 255)   { write digital(RGBLED_R,HIGH); }      if (GreenValue == 255) { write digital(RGBLED_G,HIGH); }   if (BlueValue == 255)  { write digital(RGBLED_B,HIGH); }    }

// ******************* Stop Helper/ Optimization Functions *********************************

// ******************* Start Functions Servidor Web *******************************************

// Cookie las rutinas de base se basan en la salida GIT:
//https://github.com/esp8266/ESPWebServer/blob/master/examples/SimpleAuthentification/SimpleAuthentification.ino
bool is_authentified()
{     if (servidor.hasHeader("Cookie")){       // Cookie encontrada      temp = servidor.cabecera("Cookie");       // Serial.println (temp));      Cadena SessionStr = Cadena(ESP.getChipId()) + "=" + Cadena(Sessionida);      yield();      if (temp.indexOf(SessionStr) != -1) {         // Web Authentification exitosa        temp = "";        return true;       }     }        // Web Authentification falló    temp = "";    Sessionida = Milis();    return false; 
} 

void línea de mano(){   Cadena msg;   // String cookie = servidor.cabecera("Cookie")");   // Serial.println(cookie));   if (servidor.hasArg("DISCONNECT")){     // Disconnection Users;     servidor.radiotelefonía("Location","/login");     servidor.radiotelefonía("Cache-Control","no-cache");     Sessionida = Milis();     temp = Cadena(ESP.getChipId()) + "=NA ; HTTP: / / SameSite=Strict";     servidor.radiotelefonía("Set-Cookie",temp);     temp = "";     servidor.send(301);     yield();     return;   }   if (servidor.hasArg("USERNAME") && servidor.hasArg("PASSWORD")){     temp = Cadena(ESP.getChipId());     if (servidor.arg("USERNAME") == "administrador" &&  servidor.arg("PASSWORD") == temp ){       servidor.radiotelefonía("Location","/");       servidor.radiotelefonía("Cache-Control","no-cache");       Sessionida = Milis();       temp = Cadena(ESP.getChipId()) + "=" + Cadena(Sessionida) + "; HttpOnly ; SameSite=Strict";       servidor.radiotelefonía("Set-Cookie",temp);       temp = "";       servidor.send(301);       yield();       return;     }   msg = "<script>alert ('nombre de usuario falso o contraseña incorrecta !'); < / script>";   }    CSS_Header_Template();    yield();    temp = "<head><title>Login< / title>< / head> & lt;body><DIV ALIGN=CENTER>";    servidor.sendContent(temp);    temp = "<h2>inscripción a lector de tarjetas RC522< / h2><body><br>< br>< table border=0 bgcolor=negro><tr><th>< DIV ALIGN=RIGHT>;";    servidor.sendContent(temp);    temp = "<form action='/login 'method=' post ' >Nombre de usuario: <input type=text Name=' USERNAME ' Size=17 required><br>;";    servidor.sendContent(temp);    temp = "Contraseña: <input type=password Name='PASSWORD' Size=17 required><br / ><br / ><br / ><button type='submit' ";    servidor.sendContent(temp);    temp = "nombre='Login_Button' value='1' style='height: 30px; width: 100px' >Login</button><br / ></th></tr></form></DIV></table>";    servidor.sendContent(temp);    temp = "<br><SMALL>para que el acceso funcione, se permiten Cookies para este sitio web.& lt; / SMALL>";      servidor.sendContent(temp);    temp = msg + "< / DIV>< / cuerpo>< / HTML>;";    servidor.sendContent(temp);    temp = "";
}

void pista de acción()    {   Sessionida = Milis();   temp = "Página no encontrada.\n\n";   temp += "URI: ";   temp += servidor.uri();   temp += "\nmetod: ";   temp += (servidor.método() == HTTP_GET)?"GET":"POST";   temp += "\nArguments: ";   temp += servidor.args();   temp += "\n";   for (uint8_t i=0; i<servidor.args(); i++){     temp += " " + servidor.argName(i) + ": " + servidor.arg(i) + "\n";   }   yield();   servidor.send(404, "texto / plain", temp);   temp = "";   }



void handleErasePICC()
{   if (!is_authentified())     {     servidor.radiotelefonía("Location","/login");     servidor.radiotelefonía("Cache-Control","no-cache");     servidor.send(301);     yield();     return;     }     CSS_Header_Template();   yield();   temp = "<head><title>lector de mapas RC522< / title></head> & lt;body>";   servidor.sendContent(temp);   HtmlNavStructure();   temp = "<script>alert ('por favor, mantenga la carta que debe eliminarse delante del lector.'); < / script>";      servidor.sendContent(temp);    yield();   SetRGBLed(0,255,255,false);       // LED Color modo de programación cian   EraseCard = true;         temp = "< / cuerpo>< / html>";   servidor.sendContent(temp);   servidor.cliente().stop();   temp = "";
}


void handleNewPICC()
{   if (!is_authentified())     {     servidor.radiotelefonía("Location","/login");     servidor.radiotelefonía("Cache-Control","no-cache");     servidor.send(301);     return;     }   if (servidor.hasArg("Surname") && servidor.hasArg("Givenname"))   {      Surname = servidor.arg("Surname");     Givenname = servidor.arg("Givenname");     ExpirationDate = servidor.arg("ExpDate");     if (servidor.hasArg("Opción de exploración")) { ExpirationDateActive = true; } else { ExpirationDateActive = false; }     temp = "<script>alert ('por favor, mantenga la nueva carta delante del lector!'); < / script>";        servidor.sendContent(temp);      SetRGBLed(255,255,0,false);       // LED Color Amarillo Modo De Programación     LearnNewCard = true;     yield();     return;       }         CSS_Header_Template();   yield();   temp = "<head><title>lector de mapas RC522< / title></head> & lt;body>";   servidor.sendContent(temp);   HtmlNavStructure();   temp = "";   temp = "<br / ><br / ><br / ><br / ><table border=0 ALIGN=CENTER><th>";   servidor.sendContent(temp);   temp = "<table border=1 bgcolor = black><form action=' / newPICC 'method=' post ' >;";   servidor.sendContent(temp);   temp = "<tr><th> titular de la tarjeta:<br>< div ALIGN=RIGHT>";   servidor.sendContent(temp);   temp = "Nombre: <input type=text Name= 'Surname' Size=17 maxlenght=16 placeholder= 'Max' required><br>";   servidor.sendContent(temp);   temp = "Apellido: <input type=text Name= 'Givenname' Size=17 maxlenght=16 placeholder= 'modelo' required><br>";   servidor.sendContent(temp);   temp = "< / div>< / th><th>metadatos de cartas:<br><DIV ALIGN=RIGHT>;";   servidor.sendContent(temp);       temp = "<input Name='ExpDateOption' TYPE=checkbox VALUE=1 >Fecha de caducidad:<input type=date Name= 'ExpDate' Size = 17 >;";   servidor.sendContent(temp);   temp = "<br / ><th><tr><th></table><br / >";   servidor.sendContent(temp);   temp = "<button type= 'submit' name= 'NewCard' value= '1' style= 'height: 30px; width: 200px' >Crear una nueva tarjeta inteligente< / button>";   servidor.sendContent(temp);   temp = "<br>< / form>< / tr>< / th>< / table>;";   servidor.sendContent(temp);   temp = "< / cuerpo>< / html>";   servidor.sendContent(temp);   servidor.cliente().stop();   yield();     temp = "";
}

void handleRoot()
{   if (!is_authentified()){     servidor.radiotelefonía("Location","/login");     servidor.radiotelefonía("Cache-Control","no-cache");     servidor.send(301);     return;     }   // HTML Content   CSS_Header_Template();   yield();   temp = "<head><title>lector de mapas RC522< / title></head> & lt;body>";   servidor.sendContent(temp);   HtmlNavStructure();   temp = "<div ALIGN=CENTER><br><br><br> & lt;br><BIG> Bienvenido al sitio web del lector de tarjetas inteligentes RC522.< / BIG><br>";   servidor.sendContent(temp);   temp = "Fondo de reinicio: " + Cadena(ESP.getresetreasona()) + "<br>";   servidor.sendContent(temp);   temp = "Espacio De Cabeza Libre: " + Cadena(ESP.getfreap()) + "Bytes<br>";   servidor.sendContent(temp);   temp = "Int. Flash: " + Cadena(ESP.chiprealsize de getflash()) + "Bytes<br>";   servidor.sendContent(temp);   Resultado = mfrc522.Pcd_performselfest();    mfrc522.PCD_Init();                       // Inicializando módulo de lectura MFRC522   mfrc522.PCD_SetAntennaGain(mfrc522.RxGain_max); // Pone antena en max. Recepción   mfrc522.Pcd_antennaona();    yield();   if (Resultado) {temp = "Estado del PCD RC522: OK< br>"; } else {temp = "Estado PCD RC522: ¡error!<br>"; }    servidor.sendContent(temp);   temp = "ID DE LA CPU: " + Cadena(ESP.getChipId()) + " @ " + Cadena(ESP.getCpuFreqMHz()) + "MHz<br>";    servidor.sendContent(temp);   temp = "<br> ¡ha sido registrado con éxito !<br><br><form action=' / login 'method=' get ' >;";   servidor.sendContent(temp);   temp = "<button type= 'submit' name='DISCONNECT' value='YES' style= 'height: 30px; width: 200px' >Logout< / button>";   servidor.sendContent(temp);   temp = "< / form>< / div>< / body>< / html>";   servidor.sendContent(temp);   if (servidor.hasArg("Reboot") )  // Reboot System      {     // ESP.wdtFeed();             ESP.wdtDisable();     temp = "<script>alert('el sistema se reiniciará ahora.'); < / script>";        servidor.sendContent(temp);     servidor.cliente().stop();     yield();     temp = "";     ESP.reset();     delay(4000);      }     servidor.cliente().stop();   temp = "";
}

void CSS_Header_Template() // Estilo para todas las páginas web de ESP internas. https://wiki.selfhtml.org/wiki/CSS   {    servidor.setContentLength(CONTENT_LENGTH_UNKNOWN);    temp = "";    servidor.send (200, "texto / html", temp);    temp = "¡<!DOCTYPE HTML PUBLIC '- / / / W3C/ / DTD HTML 4.01 Transitional//EN'><html lang= ' de '><meta charset= 'UTF-8' >";    servidor.sendContent(temp);    temp = "<style type='text/css'>*{margin: 0;padding: 0;}body{background:black;color:darkorchid;font-size: 16px;"; servidor.sendContent(temp); temp = "font-family: sans-serif,arial;}.nav{width: 1300px;height: 30px;margin: 0 auto;radio de la frontera: 5px;}";    servidor.sendContent(temp);    temp = "ul li{list-style: none;width: 200px;line-height: 60px;position: relative;background: darkorchid;"; servidor.sendContent(temp); temp = "box-shadow: 0px 2px 5px 0px grey;text-align: center;float: left;background-color: #010000;}ul li ul{";    servidor.sendContent(temp);    temp = "posición: absolute;}.nav > ul > li: nth-of-type (1) {radio de la frontera: 5px 0px 5px;}.nav > ul > li:nth-of-type (5)";    servidor.sendContent(temp);    temp = "{border-radius: 0px 5px 5px 0px;}ul li a{color: rgb(182, 18, 18);width: 200px;height: 58px;display: inline-block;"; servidor.sendContent(temp); temp = "text-decoration: none;}ul li a:hover{font-weight: bold;border-bottom: 2px solid #fff;}ul li ul{display: none;}";    servidor.sendContent(temp);    temp = ".nav ul li:hover ul{display: block;}.fa{margin-right: 5px;}.contenedor{width: 1000px;height: 200px;"; servidor.sendContent(temp); temp = "margin: 0 auto;padding:20px 20px;}@media screen and (max-width: 480px){header{width: 100%;}";    servidor.sendContent(temp);    temp = ".nav{display: none;width: 100%;height: auto;}ul li{width: 100%;float: none;}ul li a{width: 100%;"; servidor.sendContent(temp); temp = "display: block;}ul li ul{position: static;}ul li ul li a{background: #222;}.fa-list.modify{display: block;}";    servidor.sendContent(temp);    temp = ".container{width: 100%;height: auto;} body{overflow-x:hidden;}< / style>";    servidor.sendContent(temp);    temp = "";   }


void HtmlNavStructure()   {   temp = "<div class= 'menu' ><nav class= ' nav ' ><ul>";   servidor.sendContent(temp);   temp = "<li>< a href= ' # ' > Sistema</a>;";   servidor.sendContent(temp);   temp = "<ul>< li>< a href=' / '>Información</a>< / li>";   servidor.sendContent(temp);   temp = "<li>< a href="/es/?Reboot=YES">reinicio< / A>< / li>;";   servidor.sendContent(temp);   temp = "< / ul>";   servidor.sendContent(temp);   temp = "< / li>< li>< a href= ' #'>PICC</A>;";   servidor.sendContent(temp);   temp = "<ul><li>< a href=' / newPICC ' >Crear un nuevo mapa</a>< / li>;";   servidor.sendContent(temp);   temp = "<li>< a href=' / erasePICC ' >borrar mapa</a>< / li>< / ul>;";   servidor.sendContent(temp);   temp = "< / li>";   //temp = " </li><li><a href='#'>Acta de eventos< / A>< / li>;";   servidor.sendContent(temp);   temp = "< / ul>< / nav>< / div>;";   servidor.sendContent(temp);   temp = "";   }       void Servidor initalizhtp()    {   bool initok = false;   const char * headerkeys[] = {"User-Agent","Cookie"} ; // Cabecera de pista   size_t headerkeysize = sizeof(headerkeys)/sizeof(char*); // Cabecera de pista   servidor.on("/", handleRoot);   servidor.on("/login", línea de mano);   servidor.on("/newPICC", handleNewPICC);   servidor.on("/erasePICC", handleErasePICC);   servidor.onNotFound ( pista de acción );   servidor.colectheaders(headerkeys, headerkeysize );// Server    servidor.begin(); / Inicio del servidor Web   }        // ******************* End Functions Servidor Web *********************************************


// ******************* Start Functions WiFi Management *************************************
// Función de https://www.az-delivery.de/blogs/azdelivery-blog-fur-arduino-und-raspberry-pi/wps-mit-dem-esp8266?ls=de
bool startWPS() 
{   bool wpsuccess = WiFi.beginWPSConfig();   if(wpsuccess) {       /¡No debe ser siempre un éxito! Después de un tiempo, la SSID está vacía.       Cadena newsida = WiFi.SSID();        if(newsida.length() > 0) {         // Sólo cuando se encontró una SSID tuvimos éxito          yield();          Serial.println("ATWPS: OK");         saveCredentials(); // Save Credentials to EEPROM              } else {         Serial.println("ATWPS:NOK");       }   }   return wpsuccess; 
}

bool startWiFiClient() 
{   bool WiFiClientStarted = false;   size_t A0_ADCValue = 0;   bytes i = 0;   bytes connRes = 0;   Serial.setDebugOutput(false);  // Activar para depuración.    WiFi.hostname("Crdrr417667");   WiFi.softAPdisconnect(true);   WiFi.disconnect();   WiFi.modo(WIFI_STA);   if(loadCredentials())      {      WiFi.begin(MyWiFiConfig.APSTAName, MyWiFiConfig.WiFiPwd);      while (( connRes != 3 ) and( connRes != 4 ) and (i != 30))  // if connRes = = 0 " IDLE_STATUS-change Statius"       {        i++;       // Serial.print ("."); / / Connect observar el proceso en el puerto serie       // ESP.wdtFeed();       delay(500);       yield();       connRes  = WiFi.waitForConnectResult();       }      if (connRes == 4 ) { // if password is incorporrect       Serial.println("ATWIFI: PWDERR");             WiFi.disconnect();       }      if (connRes == 6 ) { // module is not configured in station mode       Serial.println("ATWIFI: STAERR");       WiFi.disconnect();       }      }   if(WiFi.estado() == WL_CONNECTED)      {     // ESP.wdtFeed();      Serial.print("ATIP:");     Serial.println(WiFi.localIP());     WiFi.setAutoReconnect(true); // Set whether module will attempt to reconnect to an access point in case it is disconnected.     // Setup MDNS respondedor     if (!MDNS.begin("Crdrr417667"))        {       Serial.println("ATMDNS: NOK");       } else { MDNS.addService("http", "tcp", 80); }        WiFiClientStarted = true;     } else      {     A0_ADCValue = analogRead(A0);     //No hemos tenido éxito, así que empezamos WPS cuando WPS Taster está presionado a A0 durante el Reset     if (A0_ADCValue > 499)       {         if(startWPS())            {             // ESP.wdtFeed();              delay(500);             WiFi.disconnect();             WiFi.modo(WIFI_STA);             WiFi.begin(WiFi.SSID().c_str(), WiFi.psk().c_str());             // ESP.wdtFeed();              WiFiClientStarted = true;           } else           {             WiFiClientStarted = false;             WiFi.disconnect();           }       } else       {         WiFi.disconnect();       }    }    /WiFi.printDiag (Serial); / / para depuración.   return WiFiClientStarted; 
}
// ******************* END Functions WiFi Management *************************************

// ******************* Start Functions Store WiFi Credentials to EEPROM ******************
bool loadCredentials() 
{
 bool RetValue;
 EEPROM.begin(512);
 EEPROM.get(0,MyWiFiConfig);
 EEPROM.end();
 if (Cadena(MyWiFiConfig.Configvalida) == "TK")    {     RetValue = true;   } else   {     RetValue = false; // WLAN Settings no encontrado.   }   // ESP.wdtFeed();    return RetValue; 
}

void saveCredentials() // Memoria WLAN credentials en EEPROM 
{   size_t i;   for (i = 0 ; i < sizeof(MyWiFiConfig) ; i++) // Abrir la configuración anterior      {       EEPROM.write(i, 0);       }   for (i = 0 ; i < STANameLen  ; i++) // Abrir la configuración anterior      {       MyWiFiConfig.WiFiPwd[i] = 0;       }   for (i = 0 ; i < WiFiPwdLen ; i++) // Abrir la configuración anterior      {       MyWiFiConfig.APSTAName[i] = 0;       }      temp = WiFi.SSID().c_str();   i = temp.length();   temp.toCharArray(MyWiFiConfig.APSTAName,i+1);    temp = WiFi.psk().c_str();   i = temp.length();   temp.toCharArray(MyWiFiConfig.WiFiPwd,i+1);   temp = "";      strncpy(MyWiFiConfig.Configvalida , "TK", sizeof(MyWiFiConfig.Configvalida) );    EEPROM.begin(512);   EEPROM.put(0, MyWiFiConfig);   EEPROM.commit();   EEPROM.end();   // ESP.wdtFeed(); 
}
// ******************* END Functions StoreCredentialsto EEPROM ***************************

// ******************* Start Functions CardServices  *************************************


void PCDHardReset()
{   write digital(RST_PINA,LOW);   delay(200);   write digital(RST_PINA,HIGH);   mfrc522.PCD_Reset();   mfrc522.PCD_Init();                              // Inicializando módulo de lectura MFRC522   mfrc522.PCD_SetAntennaGain(mfrc522.RxGain_max); // Pone antena en max. Recepción   mfrc522.Pcd_antennaona();
}

boolean Cardautenticate(boolean Abreviatura, bytes Sector,bytes ikey[6])
{
const bytes sectorkeytable [16] = {3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63};
bytes statusA;
statusA = 0;
for (int a = 0; a < 6;a++)   {   clave.keyByte[a] = ikey[a];   }   // Key A
if (Abreviatura)   {   statusA = mfrc522.Pcd_autenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, sectorkeytable[Sector], &clave, &(mfrc522.uid));   if (statusA != MFRC522::STATUS_OK)     {     Serial.println("ATAUTH: ERR_A");     return false;     }       }   // Clave B   else if (not Abreviatura)   {   statusA = mfrc522.Pcd_autenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B,sectorkeytable[Sector], &clave, &(mfrc522.uid));   if (statusA != MFRC522::STATUS_OK)     {     Serial.println("ATAUTH: ERR_B");     return false;     }        }
return true;   }

/ WriteData . uses Global Variable DataBuffer for Data Return
boolean CardDataWrite(bytes Sector,bytes bloque,bytes value[16])
{
bytes estado;
bytes writevector;
bytes sectorkeytable [16] = {3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63};
writevector = Sector * 4 + bloque -1 ;
for (bytes a = 0; a < 16; a++)   {   if (writevector ==  sectorkeytable[a])     {     // Serial.println ("NAK");     return false;     }   }
estado = mfrc522.MIFARE_Write(writevector, value, 16);
if (estado != MFRC522::STATUS_OK)   {   Serial.println("ATPCD: W_ERR");   // Serial.println(mfrc522. GetStatus Codename(estado)));   return false;   } else    {   // Serial.print ("OK");   return true;   }   }

// Read Data-uses Global Variable DataBuffer for Data Return
boolean CardDataRead(bytes Sector,bytes bloque)
{
bytes estado;
bytes readvector;
const bytes sectorkeytable [16] = {3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63};
bytes sized = 18;
readvector = Sector * 4 + bloque -1 ;
for (bytes a = 0; a < 16; a++)   {   if (readvector ==  sectorkeytable[a])     {      Serial.println("ATPCD: R_ERR");       return false;     }   }
estado = mfrc522.MIFARE_Read(readvector, Buffer de datos, &sized);
if (estado != MFRC522::STATUS_OK)   {   Serial.println("ATPCD: R_ERR");   return false;   } else    {   return true;   }   }

boolean ResetCardToDefault()
{
bytes ikey[16];
bytes estado,i;
bytes writevector;
const bytes sectorkeytable [16] = {3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63};
writevector = sectorkeytable[USED_Sector];
if (Cardautenticate(KEYB,USED_Sector,MiFareClassicKey.Key_B)) // Sector Autenticate for WRITE Access      {       for (i = 0; i <= 16; i++) { Buffer de datos[i] = 0; }           if (!(CardDataWrite(USED_Sector,1,Buffer de datos))) { return false; }        for (i = 0; i <= 16; i++) { Buffer de datos[i] = 0; }          if (!(CardDataWrite(USED_Sector,2,Buffer de datos))) { return false; }       for (i = 0; i <= 16; i++) { Buffer de datos[i] = 0; }          if (!(CardDataWrite(USED_Sector,3,Buffer de datos))) { return false;}      }
for (bytes i = 0; i <= 16; i++) { ikey[i] = 255; }  //Load Default Key for all Sectors 
ikey[6] = 0xFF; // Default Setting for Access Bits
ikey[7] = 0x07; // 
ikey[8] = 0x80; // 
ikey[9] = 0x69;    estado = mfrc522.MIFARE_Write(writevector, ikey, 16);
if (estado != MFRC522::STATUS_OK)   {   return false;   }
return true;   }

boolean SetSectorAccessControl (bytes Sector,bytes Akey[6],bytes Bkey[6])
{
bytes ikey[16];
bytes estado;
bytes writevector;
const bytes sectorkeytable [16] = {3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63};
writevector = sectorkeytable[Sector];
ikey[0] = Akey[0];
ikey[1] = Akey[1];
ikey[2] = Akey[2];
ikey[3] = Akey[3];
ikey[4] = Akey[4];
ikey[5] = Akey[5];
ikey[6] = 0x78; // Data Block 0-3 Access Conditions: Key B write / Key A Read
ikey[7] = 0x77; // KEY A & KEY B & Acces Bits Write:Key B / Key A Read Access Bits
ikey[8] = 0x88; // Calculator: http://calc.gmss.ru/Mifare1k/
ikey[9] = 0x69; // Valor fijo - > default hex 69
ikey[10] = Bkey[0];
ikey[11] = Bkey[1];
ikey[12] = Bkey[2];
ikey[13] = Bkey[3];
ikey[14] = Bkey[4];
ikey[15] = Bkey[5];
estado = mfrc522.MIFARE_Write(writevector, ikey, 16);
if (estado != MFRC522::STATUS_OK)   {   Serial.println("ATPCD: W_KEY_ERR");   return false;   }else    {   return true;   }   }

boolean CheckforDefaultCardKey ()
{
bytes tkey[6];
boolean CardResult; 
bytes readvector;
bytes estado;
const bytes sectorkeytable [16] = {3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63};
bytes sized = 18;
for (bytes i = 0; i <= 6; i++) { tkey[i] = 255; }  //Load Default Key for all Sectors 
CardResult = true;   if (!Cardautenticate(KEYA,USED_Sector,tkey)) { CardResult = false; };
readvector = sectorkeytable[USED_Sector];
estado = mfrc522.MIFARE_Read(readvector, Buffer de datos, &sized);
if (estado != MFRC522::STATUS_OK) { CardResult = false; } 
/¡if!((Memoria de datos[7] = 0x07) & (memoria de datos [7] = 0x80))) { CardResult = false; };
return CardResult; 
}

boolean Writenewmifareclassicpiccpicc ()   {   bytes tkey[6];   bytes i,a;   boolean CardResult;   if (CheckforDefaultCardKey())     {     for (i = 0; i <= 6; i++) { tkey[i] = 255; }  //Load Default Key for all Sectors      for (i = 0; i <= 16; i++) { Buffer de datos[i] = 0; } // Clear Variable Buffer     CardResult = true;            if (Cardautenticate(KEYA,USED_Sector,tkey)) // Sector Autenticate       {       // Serial.println ("Auth Sec 0 OK");        if (Surname.length() > 15) { a = 15; } else { a = Surname.length();}       if (Surname.length() > 0)         {         for (i = 0; i <= 16; i++) { Buffer de datos[i] = 0; }             for (i = 0; i <= a; i++) { Buffer de datos[i] = Surname[i]; }            if (!(CardDataWrite(USED_Sector,2,Buffer de datos))) { CardResult = false; } // Sector 0 Block 2 Nombre con Key A         }       if (Givenname.length() > 15) { a = 15; } else { a = Givenname.length(); }       if (Givenname.length() > 0)         {         for (i = 0; i <= 16; i++) { Buffer de datos[i] = 0; }          for (i = 0; i <= a; i++) { Buffer de datos[i] = Givenname[i]; }          if (!(CardDataWrite(USED_Sector,3,Buffer de datos))) { CardResult = false; } // Sector 0 Block 3 escribir el apellido con Key A         }             if (!(SetSectorAccessControl (USED_Sector,MiFareClassicKey.Key_A,MiFareClassicKey.Key_B))) { CardResult = false; }    // (byte Sector, byte Akey[6], byte Bkey[6])       } else {               CardResult = false;              return CardResult;              }        } else {               CardResult = false;              return CardResult;              }          if (CardResult)        {        // Serial.println ("PICC written");            CardResult = true;       }     else        {        // Serial.println ("PICC not empty");       CardResult = false;       }     yield();       return CardResult;   }


boolean Readmifareclassicpiccpicc ()
{
boolean CardResult;
bytes i,a ;
CardResult = true;    if (Cardautenticate(KEYA,USED_Sector,MiFareClassicKey.Key_A)) // Sector Autenticate with READ Key A   {   Givenname = "aaaaaaaaaaaaaaaaaaaaaaaaaa"; // PlaceHolder   Surname = "aaaaaaaaaaaaaaaaaaaaaaaaaa"; // PlaceHolder   for (i = 0; i < 18; i++) { Buffer de datos[i] = 0; } // Clear Variable Buffer   if (CardDataRead(USED_Sector,2)) // Campo extraer nombre     {         for (i = 0; i < 16; i++) { Surname[i] = char(Buffer de datos[i]);  }             } else {             return false;            }     for (i = 0; i < 18; i++) { Buffer de datos[i] = 0; } // Clear Variable Buffer          if (CardDataRead(USED_Sector,3)) // Campo extraer apellido       {                for (i = 0; i < 16; i++) { Givenname[i] = char(Buffer de datos[i]); }              } else {               return false;                 }      } else      {      return false;     }
Serial.print ("ATAUTH_S:");
Serial.println (Surname);
Serial.print ("ATAUTH_G:");
Serial.println (Givenname);
return true;
}

void CardServer()
{
# define PCD_Poll_Interval 400
# define PCD_Watchdog_Interval 60000
if (Milis() - PCD_ServiceCall_Handler >= PCD_Poll_Interval)    {    PCD_ServiceCall_Handler = Milis();        if (mfrc522.PICC_IsNewCardPresent())   // PICC = tarjeta inteligente sin contacto     {      mfrc522.PICC_ReadCardSerial();       yield();     // Distinción por tipo de tarjeta     // 0x08 para MIFARE Classic 1K     // 0x18 para MIFARE Classic 4K     // 0x11 para MIFARE PLUS     if (mfrc522.uid.sak == 0x08 || mfrc522.uid.sak == 0x18)       {       // MiFare_Classic_Processor START (mfrc522. uid.sak); / / / ejecutar sólo si una tarjeta Mifare Classic se ha mantenido delante del lector.       bytes tkey[6];        for (bytes i = 0; i <= 6; i++) { tkey[i] = 255; }  //Load Default Key for all Sectors        if(LearnNewCard) // se aprenderá un nuevo mapa.         {           if (Writenewmifareclassicpiccpicc()) { SetRGBLed(0,255,0,false); } else { SetRGBLed(255,0,0,false); }         }       else if (EraseCard)  // Eliminar datos de tarjetas.         {           if (ResetCardToDefault()) { SetRGBLed(0,255,0,false); } else { SetRGBLed(255,0,0,false); }         }             else         {         if (Readmifareclassicpiccpicc())            { /¡Tarjeta válida !           bool PinState= digitalRead(RELAIS_PIN);           PinState = !PinState;           write digital(RELAIS_PIN, PinState);           SetRGBLed(0,255,0,false);        // Led Verde           } else { SetRGBLed(255,0,0,false); }               }       LearnNewCard = false;                     // MiFare_Classic_Processor STOP (mfrc522. uid.sak);       } else if (mfrc522.uid.sak == 0x00) // Mifare Ultralight       {         SetRGBLed(255,0,0,false);       } else       {         SetRGBLed(255,0,0,false);         // Serial.print ("PICC Type not supported. Type:");          // Serial.println(mfrc522.uid.SAK); //extensión: evtl otros tipos de tarjetas         }     mfrc522.PCD_StopCrypto1();     mfrc522.PICC_HaltA();     delay(2000);     SetRGBLed(0,0,255,false);        // Led Color Azul lector básico     }   }   if (Milis() - PCD_WatchDog_Handler >= PCD_Watchdog_Interval)    {    PCD_WatchDog_Handler = Milis();    Resultado = mfrc522.Pcd_performselfest();    yield();    mfrc522.PCD_Init();                       // Reiniciando módulo de lectura MFRC522    mfrc522.PCD_SetAntennaGain(mfrc522.RxGain_max); // Pone antena en max. Recepción    mfrc522.Pcd_antennaona();     yield();    if (!(Resultado))      {       PCDHardReset();       Serial.println("ATPCD: ERR_H");     }    }   }

// ******************* Stop Functions CardServices  *************************************


 
void loop()  // Bucle principal
{   CardServer();          // Editar peticiones específicas del lector de tarjetas   yield();    servidor.handleClient(); // Editar solicitudes de servidor web    // ESP.cambiar wdtFeed(); // Watchdog. Desactivar con " wdt_disable();"
}

 

Los datos de conexión Wi-Fi se mantienen porque los guardamos en una memoria no volátil en el Sketch anterior.

Pero, ¿cómo funciona ahora la autenticación del mapa?

En pocas palabras, el módulo de lectura de cartas intenta leer un sector que sólo es legible con la clave válida cuando se presenta una tarjeta del tipo MiFare Classic (esto se comprueba antes).

Si la acción tiene éxito, los campos de nombre se leerán el nombre y el apellido y el mapa se reconocerá como válido. (Conmutando los relés) si la autenticación no es satisfactoria o si se utiliza la clave incorrecta, La tarjeta se rechazará.

La distinción entre "tarjeta válida o no válida" en esta característica nos ofrece las siguientes ventajas::

El lector puede tener derecho a utilizar varias tarjetas al mismo tiempo, sin que sea necesario que cada tarjeta se ponga a disposición del lector previamente por medio de una UUID.

Se pueden utilizar al mismo tiempo varios lectores de tarjetas con una tarjeta sin que cada lector de tarjetas conozca cada tarjeta previamente por UUID.

La variación de la definición del sector o de los materiales clave permite crear "circuitos de cálculo".

 

Estamos creando nuestra primera tarjeta de acceso. Para ello, después de Login en el elemento de menú "PICC", después "crear un nuevo mapa", pulse en él. Vemos la página siguiente:

Introducimos un nombre y un apellido en los campos previstos y pulsa el botón "Crear una nueva tarjeta inteligente"“

El LED se vuelve amarillo. ¡Estamos sosteniendo un vacío!! La tarjeta Mifare Classic se presenta ante el lector y espera a que la LED se ponga verde. ¡Eso es todo! De ahora en adelante, podemos usar esta tarjeta como tarjeta autorizada para nuestro lector.

Si queremos invalidar este mapa, vamos a Login en PICC, luego en "Borrar mapa" y pulse en él. -- Tras la notificación,:

volvamos a poner la tarjeta antes autorizada delante de los lectores. Esta es inválida.

Que te diviertas reconstruyendo hasta la siguiente parte.

Esp-8266Für arduinoProjekte für fortgeschritteneSmart home

Deja un comentario

Todos los comentarios son moderados antes de ser publicados