Search code examples
node.jsexpressauthenticationnfcmifare

Error 6800 on reading 2nd sector and more of a MIFARE Classic card


I use nfc-pcsc to read a MIFARE Classic 1K card with the ACR122U reader (USB connection). But I often get a 0x6800 error:

  • Sometimes when I read 1st sector (blocks 4-5-6)
  • Always when I read 2nd sector and more (blocks 8-9-10, 12-13-14, etc.)

But a native software like NFC-GUI can read/write all data with no difficulties.

The keys A/B are default keys (000000.... and FFFFFF....). The access bits have default values of an empty card.

Why this error? How can I read the card without error?

Note that I can read a MIFARE Ultralight card without difficulties.

NodeJS code (same as example of NFC-PCSC):

const { NFC } = require('nfc-pcsc');

const TAG_ISO_14443_3 = 'TAG_ISO_14443_3'; // ISO/IEC 14443-3 tags
const TAG_ISO_14443_4 = 'TAG_ISO_14443_4'; // ISO/IEC 14443-4 tags
const KEY_TYPE_A = 0x60;
const KEY_TYPE_B = 0x61;


const nfc = new NFC(); // optionally you can pass logger

nfc.on('reader', reader => {

    console.log(`${reader.reader.name}  device attached`);

    reader.on('card', card => {

        const CLASSIC_1K = '000100000000';
        const CLASSIC_4K = '000200000000';
        const ULTRALIGHT = '000300000000';


        console.log(`${reader.reader.name}  card detected`, card);

        let buf     = card.atr;
        let type    = buf.slice(0,12).toString('hex').toUpperCase();
        let version = null;

        if (type == '3B8F8001804F0CA000000306')
        {
            version = card.atr.slice(13,19).toString('hex');
            switch (version)
            {
                case '000100000000':
                    console.log('Mifare Classic 1k');
                    break;
                case '000200000000':
                    console.log('Mifare Classic 4k');
                    break;
                case '000300000000':
                    console.log('Mifare Ultralight');
                    break;
                default:
                    console.log('Other card');
            }
        }

        if (version == ULTRALIGHT)
        {
            .... (no difficulties)
        }
        else if (version == CLASSIC_1K)
        {

            const key_a = '000000000000'; 
            const key_b = 'FFFFFFFFFFFF'; 
            const keyTypeA = KEY_TYPE_A;
            const keyTypeB = KEY_TYPE_B;

            Promise.all([
                // Sector 1
                reader.authenticate(4, keyTypeB, key_b),
                reader.authenticate(5, keyTypeB, key_b),
                reader.authenticate(6, keyTypeB, key_b),
                // Sector 2
                reader.authenticate(8, keyTypeB, key_b),
                reader.authenticate(9, keyTypeB, key_b),
                reader.authenticate(10, keyTypeB, key_b),
            ]).
            then(() => {
                console.info(`blocks successfully authenticated`);
                reader.read(4, 32, 16) // Often OK
                .then(data => {
                    console.info(`data read`, data.toString());
                    return reader.read(8, 32, 16); // Always error
                })
                .then(data => {
                    console.info(`data read`, data.toString());
                }
                .catch(err => {
                    console.error(`error when reading data`);
                })
            })
            .catch(() => {
                console.info(`athentification error`);
            });

        }


    });

    reader.on('card.off', card => {
        console.log(`${reader.reader.name}  card removed`);
    });

    reader.on('error', err => {
        console.log(`${reader.reader.name}  an error occurred`, err);
    });

    reader.on('end', () => {
        console.log(`${reader.reader.name}  device removed`);
    });

});

nfc.on('error', err => {
    console.log('an error occurred', err);
});

Solution

  • It seems that you first authenticate to all sectors and then try to read some data from these sectors. However, this is not how MIFARE Classic authentication works. Instead, will need to authenticate to a sector (e.g. use reader.authenticate(4, keyTypeB, key_b) to authenticate with key B for the whole sector 1). You can then read data from any block of that sector (where key B is granted read access). After you finished reading from that sector, you can authenticate to the next sector (e.g. reader.authenticate(8, keyTypeB, key_b) to authenticate with key B for the whole sector 2). You can then read data from any block of that sector (where key B is granted read access).

    Also note that the default configuration for "empty" MIFARE Classic cards is Key A = FFFFFFFFFFFF, Key B = not used, read/write with Key A only. Since, the areas containing the keys are not readable (unless a key is not used), reading "000000000000" from those memory regions usually just means that no data could be read, the actual key could still be some other value. Thus, if you are sure that the cards a empty and have default permissions set, I would suggest to try to authenticate using "FFFFFFFFFFFF" as key A:

    const key_a = 'FFFFFFFFFFFF'; 
    const keyTypeA = KEY_TYPE_A;
    await reader.authenticate(4, keyTypeA, key_a);
    console.info(`sector authenticated`);
    const data1 = await reader.read(4, 48, 16);
    console.info(`data read for sector 1`, data1.toString());
    await reader.authenticate(8, keyTypeA, key_a);
    const data2 = await reader.read(8, 48, 16);
    console.info(`data read for sector 2`, data2.toString());
    

    Note: It seems that the source of confusion was misleading documentation in the MIFARE Classic example that shipped with nfc-pcsc. The documentation has been updated.