NFC + Arduino - Parte 3: seguridad

  • gmatarrubia gmatarrubia
  • hace 6 años
NFC + Arduino - Parte 3: seguridad

NFC parte 3 cabecera[blockquote author='Gene Spafford']"El único sistema seguro es aquél que está apagado en el interior de un bloque de hormigón protegido en una habitación sellada rodeada por guardias armados"[/blockquote]

Terminamos con los tutoriales de Arduino + NFC con esta tercera entrega. Si aun no has visto los 2 tutoriales precedentes, te recomiendo que antes de seguir leyendo les eches un ojo. Os dejo los enlaces.

Arduino + NFC - Parte 1

Arduino + NFC - Parte 2

Como podéis adivina,r en esta última parte voy a explicar cómo está gestionada la seguridad en los sistemas basados en NFC. Al igual que en los anteriores casos partimos de la base de que tenemos una shield de Arduino con la que queremos leer o escribir datos de la memoria EEPROM de una etiqueta Mifare 1k. Nos acordamos que para acceder a la información teníamos que autentificarnos usando una clave. La clave la sabíamos porque usábamos la que traía nuestro llavero por defecto, y en general la que suelen traer todas etiquetas Mifare 1k.

A continuación vamos a ver cómo cambiar la clave que viene por defecto a una que pongamos nosotros, cómo hacer que nuestra clave no se puede cambiar más o cómo hacer que la información que contiene no se pueda borrar, entre otras cosas. Cabe destacar que podemos cambiar la clave o los permisos de escritura/lectura por sectores, es decir, que podemos tener un sector con información oculta pero dejar accesible el resto.

Vamos a ver como se dividía la información en cada sector. Recuerda que 1 Sector -> 4 Bloques y que a su vez 1 Bloque -> 16 bytes.

En la imagen vemos la lectura del sector 4 que se corresponde con los bloques 16,17,18 y 19. En la imagen vemos el sector como viene por defecto. Y vemos que tenemos los bloques 16,17 y 18 enteros para guardar información. En caso de que queramos acceder a estos 3 bloques tenemos que pasar la autentificación del bloque 19 que es el "Sector Trailer". Podemos ver como la información del "Sector Trailer" está dividida en 3 grupos. Los 6 primeros bytes (0 - 5) contiene la clave A (KEY_A), los 4 siguientes (6 - 9) contienen la gestión del acceso al sector, y los últimos 6 bytes (10 - 15) contiene la clave B (KEY_B).

Cada una de las claves por tanto tiene 6 valores de 1 byte. Ambas suelen ser por defecto (FF,FF, FF, FF, FF, FF).

Nota: En el caso de la clave A vemos (0,0,0,0,0,0) al leer porque no tenemos acceso a su lectura y nunca lo tendremos, la tenemos que conocer.
Nota: Si modificamos los bits de acceso sin conocimiento podemos corromper el acceso y quedarnos con un bonito llavero que no sirve.

Vamos a explicar por tanto cómo utilizar de manera adecuada los bytes de acceso y por qué por defecto son (FF, 07, 80, 69). La estructura de estos bytes es a nivel bits y conforman una matriz de datos de la siguiente forma.

Vemos como a nivel de bits tenemos ternas de celdas:
  • Las celdas C1_0 C2_0 C3_0 (subíndice cero) definen los permisos al bloque 16.
  • Las celdas C1_1 C2_1 C3_1 (subíndice uno) definen los permisos del bloque 17.
  • Las celdas C1_2 C2_2 C3_2 (subíndice dos) definen el permiso del bloque 18.
  • Las celdas C1_3 C2_3 C3_3 (subíndice tres) que definen los permisos del bloque 19 (Sector Trailer).

Además vemos que el byte 9 es un byte de "propósito general", es decir, que podemos usarlo como queramos.

Para complicar las cosas un poco más, vemos que las celdas están duplicadas pero con sus celdas homologas negadas, esto quiere decir que si queremos poner C1_0 con el valor 1, en C1_0 (negado) tenemos que poner el valor 0. Si esto no se respeta, este sector puede quedar inutilizable.

Vamos a descubrir cómo podemos traducir los permisos que queremos poner con los valores que debemos poner en los 3 bytes de acceso (6 - 8). Para ello vamos a partir de los datos que hemos leído que tenemos actualmente. En el byte 6 tenemos FF que sabemos que está en hexadecimal, lo que hacemos es pasar ese número a binario para ver cada uno de los bits. Y así con los 3 bytes.

Tenemos una matriz resultante que hacemos coincidir con los nombres de la celda a la que corresponde cada uno.

tabla 2Los nombres y valores rosados representan las celdas negadas, los nombres y valores naranjas representan las celdas normales. Como la tabla no queda muy bien organizada, vamos a reorganizarla para facilitar la lectura de los resultados.

tabla 3

Así obtenemos los resultados finales. Vemos como para el bloque 16 tenemos C1 = 0, C2 = 0 y C3 = 0, para el bloque 17 y 18 tenemos los mismos resultados, por lo que tendremos los mismos permisos para los 3 bloques de datos. En cambio tenemos que para el bloque 19, que es el sector trailer tenemos que C1 = 0, C2 = 0 C3 = 1.

Para interpretar estos resultados, tenemos que ir a las siguientes tablas que son las que nos da el estándar de las etiquetas Mifare 1k.

tabla de acceso1

He remarcado con que bloques se corresponde cada tabla. También he remarcado el caso en el que estamos nosotros. Tenemos permisos para leer y escribir en los bloques de datos tanto si nos autentificamos con la KEY_A o con la KEY_B, pero sólo tenemos permiso para cambiar la KEY_A, cambiar los permisos, leer la KEY_B o cambiar la KEY_B si nos autentificamos con la KEY_A. Esto es lo que significan los valores [ FF , 7, 80 ]. El valor 69 lo han puesto así porque les gusta  :P

Extrema la cautela en caso de que quieras modificar los permisos de algún bloque.

Como siempre os dejo un código a modo de ejemplo, en este hacemos un cambio de la KEY_A del sector 1. No cambiamos ninguno de los permisos.

/*
          **************************************************
                       Autor: Gonzalo Matarrubia
          **************************************************             
                       Proyecto: 3o tutorial NFC
                           para Geeky Theory
          **************************************************
          Descripción: Este sketch hace una lectura
           del sector 1. Tras la primera lectura hace 
           un cambio de KEY_A en el mismo sector.
               Luego sigue haciendo lecturas y no 
            pasaremos el proceso de autentificación.
          **************************************************
*/
//Incluimos la librería de la shield NFC
#include <PN532.h>

//Macros
#define SCK         13
#define MOSI        11
#define SS            10
#define MISO        12

//Creamos un objeto NFC de clase PN532
PN532 NFC(SCK,MISO,MOSI,SS);


void setup () {
     Serial.begin(9600);
     if(!Serial) { delay(100); }
     
     //Configuramos el NCF
     NFC.begin();
     
     //Comprobamos que tenemos puesta la shield
     uint32_t versiondata = NFC.getFirmwareVersion();
     if( ! versiondata ) {
          while (1) { delay (100); } //No continuamos con el programa
     }
     //Configuramos el NFC para la lectura y escritura
     NFC.SAMConfig();
}

void loop () {
     // Leemos el número de identificación, mientras no haya ninguna etiqueta valdrá 0
     uint32_t id = NFC.readPassiveTargetID(PN532_MIFARE_ISO14443A);
     
     uint8_t key [ ] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF} ;  //Clave por defecto
     uint8_t key1 [ ] = { 0xAA,0xBB,0xCC,0xDD,0xEE,0xFF} ;  //Nueva Clave
     
     if ( id ) {
          Serial.print("Encontrada etiqueta #");
          Serial.println(id);
          
          /* Nos identificamos para el bloque 4 -> el bloque 4 pertenece 
             sector 1 que va del bloque 4 al 7. El 7 es el sector trailer. */   
          if( NFC.authenticateBlock (1, id, 4, KEY_A, key) ) { 
               
               //Creamos un vector para almacenar la lectura
               uint8_t lectura[16];      
               
               for(int bloque = 4; bloque<=7; bloque++) {
                    //Leemos los bloques del sector
                   if (NFC.readMemoryBlock (1, bloque, lectura) ) {
                         //Mostramos el bloque 4
                         for( int i = 0; i<16 ; i++){
                              Serial.print(lectura[i],HEX);
                              //Para que quede más ordenado
                             Serial.print("  ");
                         }
                         Serial.print (" | Block ");
                         Serial.println(bloque);
                   }               
                   //else Serial.print("Lectura fallida \n"); //Util para hacer debug
               }
           //Creamos un bloque de información para cambiar la clave
           uint8_t datos[16] = {  0xAA, 0xBB,  0xCC,  0xDD,  0xEE,  0xFF, /*Nueva KEY_A*/ // /*Nueva KEY_A*/  
                                             0xFF, 0x7,  0x80, 0x69, /*Dejamos los mismos permisos*/
                                             0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; /*Dejamos la misma KEY_B*/
           Serial.print("Cambiando clave... \n");
           NFC.writeMemoryBlock (1,7, datos);
           Serial.print("Clave cambiada. \n");
          }
     else Serial.print("Autentificacion fallida \n"); //Util para hacer debug
     }
     delay(1500);
}
                    
                         

Os dejo el vídeo en el que vemos el funcionamiento del programa.

[youtube http://www.youtube.com/watch?v=qemm-jSx3LQ&w=770]

Espero que os haya gustado, cualquier duda ponerla en los comentarios.

¡Nos vemos en próximos tutoriales!

¿Qué opinas?