Adapt to new PN532 hardware + some cleanup

This commit is contained in:
André Fiedler 2025-02-14 19:44:07 +01:00
parent eb1d124040
commit a8596314ff
14 changed files with 171 additions and 1456 deletions

View File

@ -1,971 +0,0 @@
#include <Desfire.h>
MFRC522::StatusCode DESFire::PICC_RequestATS(byte *atsBuffer, byte *atsLength)
{
MFRC522::StatusCode result;
// Build command buffer
atsBuffer[0] = 0xE0; //PICC_CMD_RATS;
atsBuffer[1] = 0x50; // FSD=64, CID=0
// Calculate CRC_A
result = PCD_CalculateCRC(atsBuffer, 2, &atsBuffer[2]);
if (result != STATUS_OK) {
return result;
}
// Transmit the buffer and receive the response, validate CRC_A.
result = PCD_TransceiveData(atsBuffer, 4, atsBuffer, atsLength, NULL, 0, true);
if (result != STATUS_OK) {
PICC_HaltA();
Serial.println("WTF???");
return result;
}
return result;
} // End PICC_RequestATS()
/**
* Transmits Protocol and Parameter Selection Request (PPS)
*
* @return STATUS_OK on success, STATUS_??? otherwise.
*/
MFRC522::StatusCode DESFire::PICC_ProtocolAndParameterSelection(byte cid, ///< The lower nibble indicates the CID of the selected PICC in the range of 0x00 and 0x0E
byte pps0, ///< PPS0
byte pps1 ///< PPS1
) {
MFRC522::StatusCode result;
byte ppsBuffer[5];
byte ppsBufferSize = 5;
ppsBuffer[0] = 0xD0 | (cid & 0x0F);
ppsBuffer[1] = pps0;
ppsBuffer[2] = pps1;
// Calculate CRC_A
result = PCD_CalculateCRC(ppsBuffer, 3, &ppsBuffer[3]);
if (result != STATUS_OK) {
return result;
}
// Transmit the buffer and receive the response, validate CRC_A.
result = PCD_TransceiveData(ppsBuffer, 5, ppsBuffer, &ppsBufferSize, NULL, 0, true);
if (result == STATUS_OK) {
// This is how my MFRC522 is by default.
// Reading https://www.nxp.com/documents/data_sheet/MFRC522.pdf it seems CRC generation can only be disabled in this mode.
if (pps1 == 0x00) {
PCD_WriteRegister(TxModeReg, 0x00);
PCD_WriteRegister(RxModeReg, 0x00);
}
}
return result;
} // End PICC_ProtocolAndParameterSelection()
/**
* @see MIFARE_BlockExchangeWithData()
*/
DESFire::StatusCode DESFire::MIFARE_BlockExchange(mifare_desfire_tag *tag, byte cmd, byte *backData, byte *backLen)
{
return MIFARE_BlockExchangeWithData(tag, cmd, NULL, NULL, backData, backLen);
} // End MIFARE_BlockExchange()
/**
*
* Frame Format for DESFire APDUs
* ==============================
*
* The frame format for DESFire APDUs is based on only the ISO 14443-4 specifications for block formats.
* This is the format used by the example firmware, and seen in Figure 3.
* - PCB Protocol Control Byte, this byte is used to transfer format information about each PDU block.
* - CID Card Identifier field, this byte is used to identify specific tags. It contains a 4 bit CID value as well
* as information on the signal strength between the reader and the tag.
* - NAD Node Address field, the example firmware does not support the use of NAD.
* - DESFire Command Code This is discussed in the next section.
* - Data Bytes This field contains all of the Data Bytes for the command
*
* |-----|-----|-----|---------|------|----------|
* | PCB | CID | NAD | Command | Data | Checksum |
* |-----|-----|-----|---------|------|----------|
*
* Documentation: http://read.pudn.com/downloads64/ebook/225463/M305_DESFireISO14443.pdf
* http://www.ti.com.cn/cn/lit/an/sloa213/sloa213.pdf
*/
DESFire::StatusCode DESFire::MIFARE_BlockExchangeWithData(mifare_desfire_tag *tag, byte cmd, byte *sendData, byte *sendLen, byte *backData, byte *backLen)
{
StatusCode result;
byte buffer[64];
byte bufferSize = 64;
byte sendSize = 3;
buffer[0] = tag->pcb;
buffer[1] = tag->cid;
buffer[2] = cmd;
// Append data if available
if (sendData != NULL && sendLen != NULL) {
if (*sendLen > 0) {
memcpy(&buffer[3], sendData, *sendLen);
sendSize = sendSize + *sendLen;
}
}
// Update the PCB
if (tag->pcb == 0x0A)
tag->pcb = 0x0B;
else
tag->pcb = 0x0A;
// Calculate CRC_A
result.mfrc522 = PCD_CalculateCRC(buffer, sendSize, &buffer[sendSize]);
if (result.mfrc522 != STATUS_OK) {
return result;
}
result.mfrc522 = PCD_TransceiveData(buffer, sendSize + 2, buffer, &bufferSize);
if (result.mfrc522 != STATUS_OK) {
return result;
}
// Set the DESFire status code
result.desfire = (DesfireStatusCode)(buffer[2]);
// Copy data to backData and backLen
if (backData != NULL && backLen != NULL) {
memcpy(backData, &buffer[3], bufferSize - 5);
*backLen = bufferSize - 5;
}
return result;
} // End MIFARE_BlockExchangeWithData()
DESFire::StatusCode DESFire::MIFARE_DESFIRE_GetVersion(mifare_desfire_tag *tag, MIFARE_DESFIRE_Version_t *versionInfo)
{
StatusCode result;
byte versionBuffer[64];
byte versionBufferSize = 64;
result = MIFARE_BlockExchange(tag, 0x60, versionBuffer, &versionBufferSize);
if (result.mfrc522 == STATUS_OK) {
byte hardwareVersion[2];
byte storageSize;
versionInfo->hardware.vendor_id = versionBuffer[0];
versionInfo->hardware.type = versionBuffer[1];
versionInfo->hardware.subtype = versionBuffer[2];
versionInfo->hardware.version_major = versionBuffer[3];
versionInfo->hardware.version_minor = versionBuffer[4];
versionInfo->hardware.storage_size = versionBuffer[5];
versionInfo->hardware.protocol = versionBuffer[6];
if (result.desfire == MF_ADDITIONAL_FRAME) {
result = MIFARE_BlockExchange(tag, 0xAF, versionBuffer, &versionBufferSize);
if (result.mfrc522 == STATUS_OK) {
versionInfo->software.vendor_id = versionBuffer[0];
versionInfo->software.type = versionBuffer[1];
versionInfo->software.subtype = versionBuffer[2];
versionInfo->software.version_major = versionBuffer[3];
versionInfo->software.version_minor = versionBuffer[4];
versionInfo->software.storage_size = versionBuffer[5];
versionInfo->software.protocol = versionBuffer[6];
} else {
Serial.print("Failed to send AF: ");
Serial.println(GetStatusCodeName(result));
}
if (result.desfire == MF_ADDITIONAL_FRAME) {
byte nad = 0x60;
result = MIFARE_BlockExchange(tag, 0xAF, versionBuffer, &versionBufferSize);
if (result.mfrc522 == STATUS_OK) {
memcpy(versionInfo->uid, &versionBuffer[0], 7);
memcpy(versionInfo->batch_number, &versionBuffer[7], 5);
versionInfo->production_week = versionBuffer[12];
versionInfo->production_year = versionBuffer[13];
} else {
Serial.print("Failed to send AF: ");
Serial.println(GetStatusCodeName(result));
}
}
if (result.desfire == MF_ADDITIONAL_FRAME) {
Serial.println("GetVersion(): More data???");
}
}
}
else {
Serial.println("Version(): Failure.");
}
return result;
} // End MIFARE_DESFIRE_GetVersion
DESFire::StatusCode DESFire::MIFARE_DESFIRE_SelectApplication(mifare_desfire_tag *tag, mifare_desfire_aid_t *aid)
{
StatusCode result;
byte buffer[64];
byte bufferSize = MIFARE_AID_SIZE;
for (byte i = 0; i < MIFARE_AID_SIZE; i++) {
buffer[i] = aid->data[i];
}
result = MIFARE_BlockExchangeWithData(tag, 0x5A, buffer, &bufferSize, buffer, &bufferSize);
if (IsStatusCodeOK(result)) {
// keep track of the application
memcpy(tag->selected_application, aid->data, MIFARE_AID_SIZE);
}
return result;
}
DESFire::StatusCode DESFire::MIFARE_DESFIRE_GetFileIDs(mifare_desfire_tag *tag, byte *files, byte *filesCount)
{
StatusCode result;
byte bufferSize = MIFARE_MAX_FILE_COUNT + 5;
byte buffer[bufferSize];
result = MIFARE_BlockExchange(tag, 0x6F, buffer, &bufferSize);
if (IsStatusCodeOK(result)) {
*filesCount = bufferSize;
memcpy(files, &buffer, *filesCount);
}
return result;
} // End MIFARE_DESFIRE_GetFileIDs
DESFire::StatusCode DESFire::MIFARE_DESFIRE_GetFileSettings(mifare_desfire_tag *tag, byte *file, mifare_desfire_file_settings_t *fileSettings)
{
StatusCode result;
byte buffer[21];
byte bufferSize = 21;
byte sendLen = 1;
buffer[0] = *file;
result = MIFARE_BlockExchangeWithData(tag, 0xF5, buffer, &sendLen, buffer, &bufferSize);
if (IsStatusCodeOK(result)) {
fileSettings->file_type = buffer[0];
fileSettings->communication_settings = buffer[1];
fileSettings->access_rights = ((uint16_t)(buffer[2]) << 8) | (buffer[3]);
switch (buffer[0]) {
case MDFT_STANDARD_DATA_FILE:
case MDFT_BACKUP_DATA_FILE:
fileSettings->settings.standard_file.file_size = ((uint32_t)(buffer[4])) | ((uint32_t)(buffer[5]) << 8) | ((uint32_t)(buffer[6]) << 16);
break;
case MDFT_VALUE_FILE_WITH_BACKUP:
fileSettings->settings.value_file.lower_limit = ((uint32_t)(buffer[4])) | ((uint32_t)(buffer[5]) << 8) | ((uint32_t)(buffer[6]) << 16) | ((uint32_t)(buffer[7]) << 24);
fileSettings->settings.value_file.upper_limit = ((uint32_t)(buffer[8])) | ((uint32_t)(buffer[9]) << 8) | ((uint32_t)(buffer[10]) << 16) | ((uint32_t)(buffer[11]) << 24);
fileSettings->settings.value_file.limited_credit_value = ((uint32_t)(buffer[12])) | ((uint32_t)(buffer[13]) << 8) | ((uint32_t)(buffer[14]) << 16) | ((uint32_t)(buffer[15]) << 24);
fileSettings->settings.value_file.limited_credit_enabled = buffer[16];
break;
case MDFT_LINEAR_RECORD_FILE_WITH_BACKUP:
case MDFT_CYCLIC_RECORD_FILE_WITH_BACKUP:
fileSettings->settings.record_file.record_size = ((uint32_t)(buffer[4])) | ((uint32_t)(buffer[5]) << 8) | ((uint32_t)(buffer[6]) << 16);
fileSettings->settings.record_file.max_number_of_records = ((uint32_t)(buffer[7])) | ((uint32_t)(buffer[8]) << 8) | ((uint32_t)(buffer[9]) << 16);
fileSettings->settings.record_file.current_number_of_records = ((uint32_t)(buffer[10])) | ((uint32_t)(buffer[11]) << 8) | ((uint32_t)(buffer[12]) << 16);
break;
default:
//return FAIL;
result.mfrc522 = STATUS_ERROR;
return result;
}
}
return result;
} // End MIFARE_DESFIRE_GetFileSettings
DESFire::StatusCode DESFire::MIFARE_DESFIRE_GetKeySettings(mifare_desfire_tag *tag, byte *settings, byte *maxKeys)
{
StatusCode result;
byte buffer[7];
byte bufferSize = 7;
result = MIFARE_BlockExchange(tag, 0x45, buffer, &bufferSize);
if (IsStatusCodeOK(result)) {
*settings = buffer[0];
*maxKeys = buffer[1];
}
return result;
} // End MIFARE_DESFIRE_GetKeySettings()
DESFire::StatusCode DESFire::MIFARE_DESFIRE_GetKeyVersion(mifare_desfire_tag *tag, byte key, byte *version)
{
StatusCode result;
byte buffer[6];
byte bufferSize = 6;
byte sendLen = 1;
buffer[0] = key;
result = MIFARE_BlockExchangeWithData(tag, 0x64, buffer, &sendLen, buffer, &bufferSize);
if (IsStatusCodeOK(result)) {
*version = buffer[0];
}
return result;
}
DESFire::StatusCode DESFire::MIFARE_DESFIRE_ReadData(mifare_desfire_tag *tag, byte fid, uint32_t offset, uint32_t length, byte *backData, size_t *backLen)
{
StatusCode result;
byte buffer[64];
byte bufferSize = 64;
byte sendLen = 7;
size_t outSize = 0;
// file ID
buffer[0] = fid;
// offset
buffer[1] = (offset & 0x00000F);
buffer[2] = (offset & 0x00FF00) >> 8;
buffer[3] = (offset & 0xFF0000) >> 16;
// length
buffer[4] = (length & 0x0000FF);
buffer[5] = (length & 0x00FF00) >> 8;
buffer[6] = (length & 0xFF0000) >> 16;
result = MIFARE_BlockExchangeWithData(tag, 0xBD, buffer, &sendLen, buffer, &bufferSize);
if (result.mfrc522 == STATUS_OK) {
do {
// Copy the data
memcpy(backData + outSize, buffer, bufferSize);
outSize += bufferSize;
*backLen = outSize;
if (result.desfire == MF_ADDITIONAL_FRAME) {
result = MIFARE_BlockExchange(tag, 0xAF, buffer, &bufferSize);
}
} while (result.mfrc522 == STATUS_OK && result.desfire == MF_ADDITIONAL_FRAME);
}
return result;
}
DESFire::StatusCode DESFire::MIFARE_DESFIRE_GetValue(mifare_desfire_tag *tag, byte fid, int32_t *value)
{
StatusCode result;
byte buffer[MFRC522::FIFO_SIZE];
byte bufferSize = MFRC522::FIFO_SIZE;
byte sendLen = 1;
size_t outSize = 0;
buffer[0] = fid;
result = MIFARE_BlockExchangeWithData(tag, 0x6C, buffer, &sendLen, buffer, &bufferSize);
if (IsStatusCodeOK(result)) {
*value = ((uint32_t)buffer[0] | ((uint32_t)buffer[1] << 8) | ((uint32_t)buffer[2] << 16) | ((uint32_t)buffer[3] << 24));
}
return result;
} // End MIFARE_DESFIRE_GetValue()
DESFire::StatusCode DESFire::MIFARE_DESFIRE_GetApplicationIds(mifare_desfire_tag *tag, mifare_desfire_aid_t *aids, byte *applicationCount)
{
StatusCode result;
// MIFARE_MAX_APPLICATION_COUNT * MIFARE_AID_SIZE + PCB (1 byte) + CID (1 byte) + Checksum (2 bytes)
// I also add an extra byte in case NAD is needed
byte bufferSize = (MIFARE_MAX_APPLICATION_COUNT * MIFARE_AID_SIZE) + 5;
byte buffer[bufferSize];
byte aidBuffer[MIFARE_MAX_APPLICATION_COUNT * MIFARE_AID_SIZE];
byte aidBufferSize = 0;
result = MIFARE_BlockExchange(tag, 0x6A, buffer, &bufferSize);
if (result.mfrc522 != STATUS_OK)
return result;
// MIFARE_MAX_APPLICATION_COUNT (28) * MIFARE_AID_SIZE + PCB (1) + CID (1) + Checksum (2) = 88
// Even if the NAD byte is not present we could GET a 0xAF response.
if (result.desfire == MF_OPERATION_OK && bufferSize == 0x00) {
// Empty application list
*applicationCount = 0;
return result;
}
memcpy(aidBuffer, buffer, bufferSize);
aidBufferSize = bufferSize;
while (result.desfire == MF_ADDITIONAL_FRAME) {
bufferSize = (MIFARE_MAX_APPLICATION_COUNT * MIFARE_AID_SIZE) + 5;
result = MIFARE_BlockExchange(tag, 0xAF, buffer, &bufferSize);
if (result.mfrc522 != STATUS_OK)
return result;
// Make sure we have space (Just in case)
if ((aidBufferSize + bufferSize) > (MIFARE_MAX_APPLICATION_COUNT * MIFARE_AID_SIZE)) {
result.mfrc522 = STATUS_NO_ROOM;
return result;
}
// Append the new data
memcpy(aidBuffer + aidBufferSize, buffer, bufferSize);
}
// Applications are identified with a 3 byte application identifier(AID)
// we also received the status byte:
if ((aidBufferSize % 3) != 0) {
Serial.println(F("MIFARE_DESFIRE_GetApplicationIds(): Data is not a modulus of 3."));
// TODO: Some kind of failure
result.mfrc522 = STATUS_ERROR;
return result;
}
*applicationCount = aidBufferSize / 3;
for (byte i = 0; i < *applicationCount; i++) {
aids[i].data[0] = aidBuffer[(i * 3)];
aids[i].data[1] = aidBuffer[1 + (i * 3)];
aids[i].data[2] = aidBuffer[2 + (i * 3)];
}
return result;
} // End MIFARE_DESFIRE_GetApplicationIds()
/**
* Returns a __FlashStringHelper pointer to a status code name.
*
* @return const __FlashStringHelper *
*/
const __FlashStringHelper *DESFire::GetStatusCodeName(StatusCode code)
{
if (code.mfrc522 != MFRC522::STATUS_OK) {
return MFRC522::GetStatusCodeName(code.mfrc522);
}
switch (code.desfire) {
case MF_OPERATION_OK: return F("Successful operation.");
case MF_NO_CHANGES: return F("No changes done to backup files.");
case MF_OUT_OF_EEPROM_ERROR: return F("Insufficient NV-Mem. to complete cmd.");
case MF_ILLEGAL_COMMAND_CODE: return F("Command code not supported.");
case MF_INTEGRITY_ERROR: return F("CRC or MAC does not match data.");
case MF_NO_SUCH_KEY: return F("Invalid key number specified.");
case MF_LENGTH_ERROR: return F("Length of command string invalid.");
case MF_PERMISSION_ERROR: return F("Curr conf/status doesnt allow cmd.");
case MF_PARAMETER_ERROR: return F("Value of the parameter(s) invalid.");
case MF_APPLICATION_NOT_FOUND: return F("Requested AID not present on PICC.");
case MF_APPL_INTEGRITY_ERROR: return F("Unrecoverable err within app.");
case MF_AUTHENTICATION_ERROR: return F("Current authentication status doesn't allow requested command.");
case MF_ADDITIONAL_FRAME: return F("Additional data frame to be sent.");
case MF_BOUNDARY_ERROR: return F("Attempt to read/write beyond limits.");
case MF_PICC_INTEGRITY_ERROR: return F("Unrecoverable error within PICC.");
case MF_COMMAND_ABORTED: return F("Previous command not fully completed.");
case MF_PICC_DISABLED_ERROR: return F("PICC disabled by unrecoverable error.");
case MF_COUNT_ERROR: return F("Cant create more apps, already @ 28.");
case MF_DUPLICATE_ERROR: return F("Cant create dup. file/app.");
case MF_EEPROM_ERROR: return F("Couldnt complete NV-write operation.");
case MF_FILE_NOT_FOUND: return F("Specified file number doesnt exist.");
case MF_FILE_INTEGRITY_ERROR: return F("Unrecoverable error within file.");
default: return F("Unknown error");
}
} // End GetStatusCodeName()
const __FlashStringHelper *DESFire::GetFileTypeName(mifare_desfire_file_types fileType)
{
switch (fileType) {
case MDFT_STANDARD_DATA_FILE: return F("Standard data file.");
case MDFT_BACKUP_DATA_FILE: return F("Backup data file.");
case MDFT_VALUE_FILE_WITH_BACKUP: return F("Value file with backup.");
case MDFT_LINEAR_RECORD_FILE_WITH_BACKUP: return F("Linear record file with backup.");
case MDFT_CYCLIC_RECORD_FILE_WITH_BACKUP: return F("Cyclic record file with backup.");
default: return F("Unknown file type.");
}
} // End GetFileTypeName()
const __FlashStringHelper *DESFire::GetCommunicationModeName(mifare_desfire_communication_modes communicationMode)
{
switch (communicationMode) {
case MDCM_PLAIN: return(F("Plain Communication."));
case MDCM_MACED: return(F("Plain Comm secured by DES/3DES MACing."));
case MDCM_ENCIPHERED: return(F("Fully DES/3DES enciphered comm."));
default: return F("Unknown communication mode.");
}
} // End GetCommunicationModeName()
bool DESFire::IsStatusCodeOK(StatusCode code)
{
if (code.mfrc522 != STATUS_OK)
return false;
if (code.desfire != MF_OPERATION_OK)
return false;
return true;
} // End IsStatusCodeOK();
void DESFire::PICC_DumpMifareDesfireMasterKey(mifare_desfire_tag *tag)
{
StatusCode response;
mifare_desfire_aid_t aid;
aid.data[0] = 0x00;
aid.data[1] = 0x00;
aid.data[2] = 0x00;
Serial.println(F("-- Desfire Master Key ---------------------------------------"));
Serial.println(F("-------------------------------------------------------------"));
// Select the current application.
response = MIFARE_DESFIRE_SelectApplication(tag, &aid);
if (!IsStatusCodeOK(response)) {
Serial.println(F("Error: Failed to select application."));
Serial.println(GetStatusCodeName(response));
Serial.println(F("-------------------------------------------------------------"));
return;
}
// Get Key settings
byte keySettings;
byte keyCount = 0;
byte keyVersion;
response = MIFARE_DESFIRE_GetKeySettings(tag, &keySettings, &keyCount);
if (IsStatusCodeOK(response)) {
Serial.print(F(" Key settings : 0x"));
if (keySettings < 0x10)
Serial.print(F("0"));
Serial.println(keySettings, HEX);
Serial.print(F(" Max num keys : "));
Serial.println(keyCount);
// Output key versions
if (keyCount > 0) {
Serial.println(F(" ----------------------------------------------------------"));
Serial.println(F(" Key Versions"));
// Get key versions (No output will be outputed later)
for (byte ixKey = 0; ixKey < keyCount; ixKey++) {
response = MIFARE_DESFIRE_GetKeyVersion(tag, ixKey, &keyVersion);
Serial.print(F(" Key 0x"));
if (ixKey < 0x10)
Serial.print(F("0"));
Serial.print(ixKey, HEX);
Serial.print(F(" : "));
if (IsStatusCodeOK(response)) {
Serial.print(F("0x"));
if (keyVersion < 0x10)
Serial.print(F("0"));
Serial.println(keyVersion, HEX);
} else {
Serial.println(GetStatusCodeName(response));
}
}
}
}
else {
Serial.println(F(" Error: Failed to get application key settings."));
// Just to be sure..
keyCount = 0;
}
Serial.println(F("-------------------------------------------------------------"));
} // End PICC_DumpMifareDesfireMasterKey()
void DESFire::PICC_DumpMifareDesfireVersion(mifare_desfire_tag *tag, MIFARE_DESFIRE_Version_t *versionInfo)
{
Serial.println(F("-- Desfire Information --------------------------------------"));
Serial.println(F("-------------------------------------------------------------"));
switch (versionInfo->hardware.version_major) {
case 0x00:
Serial.println(F(" Card type : MIFARE DESFire (MF3ICD40)"));
switch (versionInfo->hardware.storage_size) {
case 0x16:
Serial.print(F(" 2K"));
break;
case 0x18:
Serial.print(F(" 4K"));
break;
case 0x1A:
Serial.print(F(" 8K"));
break;
}
Serial.println();
break;
case 0x01:
Serial.print(F(" Card type : MIFARE DESFire EV1"));
switch (versionInfo->hardware.storage_size) {
case 0x16:
Serial.print(F(" 2K"));
break;
case 0x18:
Serial.print(F(" 4K"));
break;
case 0x1A:
Serial.print(F(" 8K"));
break;
}
Serial.println();
break;
case 0x12:
Serial.print(F(" Card type : MIFARE DESFire EV2"));
switch (versionInfo->hardware.storage_size) {
case 0x16:
Serial.print(F(" 2K"));
break;
case 0x18:
Serial.print(F(" 4K"));
break;
case 0x1A:
Serial.print(F(" 8K"));
break;
}
Serial.println();
break;
}
// UID
Serial.print(F(" UID :"));
for (byte i = 0; i < 7; i++) {
if (versionInfo->uid[i] < 0x10)
Serial.print(F(" 0"));
else
Serial.print(F(" "));
Serial.print(versionInfo->uid[i], HEX);
}
Serial.println();
// Batch
Serial.print(F(" Batch number :"));
for (byte i = 0; i < 5; i++) {
if (versionInfo->batch_number[i] < 0x10)
Serial.print(F(" 0"));
else
Serial.print(F(" "));
Serial.print(versionInfo->batch_number[i], HEX);
}
Serial.println();
Serial.print(F(" Production week : 0x"));
if (versionInfo->production_week < 0x10)
Serial.print(F("0"));
Serial.println(versionInfo->production_week, HEX);
Serial.print(F(" Production year : 0x"));
if (versionInfo->production_year < 0x10)
Serial.print(F("0"));
Serial.println(versionInfo->production_year, HEX);
Serial.println(F(" ----------------------------------------------------------"));
Serial.println(F(" Hardware Information"));
Serial.print(F(" Vendor ID : 0x"));
if (versionInfo->hardware.vendor_id < 0x10)
Serial.print(F("0"));
Serial.print(versionInfo->hardware.vendor_id, HEX);
if (versionInfo->hardware.vendor_id == 0x04)
Serial.print(F(" (NXP)"));
Serial.println();
Serial.print(F(" Type : 0x"));
if (versionInfo->hardware.type < 0x10)
Serial.print(F("0"));
Serial.println(versionInfo->hardware.type, HEX);
Serial.print(F(" Subtype : 0x"));
if (versionInfo->hardware.subtype < 0x10)
Serial.print(F("0"));
Serial.println(versionInfo->hardware.subtype, HEX);
Serial.print(F(" Version : "));
Serial.print(versionInfo->hardware.version_major);
Serial.print(F("."));
Serial.println(versionInfo->hardware.version_minor);
Serial.print(F(" Storage size : 0x"));
if (versionInfo->hardware.storage_size < 0x10)
Serial.print(F("0"));
Serial.print(versionInfo->hardware.storage_size, HEX);
switch (versionInfo->hardware.storage_size) {
case 0x16:
Serial.print(F(" (2048 bytes)"));
break;
case 0x18:
Serial.print(F(" (4096 bytes)"));
break;
case 0x1A:
Serial.print(F(" (8192 bytes)"));
break;
}
Serial.println();
Serial.print(F(" Protocol : 0x"));
if (versionInfo->hardware.protocol < 0x10)
Serial.print(F("0"));
Serial.println(versionInfo->hardware.protocol, HEX);
Serial.println(F(" ----------------------------------------------------------"));
Serial.println(F(" Software Information"));
Serial.print(F(" Vendor ID : 0x"));
if (versionInfo->software.vendor_id < 0x10)
Serial.print(F("0"));
Serial.print(versionInfo->software.vendor_id, HEX);
if (versionInfo->software.vendor_id == 0x04)
Serial.print(F(" (NXP)"));
Serial.println();
Serial.print(F(" Type : 0x"));
if (versionInfo->software.type < 0x10)
Serial.print(F("0"));
Serial.println(versionInfo->software.type, HEX);
Serial.print(F(" Subtype : 0x"));
if (versionInfo->software.subtype < 0x10)
Serial.print(F("0"));
Serial.println(versionInfo->software.subtype, HEX);
Serial.print(F(" Version : "));
Serial.print(versionInfo->software.version_major);
Serial.print(F("."));
Serial.println(versionInfo->software.version_minor);
Serial.print(F(" Storage size : 0x"));
if (versionInfo->software.storage_size < 0x10)
Serial.print(F("0"));
Serial.print(versionInfo->software.storage_size, HEX);
switch (versionInfo->software.storage_size) {
case 0x16:
Serial.print(F(" (2048 bytes)"));
break;
case 0x18:
Serial.print(F(" (4096 bytes)"));
break;
case 0x1A:
Serial.print(F(" (8192 bytes)"));
break;
}
Serial.println();
Serial.print(F(" Protocol : 0x"));
if (versionInfo->software.protocol < 0x10)
Serial.print(F("0"));
Serial.println(versionInfo->software.protocol, HEX);
Serial.println(F("-------------------------------------------------------------"));
}
void DESFire::PICC_DumpMifareDesfireApplication(mifare_desfire_tag *tag, mifare_desfire_aid_t *aid)
{
StatusCode response;
Serial.println(F("-- Desfire Application --------------------------------------"));
Serial.println(F("-------------------------------------------------------------"));
Serial.print(F(" AID :"));
for (byte i = 0; i < 3; i++) {
if (aid->data[i] < 0x10)
Serial.print(F(" 0"));
else
Serial.print(F(" "));
Serial.print(aid->data[i], HEX);
}
Serial.println();
// Select the current application.
response = MIFARE_DESFIRE_SelectApplication(tag, aid);
if (!IsStatusCodeOK(response)) {
Serial.println(F("Error: Failed to select application."));
Serial.println(GetStatusCodeName(response));
Serial.println(F("-------------------------------------------------------------"));
return;
}
// Get Key settings
byte keySettings;
byte keyCount = 0;
byte keyVersion[16];
response = MIFARE_DESFIRE_GetKeySettings(tag, &keySettings, &keyCount);
if (IsStatusCodeOK(response)) {
Serial.print(F(" Key settings : 0x"));
if (keySettings < 0x10)
Serial.print(F("0"));
Serial.println(keySettings, HEX);
Serial.print(F(" Max num keys : "));
Serial.println(keyCount);
// Get key versions (No output will be outputed later)
for (byte ixKey = 0; ixKey < keyCount; ixKey++) {
response = MIFARE_DESFIRE_GetKeyVersion(tag, ixKey, &(keyVersion[ixKey]));
if (!IsStatusCodeOK(response))
keyVersion[ixKey] = 0x00;
}
} else {
Serial.println(F(" Error: Failed to get application key settings."));
// Just to be sure..
keyCount = 0;
}
// Get the files
byte files[MIFARE_MAX_FILE_COUNT];
byte filesCount = 0;
response = MIFARE_DESFIRE_GetFileIDs(tag, files, &filesCount);
if (!IsStatusCodeOK(response)) {
Serial.println(F(" Error: Failed to get application file IDs."));
Serial.print(F(" "));
Serial.println(GetStatusCodeName(response));
Serial.println(F("-------------------------------------------------------------"));
return;
}
// Number of files
Serial.print(F(" Num. Files : "));
Serial.println(filesCount);
// Output key versions
if (keyCount > 0) {
Serial.println(F(" ----------------------------------------------------------"));
Serial.println(F(" Key Versions"));
for (byte ixKey = 0; ixKey < keyCount; ixKey++) {
Serial.print(F(" Key 0x"));
if (ixKey < 0x10)
Serial.print(F("0"));
Serial.print(ixKey, HEX);
Serial.print(F(" : 0x"));
if (keyVersion[ixKey] < 0x10)
Serial.print(F("0"));
Serial.println(keyVersion[ixKey], HEX);
}
}
for (byte i = 0; i < filesCount; i++) {
Serial.println(F(" ----------------------------------------------------------"));
Serial.println(F(" File Information"));
Serial.print(F(" File ID : 0x"));
if (files[i] < 0x10)
Serial.print(F("0"));
Serial.println(files[i], HEX);
// Get file settings
mifare_desfire_file_settings_t fileSettings;
response = MIFARE_DESFIRE_GetFileSettings(tag, &(files[i]), &fileSettings);
if (IsStatusCodeOK(response)) {
Serial.print(F(" File Type : 0x"));
if (fileSettings.file_type < 0x10)
Serial.print(F("0"));
Serial.print(fileSettings.file_type, HEX);
Serial.print(F(" ("));
Serial.print(GetFileTypeName((mifare_desfire_file_types)fileSettings.file_type));
Serial.println(F(")"));
Serial.print(F(" Communication : 0x"));
if (fileSettings.communication_settings < 0x10)
Serial.print(F("0"));
Serial.print(fileSettings.communication_settings, HEX);
Serial.print(F(" ("));
Serial.print(GetCommunicationModeName((mifare_desfire_communication_modes)fileSettings.communication_settings));
Serial.println(F(")"));
Serial.print(F(" Access rights : 0x"));
Serial.println(fileSettings.access_rights, HEX);
switch (fileSettings.file_type) {
case MDFT_STANDARD_DATA_FILE:
case MDFT_BACKUP_DATA_FILE:
Serial.print(F(" File Size : "));
Serial.print(fileSettings.settings.standard_file.file_size);
Serial.println(F(" bytes"));
break;
case MDFT_VALUE_FILE_WITH_BACKUP:
Serial.print(F(" Lower Limit : "));
Serial.println(fileSettings.settings.value_file.lower_limit);
Serial.print(F(" Upper Limit : "));
Serial.println(fileSettings.settings.value_file.upper_limit);
Serial.print(F(" Limited credit : "));
Serial.println(fileSettings.settings.value_file.limited_credit_value);
Serial.print(F(" Limited credit : "));
if (fileSettings.settings.value_file.limited_credit_enabled == 0x00)
Serial.print(F("Disabled ("));
else
Serial.print(F("Enabled (0x"));
if (fileSettings.settings.value_file.limited_credit_enabled < 0x10)
Serial.print(F("0"));
Serial.print(fileSettings.settings.value_file.limited_credit_enabled, HEX);
Serial.println(F(")"));
break;
case MDFT_LINEAR_RECORD_FILE_WITH_BACKUP:
case MDFT_CYCLIC_RECORD_FILE_WITH_BACKUP:
Serial.print(F(" Record size : "));
Serial.println(fileSettings.settings.record_file.record_size);
Serial.print(F(" max num records: "));
Serial.println(fileSettings.settings.record_file.max_number_of_records);
Serial.print(F(" num records : "));
Serial.println(fileSettings.settings.record_file.current_number_of_records);
break;
}
switch (fileSettings.file_type) {
case MDFT_STANDARD_DATA_FILE:
case MDFT_BACKUP_DATA_FILE:
{
// Get file data
byte fileContent[fileSettings.settings.standard_file.file_size];
size_t fileContentLength = fileSettings.settings.standard_file.file_size;
response = MIFARE_DESFIRE_ReadData(tag, files[i], 0, fileSettings.settings.standard_file.file_size, fileContent, &fileContentLength);
if (response.mfrc522 == STATUS_OK) {
Serial.println(F(" ------------------------------------------------------"));
Serial.println(F(" Data"));
if (response.desfire == MF_OPERATION_OK || response.desfire == MF_ADDITIONAL_FRAME) {
for (unsigned int iByte = 0; iByte < fileContentLength; iByte++) {
if ((iByte % 16) == 0) {
if (iByte != 0)
Serial.println();
Serial.print(F(" "));
}
if (fileContent[iByte] < 0x10)
Serial.print(F(" 0"));
else
Serial.print(F(" "));
Serial.print(fileContent[iByte], HEX);
}
Serial.println();
}
else {
Serial.print(F(" "));
Serial.println(GetStatusCodeName(response));
}
}
}
break;
case MDFT_VALUE_FILE_WITH_BACKUP:
{
// Get value
int32_t fileValue;
response = MIFARE_DESFIRE_GetValue(tag, files[i], &fileValue);
Serial.print(F(" Value : "));
if (IsStatusCodeOK(response)) {
Serial.println(fileValue);
} else {
Serial.println(GetStatusCodeName(response));
}
}
break;
}
} else {
Serial.println(F(" Error: Failed to get file settings."));
Serial.print(F(" "));
Serial.println(GetStatusCodeName(response));
}
}
Serial.println(F("-------------------------------------------------------------"));
}

View File

@ -1,187 +0,0 @@
#ifndef DESFIRE_h
#define DESFIRE_h
#include <Arduino.h>
#include <SPI.h>
#include <MFRC522.h>
/* --------------------------------------
* DESFire Logical Structure
* --------------------------------------
*/
#define MIFARE_MAX_APPLICATION_COUNT 28 /* max applications on one PICC */
#define MIFARE_MAX_FILE_COUNT 16 /* max # of files in each application */
#define MIFARE_UID_BYTES 7 /* number of UID bytes */
#define MIFARE_AID_SIZE 3 /* number of AID bytes */
class DESFire : public MFRC522 {
public:
// DESFire Status and Error Codes.
enum DesfireStatusCode : byte {
MF_OPERATION_OK = 0x00, /* successful operation */
MF_NO_CHANGES = 0x0C, /* no changes done to backup files */
MF_OUT_OF_EEPROM_ERROR = 0x0E, /* insufficient NV-Mem. to complete cmd */
MF_ILLEGAL_COMMAND_CODE = 0x1C, /* command code not supported */
MF_INTEGRITY_ERROR = 0x1E, /* CRC or MAC does not match data */
MF_NO_SUCH_KEY = 0x40, /* invalid key number specified */
MF_LENGTH_ERROR = 0x7E, /* length of command string invalid */
MF_PERMISSION_ERROR = 0x9D, /* curr conf/status doesnt allow cmd */
MF_PARAMETER_ERROR = 0x9E, /* value of the parameter(s) invalid */
MF_APPLICATION_NOT_FOUND = 0xA0, /* requested AID not present on PICC */
MF_APPL_INTEGRITY_ERROR = 0xA1, /* unrecoverable err within app */
MF_AUTHENTICATION_ERROR = 0xAE, /* cur auth status doesnt allow req cmd */
MF_ADDITIONAL_FRAME = 0xAF, /* additional data frame to be sent */
MF_BOUNDARY_ERROR = 0xBE, /* attempt to read/write beyond limits */
MF_PICC_INTEGRITY_ERROR = 0xC1, /* unrecoverable error within PICC */
MF_COMMAND_ABORTED = 0xCA, /* previous command not fully completed */
MF_PICC_DISABLED_ERROR = 0xCD, /* PICC disabled by unrecoverable error */
MF_COUNT_ERROR = 0xCE, /* cant create more apps, already @ 28 */
MF_DUPLICATE_ERROR = 0xDE, /* cant create dup. file/app */
MF_EEPROM_ERROR = 0xEE, /* couldnt complete NV-write operation */
MF_FILE_NOT_FOUND = 0xF0, /* specified file number doesnt exist */
MF_FILE_INTEGRITY_ERROR = 0xF1 /* unrecoverable error within file */
};
// DESFire file types
enum mifare_desfire_file_types : byte {
MDFT_STANDARD_DATA_FILE = 0x00,
MDFT_BACKUP_DATA_FILE = 0x01,
MDFT_VALUE_FILE_WITH_BACKUP = 0x02,
MDFT_LINEAR_RECORD_FILE_WITH_BACKUP = 0x03,
MDFT_CYCLIC_RECORD_FILE_WITH_BACKUP = 0x04
};
// DESFire communication modes
enum mifare_desfire_communication_modes : byte {
MDCM_PLAIN = 0x00, /* Plain Communication */
MDCM_MACED = 0x01, /* Plain Comm secured by DES/3DES MACing */
MDCM_ENCIPHERED = 0x03 /* Fully DES/3DES enciphered comm. */
};
// A struct used for passing a MIFARE DESFire Version
typedef struct {
struct {
uint8_t vendor_id;
uint8_t type;
uint8_t subtype;
uint8_t version_major;
uint8_t version_minor;
uint8_t storage_size;
uint8_t protocol;
} hardware;
struct {
uint8_t vendor_id;
uint8_t type;
uint8_t subtype;
uint8_t version_major;
uint8_t version_minor;
uint8_t storage_size;
uint8_t protocol;
} software;
uint8_t uid[7];
uint8_t batch_number[5];
uint8_t production_week;
uint8_t production_year;
} MIFARE_DESFIRE_Version_t;
typedef struct {
uint8_t data[MIFARE_AID_SIZE];
} mifare_desfire_aid_t;
typedef struct {
MFRC522::StatusCode mfrc522;
DesfireStatusCode desfire;
} StatusCode;
// A struct used for passing a MIFARE DESFire Version
typedef struct {
uint8_t file_type;
uint8_t communication_settings;
uint16_t access_rights;
union {
struct {
uint32_t file_size;
} standard_file;
struct {
int32_t lower_limit;
int32_t upper_limit;
int32_t limited_credit_value;
uint8_t limited_credit_enabled;
} value_file;
struct {
uint32_t record_size;
uint32_t max_number_of_records;
uint32_t current_number_of_records;
} record_file; /* linear and cyclic record files */
} settings;
} mifare_desfire_file_settings_t;
typedef struct {
byte cid; // Card ID
byte pcb; // Protocol Control Byte
byte selected_application[MIFARE_AID_SIZE];
} mifare_desfire_tag;
/////////////////////////////////////////////////////////////////////////////////////
// Functions for setting up the Arduino
/////////////////////////////////////////////////////////////////////////////////////
explicit DESFire() : MFRC522() {};
explicit DESFire(byte resetPowerDownPin) : MFRC522(resetPowerDownPin) {};
explicit DESFire(byte chipSelectPin, byte resetPowerDownPin) : MFRC522(chipSelectPin, resetPowerDownPin) {};
/////////////////////////////////////////////////////////////////////////////////////
// ISO/IEC 14443 functions not currentlly present in MFRC522 library
/////////////////////////////////////////////////////////////////////////////////////
MFRC522::StatusCode PICC_RequestATS(byte *atsBuffer, byte *atsLength);
MFRC522::StatusCode PICC_ProtocolAndParameterSelection(byte cid, byte pps0, byte pps1 = 0x00);
/////////////////////////////////////////////////////////////////////////////////////
// Functions for MIFARE DESFire
/////////////////////////////////////////////////////////////////////////////////////
StatusCode MIFARE_DESFIRE_GetVersion(mifare_desfire_tag *tag, MIFARE_DESFIRE_Version_t *versionInfo);
StatusCode MIFARE_DESFIRE_GetApplicationIds(mifare_desfire_tag *tag, mifare_desfire_aid_t *aids, byte *applicationCount);
StatusCode MIFARE_DESFIRE_SelectApplication(mifare_desfire_tag *tag, mifare_desfire_aid_t *aid);
StatusCode MIFARE_DESFIRE_GetKeySettings(mifare_desfire_tag *tag, byte *settings, byte *maxKeys);
StatusCode MIFARE_DESFIRE_GetKeyVersion(mifare_desfire_tag *tag, byte key, byte *version);
/////////////////////////////////////////////////////////////////////////////////////
// MIFARE DESFire application level commands
/////////////////////////////////////////////////////////////////////////////////////
StatusCode MIFARE_DESFIRE_GetFileIDs(mifare_desfire_tag *tag, byte *files, byte *filesCount);
StatusCode MIFARE_DESFIRE_GetFileSettings(mifare_desfire_tag *tag, byte *file, mifare_desfire_file_settings_t *fileSettings);
/////////////////////////////////////////////////////////////////////////////////////
// MIFARE DESFire data manipulation commands
/////////////////////////////////////////////////////////////////////////////////////
StatusCode MIFARE_DESFIRE_ReadData(mifare_desfire_tag *tag, byte fid, uint32_t offset, uint32_t length, byte *backData, size_t *backLen);
StatusCode MIFARE_DESFIRE_GetValue(mifare_desfire_tag *tag, byte fid, int32_t *value);
/////////////////////////////////////////////////////////////////////////////////////
// Support functions
/////////////////////////////////////////////////////////////////////////////////////
static const __FlashStringHelper *GetDesfireStatusCodeName(DesfireStatusCode code);
virtual const __FlashStringHelper *GetStatusCodeName(MFRC522::StatusCode code) { return MFRC522::GetStatusCodeName(code); };
static const __FlashStringHelper *GetStatusCodeName(StatusCode code);
static const __FlashStringHelper *GetFileTypeName(mifare_desfire_file_types fileType);
static const __FlashStringHelper *GetCommunicationModeName(mifare_desfire_communication_modes communicationMode);
bool IsStatusCodeOK(StatusCode code);
/////////////////////////////////////////////////////////////////////////////////////
// Functions for debugging
/////////////////////////////////////////////////////////////////////////////////////
void PICC_DumpMifareDesfireMasterKey(mifare_desfire_tag *tag);
void PICC_DumpMifareDesfireVersion(mifare_desfire_tag *tag, MIFARE_DESFIRE_Version_t *versionInfo);
void PICC_DumpMifareDesfireApplication(mifare_desfire_tag *tag, mifare_desfire_aid_t *aid);
protected:
/////////////////////////////////////////////////////////////////////////////////////
// Helper methods
/////////////////////////////////////////////////////////////////////////////////////
StatusCode MIFARE_BlockExchange(mifare_desfire_tag *tag, byte cmd, byte *backData = NULL, byte *backLen = NULL);
StatusCode MIFARE_BlockExchangeWithData(mifare_desfire_tag *tag, byte cmd, byte *sendData = NULL, byte *sendLen = NULL, byte *backData = NULL, byte *backLen = NULL);
};
#endif

View File

@ -1,20 +0,0 @@
# RFID-DESFire #
Arduino library for MFRC522 and other RFID RC522 based modules.
This library extends the [MFRC522 library](https://github.com/miguelbalboa/rfid) adding extra functionality for MIFARE DESFire cards.
At the current stage no crypto has been implemented and a very limited subset of commands are available.
## Requirements ##
- [MFRC522 library](https://github.com/miguelbalboa/rfid)
### Future requirements ###
This libraries have been planned but need to be implemented and confirmed to be working properlly with DESFire library.
- [Arduino DES library](https://github.com/spaniakos/ArduinoDES/) (Not yet implemented)
- [Arduino AES library](https://github.com/spaniakos/AES/) (Not yet implemented)
## Credits ##
[EasyPay](https://github.com/nceruchalu/easypay) has been an invaluable source of information due to the great documentation in its comments.

View File

@ -1,143 +0,0 @@
/*
* --------------------------------------------------------------------------------------------------------------------
* Example sketch/program showing how to read data from a PICC to serial.
* --------------------------------------------------------------------------------------------------------------------
* This is a MFRC522 library example; for further details and other examples see: https://github.com/miguelbalboa/rfid
*
* Example sketch/program showing how to read data from a PICC (that is: a RFID Tag or Card) using a MFRC522 based RFID
* Reader on the Arduino SPI interface.
*
* When the Arduino and the MFRC522 module are connected (see the pin layout below), load this sketch into Arduino IDE
* then verify/compile and upload it. To see the output: use Tools, Serial Monitor of the IDE (hit Ctrl+Shft+M). When
* you present a PICC (that is: a RFID Tag or Card) at reading distance of the MFRC522 Reader/PCD, the serial output
* will show the ID/UID, type and any data blocks it can read. Note: you may see "Timeout in communication" messages
* when removing the PICC from reading distance too early.
*
* If your reader supports it, this sketch/program will read all the PICCs presented (that is: multiple tag reading).
* So if you stack two or more PICCs on top of each other and present them to the reader, it will first output all
* details of the first and then the next PICC. Note that this may take some time as all data blocks are dumped, so
* keep the PICCs at reading distance until complete.
*
* @license Released into the public domain.
*
* Typical pin layout used:
* -----------------------------------------------------------------------------------------
* MFRC522 Arduino Arduino Arduino Arduino Arduino
* Reader/PCD Uno/101 Mega Nano v3 Leonardo/Micro Pro Micro
* Signal Pin Pin Pin Pin Pin Pin
* -----------------------------------------------------------------------------------------
* RST/Reset RST 9 5 D9 RESET/ICSP-5 RST
* SPI SS SDA(SS) 10 53 D10 10 10
* SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16
* SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14
* SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15
*/
#include <SPI.h>
#include <MFRC522.h>
#include <Desfire.h>
#define RST_PIN 9 // Configurable, see typical pin layout above
#define SS_PIN 10 // Configurable, see typical pin layout above
DESFire mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance
void setup() {
Serial.begin(9600); // Initialize serial communications with the PC
while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4)
SPI.begin(); // Init SPI bus
mfrc522.PCD_Init(); // Init MFRC522
mfrc522.PCD_DumpVersionToSerial(); // Show details of PCD - MFRC522 Card Reader details
Serial.println(F("Scan PICC to see UID, SAK, type, and data blocks..."));
}
void loop() {
// Look for new cards
if ( ! mfrc522.PICC_IsNewCardPresent()) {
return;
}
// Select one of the cards
if ( ! mfrc522.PICC_ReadCardSerial()) {
return;
}
if (mfrc522.uid.sak != 0x20) {
// Dump debug info about the card; PICC_HaltA() is automatically called
mfrc522.PICC_DumpToSerial(&(mfrc522.uid));
return;
}
// Show an extra line
Serial.println();
DESFire::mifare_desfire_tag tag;
DESFire::StatusCode response;
tag.pcb = 0x0A;
tag.cid = 0x00;
memset(tag.selected_application, 0, 3);
// Make sure none DESFire status codes have DESFireStatus code to OK
response.desfire = DESFire::MF_OPERATION_OK;
byte ats[16];
byte atsLength = 16;
response.mfrc522 = mfrc522.PICC_RequestATS(ats, &atsLength);
if ( ! mfrc522.IsStatusCodeOK(response)) {
Serial.println(F("Failed to get ATS!"));
Serial.println(mfrc522.GetStatusCodeName(response));
Serial.println(response.mfrc522);
mfrc522.PICC_HaltA();
return;
}
// TODO: Should do checks but since I know my DESFire allows and requires PPS...
// PPS1 is ommitted and, therefore, 0x00 is used (106kBd)
response.mfrc522 = mfrc522.PICC_ProtocolAndParameterSelection(0x00, 0x11);
if ( ! mfrc522.IsStatusCodeOK(response)) {
Serial.println(F("Failed to perform protocol and parameter selection (PPS)!"));
Serial.println(mfrc522.GetStatusCodeName(response));
mfrc522.PICC_HaltA();
return;
}
// MIFARE DESFire should respond to a GetVersion command
DESFire::MIFARE_DESFIRE_Version_t desfireVersion;
response = mfrc522.MIFARE_DESFIRE_GetVersion(&tag, &desfireVersion);
if ( ! mfrc522.IsStatusCodeOK(response)) {
Serial.println(F("Failed to get a response for GetVersion!"));
Serial.println(mfrc522.GetStatusCodeName(response));
mfrc522.PICC_HaltA();
return;
}
// Dump MIFARE DESFire version information.
// NOTE: KEEP YOUR CARD CLOSE TO THE READER!
// This method takes some time and the card will be read
// once output ends! If you remove the card too fast
// a timeout will occur!
mfrc522.PICC_DumpMifareDesfireVersion(&tag, &desfireVersion);
mfrc522.PICC_DumpMifareDesfireMasterKey(&tag);
DESFire::mifare_desfire_aid_t aids[MIFARE_MAX_APPLICATION_COUNT];
byte applicationCount = 0;
response = mfrc522.MIFARE_DESFIRE_GetApplicationIds(&tag, aids, &applicationCount);
if ( ! mfrc522.IsStatusCodeOK(response)) {
Serial.println(F("Failed to get application IDs!"));
Serial.println(mfrc522.GetStatusCodeName(response));
mfrc522.PICC_HaltA();
return;
}
// Dump all applications
for (byte aidIndex = 0; aidIndex < applicationCount; aidIndex++) {
mfrc522.PICC_DumpMifareDesfireApplication(&tag, &(aids[aidIndex]));
}
// Call PICC_HaltA()
mfrc522.PICC_HaltA();
Serial.println();
}

View File

@ -21,3 +21,6 @@ lib_deps =
uipethernet/UIPEthernet@^2.0.12
bblanchon/ArduinoJson@^6.19.4
mbed-feb11/Crypto@0.0.0+sha.f04410cef037
adafruit/Adafruit PN532@^1.3.3
build_flags =
-D PN532_I2C_ADDRESS=0x24

View File

@ -1,21 +1,48 @@
#include "nfc.h"
#include "NFC.h"
#include <SPI.h>
#include <PubSubClient.h>
#include <DESFire.h>
#include "helpers.h"
NFC::NFC(int pin_ss, int pin_rst)
NFC::NFC(int pin_sda, int pin_scl)
{
rfid = new DESFire(pin_ss, pin_rst);
Serial.println("NFC: Start");
SPI.begin();
rfid->PCD_Init();
rfid->PCD_DumpVersionToSerial();
rfid = new Adafruit_PN532(PN532_IRQ, PN532_RESET);
rfid->begin();
uint32_t versiondata = rfid->getFirmwareVersion();
if (!versiondata)
{
Serial.print("Didn't find PN53x board");
while (1)
{
yield(); // halt
}
}
// Got ok data, print it out!
Serial.print("NFC: Found chip PN5");
Serial.println((versiondata >> 24) & 0xFF, HEX);
Serial.print("NFC: Firmware ver. ");
Serial.print((versiondata >> 16) & 0xFF, DEC);
Serial.print('.');
Serial.println((versiondata >> 8) & 0xFF, DEC);
}
MFRC522::Uid NFC::getUID()
String NFC::getUID()
{
return uid;
String uidStr;
for (int i = 0; i < uidLength; i++)
{
if (uid[i] < 0x10)
{
uidStr += "0";
}
uidStr += String(uid[i], HEX);
}
return uidStr;
}
bool NFC::hasCardSelected()
@ -25,72 +52,129 @@ bool NFC::hasCardSelected()
bool NFC::testNFC()
{
return rfid->PCD_PerformSelfTest();
// return rfid->PCD_PerformSelfTest();
}
bool NFC::checkforCard()
{
// RequestA
if (!rfid->PICC_IsNewCardPresent())
// Attempt to read a card (timeout in milliseconds, adjust as needed)
if (!rfid->readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 1000))
{
Serial.println("No card found within the timeout period");
// No card found within the timeout period
return false;
}
// PICC_Select ???
if (!rfid->PICC_ReadCardSerial())
// At this point, a card has been detected and its UID is in 'uid' with length 'uidLength'
// If you expect a DESFire card, you might assume its UID is 7 bytes.
// (Alternatively, if you extend the library to capture the SAK, you could check that here.)
if (uidLength == 7)
{
return false;
}
// Check for DESFire
if (rfid->uid.sak != 0x20)
{
return false;
}
Serial.println("DESFire card detected");
Serial.print("UID: ");
printbytes(uid, uidLength);
return true;
}
// If we're getting 4 bytes, we might be scanning a MIFARE Classic or another ISO14443A card.
if (uidLength == 4)
{
Serial.println("MIFARE Classic or another ISO14443A card detected");
Serial.print("UID: ");
printbytes(uid, uidLength);
return true;
}
// The card detected does not match the expected DESFireor MIFARE Classic UID length
Serial.println("No card detected");
return false;
}
bool NFC::connecttoCard()
{
// RATS
byte ats[16];
byte atsLength = 16;
MFRC522::StatusCode state = rfid->PICC_RequestATS(ats, &atsLength);
if (state != MFRC522::STATUS_OK)
// --- RATS (Request for ATS) ---
// RATS command format: [0xE0, param]
// The parameter byte encodes the Frame Size and other options.
// Here we use 0x80 as an example parameter (adjust as needed for your card).
uint8_t ratsCmd[] = {0xE0, 0x80};
uint8_t atsResponse[16]; // Buffer to hold the ATS response
uint8_t atsResponseLength = sizeof(atsResponse);
// Send the RATS command via inDataExchange.
// inDataExchange() returns a negative value on error.
int result = rfid->inDataExchange(ratsCmd, sizeof(ratsCmd), atsResponse, &atsResponseLength);
if (result < 0)
{
Serial.println(F("Failed ATS"));
Serial.println(state);
rfid->PICC_HaltA();
// PN532 does not offer a dedicated halt command,
// so you may simply return false to abort communication.
return false;
}
// PPS
state = rfid->PICC_ProtocolAndParameterSelection(0x00, 0x11, 0x00);
if (state != MFRC522::STATUS_OK)
// (Optional) Process the ATS response here if needed.
// The ATS provides card-specific information, such as supported protocols and frame sizes.
// --- PPS (Protocol and Parameter Selection) ---
// PPS command format (example): [0xD0, PPS0, PPS1]
// In this example, PPS0 is set to 0x11 and PPS1 to 0x00.
// Adjust the values according to your protocol requirements.
uint8_t ppsCmd[] = {0xD0, 0x11, 0x00};
uint8_t ppsResponse[16];
uint8_t ppsResponseLength = sizeof(ppsResponse);
result = rfid->inDataExchange(ppsCmd, sizeof(ppsCmd), ppsResponse, &ppsResponseLength);
if (result < 0)
{
Serial.println("Failed PPS");
Serial.println(state);
rfid->PICC_HaltA();
Serial.println(F("Failed PPS"));
return false;
}
// If both exchanges were successful, mark the card as selected.
cardSelected = true;
uid = rfid->uid;
// Save any card-specific data if needed here.
// Note: Unlike MFRC522, Adafruit_PN532 does not store the UID in a public member.
// We save the UID when we detect the card.
pcb = 0x0A;
return true;
}
bool NFC::disconnectCard()
{
MFRC522::StatusCode state = rfid->PICC_HaltA();
if (state != MFRC522::STATUS_OK)
// ISO14443A HALT command: 0x50, 0x00, followed by the two-byte CRC.
// You can either compute the CRC or, if your setup automatically appends it,
// simply send the command without CRC bytes.
// Here, we assume you have to include the CRC.
// Note: The correct CRC for [0x50, 0x00] is usually 0x00, 0x00 if the PN532
// is set to auto-calculate CRC, or you can compute it if needed.
// Option 1: If your PN532 is configured to append the CRC automatically,
// you can send just the two-byte command.
uint8_t haltCmd[] = {0x50, 0x00};
uint8_t response[8];
uint8_t responseLength = sizeof(response);
int status = rfid->inDataExchange(haltCmd, sizeof(haltCmd), response, &responseLength);
if (status < 0)
{
Serial.println(F("Failed PICC_HaltA"));
Serial.println(state);
Serial.println(F("Failed to send HALT command"));
Serial.println(status);
return false;
}
// Option 2: If you need to send a complete command with CRC, you must
// calculate the CRC bytes for [0x50, 0x00]. For many ISO14443A cards, the CRC
// may be 0x00, 0x00 when using a PN532 in auto-CRC mode, so this might work:
//
// uint8_t haltCmd[] = { 0x50, 0x00, 0x00, 0x00 };
// int status = rfid->inDataExchange(haltCmd, sizeof(haltCmd), response, &responseLength);
// if (status < 0) { ... }
//
// Uncomment the above block and comment out the Option 1 block if needed.
return true;
}
@ -98,48 +182,3 @@ bool NFC::testCard()
{
return true;
}
MFRC522::StatusCode NFC::Transceive(byte *command, byte command_len, byte *response, byte *response_len)
{
if (command_len >= 0xFF - 4)
{
return MFRC522::STATUS_NO_ROOM;
}
MFRC522::StatusCode state;
byte request_buffer[APDU_BUFFER_SIZE + 4]; // APDU Size + PCB + CID + 2x CRC
byte request_buffer_size = command_len + 4;
request_buffer[0] = pcb;
request_buffer[1] = cid;
memcpy(&request_buffer[2], command, command_len);
// Update the PCB
if (pcb == 0x0A)
pcb = 0x0B;
else
pcb = 0x0A;
// Calculate CRC_A
state = rfid->PCD_CalculateCRC(request_buffer, request_buffer_size - 2, &request_buffer[request_buffer_size - 2]);
if (state != MFRC522::STATUS_OK)
{
return state;
}
byte response_buffer[APDU_BUFFER_SIZE + 4] = {0}; // APDU Size + PCB + CID + 2x CRC
byte response_buffer_size = 0xFF;
printbytes(request_buffer, request_buffer_size);
state = rfid->PCD_TransceiveData(request_buffer, request_buffer_size, response_buffer, &response_buffer_size);
if (state != MFRC522::STATUS_OK)
{
return state;
}
memcpy(response, response_buffer + 2, response_buffer_size - 4);
*response_len = response_buffer_size - 4;
return state;
}

View File

@ -2,29 +2,31 @@
#define NFC_H
#include <PubSubClient.h>
#include <Desfire.h>
#include <Pins.h>
#include <Wire.h>
#include <Adafruit_PN532.h>
#define APDU_BUFFER_SIZE 256
class NFC
{
private:
MFRC522::Uid uid;
uint8_t uid[7]; // Buffer to hold the card's UID (PN532 supports up to 7 bytes for ISO14443A)
uint8_t uidLength; // Length of the UID (will be set by readPassiveTargetID)
byte pcb = 0x0A;
byte cid = 0x00;
bool cardSelected = false;
public:
DESFire *rfid;
NFC(int pin_ss, int pin_rst);
Adafruit_PN532 *rfid;
NFC(int pin_sda, int pin_scl);
bool testNFC();
bool checkforCard();
bool connecttoCard();
bool disconnectCard();
bool testCard();
bool hasCardSelected();
MFRC522::Uid getUID();
MFRC522::StatusCode Transceive(byte *command, byte command_len, byte *response, byte *response_len);
String getUID();
};
#endif

View File

@ -1,5 +1,5 @@
#include "otaproxy.h"
#include "nfc.h"
#include "OTAProxy.h"
#include "NFC.h"
#include <PubSubClient.h>
OTAProxy::OTAProxy(PubSubClient *mqttClient, NFC *nfc, int id)
@ -24,39 +24,45 @@ void OTAProxy::startOTA()
char topic[] = "fabreader/00000/startOTA";
sprintf(topic, "fabreader/%05d/startOTA", id);
MFRC522::Uid uid = nfc->getUID();
mqtt->publish(topic, uid.uidByte, uid.size);
String uid = nfc->getUID();
mqtt->publish(topic, uid.c_str(), uid.length());
Serial.println("Start OTA");
}
void OTAProxy::continueOTA(char *topic, byte *payload, unsigned int length)
{
char topic_requestOTA[] = "fabreader/00000/requestOTA";
// Create topic strings with sufficient buffer sizes
char topic_requestOTA[32];
sprintf(topic_requestOTA, "fabreader/%05d/requestOTA", id);
char topic_responseOTA[] = "fabreader/00000/responseOTA";
char topic_responseOTA[32];
sprintf(topic_responseOTA, "fabreader/%05d/responseOTA", id);
char topic_stopOTA[] = "fabreader/00000/stopOTA";
char topic_stopOTA[32];
sprintf(topic_stopOTA, "fabreader/%05d/stopOTA", id);
char topic_restartOTA[] = "fabreader/00000/restartOTA";
char topic_restartOTA[32];
sprintf(topic_restartOTA, "fabreader/%05d/restartOTA", id);
if (!strcmp(topic, topic_requestOTA))
{
Serial.println("Request OTA");
byte response[APDU_BUFFER_SIZE] = {0};
byte response_len;
MFRC522::StatusCode status;
byte response[APDU_BUFFER_SIZE] = {0};
// Maximum value for a uint8_t is 255.
uint8_t response_len = 255; // Use 255 even if APDU_BUFFER_SIZE is 256
// Cast length (unsigned int) to uint8_t, assuming it fits within 255 bytes.
uint8_t sendLength = (uint8_t)length;
Serial.println("Run Transceive");
status = nfc->Transceive(payload, length, response, &response_len);
Serial.printf("PICC_Tranceive: 0x%02x\n", status);
int status = nfc->rfid->inDataExchange(payload, sendLength, response, &response_len);
Serial.printf("PICC_Transceive: 0x%02x\n", status);
if (status != MFRC522::STATUS_OK)
if (status < 0)
{
cancelOTA();
return;
@ -71,14 +77,11 @@ void OTAProxy::continueOTA(char *topic, byte *payload, unsigned int length)
nfc->disconnectCard();
activeOTA = false;
}
// else if(!strcmp(topic, topic_restartOTA))
// else if (!strcmp(topic, topic_restartOTA))
// {
// Serial.println("Restart OTA");
// while(!(nfc->deselectCard()));
// // if(nfc->hasNewCard())
// // {
// // startOTA();
// // }
// // Add your PN532-specific card deselection logic here if needed.
// // For example, you might send a halt command or wait until the card is removed.
// }
}
@ -86,11 +89,14 @@ void OTAProxy::cancelOTA()
{
char topic_cancelOTA[] = "fabreader/00000/cancelOTA";
sprintf(topic_cancelOTA, "fabreader/%05d/cancelOTA", id);
MFRC522::Uid uid = nfc->getUID();
mqtt->publish(topic_cancelOTA, uid.uidByte, uid.size);
String uid = nfc->getUID();
mqtt->publish(topic_cancelOTA, uid.c_str(), uid.length());
while (!(nfc->disconnectCard()))
;
{
yield();
}
activeOTA = false;
Serial.println("Cancel OTA");

View File

@ -2,7 +2,7 @@
#define OTAProxy_H
#include <PubSubClient.h>
#include "nfc.h"
#include "NFC.h"
#define MSG_BUFFER_SIZE 50
class OTAProxy

View File

@ -1,18 +1,12 @@
#ifndef PINS_H
#define PINS_H
// Pins for ESP8266
#define PIN_SDA 4
#define PIN_SCL 5
#define PN532_IRQ 4
#define PN532_RESET 5
#define PIN_SPI_MOSI 13
#define PIN_SPI_MISO 12
#define PIN_SPI_SCK 14
#define PIN_SDA 16
#define PIN_SCL 17
#define PIN_RFID_RST 0
#define PIN_RFID_SPI_SS 2
#define PIN_ETH_SPI_SS 15
#define PIN_BUZZER 2
#define PIN_BUZZER 16
#define PIN_BUTTON A0
#endif

View File

@ -6,7 +6,6 @@
#include <NFC.h>
#include <OTAProxy.h>
#include <helpers.h>
#include <Desfire.h>
#include <Display.h>
#include <WiFi.h>
@ -14,10 +13,8 @@
#include <WiFiClient.h>
#include <PubSubClient.h>
#include <SPI.h>
WiFiClient espClient;
// Config_Data config;
PubSubClient *mqtt;
NFC *nfc;
@ -115,20 +112,15 @@ void setup()
Serial.println("Booting ...");
pinMode(PIN_BUZZER, OUTPUT);
pinMode(PIN_BUTTON, INPUT);
pinMode(PIN_ETH_SPI_SS, OUTPUT);
digitalWrite(PIN_ETH_SPI_SS, HIGH);
display = new Display(PIN_SDA, PIN_SCL, FABREADERID);
display->clearReaderInfo();
display->writeInfo("Start NFC ...");
Serial.println("Connecting NFC ...");
nfc = new NFC(PIN_RFID_SPI_SS, PIN_RFID_RST);
if (!(nfc->rfid->PCD_PerformSelfTest()))
{
Serial.println("NFC Test failed");
}
nfc = new NFC(PIN_SDA, PIN_SCL);
Serial.println("NFC connected");
display->writeInfo("Start WIFI ...");