Search code examples
androidauthenticationnfcmifarelow-level

How to read the Mifare Classic tag using the NfcA class?


I have an NFC tag with support for NfcA and MifareClassic technologies. After a firmware update, my phone does not support the MifareClassic technology anymore. On older firmware, reading the MifareClassic tag worked fine.

Is it possible to use the NfcA class to read the Mifare Classic tag? How can this be done?

The key for authenticating the technology of Mifare Classic A0A1A2A3A4A5 (For example)

public static String[] readTag(Tag tag) {
    byte[] readedData;
    byte[] PASSWORD = new byte[]{(byte) 0xA0, (byte) 0xA1, (byte) 0xA2, (byte) 0xA3, (byte) 0xA4, (byte) 0xA5};
    NfcA nfca = NfcA.get(tag);
    try {

        nfca.connect();
        readedData = nfca.transceive(new byte[]{
                (byte) 0x30,
                (byte) (0 & 0x0ff),PASSWORD // (for example)
        });
    } catch (Exception e) {
    }
}

Commands for transceive


Solution

  • First of all, in order to communicate (i.e. authenticate, perform read/write operations) with MIFARE Classic tags, you will need a device with NFC hardware that supports MIFARE Classic. Due to NXP's licensing policies, this is typically only possible on devices with NFC chipsets from NXP. Other NFC chipsets will usually only allow you to perform anti-collision and enumeration (i.e. detect the tag and read its (N)UID).

    Since you indicated that the capability to access MIFARE Classic (through the MifareClassic tag technology object) was lost as a result of a firmware update, I would assume that your NFC chipset is capable of accessing MIFARE Classic.

    NXP's NFC controllers transparently abstract access to MIFARE Classic tags with MIFARE reader commands (plain-text commands for authentication, binary read/write, and value block operations). The chipset automatically takes care of translating these abstract commands to actual MIFARE Classic commands, mutual authentication, and session encryption. The MifareClassic tag technology object implements the following commands:

    • authenticateSectorWithKeyA(sectorIndex, key):
      +----------+-------------+--------------------+-------------------+
      |   0x60   | BLOCK_INDEX | UID (last 4 bytes) |       KEY_A       |
      | (1 byte) |  (1 byte)   |     (4 bytes)      |     (6 bytes)     |
      +----------+-------------+--------------------+-------------------+
      
    • authenticateSectorWithKeyB(sectorIndex, key):
      +----------+-------------+--------------------+-------------------+
      |   0x61   | BLOCK_INDEX | UID (last 4 bytes) |       KEY_B       |
      | (1 byte) |  (1 byte)   |     (4 bytes)      |     (6 bytes)     |
      +----------+-------------+--------------------+-------------------+
      
    • readBlock(blockIndex):
      +----------+-------------+
      |   0x30   | BLOCK_INDEX |
      | (1 byte) |  (1 byte)   |
      +----------+-------------+
      
    • writeBlock(sectorIndex, data):
      +----------+-------------+--------------------+
      |   0xA0   | BLOCK_INDEX |        DATA        |
      | (1 byte) |  (1 byte)   |     (16 bytes)     |
      +----------+-------------+--------------------+
      
    • increment(blockIndex, value):
      +----------+-------------+-------------------+
      |   0xC1   | BLOCK_INDEX |       VALUE       |
      | (1 byte) |  (1 byte)   |     (4 bytes)     |
      +----------+-------------+-------------------+
      
    • decrement(blockIndex, value):
      +----------+-------------+-------------------+
      |   0xC0   | BLOCK_INDEX |       VALUE       |
      | (1 byte) |  (1 byte)   |     (4 bytes)     |
      +----------+-------------+-------------------+
      
    • transfer(blockIndex):
      +----------+-------------+
      |   0xB0   | BLOCK_INDEX |
      | (1 byte) |  (1 byte)   |
      +----------+-------------+
      
    • restore(blockIndex):
      +----------+-------------+
      |   0xC2   | BLOCK_INDEX |
      | (1 byte) |  (1 byte)   |
      +----------+-------------+
      

    For current NFC controllers (those that use NCI; not applicable to e.g. PN544) these commands are wrapped into special NCI commands by the Android NFC system service (see phNxpExtns.c and phNxpExtns_MifareStd.c).

    Depending on why your device fails to enumerate the MifareClassic tag technology, you might be lucky and the NFC stack of your device already handles that wrapping. In that case, you should be able to send the above commands using the NfcA tag object.

    However, you device might fail to enumerate the MifareClassic tag technology because it simply detects the tag as regular Type 2 tag (or other NFC-A tag). In that case, NativeNfcTag.cpp will not perform the additional wrapping. You might still be able to acutally create the wrapped commands by following the same strategy that's performed in phNxpExtns.c and phNxpExtns_MifareStd.c. However, I'm not sure what other side-effects the incorrect detection might have (e.g. different interface initialization).