cancel
Showing results for 
Search instead for 
Did you mean: 

Mifare Classic Authentication Failure on ST25R3916B

AMCI
Associate

We are using an ST25R3916B  to read and write to both ST25TV512C tags and Mifare Classic 1K cards. While we have successfully communicated with ST25 tags, we are encountering issues at the authentication stage with Mifare Classic.

Steps We Are Following:

  1. Ensure Activation State

Before sending authentication commands, we ensure the reader is in the RFAL_NFC_STATE_ACTIVATED state.

  1. Send Authentication Command to Retrieve the 4-Byte Challenge

Mifare Classic authentication follows a challenge-response mechanism. To begin, we must send the authentication command and obtain a 4-byte random challenge from the card.

The command consists of:

  • Byte 0: Authentication command (0x60 for Key A, 0x61 for Key B).
  • Byte 1: Target block number.
  • Bytes 2-7: 6-byte key (Key A or Key B).
  • Bytes 8-11: Card UID (copied from the detected card structure).

uint8_t authCmd[12];

authCmd[0] = 0x60; // Authenticate using Key A

authCmd[1] = block; // Block number

memcpy(&authCmd[2], keyA, 6); // Copy the 6-byte key

memcpy(&authCmd[8], device.nfcid, device.nfcidLen); // Copy the UID

 

We then transmit this using rfalTransceiveBlockingTxRx():

status = driver.rfalTransceiveBlockingTxRx(

    authCmd,         // Transmit buffer

    sizeof(authCmd), // Transmit length

    response,        // Receive buffer

    sizeof(response), // Max receive length

    &responseLen,    // Actual response length

    RFAL_TXRX_FLAGS_DEFAULT,

    RFAL_FWT_NONE

);

  • If successful, the card should respond with a 4-byte challenge.
  • This challenge must be processed using the Mifare Classic 48-bit cipher to generate the authentication response.

Current Issue

In the code below, I attempt to send only 4 bytes for authentication, which includes CRC values (D1 and 3D) for 0x60 and 0x04:

uint8_t authCmd[4];

authCmd[0] = 0x60;  // Authentication command (Key A)

authCmd[1] = 0x04;  // Block number

authCmd[2] = 0xD1;  // CRC for 60 and 04

authCmd[3] = 0x3D;  // CRC

 

status = driver.rfalTransceiveBlockingTxRx(

    authCmd,         // Transmit buffer

    sizeof(authCmd), // Transmit length

    response,        // Receive buffer

    sizeof(response), // Max receive length

    &responseLen,    // Actual response length

    RFAL_TXRX_FLAGS_DEFAULT,

    RFAL_FWT_NONE

);

  • I receive ERR_IO (3), which is a generic I/O error.
  • Sometimes, I get ERR_TIMEOUT (4), but I believe ERR_IO is the root cause.

 

What I Need Help With

  • What RFAL calls should I use after reaching RFAL_NFC_STATE_ACTIVATED to retrieve the 4-byte challenge?
  • Am I correctly handling Mifare Classic authentication in RFAL?
  • Should I allow RFAL to handle CRC, or should I manually append it?
  • Any debugging suggestions for ERR_IO?

Any help would be greatly appreciated.

Thanks in advance.

 

3 REPLIES 3
Ulysses HERNIOSUS
ST Employee

Hi AMCI,

you can find some hints here: nfc0541-read-mifare-classic-1k .

Concerning the ERR_IO: No idea really, please debug through and isolate the code line. There should only be very few ERR_IO and in normal operation this error code shouldn't appear.

Regards, Ulysses

MarkU
Associate

I have the same issue with not being able to receive the NONCE upon calling the code here.   After powering up the ST25R3916B, we set it to 3.3V in register 0x01H by setting bit 7 high Updated value of register 0x1 (Binary): 0b10000000.

With or without the register setting, I can successfully get the device, identify it as Mifare Classic 1K (ATQA: 04 00
SAK: 08) and then proceed to the authentication using the code below.

In some other threads, I read that I should be using bit-oriented calls vs. byte.  Does that apply to the call to which the device responds with the nonce.  I'm not able to get the nonce. My ESP32S3 MCU goes to watchdog and reboots.

bool CrNfc::authenticateMifareClassic(const rfalNfcDevice *device)
{
uint8_t authCmd[12] = {0}; // Authentication request
uint8_t response[4] = {0}; // Expected nonce response
uint16_t responseLen = 0;

authCmd[0] = 0x60;
authCmd[1] = 0x04 // Block address (first block in sector)

memcpy(&authCmd[2], mifareKey, 6);
if (device->nfcidLen < 4)
{
Serial.println(":cross_mark: Error: NFCID too short!");
return false;
}

memcpy(&authCmd[8], &device->nfcid[device->nfcidLen - 4], 4); // Last 4 bytes of NFCID1


ReturnCode ret = driver.rfalTransceiveBlockingTxRx(authCmd, sizeof(authCmd), response, sizeof(response), &responseLen, RFAL_TXRX_FLAGS_DEFAULT, RFAL_FWT_NONE);

if (ret == ERR_NONE && responseLen == 4)
{
Serial.printf("Authentication Step 1: Nonce Received %02X %02X %02X %02X\n",
response[0], response[1], response[2], response[3]);

// Here, we need to process the nonce with Crypto-1 to generate the response.
Serial.println("Crypto-1 Processing Required (Not handled by ST RFAL).");

return true;
}
else
{
Serial.printf("Authentication Failed (Step 1) %d.\n", ret);
return false;
}
}

Hello,

I think this 60 command is in reality much shorter. Please check against the specs of the cards in use. To my knowledge it can be sent using normal mode. First verify that. And once you get a response - as subsequent commands need to be sent in low level mode (SW generated parity and CRC)-  it will be a good idea to already exercise with this command the transmission and reception of low level frames.

A similar approach with a T2T command was discussed under keyword "template" in the mentioned other community thread.

BR, Ulysses