NFC?
NFC (Near Field Communication) é um padrão de comunicação via rádio a pequenas distâncias (até 10 cm). Não por acaso, este padrão é o mesmo usado na comunicação dos cartões e tags MIFARE.
O Shield
O coração do shield NFC (cuja documentação pode ser vista na loja e na wiki do fabricante) é o chip PN532 do NXP. É um circuito integrado bastante versátil (inclui um microcontrolador); o firmware suporta uma quantidade grande de recursos (o manual tem 200 páginas e não entra nos detalhes do comandos MIFARE).
Felizmente existe uma biblioteca para uso do PN532 (desenvolvida pela Adafruit com colaborações do SeeedStudio). Ela disponibiliza apenas o básico, mas é o suficiente para algumas experiências. Eu usei a versão no site da Elecfreaks, mas no site da Adafruit tem um versão mais nova.
A comunicação entre o PN532 e o Arduino é via SPI, os demais pinos permanecem disponíveis (a placa possui conectores para interligar um outro shield). A antena está na própria placa, o alcance é limitado a 1 cm.
Cartões e Tags
Para fazer testes precisamos de alguns cartões e tags. Eu comprei alguns no SeeedStudio, todos com 1K byte de memória:
- Cartões
- Tag em formato de chaveiro
- Tag adesivo (etiqueta para livros)
![]() |
Minha coleção de tags MIFARE |
![]() |
Tag MIFARE no formato de chaveiro |
![]() |
A etiqueta adesiva permite ver a antena. |
O objetivo desta demonstração é exercitar a gravação e leitura de dados e o uso das chaves de segurança. O software requer a biblioteca PN532:
- // Exemplo de leitura/escrita em cartão/tag MIFARE com 1K de memória
- // usando um NFC Shield baseado no PN532 da NXP
- // Código a partir de exemplos do Seeed Technology Inc (www.seeedstudio.com)
- #include <PN532.h>
- // Pinos usados para conexão
- #define SCK 13
- #define MOSI 11
- #define SS 10
- #define MISO 12
- // Objeto para acesso ao PN532
- PN532 nfc(SCK, MISO, MOSI, SS);
- // Chave de fabrica
- // Pode mudar conforme o fabricante do cartao/tag !
- uint8_t chave_fab[]= {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
- // Chaves para o nosso teste
- uint8_t chave_A[]= {0x01,0x02,0x03,0x04,0x05,0x06};
- uint8_t chave_B[]= {0x06,0x05,0x04,0x03,0x02,0x01};
- // Blocos que vamos usar
- // No MIFARE 1K os blocos são numerados de 0 a 63
- // Cada setor tem 4 blocos, o último bloco do setor é de configuração
- const uint8_t bloco_dados = 12; // protegeBloco assume que é o 1o bloco do setor
- const uint8_t bloco_cfg = 15; // CUIDADO COM O QUE ESCREVER AQUI
- // Iniciacao
- void setup(void) {
- Serial.begin(9600);
- Serial.println("Demonstracao MIFARE");
- Serial.println();
- nfc.begin();
- uint32_t versiondata = nfc.getFirmwareVersion();
- if (! versiondata) {
- Serial.print("NFC shield nao encontrada");
- while (1); // fica parado aqui
- }
- // Informacoes sobre o shield
- Serial.print("Achou NFC Shield ");
- Serial.println((versiondata>>24) & 0xFF, HEX);
- Serial.print("Firmware ver. ");
- Serial.print((versiondata>>16) & 0xFF, DEC);
- Serial.print('.');
- Serial.println((versiondata>>8) & 0xFF, DEC);
- // configura a placa
- nfc.SAMConfig();
- }
- // Laco principal
- void loop(void) {
- uint32_t id;
- char cmd [3];
- uint8_t bloco[16];
- uint8_t *pChave;
- // procura um cartao ou tag MIFERE
- id = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A);
- if (id == 0) {
- // nao achou
- delay (500); // aguarda meio segundo
- return; // tenta de novo
- }
- Serial.println();
- Serial.print("Achou cartao #");
- Serial.println(id);
- Serial.print("Cmd: ");
- leCmd (cmd);
- switch (cmd[0]) {
- case 'P': // Protege
- if (!protegeBloco (id)) {
- Serial.println ("Erro ao proteger o bloco!");
- } else {
- Serial.println ("Bloco protegido.");
- }
- break;
- case 'D': // Desprotege
- if (!cfgPadrao (id))
- {
- Serial.println ("Erro ao retornar a config de fabrica!");
- } else {
- Serial.println ("Retornado a config de fabrica.");
- }
- break;
- case 'L': // Lê
- pChave = chave_fab;
- if (cmd[1] == 'A')
- pChave = chave_A;
- else if (cmd[1] == 'B')
- pChave = chave_B;
- if (!nfc.authenticateBlock(1, id, bloco_dados,
- (cmd[1] == 'B')? KEY_B : KEY_A, pChave))
- {
- Serial.println ("Autenticacao falhou!");
- break;
- }
- if (!nfc.readMemoryBlock(1, bloco_dados, bloco))
- {
- Serial.println ("Erro na leitura!");
- break;
- }
- dump (bloco);
- break;
- case 'E': // Escreve
- pChave = chave_fab;
- if (cmd[1] == 'A')
- pChave = chave_A;
- else if (cmd[1] == 'B')
- pChave = chave_B;
- if (!nfc.authenticateBlock(1, id, bloco_dados,
- (cmd[1] == 'B')? KEY_B : KEY_A, pChave))
- {
- Serial.println ("Autenticacao falhou!");
- break;
- }
- for (int i = 0; i < 16; i++)
- bloco[i] = i;
- if (!nfc.writeMemoryBlock(1, bloco_dados, bloco))
- {
- Serial.println ("Erro na escrita!");
- break;
- }
- dump (bloco);
- break;
- }
- esperaEnter();
- }
- // Configura as chaves A e B
- // Escrita com chave B
- // Leitura com A ou B
- int protegeBloco (uint32_t id) {
- uint8_t bloco[16];
- // Monta a configuração
- memcpy (bloco, chave_A, 6);
- bloco[6] = 0x78;
- bloco[7] = 0x77;
- bloco[8] = 0x88;
- bloco[9] = 0;
- memcpy (bloco+10, chave_B, 6);
- dump (bloco);
- // Autentica no bloco de configuracao
- if (!nfc.authenticateBlock(1, id, bloco_cfg, KEY_A, chave_fab)) {
- Serial.println ("Autenticacao com chave da fabrica na cfg falhou!");
- return false;
- }
- // Grava o bloco de configuracao
- if (!nfc.writeMemoryBlock (1, bloco_cfg, bloco)) {
- Serial.println ("Erro ao gravar cfg!");
- return false;
- }
- return true;
- }
- // Volta a configuracao de fabrica
- int cfgPadrao (uint32_t id) {
- uint8_t bloco[16];
- // Monta a configuração
- memcpy (bloco, chave_fab, 6);
- bloco[6] = 0xFF;
- bloco[7] = 0x07;
- bloco[8] = 0x80;
- bloco[9] = 0;
- memcpy (bloco+10, chave_fab, 6);
- // Autentica no bloco de configuracao
- if (!nfc.authenticateBlock(1, id, bloco_cfg, KEY_B, chave_B)) {
- Serial.println ("Autenticacao com chave B na cfg falhou!");
- return false;
- }
- // Grava o bloco de configuracao
- if (!nfc.writeMemoryBlock (1, bloco_cfg, bloco)) {
- Serial.println ("Erro ao gravar cfg!");
- return false;
- }
- return true;
- }
- // Le um comando finaliado por ENTER
- void leCmd (char *cmd)
- {
- int i = 0;
- int c;
- while (true) {
- c = Serial.read();
- if (c == -1)
- continue;
- if (c == 0x0d)
- break;
- if ((c >= 'a') && (c <= 'z')) {
- c -= 0x20;
- }
- if (i < 4)
- cmd[i++] = c;
- }
- cmd[i] = 0;
- Serial.println (cmd);
- }
- // Aguarda receber um ENTER
- void esperaEnter() {
- Serial.print ("Digite ENTER...");
- while (Serial.read() != 0x0d)
- ;
- Serial.println ();
- }
- // Lista o conteudo de um bloco
- void dump (uint8_t *pBloco)
- {
- for (uint8_t i=0; i<16; i++)
- {
- Serial.print(*pBloco++, HEX);
- Serial.print(" ");
- }
- Serial.println();
- }
A chave de fábrica utilizada na demonstração é a dos cartões e tags que eu tenho. Cartões e tags de outro fabricante podem ter uma chave diferente.
O transcript abaixo mostra a seguinte sequência de operações:
- O setor de dados é lido com a chave de fábrica.
- São reprogramadas as chaves e as condições de acesso.
- Não é mais possível a leitura do setor de dados com a chave de fábrica.
- A leitura do setor de dados pode ser feita com as chaves A e B.
- A escrita no setor de dados só pode ser feita com a chave B.
- São restauradas as chaves e condições de acesso de fábrica.
Demonstracao MIFARE Achou NFC Shield 32 Firmware ver. 1.6 Achou cartao #706831521 Cmd: L 0 1 2 3 4 5 6 7 8 9 A B C D E F Digite ENTER... Achou cartao #706831521 Cmd: P 1 2 3 4 5 6 78 77 88 0 6 5 4 3 2 1 Bloco protegido. Digite ENTER... Achou cartao #706831521 Cmd: L Autenticacao falhou! Digite ENTER... Achou cartao #706831521 Cmd: LA 0 1 2 3 4 5 6 7 8 9 A B C D E F Digite ENTER... Achou cartao #706831521 Cmd: LB 0 1 2 3 4 5 6 7 8 9 A B C D E F Digite ENTER... Achou cartao #706831521 Cmd: EA Erro na escrita! Digite ENTER... Achou cartao #706831521 Cmd: EB 0 1 2 3 4 5 6 7 8 9 A B C D E F Digite ENTER... Achou cartao #706831521 Cmd: D Retornado a config de fabrica. Digite ENTER... Achou cartao #706831521 Cmd: L 0 1 2 3 4 5 6 7 8 9 A B C D E F Digite ENTER...ATENÇÃO: ao reprogramar as configurações de segurança você corre o risco de perder o acesso ao setor. Se você for fazer experiências, se prepare para ficar com um cartão ou tag "capenga"!
Nenhum comentário:
Postar um comentário