PN532 akses Value Block Pada MIFARE ISO14443A

MIFARE classic merupakan kartu tanpa kontak buatan NXP Semiconductors yang sudah umum digunakan sebagai nfc-tag yang dapat dibuat menjadi seperti e-wallet, kartu keamanan, kartu absen maupun kartu untuk memberikan perintah kepada HP. Dalam kartu NFC ada berbagai format yang umum digunakan seperti NDEF, APDU ataupun format MIFARE classic biasa, pada artikel ini akan dibahas cara mengakses memory MIFARE classic menggunakan NFC reader PN532 khususnya untuk mengolah value block sehingga dapat digunakan seperti dompet digital:

  1. PN532 Board
  2. Jumper male female
  3. NFC Tag ISO14443, seperti: EL-MF1A
  4. Arduino Mega 2560 beserta USB nya

Langkah-langkah

Langkah 1 : Penjelasan Value BLock MIFARE Classic 1K EV-1 ISO 14443 Type A

Komunikasi yang digunakan untuk melakukan akses ke memory dari PN532 adalah I2C, untuk setting dan persiapan pada PN532 dapat dilihat pada artikel dengan link berikut Persiapan Hardware dan Software.

Setelah persiapan sudah dilakukan pertama-tama perlu diketahui bahwa isi memori dari MIFARE classic 1k terbagi menjadi blok manufaktur, blok data, sector trailer dan blok value. karena blok data sudah dicontohkan pada artikel sebelumnya, maka dari artikel ini akan membahas lebih dalam tentang blok value, pahami dahulu isi dari blok value.

gambar 1

Pada blok tersebut sudah terdapat format yang harus dipenuhi agar meningkatkan keamanan data yang disimpan pada blok value, pada bit ke 12 hingga ke 15 merupakan address yang diatur sebagai backup sehingga data tetap aman. untuk membuat blok seperti diatas hanya dapat menggunakan perintah write contoh penulisan data bernilai 0 pada value block dapat dilihat pada tabel dibawah.

0000ffffffff000032cd32cd

Pada tabel diatas berarti dompet yang berisi 0 dan block 50 (0x32) sebagai backup.

Setelah blok sudah diatur, maka perlu mengatur akses bit dari blok tersebut sehingga blok terdaftar sebagai value blok.

gambar 2

Pada gambar 2 dapat dilihat bagian akses bit yang dapat melakukan proses add, substract, transfer dan restore hanya ada pada nomor 0 dan 6, maka dari itu kita atur blok yang ingin kita jadikan sebagai dompet digital sesuai dengan akses bit tersebut, dibawah diatur dahulu akses bitnya menjadi seperti tabel dibawah.

Bit 7Bit 6Bit 5Bit 4Bit 3Bit 2Bit 1Bit 0
0x1401011101
0x0500100111
0x2010001010

jika akses bit sudah menjadi seperti pada tabel diatas maka atur isi dari blok 1 pada sektor tersebut menjadi seperti diabwah ini.

0000ffffffff000032cd32cd

Data sudah siap dijadikan seperti dompet digital.

Langkah 2 : Praktek Dengan Program

Program yang digunakan untuk melakukan akses dari kartu memori NFC adalah program dibagian paling bawah, cara menggunakannya adalah dengan bantuan serial monitor dengan format sebagai berikut.

KondisiStarterModeNomor Blok 1Nomor Blok 2
1/r2540
2/r250
3/r
4/w25
5/i4950
6/d4950
  • Kondisi 1 adalah melakukan pembacaan memori pada blok 25 hingga 40
  • Kondisi 2 adalah melakukan pembacaan memori pada blok 25
  • Kondisi 3 adalah melakukan pembacaan memori keseluruhan
  • Kondisi 4 adalah melakukan pengisian variabel isi data pada memori pada blok 25
  • Kondisi 5 adalah melakukan Increment pada blok 49 dan hasilnya disimpan di blok 50
  • Kondisi 6 adalah melakukan Decrement pada blok 49 dan hasilnya disimpan di blok 50

Sebagai contoh kita akan menggunakan blok ke 49 sebagai dompet. dan blok 50 sebagai tempat backup, setelah variabel isi data sudah diatur selanjutnya kita atur akses bit sesuai dengan yang dibahas dilangkah sebelumnya dengan key A dan key B diisi 0xff semua untuk mempermudah contoh dengan mengatur isi data menjadi seperti dibawah ini.

uint8_t isiData[16] =
{0xff,0xff,0xff,0xff,0xff,0xff,0x5d,0x27,0x8a,0,0xff,0xff,0xff,0xff,0xff,0xff};

Jika sudah maka upload program dan isi data tersebut ke sektor trailer dengan mengetikan /w51 pada serial monitor.

gambar 3

Selanjutnya kita isi dahulu blok ke 49 dengan cara menulis isi blok pada variable blok menjadi seperti dibawah ini.

uint8_t isiData[16] = {0,0,0,0,0xff,0xff,0xff,0xff,0,0,0,0,0x32,0xcd,0x32,0xcd};

Dengan cara yang sama ketikan /w49.

gambar 4

Jika sudah maka blok ke 49 sudah terisi dan siap digunakan sebagai dompet digital.

Setelah selesai kita sudah bisa melakukan increment dan decrement, setelah increment dan decrement dilakukan maka isi data akan tersimpan di internal transfer buffer, maka dari itu perlu melakukan perintah transfer untuk mengambil data dari internal transfer buffer.

Namun dari program sudah diatur ketika melakukan increment langsung saja pilih data increment disimpan dimana, contoh disini isi data akan melakukan increment senilai 5, maka isi dulu variabel incData menjadi seperti berikut.

uint8_t incData[4] = {0,0,0,5};

Setelah itu gunakan perintah /i49,50 untuk melakukan increment dan menyimpan nya pada blok 50 sehinggan outpunya akan menjadi seperti berikut.

gambar 5

Setelah itu jika dibaca blok 49 dan 50 dengan menggunakan perintah /r49,50 akan menjadi seperti berikut.

gambar 6

Untuk perintah decrement sama dengan perintah increment cukup ganti huruf i dengan huruf d.

#include <Wire.h>
#include <PN532_I2C.h>
#include <PN532.h>
#include <NfcAdapter.h>
 
PN532_I2C pn532i2c(Wire);
PN532 nfc(pn532i2c);
 
#define IRQ   (2)
#define RESET (3)  // Not connected by default on the NFC Shield
uint8_t keyuniversal[6] = {255,255,255,255,255,255};
//uint8_t isiData[16] = {0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x32,0xCD,0x32,0xCD};
uint8_t isiData[16] = {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
uint8_t incData[4] = {0,0,0,5};
bool    auth = true;
 
uint8_t success;
uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 };
uint8_t uidLength;
bool authenticated = false;
bool readMode,writeMode;
unsigned short int purseMode;
uint8_t data[16];
int tes;
 
void setup(void) {
  // has to be fast to dump the entire memory contents!
  Serial.begin(115200);
  Serial.println("Looking for PN532...");
 
  nfc.begin();
 
  uint32_t versiondata = nfc.getFirmwareVersion();
  if (! versiondata) {
    Serial.print("Didn't find PN53x board");
    while (1); // halt
  }
  // Got ok data, print it out!
  Serial.print("Found chip PN5"); Serial.println((versiondata>>24) & 0xFF, HEX);
  Serial.print("Firmware ver. "); Serial.print((versiondata>>16) & 0xFF, DEC);
  Serial.print('.'); Serial.println((versiondata>>8) & 0xFF, DEC);
 
  // configure board to read RFID tags
  nfc.SAMConfig();
 
  Serial.println("Waiting for an ISO14443A Card ...");
}
 
void loop() {
  if(Serial.available()){
    String input = Serial.readStringUntil("\n");
    if(input.substring(0,1)=="/"){
      if(input.substring(1,2)=="r") readMode = true;
      if(input.substring(1,2)=="w") writeMode = true;
      if(input.substring(1,2)=="i") purseMode = 1;
      if(input.substring(1,2)=="d") purseMode = 2;
        if(readMode){
          if(input.length()==4){
            check(0,63);
            input="";
          }
          if(input != ""){
            int idx;
            input = input.substring(2);
            idx = input.indexOf(',');
            if(idx>0){
              int input1 = input.substring(0,idx).toInt();
              int input2 = input.substring(idx+1).toInt();
              if((input1>0 && input1<64) &&(input2>0 && input2<64) && (input2 > input1)){
                check(input1,input2);
                input="";
              }else{
                Serial.println("Input Tidak Valid\n\n\n");
              }
            }else{
              int checkNum = input.toInt();
              if(checkNum>=0 && checkNum<64){
                check(checkNum,checkNum);
                input="";
              }
            }
          }
 
      }else if(writeMode){
        //isi data block sebesar 16 byte didalam array
        int wInput;
        input = input.substring(2);
        wInput = input.toInt();
        if(wInput == 0 || wInput > 63 || wInput < 0){
          Serial.println("Input Tidak Valid");
        }else if(wInput %4 == 3){
          Serial.println("Pengecekan Block \n\n\n");
          if(blockCheck(isiData[6],isiData[7],isiData[8])){
            Serial.println("Block Valid\n\n\n");
            writeBlock(wInput , isiData, 16);
          }
          else{
            Serial.println("Block Tidak Valid");
          }
        }
        else{
          writeBlock(wInput , isiData, 16);
          input="";
        }
      }else if(purseMode > 0){
        int idx;
        input = input.substring(2);
        idx = input.indexOf(',');
        if(idx>0){
          int incBlock;
          int fetchBlock;
          int input1 = input.substring(0,idx).toInt();
          int input2 = input.substring(idx+1).toInt();
          incBlock = input1;
          fetchBlock = input2;
 
          if((input1>0 && input1<64) &&((input2>0 && input2<64) && input2%4 != 3)){
              if(purseMode == 1){
//                Serial.println("Increment");
                purse(incBlock,fetchBlock,1);
              }else if(purseMode == 2){
//                Serial.println("Decrement");
                purse(incBlock,fetchBlock,0);
              }
              purseMode =0;
          }else{
            Serial.println("Input Tidak Valid\n\n\n");
          }
        }
      }
    }
  }
 
}
 
void purse(int incBlock,int fetchBlock,bool mode){
  if(isValFormat(incBlock)){
    success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);
    if(success){
      if(uidLength == 4){
          success = nfc.mifareclassic_AuthenticateBlock (uid, uidLength, incBlock, auth, keyuniversal);
          if(success){
            if(mode){
              success = nfc.mifareclassic_Increment(incBlock, incData);
//              Serial.println("Mode Increment");
            }else{
              success = nfc.mifareclassic_Decrement(incBlock, incData);
//              Serial.println("Mode Decrement");
            }
 
          }else{
            success = false;
            Serial.println("failed to increment");
          }
          if(success){
            Serial.println("Value block to transfer block");
            Serial.println("Trying to retrieve transfer block.... ");
            success = nfc.mifareclassic_AuthenticateBlock (uid, uidLength, fetchBlock, auth, keyuniversal);
            if(success){
              success = nfc.mifareclassic_Transfer(fetchBlock);
              if(success){
                Serial.println("Retrieve completed ");
              }else{
                Serial.println("Failed to retrieve");
              }
            }
          }
        }
    }
  }else{
    Serial.println("Block format is wrong..");
  }
 
}
 
bool isValFormat(int incBlock){
  bool valid;
  check(incBlock,incBlock);
  for(int i =0;i<4;i++){
    if(((data[i] == data[i+8]) ? ((data[i] == (data[i+4]^0xff))? true :false) : false)){
      valid = true;
    }
    else{
      valid = false;
      return valid;
    }
  }
  valid = data[12] == data[14] ? (data[12] == (data[13] ^ 0xFF) ? (data[12] == (data[15] ^ 0xFF) ? true : false ): false) : false ;
 
  return valid;
}
 
 
 
void flushSerial(){
  while(Serial.available()>0){
    Serial.read();
  }
}
 
void check(uint8_t block1,uint8_t block2){
  success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);
  if(success){
    Serial.println("\n\nFound an ISO14443A card");
    nfc.PrintHex(uid, uidLength);
    Serial.println("");
    if(uidLength == 4){
      for(int currentBlock = block1;currentBlock<=block2;currentBlock++){
        success = nfc.mifareclassic_AuthenticateBlock (uid, uidLength, currentBlock, auth, keyuniversal);
        if(currentBlock%4 == 0 && block1 != block2){
          Serial.print("------------------------Sector ");Serial.print(currentBlock/4, DEC);Serial.println("-------------------------");
        }
        if(!success){
          Serial.println("Authentication error");
          Serial.println("Block ");Serial.print(currentBlock, DEC);Serial.println(" unable to authenticate");
        }else{
          success = nfc.mifareclassic_ReadDataBlock(currentBlock, data);
          if(success){
            Serial.print("Block ");
            Serial.print(currentBlock, DEC);
            if(currentBlock<10) Serial.print("  ");
            if(currentBlock>10) Serial.print(" ");
            nfc.PrintHexChar(data, 16);
          }
          else{
            Serial.print("Block ");
            Serial.print(currentBlock, DEC);
            Serial.println(" unable to read this block");
          }
        }
      }
 
    }
    else if(uidLength == 7){
      Serial.println("This is Mifare Ultralight Card");
    }
  }
  readMode = false;
}
 
void writeBlock(int currentBlock,uint8_t arrayBlock[],int panjang){
  success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength);
  if(success){
    Serial.println("Found an ISO14443A card");
    nfc.PrintHex(uid, uidLength);
    Serial.println("");
    if(uidLength == 4){
      success = nfc.mifareclassic_AuthenticateBlock (uid, uidLength, currentBlock, auth, keyuniversal);
      if(!success){
        Serial.println("Authentication error");
        Serial.println("Block ");Serial.print(currentBlock, DEC);Serial.println(" unable to authenticate");
      }else{
        success = nfc.mifareclassic_WriteDataBlock(currentBlock, arrayBlock);
        if(success){
          Serial.println("Data berhasil diubah\n\n");
        }
        else{
          Serial.print("Block ");
          Serial.print(currentBlock, DEC);
          Serial.println(" unable to Write this block");
        }
      }
 
 
    }
    else if(uidLength == 7){
      Serial.println("This is Mifare Ultralight Card");
    }
  }
  writeMode = false;
 
}
 
bool blockCheck(byte b6, byte b7,byte b8){
  bool valid = false;
  if(((b6 & 0xF0) >> 4) ^ 0x0F == b8 & 0x0F) {
    if((b6 & 0x0F) ^ 0x0F == ((b7 & 0xF0) >> 4)) {
      if((b7 & 0x0F) ^ 0x0F == (b8 & 0xF0) >> 4) {
        valid = true;
      }
    }
  }
  return valid;
}
 

Untuk tutorial dalam bentuk video dapat dilihat dalam video berikut