W tym poście chciałbym opisać sposób odczytu tagów NFC NDEF wykonanych na karcie Mifare za pomocą programu NFC Tools.
Struktura karty:
Do przygotowania tagu NFC wykorzystałem czystą kartę Mifare:
Następnie na karcie zapisałem w formacie tekstowym następującą wiadomość abcdefgh1234567890 czyli 25 bajtów. Do zapisu wykorzystałem wspomniany wcześniej program NFC Tools. Poprawny zapis na danych do karty wymaga aby była ona czysta, bez żadnych danych w sektorach z zabezpieczonymi własnymi kluczami (jeśli karta nie ma domyślnych kluczy dostępu to należy ją sformatować). Informacje zostały zapisane w kodzie ASCII co oznacza, że w sektorze powinny znajdować się bajty:
0x61 (a); 0x62 (b); 0x63 (c); 0x64 (d); 0x65 (e); 0x66 (f); 0x67 (g); 0x68 (h);
0x31 (1); 0x32 (2); 0x33 (3); 0x34 (4); 0x35 (5); 0x36 (6); 0x37 (7); 0x38 (8); 0x39 (9); 0x30 (0);
Struktura karty wygląda następująco:
Jak widać powyżej karta sformatowana będzie zawierała sektor MAD, w kolejnych sektorach będą przechowywane wiadomości NDEF.
Powyżej sektor MAD składa się z bajtu CRC,
Dane z zmodyfikowanego sektora 1:
Sektor 0 - A0A1A2A3A4A5 - Klucz standardowy dla sektora MAD
Sektor od 1 - D3F7D3F7D3F7
Nie został jedynie zmodyfikowany sektor 0 blok 0, gdzie zapisany jest numer seryjny karty.
Zmianie uległy także bajty tzw. AcessBits z domyślnych FF078069 na:
787788 C1 -
Bloki danych z konfiguracją C1 - 1, C2 - 0, C3 - 0 (zapis możliwy tylko z użyciem klucza A odczyt z użyciem klucza A lub B)
Sektor trailer C1 - 0, C2 - 1, C3 - 1 (Zapis klucza tylko z użyciem klucza B, odczyt bitów konfiguracyjnych z użyciem klucza A lub B)
C1 jest to wartość General Purpouse Byte, który oznacza, że sektor zawiera MAD.
7F0788 40 -
Bloki danych z konfiguracją C1 - 0, C2 - 0, C3 - 0 (wszystkie operacje z użyciem klucza A lub B)
Sektor trailer C1 - 0, C2 - 1, C3 - 1 (Zapis klucza tylko z użyciem klucza B, odczyt bitów konfiguracyjnych z użyciem klucza A lub B)
Kalkulator do obliczenia tych wartości dostępny pod tym linkiem.
W celu przechowywania danych na karcie w formacie NDEF, wiadomość musi być zapisana w bloku zwanym TLV
Dane które chcemy odczytać zostały zapisane w sektorze 1;
- 0x00 - Rekord 1
- 0x00 - Rekord 1
- 0x03 - Rozpoczęcie danych dla Rekord 2. Blok zawiera wiadomość NDEF.
- 0x19 - Długość danych 25 decymalnie. Bez bajtu końca bloku TLV
- 0xD1 - Nagłówek wiadomości NDEF
- 0x01 - Type Length Długość danych.
- 0x15 - długość danych Payload. 21 bajtów dziesiętnie.
- 0x54 - typ danych. W tym przypadku 'T' czyli tekst. Wartość 0x55 oznacza wiadomość URI.
- 0x02 - Informacja o kodowaniu UTF8, tzw. Language Code dwa kolejne bajty.
- 0x65, 0x6E - 'e' 'n'. Czyli wiadomość zapisana w języku angielskim.
- PAYLOAD - 21 bajtów
- 0xFE - TLV Terminator, oznaczenie końca bloku TLV.
Aplikacja odczytana za pomocą programu NFC Tools zwraca następujące informacje:
Odczyt danych PN532:
W celu odczytania danych wystarczy pobrać dane z sektorów.
Na samym początku procedura uwierzytelnienia sektora. Wszystkie bloki można odczytać za pomocą klucza A (D3 F7 D3 F7 D3 F7) :
- Mifare_StatusTypeDef Mifare_AuthMifare_SectorA(uint8_t tg, uint8_t sector, uint8_t* keyA, uint8_t *uid, uint8_t uidLen, uint16_t timeout){
- uint8_t sendFrame[] = {MIFARE_CMD_AUTH_A, sector*4,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x66, 0xE9, 0xAC, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
- /* Prepare the frame for send */
- for(uint8_t i=0; i<6; i++) { sendFrame[2+i] = *(keyA + i); }
- for(uint8_t i=0; i<uidLen; i++ ) { sendFrame[8+i] = *(uid + i); }
- if(PN532_InDataExchange_Auth(sendFrame, 8+uidLen, tg, timeout) != PN532_OK) {
- return MIFARE_AUTH_ERROR;
- }
- return MIFARE_OK;
- }
lub klucza B (FF FF FF FF FF FF):
- Mifare_StatusTypeDef Mifare_AuthMifare_SectorB(uint8_t tg, uint8_t sector, uint8_t* keyB, uint8_t *uid, uint8_t uidLen, uint16_t timeout){
- uint8_t sendFrame[] = {MIFARE_CMD_AUTH_B, sector*4,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x66, 0xE9, 0xAC, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
- /* Prepare the frame for send */
- for(uint8_t i=0; i<6; i++) { sendFrame[2+i] = *(keyB + i); }
- for(uint8_t i=0; i<uidLen; i++ ) { sendFrame[8+i] = *(uid + i); }
- if(PN532_InDataExchange_Auth(sendFrame, 8+uidLen, tg, timeout) != PN532_OK) {
- return MIFARE_AUTH_ERROR;
- }
- return MIFARE_OK;
- }
Dalej należy odczytać dane z poszczególnych bloków:
- //Mifare_ReadBlock(Mifare_CardData_Struct.selectedTgNumber, 1, 0, &Mifare_CardData_Struct.sector_9_1_data[0], 16, 1000);
- //Mifare_ReadBlock(Mifare_CardData_Struct.selectedTgNumber, 1, 1, &Mifare_CardData_Struct.sector_10_1_data[0], 16, 1000);
- //Mifare_ReadBlock(Mifare_CardData_Struct.selectedTgNumber, 1, 2, &Mifare_CardData_Struct.sector_10_2_data[0], 16, 1000);
- Mifare_StatusTypeDef Mifare_ReadBlock(uint8_t tg, uint8_t sector, uint8_t blockNumber, uint8_t* dataBuffer, uint8_t length, uint16_t timeout){
- uint8_t sendFrame[] = {MIFARE_CMD_READ, (sector * 4) + blockNumber};
- uint8_t response[255] = {0};
- PN532_StatusTypeDef opStatus = PN532_InDataExchange_Read(sendFrame, 2, tg, response, 255, timeout);
- if(opStatus != PN532_OK) {
- return MIFARE_PN532_ERROR;
- }
- for(uint8_t i=0; i<16; i++) {
- *(dataBuffer + i) = response[i];
- }
- return MIFARE_OK;
- }
Teraz wyświetlam odczytane dane:
- standard card type
- AUTH OK
- serial_number: [0A] [80] [C0] [73]
- 1_0 - [00] [00] [03] [19] [d1] [01] [15] [54] [02] [65] [6e] [61] [62] [63] [64] [65]
- 1_1 - [66] [67] [68] [31] [32] [33] [34] [35] [36] [37] [38] [39] [30] [fe] [00] [00]
- 1_2 - [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00] [00]